refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD across contents and paths, upstream package/repo coordinates -> @golem15/msd-core and golem15com/msd-core. Deep links into upstream history, sibling upstream packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is. Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line, package/plugin identity, regenerated lockfile, install-tree fixtures, derived registries and benchmark baseline; migration checksum baseline re-locked (MSD keeps its own install state, so no install had applied the old sums); sort-order and regex-escaped expectations in tests adjusted.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* GSD plugin for OpenCode.ai (CommonJS)
|
||||
* MSD plugin for OpenCode.ai (CommonJS)
|
||||
*
|
||||
* Architecture: SUBPROCESS REUSE. Instead of re-implementing hook logic inside
|
||||
* the plugin, this file is a thin adapter that spawns the existing Claude Code
|
||||
@@ -11,36 +11,36 @@
|
||||
* - block → throw Error (OpenCode returns the error to the model)
|
||||
* - advisory → output.metadata + console.error (best-effort surfacing)
|
||||
*
|
||||
* Namespace conversion (/gsd:xxx → /gsd-xxx) reuses scripts/fix-slash-commands.cjs
|
||||
* Namespace conversion (/msd:xxx → /msd-xxx) reuses scripts/fix-slash-commands.cjs
|
||||
* via require(), keeping the single source of truth.
|
||||
*
|
||||
* ── Two distribution shapes, one adapter (issue #1914) ─────────────────────
|
||||
* This single file serves both distribution paths, distinguished at load time
|
||||
* by REPO_ROOT (path.resolve(__dirname, "../..")):
|
||||
*
|
||||
* • Option 1 — file copy (the supported GSD path). `bin/install.js` copies
|
||||
* this file to <opencodeConfigDir>/plugins/gsd-core.js, so REPO_ROOT is the
|
||||
* OpenCode config dir. GSD's own install already stages `hooks/*.js` and
|
||||
* `gsd-core/` there (ADR-857 skips hook *registration* for OpenCode, not the
|
||||
* • Option 1 — file copy (the supported MSD path). `bin/install.js` copies
|
||||
* this file to <opencodeConfigDir>/plugins/msd-core.js, so REPO_ROOT is the
|
||||
* OpenCode config dir. MSD's own install already stages `hooks/*.js` and
|
||||
* `msd-core/` there (ADR-857 skips hook *registration* for OpenCode, not the
|
||||
* file copy), so the hook bridge and content rewriting resolve natively.
|
||||
* Commands/agents/skills are ALREADY registered by GSD's native file copy in
|
||||
* Commands/agents/skills are ALREADY registered by MSD's native file copy in
|
||||
* this mode, so the plugin's own config-hook registration is redundant and is
|
||||
* SKIPPED (see IS_PACKAGE_TREE) to avoid double-registration.
|
||||
*
|
||||
* • Option 2 — package / git-spec. When loaded from the package tree (npm
|
||||
* `main`, or an OpenCode git-spec install), REPO_ROOT is the package root and
|
||||
* the source layout (commands/gsd/, agents/, skills/) is present. Here the
|
||||
* the source layout (commands/msd/, agents/, skills/) is present. Here the
|
||||
* plugin IS the sole registrar, so it registers commands/agents/skills too.
|
||||
*
|
||||
* IS_PACKAGE_TREE keys off the presence of the SOURCE command layout
|
||||
* (commands/gsd/), which only exists in the package tree — never in an installed
|
||||
* (commands/msd/), which only exists in the package tree — never in an installed
|
||||
* config dir (that uses the flattened command/ layout). The hook bridge and
|
||||
* Read-time content rewriting run in BOTH modes; only the config-hook
|
||||
* registration of commands/agents/skills is gated.
|
||||
*
|
||||
* Runtime-specific hooks are deliberately excluded:
|
||||
* - gsd-statusline.js / gsd-update-banner.js (Claude Code statusline)
|
||||
* - gsd-cursor-*.js (Cursor-specific)
|
||||
* - msd-statusline.js / msd-update-banner.js (Claude Code statusline)
|
||||
* - msd-cursor-*.js (Cursor-specific)
|
||||
* - *.sh scripts (invoked directly by commands/agents, not hook events)
|
||||
*/
|
||||
|
||||
@@ -51,12 +51,12 @@ const fs = require("fs");
|
||||
const os = require("os");
|
||||
const { spawnSync } = require("child_process");
|
||||
|
||||
// Resolve REPO_ROOT to the directory that actually holds the GSD payload
|
||||
// (hooks/ + gsd-core/). This must work across three physical layouts because a
|
||||
// Resolve REPO_ROOT to the directory that actually holds the MSD payload
|
||||
// (hooks/ + msd-core/). This must work across three physical layouts because a
|
||||
// single adapter file serves both distribution shapes (see header):
|
||||
// • package/git-spec tree: <root>/.opencode/plugins/gsd-core.js → <root>
|
||||
// • global file-copy: ~/.config/opencode/plugins/gsd-core.js → ~/.config/opencode
|
||||
// • local file-copy: <proj>/.opencode/plugins/gsd-core.js → <proj>/.opencode
|
||||
// • package/git-spec tree: <root>/.opencode/plugins/msd-core.js → <root>
|
||||
// • global file-copy: ~/.config/opencode/plugins/msd-core.js → ~/.config/opencode
|
||||
// • local file-copy: <proj>/.opencode/plugins/msd-core.js → <proj>/.opencode
|
||||
// A fixed "../.." only works for the first; the copied layouts sit one level
|
||||
// shallower. Walking up to the first ancestor containing BOTH payload markers
|
||||
// resolves all three deterministically. Falls back to the package-tree
|
||||
@@ -66,7 +66,7 @@ function resolveRepoRoot(startDir) {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
if (
|
||||
fs.existsSync(path.join(dir, "hooks")) &&
|
||||
fs.existsSync(path.join(dir, "gsd-core"))
|
||||
fs.existsSync(path.join(dir, "msd-core"))
|
||||
) {
|
||||
return dir;
|
||||
}
|
||||
@@ -84,16 +84,16 @@ function resolveRepoRoot(startDir) {
|
||||
// CJS: __dirname is a global, no need to derive from import.meta.url
|
||||
const REPO_ROOT = resolveRepoRoot(__dirname);
|
||||
const HOOKS_DIR = path.join(REPO_ROOT, "hooks");
|
||||
const COMMANDS = path.join(REPO_ROOT, "commands", "gsd");
|
||||
const COMMANDS = path.join(REPO_ROOT, "commands", "msd");
|
||||
const AGENTS = path.join(REPO_ROOT, "agents");
|
||||
const SKILLS = path.join(REPO_ROOT, "skills");
|
||||
const GSD_CORE = path.join(REPO_ROOT, "gsd-core");
|
||||
const MSD_CORE = path.join(REPO_ROOT, "msd-core");
|
||||
|
||||
// True only when loaded from the package/source tree (Option 2), detected by the
|
||||
// presence of the SOURCE command layout (commands/gsd/). In an installed OpenCode
|
||||
// presence of the SOURCE command layout (commands/msd/). In an installed OpenCode
|
||||
// config dir (Option 1) this directory is absent — the flattened command/ layout
|
||||
// is used instead — so the plugin skips its own command/agent/skill registration
|
||||
// and lets GSD's native file copy own that surface (avoids double-registration).
|
||||
// and lets MSD's native file copy own that surface (avoids double-registration).
|
||||
const IS_PACKAGE_TREE = fs.existsSync(COMMANDS);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -198,7 +198,7 @@ function mapToolInput(args) {
|
||||
* - exit 2: block (Claude convention; reason in stdout JSON)
|
||||
* - any error: exit 0 silently (hooks swallow their own errors)
|
||||
*
|
||||
* @param {string} hookFile filename under hooks/, e.g. "gsd-prompt-guard.js"
|
||||
* @param {string} hookFile filename under hooks/, e.g. "msd-prompt-guard.js"
|
||||
* @param {object} payload stdin JSON (hook_event_name, tool_name, ...)
|
||||
* @param {object} [opts]
|
||||
* @param {number} [opts.timeout=8000] spawn timeout in ms
|
||||
@@ -217,9 +217,9 @@ function runHook(hookFile, payload, opts = {}) {
|
||||
if (!warnedMissingHooks.has(hookFile)) {
|
||||
warnedMissingHooks.add(hookFile);
|
||||
console.error(
|
||||
`[gsd-core] hook script missing: ${hookPath} — ${hookFile} is NOT ` +
|
||||
"enforced. The GSD install may be incomplete; reinstall (or run " +
|
||||
"/gsd-update) to restage the hooks/ bundle.",
|
||||
`[msd-core] hook script missing: ${hookPath} — ${hookFile} is NOT ` +
|
||||
"enforced. The MSD install may be incomplete; reinstall (or run " +
|
||||
"/msd-update) to restage the hooks/ bundle.",
|
||||
);
|
||||
}
|
||||
return { stdout: "", exitCode: 0, timedOut: false };
|
||||
@@ -247,7 +247,7 @@ function runHook(hookFile, payload, opts = {}) {
|
||||
/**
|
||||
* In-process check for whether context-usage warnings are disabled in project
|
||||
* config. Mirrors the exact semantics of the same check inside
|
||||
* hooks/gsd-context-monitor.js (introduced by #1073): an explicit
|
||||
* hooks/msd-context-monitor.js (introduced by #1073): an explicit
|
||||
* `config.hooks.context_warnings === false` disables them; a missing or
|
||||
* unparseable .planning/config.json keeps them enabled (the default).
|
||||
*
|
||||
@@ -278,7 +278,7 @@ function contextWarningsDisabled(cwd) {
|
||||
* Parse a hook's stdout and apply its effect to the OpenCode output object.
|
||||
*
|
||||
* - Block → throw Error(parsed.reason) so OpenCode aborts the tool call
|
||||
* - Advisory→ append to output.metadata._gsdAdvisory[] and log to stderr
|
||||
* - Advisory→ append to output.metadata._msdAdvisory[] and log to stderr
|
||||
* - Silent → no-op
|
||||
*
|
||||
* @param {{ stdout: string, exitCode: number }} hookResult
|
||||
@@ -301,7 +301,7 @@ function handleHookResult(hookResult, output) {
|
||||
const isBlock = exitCode === 2 || (parsed && parsed.decision === "block");
|
||||
if (isBlock) {
|
||||
const reason =
|
||||
(parsed && parsed.reason) || "Blocked by GSD hook (no reason provided).";
|
||||
(parsed && parsed.reason) || "Blocked by MSD hook (no reason provided).";
|
||||
throw new Error(reason);
|
||||
}
|
||||
|
||||
@@ -317,10 +317,10 @@ function handleHookResult(hookResult, output) {
|
||||
// sequence (prompt guard, read guard, worktree guard, workflow guard).
|
||||
// Storing a scalar would let a later advisory clobber an earlier one, so
|
||||
// collect them all.
|
||||
if (!Array.isArray(output.metadata._gsdAdvisory)) {
|
||||
output.metadata._gsdAdvisory = [];
|
||||
if (!Array.isArray(output.metadata._msdAdvisory)) {
|
||||
output.metadata._msdAdvisory = [];
|
||||
}
|
||||
output.metadata._gsdAdvisory.push(advisory);
|
||||
output.metadata._msdAdvisory.push(advisory);
|
||||
}
|
||||
// Best-effort visibility when metadata isn't surfaced to the model
|
||||
console.error(advisory);
|
||||
@@ -347,7 +347,7 @@ function parseFrontmatter(content) {
|
||||
}
|
||||
|
||||
// Rewrite @~/.claude/ includes to point at the repo root.
|
||||
// Also applies /gsd:xxx → /gsd-xxx namespace conversion via the shared
|
||||
// Also applies /msd:xxx → /msd-xxx namespace conversion via the shared
|
||||
// transform from scripts/fix-slash-commands.cjs (single source of truth).
|
||||
function rewriteRefs(content) {
|
||||
let out = content.replace(/@~\/\.claude\//g, `@${REPO_ROOT}/`);
|
||||
@@ -370,38 +370,38 @@ function loadDir(dir, keyFn, valFn) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Runtime content transform — for Read tool results on GSD-managed files
|
||||
// Runtime content transform — for Read tool results on MSD-managed files
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Directories whose .md files may contain ~/.claude/ paths and gsd: namespace
|
||||
// Directories whose .md files may contain ~/.claude/ paths and msd: namespace
|
||||
// refs. When the model reads these via the Read tool, we transparently rewrite
|
||||
// both so OpenCode sees correct paths and hyphen-form command names.
|
||||
const GSD_MANAGED_DIRS = [
|
||||
path.join(GSD_CORE, "workflows"),
|
||||
path.join(GSD_CORE, "references"),
|
||||
path.join(GSD_CORE, "templates"),
|
||||
path.join(GSD_CORE, "contexts"),
|
||||
const MSD_MANAGED_DIRS = [
|
||||
path.join(MSD_CORE, "workflows"),
|
||||
path.join(MSD_CORE, "references"),
|
||||
path.join(MSD_CORE, "templates"),
|
||||
path.join(MSD_CORE, "contexts"),
|
||||
COMMANDS,
|
||||
AGENTS,
|
||||
SKILLS,
|
||||
];
|
||||
|
||||
function isGsdManagedFile(filePath) {
|
||||
function isMsdManagedFile(filePath) {
|
||||
if (!filePath) return false;
|
||||
const resolved = path.resolve(filePath);
|
||||
return GSD_MANAGED_DIRS.some(
|
||||
return MSD_MANAGED_DIRS.some(
|
||||
(dir) => resolved === dir || resolved.startsWith(dir + path.sep),
|
||||
);
|
||||
}
|
||||
|
||||
// Rewrite content for OpenCode consumption:
|
||||
// 1. @-include paths: @~/.claude/ → @<REPO_ROOT>/
|
||||
// 2. plain-text paths: ~/.claude/gsd-core/ → <GSD_CORE>/
|
||||
// 3. namespace: gsd:xxx → gsd-xxx (via fix-slash-commands.cjs)
|
||||
// 2. plain-text paths: ~/.claude/msd-core/ → <MSD_CORE>/
|
||||
// 3. namespace: msd:xxx → msd-xxx (via fix-slash-commands.cjs)
|
||||
function rewriteContent(content) {
|
||||
let out = content;
|
||||
out = out.replace(/@~\/\.claude\//g, `@${REPO_ROOT}/`);
|
||||
out = out.replace(/~\/\.claude\/gsd-core\//g, `${GSD_CORE}/`);
|
||||
out = out.replace(/~\/\.claude\/msd-core\//g, `${MSD_CORE}/`);
|
||||
const transform = getNamespaceConverter();
|
||||
if (transform && _cmdNames && _cmdNames.length) {
|
||||
out = transform(out, _cmdNames);
|
||||
@@ -415,18 +415,18 @@ function rewriteContent(content) {
|
||||
//
|
||||
// OpenCode's skill loader reads SKILL.md files directly from disk and resolves
|
||||
// @-includes internally — this bypasses our tool.execute hooks. To make
|
||||
// @~/.claude/gsd-core/... includes resolve, we copy all SKILL.md files to a
|
||||
// cache directory with paths rewritten to the actual GSD_CORE location.
|
||||
// @~/.claude/msd-core/... includes resolve, we copy all SKILL.md files to a
|
||||
// cache directory with paths rewritten to the actual MSD_CORE location.
|
||||
//
|
||||
// Only used in package-tree mode (Option 2). In an installed OpenCode config
|
||||
// dir (Option 1) skills are already staged + registered by GSD's native file
|
||||
// dir (Option 1) skills are already staged + registered by MSD's native file
|
||||
// copy, so we never register skills from the plugin (see IS_PACKAGE_TREE).
|
||||
|
||||
const SKILLS_CACHE = path.join(
|
||||
os.homedir(),
|
||||
".cache",
|
||||
"opencode",
|
||||
"gsd-skills",
|
||||
"msd-skills",
|
||||
);
|
||||
|
||||
function prepareSkillsCache() {
|
||||
@@ -439,8 +439,8 @@ function prepareSkillsCache() {
|
||||
// Rewrite @-include paths only; namespace conversion is handled at
|
||||
// Read-time via tool.execute.after for workflow/reference files.
|
||||
const rewritten = raw
|
||||
.replace(/@~\/\.claude\/gsd-core\//g, `@${GSD_CORE}/`)
|
||||
.replace(/~\/\.claude\/gsd-core\//g, `${GSD_CORE}/`);
|
||||
.replace(/@~\/\.claude\/msd-core\//g, `@${MSD_CORE}/`)
|
||||
.replace(/~\/\.claude\/msd-core\//g, `${MSD_CORE}/`);
|
||||
const destDir = path.join(SKILLS_CACHE, dir);
|
||||
fs.mkdirSync(destDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(destDir, "SKILL.md"), rewritten);
|
||||
@@ -452,25 +452,25 @@ function prepareSkillsCache() {
|
||||
// Plugin entry
|
||||
// ===========================================================================
|
||||
|
||||
const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
const MsdCorePlugin = async ({ directory } = {}) => {
|
||||
if (directory) currentCwd = directory;
|
||||
|
||||
return {
|
||||
// ── Config: register commands / agents / skills paths ──────────────
|
||||
// Only in package-tree mode (Option 2). In an installed config dir
|
||||
// (Option 1) GSD's native file copy already registered these, so the
|
||||
// (Option 1) MSD's native file copy already registered these, so the
|
||||
// plugin stays out of registration to avoid double-registering.
|
||||
config: async (config) => {
|
||||
if (!IS_PACKAGE_TREE) return;
|
||||
|
||||
// Commands (commands/gsd/*.md → gsd-<name>)
|
||||
// Commands (commands/msd/*.md → msd-<name>)
|
||||
config.command = config.command || {};
|
||||
const cmds = loadDir(
|
||||
COMMANDS,
|
||||
(f) => "gsd-" + f.slice(0, -3),
|
||||
(f) => "msd-" + f.slice(0, -3),
|
||||
(body, fm, name) => ({
|
||||
template: rewriteRefs(body.trim()),
|
||||
description: fm.description || `GSD ${name.slice(0, -3)} command`,
|
||||
description: fm.description || `MSD ${name.slice(0, -3)} command`,
|
||||
}),
|
||||
);
|
||||
for (const [k, v] of Object.entries(cmds)) {
|
||||
@@ -484,7 +484,7 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
(f) => f.slice(0, -3),
|
||||
(body, fm, name) => ({
|
||||
prompt: rewriteRefs(body.trim()),
|
||||
description: fm.description || `GSD ${name.slice(0, -3)} agent`,
|
||||
description: fm.description || `MSD ${name.slice(0, -3)} agent`,
|
||||
mode: fm.mode || "subagent",
|
||||
}),
|
||||
);
|
||||
@@ -508,7 +508,7 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
// ── shell.env ───────────────────────────────────────────────────────
|
||||
"shell.env": async (_input, output) => {
|
||||
output.env = output.env || {};
|
||||
output.env.GSD_DIR = GSD_CORE;
|
||||
output.env.MSD_DIR = MSD_CORE;
|
||||
},
|
||||
|
||||
// ── tool.execute.before — PreToolUse hooks ─────────────────────────
|
||||
@@ -517,14 +517,14 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
const toolInput = mapToolInput(output.args || {});
|
||||
const cwd = currentCwd;
|
||||
|
||||
// 0. Read path rewrite — redirect ~/.claude/gsd-core/ to actual GSD_CORE
|
||||
// 0. Read path rewrite — redirect ~/.claude/msd-core/ to actual MSD_CORE
|
||||
// so the model can read workflow/reference/template files that SKILL.md
|
||||
// and command templates reference via the canonical Claude path.
|
||||
if (claudeTool === "Read" && toolInput.file_path) {
|
||||
const original = toolInput.file_path;
|
||||
const rewritten = original
|
||||
.replace(/^~\/\.claude\/gsd-core\//, GSD_CORE + "/")
|
||||
.replace(/(?:.*)\/\.claude\/gsd-core\//, GSD_CORE + "/");
|
||||
.replace(/^~\/\.claude\/msd-core\//, MSD_CORE + "/")
|
||||
.replace(/(?:.*)\/\.claude\/msd-core\//, MSD_CORE + "/");
|
||||
if (rewritten !== original) {
|
||||
const args = output.args || {};
|
||||
if (args.filePath) args.filePath = rewritten;
|
||||
@@ -539,7 +539,7 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
cwd,
|
||||
};
|
||||
// NOTE: session_id intentionally omitted for PreToolUse hooks.
|
||||
// gsd-read-guard.js treats a non-empty session_id as a Claude Code
|
||||
// msd-read-guard.js treats a non-empty session_id as a Claude Code
|
||||
// session and skips its advisory. On OpenCode we WANT the advisory.
|
||||
const prePayload = (overrides = {}) => ({
|
||||
...basePayload,
|
||||
@@ -550,42 +550,42 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
|
||||
const isWriteLike = ["Write", "Edit", "MultiEdit"].includes(claudeTool);
|
||||
|
||||
// 1. gsd-prompt-guard.js — injection scan on .planning/ writes
|
||||
// 1. msd-prompt-guard.js — injection scan on .planning/ writes
|
||||
if (claudeTool === "Write" || claudeTool === "Edit") {
|
||||
const r = runHook("gsd-prompt-guard.js", prePayload());
|
||||
const r = runHook("msd-prompt-guard.js", prePayload());
|
||||
handleHookResult(r, output);
|
||||
}
|
||||
|
||||
// 2. gsd-read-guard.js — read-before-edit advisory
|
||||
// 2. msd-read-guard.js — read-before-edit advisory
|
||||
if (claudeTool === "Write" || claudeTool === "Edit") {
|
||||
const r = runHook("gsd-read-guard.js", prePayload());
|
||||
const r = runHook("msd-read-guard.js", prePayload());
|
||||
handleHookResult(r, output);
|
||||
}
|
||||
|
||||
// 3. gsd-worktree-path-guard.js — hard-block edits outside worktree
|
||||
// 3. msd-worktree-path-guard.js — hard-block edits outside worktree
|
||||
if (isWriteLike) {
|
||||
const r = runHook("gsd-worktree-path-guard.js", prePayload());
|
||||
const r = runHook("msd-worktree-path-guard.js", prePayload());
|
||||
handleHookResult(r, output);
|
||||
}
|
||||
|
||||
// 4. gsd-write-guard.js — hard-block catastrophic shrink of curated
|
||||
// 4. msd-write-guard.js — hard-block catastrophic shrink of curated
|
||||
// .planning/ artifacts (ROADMAP.md, milestones/*-ROADMAP.md, STATE.md)
|
||||
if (claudeTool === "Write") {
|
||||
const r = runHook("gsd-write-guard.js", prePayload());
|
||||
const r = runHook("msd-write-guard.js", prePayload());
|
||||
handleHookResult(r, output);
|
||||
}
|
||||
|
||||
// 5. gsd-workflow-guard.js — workflow advisory + git-force-add block
|
||||
// 5. msd-workflow-guard.js — workflow advisory + git-force-add block
|
||||
// (covers Write/Edit/MultiEdit AND Bash force-add detection)
|
||||
if (isWriteLike || claudeTool === "Bash") {
|
||||
const r = runHook("gsd-workflow-guard.js", prePayload());
|
||||
const r = runHook("msd-workflow-guard.js", prePayload());
|
||||
handleHookResult(r, output);
|
||||
}
|
||||
|
||||
// 6. gsd-secret-read-guard.js — hard-block reads of .env / .env.<suffix> /
|
||||
// 6. msd-secret-read-guard.js — hard-block reads of .env / .env.<suffix> /
|
||||
// .secrets via Read (file_path), Grep (path or glob) and Bash (command)
|
||||
if (["Read", "Grep", "Bash"].includes(claudeTool)) {
|
||||
const r = runHook("gsd-secret-read-guard.js", prePayload());
|
||||
const r = runHook("msd-secret-read-guard.js", prePayload());
|
||||
handleHookResult(r, output);
|
||||
}
|
||||
},
|
||||
@@ -598,12 +598,12 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
const toolInput = mapToolInput(input.args || {});
|
||||
const cwd = currentCwd;
|
||||
|
||||
// GSD content transform — rewrite paths + namespace in Read results
|
||||
// MSD content transform — rewrite paths + namespace in Read results
|
||||
// BEFORE injection scanning so the scanner sees the final content.
|
||||
if (
|
||||
claudeTool === "Read" &&
|
||||
output.output &&
|
||||
isGsdManagedFile(toolInput.file_path)
|
||||
isMsdManagedFile(toolInput.file_path)
|
||||
) {
|
||||
const content =
|
||||
typeof output.output === "string"
|
||||
@@ -612,7 +612,7 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
output.output = rewriteContent(content);
|
||||
}
|
||||
|
||||
// gsd-read-injection-scanner.js — scan Read/WebFetch/WebSearch results
|
||||
// msd-read-injection-scanner.js — scan Read/WebFetch/WebSearch results
|
||||
if (
|
||||
claudeTool === "Read" ||
|
||||
claudeTool === "WebFetch" ||
|
||||
@@ -625,12 +625,12 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
tool_response: output.output,
|
||||
cwd,
|
||||
};
|
||||
const r = runHook("gsd-read-injection-scanner.js", payload);
|
||||
const r = runHook("msd-read-injection-scanner.js", payload);
|
||||
handleHookResult(r, output);
|
||||
return;
|
||||
}
|
||||
|
||||
// gsd-context-monitor.js — context usage warnings (Bash/Edit/Write/Task/...)
|
||||
// msd-context-monitor.js — context usage warnings (Bash/Edit/Write/Task/...)
|
||||
// Only meaningful when a session_id is tracked (writes metrics sentinel).
|
||||
// #2697: skip the subprocess spawn entirely when context warnings are
|
||||
// explicitly disabled in project config — the hook would exit early anyway,
|
||||
@@ -644,7 +644,7 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
session_id: currentSessionId,
|
||||
cwd,
|
||||
};
|
||||
const r = runHook("gsd-context-monitor.js", payload);
|
||||
const r = runHook("msd-context-monitor.js", payload);
|
||||
handleHookResult(r, output);
|
||||
}
|
||||
},
|
||||
@@ -657,13 +657,13 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
session_id: currentSessionId,
|
||||
cwd: currentCwd,
|
||||
};
|
||||
const r = runHook("gsd-context-monitor.js", payload);
|
||||
const r = runHook("msd-context-monitor.js", payload);
|
||||
handleHookResult(r, output);
|
||||
|
||||
// Also inject a GSD compaction breadcrumb (mirrors the original plugin)
|
||||
// Also inject a MSD compaction breadcrumb (mirrors the original plugin)
|
||||
output.context = output.context || [];
|
||||
output.context.push(
|
||||
`[GSD] Active session: ${currentSessionId}. Preserve any in-flight phase/plan state.`,
|
||||
`[MSD] Active session: ${currentSessionId}. Preserve any in-flight phase/plan state.`,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -679,14 +679,14 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
info?.id || event.sessionID || event.session_id || null;
|
||||
if (info?.directory) currentCwd = info.directory;
|
||||
|
||||
// gsd-ensure-canonical-path.js — no stdin dependency; silent
|
||||
runHook("gsd-ensure-canonical-path.js", {
|
||||
// msd-ensure-canonical-path.js — no stdin dependency; silent
|
||||
runHook("msd-ensure-canonical-path.js", {
|
||||
hook_event_name: "SessionStart",
|
||||
session_id: currentSessionId,
|
||||
cwd: currentCwd,
|
||||
});
|
||||
// gsd-check-update.js — spawns its own background worker; no stdin
|
||||
runHook("gsd-check-update.js", {
|
||||
// msd-check-update.js — spawns its own background worker; no stdin
|
||||
runHook("msd-check-update.js", {
|
||||
hook_event_name: "SessionStart",
|
||||
session_id: currentSessionId,
|
||||
cwd: currentCwd,
|
||||
@@ -709,17 +709,17 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
event: "change",
|
||||
cwd,
|
||||
};
|
||||
const r = runHook("gsd-config-reload.js", payload);
|
||||
const r = runHook("msd-config-reload.js", payload);
|
||||
// Advisory-only (additionalContext); surface to logs
|
||||
handleHookResult(r);
|
||||
return;
|
||||
}
|
||||
|
||||
// session.idle ↔ Claude Stop lifecycle point (#1682 Slice 1b/c).
|
||||
// OpenCode fires session.idle when the run quiesces. GSD maps it to the
|
||||
// OpenCode fires session.idle when the run quiesces. MSD maps it to the
|
||||
// Stop equivalent — the opencode-subset lifecycle peer of compaction
|
||||
// (compaction preserves state across context-window summarization; idle
|
||||
// marks end-of-turn). No-op sentinel today (GSD state is already
|
||||
// marks end-of-turn). No-op sentinel today (MSD state is already
|
||||
// persisted to .planning/), but it MUST be recognized so the declared
|
||||
// opencode-subset surface is fully wired and a future Stop-class hook can
|
||||
// attach without a plugin change.
|
||||
@@ -728,9 +728,9 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
}
|
||||
|
||||
// permission.asked / permission.replied — OpenCode permission lifecycle
|
||||
// (#2087, opencode.ai/docs/plugins). GSD gates tool INPUTS at
|
||||
// (#2087, opencode.ai/docs/plugins). MSD gates tool INPUTS at
|
||||
// tool.execute.before (read-guard, injection-scanner); the permission
|
||||
// grant/deny decision itself carries no GSD workflow-phase contribution,
|
||||
// grant/deny decision itself carries no MSD workflow-phase contribution,
|
||||
// so these are recognized sentinels — wired so a future permission-aware
|
||||
// gate can attach without a plugin change (the engine owns phase
|
||||
// sequencing; this host bus is session/tool/permission-scoped, never
|
||||
@@ -739,7 +739,7 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// session.error — OpenCode session-error lifecycle point (#2087). No GSD
|
||||
// session.error — OpenCode session-error lifecycle point (#2087). No MSD
|
||||
// hook fires here today (loop state is already persisted to .planning/);
|
||||
// recognized so the declared extension-event surface is fully wired and a
|
||||
// future error-class hook can attach without a plugin change.
|
||||
@@ -761,7 +761,7 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
// The subtlety: depending on how OpenCode's runtime (Node or Bun) imports a
|
||||
// CommonJS file, `mod` may be the raw `module.exports` OR an ESM namespace of
|
||||
// the form `{ default: module.exports, ...syntheticNamedExports }`. A plain
|
||||
// `module.exports = { id: "gsd-core", server }` literal risks a string `id`
|
||||
// `module.exports = { id: "msd-core", server }` literal risks a string `id`
|
||||
// appearing in `Object.values(mod)` (as a raw property, or as a lexer-
|
||||
// synthesized named export) — which would trip the throw. Two defenses:
|
||||
// 1. `id` is defined NON-ENUMERABLE, so it never appears in Object.values yet
|
||||
@@ -772,23 +772,23 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
||||
// Result: raw-CJS `Object.values` = `[server]`; ESM `Object.values` =
|
||||
// `[{server, <id non-enum>}]` — both fully extractable. Test-only helpers hang
|
||||
// off the `server` FUNCTION (`server._internals`), never as a sibling export.
|
||||
GsdCorePlugin._internals = {
|
||||
MsdCorePlugin._internals = {
|
||||
REPO_ROOT,
|
||||
IS_PACKAGE_TREE,
|
||||
mapToolName,
|
||||
mapToolInput,
|
||||
parseFrontmatter,
|
||||
rewriteContent,
|
||||
isGsdManagedFile,
|
||||
isMsdManagedFile,
|
||||
handleHookResult,
|
||||
GsdCorePlugin,
|
||||
MsdCorePlugin,
|
||||
};
|
||||
|
||||
const gsdCorePluginExport = { server: GsdCorePlugin };
|
||||
Object.defineProperty(gsdCorePluginExport, "id", {
|
||||
value: "gsd-core",
|
||||
const msdCorePluginExport = { server: MsdCorePlugin };
|
||||
Object.defineProperty(msdCorePluginExport, "id", {
|
||||
value: "msd-core",
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
configurable: false,
|
||||
});
|
||||
module.exports = gsdCorePluginExport;
|
||||
module.exports = msdCorePluginExport;
|
||||
Reference in New Issue
Block a user