@opengsd/gsd-core 1.5.0-rc.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/LICENSE +1 -1
- package/agents/gsd-executor.md +22 -0
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-planner.md +4 -0
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-verifier.md +45 -13
- package/bin/install.js +56 -100
- package/commands/gsd/autonomous.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/plan-phase.md +1 -1
- package/gemini-extension.json +1 -1
- package/gsd-core/bin/gsd-tools.cjs +85 -53
- package/gsd-core/bin/lib/agent-command-router.cjs +2 -2
- package/gsd-core/bin/lib/agent-install-check.cjs +143 -0
- package/gsd-core/bin/lib/audit-command-router.cjs +4 -4
- package/gsd-core/bin/lib/capability-activation.cjs +37 -10
- package/gsd-core/bin/lib/capability-registry.cjs +2 -0
- package/gsd-core/bin/lib/capability-state.cjs +80 -30
- package/gsd-core/bin/lib/capability-writer.cjs +5 -4
- package/gsd-core/bin/lib/check-command-router.cjs +50 -51
- package/gsd-core/bin/lib/commands.cjs +20 -2
- package/gsd-core/bin/lib/config-loader.cjs +3 -4
- package/gsd-core/bin/lib/config-schema.cjs +1 -1
- package/gsd-core/bin/lib/config-types.cjs +2 -1
- package/gsd-core/bin/lib/config.cjs +5 -2
- package/gsd-core/bin/lib/decisions.cjs +19 -1
- package/gsd-core/bin/lib/docs.cjs +14 -2
- package/gsd-core/bin/lib/frontmatter.cjs +2 -2
- package/gsd-core/bin/lib/gap-checker.cjs +5 -2
- package/gsd-core/bin/lib/git-base-branch.cjs +27 -1
- package/gsd-core/bin/lib/graphify-command-router.cjs +6 -8
- package/gsd-core/bin/lib/graphify.cjs +7 -31
- package/gsd-core/bin/lib/gsd2-import.cjs +2 -2
- package/gsd-core/bin/lib/init.cjs +30 -4
- package/gsd-core/bin/lib/intel-command-router.cjs +6 -3
- package/gsd-core/bin/lib/intel.cjs +28 -34
- package/gsd-core/bin/lib/io.cjs +2 -4
- package/gsd-core/bin/lib/learnings.cjs +2 -2
- package/gsd-core/bin/lib/loop-resolver.cjs +45 -167
- package/gsd-core/bin/lib/milestone.cjs +13 -5
- package/gsd-core/bin/lib/model-resolver.cjs +3 -4
- package/gsd-core/bin/lib/phase-id.cjs +3 -5
- package/gsd-core/bin/lib/phase-locator.cjs +3 -6
- package/gsd-core/bin/lib/phase.cjs +59 -13
- package/gsd-core/bin/lib/probe-core.cjs +40 -11
- package/gsd-core/bin/lib/profile-output.cjs +5 -2
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +660 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-parser.cjs +28 -25
- package/gsd-core/bin/lib/roadmap.cjs +9 -4
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +4 -1
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +24 -3
- package/gsd-core/bin/lib/state.cjs +75 -17
- package/gsd-core/bin/lib/task-command-router.cjs +2 -2
- package/gsd-core/bin/lib/teams-status.cjs +74 -0
- package/gsd-core/bin/lib/template.cjs +11 -2
- package/gsd-core/bin/lib/uat.cjs +64 -2
- package/gsd-core/bin/lib/verification.cjs +8 -5
- package/gsd-core/bin/lib/verify.cjs +311 -4
- package/gsd-core/bin/lib/workstream-inventory.cjs +2 -2
- package/gsd-core/bin/lib/workstream.cjs +8 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +44 -3
- package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
- package/gsd-core/references/planner-antipatterns.md +46 -0
- package/gsd-core/references/planning-config.md +5 -1
- package/gsd-core/references/prohibition-probe.md +80 -2
- package/gsd-core/references/worktree-branch-check.md +11 -5
- package/gsd-core/templates/verification-report.md +16 -3
- package/gsd-core/workflows/docs-update.md +23 -31
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +9 -0
- package/gsd-core/workflows/execute-phase.md +7 -2
- package/gsd-core/workflows/map-codebase.md +8 -10
- package/gsd-core/workflows/plan-phase.md +6 -0
- package/gsd-core/workflows/quick.md +26 -2
- package/gsd-core/workflows/settings-advanced.md +5 -5
- package/gsd-core/workflows/settings-integrations.md +5 -5
- package/gsd-core/workflows/spec-phase.md +30 -2
- package/gsd-core/workflows/verify-phase.md +22 -7
- package/hooks/dist/gsd-worktree-path-guard.js +34 -18
- package/hooks/gsd-worktree-path-guard.js +34 -18
- package/package.json +3 -3
- package/scripts/ci-prepare-test-scope.cjs +56 -14
- package/scripts/diff-touches-shipped-paths.cjs +5 -11
- package/scripts/gen-capability-registry.cjs +27 -1
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
- package/scripts/research-profiles.cjs +2 -2
- package/scripts/run-tests.cjs +1 -0
- package/gsd-core/bin/lib/core.cjs +0 -345
|
@@ -194,13 +194,14 @@
|
|
|
194
194
|
const fs = require('fs');
|
|
195
195
|
const path = require('path');
|
|
196
196
|
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
197
|
-
const
|
|
198
|
-
const { error, ERROR_REASON } =
|
|
197
|
+
const io = require('./lib/io.cjs');
|
|
198
|
+
const { error, ERROR_REASON, setJsonErrorMode, output } = io;
|
|
199
|
+
const projectRoot = require('./lib/project-root.cjs');
|
|
199
200
|
// Resolve findProjectRoot lazily at call time rather than binding it at module
|
|
200
|
-
// load. It is
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
const findProjectRoot = (...args) =>
|
|
201
|
+
// load. It is sourced from project-root.cjs; a call-time lookup is robust
|
|
202
|
+
// against any require/load-ordering edge where the export isn't bound yet
|
|
203
|
+
// when this entrypoint is first required (#604).
|
|
204
|
+
const findProjectRoot = (...args) => projectRoot.findProjectRoot(...args);
|
|
204
205
|
const { getActiveWorkstream } = require('./lib/planning-workspace.cjs');
|
|
205
206
|
const { resolveActiveWorkstream, applyResolvedWorkstreamEnv } = require('./lib/active-workstream-store.cjs');
|
|
206
207
|
const state = require('./lib/state.cjs');
|
|
@@ -255,7 +256,7 @@ const { getEffectiveAuthority, classifyDriftSeverity } = require('./lib/plan-dri
|
|
|
255
256
|
* @param {string} opts.cwd - project dir
|
|
256
257
|
* @param {boolean} opts.raw - raw output mode
|
|
257
258
|
* @param {Function} opts.error - error reporter
|
|
258
|
-
* @param {Function} opts.output - output emitter (
|
|
259
|
+
* @param {Function} opts.output - output emitter (output)
|
|
259
260
|
*/
|
|
260
261
|
function _dispatchNonFamily({ registryCommand, registryArgs, legacyCommand, legacyArgs, cwd, raw, error, output }) {
|
|
261
262
|
void registryCommand;
|
|
@@ -294,7 +295,7 @@ function _dispatchNonFamily({ registryCommand, registryArgs, legacyCommand, lega
|
|
|
294
295
|
* @param {string[]} opts.args Remaining args passed to the router
|
|
295
296
|
* @param {string} opts.cwd Project working directory
|
|
296
297
|
* @param {boolean} opts.raw Raw output mode flag
|
|
297
|
-
* @param {Function} opts.error Error reporter (
|
|
298
|
+
* @param {Function} opts.error Error reporter (io.error)
|
|
298
299
|
* @param {object} [opts.registry] Injectable registry (for tests)
|
|
299
300
|
* @param {Function} [opts.requireModule] Injectable module loader (for tests)
|
|
300
301
|
* @returns {boolean} true if the command was dispatched, false otherwise
|
|
@@ -404,10 +405,10 @@ async function main() {
|
|
|
404
405
|
// their plain-text diagnostic.
|
|
405
406
|
const jsonErrorsIdx = args.indexOf('--json-errors');
|
|
406
407
|
if (jsonErrorsIdx !== -1) {
|
|
407
|
-
|
|
408
|
+
setJsonErrorMode(true);
|
|
408
409
|
args.splice(jsonErrorsIdx, 1);
|
|
409
410
|
} else if (process.env.GSD_JSON_ERRORS === '1') {
|
|
410
|
-
|
|
411
|
+
setJsonErrorMode(true);
|
|
411
412
|
}
|
|
412
413
|
|
|
413
414
|
// Optional cwd override for sandboxed subagents running outside project root.
|
|
@@ -433,7 +434,7 @@ async function main() {
|
|
|
433
434
|
// Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree.
|
|
434
435
|
// However, in monorepo worktrees where the subdirectory itself owns .planning/,
|
|
435
436
|
// skip worktree resolution — the CWD is already the correct project root.
|
|
436
|
-
const { resolveWorktreeRoot } = require('./lib/
|
|
437
|
+
const { resolveWorktreeRoot } = require('./lib/worktree-safety.cjs');
|
|
437
438
|
if (!fs.existsSync(path.join(cwd, '.planning'))) {
|
|
438
439
|
const worktreeRoot = resolveWorktreeRoot(cwd);
|
|
439
440
|
if (worktreeRoot !== cwd) {
|
|
@@ -596,7 +597,7 @@ async function main() {
|
|
|
596
597
|
}
|
|
597
598
|
|
|
598
599
|
// Intercept stdout to transparently resolve @file: references (#1891).
|
|
599
|
-
//
|
|
600
|
+
// io.cjs output() writes @file:<path> when JSON > 50KB. The --pick path
|
|
600
601
|
// already resolves this, but the normal path wrote @file: to stdout, forcing
|
|
601
602
|
// every workflow to have a bash-specific `if [[ "$INIT" == @file:* ]]` check
|
|
602
603
|
// that breaks on PowerShell and other non-bash shells.
|
|
@@ -802,7 +803,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
802
803
|
cwd,
|
|
803
804
|
raw,
|
|
804
805
|
error,
|
|
805
|
-
output:
|
|
806
|
+
output: output,
|
|
806
807
|
});
|
|
807
808
|
if (!handled) phase.cmdFindPhase(cwd, args[1], raw);
|
|
808
809
|
break;
|
|
@@ -895,7 +896,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
895
896
|
cwd,
|
|
896
897
|
raw,
|
|
897
898
|
error,
|
|
898
|
-
output:
|
|
899
|
+
output: output,
|
|
899
900
|
});
|
|
900
901
|
if (handled) break;
|
|
901
902
|
}
|
|
@@ -957,7 +958,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
957
958
|
cwd,
|
|
958
959
|
raw,
|
|
959
960
|
error,
|
|
960
|
-
output:
|
|
961
|
+
output: output,
|
|
961
962
|
});
|
|
962
963
|
if (!handled) commands.cmdGenerateSlug(args[1], raw);
|
|
963
964
|
break;
|
|
@@ -997,7 +998,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
997
998
|
cwd,
|
|
998
999
|
raw,
|
|
999
1000
|
error,
|
|
1000
|
-
output:
|
|
1001
|
+
output: output,
|
|
1001
1002
|
});
|
|
1002
1003
|
if (!handled) config.cmdConfigEnsureSection(cwd, raw);
|
|
1003
1004
|
break;
|
|
@@ -1013,7 +1014,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1013
1014
|
cwd,
|
|
1014
1015
|
raw,
|
|
1015
1016
|
error,
|
|
1016
|
-
output:
|
|
1017
|
+
output: output,
|
|
1017
1018
|
});
|
|
1018
1019
|
if (!handled) config.cmdConfigSet(cwd, args[1], args[2], raw);
|
|
1019
1020
|
break;
|
|
@@ -1029,7 +1030,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1029
1030
|
cwd,
|
|
1030
1031
|
raw,
|
|
1031
1032
|
error,
|
|
1032
|
-
output:
|
|
1033
|
+
output: output,
|
|
1033
1034
|
});
|
|
1034
1035
|
if (!handled) config.cmdConfigSetModelProfile(cwd, args[1], raw);
|
|
1035
1036
|
break;
|
|
@@ -1054,7 +1055,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1054
1055
|
cwd,
|
|
1055
1056
|
raw,
|
|
1056
1057
|
error,
|
|
1057
|
-
output:
|
|
1058
|
+
output: output,
|
|
1058
1059
|
});
|
|
1059
1060
|
if (!handled) config.cmdConfigGet(cwd, args[1], raw, defaultValue);
|
|
1060
1061
|
break;
|
|
@@ -1070,7 +1071,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1070
1071
|
cwd,
|
|
1071
1072
|
raw,
|
|
1072
1073
|
error,
|
|
1073
|
-
output:
|
|
1074
|
+
output: output,
|
|
1074
1075
|
});
|
|
1075
1076
|
if (!handled) config.cmdConfigNewProject(cwd, args[1], raw);
|
|
1076
1077
|
break;
|
|
@@ -1183,7 +1184,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1183
1184
|
args,
|
|
1184
1185
|
cwd,
|
|
1185
1186
|
raw,
|
|
1186
|
-
output:
|
|
1187
|
+
output: output,
|
|
1187
1188
|
error,
|
|
1188
1189
|
});
|
|
1189
1190
|
break;
|
|
@@ -1255,12 +1256,12 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1255
1256
|
const configDirIdx = args.indexOf('--config-dir');
|
|
1256
1257
|
if (configDirEqArg) {
|
|
1257
1258
|
const value = configDirEqArg.slice('--config-dir='.length).trim();
|
|
1258
|
-
if (!value) error('Missing value for --config-dir',
|
|
1259
|
+
if (!value) error('Missing value for --config-dir', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1259
1260
|
loopConfigDir = value;
|
|
1260
1261
|
} else if (configDirIdx !== -1) {
|
|
1261
1262
|
const value = args[configDirIdx + 1];
|
|
1262
1263
|
if (!value || value.startsWith('--')) {
|
|
1263
|
-
error('Missing value for --config-dir',
|
|
1264
|
+
error('Missing value for --config-dir', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1264
1265
|
}
|
|
1265
1266
|
loopConfigDir = value;
|
|
1266
1267
|
}
|
|
@@ -1270,12 +1271,12 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1270
1271
|
const activeCapIdx = args.indexOf('--active-cap');
|
|
1271
1272
|
if (activeCapEqArg) {
|
|
1272
1273
|
const value = activeCapEqArg.slice('--active-cap='.length).trim();
|
|
1273
|
-
if (!value) error('Missing value for --active-cap (e.g. --active-cap tdd)',
|
|
1274
|
+
if (!value) error('Missing value for --active-cap (e.g. --active-cap tdd)', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1274
1275
|
loopActiveCap = value;
|
|
1275
1276
|
} else if (activeCapIdx !== -1) {
|
|
1276
1277
|
const value = args[activeCapIdx + 1];
|
|
1277
1278
|
if (!value || value.startsWith('--')) {
|
|
1278
|
-
error('Missing value for --active-cap (e.g. --active-cap tdd)',
|
|
1279
|
+
error('Missing value for --active-cap (e.g. --active-cap tdd)', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1279
1280
|
}
|
|
1280
1281
|
loopActiveCap = value;
|
|
1281
1282
|
}
|
|
@@ -1286,7 +1287,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1286
1287
|
} else {
|
|
1287
1288
|
error(
|
|
1288
1289
|
`Unknown loop subcommand: ${loopSubcommand}. Available: render-hooks`,
|
|
1289
|
-
|
|
1290
|
+
ERROR_REASON ? ERROR_REASON.SDK_UNKNOWN_COMMAND : undefined,
|
|
1290
1291
|
);
|
|
1291
1292
|
}
|
|
1292
1293
|
break;
|
|
@@ -1307,7 +1308,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1307
1308
|
const configDirVal = args[configDirIdx + 1];
|
|
1308
1309
|
// Validate that --config-dir has a following non-flag value.
|
|
1309
1310
|
if (!configDirVal || configDirVal.startsWith('--')) {
|
|
1310
|
-
error('Missing value for --config-dir',
|
|
1311
|
+
error('Missing value for --config-dir', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1311
1312
|
}
|
|
1312
1313
|
configDir = configDirVal;
|
|
1313
1314
|
}
|
|
@@ -1317,7 +1318,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1317
1318
|
// capability set <id> [--on|--off|--enable|--disable] [--gate <key>=<bool>]... [--config-dir <dir>] [--runtime <r>] [--scope <s>]
|
|
1318
1319
|
const capId = args[2];
|
|
1319
1320
|
if (!capId || capId.startsWith('--')) {
|
|
1320
|
-
error('Missing capability id for: capability set <id>',
|
|
1321
|
+
error('Missing capability id for: capability set <id>', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1321
1322
|
}
|
|
1322
1323
|
// Parse --config-dir
|
|
1323
1324
|
const setConfigDirIdx = args.indexOf('--config-dir');
|
|
@@ -1325,7 +1326,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1325
1326
|
if (setConfigDirIdx !== -1) {
|
|
1326
1327
|
const setConfigDirVal = args[setConfigDirIdx + 1];
|
|
1327
1328
|
if (!setConfigDirVal || setConfigDirVal.startsWith('--')) {
|
|
1328
|
-
error('Missing value for --config-dir',
|
|
1329
|
+
error('Missing value for --config-dir', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1329
1330
|
}
|
|
1330
1331
|
setConfigDir = setConfigDirVal;
|
|
1331
1332
|
}
|
|
@@ -1334,7 +1335,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1334
1335
|
const hasOn = args.includes('--on') || args.includes('--enable');
|
|
1335
1336
|
const hasOff = args.includes('--off') || args.includes('--disable');
|
|
1336
1337
|
if (hasOn && hasOff) {
|
|
1337
|
-
error('Conflicting flags: --on/--enable and --off/--disable cannot both be present',
|
|
1338
|
+
error('Conflicting flags: --on/--enable and --off/--disable cannot both be present', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1338
1339
|
}
|
|
1339
1340
|
let setEnabled;
|
|
1340
1341
|
if (hasOn) {
|
|
@@ -1348,16 +1349,16 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1348
1349
|
if (args[gi] === '--gate') {
|
|
1349
1350
|
const gateVal = args[gi + 1];
|
|
1350
1351
|
if (!gateVal || gateVal.startsWith('--')) {
|
|
1351
|
-
error('Missing value for --gate (expected <key>=<true|false>)',
|
|
1352
|
+
error('Missing value for --gate (expected <key>=<true|false>)', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1352
1353
|
}
|
|
1353
1354
|
const eqIdx = gateVal.indexOf('=');
|
|
1354
1355
|
if (eqIdx === -1) {
|
|
1355
|
-
error(`Malformed --gate value "${gateVal}": expected <key>=<true|false>`,
|
|
1356
|
+
error(`Malformed --gate value "${gateVal}": expected <key>=<true|false>`, ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1356
1357
|
}
|
|
1357
1358
|
const gateKey = gateVal.slice(0, eqIdx);
|
|
1358
1359
|
const gateBoolStr = gateVal.slice(eqIdx + 1);
|
|
1359
1360
|
if (gateBoolStr !== 'true' && gateBoolStr !== 'false') {
|
|
1360
|
-
error(`Malformed --gate value "${gateVal}": bool must be true or false`,
|
|
1361
|
+
error(`Malformed --gate value "${gateVal}": bool must be true or false`, ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1361
1362
|
}
|
|
1362
1363
|
setGates[gateKey] = gateBoolStr === 'true';
|
|
1363
1364
|
gi++; // skip consumed value
|
|
@@ -1369,7 +1370,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1369
1370
|
if (runtimeIdx !== -1) {
|
|
1370
1371
|
const runtimeVal = args[runtimeIdx + 1];
|
|
1371
1372
|
if (!runtimeVal || runtimeVal.startsWith('--')) {
|
|
1372
|
-
error('Missing value for --runtime',
|
|
1373
|
+
error('Missing value for --runtime', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1373
1374
|
}
|
|
1374
1375
|
setRuntime = runtimeVal;
|
|
1375
1376
|
}
|
|
@@ -1378,7 +1379,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1378
1379
|
if (scopeIdx !== -1) {
|
|
1379
1380
|
const scopeVal = args[scopeIdx + 1];
|
|
1380
1381
|
if (!scopeVal || scopeVal.startsWith('--')) {
|
|
1381
|
-
error('Missing value for --scope',
|
|
1382
|
+
error('Missing value for --scope', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
1382
1383
|
}
|
|
1383
1384
|
setScope = scopeVal;
|
|
1384
1385
|
}
|
|
@@ -1392,7 +1393,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1392
1393
|
} else {
|
|
1393
1394
|
error(
|
|
1394
1395
|
`Unknown capability subcommand: ${capSubcommand}. Available: state, set`,
|
|
1395
|
-
|
|
1396
|
+
ERROR_REASON ? ERROR_REASON.SDK_UNKNOWN_COMMAND : undefined,
|
|
1396
1397
|
);
|
|
1397
1398
|
}
|
|
1398
1399
|
break;
|
|
@@ -1484,7 +1485,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1484
1485
|
cwd,
|
|
1485
1486
|
raw,
|
|
1486
1487
|
error,
|
|
1487
|
-
output:
|
|
1488
|
+
output: output,
|
|
1488
1489
|
});
|
|
1489
1490
|
if (!handled) docs.cmdDocsInit(cwd, raw);
|
|
1490
1491
|
break;
|
|
@@ -1518,6 +1519,17 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1518
1519
|
break;
|
|
1519
1520
|
}
|
|
1520
1521
|
|
|
1522
|
+
// ─── teams-status ──────────────────────────────────────────────────────
|
|
1523
|
+
// Read-only detector for claude-code's experimental agent-teams feature.
|
|
1524
|
+
// issue #1355: stop gsd-core hanging silently under claude-code agent-teams.
|
|
1525
|
+
// No capability registration needed — this is a diagnostic query command,
|
|
1526
|
+
// not a feature capability.
|
|
1527
|
+
case 'teams-status': {
|
|
1528
|
+
const teamsStatus = require('./lib/teams-status.cjs');
|
|
1529
|
+
teamsStatus.cmdTeamsStatus(cwd, { active: args.includes('--active') });
|
|
1530
|
+
break;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1521
1533
|
// ─── detect-custom-files ───────────────────────────────────────────────
|
|
1522
1534
|
// CJS-native: no SDK counterpart exists in the command registry.
|
|
1523
1535
|
// detect-custom-files reads a gsd-file-manifest.json against the
|
|
@@ -1565,18 +1577,30 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1565
1577
|
|
|
1566
1578
|
const manifestKeys = new Set(Object.keys(manifest.files || {}));
|
|
1567
1579
|
|
|
1568
|
-
// GSD-managed directories to scan for user-added files.
|
|
1569
|
-
//
|
|
1570
|
-
|
|
1580
|
+
// GSD-managed directories to scan for user-added files. Whole-owned
|
|
1581
|
+
// roots are wiped recursively; shared runtime roots are pruned by the
|
|
1582
|
+
// same gsd-* top-level prefix used by install.js _removeGsdEntries.
|
|
1583
|
+
const GSD_WHOLE_MANAGED_DIRS = [
|
|
1571
1584
|
'gsd-core',
|
|
1572
|
-
'agents',
|
|
1573
1585
|
path.join('commands', 'gsd'),
|
|
1586
|
+
];
|
|
1587
|
+
const GSD_PREFIX_MANAGED_DIRS = [
|
|
1588
|
+
'agents',
|
|
1574
1589
|
'hooks',
|
|
1575
1590
|
'skills',
|
|
1576
1591
|
];
|
|
1577
1592
|
|
|
1578
1593
|
function collectCustomFiles(dir, baseDir, manifestKeys, out) {
|
|
1579
1594
|
if (!fs.existsSync(dir)) return;
|
|
1595
|
+
const stat = fs.statSync(dir);
|
|
1596
|
+
if (stat.isFile()) {
|
|
1597
|
+
const relPath = path.relative(baseDir, dir).replace(/\\/g, '/');
|
|
1598
|
+
if (!manifestKeys.has(relPath)) {
|
|
1599
|
+
out.push(relPath);
|
|
1600
|
+
}
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (!stat.isDirectory()) return;
|
|
1580
1604
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
1581
1605
|
const fullPath = path.join(dir, entry.name);
|
|
1582
1606
|
if (entry.isDirectory()) {
|
|
@@ -1592,11 +1616,19 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1592
1616
|
}
|
|
1593
1617
|
|
|
1594
1618
|
const customFiles = [];
|
|
1595
|
-
for (const managedDir of
|
|
1619
|
+
for (const managedDir of GSD_WHOLE_MANAGED_DIRS) {
|
|
1596
1620
|
const absDir = path.join(resolvedConfigDir, managedDir);
|
|
1597
1621
|
if (!fs.existsSync(absDir)) continue;
|
|
1598
1622
|
collectCustomFiles(absDir, resolvedConfigDir, manifestKeys, customFiles);
|
|
1599
1623
|
}
|
|
1624
|
+
for (const managedDir of GSD_PREFIX_MANAGED_DIRS) {
|
|
1625
|
+
const absDir = path.join(resolvedConfigDir, managedDir);
|
|
1626
|
+
if (!fs.existsSync(absDir)) continue;
|
|
1627
|
+
for (const entry of fs.readdirSync(absDir, { withFileTypes: true })) {
|
|
1628
|
+
if (!entry.name.startsWith('gsd-')) continue;
|
|
1629
|
+
collectCustomFiles(path.join(absDir, entry.name), resolvedConfigDir, manifestKeys, customFiles);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1600
1632
|
|
|
1601
1633
|
const out = {
|
|
1602
1634
|
custom_files: customFiles,
|
|
@@ -1810,7 +1842,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1810
1842
|
// ─── Research Store ────────────────────────────────────────────────────
|
|
1811
1843
|
//
|
|
1812
1844
|
// research-store get <key> [--kind <k>]
|
|
1813
|
-
// -> getResearch(cwd, key, { homeDir }); searches both tiers;
|
|
1845
|
+
// -> getResearch(cwd, key, { homeDir }); searches both tiers; output(result, raw)
|
|
1814
1846
|
// (--kind is accepted for backward compatibility but no longer drives tier selection)
|
|
1815
1847
|
// research-store put <key> --content <str> --source <s> --provider <p>
|
|
1816
1848
|
// --confidence <c> --kind <k>
|
|
@@ -1834,7 +1866,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1834
1866
|
}
|
|
1835
1867
|
// --kind is accepted but no longer drives tier selection; getResearch searches both tiers
|
|
1836
1868
|
const result = researchStore.getResearch(cwd, key, { homeDir });
|
|
1837
|
-
|
|
1869
|
+
output(result, raw);
|
|
1838
1870
|
} else if (subcommand === 'put') {
|
|
1839
1871
|
const key = args[2];
|
|
1840
1872
|
if (!key || key.startsWith('--')) {
|
|
@@ -1866,7 +1898,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1866
1898
|
error('Usage: gsd-tools research-store put <key> --content <str> --source <s> --provider <p> --confidence <c> --kind <k>', ERROR_REASON.USAGE);
|
|
1867
1899
|
}
|
|
1868
1900
|
const entry = researchStore.putResearch(cwd, key, { content, source, provider, confidence, kind }, { homeDir });
|
|
1869
|
-
|
|
1901
|
+
output(entry, raw);
|
|
1870
1902
|
} else {
|
|
1871
1903
|
error('Unknown research-store subcommand. Available: get, put', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
1872
1904
|
}
|
|
@@ -1902,14 +1934,14 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1902
1934
|
const { ecosystem = '', config: planConfig = {}, questions } = planInput;
|
|
1903
1935
|
const homeDir = process.env.HOME || require('os').homedir();
|
|
1904
1936
|
const plan = researchProvider.planResearch({ questions, ecosystem, config: planConfig, cwd, homeDir });
|
|
1905
|
-
|
|
1937
|
+
output(plan, raw);
|
|
1906
1938
|
break;
|
|
1907
1939
|
}
|
|
1908
1940
|
|
|
1909
1941
|
// ─── Classify Confidence ──────────────────────────────────────────────
|
|
1910
1942
|
//
|
|
1911
1943
|
// classify-confidence --provider <id> [--package <name> --ecosystem <npm|pypi|crates>] [--verified]
|
|
1912
|
-
// -> classifyConfidence({ provider, verifiedAgainstOfficial, legitimacyVerdict });
|
|
1944
|
+
// -> classifyConfidence({ provider, verifiedAgainstOfficial, legitimacyVerdict }); output(result, raw)
|
|
1913
1945
|
//
|
|
1914
1946
|
// legitimacyVerdict is CODE-COMPUTED via checkPackages — never caller-supplied — so an agent cannot self-assert OK→HIGH.
|
|
1915
1947
|
|
|
@@ -1936,7 +1968,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1936
1968
|
legitimacyVerdict = results[0] ? results[0].verdict : null;
|
|
1937
1969
|
}
|
|
1938
1970
|
const confidence = researchProvider.classifyConfidence({ provider, verifiedAgainstOfficial: verified, legitimacyVerdict });
|
|
1939
|
-
|
|
1971
|
+
output({ provider, package: pkg || null, ecosystem: ecosystem || null, legitimacyVerdict, verified, confidence }, raw);
|
|
1940
1972
|
break;
|
|
1941
1973
|
}
|
|
1942
1974
|
|
|
@@ -1980,7 +2012,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1980
2012
|
} catch (pkgErr) {
|
|
1981
2013
|
error(`package-legitimacy: ${pkgErr && pkgErr.message ? pkgErr.message : String(pkgErr)}`, ERROR_REASON.UNKNOWN);
|
|
1982
2014
|
}
|
|
1983
|
-
|
|
2015
|
+
output(pkgResults, raw);
|
|
1984
2016
|
break;
|
|
1985
2017
|
}
|
|
1986
2018
|
|
|
@@ -2101,7 +2133,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
2101
2133
|
}
|
|
2102
2134
|
}
|
|
2103
2135
|
|
|
2104
|
-
|
|
2136
|
+
output({ valid: errors.length === 0, errors, slots }, raw);
|
|
2105
2137
|
break;
|
|
2106
2138
|
}
|
|
2107
2139
|
|
|
@@ -2114,7 +2146,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
2114
2146
|
|
|
2115
2147
|
// Read config.json directly for both plan_review.source_grounding_authority
|
|
2116
2148
|
// and intel.enabled. Neither key is in the config-loader.cjs whitelist that
|
|
2117
|
-
//
|
|
2149
|
+
// config-loader.cjs's loadConfig() whitelist does not return; plan_review is only in config.cjs's private
|
|
2118
2150
|
// buildConfig(), and intel is a federated capability config key.
|
|
2119
2151
|
let configuredAuthority = 'grep';
|
|
2120
2152
|
let intelEnabled = false;
|
|
@@ -2138,7 +2170,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
2138
2170
|
|
|
2139
2171
|
if (subcommand === 'authority') {
|
|
2140
2172
|
// Pass rawValue as 3rd arg so --raw returns unquoted string (not JSON)
|
|
2141
|
-
|
|
2173
|
+
output(effectiveAuthority, raw, effectiveAuthority);
|
|
2142
2174
|
break;
|
|
2143
2175
|
}
|
|
2144
2176
|
|
|
@@ -2155,7 +2187,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
2155
2187
|
? authVal
|
|
2156
2188
|
: effectiveAuthority;
|
|
2157
2189
|
const result = classifyDriftSeverity({ status: statusVal, authority: authorityForClassify });
|
|
2158
|
-
|
|
2190
|
+
output(result, raw);
|
|
2159
2191
|
break;
|
|
2160
2192
|
}
|
|
2161
2193
|
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* from the prior hand-written .cjs; only types are added.
|
|
8
8
|
*/
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
10
|
-
const
|
|
11
|
-
const { output, error, ERROR_REASON } =
|
|
10
|
+
const io = require("./io.cjs");
|
|
11
|
+
const { output, error, ERROR_REASON } = io;
|
|
12
12
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
13
13
|
const QUOTA_SENTINELS = [
|
|
14
14
|
'429',
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Agent Install Check — moved from core.cts (ADR-857 T0 #1268 phase rehome-core-squatters).
|
|
4
|
+
*
|
|
5
|
+
* Owns:
|
|
6
|
+
* - getAgentsDir(runtime?): string
|
|
7
|
+
* - checkAgentsInstalled(runtime?): AgentsInstalledResult
|
|
8
|
+
*
|
|
9
|
+
* The core.cjs re-export spine was retired in epic #1267; callers import
|
|
10
|
+
* these symbols from agent-install-check.cjs directly.
|
|
11
|
+
*/
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
16
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
18
|
+
const modelProfiles = require("./model-profiles.cjs");
|
|
19
|
+
const { MODEL_PROFILES } = modelProfiles;
|
|
20
|
+
const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the agents directory for the given runtime.
|
|
23
|
+
*
|
|
24
|
+
* Priority:
|
|
25
|
+
* 1. GSD_AGENTS_DIR env var (explicit override, any runtime)
|
|
26
|
+
* 2. For claude runtime: __dirname-relative path (agents/ sibling of gsd-core/)
|
|
27
|
+
* This is correct for both repo runs and real installs (the runtime config dir's
|
|
28
|
+
* agents/ folder) because gsd-tools.cjs lives inside gsd-core/bin/ in both cases.
|
|
29
|
+
* 3. For non-claude runtimes: getGlobalConfigDir(runtime)/agents
|
|
30
|
+
*
|
|
31
|
+
* @param runtime - the active runtime name; defaults to GSD_RUNTIME env, then 'claude'
|
|
32
|
+
*/
|
|
33
|
+
function getAgentsDir(runtime) {
|
|
34
|
+
if (process.env['GSD_AGENTS_DIR']) {
|
|
35
|
+
return process.env['GSD_AGENTS_DIR'];
|
|
36
|
+
}
|
|
37
|
+
const resolved = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
|
|
38
|
+
if (resolved === 'claude') {
|
|
39
|
+
return node_path_1.default.join(__dirname, '..', '..', '..', 'agents');
|
|
40
|
+
}
|
|
41
|
+
return node_path_1.default.join((0, runtime_homes_cjs_1.getGlobalConfigDir)(resolved), 'agents');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check which GSD agents are installed on disk.
|
|
45
|
+
*
|
|
46
|
+
* @param runtime - the active runtime name; defaults to GSD_RUNTIME env, then 'claude'
|
|
47
|
+
*/
|
|
48
|
+
function checkAgentsInstalled(runtime) {
|
|
49
|
+
const resolvedRuntime = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
|
|
50
|
+
const agentsDir = getAgentsDir(resolvedRuntime);
|
|
51
|
+
const expectedAgents = Object.keys(MODEL_PROFILES);
|
|
52
|
+
const installed = [];
|
|
53
|
+
const missing = [];
|
|
54
|
+
if (!node_fs_1.default.existsSync(agentsDir)) {
|
|
55
|
+
return {
|
|
56
|
+
agents_installed: false,
|
|
57
|
+
missing_agents: expectedAgents,
|
|
58
|
+
installed_agents: [],
|
|
59
|
+
incomplete_agents: [],
|
|
60
|
+
agents_dir: agentsDir,
|
|
61
|
+
agent_runtime: resolvedRuntime,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
for (const agent of expectedAgents) {
|
|
65
|
+
const agentFile = node_path_1.default.join(agentsDir, `${agent}.md`);
|
|
66
|
+
const agentFileCopilot = node_path_1.default.join(agentsDir, `${agent}.agent.md`);
|
|
67
|
+
const agentFileCodex = node_path_1.default.join(agentsDir, `${agent}.toml`);
|
|
68
|
+
const agentFileKimiYaml = node_path_1.default.join(agentsDir, 'subagents', `${agent}.yaml`);
|
|
69
|
+
const agentFileKimiPrompt = node_path_1.default.join(agentsDir, 'subagents', `${agent}.md`);
|
|
70
|
+
const kimiAgentInstalled = resolvedRuntime === 'kimi' &&
|
|
71
|
+
node_fs_1.default.existsSync(agentFileKimiYaml) &&
|
|
72
|
+
node_fs_1.default.existsSync(agentFileKimiPrompt);
|
|
73
|
+
if (node_fs_1.default.existsSync(agentFile) ||
|
|
74
|
+
node_fs_1.default.existsSync(agentFileCopilot) ||
|
|
75
|
+
node_fs_1.default.existsSync(agentFileCodex) ||
|
|
76
|
+
kimiAgentInstalled) {
|
|
77
|
+
installed.push(agent);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
missing.push(agent);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// ── Manifest-backed completeness check ──────────────────────────────────────
|
|
84
|
+
// If a gsd-file-manifest.json exists alongside the agents dir (parent dir),
|
|
85
|
+
// verify that every manifest-tracked file for each expected agent is present
|
|
86
|
+
// on disk. Missing manifest-tracked files indicate an incomplete install even
|
|
87
|
+
// when the plain presence check above passed (e.g. .md present, .toml absent).
|
|
88
|
+
// If no manifest is found the check is a no-op (graceful for claude/bundled).
|
|
89
|
+
const incomplete = [];
|
|
90
|
+
const manifestPath = node_path_1.default.join(node_path_1.default.dirname(agentsDir), 'gsd-file-manifest.json');
|
|
91
|
+
let manifestFiles = {};
|
|
92
|
+
try {
|
|
93
|
+
const raw = node_fs_1.default.readFileSync(manifestPath, 'utf8');
|
|
94
|
+
const parsed = JSON.parse(raw);
|
|
95
|
+
if (parsed !== null &&
|
|
96
|
+
typeof parsed === 'object' &&
|
|
97
|
+
'files' in parsed &&
|
|
98
|
+
typeof parsed['files'] === 'object' &&
|
|
99
|
+
parsed['files'] !== null) {
|
|
100
|
+
manifestFiles = parsed['files'];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// No manifest or unreadable — completeness check is skipped
|
|
105
|
+
}
|
|
106
|
+
if (Object.keys(manifestFiles).length > 0) {
|
|
107
|
+
for (const agent of expectedAgents) {
|
|
108
|
+
// Find all manifest keys that belong to this agent:
|
|
109
|
+
// key must be "agents/<agentName>.<ext>" with no further path segments.
|
|
110
|
+
const agentPrefix = `agents/${agent}.`;
|
|
111
|
+
const agentManifestKeys = Object.keys(manifestFiles).filter(key => {
|
|
112
|
+
if (!key.startsWith(agentPrefix))
|
|
113
|
+
return false;
|
|
114
|
+
const rest = key.slice(agentPrefix.length);
|
|
115
|
+
// rest must be a bare extension (no slashes, non-empty)
|
|
116
|
+
return rest.length > 0 && !rest.includes('/');
|
|
117
|
+
});
|
|
118
|
+
if (agentManifestKeys.length === 0) {
|
|
119
|
+
// Agent not tracked in manifest — skip completeness check for this agent
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const allPresent = agentManifestKeys.every(key => {
|
|
123
|
+
const basename = key.slice('agents/'.length);
|
|
124
|
+
return node_fs_1.default.existsSync(node_path_1.default.join(agentsDir, basename));
|
|
125
|
+
});
|
|
126
|
+
if (!allPresent) {
|
|
127
|
+
incomplete.push(agent);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
agents_installed: installed.length > 0 && missing.length === 0 && incomplete.length === 0,
|
|
133
|
+
missing_agents: missing,
|
|
134
|
+
installed_agents: installed,
|
|
135
|
+
incomplete_agents: incomplete,
|
|
136
|
+
agents_dir: agentsDir,
|
|
137
|
+
agent_runtime: resolvedRuntime,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
module.exports = {
|
|
141
|
+
getAgentsDir,
|
|
142
|
+
checkAgentsInstalled,
|
|
143
|
+
};
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* inline case arms which each required only their own module).
|
|
25
25
|
*/
|
|
26
26
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
27
|
-
const
|
|
27
|
+
const io = require("./io.cjs");
|
|
28
28
|
// ─── routeAuditUat ────────────────────────────────────────────────────────────
|
|
29
29
|
function routeAuditUat({ args, cwd, raw, error, _uat }) {
|
|
30
30
|
// Suppress unused-variable warnings for args/error — this command has no
|
|
@@ -42,16 +42,16 @@ function routeAuditOpen({ args, cwd, raw, error, _audit, _core }) {
|
|
|
42
42
|
void error;
|
|
43
43
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
44
44
|
const a = _audit ?? require('./audit.cjs');
|
|
45
|
-
const c = _core ??
|
|
45
|
+
const c = _core ?? io;
|
|
46
46
|
const wantJson = args.includes('--json');
|
|
47
47
|
const result = a.auditOpenArtifacts(cwd);
|
|
48
48
|
if (wantJson) {
|
|
49
|
-
//
|
|
49
|
+
// io.output JSON-stringifies its first arg; pass the object directly.
|
|
50
50
|
c.output(result, raw);
|
|
51
51
|
}
|
|
52
52
|
else {
|
|
53
53
|
// Human-readable report must bypass JSON encoding — use the rawValue
|
|
54
|
-
// form (third arg) which
|
|
54
|
+
// form (third arg) which io.output emits verbatim.
|
|
55
55
|
c.output(null, true, a.formatAuditReport(result));
|
|
56
56
|
}
|
|
57
57
|
}
|