@opengsd/gsd-core 1.7.0-rc.2 → 1.7.0-rc.4
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/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/GEMINI.md +25 -23
- package/README.ja-JP.md +4 -3
- package/README.ko-KR.md +4 -3
- package/README.md +7 -6
- package/README.pt-BR.md +4 -3
- package/README.zh-CN.md +4 -3
- package/agents/gsd-advisor-researcher.md +1 -1
- package/agents/gsd-ai-researcher.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-domain-researcher.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +4 -4
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +1 -1
- package/agents/gsd-phase-researcher.md +2 -2
- package/agents/gsd-plan-checker.md +1 -1
- package/agents/gsd-planner.md +2 -2
- package/agents/gsd-project-researcher.md +2 -2
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-researcher.md +2 -2
- package/agents/gsd-verifier.md +2 -2
- package/bin/install.js +132 -464
- package/commands/gsd/map-codebase.md +4 -4
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +9 -5
- package/commands/gsd/next.md +29 -0
- package/commands/gsd/ns-project.md +2 -1
- package/commands/gsd/ns-workflow.md +5 -2
- package/commands/gsd/onboard.md +46 -0
- package/gsd-core/bin/ensure-runtime-build.cjs +246 -0
- package/gsd-core/bin/gsd-tools.cjs +72 -3
- package/gsd-core/bin/lib/capability-registry.cjs +684 -264
- package/gsd-core/bin/lib/capability-state.cjs +117 -18
- package/gsd-core/bin/lib/capability-writer.cjs +11 -1
- package/gsd-core/bin/lib/check-command-router.cjs +111 -1
- package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +136 -0
- package/gsd-core/bin/lib/claude-orchestration.cjs +404 -0
- package/gsd-core/bin/lib/clusters.cjs +2 -0
- package/gsd-core/bin/lib/command-aliases.cjs +8 -0
- package/gsd-core/bin/lib/config.cjs +19 -1
- package/gsd-core/bin/lib/core-utils.cjs +41 -0
- package/gsd-core/bin/lib/external-job.cjs +287 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +151 -0
- package/gsd-core/bin/lib/graphify.cjs +41 -9
- package/gsd-core/bin/lib/init-command-router.cjs +4 -0
- package/gsd-core/bin/lib/init.cjs +25 -69
- package/gsd-core/bin/lib/install-engine.cjs +8 -5
- package/gsd-core/bin/lib/install-profiles.cjs +2 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +7 -1
- package/gsd-core/bin/lib/milestone.cjs +7 -1
- package/gsd-core/bin/lib/model-resolver.cjs +69 -4
- package/gsd-core/bin/lib/onboard-projection.cjs +309 -0
- package/gsd-core/bin/lib/phase.cjs +14 -2
- package/gsd-core/bin/lib/plan-scan.cjs +9 -1
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +21 -19
- package/gsd-core/bin/lib/roadmap.cjs +17 -2
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +18 -13
- package/gsd-core/bin/lib/runtime-name-policy.cjs +11 -11
- package/gsd-core/bin/lib/shell-command-projection.cjs +12 -8
- package/gsd-core/bin/lib/smart-entry.cjs +521 -0
- package/gsd-core/bin/lib/surface.cjs +63 -33
- package/gsd-core/bin/lib/update-context.cjs +0 -5
- package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
- package/gsd-core/bin/shared/model-catalog.json +5 -5
- package/gsd-core/bin/shared/runtime-aliases.manifest.json +0 -5
- package/gsd-core/references/gates.md +1 -1
- package/gsd-core/references/gsd-run-resolver.md +8 -0
- package/gsd-core/references/planning-config.md +1 -1
- package/gsd-core/templates/project.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +1 -1
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +1 -1
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous.md +1 -1
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review-fix.md +1 -1
- package/gsd-core/workflows/code-review.md +1 -1
- package/gsd-core/workflows/complete-milestone.md +1 -1
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/auto.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +5 -4
- package/gsd-core/workflows/docs-update.md +1 -1
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
- package/gsd-core/workflows/execute-phase.md +4 -5
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/explore.md +1 -1
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- package/gsd-core/workflows/help/modes/brief.md +2 -1
- package/gsd-core/workflows/help/modes/default.md +2 -1
- package/gsd-core/workflows/help/modes/full.md +25 -1
- package/gsd-core/workflows/help/modes/topic.md +2 -1
- package/gsd-core/workflows/import.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +1 -1
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +1 -1
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +1 -1
- package/gsd-core/workflows/new-project.md +1 -1
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/onboard.md +277 -0
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-milestone-gaps.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase.md +9 -2
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +1 -1
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress.md +1 -1
- package/gsd-core/workflows/quick.md +21 -1
- package/gsd-core/workflows/reapply-patches.md +1 -1
- package/gsd-core/workflows/remove-phase.md +1 -1
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +1 -1
- package/gsd-core/workflows/review.md +43 -7
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/settings-advanced.md +3 -1
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +1 -1
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +120 -0
- package/gsd-core/workflows/spec-phase.md +8 -6
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/thread.md +1 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +1 -1
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +1 -1
- package/gsd-core/workflows/update.md +3 -6
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-phase.md +1 -1
- package/gsd-core/workflows/verify-work.md +62 -4
- package/package.json +3 -5
- package/scripts/ci-test-scope.cjs +19 -1
- package/scripts/gen-golden-install-parity-zcode.cjs +77 -0
- package/scripts/generate-package-identity.cjs +27 -2
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -148
- package/scripts/lint-regression-test-names.allowlist.json +1 -273
- package/scripts/lint-test-file-count.allowlist.json +7 -91
- package/scripts/research-profiles.cjs +6 -6
- package/scripts/run-tests.cjs +18 -3
- package/scripts/slurm-adapter.cjs +269 -0
- package/scripts/sync-manifest-versions.cjs +3 -2
- package/scripts/sync-runtime-launcher.cjs +20 -2
- package/skills/gsd-map-codebase/SKILL.md +3 -3
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +9 -5
- package/skills/gsd-next/SKILL.md +30 -0
- package/skills/gsd-ns-project/SKILL.md +1 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -1
- package/skills/gsd-onboard/SKILL.md +46 -0
- package/gemini-extension.json +0 -6
|
@@ -45,7 +45,7 @@ const configLoaderMod = require("./config-loader.cjs");
|
|
|
45
45
|
const { loadConfig } = configLoaderMod;
|
|
46
46
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
47
47
|
const installProfilesMod = require("./install-profiles.cjs");
|
|
48
|
-
const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires } = installProfilesMod;
|
|
48
|
+
const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires, parseCallsAgents } = installProfilesMod;
|
|
49
49
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
50
|
const surfaceMod = require("./surface.cjs");
|
|
51
51
|
const { resolveSurface } = surfaceMod;
|
|
@@ -282,22 +282,90 @@ function _loadInstalledSkillsManifest(configDir) {
|
|
|
282
282
|
}
|
|
283
283
|
return manifest;
|
|
284
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* #1858 — Build a skill dependency manifest from a FLAT commands/gsd-<stem>.md
|
|
287
|
+
* source layout (the Claude local project install shape, where the `gsd-`
|
|
288
|
+
* prefix is baked into each filename at the commands/ level and there is no
|
|
289
|
+
* commands/gsd/ subdir). Strips the `gsd-` prefix so stems match the nested
|
|
290
|
+
* loader's output (gsd-validate-phase.md → validate-phase, same as nested
|
|
291
|
+
* validate-phase.md).
|
|
292
|
+
*
|
|
293
|
+
* Map shape is identical to loadSkillsManifest: each stem maps to its
|
|
294
|
+
* `requires` deps (parsed via the same shared parseRequires) and carries a
|
|
295
|
+
* companion `_calls_agents_<stem>` key (parsed via parseCallsAgents) so the
|
|
296
|
+
* flat and nested paths cannot drift.
|
|
297
|
+
*
|
|
298
|
+
* Returns an empty Map when the parent directory does not exist or contains
|
|
299
|
+
* no gsd-*.md files (so _resolveManifest can use size>0 as the "flat layout
|
|
300
|
+
* present" signal and fall through to the installed-skills branch otherwise).
|
|
301
|
+
*/
|
|
302
|
+
function _loadFlatCommandsGsdManifest(commandsParentDir) {
|
|
303
|
+
const manifest = new Map();
|
|
304
|
+
let entries;
|
|
305
|
+
try {
|
|
306
|
+
entries = node_fs_1.default.readdirSync(commandsParentDir, { withFileTypes: true });
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
return manifest;
|
|
310
|
+
}
|
|
311
|
+
for (const entry of entries) {
|
|
312
|
+
if (!entry.isFile())
|
|
313
|
+
continue;
|
|
314
|
+
if (!entry.name.startsWith('gsd-'))
|
|
315
|
+
continue;
|
|
316
|
+
if (!entry.name.endsWith('.md'))
|
|
317
|
+
continue;
|
|
318
|
+
// Strip 'gsd-' prefix (4 chars) and '.md' suffix (3 chars) → stem.
|
|
319
|
+
const stem = entry.name.slice(4, -3);
|
|
320
|
+
if (!stem)
|
|
321
|
+
continue;
|
|
322
|
+
// Mirror loadSkillsManifest's try/catch structure exactly: wrap read +
|
|
323
|
+
// parse + set together so an unreadable file OR a thrown parser degrades
|
|
324
|
+
// both keys to [] (parity; closes the latent catch-scope drift a reviewer
|
|
325
|
+
// flagged — both parsers are non-throwing today, but the structural
|
|
326
|
+
// match future-proofs the "identical Map shape" contract).
|
|
327
|
+
try {
|
|
328
|
+
const content = node_fs_1.default.readFileSync(node_path_1.default.join(commandsParentDir, entry.name), 'utf8');
|
|
329
|
+
manifest.set(stem, parseRequires(content));
|
|
330
|
+
manifest.set(`_calls_agents_${stem}`, parseCallsAgents(content));
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
manifest.set(stem, []);
|
|
334
|
+
manifest.set(`_calls_agents_${stem}`, []);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return manifest;
|
|
338
|
+
}
|
|
285
339
|
/**
|
|
286
340
|
* Resolve the skill dependency manifest for capability-state resolution.
|
|
287
341
|
*
|
|
288
|
-
* Resolution order
|
|
289
|
-
* 1. If commandsGsdDir exists, load from source
|
|
290
|
-
*
|
|
342
|
+
* Resolution order:
|
|
343
|
+
* 1. If commandsGsdDir exists, load from the nested source layout
|
|
344
|
+
* (repo-checkout behavior: <repo>/commands/gsd/*.md).
|
|
345
|
+
* 2. #1858 — otherwise, if the flat source layout is present (gsd-<stem>.md
|
|
346
|
+
* files in dirname(commandsGsdDir)), load from there. This is the Claude
|
|
347
|
+
* local project install shape where commands/gsd/ does not exist but
|
|
348
|
+
* commands/gsd-<stem>.md files do.
|
|
349
|
+
* 3. #1160 — otherwise, fall back to installed skills at
|
|
350
|
+
* configDir/skills/gsd-[stem]/SKILL.md.
|
|
291
351
|
*
|
|
292
|
-
* In an installed runtime
|
|
293
|
-
*
|
|
294
|
-
* materialise the full-sentinel to an empty Set, making every
|
|
295
|
-
* unsurfaced even when the skill was physically installed
|
|
352
|
+
* In an installed runtime both source trees are absent; only the skills/
|
|
353
|
+
* layout exists. Returning an empty manifest caused resolveSurface to
|
|
354
|
+
* materialise the full-sentinel to an empty Set, making every skill-bearing
|
|
355
|
+
* capability appear unsurfaced even when the skill was physically installed
|
|
356
|
+
* (#1160) or authored as a flat command file (#1858).
|
|
296
357
|
*/
|
|
297
358
|
function _resolveManifest(commandsGsdDir, configDir) {
|
|
298
359
|
if (node_fs_1.default.existsSync(commandsGsdDir)) {
|
|
299
360
|
return loadSkillsManifest(commandsGsdDir);
|
|
300
361
|
}
|
|
362
|
+
// #1858: flat source layout — gsd-<stem>.md files at dirname(commandsGsdDir).
|
|
363
|
+
// Only claim the flat branch when it actually has gsd-*.md files; otherwise
|
|
364
|
+
// fall through to the installed-skills branch (a commands/ dir with no gsd
|
|
365
|
+
// files must not shadow an installed skills/ tree).
|
|
366
|
+
const flat = _loadFlatCommandsGsdManifest(node_path_1.default.dirname(commandsGsdDir));
|
|
367
|
+
if (flat.size > 0)
|
|
368
|
+
return flat;
|
|
301
369
|
return _loadInstalledSkillsManifest(configDir);
|
|
302
370
|
}
|
|
303
371
|
/**
|
|
@@ -334,7 +402,7 @@ function _resolveManifest(commandsGsdDir, configDir) {
|
|
|
334
402
|
* @param raw Whether to emit raw JSON (io.output raw mode)
|
|
335
403
|
* @param _options Reserved for future use
|
|
336
404
|
*/
|
|
337
|
-
function resolveCapabilityRuntimeState(cwd, runtimeConfigDir, configOverride) {
|
|
405
|
+
function resolveCapabilityRuntimeState(cwd, runtimeConfigDir, configOverride, runtimeOverride) {
|
|
338
406
|
const warnings = [];
|
|
339
407
|
// Resolve runtimeConfigDir using the canonical runtime-homes resolver.
|
|
340
408
|
// When not provided, the active runtime is detected via the canonical
|
|
@@ -350,13 +418,40 @@ function resolveCapabilityRuntimeState(cwd, runtimeConfigDir, configOverride) {
|
|
|
350
418
|
try {
|
|
351
419
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
352
420
|
const runtimeHomes = require('./runtime-homes.cjs');
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
|
|
359
|
-
|
|
421
|
+
// #2003: an explicit --runtime override bypasses the persisted-runtime
|
|
422
|
+
// fallback (GSD_RUNTIME → config.runtime → 'claude') so, e.g., a repo with
|
|
423
|
+
// persisted runtime:"codex" resolves the Claude config dir when the operator
|
|
424
|
+
// is driving from Claude Code. Canonicalize via runtime-name-policy (handles
|
|
425
|
+
// aliases like codex-app → codex); if canonicalization yields nothing, fall
|
|
426
|
+
// through to the persisted-runtime resolution below. Mirrors the update-
|
|
427
|
+
// context / effort sync precedent (read/diagnostic paths accepting both
|
|
428
|
+
// --config-dir and --runtime).
|
|
429
|
+
if (typeof runtimeOverride === 'string' && runtimeOverride.trim() !== '') {
|
|
430
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
431
|
+
const runtimeNamePolicy = require('./runtime-name-policy.cjs');
|
|
432
|
+
const canonical = runtimeNamePolicy.canonicalizeRuntimeName(runtimeOverride);
|
|
433
|
+
if (canonical) {
|
|
434
|
+
resolvedConfigDir = runtimeHomes.getGlobalConfigDir(canonical);
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
// #2003: unknown runtime override — warn (don't silently ignore the
|
|
438
|
+
// explicit input) and fall through to persisted-runtime resolution.
|
|
439
|
+
// Avoids a silent-wrong-result on this diagnostic command for typos
|
|
440
|
+
// (e.g. "cluade") or runtimes known to runtime-homes but not yet to
|
|
441
|
+
// the alias manifest (e.g. "grok"). The warning surfaces via the
|
|
442
|
+
// `warnings[]` channel consumed by cmdCapabilityState/cmdLoopRenderHooks.
|
|
443
|
+
warnings.push(`--runtime "${runtimeOverride}" is not a known runtime; falling back to auto-detected/persisted runtime resolution`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (!resolvedConfigDir) {
|
|
447
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
448
|
+
const runtimeSlash = require('./runtime-slash.cjs');
|
|
449
|
+
// Detect the active runtime via GSD_RUNTIME → config.runtime → 'claude'.
|
|
450
|
+
// resolveRuntime reads config.json directly (no side effects) and returns
|
|
451
|
+
// a lowercased canonical runtime name.
|
|
452
|
+
const detectedRuntime = runtimeSlash.resolveRuntime(cwd);
|
|
453
|
+
resolvedConfigDir = runtimeHomes.getGlobalConfigDir(detectedRuntime);
|
|
454
|
+
}
|
|
360
455
|
}
|
|
361
456
|
catch {
|
|
362
457
|
// Defensive fallback: use ~/.claude if the canonical resolver throws.
|
|
@@ -450,8 +545,11 @@ function resolveCapabilityRuntimeState(cwd, runtimeConfigDir, configOverride) {
|
|
|
450
545
|
capabilities: result.capabilities,
|
|
451
546
|
};
|
|
452
547
|
}
|
|
453
|
-
function cmdCapabilityState(cwd, runtimeConfigDir, raw,
|
|
454
|
-
|
|
548
|
+
function cmdCapabilityState(cwd, runtimeConfigDir, raw, options = {}) {
|
|
549
|
+
// #2003: thread an explicit --runtime override so the config-dir resolution
|
|
550
|
+
// bypasses the persisted-runtime fallback (GSD_RUNTIME → config.runtime).
|
|
551
|
+
const runtimeOverride = typeof options['runtime'] === 'string' ? options['runtime'] : undefined;
|
|
552
|
+
const result = resolveCapabilityRuntimeState(cwd, runtimeConfigDir, undefined, runtimeOverride);
|
|
455
553
|
for (const warning of result.warnings) {
|
|
456
554
|
coreError(`capability state: ${warning}`);
|
|
457
555
|
}
|
|
@@ -491,6 +589,7 @@ module.exports = {
|
|
|
491
589
|
// Exported for tests
|
|
492
590
|
_resolveCommandsGsdDir,
|
|
493
591
|
_loadInstalledSkillsManifest,
|
|
592
|
+
_loadFlatCommandsGsdManifest,
|
|
494
593
|
_resolveManifest,
|
|
495
594
|
_isSafePropKey,
|
|
496
595
|
};
|
|
@@ -250,8 +250,18 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
|
|
|
250
250
|
const layout = runtimeArtifactLayout.resolveRuntimeArtifactLayout(runtime, resolvedConfigDir, scope);
|
|
251
251
|
const commandsGsdDir = _resolveCommandsGsdDir();
|
|
252
252
|
const manifest = _resolveManifest(commandsGsdDir, resolvedConfigDir);
|
|
253
|
+
// #1575: applySurface now accepts opts.resolveAttribution so surface-path
|
|
254
|
+
// agents get the same Co-Authored-By trailer as the install path. The
|
|
255
|
+
// resolver is not threaded here yet — the CLI command handler does not have
|
|
256
|
+
// access to getCommitAttribution (which lives in bin/install.js). Until that
|
|
257
|
+
// is refactored into a shared module, surface-path agents for descriptor-
|
|
258
|
+
// driven runtimes will lack the Co-Authored-By trailer that install adds.
|
|
259
|
+
// Parity is proven when resolveAttribution IS provided (see
|
|
260
|
+
// tests/issue-1575-agent-descriptor-parity.test.cjs).
|
|
253
261
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
254
|
-
applySurface(resolvedConfigDir, layout, manifest, undefined, registry
|
|
262
|
+
applySurface(resolvedConfigDir, layout, manifest, undefined, registry, opts?.materialize?.resolveAttribution
|
|
263
|
+
? { resolveAttribution: opts.materialize.resolveAttribution }
|
|
264
|
+
: undefined);
|
|
255
265
|
}
|
|
256
266
|
catch (err) {
|
|
257
267
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -34,6 +34,10 @@ const { getRoadmapPhaseWithFallback } = roadmapModule;
|
|
|
34
34
|
const gapCheckerModule = require("./gap-checker.cjs");
|
|
35
35
|
const { runGapAnalysis } = gapCheckerModule;
|
|
36
36
|
const prohibition_enforcement_cjs_1 = require("./prohibition-enforcement.cjs");
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
38
|
+
const gatePredicateEval = require("./gate-predicate-evaluator.cjs");
|
|
39
|
+
const { evaluatePredicate } = gatePredicateEval;
|
|
40
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
37
41
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
38
42
|
function normalizePhrase(text) {
|
|
39
43
|
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
@@ -778,6 +782,100 @@ function cmdGapAnalysisPlanPost(projectDir, args, raw) {
|
|
|
778
782
|
message: result.table || result.summary || '',
|
|
779
783
|
}, raw, undefined);
|
|
780
784
|
}
|
|
785
|
+
// ─── predicate (generic gate-predicate evaluator, #2008) ──────────────────────
|
|
786
|
+
/**
|
|
787
|
+
* Production subprocess binding for the gate-predicate evaluator. Wraps the
|
|
788
|
+
* bounded `execTool` seam (shell-command-projection) as a `runBoundedShell`
|
|
789
|
+
* the pure evaluator consumes. `sh -c` runs the interpolated command; the
|
|
790
|
+
* subprocess inherits the process env and is killed (SIGTERM) on timeout.
|
|
791
|
+
*
|
|
792
|
+
* `timedOut` is derived from the kill signal: spawnSync sets `signal: 'SIGTERM'`
|
|
793
|
+
* when the `timeout` fires, distinct from a normal non-zero exit code. A command
|
|
794
|
+
* that self-terminates with SIGTERM is indistinguishable at this seam and is
|
|
795
|
+
* reported as a timeout — either way the gate blocks (non-zero), so the outcome
|
|
796
|
+
* is fail-closed and correct. See ADR-2008.
|
|
797
|
+
*/
|
|
798
|
+
function buildPredicateDeps() {
|
|
799
|
+
return {
|
|
800
|
+
runBoundedShell(opts) {
|
|
801
|
+
const r = (0, shell_command_projection_cjs_1.execTool)('sh', ['-c', opts.command], { cwd: opts.cwd, timeout: opts.timeoutMs });
|
|
802
|
+
return {
|
|
803
|
+
exitCode: r.exitCode,
|
|
804
|
+
stdout: r.stdout,
|
|
805
|
+
stderr: r.stderr,
|
|
806
|
+
signal: r.signal,
|
|
807
|
+
timedOut: r.signal === 'SIGTERM',
|
|
808
|
+
};
|
|
809
|
+
},
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
/** Parse `--flag value` pairs from an args array into a map (last write wins). */
|
|
813
|
+
function parsePredicateFlags(args) {
|
|
814
|
+
const out = {};
|
|
815
|
+
for (let i = 0; i < args.length; i++) {
|
|
816
|
+
const a = args[i];
|
|
817
|
+
if (typeof a !== 'string')
|
|
818
|
+
continue;
|
|
819
|
+
if (!a.startsWith('--'))
|
|
820
|
+
continue;
|
|
821
|
+
const key = a.slice(2);
|
|
822
|
+
const next = args[i + 1];
|
|
823
|
+
if (key.length > 0 && typeof next === 'string' && !next.startsWith('--')) {
|
|
824
|
+
out[key] = next;
|
|
825
|
+
i++;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return out;
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* `check predicate` — generic evaluator for capability gate `check.predicate`
|
|
832
|
+
* blocks (#2008). The workflow gate-dispatch invokes this for any gate whose
|
|
833
|
+
* `check` carries a `predicate` (instead of a `query`); the predicate object is
|
|
834
|
+
* passed as `--predicate '<json>'`. Emits the standard `{ block, message,
|
|
835
|
+
* details? }` gate contract on success. A malformed predicate / unknown kind
|
|
836
|
+
* THROWS inside the evaluator and is mapped here to `error()` (non-zero exit),
|
|
837
|
+
* which the workflow's two-step gate contract treats as a step-1 command failure
|
|
838
|
+
* routed per the gate's `onError`.
|
|
839
|
+
*
|
|
840
|
+
* Invocation:
|
|
841
|
+
* gsd_run check predicate --predicate '<json>' \
|
|
842
|
+
* [--phase-dir <dir>] [--phase-number <n>] [--phase-req-ids <ids>] --raw
|
|
843
|
+
*
|
|
844
|
+
* The subprocess runs at the runtime project root (the `cwd` passed to this
|
|
845
|
+
* router), inheriting the process env. Interpolation placeholders
|
|
846
|
+
* ${PHASE_NUMBER}/${PHASE_DIR}/${PHASE_REQ_IDS} are substituted from the flags.
|
|
847
|
+
*/
|
|
848
|
+
function cmdCheckPredicate(projectDir, args, raw) {
|
|
849
|
+
const flags = parsePredicateFlags(args);
|
|
850
|
+
const predicateJson = flags['predicate'];
|
|
851
|
+
if (!predicateJson) {
|
|
852
|
+
error('predicate requires --predicate <json> (the gate hook check.predicate object)', ERROR_REASON.SDK_MISSING_ARG);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
let predicate;
|
|
856
|
+
try {
|
|
857
|
+
predicate = JSON.parse(predicateJson);
|
|
858
|
+
}
|
|
859
|
+
catch {
|
|
860
|
+
error('predicate --predicate value must be valid JSON', ERROR_REASON.USAGE);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
const ctx = {
|
|
864
|
+
cwd: projectDir,
|
|
865
|
+
phaseNumber: flags['phase-number'],
|
|
866
|
+
phaseDir: flags['phase-dir'],
|
|
867
|
+
phaseReqIds: flags['phase-req-ids'],
|
|
868
|
+
};
|
|
869
|
+
let result;
|
|
870
|
+
try {
|
|
871
|
+
result = evaluatePredicate(predicate, ctx, buildPredicateDeps());
|
|
872
|
+
}
|
|
873
|
+
catch (e) {
|
|
874
|
+
error(`gate predicate evaluation failed: ${e.message}`, ERROR_REASON.USAGE);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
output(result, raw, undefined);
|
|
878
|
+
}
|
|
781
879
|
function routeCheckCommand({ args, cwd, raw }) {
|
|
782
880
|
// Normalize dots to hyphens in the subcommand so both forms are accepted.
|
|
783
881
|
// This makes `check.query = "ui.plan-gate"` (dotted form in capability.json gates)
|
|
@@ -830,6 +928,15 @@ function routeCheckCommand({ args, cwd, raw }) {
|
|
|
830
928
|
cmdVerifyCodebaseDrift(cwd, raw);
|
|
831
929
|
return;
|
|
832
930
|
}
|
|
931
|
+
if (subcommand === 'predicate') {
|
|
932
|
+
// Generic gate-predicate evaluator (#2008). The workflow gate-dispatch calls
|
|
933
|
+
// this for any gate whose `check` carries a `predicate` (instead of a `query`),
|
|
934
|
+
// passing the predicate object as --predicate '<json>'. NOTE: unlike the
|
|
935
|
+
// `check.query` subcommands above (which take positional phase args), this
|
|
936
|
+
// subcommand parses --flag value pairs.
|
|
937
|
+
cmdCheckPredicate(cwd, args, raw);
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
833
940
|
if (subcommand === 'prohibition-enforcement') {
|
|
834
941
|
// The deterministic test-tier prohibition PRODUCER/gate (#1259, ADR-550 D5d). Locates the
|
|
835
942
|
// wired mechanical check (node-test or lint-rule), confirms fail-first, runs it, builds
|
|
@@ -838,7 +945,7 @@ function routeCheckCommand({ args, cwd, raw }) {
|
|
|
838
945
|
(0, prohibition_enforcement_cjs_1.routeProhibitionEnforcement)(args, raw);
|
|
839
946
|
return;
|
|
840
947
|
}
|
|
841
|
-
error('Unknown check subcommand. Available: auto-mode, decision-coverage-plan, decision-coverage-verify, gap-analysis-plan-post, prohibition-enforcement, tdd-review-checkpoint, ui-plan-gate, ui-safety-gate, verify-schema-drift, verify-codebase-drift', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
948
|
+
error('Unknown check subcommand. Available: auto-mode, decision-coverage-plan, decision-coverage-verify, gap-analysis-plan-post, predicate, prohibition-enforcement, tdd-review-checkpoint, ui-plan-gate, ui-safety-gate, verify-schema-drift, verify-codebase-drift', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
842
949
|
}
|
|
843
950
|
module.exports = {
|
|
844
951
|
routeCheckCommand,
|
|
@@ -848,4 +955,7 @@ module.exports = {
|
|
|
848
955
|
computeUiSafetyGate,
|
|
849
956
|
cmdGapAnalysisPlanPost,
|
|
850
957
|
cmdTddReviewCheckpoint,
|
|
958
|
+
cmdCheckPredicate,
|
|
959
|
+
buildPredicateDeps,
|
|
960
|
+
parsePredicateFlags,
|
|
851
961
|
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Claude orchestration command router — CLI dispatcher for
|
|
4
|
+
* `gsd-tools claude-orchestration <subcommand>`.
|
|
5
|
+
*
|
|
6
|
+
* #1143 — thin CLI adapter over the pure `claude-orchestration.cjs` module.
|
|
7
|
+
* Lets execute-phase (or any orchestrator) invoke the Workflow-backend
|
|
8
|
+
* detection and the Workflow-script emitter through the standard capability
|
|
9
|
+
* command surface (ADR-959) instead of a bare `require()`.
|
|
10
|
+
*
|
|
11
|
+
* Router signature: { args, cwd, raw, error } — identical to the other host
|
|
12
|
+
* routers; discovered by dispatchCapabilityCommand via the registry's
|
|
13
|
+
* commandFamilies index.
|
|
14
|
+
*
|
|
15
|
+
* Subcommands:
|
|
16
|
+
* detect-backend [--runtime <id>] [--agent-sdk-version <ver>] [--no-nested-dispatch]
|
|
17
|
+
* Resolves whether the Workflow backend should activate. `--runtime`
|
|
18
|
+
* defaults to the GSD_RUNTIME env var (or 'unknown'). Reads the
|
|
19
|
+
* `claude_orchestration.*` keys from .planning/config.json. Emits
|
|
20
|
+
* { available, backend, reason }.
|
|
21
|
+
*
|
|
22
|
+
* emit-workflow --waves <path> --run-id <id> [--phase-dir <dir>] [--budget <n>]
|
|
23
|
+
* Reads a wave/plan manifest JSON file and emits the generated Workflow
|
|
24
|
+
* script + summary. The manifest shape matches emitWorkflowScript's input:
|
|
25
|
+
* { waves: [{ id, plans: [{ id, brief, files_modified: string[] }] }] }.
|
|
26
|
+
*/
|
|
27
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
28
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
29
|
+
};
|
|
30
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
31
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
33
|
+
const io = require("./io.cjs");
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
35
|
+
const core = require("./claude-orchestration.cjs");
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
|
+
const configLoader = require("./config-loader.cjs");
|
|
38
|
+
const { output } = io;
|
|
39
|
+
const { detectWorkflowBackend, emitWorkflowScript } = core;
|
|
40
|
+
const CAPABLE_HOST = { dispatch: { nested: true, background: true } };
|
|
41
|
+
function usage(error) {
|
|
42
|
+
error('Usage: gsd-tools claude-orchestration <detect-backend|emit-workflow> [...]\n' +
|
|
43
|
+
' detect-backend [--runtime <id>] [--agent-sdk-version <ver>] [--no-nested-dispatch]\n' +
|
|
44
|
+
' emit-workflow --waves <path> --run-id <id> [--phase-dir <dir>] [--budget <n>]');
|
|
45
|
+
}
|
|
46
|
+
function argValue(args, flag) {
|
|
47
|
+
const i = args.indexOf(flag);
|
|
48
|
+
return i !== -1 && i + 1 < args.length ? args[i + 1] : undefined;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Detect whether the Workflow backend should activate for the current/given
|
|
52
|
+
* runtime. Reads `claude_orchestration.*` from the project config; runtime and
|
|
53
|
+
* SDK version come from flags (the orchestrator already knows these) or env.
|
|
54
|
+
*/
|
|
55
|
+
function cmdDetectBackend(args, cwd, raw) {
|
|
56
|
+
const runtimeId = argValue(args, '--runtime') || process.env['GSD_RUNTIME'] || 'unknown';
|
|
57
|
+
const agentSdkVersion = argValue(args, '--agent-sdk-version');
|
|
58
|
+
const noNested = args.includes('--no-nested-dispatch');
|
|
59
|
+
const hostIntegration = noNested ? { dispatch: { nested: false, background: true } } : CAPABLE_HOST;
|
|
60
|
+
// Resolve the claude_orchestration.* slice from the project config (federated
|
|
61
|
+
// keys are merged by loadConfig as a nested object). A config read failure
|
|
62
|
+
// degrades to inline — it must not break the core loop.
|
|
63
|
+
let claudeSlice = {};
|
|
64
|
+
try {
|
|
65
|
+
const loaded = configLoader.loadConfig(cwd);
|
|
66
|
+
const slice = loaded['claude_orchestration'];
|
|
67
|
+
if (slice && typeof slice === 'object' && !Array.isArray(slice)) {
|
|
68
|
+
claudeSlice = slice;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
claudeSlice = {};
|
|
73
|
+
}
|
|
74
|
+
// Flatten the nested slice into the dotted-key shape detectWorkflowBackend expects.
|
|
75
|
+
const flatConfig = {};
|
|
76
|
+
for (const k of Object.keys(claudeSlice)) {
|
|
77
|
+
flatConfig['claude_orchestration.' + k] = claudeSlice[k];
|
|
78
|
+
}
|
|
79
|
+
const result = detectWorkflowBackend({ runtimeId, hostIntegration, config: flatConfig, agentSdkVersion });
|
|
80
|
+
output(result, raw);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Emit a Workflow script from a wave/plan manifest file.
|
|
84
|
+
*/
|
|
85
|
+
function cmdEmitWorkflow(args, _cwd, raw, error) {
|
|
86
|
+
const wavesPath = argValue(args, '--waves');
|
|
87
|
+
const runId = argValue(args, '--run-id');
|
|
88
|
+
const phaseDir = argValue(args, '--phase-dir') || '.planning/phases/current';
|
|
89
|
+
const budgetRaw = argValue(args, '--budget');
|
|
90
|
+
if (!wavesPath) {
|
|
91
|
+
error('emit-workflow requires --waves <path>');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!runId) {
|
|
95
|
+
error('emit-workflow requires --run-id <id>');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
let waves;
|
|
99
|
+
try {
|
|
100
|
+
const content = node_fs_1.default.readFileSync(node_path_1.default.resolve(wavesPath), 'utf8');
|
|
101
|
+
const parsed = JSON.parse(content);
|
|
102
|
+
waves = parsed['waves'];
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
error('emit-workflow: could not read/parse --waves file "' + wavesPath + '": ' + (e instanceof Error ? e.message : String(e)));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const budgetTokens = budgetRaw !== undefined ? parseInt(budgetRaw, 10) : undefined;
|
|
109
|
+
const budget = (typeof budgetTokens === 'number' && !Number.isNaN(budgetTokens)) ? budgetTokens : undefined;
|
|
110
|
+
const result = emitWorkflowScript({
|
|
111
|
+
phaseDir,
|
|
112
|
+
runId,
|
|
113
|
+
waves: waves,
|
|
114
|
+
budgetTokens: budget,
|
|
115
|
+
});
|
|
116
|
+
if (!result.ok) {
|
|
117
|
+
error('emit-workflow: ' + result.reason);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
output({ script: result.script, summary: result.summary }, raw);
|
|
121
|
+
}
|
|
122
|
+
function routeClaudeOrchestrationCommand(opts) {
|
|
123
|
+
const { args, cwd, raw, error } = opts;
|
|
124
|
+
// args[0] is the family ('claude-orchestration'); the subcommand is args[1].
|
|
125
|
+
const subcommand = args[1];
|
|
126
|
+
if (subcommand === 'detect-backend') {
|
|
127
|
+
cmdDetectBackend(args, cwd, raw);
|
|
128
|
+
}
|
|
129
|
+
else if (subcommand === 'emit-workflow') {
|
|
130
|
+
cmdEmitWorkflow(args, cwd, raw, error);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
usage(error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
module.exports = { routeClaudeOrchestrationCommand };
|