@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
|
@@ -54,6 +54,9 @@ const { checkAgentsInstalled } = agentInstallCheck;
|
|
|
54
54
|
const gitBaseBranch = require("./git-base-branch.cjs");
|
|
55
55
|
const { gitWorktreeInfoInternal } = gitBaseBranch;
|
|
56
56
|
const resolution_cjs_1 = require("./resolution.cjs");
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- onboard-projection.cjs is an export= CommonJS module
|
|
58
|
+
const onboardProjection = require("./onboard-projection.cjs");
|
|
59
|
+
const { REQUIRED_CODEBASE_MAP_FILES, buildOnboardProjection, hasCodeFilesInternal, hasPackageFileInternal, listCodebaseMapFiles, } = onboardProjection;
|
|
57
60
|
const { output, error } = io;
|
|
58
61
|
const { loadConfig, loadConfigResolved } = configLoader;
|
|
59
62
|
const { resolveModelInternal, resolveGranularityInternal, assertValidGranularityOverride } = modelResolver;
|
|
@@ -479,84 +482,23 @@ function cmdInitNewProject(cwd, raw) {
|
|
|
479
482
|
const hasFirecrawl = !!(process.env['FIRECRAWL_API_KEY'] || node_fs_1.default.existsSync(firecrawlKeyFile));
|
|
480
483
|
const exaKeyFile = node_path_1.default.join(homedir, '.gsd', 'exa_api_key');
|
|
481
484
|
const hasExaSearch = !!(process.env['EXA_API_KEY'] || node_fs_1.default.existsSync(exaKeyFile));
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
'.kt', '.kts',
|
|
488
|
-
'.c', '.cpp', '.h',
|
|
489
|
-
'.cs',
|
|
490
|
-
'.rb',
|
|
491
|
-
'.php',
|
|
492
|
-
'.dart',
|
|
493
|
-
'.m', '.mm',
|
|
494
|
-
'.scala',
|
|
495
|
-
'.groovy',
|
|
496
|
-
'.lua',
|
|
497
|
-
'.r', '.R',
|
|
498
|
-
'.zig',
|
|
499
|
-
'.ex', '.exs',
|
|
500
|
-
'.clj',
|
|
501
|
-
]);
|
|
502
|
-
const skipDirs = new Set([
|
|
503
|
-
'node_modules', '.git', '.planning', '.claude', '.codex',
|
|
504
|
-
'__pycache__', 'target', 'dist', 'build',
|
|
505
|
-
]);
|
|
506
|
-
function findCodeFiles(dir, depth) {
|
|
507
|
-
if (depth > 3)
|
|
508
|
-
return false;
|
|
509
|
-
let entries;
|
|
510
|
-
try {
|
|
511
|
-
entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
512
|
-
}
|
|
513
|
-
catch {
|
|
514
|
-
return false;
|
|
515
|
-
}
|
|
516
|
-
for (const entry of entries) {
|
|
517
|
-
if (entry.isFile() && codeExtensions.has(node_path_1.default.extname(entry.name)))
|
|
518
|
-
return true;
|
|
519
|
-
if (entry.isDirectory() && !skipDirs.has(entry.name)) {
|
|
520
|
-
if (findCodeFiles(node_path_1.default.join(dir, entry.name), depth + 1))
|
|
521
|
-
return true;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
return false;
|
|
525
|
-
}
|
|
526
|
-
hasCode = findCodeFiles(cwd, 0);
|
|
527
|
-
}
|
|
528
|
-
catch {
|
|
529
|
-
/* intentionally empty — best-effort detection */
|
|
530
|
-
}
|
|
531
|
-
hasPackageFile =
|
|
532
|
-
pathExistsInternal(cwd, 'package.json') ||
|
|
533
|
-
pathExistsInternal(cwd, 'requirements.txt') ||
|
|
534
|
-
pathExistsInternal(cwd, 'Cargo.toml') ||
|
|
535
|
-
pathExistsInternal(cwd, 'go.mod') ||
|
|
536
|
-
pathExistsInternal(cwd, 'Package.swift') ||
|
|
537
|
-
pathExistsInternal(cwd, 'build.gradle') ||
|
|
538
|
-
pathExistsInternal(cwd, 'build.gradle.kts') ||
|
|
539
|
-
pathExistsInternal(cwd, 'pom.xml') ||
|
|
540
|
-
pathExistsInternal(cwd, 'Gemfile') ||
|
|
541
|
-
pathExistsInternal(cwd, 'composer.json') ||
|
|
542
|
-
pathExistsInternal(cwd, 'pubspec.yaml') ||
|
|
543
|
-
pathExistsInternal(cwd, 'CMakeLists.txt') ||
|
|
544
|
-
pathExistsInternal(cwd, 'Makefile') ||
|
|
545
|
-
pathExistsInternal(cwd, 'build.zig') ||
|
|
546
|
-
pathExistsInternal(cwd, 'mix.exs') ||
|
|
547
|
-
pathExistsInternal(cwd, 'project.clj');
|
|
485
|
+
const hasCode = hasCodeFilesInternal(cwd);
|
|
486
|
+
const hasPackageFile = hasPackageFileInternal(cwd);
|
|
487
|
+
const isBrownfield = hasCode || hasPackageFile;
|
|
488
|
+
const codebaseMapFiles = listCodebaseMapFiles(cwd);
|
|
489
|
+
const hasCodebaseMap = codebaseMapFiles.length === REQUIRED_CODEBASE_MAP_FILES.length;
|
|
548
490
|
const result = {
|
|
549
491
|
researcher_model: resolveModelInternal(cwd, 'gsd-project-researcher'),
|
|
550
492
|
synthesizer_model: resolveModelInternal(cwd, 'gsd-research-synthesizer'),
|
|
551
493
|
roadmapper_model: resolveModelInternal(cwd, 'gsd-roadmapper'),
|
|
552
494
|
commit_docs: config.commit_docs,
|
|
553
495
|
project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
|
|
554
|
-
has_codebase_map:
|
|
496
|
+
has_codebase_map: hasCodebaseMap,
|
|
555
497
|
planning_exists: pathExistsInternal(cwd, '.planning'),
|
|
556
498
|
has_existing_code: hasCode,
|
|
557
499
|
has_package_file: hasPackageFile,
|
|
558
|
-
is_brownfield:
|
|
559
|
-
needs_codebase_map:
|
|
500
|
+
is_brownfield: isBrownfield,
|
|
501
|
+
needs_codebase_map: isBrownfield && !hasCodebaseMap,
|
|
560
502
|
...getInitGitState(cwd),
|
|
561
503
|
brave_search_available: hasBraveSearch,
|
|
562
504
|
firecrawl_available: hasFirecrawl,
|
|
@@ -656,6 +598,19 @@ function cmdInitIngestDocs(cwd, raw) {
|
|
|
656
598
|
};
|
|
657
599
|
output(withProjectRoot(cwd, result), raw);
|
|
658
600
|
}
|
|
601
|
+
function cmdInitOnboard(cwd, raw, options = {}) {
|
|
602
|
+
const config = loadConfig(cwd);
|
|
603
|
+
const workflowConfig = (config.workflow ?? {});
|
|
604
|
+
const result = {
|
|
605
|
+
...buildOnboardProjection(cwd, {
|
|
606
|
+
commitDocs: !!config.commit_docs,
|
|
607
|
+
fast: options['fast'] === true,
|
|
608
|
+
textMode: options['text'] === true || !!config.text_mode || !!workflowConfig['text_mode'],
|
|
609
|
+
}),
|
|
610
|
+
...getInitGitState(cwd),
|
|
611
|
+
};
|
|
612
|
+
output(withProjectRoot(cwd, result), raw);
|
|
613
|
+
}
|
|
659
614
|
function cmdInitResume(cwd, raw) {
|
|
660
615
|
const config = loadConfig(cwd);
|
|
661
616
|
let interruptedAgentId = null;
|
|
@@ -2099,6 +2054,7 @@ module.exports = {
|
|
|
2099
2054
|
cmdInitNewMilestone,
|
|
2100
2055
|
cmdInitQuick,
|
|
2101
2056
|
cmdInitIngestDocs,
|
|
2057
|
+
cmdInitOnboard,
|
|
2102
2058
|
cmdInitResume,
|
|
2103
2059
|
cmdInitVerifyWork,
|
|
2104
2060
|
cmdInitPhaseOp,
|
|
@@ -222,7 +222,7 @@ function migrateLegacyDevPreferencesToSkill(targetDir, saved, runtime, scope = '
|
|
|
222
222
|
* - agents: write as-is (files already carry their own `gsd-` prefix).
|
|
223
223
|
* For kimi-agents kind: recursively copy generated YAML/prompt files.
|
|
224
224
|
*/
|
|
225
|
-
function _copyStaged(stagedDir, destDir, kind, configDir) {
|
|
225
|
+
function _copyStaged(stagedDir, destDir, kind, configDir, runtime) {
|
|
226
226
|
// Defense-in-depth: verify destDir is within the install root even if the
|
|
227
227
|
// upstream assertDestWithinConfigHome check was somehow bypassed. This guards
|
|
228
228
|
// the actual write site against any future call-site drift.
|
|
@@ -275,8 +275,11 @@ function _copyStaged(stagedDir, destDir, kind, configDir) {
|
|
|
275
275
|
const stem = entry.name.slice(0, -3); // strip .md
|
|
276
276
|
let destName;
|
|
277
277
|
if (kind.kind === 'agents') {
|
|
278
|
-
// Agent files already carry the gsd- prefix in the source dir
|
|
279
|
-
|
|
278
|
+
// Agent files already carry the gsd- prefix in the source dir.
|
|
279
|
+
// #1575: copilot agents get .agent.md suffix (mirrors inline loop line ~9118).
|
|
280
|
+
destName = runtime === 'copilot'
|
|
281
|
+
? entry.name.replace(/\.md$/, '.agent.md')
|
|
282
|
+
: entry.name;
|
|
280
283
|
}
|
|
281
284
|
else if (namespacedByDir) {
|
|
282
285
|
// Directory is the namespace; don't double-prefix the filename
|
|
@@ -571,7 +574,7 @@ function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile, res
|
|
|
571
574
|
}
|
|
572
575
|
}
|
|
573
576
|
_removeGsdEntries(dest, kind);
|
|
574
|
-
_copyStaged(item.sourceDir, dest, kind, configDir);
|
|
577
|
+
_copyStaged(item.sourceDir, dest, kind, configDir, runtime);
|
|
575
578
|
// Restore user-owned dirs after the prune+copy
|
|
576
579
|
for (const [dirName, snap] of toPreserve) {
|
|
577
580
|
_restoreDir(node_path_1.default.join(dest, dirName), snap);
|
|
@@ -581,7 +584,7 @@ function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile, res
|
|
|
581
584
|
// For non-skills kinds (commands, agents): no user content to preserve;
|
|
582
585
|
// just prune stale gsd-* entries and copy new ones.
|
|
583
586
|
_removeGsdEntries(dest, kind);
|
|
584
|
-
_copyStaged(item.sourceDir, dest, kind, configDir);
|
|
587
|
+
_copyStaged(item.sourceDir, dest, kind, configDir, runtime);
|
|
585
588
|
}
|
|
586
589
|
}
|
|
587
590
|
}
|
|
@@ -42,6 +42,7 @@ const PROFILES = Object.freeze({
|
|
|
42
42
|
standard: Object.freeze([
|
|
43
43
|
// Core loop
|
|
44
44
|
'new-project',
|
|
45
|
+
'onboard',
|
|
45
46
|
'discuss-phase',
|
|
46
47
|
'plan-phase',
|
|
47
48
|
'execute-phase',
|
|
@@ -819,6 +820,7 @@ module.exports = {
|
|
|
819
820
|
writeActiveProfile,
|
|
820
821
|
// Shared internals
|
|
821
822
|
parseRequires,
|
|
823
|
+
parseCallsAgents,
|
|
822
824
|
cleanupStagedSkills,
|
|
823
825
|
// Back-compat / deprecated
|
|
824
826
|
MINIMAL_SKILL_ALLOWLIST,
|
|
@@ -415,6 +415,12 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
415
415
|
const runtimeConfigDir = typeof options['configDir'] === 'string'
|
|
416
416
|
? options['configDir']
|
|
417
417
|
: undefined;
|
|
418
|
+
// #2003: thread an explicit --runtime override into the capability-state
|
|
419
|
+
// resolver so the config-dir resolution bypasses the persisted-runtime
|
|
420
|
+
// fallback (GSD_RUNTIME → config.runtime). Without this, a repo with persisted
|
|
421
|
+
// runtime:"codex" resolves the config dir to ~/.codex and execute:post /
|
|
422
|
+
// verify:post hooks silently no-op when the operator drives from Claude Code.
|
|
423
|
+
const runtimeOverride = typeof options['runtime'] === 'string' ? options['runtime'] : undefined;
|
|
418
424
|
// Load the config snapshot ONCE and share it with both the capability-state
|
|
419
425
|
// resolver (via configOverride) and loop-hook resolution, so federated keys
|
|
420
426
|
// present in loadConfig resolve identically for `active` and for hook when/
|
|
@@ -429,7 +435,7 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
429
435
|
catch {
|
|
430
436
|
config = {};
|
|
431
437
|
}
|
|
432
|
-
const state = resolveCapabilityRuntimeState(cwd, runtimeConfigDir, config);
|
|
438
|
+
const state = resolveCapabilityRuntimeState(cwd, runtimeConfigDir, config, runtimeOverride);
|
|
433
439
|
// Load overlay-aware registry (ADR-1244 D2 wiring) so installed third-party
|
|
434
440
|
// capabilities are visible to loop rendering exactly like first-party ones.
|
|
435
441
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
@@ -263,7 +263,13 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
263
263
|
// Archive REQUIREMENTS.md
|
|
264
264
|
if (node_fs_1.default.existsSync(reqPath)) {
|
|
265
265
|
const reqContent = node_fs_1.default.readFileSync(reqPath, 'utf-8');
|
|
266
|
-
|
|
266
|
+
// Derive the display path from the same source the writer uses (reqPath), so a
|
|
267
|
+
// workstream archive header points at `.planning/workstreams/<ws>/REQUIREMENTS.md`
|
|
268
|
+
// instead of the hardcoded root path (#1993). Root case is byte-identical.
|
|
269
|
+
// Normalize to POSIX separators so the header is cross-platform (Windows
|
|
270
|
+
// path.relative yields backslashes; the original literal was forward-slash).
|
|
271
|
+
const reqDisplay = node_path_1.default.relative(cwd, reqPath).split(node_path_1.default.sep).join('/');
|
|
272
|
+
const archiveHeader = `# Requirements Archive: ${version} ${milestoneName}\n\n**Archived:** ${today}\n**Status:** SHIPPED\n\nFor current requirements, see \`${reqDisplay}\`.\n\n---\n\n`;
|
|
267
273
|
(0, shell_command_projection_cjs_1.platformWriteSync)(node_path_1.default.join(archiveDir, `${version}-REQUIREMENTS.md`), archiveHeader + reqContent);
|
|
268
274
|
}
|
|
269
275
|
// Archive audit file if exists
|
|
@@ -78,6 +78,61 @@ function warnModelPolicyUnmappable(agentType, policyModel, tier) {
|
|
|
78
78
|
function _resetModelPolicyWarningCacheForTests() {
|
|
79
79
|
_modelPolicyUnmappableWarned.clear();
|
|
80
80
|
}
|
|
81
|
+
// Dedupe stderr warnings for unmappable model_overrides Claude IDs (#2041).
|
|
82
|
+
const _modelOverrideUnmappableWarned = new Set();
|
|
83
|
+
function warnModelOverrideUnmappable(agentType, overrideValue) {
|
|
84
|
+
const key = `${agentType}::${overrideValue}`;
|
|
85
|
+
if (_modelOverrideUnmappableWarned.has(key))
|
|
86
|
+
return;
|
|
87
|
+
_modelOverrideUnmappableWarned.add(key);
|
|
88
|
+
// Cap emission length so an oversized or secret-shaped value cannot leak in
|
|
89
|
+
// full to stderr/logs (#2041 security review). MUST go to stderr — resolve-
|
|
90
|
+
// model's JSON result is parsed from stdout.
|
|
91
|
+
const safe = overrideValue.length > 64 ? overrideValue.slice(0, 64) + '…' : overrideValue;
|
|
92
|
+
process.stderr.write(`gsd: warning — model_overrides value "${safe}" for ${agentType} ` +
|
|
93
|
+
`has no Claude agent alias; falling through to tier resolution.\n`);
|
|
94
|
+
}
|
|
95
|
+
// Test-only: reset the model_overrides warn-dedupe cache between cases (#2041).
|
|
96
|
+
function _resetModelOverrideWarningCacheForTests() {
|
|
97
|
+
_modelOverrideUnmappableWarned.clear();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* #2041 — Map a `model_overrides` value to its Claude Agent-tool alias on the
|
|
101
|
+
* claude runtime, mirroring the `model_policy` path (#1144). Claude Code's
|
|
102
|
+
* Agent tool `model` parameter documents only tier aliases (opus/sonnet/haiku/
|
|
103
|
+
* fable); a full Claude model ID returned verbatim is silently dropped by the
|
|
104
|
+
* spawner. Returns the value to return verbatim, or null to signal "fall
|
|
105
|
+
* through to normal tier/dynamic-routing resolution" (used when a Claude full
|
|
106
|
+
* ID has no alias — matches model_policy's warn-and-fall-through). Non-Claude
|
|
107
|
+
* runtimes and non-Claude values always pass through verbatim.
|
|
108
|
+
*
|
|
109
|
+
* Hardening (code+security review): a `typeof` guard preserves the pre-fix
|
|
110
|
+
* no-crash behavior if a malformed config surfaces a non-string value, and an
|
|
111
|
+
* `Object.hasOwn` lookup defeats `__proto__`/`constructor` lookups on the plain
|
|
112
|
+
* object literal so those reserved keys cannot return a truthy non-string.
|
|
113
|
+
*/
|
|
114
|
+
function mapClaudeOverrideForRuntime(override, configRuntime, agentType) {
|
|
115
|
+
// Defensive: model_overrides is typed Record<string,string> but a malformed
|
|
116
|
+
// config could surface a non-string; pass through verbatim (preserving the
|
|
117
|
+
// pre-fix no-crash behaviour) and let the downstream Agent tool reject it.
|
|
118
|
+
if (typeof override !== 'string')
|
|
119
|
+
return override;
|
|
120
|
+
const onClaude = !configRuntime || configRuntime === 'claude';
|
|
121
|
+
if (!onClaude)
|
|
122
|
+
return override;
|
|
123
|
+
// Object.hasOwn guards against __proto__/constructor returning a truthy
|
|
124
|
+
// non-string from the plain object literal (#2041 security review).
|
|
125
|
+
if (Object.hasOwn(CLAUDE_POLICY_ID_TO_ALIAS, override)) {
|
|
126
|
+
return CLAUDE_POLICY_ID_TO_ALIAS[override];
|
|
127
|
+
}
|
|
128
|
+
if (CLAUDE_AGENT_ALIASES.has(override))
|
|
129
|
+
return override;
|
|
130
|
+
if (override.startsWith('claude-')) {
|
|
131
|
+
warnModelOverrideUnmappable(agentType, override);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return override;
|
|
135
|
+
}
|
|
81
136
|
/**
|
|
82
137
|
* #49 — Provider-neutral model policy preset resolution.
|
|
83
138
|
*/
|
|
@@ -134,11 +189,16 @@ function resolveModelPolicy(policy, tier) {
|
|
|
134
189
|
}
|
|
135
190
|
function resolveModelInternal(cwd, agentType) {
|
|
136
191
|
const config = loadConfig(cwd);
|
|
137
|
-
// 1. Per-agent override
|
|
192
|
+
// 1. Per-agent override (#2041: map Claude full IDs → Agent-tool aliases on
|
|
193
|
+
// the claude runtime, mirroring the model_policy path #1144; non-Claude
|
|
194
|
+
// runtimes and non-Claude values pass through verbatim).
|
|
138
195
|
const modelOverrides = config['model_overrides'];
|
|
139
196
|
const override = modelOverrides?.[agentType];
|
|
140
197
|
if (override) {
|
|
141
|
-
|
|
198
|
+
const mapped = mapClaudeOverrideForRuntime(override, config['runtime'], agentType);
|
|
199
|
+
if (mapped !== null)
|
|
200
|
+
return mapped;
|
|
201
|
+
// Unmappable Claude ID — fall through to tier resolution (matches model_policy).
|
|
142
202
|
}
|
|
143
203
|
// 2. Compute the tier
|
|
144
204
|
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
@@ -252,8 +312,12 @@ function resolveModelForTier(cwd, agentType, attempt) {
|
|
|
252
312
|
const attemptN = Number.isInteger(attempt) && attempt > 0 ? attempt : 0;
|
|
253
313
|
const modelOverrides = config['model_overrides'];
|
|
254
314
|
const override = modelOverrides?.[agentType];
|
|
255
|
-
if (override)
|
|
256
|
-
|
|
315
|
+
if (override) {
|
|
316
|
+
const mapped = mapClaudeOverrideForRuntime(override, config['runtime'], agentType);
|
|
317
|
+
if (mapped !== null)
|
|
318
|
+
return mapped;
|
|
319
|
+
// Unmappable Claude ID — fall through to dynamic_routing / model_policy resolution.
|
|
320
|
+
}
|
|
257
321
|
if (config['model_policy'] && config['runtime'] && config['runtime'] !== 'claude') {
|
|
258
322
|
return resolveModelInternal(cwd, agentType);
|
|
259
323
|
}
|
|
@@ -454,6 +518,7 @@ module.exports = {
|
|
|
454
518
|
resolveModelPolicy,
|
|
455
519
|
resolveModelInternal,
|
|
456
520
|
_resetModelPolicyWarningCacheForTests,
|
|
521
|
+
_resetModelOverrideWarningCacheForTests,
|
|
457
522
|
VALID_GRANULARITIES,
|
|
458
523
|
resolveGranularityInternal,
|
|
459
524
|
assertValidGranularityOverride,
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
6
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- core-utils.cjs is an export= CommonJS module
|
|
8
|
+
const coreUtils = require("./core-utils.cjs");
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
|
|
10
|
+
const planningWorkspace = require("./planning-workspace.cjs");
|
|
11
|
+
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
12
|
+
const { pathExistsInternal, toPosixPath } = coreUtils;
|
|
13
|
+
const { planningDir, planningRoot } = planningWorkspace;
|
|
14
|
+
const CODE_EXTENSIONS = new Set([
|
|
15
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.py', '.go', '.rs', '.swift', '.java',
|
|
16
|
+
'.kt', '.kts', '.c', '.cpp', '.cc', '.h', '.hpp', '.cs', '.rb', '.php', '.dart',
|
|
17
|
+
'.m', '.mm', '.scala', '.groovy', '.lua', '.r', '.R', '.zig', '.ex', '.exs', '.clj',
|
|
18
|
+
]);
|
|
19
|
+
const CODE_SCAN_SKIP_DIRS = new Set([
|
|
20
|
+
'node_modules', '.git', '.planning', '.claude', '.codex', '__pycache__', 'target',
|
|
21
|
+
'dist', 'build', '.next', '.nuxt', '.svelte-kit', 'coverage', 'vendor', '.venv', 'venv',
|
|
22
|
+
]);
|
|
23
|
+
const PACKAGE_FILES = [
|
|
24
|
+
'package.json', 'requirements.txt', 'pyproject.toml', 'Cargo.toml', 'go.mod',
|
|
25
|
+
'Package.swift', 'build.gradle', 'build.gradle.kts', 'pom.xml', 'Gemfile',
|
|
26
|
+
'composer.json', 'pubspec.yaml', 'CMakeLists.txt', 'Makefile', 'build.zig',
|
|
27
|
+
'mix.exs', 'project.clj',
|
|
28
|
+
];
|
|
29
|
+
const REQUIRED_CODEBASE_MAP_FILES = [
|
|
30
|
+
'STACK.md', 'ARCHITECTURE.md', 'STRUCTURE.md', 'CONVENTIONS.md', 'TESTING.md',
|
|
31
|
+
'INTEGRATIONS.md', 'CONCERNS.md',
|
|
32
|
+
];
|
|
33
|
+
const FAST_CODEBASE_MAP_FILES = [
|
|
34
|
+
'STACK.md', 'INTEGRATIONS.md', 'ARCHITECTURE.md', 'STRUCTURE.md',
|
|
35
|
+
];
|
|
36
|
+
const PLANNING_DOC_SEGMENTS = new Set([
|
|
37
|
+
'adr', 'adrs', 'prd', 'prds', 'spec', 'specs', 'rfc', 'rfcs',
|
|
38
|
+
]);
|
|
39
|
+
function hasCodeFilesInternal(dir, depth = 0) {
|
|
40
|
+
if (depth > 3)
|
|
41
|
+
return false;
|
|
42
|
+
let entries;
|
|
43
|
+
try {
|
|
44
|
+
entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
if (entry.isFile() && CODE_EXTENSIONS.has(node_path_1.default.extname(entry.name)))
|
|
51
|
+
return true;
|
|
52
|
+
if (entry.isDirectory() && !CODE_SCAN_SKIP_DIRS.has(entry.name)) {
|
|
53
|
+
if (hasCodeFilesInternal(node_path_1.default.join(dir, entry.name), depth + 1))
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
function hasPackageFileInternal(cwd) {
|
|
60
|
+
return PACKAGE_FILES.some((file) => pathExistsInternal(cwd, file));
|
|
61
|
+
}
|
|
62
|
+
function listPlanningDocCandidates(cwd) {
|
|
63
|
+
const roots = ['docs', 'adr', 'adrs', 'prd', 'prds', 'spec', 'specs', 'rfc', 'rfcs'];
|
|
64
|
+
const candidates = new Set();
|
|
65
|
+
function isPlanningDocCandidate(rel, name) {
|
|
66
|
+
const upperName = name.toUpperCase();
|
|
67
|
+
const relLower = rel.toLowerCase();
|
|
68
|
+
const pathSegments = relLower.split('/');
|
|
69
|
+
return (/(^|[-_ ])(ADR|PRD|SPEC|RFC)([-_ ]|\.)/i.test(name) ||
|
|
70
|
+
/^\d{4}[-_].+\.md$/i.test(name) ||
|
|
71
|
+
pathSegments.some((segment) => PLANNING_DOC_SEGMENTS.has(segment)) ||
|
|
72
|
+
upperName === 'REQUIREMENTS.MD');
|
|
73
|
+
}
|
|
74
|
+
function addCandidate(rel, name) {
|
|
75
|
+
if (name.toLowerCase().endsWith('.md') && isPlanningDocCandidate(rel, name)) {
|
|
76
|
+
candidates.add(toPosixPath(rel));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function visit(dir, relDir, depth) {
|
|
80
|
+
if (depth > 3)
|
|
81
|
+
return;
|
|
82
|
+
let entries;
|
|
83
|
+
try {
|
|
84
|
+
entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
91
|
+
if (entry.isDirectory()) {
|
|
92
|
+
if (!CODE_SCAN_SKIP_DIRS.has(entry.name)) {
|
|
93
|
+
visit(node_path_1.default.join(dir, entry.name), rel, depth + 1);
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (entry.isFile())
|
|
98
|
+
addCandidate(rel, entry.name);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let rootEntries = [];
|
|
102
|
+
try {
|
|
103
|
+
rootEntries = node_fs_1.default.readdirSync(cwd, { withFileTypes: true });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
rootEntries = [];
|
|
107
|
+
}
|
|
108
|
+
for (const entry of rootEntries) {
|
|
109
|
+
if (entry.isFile())
|
|
110
|
+
addCandidate(entry.name, entry.name);
|
|
111
|
+
}
|
|
112
|
+
for (const root of roots) {
|
|
113
|
+
const full = node_path_1.default.join(cwd, root);
|
|
114
|
+
if (node_fs_1.default.existsSync(full))
|
|
115
|
+
visit(full, root, 0);
|
|
116
|
+
}
|
|
117
|
+
return [...candidates].sort();
|
|
118
|
+
}
|
|
119
|
+
function listCodebaseMapFiles(cwd) {
|
|
120
|
+
const codebaseDir = node_path_1.default.join(planningRoot(cwd), 'codebase');
|
|
121
|
+
if (!node_fs_1.default.existsSync(codebaseDir))
|
|
122
|
+
return [];
|
|
123
|
+
return REQUIRED_CODEBASE_MAP_FILES.filter((file) => node_fs_1.default.existsSync(node_path_1.default.join(codebaseDir, file)));
|
|
124
|
+
}
|
|
125
|
+
function getMapReadiness(hasCompleteMap, hasFastMap) {
|
|
126
|
+
if (hasCompleteMap)
|
|
127
|
+
return 'complete';
|
|
128
|
+
if (hasFastMap)
|
|
129
|
+
return 'fast';
|
|
130
|
+
return 'none';
|
|
131
|
+
}
|
|
132
|
+
function mapSummaryStatus(mapReadiness) {
|
|
133
|
+
if (mapReadiness === 'complete')
|
|
134
|
+
return '.planning/codebase/ (complete codebase map)';
|
|
135
|
+
if (mapReadiness === 'fast')
|
|
136
|
+
return '.planning/codebase/ (fast/partial codebase map; complete map still required for project setup)';
|
|
137
|
+
return 'missing';
|
|
138
|
+
}
|
|
139
|
+
function mapFinalStatus(mapReadiness) {
|
|
140
|
+
if (mapReadiness === 'complete')
|
|
141
|
+
return 'complete';
|
|
142
|
+
if (mapReadiness === 'fast')
|
|
143
|
+
return 'fast/partial; complete map still required for project setup';
|
|
144
|
+
return 'missing';
|
|
145
|
+
}
|
|
146
|
+
function planningMissing(projectExists, requirementsExists, roadmapExists, stateExists) {
|
|
147
|
+
const missing = [];
|
|
148
|
+
if (!projectExists)
|
|
149
|
+
missing.push('PROJECT.md');
|
|
150
|
+
if (!requirementsExists)
|
|
151
|
+
missing.push('REQUIREMENTS.md');
|
|
152
|
+
if (!roadmapExists)
|
|
153
|
+
missing.push('ROADMAP.md');
|
|
154
|
+
if (!stateExists)
|
|
155
|
+
missing.push('STATE.md');
|
|
156
|
+
return missing;
|
|
157
|
+
}
|
|
158
|
+
function nextAction(params) {
|
|
159
|
+
if (params.isBrownfield && params.needsOnboardCodebaseMap) {
|
|
160
|
+
return {
|
|
161
|
+
kind: 'map-codebase',
|
|
162
|
+
command: params.fastMode ? params.handoffCommands.map_codebase_fast : params.handoffCommands.map_codebase,
|
|
163
|
+
reason: 'Existing code was detected, but the required .planning/codebase/ map is missing.',
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (params.hasPlanningArtifacts && params.missingPlanningFiles.length > 0) {
|
|
167
|
+
return {
|
|
168
|
+
kind: 'partial-planning',
|
|
169
|
+
missing: params.missingPlanningFiles,
|
|
170
|
+
reason: 'Project planning exists but required planning files are missing.',
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (params.fastMode && params.mapReadiness === 'fast' && !params.projectExists) {
|
|
174
|
+
return {
|
|
175
|
+
kind: 'complete-map-before-new-project',
|
|
176
|
+
command: params.handoffCommands.map_codebase,
|
|
177
|
+
reason: 'The fast map is enough for lightweight onboarding, but project setup still requires the complete codebase map.',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (params.hasDocsCandidates && !params.projectExists) {
|
|
181
|
+
return {
|
|
182
|
+
kind: 'ingest-docs',
|
|
183
|
+
command: params.handoffCommands.ingest_docs,
|
|
184
|
+
reason: 'Detected existing ADR/PRD/SPEC/RFC document(s) before project setup.',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (!params.isBrownfield && !params.projectExists && !params.hasDocsCandidates) {
|
|
188
|
+
return {
|
|
189
|
+
kind: 'new-project',
|
|
190
|
+
command: params.handoffCommands.new_project,
|
|
191
|
+
reason: 'No existing code or planning docs were detected.',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (!params.projectExists) {
|
|
195
|
+
return {
|
|
196
|
+
kind: 'new-project',
|
|
197
|
+
command: params.handoffCommands.new_project,
|
|
198
|
+
reason: 'Codebase context is ready for project initialization.',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (!params.onboardingSummaryExists) {
|
|
202
|
+
return {
|
|
203
|
+
kind: 'write-summary',
|
|
204
|
+
summary_path: params.onboardingSummaryPath,
|
|
205
|
+
reason: 'Onboarding summary is missing.',
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
kind: 'ready',
|
|
210
|
+
reason: 'Onboarding summary already exists.',
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function buildHandoffCommands(cwd) {
|
|
214
|
+
const runtime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
|
|
215
|
+
return {
|
|
216
|
+
ingest_docs: (0, runtime_slash_cjs_1.formatGsdSlash)('ingest-docs', runtime),
|
|
217
|
+
manager: (0, runtime_slash_cjs_1.formatGsdSlash)('manager', runtime),
|
|
218
|
+
map_codebase: (0, runtime_slash_cjs_1.formatGsdSlash)('map-codebase', runtime),
|
|
219
|
+
map_codebase_fast: (0, runtime_slash_cjs_1.formatGsdSlash)('map-codebase --fast', runtime),
|
|
220
|
+
new_project: (0, runtime_slash_cjs_1.formatGsdSlash)('new-project', runtime),
|
|
221
|
+
onboard: (0, runtime_slash_cjs_1.formatGsdSlash)('onboard', runtime),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function buildOnboardProjection(cwd, options) {
|
|
225
|
+
const handoffCommands = buildHandoffCommands(cwd);
|
|
226
|
+
const codebaseMapFiles = listCodebaseMapFiles(cwd);
|
|
227
|
+
const missingCodebaseMapFiles = REQUIRED_CODEBASE_MAP_FILES.filter((file) => !codebaseMapFiles.includes(file));
|
|
228
|
+
const missingFastCodebaseMapFiles = FAST_CODEBASE_MAP_FILES.filter((file) => !codebaseMapFiles.includes(file));
|
|
229
|
+
const docCandidates = listPlanningDocCandidates(cwd);
|
|
230
|
+
const hasCode = hasCodeFilesInternal(cwd);
|
|
231
|
+
const hasPackageFile = hasPackageFileInternal(cwd);
|
|
232
|
+
const isBrownfield = hasCode || hasPackageFile;
|
|
233
|
+
const hasCodebaseMap = codebaseMapFiles.length === REQUIRED_CODEBASE_MAP_FILES.length;
|
|
234
|
+
const hasFastCodebaseMap = missingFastCodebaseMapFiles.length === 0;
|
|
235
|
+
const mapReadinessValue = getMapReadiness(hasCodebaseMap, hasFastCodebaseMap);
|
|
236
|
+
const needsCodebaseMap = isBrownfield && !hasCodebaseMap;
|
|
237
|
+
const needsFastCodebaseMap = isBrownfield && !hasFastCodebaseMap;
|
|
238
|
+
const needsOnboardCodebaseMap = options.fast ? needsFastCodebaseMap : needsCodebaseMap;
|
|
239
|
+
const projectRootPath = node_path_1.default.join(planningRoot(cwd), 'PROJECT.md');
|
|
240
|
+
const projectScopedPath = node_path_1.default.join(planningDir(cwd), 'PROJECT.md');
|
|
241
|
+
const projectExists = node_fs_1.default.existsSync(projectRootPath) || node_fs_1.default.existsSync(projectScopedPath);
|
|
242
|
+
const requirementsExists = node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md'));
|
|
243
|
+
const roadmapExists = node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'));
|
|
244
|
+
const stateExists = node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md'));
|
|
245
|
+
const onboardingSummaryPath = node_path_1.default.join(planningRoot(cwd), 'onboarding', 'SUMMARY.md');
|
|
246
|
+
const onboardingSummaryExists = node_fs_1.default.existsSync(onboardingSummaryPath);
|
|
247
|
+
const hasPlanningArtifacts = projectExists || requirementsExists || roadmapExists || stateExists;
|
|
248
|
+
const missingPlanningFiles = planningMissing(projectExists, requirementsExists, roadmapExists, stateExists);
|
|
249
|
+
return {
|
|
250
|
+
commit_docs: options.commitDocs,
|
|
251
|
+
text_mode: options.textMode,
|
|
252
|
+
project_exists: projectExists,
|
|
253
|
+
planning_exists: node_fs_1.default.existsSync(planningRoot(cwd)),
|
|
254
|
+
requirements_exists: requirementsExists,
|
|
255
|
+
roadmap_exists: roadmapExists,
|
|
256
|
+
state_exists: stateExists,
|
|
257
|
+
config_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'config.json')),
|
|
258
|
+
handoff_commands: handoffCommands,
|
|
259
|
+
has_existing_code: hasCode,
|
|
260
|
+
has_package_file: hasPackageFile,
|
|
261
|
+
is_brownfield: isBrownfield,
|
|
262
|
+
fast_mode: options.fast,
|
|
263
|
+
map_readiness: mapReadinessValue,
|
|
264
|
+
next_action: nextAction({
|
|
265
|
+
fastMode: options.fast,
|
|
266
|
+
isBrownfield,
|
|
267
|
+
needsOnboardCodebaseMap,
|
|
268
|
+
hasDocsCandidates: docCandidates.length > 0,
|
|
269
|
+
projectExists,
|
|
270
|
+
mapReadiness: mapReadinessValue,
|
|
271
|
+
onboardingSummaryExists,
|
|
272
|
+
onboardingSummaryPath: toPosixPath(node_path_1.default.relative(cwd, onboardingSummaryPath)),
|
|
273
|
+
hasPlanningArtifacts,
|
|
274
|
+
missingPlanningFiles,
|
|
275
|
+
handoffCommands,
|
|
276
|
+
}),
|
|
277
|
+
needs_codebase_map: needsCodebaseMap,
|
|
278
|
+
needs_fast_codebase_map: needsFastCodebaseMap,
|
|
279
|
+
has_codebase_map: hasCodebaseMap,
|
|
280
|
+
has_fast_codebase_map: hasFastCodebaseMap,
|
|
281
|
+
codebase_dir_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningRoot(cwd), 'codebase')),
|
|
282
|
+
fast_codebase_map_files_required: FAST_CODEBASE_MAP_FILES,
|
|
283
|
+
codebase_map_files_present: codebaseMapFiles,
|
|
284
|
+
missing_codebase_map_files: missingCodebaseMapFiles,
|
|
285
|
+
missing_fast_codebase_map_files: missingFastCodebaseMapFiles,
|
|
286
|
+
codebase_map_summary_status: mapSummaryStatus(mapReadinessValue),
|
|
287
|
+
codebase_map_final_status: mapFinalStatus(mapReadinessValue),
|
|
288
|
+
has_docs_candidates: docCandidates.length > 0,
|
|
289
|
+
doc_candidate_count: docCandidates.length,
|
|
290
|
+
doc_candidates: docCandidates,
|
|
291
|
+
onboarding_summary_exists: onboardingSummaryExists,
|
|
292
|
+
onboarding_summary_path: toPosixPath(node_path_1.default.relative(cwd, onboardingSummaryPath)),
|
|
293
|
+
project_path: toPosixPath(node_path_1.default.relative(cwd, node_fs_1.default.existsSync(projectRootPath) ? projectRootPath : projectScopedPath)),
|
|
294
|
+
requirements_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md'))),
|
|
295
|
+
roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
|
|
296
|
+
state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
|
|
297
|
+
codebase_dir: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningRoot(cwd), 'codebase'))),
|
|
298
|
+
onboarding_dir: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningRoot(cwd), 'onboarding'))),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
module.exports = {
|
|
302
|
+
REQUIRED_CODEBASE_MAP_FILES,
|
|
303
|
+
FAST_CODEBASE_MAP_FILES,
|
|
304
|
+
buildOnboardProjection,
|
|
305
|
+
hasCodeFilesInternal,
|
|
306
|
+
hasPackageFileInternal,
|
|
307
|
+
listCodebaseMapFiles,
|
|
308
|
+
listPlanningDocCandidates,
|
|
309
|
+
};
|