@opengsd/gsd-core 1.2.0 → 1.3.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/README.ja-JP.md +46 -791
- package/README.ko-KR.md +44 -780
- package/README.md +42 -218
- package/README.pt-BR.md +47 -414
- package/README.zh-CN.md +46 -763
- package/agents/gsd-ai-researcher.md +2 -2
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +8 -8
- package/agents/gsd-doc-writer.md +7 -6
- package/agents/gsd-domain-researcher.md +15 -2
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-eval-planner.md +2 -2
- package/agents/gsd-executor.md +21 -8
- package/agents/gsd-framework-selector.md +1 -1
- package/agents/gsd-intel-updater.md +4 -4
- package/agents/gsd-phase-researcher.md +19 -6
- package/agents/gsd-plan-checker.md +3 -3
- package/agents/gsd-planner.md +36 -23
- package/agents/gsd-project-researcher.md +13 -0
- package/agents/gsd-research-synthesizer.md +6 -2
- package/agents/gsd-roadmapper.md +6 -6
- package/agents/gsd-ui-researcher.md +16 -3
- package/agents/gsd-user-profiler.md +2 -2
- package/agents/gsd-verifier.md +8 -8
- package/assets/gsd-logo-2000-transparent.png +0 -0
- package/assets/gsd-logo-2000-transparent.svg +17 -0
- package/assets/gsd-logo-2000.png +0 -0
- package/assets/gsd-logo-2000.svg +21 -0
- package/assets/terminal.svg +68 -0
- package/bin/install.js +255 -103
- package/commands/gsd/add-tests.md +1 -1
- package/commands/gsd/ai-integration-phase.md +3 -3
- package/commands/gsd/audit-fix.md +1 -1
- package/commands/gsd/audit-milestone.md +1 -1
- package/commands/gsd/audit-uat.md +1 -1
- package/commands/gsd/autonomous.md +2 -2
- package/commands/gsd/capture.md +6 -6
- package/commands/gsd/cleanup.md +1 -1
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/complete-milestone.md +2 -2
- package/commands/gsd/config.md +3 -3
- package/commands/gsd/debug.md +1 -1
- package/commands/gsd/discuss-phase.md +5 -4
- package/commands/gsd/docs-update.md +1 -1
- package/commands/gsd/eval-review.md +2 -2
- package/commands/gsd/execute-phase.md +2 -2
- package/commands/gsd/explore.md +1 -1
- package/commands/gsd/extract-learnings.md +2 -2
- package/commands/gsd/fast.md +1 -1
- package/commands/gsd/forensics.md +1 -1
- package/commands/gsd/graphify.md +9 -9
- package/commands/gsd/health.md +1 -1
- package/commands/gsd/help.md +2 -2
- package/commands/gsd/import.md +5 -5
- package/commands/gsd/inbox.md +1 -1
- package/commands/gsd/ingest-docs.md +4 -4
- package/commands/gsd/manager.md +3 -3
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/milestone-summary.md +1 -1
- package/commands/gsd/mvp-phase.md +4 -4
- package/commands/gsd/new-milestone.md +5 -5
- package/commands/gsd/new-project.md +5 -5
- package/commands/gsd/pause-work.md +2 -2
- package/commands/gsd/phase.md +4 -4
- package/commands/gsd/plan-phase.md +2 -2
- package/commands/gsd/plan-review-convergence.md +4 -4
- package/commands/gsd/pr-branch.md +1 -1
- package/commands/gsd/profile-user.md +2 -2
- package/commands/gsd/progress.md +4 -4
- package/commands/gsd/quick.md +1 -1
- package/commands/gsd/resume-work.md +1 -1
- package/commands/gsd/review.md +3 -2
- package/commands/gsd/secure-phase.md +1 -1
- package/commands/gsd/settings.md +1 -1
- package/commands/gsd/ship.md +2 -2
- package/commands/gsd/sketch.md +7 -7
- package/commands/gsd/spec-phase.md +2 -2
- package/commands/gsd/spike.md +3 -3
- package/commands/gsd/stats.md +1 -1
- package/commands/gsd/surface.md +4 -4
- package/commands/gsd/thread.md +1 -1
- package/commands/gsd/ui-phase.md +2 -2
- package/commands/gsd/ui-review.md +2 -2
- package/commands/gsd/ultraplan-phase.md +2 -2
- package/commands/gsd/undo.md +3 -3
- package/commands/gsd/update.md +3 -3
- package/commands/gsd/validate-phase.md +1 -1
- package/commands/gsd/verify-work.md +2 -2
- package/commands/gsd/workspace.md +4 -4
- package/{get-shit-done → gsd-core}/bin/check-latest-version.cjs +1 -1
- package/{get-shit-done → gsd-core}/bin/gsd-tools.cjs +50 -4
- package/gsd-core/bin/lib/active-workstream-store.cjs +291 -0
- package/gsd-core/bin/lib/adr-parser.cjs +399 -0
- package/gsd-core/bin/lib/agent-command-router.cjs +68 -0
- package/gsd-core/bin/lib/artifacts.cjs +51 -0
- package/gsd-core/bin/lib/audit.cjs +743 -0
- package/gsd-core/bin/lib/check-command-router.cjs +342 -0
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +81 -0
- package/gsd-core/bin/lib/clock.cjs +95 -0
- package/gsd-core/bin/lib/clusters.cjs +128 -0
- package/gsd-core/bin/lib/code-review-flags.cjs +59 -0
- package/gsd-core/bin/lib/command-aliases.cjs +809 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +55 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +300 -0
- package/gsd-core/bin/lib/commands.cjs +1202 -0
- package/gsd-core/bin/lib/config-schema.cjs +29 -0
- package/gsd-core/bin/lib/config-types.cjs +19 -0
- package/gsd-core/bin/lib/config.cjs +714 -0
- package/{get-shit-done → gsd-core}/bin/lib/configuration.cjs +76 -85
- package/gsd-core/bin/lib/context-utilization.cjs +48 -0
- package/gsd-core/bin/lib/core.cjs +2007 -0
- package/{get-shit-done → gsd-core}/bin/lib/decisions.cjs +9 -7
- package/gsd-core/bin/lib/docs.cjs +252 -0
- package/gsd-core/bin/lib/drift.cjs +364 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +115 -0
- package/gsd-core/bin/lib/frontmatter.cjs +442 -0
- package/gsd-core/bin/lib/gap-checker.cjs +257 -0
- package/gsd-core/bin/lib/graphify.cjs +496 -0
- package/gsd-core/bin/lib/gsd2-import.cjs +456 -0
- package/gsd-core/bin/lib/init-command-router.cjs +62 -0
- package/gsd-core/bin/lib/init.cjs +1799 -0
- package/gsd-core/bin/lib/install-profiles.cjs +526 -0
- package/gsd-core/bin/lib/installer-migration-authoring.cjs +122 -0
- package/gsd-core/bin/lib/installer-migration-report.cjs +347 -0
- package/gsd-core/bin/lib/installer-migrations/000-first-time-baseline.cjs +218 -0
- package/gsd-core/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +48 -0
- package/gsd-core/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +94 -0
- package/gsd-core/bin/lib/installer-migrations/003-rename-get-shit-done-to-gsd-core.cjs +108 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +784 -0
- package/gsd-core/bin/lib/intel.cjs +607 -0
- package/gsd-core/bin/lib/learnings.cjs +270 -0
- package/gsd-core/bin/lib/legacy-cleanup.cjs +253 -0
- package/gsd-core/bin/lib/milestone.cjs +373 -0
- package/gsd-core/bin/lib/model-catalog.cjs +154 -0
- package/gsd-core/bin/lib/model-profiles.cjs +24 -0
- package/gsd-core/bin/lib/observability/event.cjs +51 -0
- package/gsd-core/bin/lib/observability/logger.cjs +146 -0
- package/{get-shit-done → gsd-core}/bin/lib/observability/redaction.cjs +19 -21
- package/{get-shit-done → gsd-core}/bin/lib/package-identity.cjs +5 -1
- package/gsd-core/bin/lib/phase-command-router.cjs +189 -0
- package/gsd-core/bin/lib/phase-lifecycle.cjs +74 -0
- package/gsd-core/bin/lib/phase.cjs +1307 -0
- package/gsd-core/bin/lib/phases-command-router.cjs +43 -0
- package/{get-shit-done → gsd-core}/bin/lib/plan-scan.cjs +18 -24
- package/gsd-core/bin/lib/planning-workspace.cjs +245 -0
- package/gsd-core/bin/lib/profile-output.cjs +1120 -0
- package/gsd-core/bin/lib/profile-pipeline.cjs +517 -0
- package/gsd-core/bin/lib/project-root.cjs +119 -0
- package/gsd-core/bin/lib/prompt-budget.cjs +305 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +121 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +166 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +491 -0
- package/gsd-core/bin/lib/roadmap.cjs +600 -0
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +254 -0
- package/gsd-core/bin/lib/runtime-homes.cjs +169 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +96 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +119 -0
- package/{get-shit-done → gsd-core}/bin/lib/schema-detect.cjs +20 -26
- package/gsd-core/bin/lib/secrets.cjs +34 -0
- package/gsd-core/bin/lib/security.cjs +407 -0
- package/gsd-core/bin/lib/semver-compare.cjs +42 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +524 -0
- package/gsd-core/bin/lib/state-command-router.cjs +160 -0
- package/{get-shit-done → gsd-core}/bin/lib/state-document.cjs +78 -82
- package/gsd-core/bin/lib/state.cjs +2010 -0
- package/gsd-core/bin/lib/surface.cjs +406 -0
- package/gsd-core/bin/lib/task-command-router.cjs +85 -0
- package/gsd-core/bin/lib/template.cjs +237 -0
- package/gsd-core/bin/lib/uat.cjs +297 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +98 -0
- package/gsd-core/bin/lib/update-context.cjs +218 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +91 -0
- package/gsd-core/bin/lib/validate.cjs +112 -0
- package/gsd-core/bin/lib/verify-command-router.cjs +44 -0
- package/gsd-core/bin/lib/verify.cjs +1451 -0
- package/{get-shit-done → gsd-core}/bin/lib/workstream-inventory-builder.cjs +19 -12
- package/gsd-core/bin/lib/workstream-inventory.cjs +147 -0
- package/{get-shit-done → gsd-core}/bin/lib/workstream-name-policy.cjs +28 -31
- package/gsd-core/bin/lib/workstream.cjs +380 -0
- package/gsd-core/bin/lib/worktree-safety.cjs +912 -0
- package/{get-shit-done → gsd-core}/bin/shared/config-defaults.manifest.json +1 -0
- package/{get-shit-done → gsd-core}/bin/shared/config-schema.manifest.json +17 -1
- package/{get-shit-done → gsd-core}/bin/shared/model-catalog.json +27 -0
- package/{get-shit-done → gsd-core}/references/artifact-types.md +1 -1
- package/{get-shit-done → gsd-core}/references/checkpoints.md +1 -1
- package/{get-shit-done → gsd-core}/references/few-shot-examples/plan-checker.md +2 -2
- package/{get-shit-done → gsd-core}/references/few-shot-examples/verifier.md +12 -12
- package/{get-shit-done → gsd-core}/references/model-profile-resolution.md +1 -1
- package/{get-shit-done → gsd-core}/references/mvp-concepts.md +1 -1
- package/{get-shit-done → gsd-core}/references/planner-graphify-auto-update.md +2 -2
- package/{get-shit-done → gsd-core}/references/planner-human-verify-mode.md +1 -1
- package/{get-shit-done → gsd-core}/references/planner-mvp-mode.md +1 -1
- package/{get-shit-done → gsd-core}/references/ui-brand.md +4 -2
- package/{get-shit-done → gsd-core}/references/verification-patterns.md +1 -1
- package/gsd-core/references/worktree-branch-check.md +38 -0
- package/{get-shit-done → gsd-core}/references/worktree-path-safety.md +4 -26
- package/{get-shit-done → gsd-core}/templates/README.md +2 -2
- package/{get-shit-done → gsd-core}/templates/codebase/structure.md +10 -10
- package/{get-shit-done → gsd-core}/templates/copilot-instructions.md +1 -1
- package/{get-shit-done → gsd-core}/templates/phase-prompt.md +10 -10
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -0
- package/{get-shit-done → gsd-core}/workflows/add-backlog.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/add-phase.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/add-tests.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/add-todo.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/ai-integration-phase.md +4 -4
- package/{get-shit-done → gsd-core}/workflows/audit-fix.md +2 -2
- package/{get-shit-done → gsd-core}/workflows/audit-milestone.md +3 -1
- package/{get-shit-done → gsd-core}/workflows/audit-uat.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/autonomous.md +10 -8
- package/{get-shit-done → gsd-core}/workflows/check-todos.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/cleanup.md +44 -4
- package/{get-shit-done → gsd-core}/workflows/code-review-fix.md +4 -4
- package/{get-shit-done → gsd-core}/workflows/code-review.md +6 -4
- package/{get-shit-done → gsd-core}/workflows/complete-milestone.md +2 -2
- package/{get-shit-done → gsd-core}/workflows/debug.md +7 -2
- package/{get-shit-done → gsd-core}/workflows/diagnose-issues.md +6 -2
- package/{get-shit-done → gsd-core}/workflows/discovery-phase.md +5 -5
- package/{get-shit-done → gsd-core}/workflows/discuss-phase/modes/advisor.md +5 -5
- package/{get-shit-done → gsd-core}/workflows/discuss-phase/modes/auto.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/discuss-phase/modes/chain.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/discuss-phase/modes/power.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/discuss-phase-assumptions.md +4 -4
- package/{get-shit-done → gsd-core}/workflows/discuss-phase.md +6 -6
- package/{get-shit-done → gsd-core}/workflows/do.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/docs-update.md +19 -5
- package/{get-shit-done → gsd-core}/workflows/edit-phase.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/eval-review.md +3 -3
- package/{get-shit-done → gsd-core}/workflows/execute-phase/steps/codebase-drift-gate.md +16 -3
- package/{get-shit-done → gsd-core}/workflows/execute-phase/steps/post-merge-gate.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/execute-phase.md +89 -57
- package/{get-shit-done → gsd-core}/workflows/execute-plan.md +8 -8
- package/{get-shit-done → gsd-core}/workflows/explore.md +5 -3
- package/{get-shit-done → gsd-core}/workflows/extract-learnings.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/forensics.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/graduation.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/health.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/help/modes/default.md +3 -3
- package/{get-shit-done → gsd-core}/workflows/help/modes/full.md +6 -6
- package/{get-shit-done → gsd-core}/workflows/import.md +3 -1
- package/{get-shit-done → gsd-core}/workflows/ingest-docs.md +5 -5
- package/{get-shit-done → gsd-core}/workflows/insert-phase.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/list-workspaces.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/manager.md +3 -3
- package/{get-shit-done → gsd-core}/workflows/map-codebase.md +3 -1
- package/{get-shit-done → gsd-core}/workflows/milestone-summary.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/mvp-phase.md +7 -7
- package/{get-shit-done → gsd-core}/workflows/new-milestone.md +5 -5
- package/{get-shit-done → gsd-core}/workflows/new-project.md +8 -8
- package/{get-shit-done → gsd-core}/workflows/new-workspace.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/next.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/pause-work.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/plan-milestone-gaps.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/plan-phase.md +29 -28
- package/{get-shit-done → gsd-core}/workflows/plan-review-convergence.md +10 -10
- package/{get-shit-done → gsd-core}/workflows/plant-seed.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/profile-user.md +11 -11
- package/{get-shit-done → gsd-core}/workflows/progress.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/quick.md +7 -29
- package/{get-shit-done → gsd-core}/workflows/reapply-patches.md +2 -2
- package/{get-shit-done → gsd-core}/workflows/remove-phase.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/remove-workspace.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/resume-project.md +2 -2
- package/{get-shit-done → gsd-core}/workflows/review.md +110 -2
- package/{get-shit-done → gsd-core}/workflows/scan.md +3 -1
- package/{get-shit-done → gsd-core}/workflows/secure-phase.md +5 -3
- package/{get-shit-done → gsd-core}/workflows/settings-advanced.md +196 -6
- package/{get-shit-done → gsd-core}/workflows/settings-integrations.md +2 -2
- package/{get-shit-done → gsd-core}/workflows/settings.md +16 -2
- package/{get-shit-done → gsd-core}/workflows/ship.md +56 -4
- package/{get-shit-done → gsd-core}/workflows/sketch-wrap-up.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/sketch.md +5 -5
- package/{get-shit-done → gsd-core}/workflows/spec-phase.md +2 -2
- package/{get-shit-done → gsd-core}/workflows/spike-wrap-up.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/spike.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/stats.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/sync-skills.md +3 -3
- package/{get-shit-done → gsd-core}/workflows/thread.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/transition.md +2 -2
- package/{get-shit-done → gsd-core}/workflows/ui-phase.md +5 -5
- package/{get-shit-done → gsd-core}/workflows/ui-review.md +3 -3
- package/{get-shit-done → gsd-core}/workflows/ultraplan-phase.md +1 -1
- package/{get-shit-done → gsd-core}/workflows/undo.md +2 -2
- package/{get-shit-done → gsd-core}/workflows/update.md +41 -16
- package/{get-shit-done → gsd-core}/workflows/validate-phase.md +5 -3
- package/{get-shit-done → gsd-core}/workflows/verify-phase.md +4 -4
- package/{get-shit-done → gsd-core}/workflows/verify-work.md +7 -7
- package/hooks/dist/gsd-check-update-worker.js +16 -3
- package/hooks/dist/gsd-check-update.js +7 -5
- package/hooks/dist/gsd-context-monitor.js +2 -2
- package/hooks/dist/gsd-statusline.js +39 -21
- package/hooks/dist/gsd-update-banner.js +6 -2
- package/hooks/dist/gsd-worktree-path-guard.js +169 -0
- package/hooks/dist/managed-hooks-registry.cjs +35 -0
- package/hooks/gsd-check-update-worker.js +16 -3
- package/hooks/gsd-check-update.js +7 -5
- package/hooks/gsd-context-monitor.js +2 -2
- package/hooks/gsd-statusline.js +39 -21
- package/hooks/gsd-update-banner.js +6 -2
- package/hooks/gsd-worktree-path-guard.js +169 -0
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +17 -11
- package/scripts/affected-tests-lib.cjs +1 -1
- package/scripts/base64-scan.sh +12 -0
- package/scripts/build-hooks.js +5 -1
- package/scripts/changeset/cli.cjs +2 -2
- package/scripts/changeset/github-release-notes.cjs +1 -1
- package/scripts/changeset/lint.cjs +1 -1
- package/scripts/check-alias-drift.cjs +8 -8
- package/scripts/check-env.cjs +5 -1
- package/scripts/ci-test-scope.cjs +34 -9
- package/scripts/command-contract-helpers.cjs +1 -1
- package/scripts/fix-slash-commands.cjs +5 -5
- package/scripts/gen-inventory-manifest.cjs +3 -3
- package/scripts/generate-package-identity.cjs +28 -7
- package/scripts/lib/allowlist-ratchet.cjs +136 -0
- package/scripts/lint-command-contract.cjs +1 -1
- package/scripts/lint-legacy-dir-name.cjs +156 -0
- package/scripts/lint-package-identity-drift.cjs +8 -8
- package/scripts/lint-pr-check-project-dir.cjs +0 -1
- package/scripts/lint-shell-command-projection-drift.cjs +1 -1
- package/scripts/lint-skill-deps.cjs +1 -1
- package/scripts/lint-test-file-count.allowlist.json +129 -32
- package/scripts/lint-test-file-count.cjs +85 -30
- package/scripts/mutation-matrix.cjs +219 -0
- package/scripts/prompt-injection-scan.sh +5 -1
- package/scripts/release-notes/format-github-release-notes.cjs +256 -0
- package/scripts/release-tarball-smoke.cjs +9 -9
- package/scripts/run-tests.cjs +34 -2
- package/scripts/strip-prose-atrefs.cjs +2 -2
- package/scripts/sync-runtime-launcher.cjs +2 -2
- package/scripts/verify-npm-publish.cjs +252 -0
- package/get-shit-done/bin/lib/active-workstream-store.cjs +0 -302
- package/get-shit-done/bin/lib/adr-parser.cjs +0 -394
- package/get-shit-done/bin/lib/agent-command-router.cjs +0 -65
- package/get-shit-done/bin/lib/artifacts.cjs +0 -53
- package/get-shit-done/bin/lib/audit.cjs +0 -755
- package/get-shit-done/bin/lib/check-command-router.cjs +0 -333
- package/get-shit-done/bin/lib/cjs-command-router-adapter.cjs +0 -118
- package/get-shit-done/bin/lib/clock.cjs +0 -96
- package/get-shit-done/bin/lib/clusters.cjs +0 -135
- package/get-shit-done/bin/lib/code-review-flags.cjs +0 -74
- package/get-shit-done/bin/lib/command-aliases.cjs +0 -815
- package/get-shit-done/bin/lib/command-arg-projection.cjs +0 -62
- package/get-shit-done/bin/lib/command-routing-hub.cjs +0 -388
- package/get-shit-done/bin/lib/commands.cjs +0 -1188
- package/get-shit-done/bin/lib/config-schema.cjs +0 -31
- package/get-shit-done/bin/lib/config.cjs +0 -728
- package/get-shit-done/bin/lib/context-utilization.cjs +0 -47
- package/get-shit-done/bin/lib/core.cjs +0 -2121
- package/get-shit-done/bin/lib/docs.cjs +0 -270
- package/get-shit-done/bin/lib/drift.cjs +0 -388
- package/get-shit-done/bin/lib/fallow-runner.cjs +0 -109
- package/get-shit-done/bin/lib/frontmatter.cjs +0 -389
- package/get-shit-done/bin/lib/gap-checker.cjs +0 -205
- package/get-shit-done/bin/lib/graphify.cjs +0 -592
- package/get-shit-done/bin/lib/gsd2-import.cjs +0 -514
- package/get-shit-done/bin/lib/init-command-router.cjs +0 -58
- package/get-shit-done/bin/lib/init.cjs +0 -2112
- package/get-shit-done/bin/lib/install-profiles.cjs +0 -603
- package/get-shit-done/bin/lib/installer-migration-authoring.cjs +0 -117
- package/get-shit-done/bin/lib/installer-migration-report.cjs +0 -354
- package/get-shit-done/bin/lib/installer-migrations/000-first-time-baseline.cjs +0 -220
- package/get-shit-done/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +0 -41
- package/get-shit-done/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +0 -80
- package/get-shit-done/bin/lib/installer-migrations.cjs +0 -778
- package/get-shit-done/bin/lib/intel.cjs +0 -708
- package/get-shit-done/bin/lib/learnings.cjs +0 -421
- package/get-shit-done/bin/lib/milestone.cjs +0 -314
- package/get-shit-done/bin/lib/model-catalog.cjs +0 -212
- package/get-shit-done/bin/lib/model-profiles.cjs +0 -31
- package/get-shit-done/bin/lib/observability/event.cjs +0 -82
- package/get-shit-done/bin/lib/observability/logger.cjs +0 -174
- package/get-shit-done/bin/lib/phase-command-router.cjs +0 -191
- package/get-shit-done/bin/lib/phase-lifecycle.cjs +0 -80
- package/get-shit-done/bin/lib/phase.cjs +0 -1607
- package/get-shit-done/bin/lib/phases-command-router.cjs +0 -39
- package/get-shit-done/bin/lib/planning-workspace.cjs +0 -238
- package/get-shit-done/bin/lib/profile-output.cjs +0 -1141
- package/get-shit-done/bin/lib/profile-pipeline.cjs +0 -539
- package/get-shit-done/bin/lib/project-root.cjs +0 -112
- package/get-shit-done/bin/lib/prompt-budget.cjs +0 -399
- package/get-shit-done/bin/lib/review-reviewer-selection.cjs +0 -125
- package/get-shit-done/bin/lib/roadmap-command-router.cjs +0 -28
- package/get-shit-done/bin/lib/roadmap.cjs +0 -650
- package/get-shit-done/bin/lib/runtime-artifact-layout.cjs +0 -301
- package/get-shit-done/bin/lib/runtime-homes.cjs +0 -222
- package/get-shit-done/bin/lib/runtime-name-policy.cjs +0 -83
- package/get-shit-done/bin/lib/runtime-slash.cjs +0 -112
- package/get-shit-done/bin/lib/secrets.cjs +0 -32
- package/get-shit-done/bin/lib/security.cjs +0 -600
- package/get-shit-done/bin/lib/semver-compare.cjs +0 -35
- package/get-shit-done/bin/lib/shell-command-projection.cjs +0 -500
- package/get-shit-done/bin/lib/state-command-router.cjs +0 -252
- package/get-shit-done/bin/lib/state.cjs +0 -2038
- package/get-shit-done/bin/lib/surface.cjs +0 -470
- package/get-shit-done/bin/lib/task-command-router.cjs +0 -81
- package/get-shit-done/bin/lib/template.cjs +0 -228
- package/get-shit-done/bin/lib/uat.cjs +0 -289
- package/get-shit-done/bin/lib/update-context.cjs +0 -209
- package/get-shit-done/bin/lib/validate-command-router.cjs +0 -83
- package/get-shit-done/bin/lib/validate.cjs +0 -92
- package/get-shit-done/bin/lib/verify-command-router.cjs +0 -40
- package/get-shit-done/bin/lib/verify.cjs +0 -1511
- package/get-shit-done/bin/lib/workstream-inventory.cjs +0 -146
- package/get-shit-done/bin/lib/workstream.cjs +0 -389
- package/get-shit-done/bin/lib/worktree-safety.cjs +0 -985
- package/get-shit-done/workflows/_runtime-launcher.snippet.sh +0 -1
- package/scripts/lint-no-source-grep-extras.cjs +0 -81
- package/scripts/lint-no-source-grep.cjs +0 -174
- package/scripts/lint-shared-module-handsync.cjs +0 -388
- package/scripts/shared-module-handsync-allowlist.json +0 -183
- /package/{get-shit-done → gsd-core}/bin/shared/runtime-aliases.manifest.json +0 -0
- /package/{get-shit-done → gsd-core}/bin/verify-reapply-patches.cjs +0 -0
- /package/{get-shit-done → gsd-core}/contexts/dev.md +0 -0
- /package/{get-shit-done → gsd-core}/contexts/research.md +0 -0
- /package/{get-shit-done → gsd-core}/contexts/review.md +0 -0
- /package/{get-shit-done → gsd-core}/references/agent-contracts.md +0 -0
- /package/{get-shit-done → gsd-core}/references/ai-evals.md +0 -0
- /package/{get-shit-done → gsd-core}/references/ai-frameworks.md +0 -0
- /package/{get-shit-done → gsd-core}/references/autonomous-smart-discuss.md +0 -0
- /package/{get-shit-done → gsd-core}/references/common-bug-patterns.md +0 -0
- /package/{get-shit-done → gsd-core}/references/context-budget.md +0 -0
- /package/{get-shit-done → gsd-core}/references/continuation-format.md +0 -0
- /package/{get-shit-done → gsd-core}/references/debugger-philosophy.md +0 -0
- /package/{get-shit-done → gsd-core}/references/decimal-phase-calculation.md +0 -0
- /package/{get-shit-done → gsd-core}/references/doc-conflict-engine.md +0 -0
- /package/{get-shit-done → gsd-core}/references/domain-probes.md +0 -0
- /package/{get-shit-done → gsd-core}/references/execute-mvp-tdd.md +0 -0
- /package/{get-shit-done → gsd-core}/references/executor-examples.md +0 -0
- /package/{get-shit-done → gsd-core}/references/gate-prompts.md +0 -0
- /package/{get-shit-done → gsd-core}/references/gates.md +0 -0
- /package/{get-shit-done → gsd-core}/references/git-integration.md +0 -0
- /package/{get-shit-done → gsd-core}/references/git-planning-commit.md +0 -0
- /package/{get-shit-done → gsd-core}/references/ios-scaffold.md +0 -0
- /package/{get-shit-done → gsd-core}/references/mandatory-initial-read.md +0 -0
- /package/{get-shit-done → gsd-core}/references/model-profiles.md +0 -0
- /package/{get-shit-done → gsd-core}/references/phase-argument-parsing.md +0 -0
- /package/{get-shit-done → gsd-core}/references/planner-antipatterns.md +0 -0
- /package/{get-shit-done → gsd-core}/references/planner-chunked.md +0 -0
- /package/{get-shit-done → gsd-core}/references/planner-gap-closure.md +0 -0
- /package/{get-shit-done → gsd-core}/references/planner-interface-context.md +0 -0
- /package/{get-shit-done → gsd-core}/references/planner-reviews.md +0 -0
- /package/{get-shit-done → gsd-core}/references/planner-revision.md +0 -0
- /package/{get-shit-done → gsd-core}/references/planner-source-audit.md +0 -0
- /package/{get-shit-done → gsd-core}/references/planning-config.md +0 -0
- /package/{get-shit-done → gsd-core}/references/project-skills-discovery.md +0 -0
- /package/{get-shit-done → gsd-core}/references/questioning.md +0 -0
- /package/{get-shit-done → gsd-core}/references/revision-loop.md +0 -0
- /package/{get-shit-done → gsd-core}/references/scout-codebase.md +0 -0
- /package/{get-shit-done → gsd-core}/references/skeleton-template.md +0 -0
- /package/{get-shit-done → gsd-core}/references/sketch-interactivity.md +0 -0
- /package/{get-shit-done → gsd-core}/references/sketch-theme-system.md +0 -0
- /package/{get-shit-done → gsd-core}/references/sketch-tooling.md +0 -0
- /package/{get-shit-done → gsd-core}/references/sketch-variant-patterns.md +0 -0
- /package/{get-shit-done → gsd-core}/references/spidr-splitting.md +0 -0
- /package/{get-shit-done → gsd-core}/references/tdd.md +0 -0
- /package/{get-shit-done → gsd-core}/references/thinking-models-debug.md +0 -0
- /package/{get-shit-done → gsd-core}/references/thinking-models-execution.md +0 -0
- /package/{get-shit-done → gsd-core}/references/thinking-models-planning.md +0 -0
- /package/{get-shit-done → gsd-core}/references/thinking-models-research.md +0 -0
- /package/{get-shit-done → gsd-core}/references/thinking-models-verification.md +0 -0
- /package/{get-shit-done → gsd-core}/references/thinking-partner.md +0 -0
- /package/{get-shit-done → gsd-core}/references/universal-anti-patterns.md +0 -0
- /package/{get-shit-done → gsd-core}/references/user-profiling.md +0 -0
- /package/{get-shit-done → gsd-core}/references/user-story-template.md +0 -0
- /package/{get-shit-done → gsd-core}/references/verification-overrides.md +0 -0
- /package/{get-shit-done → gsd-core}/references/verify-mvp-mode.md +0 -0
- /package/{get-shit-done → gsd-core}/references/workstream-flag.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/AI-SPEC.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/DEBUG.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/SECURITY.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/UAT.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/UI-SPEC.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/VALIDATION.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/claude-md.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/codebase/architecture.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/codebase/concerns.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/codebase/conventions.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/codebase/integrations.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/codebase/stack.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/codebase/testing.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/config.json +0 -0
- /package/{get-shit-done → gsd-core}/templates/context.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/continue-here.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/debug-subagent-prompt.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/dev-preferences.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/discovery.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/discussion-log.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/milestone-archive.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/milestone.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/planner-subagent-prompt.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/project.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/requirements.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/research-project/ARCHITECTURE.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/research-project/FEATURES.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/research-project/PITFALLS.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/research-project/STACK.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/research-project/SUMMARY.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/research.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/retrospective.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/roadmap.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/spec.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/state.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/summary-complex.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/summary-minimal.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/summary-standard.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/summary.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/user-profile.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/user-setup.md +0 -0
- /package/{get-shit-done → gsd-core}/templates/verification-report.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/analyze-dependencies.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/discuss-phase/modes/all.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/discuss-phase/modes/analyze.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/discuss-phase/modes/batch.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/discuss-phase/modes/default.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/discuss-phase/modes/text.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/discuss-phase/templates/checkpoint.json +0 -0
- /package/{get-shit-done → gsd-core}/workflows/discuss-phase/templates/context.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/discuss-phase/templates/discussion-log.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/discuss-phase-power.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/execute-phase/steps/per-plan-worktree-gate.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/fast.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/help/modes/brief.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/help/modes/topic.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/help.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/inbox.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/list-phase-assumptions.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/node-repair.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/note.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/pr-branch.md +0 -0
- /package/{get-shit-done → gsd-core}/workflows/session-report.md +0 -0
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
3
2
|
/**
|
|
4
|
-
* Shared parser for CONTEXT.md <decisions> blocks
|
|
3
|
+
* Shared parser for CONTEXT.md <decisions> blocks (ADR-457 build-at-publish:
|
|
4
|
+
* the hand-written bin/lib/decisions.cjs collapsed to a TypeScript source of
|
|
5
|
+
* truth). Behaviour is preserved byte-for-behaviour from the prior hand-written
|
|
6
|
+
* .cjs; only types are added.
|
|
7
|
+
*
|
|
5
8
|
* Accepts both numeric (D-42) and alphanumeric (D-INFRA-01) IDs.
|
|
6
9
|
* Returns {id, text, category, tags, trackable} per decision.
|
|
7
10
|
* CJS callers that only use {id, text} safely ignore the extra fields.
|
|
8
11
|
*/
|
|
9
|
-
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.parseDecisions = parseDecisions;
|
|
10
14
|
const DISCRETION_HEADINGS = new Set([
|
|
11
15
|
"claude's discretion",
|
|
12
16
|
'claudes discretion',
|
|
@@ -79,7 +83,7 @@ function parseDecisions(content) {
|
|
|
79
83
|
// (review F20).
|
|
80
84
|
const normalized = category
|
|
81
85
|
.toLowerCase()
|
|
82
|
-
.replace(/[
|
|
86
|
+
.replace(/[‘’‚‛“”„‟'"`]/g, '')
|
|
83
87
|
.trim();
|
|
84
88
|
inDiscretion = DISCRETION_HEADINGS.has(normalized);
|
|
85
89
|
continue;
|
|
@@ -112,5 +116,3 @@ function parseDecisions(content) {
|
|
|
112
116
|
flush();
|
|
113
117
|
return out;
|
|
114
118
|
}
|
|
115
|
-
|
|
116
|
-
module.exports = { parseDecisions };
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Docs — Commands for the docs-update workflow
|
|
4
|
+
*
|
|
5
|
+
* Provides `cmdDocsInit` which returns project signals, existing doc inventory
|
|
6
|
+
* with GSD marker detection, doc tooling detection, monorepo awareness, and
|
|
7
|
+
* model resolution. Used by Phase 2 to route doc generation appropriately.
|
|
8
|
+
*
|
|
9
|
+
* ADR-457 build-at-publish: the hand-written bin/lib/docs.cjs collapsed
|
|
10
|
+
* to a TypeScript source of truth. Behaviour is preserved byte-for-behaviour
|
|
11
|
+
* from the prior hand-written .cjs; only strict types are added.
|
|
12
|
+
*/
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
17
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
19
|
+
const core = require("./core.cjs");
|
|
20
|
+
const { output, loadConfig, resolveModelInternal, pathExistsInternal, toPosixPath, checkAgentsInstalled } = core;
|
|
21
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
22
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
23
|
+
const GSD_MARKER = '<!-- generated-by: gsd-doc-writer -->';
|
|
24
|
+
const SKIP_DIRS = new Set([
|
|
25
|
+
'node_modules', '.git', '.planning', '.claude', '__pycache__',
|
|
26
|
+
'target', 'dist', 'build', '.next', '.nuxt', 'coverage',
|
|
27
|
+
'.vscode', '.idea',
|
|
28
|
+
]);
|
|
29
|
+
// ─── Private helpers ──────────────────────────────────────────────────────────
|
|
30
|
+
/**
|
|
31
|
+
* Check whether a file begins with the GSD doc writer marker.
|
|
32
|
+
* Reads the first 500 bytes only — avoids loading large files.
|
|
33
|
+
*/
|
|
34
|
+
function hasGsdMarker(filePath) {
|
|
35
|
+
try {
|
|
36
|
+
const buf = Buffer.alloc(500);
|
|
37
|
+
const fd = node_fs_1.default.openSync(filePath, 'r');
|
|
38
|
+
const bytesRead = node_fs_1.default.readSync(fd, buf, 0, 500, 0);
|
|
39
|
+
node_fs_1.default.closeSync(fd);
|
|
40
|
+
return buf.slice(0, bytesRead).toString('utf-8').includes(GSD_MARKER);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Recursively scan the project root (immediate .md files) and docs/ directory
|
|
48
|
+
* (up to 4 levels deep) for Markdown files, excluding dirs in SKIP_DIRS.
|
|
49
|
+
*/
|
|
50
|
+
function scanExistingDocs(cwd) {
|
|
51
|
+
const MAX_DEPTH = 4;
|
|
52
|
+
const results = [];
|
|
53
|
+
function walkDir(dir, depth) {
|
|
54
|
+
if (depth > MAX_DEPTH)
|
|
55
|
+
return;
|
|
56
|
+
try {
|
|
57
|
+
const entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
if (SKIP_DIRS.has(entry.name))
|
|
60
|
+
continue;
|
|
61
|
+
const abs = node_path_1.default.join(dir, entry.name);
|
|
62
|
+
if (entry.isDirectory()) {
|
|
63
|
+
walkDir(abs, depth + 1);
|
|
64
|
+
}
|
|
65
|
+
else if (entry.isFile() && entry.name.toLowerCase().endsWith('.md')) {
|
|
66
|
+
const rel = toPosixPath(node_path_1.default.relative(cwd, abs));
|
|
67
|
+
results.push({ path: rel, has_gsd_marker: hasGsdMarker(abs) });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch { /* directory may not exist — best-effort */ }
|
|
72
|
+
}
|
|
73
|
+
// Scan root-level .md files (non-recursive)
|
|
74
|
+
try {
|
|
75
|
+
const entries = node_fs_1.default.readdirSync(cwd, { withFileTypes: true });
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
if (entry.isFile() && entry.name.toLowerCase().endsWith('.md')) {
|
|
78
|
+
const abs = node_path_1.default.join(cwd, entry.name);
|
|
79
|
+
const rel = toPosixPath(node_path_1.default.relative(cwd, abs));
|
|
80
|
+
results.push({ path: rel, has_gsd_marker: hasGsdMarker(abs) });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch { /* best-effort */ }
|
|
85
|
+
// Recursively scan docs/ directory
|
|
86
|
+
const docsDir = node_path_1.default.join(cwd, 'docs');
|
|
87
|
+
walkDir(docsDir, 1);
|
|
88
|
+
// Fallback: if docs/ does not exist, try documentation/ or doc/
|
|
89
|
+
try {
|
|
90
|
+
node_fs_1.default.statSync(docsDir);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
const alternatives = ['documentation', 'doc'];
|
|
94
|
+
for (const alt of alternatives) {
|
|
95
|
+
const altDir = node_path_1.default.join(cwd, alt);
|
|
96
|
+
try {
|
|
97
|
+
const stat = node_fs_1.default.statSync(altDir);
|
|
98
|
+
if (stat.isDirectory()) {
|
|
99
|
+
walkDir(altDir, 1);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch { /* not present */ }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return results.sort((a, b) => a.path.localeCompare(b.path));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Detect project type signals from the filesystem and package.json.
|
|
110
|
+
* All checks are best-effort and never throw.
|
|
111
|
+
*/
|
|
112
|
+
function detectProjectType(cwd) {
|
|
113
|
+
const exists = (rel) => {
|
|
114
|
+
try {
|
|
115
|
+
return pathExistsInternal(cwd, rel);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
// Read package.json once — used by has_cli_bin, is_monorepo, has_tests checks.
|
|
122
|
+
const pkgRaw = (0, shell_command_projection_cjs_1.platformReadSync)(node_path_1.default.join(cwd, 'package.json'));
|
|
123
|
+
let pkg = null;
|
|
124
|
+
if (pkgRaw) {
|
|
125
|
+
try {
|
|
126
|
+
pkg = JSON.parse(pkgRaw);
|
|
127
|
+
}
|
|
128
|
+
catch { /* invalid JSON */ }
|
|
129
|
+
}
|
|
130
|
+
// has_cli_bin: package.json has a `bin` field
|
|
131
|
+
const binField = pkg?.['bin'];
|
|
132
|
+
const has_cli_bin = !!(binField && (typeof binField === 'string' ||
|
|
133
|
+
(typeof binField === 'object' && Object.keys(binField).length > 0)));
|
|
134
|
+
// is_monorepo: pnpm-workspace.yaml, lerna.json, or package.json workspaces
|
|
135
|
+
let is_monorepo = exists('pnpm-workspace.yaml') || exists('lerna.json');
|
|
136
|
+
if (!is_monorepo && pkg) {
|
|
137
|
+
is_monorepo = Array.isArray(pkg['workspaces']) && pkg['workspaces'].length > 0;
|
|
138
|
+
}
|
|
139
|
+
// has_tests: common test directories or test frameworks in devDependencies
|
|
140
|
+
let has_tests = exists('test') || exists('tests') || exists('__tests__') || exists('spec');
|
|
141
|
+
if (!has_tests && pkg) {
|
|
142
|
+
const devDeps = Object.keys(pkg['devDependencies'] || {});
|
|
143
|
+
has_tests = devDeps.some(d => ['vitest', 'jest', 'mocha', 'jasmine', 'ava'].includes(d));
|
|
144
|
+
}
|
|
145
|
+
// has_deploy_config: various deployment config files
|
|
146
|
+
const deployFiles = [
|
|
147
|
+
'Dockerfile', 'docker-compose.yml', 'docker-compose.yaml',
|
|
148
|
+
'fly.toml', 'render.yaml', 'vercel.json', 'netlify.toml', 'railway.json',
|
|
149
|
+
'.github/workflows/deploy.yml', '.github/workflows/deploy.yaml',
|
|
150
|
+
];
|
|
151
|
+
const has_deploy_config = deployFiles.some(f => exists(f));
|
|
152
|
+
return {
|
|
153
|
+
has_package_json: exists('package.json'),
|
|
154
|
+
has_api_routes: (exists('src/app/api') || exists('routes') || exists('src/routes') ||
|
|
155
|
+
exists('api') || exists('server')),
|
|
156
|
+
has_cli_bin,
|
|
157
|
+
is_open_source: exists('LICENSE') || exists('LICENSE.md'),
|
|
158
|
+
has_deploy_config,
|
|
159
|
+
is_monorepo,
|
|
160
|
+
has_tests,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Detect known documentation tooling in the project.
|
|
165
|
+
*/
|
|
166
|
+
function detectDocTooling(cwd) {
|
|
167
|
+
const exists = (rel) => {
|
|
168
|
+
try {
|
|
169
|
+
return pathExistsInternal(cwd, rel);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
return {
|
|
176
|
+
docusaurus: exists('docusaurus.config.js') || exists('docusaurus.config.ts'),
|
|
177
|
+
vitepress: (exists('.vitepress/config.js') ||
|
|
178
|
+
exists('.vitepress/config.ts') ||
|
|
179
|
+
exists('.vitepress/config.mts')),
|
|
180
|
+
mkdocs: exists('mkdocs.yml'),
|
|
181
|
+
storybook: exists('.storybook'),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Extract monorepo workspace globs from pnpm-workspace.yaml, package.json
|
|
186
|
+
* workspaces, or lerna.json.
|
|
187
|
+
*/
|
|
188
|
+
function detectMonorepoWorkspaces(cwd) {
|
|
189
|
+
// pnpm-workspace.yaml
|
|
190
|
+
const pnpmRaw = (0, shell_command_projection_cjs_1.platformReadSync)(node_path_1.default.join(cwd, 'pnpm-workspace.yaml'));
|
|
191
|
+
if (pnpmRaw) {
|
|
192
|
+
const workspaces = [];
|
|
193
|
+
for (const line of pnpmRaw.split('\n')) {
|
|
194
|
+
const m = line.match(/^\s*-\s+['"]?(.+?)['"]?\s*$/);
|
|
195
|
+
if (m)
|
|
196
|
+
workspaces.push(m[1].trim());
|
|
197
|
+
}
|
|
198
|
+
if (workspaces.length > 0)
|
|
199
|
+
return workspaces;
|
|
200
|
+
}
|
|
201
|
+
// package.json workspaces
|
|
202
|
+
const pkgRaw = (0, shell_command_projection_cjs_1.platformReadSync)(node_path_1.default.join(cwd, 'package.json'));
|
|
203
|
+
if (pkgRaw) {
|
|
204
|
+
try {
|
|
205
|
+
const pkg = JSON.parse(pkgRaw);
|
|
206
|
+
if (Array.isArray(pkg['workspaces']) && pkg['workspaces'].length > 0) {
|
|
207
|
+
return pkg['workspaces'];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch { /* invalid JSON */ }
|
|
211
|
+
}
|
|
212
|
+
// lerna.json
|
|
213
|
+
const lernaRaw = (0, shell_command_projection_cjs_1.platformReadSync)(node_path_1.default.join(cwd, 'lerna.json'));
|
|
214
|
+
if (lernaRaw) {
|
|
215
|
+
try {
|
|
216
|
+
const lerna = JSON.parse(lernaRaw);
|
|
217
|
+
if (Array.isArray(lerna['packages']) && lerna['packages'].length > 0) {
|
|
218
|
+
return lerna['packages'];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch { /* invalid JSON */ }
|
|
222
|
+
}
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
// ─── Public commands ──────────────────────────────────────────────────────────
|
|
226
|
+
/**
|
|
227
|
+
* Return JSON context for the docs-update workflow: project signals, existing
|
|
228
|
+
* doc inventory, doc tooling detection, monorepo workspaces, and model
|
|
229
|
+
* resolution. Follows the cmdInitMapCodebase pattern.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* node gsd-tools.cjs docs-init --raw
|
|
233
|
+
*/
|
|
234
|
+
function cmdDocsInit(cwd, raw) {
|
|
235
|
+
const config = loadConfig(cwd);
|
|
236
|
+
const result = {
|
|
237
|
+
doc_writer_model: resolveModelInternal(cwd, 'gsd-doc-writer'),
|
|
238
|
+
commit_docs: config.commit_docs,
|
|
239
|
+
existing_docs: scanExistingDocs(cwd),
|
|
240
|
+
project_type: detectProjectType(cwd),
|
|
241
|
+
doc_tooling: detectDocTooling(cwd),
|
|
242
|
+
monorepo_workspaces: detectMonorepoWorkspaces(cwd),
|
|
243
|
+
planning_exists: pathExistsInternal(cwd, '.planning'),
|
|
244
|
+
};
|
|
245
|
+
// Inject project_root and agent installation status (mirrors withProjectRoot in init.cjs)
|
|
246
|
+
result['project_root'] = cwd;
|
|
247
|
+
const agentStatus = checkAgentsInstalled();
|
|
248
|
+
result['agents_installed'] = agentStatus.agents_installed;
|
|
249
|
+
result['missing_agents'] = agentStatus.missing_agents;
|
|
250
|
+
output(result, raw, undefined);
|
|
251
|
+
}
|
|
252
|
+
module.exports = { cmdDocsInit };
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codebase Drift Detection (#2003)
|
|
3
|
+
*
|
|
4
|
+
* Detects structural drift between a committed codebase and the
|
|
5
|
+
* `.planning/codebase/STRUCTURE.md` map produced by `gsd-codebase-mapper`.
|
|
6
|
+
*
|
|
7
|
+
* Four categories of drift element:
|
|
8
|
+
* - new_dir → a newly-added file whose directory prefix does not appear
|
|
9
|
+
* in STRUCTURE.md
|
|
10
|
+
* - barrel → a newly-added barrel export at
|
|
11
|
+
* (packages|apps)/<name>/src/index.(ts|tsx|js|mjs|cjs)
|
|
12
|
+
* - migration → a newly-added migration file under one of the recognized
|
|
13
|
+
* migration directories (supabase, prisma, drizzle, src/migrations, …)
|
|
14
|
+
* - route → a newly-added route module under a `routes/` or `api/` dir
|
|
15
|
+
*
|
|
16
|
+
* Each file is counted at most once; when a file matches multiple categories
|
|
17
|
+
* the most specific category wins (migration > route > barrel > new_dir).
|
|
18
|
+
*
|
|
19
|
+
* Design decisions (see PR for full rubber-duck):
|
|
20
|
+
* - The library is pure. It takes parsed git diff output and returns a
|
|
21
|
+
* structured result. The CLI/workflow layer is responsible for running
|
|
22
|
+
* git and for spawning mappers.
|
|
23
|
+
* - `last_mapped_commit` is stored as YAML-style frontmatter at the top of
|
|
24
|
+
* each `.planning/codebase/*.md` file. This keeps the baseline attached
|
|
25
|
+
* to the file, survives git moves, and avoids a sidecar JSON.
|
|
26
|
+
* - The detector NEVER throws on malformed input — it returns a
|
|
27
|
+
* `{ skipped: true }` result. The phase workflow depends on this
|
|
28
|
+
* non-blocking guarantee.
|
|
29
|
+
*
|
|
30
|
+
* ADR-457 build-at-publish: the hand-written bin/lib/drift.cjs collapsed to
|
|
31
|
+
* a TypeScript source of truth. Behaviour is preserved byte-for-behaviour from
|
|
32
|
+
* the prior hand-written .cjs; only types are added.
|
|
33
|
+
*/
|
|
34
|
+
'use strict';
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
39
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
40
|
+
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
41
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
42
|
+
const DRIFT_CATEGORIES = Object.freeze(['new_dir', 'barrel', 'migration', 'route']);
|
|
43
|
+
// Category priority when a single file matches multiple rules.
|
|
44
|
+
// Higher index = more specific = wins.
|
|
45
|
+
const CATEGORY_PRIORITY = { new_dir: 0, barrel: 1, route: 2, migration: 3 };
|
|
46
|
+
const BARREL_RE = /^(packages|apps)\/[^/]+\/src\/index\.(ts|tsx|js|mjs|cjs)$/;
|
|
47
|
+
const MIGRATION_RES = [
|
|
48
|
+
/^supabase\/migrations\/.+\.sql$/,
|
|
49
|
+
/^prisma\/migrations\/.+/,
|
|
50
|
+
/^drizzle\/meta\/.+/,
|
|
51
|
+
/^drizzle\/migrations\/.+/,
|
|
52
|
+
/^src\/migrations\/.+\.(ts|js|sql)$/,
|
|
53
|
+
/^db\/migrations\/.+\.(sql|ts|js)$/,
|
|
54
|
+
/^migrations\/.+\.(sql|ts|js)$/,
|
|
55
|
+
];
|
|
56
|
+
const ROUTE_RES = [
|
|
57
|
+
/^(apps|packages)\/[^/]+\/src\/routes\/.+\.(ts|tsx|js|jsx|mjs|cjs)$/,
|
|
58
|
+
/^src\/routes\/.+\.(ts|tsx|js|jsx|mjs|cjs)$/,
|
|
59
|
+
/^src\/api\/.+\.(ts|tsx|js|jsx|mjs|cjs)$/,
|
|
60
|
+
/^(apps|packages)\/[^/]+\/src\/api\/.+\.(ts|tsx|js|jsx|mjs|cjs)$/,
|
|
61
|
+
];
|
|
62
|
+
// A conservative allowlist for `--paths` arguments passed to the mapper:
|
|
63
|
+
// repo-relative path components separated by /, containing only
|
|
64
|
+
// alphanumerics, dash, underscore, and dot (no `..`, no `/..`).
|
|
65
|
+
const SAFE_PATH_RE = /^(?!.*\.\.)(?:[A-Za-z0-9_.][A-Za-z0-9_.\-]*)(?:\/[A-Za-z0-9_.][A-Za-z0-9_.\-]*)*$/;
|
|
66
|
+
/**
|
|
67
|
+
* Classify a single file path into a drift category or null.
|
|
68
|
+
*/
|
|
69
|
+
function classifyFile(file) {
|
|
70
|
+
if (typeof file !== 'string' || !file)
|
|
71
|
+
return null;
|
|
72
|
+
const norm = file.replace(/\\/g, '/');
|
|
73
|
+
if (MIGRATION_RES.some((r) => r.test(norm)))
|
|
74
|
+
return 'migration';
|
|
75
|
+
if (ROUTE_RES.some((r) => r.test(norm)))
|
|
76
|
+
return 'route';
|
|
77
|
+
if (BARREL_RE.test(norm))
|
|
78
|
+
return 'barrel';
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* True iff any prefix of `file` (dir1, dir1/dir2, …) appears as a substring
|
|
83
|
+
* of `structureMd`. Used to decide whether a file is in "mapped territory".
|
|
84
|
+
*
|
|
85
|
+
* Matching is deliberately substring-based — STRUCTURE.md is free-form
|
|
86
|
+
* markdown, not a structured manifest. If the map mentions `src/lib/` the
|
|
87
|
+
* check `structureMd.includes('src/lib')` holds.
|
|
88
|
+
*/
|
|
89
|
+
function isPathMapped(file, structureMd) {
|
|
90
|
+
const norm = file.replace(/\\/g, '/');
|
|
91
|
+
const parts = norm.split('/');
|
|
92
|
+
// Check prefixes from longest to shortest; any hit means "mapped".
|
|
93
|
+
for (let i = parts.length - 1; i >= 1; i--) {
|
|
94
|
+
const prefix = parts.slice(0, i).join('/');
|
|
95
|
+
if (structureMd.includes(prefix))
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
// Finally, if even the top-level dir is mentioned, count as mapped.
|
|
99
|
+
if (parts.length > 0 && structureMd.includes(parts[0] + '/'))
|
|
100
|
+
return true;
|
|
101
|
+
if (parts.length > 0 && structureMd.includes('`' + parts[0] + '`'))
|
|
102
|
+
return true;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
// ─── Main detection ──────────────────────────────────────────────────────────
|
|
106
|
+
/**
|
|
107
|
+
* Detect codebase drift.
|
|
108
|
+
*/
|
|
109
|
+
function detectDrift(input) {
|
|
110
|
+
try {
|
|
111
|
+
if (!input || typeof input !== 'object') {
|
|
112
|
+
return skipped('invalid-input');
|
|
113
|
+
}
|
|
114
|
+
const inp = input;
|
|
115
|
+
const { addedFiles, modifiedFiles, deletedFiles, structureMd, } = inp;
|
|
116
|
+
const threshold = Number.isInteger(inp.threshold) && inp.threshold >= 1
|
|
117
|
+
? inp.threshold
|
|
118
|
+
: 3;
|
|
119
|
+
const action = inp.action === 'auto-remap' ? 'auto-remap' : 'warn';
|
|
120
|
+
if (structureMd === null || structureMd === undefined) {
|
|
121
|
+
return skipped('missing-structure-md');
|
|
122
|
+
}
|
|
123
|
+
if (typeof structureMd !== 'string') {
|
|
124
|
+
return skipped('invalid-structure-md');
|
|
125
|
+
}
|
|
126
|
+
const added = Array.isArray(addedFiles) ? addedFiles.filter((x) => typeof x === 'string') : [];
|
|
127
|
+
const modified = Array.isArray(modifiedFiles) ? modifiedFiles : [];
|
|
128
|
+
const deleted = Array.isArray(deletedFiles) ? deletedFiles : [];
|
|
129
|
+
// Build elements. One element per file, highest-priority category wins.
|
|
130
|
+
const elements = [];
|
|
131
|
+
const seen = new Map();
|
|
132
|
+
for (const rawFile of added) {
|
|
133
|
+
const file = rawFile.replace(/\\/g, '/');
|
|
134
|
+
const specific = classifyFile(file);
|
|
135
|
+
let category = specific;
|
|
136
|
+
if (!category) {
|
|
137
|
+
if (!isPathMapped(file, structureMd)) {
|
|
138
|
+
category = 'new_dir';
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
continue; // mapped, known, ordinary file — not drift
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Dedup: if we've already counted this path at higher-or-equal priority, skip
|
|
145
|
+
const prior = seen.get(file);
|
|
146
|
+
if (prior && CATEGORY_PRIORITY[prior] >= CATEGORY_PRIORITY[category])
|
|
147
|
+
continue;
|
|
148
|
+
seen.set(file, category);
|
|
149
|
+
}
|
|
150
|
+
for (const [file, category] of seen.entries()) {
|
|
151
|
+
elements.push({ category, path: file });
|
|
152
|
+
}
|
|
153
|
+
// Sort for stable output.
|
|
154
|
+
elements.sort((a, b) => a.category === b.category
|
|
155
|
+
? a.path.localeCompare(b.path)
|
|
156
|
+
: a.category.localeCompare(b.category));
|
|
157
|
+
const actionRequired = elements.length >= threshold;
|
|
158
|
+
let directive = 'none';
|
|
159
|
+
let spawnMapper = false;
|
|
160
|
+
let affectedPaths = [];
|
|
161
|
+
let message = '';
|
|
162
|
+
if (actionRequired) {
|
|
163
|
+
directive = action;
|
|
164
|
+
affectedPaths = chooseAffectedPaths(elements.map((e) => e.path));
|
|
165
|
+
if (action === 'auto-remap') {
|
|
166
|
+
spawnMapper = true;
|
|
167
|
+
}
|
|
168
|
+
message = buildMessage(elements, affectedPaths, action, inp.runtime);
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
skipped: false,
|
|
172
|
+
elements,
|
|
173
|
+
actionRequired,
|
|
174
|
+
directive,
|
|
175
|
+
spawnMapper,
|
|
176
|
+
affectedPaths,
|
|
177
|
+
threshold,
|
|
178
|
+
action,
|
|
179
|
+
message,
|
|
180
|
+
counts: {
|
|
181
|
+
added: added.length,
|
|
182
|
+
modified: modified.length,
|
|
183
|
+
deleted: deleted.length,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
// Non-blocking: never throw from this function.
|
|
189
|
+
const errMsg = err?.message ? err.message : String(err);
|
|
190
|
+
return skipped('exception:' + errMsg);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function skipped(reason) {
|
|
194
|
+
return {
|
|
195
|
+
skipped: true,
|
|
196
|
+
reason,
|
|
197
|
+
elements: [],
|
|
198
|
+
actionRequired: false,
|
|
199
|
+
directive: 'none',
|
|
200
|
+
spawnMapper: false,
|
|
201
|
+
affectedPaths: [],
|
|
202
|
+
message: '',
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function buildMessage(elements, affectedPaths, action, runtime) {
|
|
206
|
+
const byCat = {};
|
|
207
|
+
for (const e of elements) {
|
|
208
|
+
if (!byCat[e.category])
|
|
209
|
+
byCat[e.category] = [];
|
|
210
|
+
byCat[e.category].push(e.path);
|
|
211
|
+
}
|
|
212
|
+
const lines = [
|
|
213
|
+
`Codebase drift detected: ${elements.length} structural element(s) since last mapping.`,
|
|
214
|
+
'',
|
|
215
|
+
];
|
|
216
|
+
const labels = {
|
|
217
|
+
new_dir: 'New directories',
|
|
218
|
+
barrel: 'New barrel exports',
|
|
219
|
+
migration: 'New migrations',
|
|
220
|
+
route: 'New route modules',
|
|
221
|
+
};
|
|
222
|
+
for (const cat of ['new_dir', 'barrel', 'migration', 'route']) {
|
|
223
|
+
if (byCat[cat]) {
|
|
224
|
+
lines.push(`${labels[cat]}:`);
|
|
225
|
+
for (const p of byCat[cat])
|
|
226
|
+
lines.push(` - ${p}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
lines.push('');
|
|
230
|
+
if (action === 'auto-remap') {
|
|
231
|
+
lines.push(`Auto-remap scheduled for paths: ${affectedPaths.join(', ')}`);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
// drift.cts is a pure library — it must never read env/config. The
|
|
235
|
+
// caller (verify.cmdVerifyCodebaseDrift) resolves the runtime once and
|
|
236
|
+
// passes it in via input.runtime so emitted commands match the project
|
|
237
|
+
// the caller is targeting, not the current process directory.
|
|
238
|
+
const mapCmd = (0, runtime_slash_cjs_1.formatGsdSlash)('map-codebase', runtime || 'claude');
|
|
239
|
+
lines.push(`Run ${String(mapCmd)} --paths ${affectedPaths.join(',')} to refresh planning context.`);
|
|
240
|
+
}
|
|
241
|
+
return lines.join('\n');
|
|
242
|
+
}
|
|
243
|
+
// ─── Affected paths ──────────────────────────────────────────────────────────
|
|
244
|
+
/**
|
|
245
|
+
* Collapse a list of drifted file paths into a sorted, deduplicated list of
|
|
246
|
+
* the top-level directory prefixes (depth 2 when the repo uses an
|
|
247
|
+
* `<apps|packages>/<name>/…` layout; depth 1 otherwise).
|
|
248
|
+
*/
|
|
249
|
+
function chooseAffectedPaths(paths) {
|
|
250
|
+
const out = new Set();
|
|
251
|
+
for (const raw of paths || []) {
|
|
252
|
+
if (typeof raw !== 'string' || !raw)
|
|
253
|
+
continue;
|
|
254
|
+
const file = raw.replace(/\\/g, '/');
|
|
255
|
+
const parts = file.split('/');
|
|
256
|
+
if (parts.length === 0)
|
|
257
|
+
continue;
|
|
258
|
+
const top = parts[0];
|
|
259
|
+
if ((top === 'apps' || top === 'packages') && parts.length >= 2) {
|
|
260
|
+
out.add(`${top}/${parts[1]}`);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
out.add(top);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return [...out].sort();
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Filter `paths` to only those that are safe to splice into a mapper prompt.
|
|
270
|
+
* Any path that is absolute, contains traversal, or includes shell
|
|
271
|
+
* metacharacters is dropped.
|
|
272
|
+
*/
|
|
273
|
+
function sanitizePaths(paths) {
|
|
274
|
+
if (!Array.isArray(paths))
|
|
275
|
+
return [];
|
|
276
|
+
const out = [];
|
|
277
|
+
for (const p of paths) {
|
|
278
|
+
if (typeof p !== 'string')
|
|
279
|
+
continue;
|
|
280
|
+
if (p.startsWith('/'))
|
|
281
|
+
continue;
|
|
282
|
+
if (!SAFE_PATH_RE.test(p))
|
|
283
|
+
continue;
|
|
284
|
+
out.push(p);
|
|
285
|
+
}
|
|
286
|
+
return out;
|
|
287
|
+
}
|
|
288
|
+
// ─── Frontmatter helpers ─────────────────────────────────────────────────────
|
|
289
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
290
|
+
function parseFrontmatter(content) {
|
|
291
|
+
if (typeof content !== 'string')
|
|
292
|
+
return { data: {}, body: '' };
|
|
293
|
+
const m = content.match(FRONTMATTER_RE);
|
|
294
|
+
if (!m)
|
|
295
|
+
return { data: {}, body: content };
|
|
296
|
+
const data = {};
|
|
297
|
+
for (const line of m[1].split(/\r?\n/)) {
|
|
298
|
+
const kv = line.match(/^([A-Za-z0-9_][A-Za-z0-9_-]*):\s*(.*)$/);
|
|
299
|
+
if (!kv)
|
|
300
|
+
continue;
|
|
301
|
+
data[kv[1]] = kv[2];
|
|
302
|
+
}
|
|
303
|
+
return { data, body: content.slice(m[0].length) };
|
|
304
|
+
}
|
|
305
|
+
function serializeFrontmatter(data, body) {
|
|
306
|
+
const keys = Object.keys(data);
|
|
307
|
+
if (keys.length === 0)
|
|
308
|
+
return body;
|
|
309
|
+
const lines = ['---'];
|
|
310
|
+
for (const k of keys)
|
|
311
|
+
lines.push(`${k}: ${data[k]}`);
|
|
312
|
+
lines.push('---');
|
|
313
|
+
return lines.join('\n') + '\n' + body;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Read `last_mapped_commit` from the frontmatter of a `.planning/codebase/*.md`
|
|
317
|
+
* file. Returns null if the file does not exist or has no frontmatter.
|
|
318
|
+
*/
|
|
319
|
+
function readMappedCommit(filePath) {
|
|
320
|
+
let content;
|
|
321
|
+
try {
|
|
322
|
+
content = node_fs_1.default.readFileSync(filePath, 'utf8');
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
const { data } = parseFrontmatter(content);
|
|
328
|
+
const sha = data['last_mapped_commit'];
|
|
329
|
+
return typeof sha === 'string' && sha.length > 0 ? sha : null;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Upsert `last_mapped_commit` and `last_mapped_at` into the frontmatter of
|
|
333
|
+
* the given file, preserving any other frontmatter keys and the body.
|
|
334
|
+
*/
|
|
335
|
+
function writeMappedCommit(filePath, commitSha, isoDate) {
|
|
336
|
+
// Symmetric with readMappedCommit (which returns null on missing files):
|
|
337
|
+
// tolerate a missing target by creating a minimal frontmatter-only file
|
|
338
|
+
// rather than throwing ENOENT. This matters when a mapper produces a new
|
|
339
|
+
// doc and the caller stamps it before any prior content existed.
|
|
340
|
+
let content = '';
|
|
341
|
+
try {
|
|
342
|
+
content = node_fs_1.default.readFileSync(filePath, 'utf8');
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
if (err.code !== 'ENOENT')
|
|
346
|
+
throw err;
|
|
347
|
+
}
|
|
348
|
+
const { data, body } = parseFrontmatter(content);
|
|
349
|
+
data['last_mapped_commit'] = commitSha;
|
|
350
|
+
if (isoDate)
|
|
351
|
+
data['last_mapped_at'] = isoDate;
|
|
352
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(filePath, serializeFrontmatter(data, body));
|
|
353
|
+
}
|
|
354
|
+
module.exports = {
|
|
355
|
+
DRIFT_CATEGORIES,
|
|
356
|
+
classifyFile,
|
|
357
|
+
detectDrift,
|
|
358
|
+
chooseAffectedPaths,
|
|
359
|
+
sanitizePaths,
|
|
360
|
+
readMappedCommit,
|
|
361
|
+
writeMappedCommit,
|
|
362
|
+
// Exposed for the CLI layer to reuse the same parser.
|
|
363
|
+
parseFrontmatter,
|
|
364
|
+
};
|