gsd-pi 2.43.0 → 2.44.0-dev.0b97ffd
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.md +30 -12
- package/dist/cli.js +13 -1
- package/dist/help-text.js +24 -0
- package/dist/resources/extensions/bg-shell/overlay.js +3 -0
- package/dist/resources/extensions/github-sync/sync.js +2 -1
- package/dist/resources/extensions/gsd/auto/loop.js +0 -2
- package/dist/resources/extensions/gsd/auto/phases.js +7 -12
- package/dist/resources/extensions/gsd/auto-dashboard.js +19 -18
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +34 -19
- package/dist/resources/extensions/gsd/auto-dispatch.js +36 -21
- package/dist/resources/extensions/gsd/auto-post-unit.js +128 -14
- package/dist/resources/extensions/gsd/auto-prompts.js +202 -92
- package/dist/resources/extensions/gsd/auto-recovery.js +83 -135
- package/dist/resources/extensions/gsd/auto-start.js +10 -0
- package/dist/resources/extensions/gsd/auto-supervisor.js +14 -0
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -7
- package/dist/resources/extensions/gsd/auto-verification.js +5 -10
- package/dist/resources/extensions/gsd/auto-worktree.js +123 -30
- package/dist/resources/extensions/gsd/auto.js +1 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +611 -0
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +28 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +3 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +15 -1
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
- package/dist/resources/extensions/gsd/commands-maintenance.js +78 -3
- package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -31
- package/dist/resources/extensions/gsd/db-writer.js +95 -4
- package/dist/resources/extensions/gsd/dispatch-guard.js +35 -30
- package/dist/resources/extensions/gsd/doctor-checks.js +28 -11
- package/dist/resources/extensions/gsd/doctor-environment.js +28 -0
- package/dist/resources/extensions/gsd/doctor-types.js +0 -15
- package/dist/resources/extensions/gsd/doctor.js +46 -282
- package/dist/resources/extensions/gsd/file-watcher.js +5 -1
- package/dist/resources/extensions/gsd/files.js +14 -198
- package/dist/resources/extensions/gsd/git-service.js +4 -0
- package/dist/resources/extensions/gsd/gitignore.js +4 -0
- package/dist/resources/extensions/gsd/gsd-db.js +819 -197
- package/dist/resources/extensions/gsd/guided-flow.js +18 -8
- package/dist/resources/extensions/gsd/markdown-renderer.js +862 -0
- package/dist/resources/extensions/gsd/md-importer.js +182 -4
- package/dist/resources/extensions/gsd/native-git-bridge.js +10 -1
- package/dist/resources/extensions/gsd/parallel-eligibility.js +14 -19
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +38 -0
- package/dist/resources/extensions/gsd/parsers-legacy.js +239 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +9 -0
- package/dist/resources/extensions/gsd/preferences.js +1 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +22 -9
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +15 -5
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -7
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +6 -6
- package/dist/resources/extensions/gsd/reactive-graph.js +33 -3
- package/dist/resources/extensions/gsd/skill-health.js +3 -1
- package/dist/resources/extensions/gsd/state.js +484 -30
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +128 -0
- package/dist/resources/extensions/gsd/tools/complete-slice.js +244 -0
- package/dist/resources/extensions/gsd/tools/complete-task.js +204 -0
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +205 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +155 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +94 -0
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +152 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +146 -0
- package/dist/resources/extensions/gsd/triage-resolution.js +17 -1
- package/dist/resources/extensions/gsd/undo.js +197 -3
- package/dist/resources/extensions/gsd/visualizer-data.js +53 -16
- package/dist/resources/extensions/gsd/workspace-index.js +63 -39
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -17
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +4 -4
- package/dist/web/standalone/.next/routes-manifest.json +6 -0
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -0
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -0
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -0
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +5 -5
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -17
- package/dist/web/standalone/.next/server/chunks/229.js +3 -3
- package/dist/web/standalone/.next/server/chunks/471.js +3 -3
- package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/alS4hoANx0TK4UVZY27da/_buildManifest.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{4024.c195dc1fdd2adbea.js → 4024.0de81b543b28b9fe.js} +2 -2
- package/dist/web/standalone/.next/static/chunks/app/_global-error/{page-d07a2c023f1aef1e.js → page-d83ba70a25a85472.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/boot/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-fa307370fcf9fb2c.js → webpack-9014b5adb127a98a.js} +1 -1
- package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +4 -4
- package/packages/pi-ai/dist/models.custom.d.ts +173 -0
- package/packages/pi-ai/dist/models.custom.d.ts.map +1 -0
- package/packages/pi-ai/dist/models.custom.js +170 -0
- package/packages/pi-ai/dist/models.custom.js.map +1 -0
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.js +16 -1
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/models.test.d.ts +2 -0
- package/packages/pi-ai/dist/models.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/models.test.js +67 -0
- package/packages/pi-ai/dist/models.test.js.map +1 -0
- package/packages/pi-ai/src/models.custom.ts +172 -0
- package/packages/pi-ai/src/models.test.ts +85 -0
- package/packages/pi-ai/src/models.ts +17 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -3
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +21 -34
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +4 -4
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +80 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +63 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +37 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/fallback-resolver.js +2 -3
- package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +12 -2
- package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +38 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +192 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +69 -21
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +255 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-commands.d.ts +25 -0
- package/packages/pi-coding-agent/dist/core/package-commands.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/package-commands.js +253 -0
- package/packages/pi-coding-agent/dist/core/package-commands.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.js +225 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +4 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +3 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +11 -199
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +21 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +8 -15
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/print-mode.js +45 -34
- package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +7 -2
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +26 -37
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +2 -2
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +3 -3
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +4 -4
- package/packages/pi-coding-agent/src/core/extensions/index.ts +5 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +96 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +89 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/extensions/types.ts +44 -0
- package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +15 -2
- package/packages/pi-coding-agent/src/core/fallback-resolver.ts +2 -3
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +274 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +83 -21
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +288 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +39 -3
- package/packages/pi-coding-agent/src/core/package-commands.test.ts +240 -0
- package/packages/pi-coding-agent/src/core/package-commands.ts +310 -0
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
- package/packages/pi-coding-agent/src/core/sdk.ts +4 -0
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
- package/packages/pi-coding-agent/src/index.ts +7 -0
- package/packages/pi-coding-agent/src/main.ts +11 -232
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +20 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +9 -16
- package/packages/pi-coding-agent/src/modes/print-mode.ts +42 -32
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +8 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -1
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +8 -15
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/bg-shell/overlay.ts +4 -0
- package/src/resources/extensions/github-sync/sync.ts +2 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -8
- package/src/resources/extensions/gsd/auto/loop.ts +0 -2
- package/src/resources/extensions/gsd/auto/phases.ts +7 -20
- package/src/resources/extensions/gsd/auto/types.ts +0 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +20 -16
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +34 -19
- package/src/resources/extensions/gsd/auto-dispatch.ts +38 -21
- package/src/resources/extensions/gsd/auto-post-unit.ts +150 -15
- package/src/resources/extensions/gsd/auto-prompts.ts +186 -103
- package/src/resources/extensions/gsd/auto-recovery.ts +77 -142
- package/src/resources/extensions/gsd/auto-start.ts +14 -0
- package/src/resources/extensions/gsd/auto-supervisor.ts +14 -0
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +6 -7
- package/src/resources/extensions/gsd/auto-verification.ts +4 -9
- package/src/resources/extensions/gsd/auto-worktree.ts +126 -30
- package/src/resources/extensions/gsd/auto.ts +0 -9
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +675 -4
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +31 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +3 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +15 -1
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
- package/src/resources/extensions/gsd/commands-maintenance.ts +86 -3
- package/src/resources/extensions/gsd/dashboard-overlay.ts +17 -13
- package/src/resources/extensions/gsd/db-writer.ts +105 -4
- package/src/resources/extensions/gsd/dispatch-guard.ts +32 -30
- package/src/resources/extensions/gsd/doctor-checks.ts +25 -11
- package/src/resources/extensions/gsd/doctor-environment.ts +31 -0
- package/src/resources/extensions/gsd/doctor-types.ts +0 -23
- package/src/resources/extensions/gsd/doctor.ts +45 -295
- package/src/resources/extensions/gsd/file-watcher.ts +4 -1
- package/src/resources/extensions/gsd/files.ts +16 -220
- package/src/resources/extensions/gsd/git-service.ts +4 -0
- package/src/resources/extensions/gsd/gitignore.ts +4 -0
- package/src/resources/extensions/gsd/gsd-db.ts +1157 -370
- package/src/resources/extensions/gsd/guided-flow.ts +20 -8
- package/src/resources/extensions/gsd/markdown-renderer.ts +1098 -0
- package/src/resources/extensions/gsd/md-importer.ts +211 -2
- package/src/resources/extensions/gsd/native-git-bridge.ts +12 -1
- package/src/resources/extensions/gsd/parallel-eligibility.ts +14 -18
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +43 -0
- package/src/resources/extensions/gsd/parsers-legacy.ts +271 -0
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +9 -0
- package/src/resources/extensions/gsd/preferences.ts +1 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +22 -9
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +15 -5
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
- package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -7
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
- package/src/resources/extensions/gsd/prompts/replan-slice.md +6 -6
- package/src/resources/extensions/gsd/reactive-graph.ts +33 -3
- package/src/resources/extensions/gsd/skill-health.ts +2 -1
- package/src/resources/extensions/gsd/state.ts +547 -29
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
- package/src/resources/extensions/gsd/tests/atomic-task-closeout.test.ts +8 -120
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +20 -11
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +600 -513
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
- package/src/resources/extensions/gsd/tests/auto-stash-merge.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +540 -668
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
- package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +39 -60
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +375 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +387 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -2
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +512 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +644 -84
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
- package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +192 -161
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +30 -90
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +57 -80
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +164 -0
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +55 -153
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +53 -97
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
- package/src/resources/extensions/gsd/tests/doctor.test.ts +109 -149
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +278 -0
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +232 -0
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
- package/src/resources/extensions/gsd/tests/git-service.test.ts +291 -390
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +440 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -270
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +643 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1161 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +429 -0
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +89 -97
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +127 -164
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
- package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
- package/src/resources/extensions/gsd/tests/parsers.test.ts +548 -612
- package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +176 -113
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +305 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
- package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +325 -0
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
- package/src/resources/extensions/gsd/tests/replan-handler.test.ts +410 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
- package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +296 -0
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
- package/src/resources/extensions/gsd/tests/schema-v9-sequence.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
- package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +19 -13
- package/src/resources/extensions/gsd/tests/undo.test.ts +321 -1
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +0 -142
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +12 -5
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
- package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +176 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +300 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +245 -0
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +249 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +194 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +116 -0
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +203 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +192 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +20 -1
- package/src/resources/extensions/gsd/types.ts +50 -0
- package/src/resources/extensions/gsd/undo.ts +247 -3
- package/src/resources/extensions/gsd/visualizer-data.ts +54 -17
- package/src/resources/extensions/gsd/workspace-index.ts +64 -46
- package/dist/resources/extensions/gsd/auto-observability.js +0 -56
- package/dist/resources/extensions/gsd/observability-validator.js +0 -422
- package/dist/resources/extensions/gsd/roadmap-mutations.js +0 -110
- package/dist/web/standalone/.next/static/VvclDCW6TAWjEyLU-EYL1/_buildManifest.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/css/123c0bb039697968.css +0 -1
- package/src/resources/extensions/gsd/auto-observability.ts +0 -74
- package/src/resources/extensions/gsd/observability-validator.ts +0 -456
- package/src/resources/extensions/gsd/roadmap-mutations.ts +0 -134
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +0 -174
- package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +0 -474
- /package/dist/web/standalone/.next/static/{VvclDCW6TAWjEyLU-EYL1 → alS4hoANx0TK4UVZY27da}/_ssgManifest.js +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { describe, test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { parseRoadmap, parsePlan } from '../parsers-legacy.ts';
|
|
4
|
+
import { parseTaskPlanFile, parseSummary, parseContinue, parseRequirementCounts, parseSecretsManifest, formatSecretsManifest } from '../files.ts';
|
|
5
5
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6
6
|
// parseRoadmap tests
|
|
7
7
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
{
|
|
9
|
+
|
|
10
|
+
describe('parsers', () => {
|
|
11
|
+
test('parseRoadmap: full roadmap', () => {
|
|
11
12
|
const content = `# M001: GSD Extension — Hierarchical Planning
|
|
12
13
|
|
|
13
14
|
**Vision:** Build a structured planning system for coding agents.
|
|
@@ -56,44 +57,43 @@ Consumes from S03:
|
|
|
56
57
|
|
|
57
58
|
const r = parseRoadmap(content);
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
assert.deepStrictEqual(r.title, 'M001: GSD Extension — Hierarchical Planning', 'roadmap title');
|
|
61
|
+
assert.deepStrictEqual(r.vision, 'Build a structured planning system for coding agents.', 'roadmap vision');
|
|
62
|
+
assert.deepStrictEqual(r.successCriteria.length, 3, 'success criteria count');
|
|
63
|
+
assert.deepStrictEqual(r.successCriteria[0], 'All parsers have test coverage', 'first success criterion');
|
|
64
|
+
assert.deepStrictEqual(r.successCriteria[2], 'State derivation works correctly', 'third success criterion');
|
|
64
65
|
|
|
65
66
|
// Slices
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
67
|
+
assert.deepStrictEqual(r.slices.length, 3, 'slice count');
|
|
68
|
+
|
|
69
|
+
assert.deepStrictEqual(r.slices[0].id, 'S01', 'S01 id');
|
|
70
|
+
assert.deepStrictEqual(r.slices[0].title, 'Types + File I/O', 'S01 title');
|
|
71
|
+
assert.deepStrictEqual(r.slices[0].risk, 'low', 'S01 risk');
|
|
72
|
+
assert.deepStrictEqual(r.slices[0].depends, [], 'S01 depends');
|
|
73
|
+
assert.deepStrictEqual(r.slices[0].done, true, 'S01 done');
|
|
74
|
+
assert.deepStrictEqual(r.slices[0].demo, 'All types defined and parsers work.', 'S01 demo');
|
|
75
|
+
|
|
76
|
+
assert.deepStrictEqual(r.slices[1].id, 'S02', 'S02 id');
|
|
77
|
+
assert.deepStrictEqual(r.slices[1].title, 'State Derivation', 'S02 title');
|
|
78
|
+
assert.deepStrictEqual(r.slices[1].risk, 'medium', 'S02 risk');
|
|
79
|
+
assert.deepStrictEqual(r.slices[1].depends, ['S01'], 'S02 depends');
|
|
80
|
+
assert.deepStrictEqual(r.slices[1].done, false, 'S02 done');
|
|
81
|
+
|
|
82
|
+
assert.deepStrictEqual(r.slices[2].id, 'S03', 'S03 id');
|
|
83
|
+
assert.deepStrictEqual(r.slices[2].risk, 'high', 'S03 risk');
|
|
84
|
+
assert.deepStrictEqual(r.slices[2].depends, ['S01', 'S02'], 'S03 depends');
|
|
85
|
+
assert.deepStrictEqual(r.slices[2].done, false, 'S03 done');
|
|
85
86
|
|
|
86
87
|
// Boundary map
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
{
|
|
88
|
+
assert.deepStrictEqual(r.boundaryMap.length, 2, 'boundary map entry count');
|
|
89
|
+
assert.deepStrictEqual(r.boundaryMap[0].fromSlice, 'S01', 'bm[0] from');
|
|
90
|
+
assert.deepStrictEqual(r.boundaryMap[0].toSlice, 'S02', 'bm[0] to');
|
|
91
|
+
assert.ok(r.boundaryMap[0].produces.includes('types.ts'), 'bm[0] produces mentions types.ts');
|
|
92
|
+
assert.deepStrictEqual(r.boundaryMap[1].fromSlice, 'S02', 'bm[1] from');
|
|
93
|
+
assert.deepStrictEqual(r.boundaryMap[1].toSlice, 'S03', 'bm[1] to');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('parseRoadmap: empty slices section', () => {
|
|
97
97
|
const content = `# M002: Empty Milestone
|
|
98
98
|
|
|
99
99
|
**Vision:** Nothing yet.
|
|
@@ -104,13 +104,12 @@ console.log('\n=== parseRoadmap: empty slices section ===');
|
|
|
104
104
|
`;
|
|
105
105
|
|
|
106
106
|
const r = parseRoadmap(content);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
107
|
+
assert.deepStrictEqual(r.title, 'M002: Empty Milestone', 'title with empty slices');
|
|
108
|
+
assert.deepStrictEqual(r.slices.length, 0, 'no slices parsed');
|
|
109
|
+
assert.deepStrictEqual(r.boundaryMap.length, 0, 'no boundary map entries');
|
|
110
|
+
});
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
{
|
|
112
|
+
test('parseRoadmap: malformed checkbox lines', () => {
|
|
114
113
|
// Lines that don't match the expected bold pattern should be skipped
|
|
115
114
|
const content = `# M003: Malformed
|
|
116
115
|
|
|
@@ -129,15 +128,14 @@ console.log('\n=== parseRoadmap: malformed checkbox lines ===');
|
|
|
129
128
|
|
|
130
129
|
const r = parseRoadmap(content);
|
|
131
130
|
// Only S02 and S03 should be parsed (malformed lines without bold markers are skipped)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
{
|
|
131
|
+
assert.deepStrictEqual(r.slices.length, 2, 'only valid slices parsed from malformed input');
|
|
132
|
+
assert.deepStrictEqual(r.slices[0].id, 'S02', 'first valid slice is S02');
|
|
133
|
+
assert.deepStrictEqual(r.slices[0].done, true, 'S02 done');
|
|
134
|
+
assert.deepStrictEqual(r.slices[1].id, 'S03', 'second valid slice is S03');
|
|
135
|
+
assert.deepStrictEqual(r.slices[1].depends, ['S02'], 'S03 depends on S02');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('parseRoadmap: lowercase vs uppercase X for done', () => {
|
|
141
139
|
const content = `# M004: Case Test
|
|
142
140
|
|
|
143
141
|
**Vision:** Test X case sensitivity.
|
|
@@ -155,14 +153,13 @@ console.log('\n=== parseRoadmap: lowercase vs uppercase X for done ===');
|
|
|
155
153
|
`;
|
|
156
154
|
|
|
157
155
|
const r = parseRoadmap(content);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
{
|
|
156
|
+
assert.deepStrictEqual(r.slices.length, 3, 'all three slices parsed');
|
|
157
|
+
assert.deepStrictEqual(r.slices[0].done, true, 'lowercase x is done');
|
|
158
|
+
assert.deepStrictEqual(r.slices[1].done, true, 'uppercase X is done');
|
|
159
|
+
assert.deepStrictEqual(r.slices[2].done, false, 'space is not done');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('parseRoadmap: missing boundary map', () => {
|
|
166
163
|
const content = `# M005: No Boundary Map
|
|
167
164
|
|
|
168
165
|
**Vision:** A roadmap without a boundary map section.
|
|
@@ -179,29 +176,27 @@ console.log('\n=== parseRoadmap: missing boundary map ===');
|
|
|
179
176
|
`;
|
|
180
177
|
|
|
181
178
|
const r = parseRoadmap(content);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
{
|
|
179
|
+
assert.deepStrictEqual(r.title, 'M005: No Boundary Map', 'title');
|
|
180
|
+
assert.deepStrictEqual(r.slices.length, 1, 'one slice');
|
|
181
|
+
assert.deepStrictEqual(r.boundaryMap.length, 0, 'empty boundary map when section missing');
|
|
182
|
+
assert.deepStrictEqual(r.successCriteria.length, 1, 'one success criterion');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('parseRoadmap: no sections at all', () => {
|
|
190
186
|
const content = `# M006: Bare Minimum
|
|
191
187
|
|
|
192
188
|
Just a title and nothing else.
|
|
193
189
|
`;
|
|
194
190
|
|
|
195
191
|
const r = parseRoadmap(content);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
{
|
|
192
|
+
assert.deepStrictEqual(r.title, 'M006: Bare Minimum', 'title from bare roadmap');
|
|
193
|
+
assert.deepStrictEqual(r.vision, '', 'empty vision');
|
|
194
|
+
assert.deepStrictEqual(r.successCriteria.length, 0, 'no success criteria');
|
|
195
|
+
assert.deepStrictEqual(r.slices.length, 0, 'no slices');
|
|
196
|
+
assert.deepStrictEqual(r.boundaryMap.length, 0, 'no boundary map');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('parseRoadmap: slice with no demo blockquote', () => {
|
|
205
200
|
const content = `# M007: No Demo
|
|
206
201
|
|
|
207
202
|
**Vision:** Testing slices without demo lines.
|
|
@@ -213,13 +208,12 @@ console.log('\n=== parseRoadmap: slice with no demo blockquote ===');
|
|
|
213
208
|
`;
|
|
214
209
|
|
|
215
210
|
const r = parseRoadmap(content);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
211
|
+
assert.deepStrictEqual(r.slices.length, 2, 'two slices without demos');
|
|
212
|
+
assert.deepStrictEqual(r.slices[0].demo, '', 'S01 demo empty');
|
|
213
|
+
assert.deepStrictEqual(r.slices[1].demo, '', 'S02 demo empty');
|
|
214
|
+
});
|
|
220
215
|
|
|
221
|
-
|
|
222
|
-
{
|
|
216
|
+
test('parseRoadmap: missing risk defaults to low', () => {
|
|
223
217
|
const content = `# M008: Default Risk
|
|
224
218
|
|
|
225
219
|
**Vision:** Test default risk.
|
|
@@ -231,16 +225,14 @@ console.log('\n=== parseRoadmap: missing risk defaults to low ===');
|
|
|
231
225
|
`;
|
|
232
226
|
|
|
233
227
|
const r = parseRoadmap(content);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
228
|
+
assert.deepStrictEqual(r.slices.length, 1, 'one slice');
|
|
229
|
+
assert.deepStrictEqual(r.slices[0].risk, 'low', 'default risk is low');
|
|
230
|
+
});
|
|
237
231
|
|
|
238
232
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
239
233
|
// parsePlan tests
|
|
240
234
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
241
|
-
|
|
242
|
-
console.log('\n=== parsePlan: full plan ===');
|
|
243
|
-
{
|
|
235
|
+
test('parsePlan: full plan', () => {
|
|
244
236
|
const content = `---
|
|
245
237
|
estimated_steps: 6
|
|
246
238
|
estimated_files: 3
|
|
@@ -276,42 +268,41 @@ skills_used:
|
|
|
276
268
|
`;
|
|
277
269
|
|
|
278
270
|
const taskPlan = parseTaskPlanFile(content);
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
271
|
+
assert.deepStrictEqual(taskPlan.frontmatter.estimated_steps, 6, 'task plan frontmatter estimated_steps');
|
|
272
|
+
assert.deepStrictEqual(taskPlan.frontmatter.estimated_files, 3, 'task plan frontmatter estimated_files');
|
|
273
|
+
assert.deepStrictEqual(taskPlan.frontmatter.skills_used.length, 2, 'task plan frontmatter skills_used count');
|
|
274
|
+
assert.deepStrictEqual(taskPlan.frontmatter.skills_used[0], 'typescript', 'first task plan skill');
|
|
275
|
+
assert.deepStrictEqual(taskPlan.frontmatter.skills_used[1], 'testing', 'second task plan skill');
|
|
284
276
|
|
|
285
277
|
const p = parsePlan(content);
|
|
286
278
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
279
|
+
assert.deepStrictEqual(p.id, 'S01', 'plan id');
|
|
280
|
+
assert.deepStrictEqual(p.title, 'Parser Test Suite', 'plan title');
|
|
281
|
+
assert.deepStrictEqual(p.goal, 'All 5 parsers have test coverage with edge cases.', 'plan goal');
|
|
282
|
+
assert.deepStrictEqual(p.demo, '`node --test tests/parsers.test.ts` passes with zero failures.', 'plan demo');
|
|
291
283
|
|
|
292
284
|
// Must-haves
|
|
293
|
-
|
|
294
|
-
|
|
285
|
+
assert.deepStrictEqual(p.mustHaves.length, 3, 'must-have count');
|
|
286
|
+
assert.deepStrictEqual(p.mustHaves[0], 'parseRoadmap tests cover happy path and edge cases', 'first must-have');
|
|
295
287
|
|
|
296
288
|
// Tasks
|
|
297
|
-
|
|
289
|
+
assert.deepStrictEqual(p.tasks.length, 2, 'task count');
|
|
298
290
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
291
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'T01 id');
|
|
292
|
+
assert.deepStrictEqual(p.tasks[0].title, 'Test parseRoadmap and parsePlan', 'T01 title');
|
|
293
|
+
assert.deepStrictEqual(p.tasks[0].done, false, 'T01 not done');
|
|
294
|
+
assert.ok(p.tasks[0].description.includes('comprehensive tests'), 'T01 description content');
|
|
303
295
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
296
|
+
assert.deepStrictEqual(p.tasks[1].id, 'T02', 'T02 id');
|
|
297
|
+
assert.deepStrictEqual(p.tasks[1].title, 'Test parseSummary and parseContinue', 'T02 title');
|
|
298
|
+
assert.deepStrictEqual(p.tasks[1].done, true, 'T02 done');
|
|
307
299
|
|
|
308
300
|
// Files likely touched
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
301
|
+
assert.deepStrictEqual(p.filesLikelyTouched.length, 3, 'files likely touched count');
|
|
302
|
+
assert.ok(p.filesLikelyTouched[0].includes('tests/parsers.test.ts'), 'first file');
|
|
303
|
+
});
|
|
312
304
|
|
|
313
|
-
|
|
314
|
-
{
|
|
305
|
+
test('parseTaskPlanFile: defaults missing frontmatter fields', () => {
|
|
315
306
|
const content = `# T01: Minimal task plan
|
|
316
307
|
|
|
317
308
|
## Description
|
|
@@ -320,13 +311,12 @@ No frontmatter here.
|
|
|
320
311
|
`;
|
|
321
312
|
|
|
322
313
|
const taskPlan = parseTaskPlanFile(content);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
314
|
+
assert.deepStrictEqual(taskPlan.frontmatter.estimated_steps, undefined, 'estimated_steps defaults undefined');
|
|
315
|
+
assert.deepStrictEqual(taskPlan.frontmatter.estimated_files, undefined, 'estimated_files defaults undefined');
|
|
316
|
+
assert.deepStrictEqual(taskPlan.frontmatter.skills_used.length, 0, 'skills_used defaults empty array');
|
|
317
|
+
});
|
|
327
318
|
|
|
328
|
-
|
|
329
|
-
{
|
|
319
|
+
test('parseTaskPlanFile: accepts scalar skills_used and numeric strings', () => {
|
|
330
320
|
const content = `---
|
|
331
321
|
estimated_steps: "9"
|
|
332
322
|
estimated_files: "4"
|
|
@@ -337,14 +327,13 @@ skills_used: react-best-practices
|
|
|
337
327
|
`;
|
|
338
328
|
|
|
339
329
|
const taskPlan = parseTaskPlanFile(content);
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
{
|
|
330
|
+
assert.deepStrictEqual(taskPlan.frontmatter.estimated_steps, 9, 'string estimated_steps parsed');
|
|
331
|
+
assert.deepStrictEqual(taskPlan.frontmatter.estimated_files, 4, 'string estimated_files parsed');
|
|
332
|
+
assert.deepStrictEqual(taskPlan.frontmatter.skills_used.length, 1, 'scalar skills_used normalized to array');
|
|
333
|
+
assert.deepStrictEqual(taskPlan.frontmatter.skills_used[0], 'react-best-practices', 'scalar skill preserved');
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test('parseTaskPlanFile: filters blank skills_used items', () => {
|
|
348
337
|
const content = `---
|
|
349
338
|
skills_used:
|
|
350
339
|
- react
|
|
@@ -356,13 +345,12 @@ skills_used:
|
|
|
356
345
|
`;
|
|
357
346
|
|
|
358
347
|
const taskPlan = parseTaskPlanFile(content);
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
348
|
+
assert.deepStrictEqual(taskPlan.frontmatter.skills_used.length, 2, 'blank skill entries removed');
|
|
349
|
+
assert.deepStrictEqual(taskPlan.frontmatter.skills_used[0], 'react', 'first remaining skill');
|
|
350
|
+
assert.deepStrictEqual(taskPlan.frontmatter.skills_used[1], 'testing', 'second remaining skill');
|
|
351
|
+
});
|
|
363
352
|
|
|
364
|
-
|
|
365
|
-
{
|
|
353
|
+
test('parseTaskPlanFile: invalid numeric frontmatter ignored', () => {
|
|
366
354
|
const content = `---
|
|
367
355
|
estimated_steps: many
|
|
368
356
|
estimated_files: unknown
|
|
@@ -372,12 +360,11 @@ estimated_files: unknown
|
|
|
372
360
|
`;
|
|
373
361
|
|
|
374
362
|
const taskPlan = parseTaskPlanFile(content);
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
363
|
+
assert.deepStrictEqual(taskPlan.frontmatter.estimated_steps, undefined, 'invalid estimated_steps ignored');
|
|
364
|
+
assert.deepStrictEqual(taskPlan.frontmatter.estimated_files, undefined, 'invalid estimated_files ignored');
|
|
365
|
+
});
|
|
378
366
|
|
|
379
|
-
|
|
380
|
-
{
|
|
367
|
+
test('parseTaskPlanFile: parsePlan ignores task-plan frontmatter', () => {
|
|
381
368
|
const content = `---
|
|
382
369
|
estimated_steps: 2
|
|
383
370
|
estimated_files: 1
|
|
@@ -397,12 +384,11 @@ skills_used:
|
|
|
397
384
|
`;
|
|
398
385
|
|
|
399
386
|
const p = parsePlan(content);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
387
|
+
assert.deepStrictEqual(p.id, 'S11', 'plan id still parsed with frontmatter');
|
|
388
|
+
assert.deepStrictEqual(p.tasks.length, 1, 'task still parsed with frontmatter');
|
|
389
|
+
});
|
|
403
390
|
|
|
404
|
-
|
|
405
|
-
{
|
|
391
|
+
test('parsePlan: multi-line task description concatenation', () => {
|
|
406
392
|
const content = `# S02: Multi-line Test
|
|
407
393
|
|
|
408
394
|
**Goal:** Test multi-line descriptions.
|
|
@@ -429,16 +415,15 @@ console.log('\n=== parsePlan: multi-line task description concatenation ===');
|
|
|
429
415
|
|
|
430
416
|
const p = parsePlan(content);
|
|
431
417
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
418
|
+
assert.deepStrictEqual(p.tasks.length, 2, 'two tasks');
|
|
419
|
+
assert.ok(p.tasks[0].description.includes('First line'), 'T01 desc has first line');
|
|
420
|
+
assert.ok(p.tasks[0].description.includes('Second line'), 'T01 desc has second line');
|
|
421
|
+
assert.ok(p.tasks[0].description.includes('Third line'), 'T01 desc has third line');
|
|
422
|
+
assert.ok(p.tasks[0].description.includes('description. Second'), 'lines joined with space');
|
|
423
|
+
assert.deepStrictEqual(p.tasks[1].description, 'Just one line.', 'T02 single-line desc');
|
|
424
|
+
});
|
|
439
425
|
|
|
440
|
-
|
|
441
|
-
{
|
|
426
|
+
test('parsePlan: frontmatter does not pollute task descriptions', () => {
|
|
442
427
|
const content = `---
|
|
443
428
|
estimated_steps: 2
|
|
444
429
|
estimated_files: 1
|
|
@@ -456,12 +441,11 @@ skills_used:
|
|
|
456
441
|
`;
|
|
457
442
|
|
|
458
443
|
const p = parsePlan(content);
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
444
|
+
assert.deepStrictEqual(p.tasks.length, 1, 'one task parsed with frontmatter');
|
|
445
|
+
assert.deepStrictEqual(p.tasks[0].description, 'First line of description. Second line of description.', 'frontmatter excluded from description');
|
|
446
|
+
});
|
|
462
447
|
|
|
463
|
-
|
|
464
|
-
{
|
|
448
|
+
test('parsePlan: task with missing estimate', () => {
|
|
465
449
|
const content = `# S03: No Estimate
|
|
466
450
|
|
|
467
451
|
**Goal:** Handle tasks without estimates.
|
|
@@ -477,15 +461,14 @@ console.log('\n=== parsePlan: task with missing estimate ===');
|
|
|
477
461
|
`;
|
|
478
462
|
|
|
479
463
|
const p = parsePlan(content);
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
{
|
|
464
|
+
assert.deepStrictEqual(p.tasks.length, 2, 'two tasks parsed');
|
|
465
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'T01 id');
|
|
466
|
+
assert.deepStrictEqual(p.tasks[0].title, 'No Estimate Task', 'T01 title without estimate');
|
|
467
|
+
assert.deepStrictEqual(p.tasks[0].done, false, 'T01 not done');
|
|
468
|
+
assert.deepStrictEqual(p.tasks[1].id, 'T02', 'T02 id');
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test('parsePlan: empty tasks section', () => {
|
|
489
472
|
const content = `# S04: Empty Tasks
|
|
490
473
|
|
|
491
474
|
**Goal:** No tasks yet.
|
|
@@ -503,14 +486,13 @@ console.log('\n=== parsePlan: empty tasks section ===');
|
|
|
503
486
|
`;
|
|
504
487
|
|
|
505
488
|
const p = parsePlan(content);
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
{
|
|
489
|
+
assert.deepStrictEqual(p.id, 'S04', 'plan id with empty tasks');
|
|
490
|
+
assert.deepStrictEqual(p.tasks.length, 0, 'no tasks');
|
|
491
|
+
assert.deepStrictEqual(p.mustHaves.length, 1, 'one must-have');
|
|
492
|
+
assert.deepStrictEqual(p.filesLikelyTouched.length, 1, 'one file');
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test('parsePlan: no H1', () => {
|
|
514
496
|
const content = `**Goal:** A plan without a heading.
|
|
515
497
|
**Demo:** Still parses.
|
|
516
498
|
|
|
@@ -521,15 +503,14 @@ console.log('\n=== parsePlan: no H1 ===');
|
|
|
521
503
|
`;
|
|
522
504
|
|
|
523
505
|
const p = parsePlan(content);
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
{
|
|
506
|
+
assert.deepStrictEqual(p.id, '', 'empty id without H1');
|
|
507
|
+
assert.deepStrictEqual(p.title, '', 'empty title without H1');
|
|
508
|
+
assert.deepStrictEqual(p.goal, 'A plan without a heading.', 'goal still parsed');
|
|
509
|
+
assert.deepStrictEqual(p.tasks.length, 1, 'task still parsed');
|
|
510
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'task id');
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test('parsePlan: task estimate backtick in description', () => {
|
|
533
514
|
const content = `# S05: Estimate Handling
|
|
534
515
|
|
|
535
516
|
**Goal:** Test estimate text handling.
|
|
@@ -542,14 +523,13 @@ console.log('\n=== parsePlan: task estimate backtick in description ===');
|
|
|
542
523
|
`;
|
|
543
524
|
|
|
544
525
|
const p = parsePlan(content);
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
{
|
|
526
|
+
assert.deepStrictEqual(p.tasks.length, 1, 'one task');
|
|
527
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'task id');
|
|
528
|
+
assert.deepStrictEqual(p.tasks[0].title, 'With Estimate', 'title excludes estimate');
|
|
529
|
+
assert.ok(p.tasks[0].description.includes('Main description'), 'description from continuation line');
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
test('parsePlan: uppercase X for done', () => {
|
|
553
533
|
const content = `# S06: Case Test
|
|
554
534
|
|
|
555
535
|
**Goal:** Test case.
|
|
@@ -565,12 +545,11 @@ console.log('\n=== parsePlan: uppercase X for done ===');
|
|
|
565
545
|
`;
|
|
566
546
|
|
|
567
547
|
const p = parsePlan(content);
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
548
|
+
assert.deepStrictEqual(p.tasks[0].done, true, 'uppercase X is done');
|
|
549
|
+
assert.deepStrictEqual(p.tasks[1].done, true, 'lowercase x is done');
|
|
550
|
+
});
|
|
571
551
|
|
|
572
|
-
|
|
573
|
-
{
|
|
552
|
+
test('parsePlan: no Must-Haves section', () => {
|
|
574
553
|
const content = `# S07: No Must-Haves
|
|
575
554
|
|
|
576
555
|
**Goal:** Test missing must-haves.
|
|
@@ -583,12 +562,11 @@ console.log('\n=== parsePlan: no Must-Haves section ===');
|
|
|
583
562
|
`;
|
|
584
563
|
|
|
585
564
|
const p = parsePlan(content);
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
}
|
|
565
|
+
assert.deepStrictEqual(p.mustHaves.length, 0, 'empty must-haves');
|
|
566
|
+
assert.deepStrictEqual(p.tasks.length, 1, 'task still parsed');
|
|
567
|
+
});
|
|
589
568
|
|
|
590
|
-
|
|
591
|
-
{
|
|
569
|
+
test('parsePlan: no Files Likely Touched section', () => {
|
|
592
570
|
const content = `# S08: No Files
|
|
593
571
|
|
|
594
572
|
**Goal:** Test missing files section.
|
|
@@ -601,11 +579,10 @@ console.log('\n=== parsePlan: no Files Likely Touched section ===');
|
|
|
601
579
|
`;
|
|
602
580
|
|
|
603
581
|
const p = parsePlan(content);
|
|
604
|
-
|
|
605
|
-
}
|
|
582
|
+
assert.deepStrictEqual(p.filesLikelyTouched.length, 0, 'empty files likely touched');
|
|
583
|
+
});
|
|
606
584
|
|
|
607
|
-
|
|
608
|
-
{
|
|
585
|
+
test('parsePlan: old-format task entries (no sublines)', () => {
|
|
609
586
|
const content = `# S09: Old Format
|
|
610
587
|
|
|
611
588
|
**Goal:** Test old-format compatibility.
|
|
@@ -618,16 +595,15 @@ console.log('\n=== parsePlan: old-format task entries (no sublines) ===');
|
|
|
618
595
|
`;
|
|
619
596
|
|
|
620
597
|
const p = parsePlan(content);
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
{
|
|
598
|
+
assert.deepStrictEqual(p.tasks.length, 1, 'one task parsed');
|
|
599
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'task id');
|
|
600
|
+
assert.deepStrictEqual(p.tasks[0].title, 'Classic Task', 'task title');
|
|
601
|
+
assert.deepStrictEqual(p.tasks[0].done, false, 'task not done');
|
|
602
|
+
assert.deepStrictEqual(p.tasks[0].files, undefined, 'files is undefined for old-format entry');
|
|
603
|
+
assert.deepStrictEqual(p.tasks[0].verify, undefined, 'verify is undefined for old-format entry');
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
test('parsePlan: new-format task entries with Files and Verify sublines', () => {
|
|
631
607
|
const content = `# S10: New Format
|
|
632
608
|
|
|
633
609
|
**Goal:** Test new-format subline extraction.
|
|
@@ -642,18 +618,17 @@ console.log('\n=== parsePlan: new-format task entries with Files and Verify subl
|
|
|
642
618
|
`;
|
|
643
619
|
|
|
644
620
|
const p = parsePlan(content);
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
{
|
|
621
|
+
assert.deepStrictEqual(p.tasks.length, 1, 'one task parsed');
|
|
622
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'task id');
|
|
623
|
+
assert.ok(Array.isArray(p.tasks[0].files), 'files is an array');
|
|
624
|
+
assert.deepStrictEqual(p.tasks[0].files!.length, 2, 'files array has two entries');
|
|
625
|
+
assert.deepStrictEqual(p.tasks[0].files![0], 'types.ts', 'first file is types.ts');
|
|
626
|
+
assert.deepStrictEqual(p.tasks[0].files![1], 'files.ts', 'second file is files.ts');
|
|
627
|
+
assert.deepStrictEqual(p.tasks[0].verify, 'run the test suite', 'verify string extracted correctly');
|
|
628
|
+
assert.ok(p.tasks[0].description.includes('Why: because we need typed plan entries'), 'Why line accumulates into description');
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
test('parsePlan: heading-style task entries (### T01 -- Title)', () => {
|
|
657
632
|
const content = `# S11: Heading Style
|
|
658
633
|
|
|
659
634
|
**Goal:** Test heading-style task parsing.
|
|
@@ -673,20 +648,19 @@ Some description for the second task.
|
|
|
673
648
|
`;
|
|
674
649
|
|
|
675
650
|
const p = parsePlan(content);
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
{
|
|
651
|
+
assert.deepStrictEqual(p.tasks.length, 2, 'heading-style task count');
|
|
652
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'heading T01 id');
|
|
653
|
+
assert.deepStrictEqual(p.tasks[0].title, 'Implement feature', 'heading T01 title');
|
|
654
|
+
assert.deepStrictEqual(p.tasks[0].done, false, 'heading T01 not done (headings have no checkbox)');
|
|
655
|
+
assert.deepStrictEqual(p.tasks[0].files![0], 'src/feature.ts', 'heading T01 files extracted');
|
|
656
|
+
assert.deepStrictEqual(p.tasks[0].verify, 'npm test', 'heading T01 verify extracted');
|
|
657
|
+
assert.deepStrictEqual(p.tasks[1].id, 'T02', 'heading T02 id');
|
|
658
|
+
assert.deepStrictEqual(p.tasks[1].title, 'Write tests', 'heading T02 title');
|
|
659
|
+
assert.deepStrictEqual(p.tasks[1].estimate, '1h', 'heading T02 estimate');
|
|
660
|
+
assert.ok(p.tasks[1].description.includes('Some description'), 'heading T02 description');
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
test('parsePlan: heading-style with colon separator (### T01: Title)', () => {
|
|
690
664
|
const content = `# S12: Heading Colon Style
|
|
691
665
|
|
|
692
666
|
**Goal:** Test colon-separated heading tasks.
|
|
@@ -702,16 +676,15 @@ console.log('\n=== parsePlan: heading-style with colon separator (### T01: Title
|
|
|
702
676
|
`;
|
|
703
677
|
|
|
704
678
|
const p = parsePlan(content);
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
{
|
|
679
|
+
assert.deepStrictEqual(p.tasks.length, 2, 'colon heading task count');
|
|
680
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'colon heading T01 id');
|
|
681
|
+
assert.deepStrictEqual(p.tasks[0].title, 'Setup project', 'colon heading T01 title');
|
|
682
|
+
assert.deepStrictEqual(p.tasks[1].id, 'T02', 'colon heading T02 id');
|
|
683
|
+
assert.deepStrictEqual(p.tasks[1].title, 'Add CI pipeline', 'colon heading T02 title');
|
|
684
|
+
assert.deepStrictEqual(p.tasks[1].estimate, '30m', 'colon heading T02 estimate');
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
test('parsePlan: heading-style with em-dash separator (### T01 — Title)', () => {
|
|
715
688
|
const content = `# S13: Em-Dash Style
|
|
716
689
|
|
|
717
690
|
**Goal:** Test em-dash separated heading tasks.
|
|
@@ -725,13 +698,12 @@ Widget description.
|
|
|
725
698
|
`;
|
|
726
699
|
|
|
727
700
|
const p = parsePlan(content);
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
}
|
|
701
|
+
assert.deepStrictEqual(p.tasks.length, 1, 'em-dash heading task count');
|
|
702
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'em-dash heading T01 id');
|
|
703
|
+
assert.deepStrictEqual(p.tasks[0].title, 'Build the widget', 'em-dash heading T01 title');
|
|
704
|
+
});
|
|
732
705
|
|
|
733
|
-
|
|
734
|
-
{
|
|
706
|
+
test('parsePlan: mixed checkbox and heading-style tasks', () => {
|
|
735
707
|
const content = `# S14: Mixed Format
|
|
736
708
|
|
|
737
709
|
**Goal:** Test mixed formats.
|
|
@@ -751,23 +723,21 @@ A heading-style task.
|
|
|
751
723
|
`;
|
|
752
724
|
|
|
753
725
|
const p = parsePlan(content);
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
}
|
|
726
|
+
assert.deepStrictEqual(p.tasks.length, 3, 'mixed format task count');
|
|
727
|
+
assert.deepStrictEqual(p.tasks[0].id, 'T01', 'mixed T01 id');
|
|
728
|
+
assert.deepStrictEqual(p.tasks[0].done, false, 'mixed T01 not done');
|
|
729
|
+
assert.deepStrictEqual(p.tasks[1].id, 'T02', 'mixed T02 id');
|
|
730
|
+
assert.deepStrictEqual(p.tasks[1].title, 'Heading task', 'mixed T02 title');
|
|
731
|
+
assert.deepStrictEqual(p.tasks[1].estimate, '15m', 'mixed T02 estimate');
|
|
732
|
+
assert.deepStrictEqual(p.tasks[1].done, false, 'mixed T02 not done (heading style)');
|
|
733
|
+
assert.deepStrictEqual(p.tasks[2].id, 'T03', 'mixed T03 id');
|
|
734
|
+
assert.deepStrictEqual(p.tasks[2].done, true, 'mixed T03 done');
|
|
735
|
+
});
|
|
764
736
|
|
|
765
737
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
766
738
|
// parseSummary tests
|
|
767
739
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
768
|
-
|
|
769
|
-
console.log('\n=== parseSummary: full summary with all frontmatter fields ===');
|
|
770
|
-
{
|
|
740
|
+
test('parseSummary: full summary with all frontmatter fields', () => {
|
|
771
741
|
const content = `---
|
|
772
742
|
id: T01
|
|
773
743
|
parent: S01
|
|
@@ -822,52 +792,51 @@ None.
|
|
|
822
792
|
const s = parseSummary(content);
|
|
823
793
|
|
|
824
794
|
// Frontmatter fields
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
795
|
+
assert.deepStrictEqual(s.frontmatter.id, 'T01', 'summary id');
|
|
796
|
+
assert.deepStrictEqual(s.frontmatter.parent, 'S01', 'summary parent');
|
|
797
|
+
assert.deepStrictEqual(s.frontmatter.milestone, 'M001', 'summary milestone');
|
|
798
|
+
assert.deepStrictEqual(s.frontmatter.provides.length, 2, 'provides count');
|
|
799
|
+
assert.deepStrictEqual(s.frontmatter.provides[0], 'parseRoadmap test coverage', 'first provides');
|
|
800
|
+
assert.deepStrictEqual(s.frontmatter.provides[1], 'parsePlan test coverage', 'second provides');
|
|
831
801
|
|
|
832
802
|
// requires (nested objects)
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
803
|
+
assert.deepStrictEqual(s.frontmatter.requires.length, 2, 'requires count');
|
|
804
|
+
assert.deepStrictEqual(s.frontmatter.requires[0].slice, 'S00', 'first requires slice');
|
|
805
|
+
assert.deepStrictEqual(s.frontmatter.requires[0].provides, 'type definitions', 'first requires provides');
|
|
806
|
+
assert.deepStrictEqual(s.frontmatter.requires[1].slice, 'S02', 'second requires slice');
|
|
807
|
+
assert.deepStrictEqual(s.frontmatter.requires[1].provides, 'state derivation', 'second requires provides');
|
|
808
|
+
|
|
809
|
+
assert.deepStrictEqual(s.frontmatter.affects.length, 1, 'affects count');
|
|
810
|
+
assert.deepStrictEqual(s.frontmatter.affects[0], 'auto-mode dispatch', 'affects value');
|
|
811
|
+
assert.deepStrictEqual(s.frontmatter.key_files.length, 2, 'key_files count');
|
|
812
|
+
assert.deepStrictEqual(s.frontmatter.key_decisions.length, 1, 'key_decisions count');
|
|
813
|
+
assert.deepStrictEqual(s.frontmatter.patterns_established.length, 1, 'patterns_established count');
|
|
814
|
+
assert.deepStrictEqual(s.frontmatter.drill_down_paths.length, 1, 'drill_down_paths count');
|
|
845
815
|
|
|
846
816
|
// observability_surfaces extraction
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
817
|
+
assert.deepStrictEqual(s.frontmatter.observability_surfaces.length, 2, 'observability_surfaces count');
|
|
818
|
+
assert.deepStrictEqual(s.frontmatter.observability_surfaces[0], 'test pass/fail output from node --test', 'first observability surface');
|
|
819
|
+
assert.deepStrictEqual(s.frontmatter.observability_surfaces[1], 'exit code 1 on failure', 'second observability surface');
|
|
850
820
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
821
|
+
assert.deepStrictEqual(s.frontmatter.duration, '23min', 'duration');
|
|
822
|
+
assert.deepStrictEqual(s.frontmatter.verification_result, 'pass', 'verification_result');
|
|
823
|
+
assert.deepStrictEqual(s.frontmatter.completed_at, '2025-03-10T08:00:00Z', 'completed_at');
|
|
854
824
|
|
|
855
825
|
// Body fields
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
826
|
+
assert.deepStrictEqual(s.title, 'T01: Test parseRoadmap and parsePlan', 'summary title');
|
|
827
|
+
assert.deepStrictEqual(s.oneLiner, 'Created parsers.test.ts with 98 assertions across 16 test groups.', 'one-liner');
|
|
828
|
+
assert.ok(s.whatHappened.includes('comprehensive tests'), 'whatHappened content');
|
|
829
|
+
assert.deepStrictEqual(s.deviations, 'None.', 'deviations');
|
|
860
830
|
|
|
861
831
|
// Files modified
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
{
|
|
832
|
+
assert.deepStrictEqual(s.filesModified.length, 3, 'filesModified count');
|
|
833
|
+
assert.deepStrictEqual(s.filesModified[0].path, 'tests/parsers.test.ts', 'first file path');
|
|
834
|
+
assert.ok(s.filesModified[0].description.includes('98 assertions'), 'first file description');
|
|
835
|
+
assert.deepStrictEqual(s.filesModified[1].path, 'types.ts', 'second file path');
|
|
836
|
+
assert.deepStrictEqual(s.filesModified[2].path, 'files.ts', 'third file path');
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
test('parseSummary: one-liner extraction (bold-wrapped line after H1)', () => {
|
|
871
840
|
const content = `# S01: Parser Test Suite
|
|
872
841
|
|
|
873
842
|
**All 5 parsers have test coverage with edge cases.**
|
|
@@ -878,12 +847,11 @@ Things happened.
|
|
|
878
847
|
`;
|
|
879
848
|
|
|
880
849
|
const s = parseSummary(content);
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
}
|
|
850
|
+
assert.deepStrictEqual(s.title, 'S01: Parser Test Suite', 'title');
|
|
851
|
+
assert.deepStrictEqual(s.oneLiner, 'All 5 parsers have test coverage with edge cases.', 'bold one-liner');
|
|
852
|
+
});
|
|
884
853
|
|
|
885
|
-
|
|
886
|
-
{
|
|
854
|
+
test('parseSummary: non-bold paragraph after H1 (empty one-liner)', () => {
|
|
887
855
|
const content = `# T02: Some Task
|
|
888
856
|
|
|
889
857
|
This is just a regular paragraph, not bold.
|
|
@@ -894,12 +862,11 @@ Did stuff.
|
|
|
894
862
|
`;
|
|
895
863
|
|
|
896
864
|
const s = parseSummary(content);
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
}
|
|
865
|
+
assert.deepStrictEqual(s.title, 'T02: Some Task', 'title');
|
|
866
|
+
assert.deepStrictEqual(s.oneLiner, '', 'non-bold line results in empty one-liner');
|
|
867
|
+
});
|
|
900
868
|
|
|
901
|
-
|
|
902
|
-
{
|
|
869
|
+
test('parseSummary: files-modified parsing (backtick path — description format)', () => {
|
|
903
870
|
const content = `# T03: File Changes
|
|
904
871
|
|
|
905
872
|
**One-liner.**
|
|
@@ -912,15 +879,14 @@ console.log('\n=== parseSummary: files-modified parsing (backtick path — descr
|
|
|
912
879
|
`;
|
|
913
880
|
|
|
914
881
|
const s = parseSummary(content);
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
{
|
|
882
|
+
assert.deepStrictEqual(s.filesModified.length, 3, 'three files');
|
|
883
|
+
assert.deepStrictEqual(s.filesModified[0].path, 'src/index.ts', 'first path');
|
|
884
|
+
assert.deepStrictEqual(s.filesModified[0].description, 'main entry point', 'first description');
|
|
885
|
+
assert.deepStrictEqual(s.filesModified[1].path, 'src/utils.ts', 'second path');
|
|
886
|
+
assert.deepStrictEqual(s.filesModified[2].path, 'README.md', 'third path');
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
test('parseSummary: missing frontmatter (safe defaults)', () => {
|
|
924
890
|
const content = `# T04: No Frontmatter
|
|
925
891
|
|
|
926
892
|
**Did something.**
|
|
@@ -931,26 +897,25 @@ No frontmatter at all.
|
|
|
931
897
|
`;
|
|
932
898
|
|
|
933
899
|
const s = parseSummary(content);
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
{
|
|
900
|
+
assert.deepStrictEqual(s.frontmatter.id, '', 'default id empty');
|
|
901
|
+
assert.deepStrictEqual(s.frontmatter.parent, '', 'default parent empty');
|
|
902
|
+
assert.deepStrictEqual(s.frontmatter.milestone, '', 'default milestone empty');
|
|
903
|
+
assert.deepStrictEqual(s.frontmatter.provides.length, 0, 'default provides empty');
|
|
904
|
+
assert.deepStrictEqual(s.frontmatter.requires.length, 0, 'default requires empty');
|
|
905
|
+
assert.deepStrictEqual(s.frontmatter.affects.length, 0, 'default affects empty');
|
|
906
|
+
assert.deepStrictEqual(s.frontmatter.key_files.length, 0, 'default key_files empty');
|
|
907
|
+
assert.deepStrictEqual(s.frontmatter.key_decisions.length, 0, 'default key_decisions empty');
|
|
908
|
+
assert.deepStrictEqual(s.frontmatter.patterns_established.length, 0, 'default patterns_established empty');
|
|
909
|
+
assert.deepStrictEqual(s.frontmatter.drill_down_paths.length, 0, 'default drill_down_paths empty');
|
|
910
|
+
assert.deepStrictEqual(s.frontmatter.observability_surfaces.length, 0, 'default observability_surfaces empty');
|
|
911
|
+
assert.deepStrictEqual(s.frontmatter.duration, '', 'default duration empty');
|
|
912
|
+
assert.deepStrictEqual(s.frontmatter.verification_result, 'untested', 'default verification_result');
|
|
913
|
+
assert.deepStrictEqual(s.frontmatter.completed_at, '', 'default completed_at empty');
|
|
914
|
+
assert.deepStrictEqual(s.title, 'T04: No Frontmatter', 'title still parsed');
|
|
915
|
+
assert.deepStrictEqual(s.oneLiner, 'Did something.', 'one-liner still parsed');
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
test('parseSummary: empty body', () => {
|
|
954
919
|
const content = `---
|
|
955
920
|
id: T05
|
|
956
921
|
parent: S01
|
|
@@ -959,16 +924,15 @@ milestone: M001
|
|
|
959
924
|
`;
|
|
960
925
|
|
|
961
926
|
const s = parseSummary(content);
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
{
|
|
927
|
+
assert.deepStrictEqual(s.frontmatter.id, 'T05', 'id from frontmatter');
|
|
928
|
+
assert.deepStrictEqual(s.title, '', 'empty title');
|
|
929
|
+
assert.deepStrictEqual(s.oneLiner, '', 'empty one-liner');
|
|
930
|
+
assert.deepStrictEqual(s.whatHappened, '', 'empty whatHappened');
|
|
931
|
+
assert.deepStrictEqual(s.deviations, '', 'empty deviations');
|
|
932
|
+
assert.deepStrictEqual(s.filesModified.length, 0, 'no files modified');
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
test('parseSummary: summary with requires array (nested objects)', () => {
|
|
972
936
|
const content = `---
|
|
973
937
|
id: T06
|
|
974
938
|
parent: S02
|
|
@@ -1003,20 +967,18 @@ Tested.
|
|
|
1003
967
|
`;
|
|
1004
968
|
|
|
1005
969
|
const s = parseSummary(content);
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
}
|
|
970
|
+
assert.deepStrictEqual(s.frontmatter.requires.length, 3, 'three requires entries');
|
|
971
|
+
assert.deepStrictEqual(s.frontmatter.requires[0].slice, 'S01', 'first requires slice');
|
|
972
|
+
assert.deepStrictEqual(s.frontmatter.requires[0].provides, 'parser functions', 'first requires provides');
|
|
973
|
+
assert.deepStrictEqual(s.frontmatter.requires[1].slice, 'S00', 'second requires slice');
|
|
974
|
+
assert.deepStrictEqual(s.frontmatter.requires[2].slice, 'S03', 'third requires slice');
|
|
975
|
+
assert.deepStrictEqual(s.frontmatter.requires[2].provides, 'state engine', 'third requires provides');
|
|
976
|
+
});
|
|
1013
977
|
|
|
1014
978
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1015
979
|
// parseContinue tests
|
|
1016
980
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1017
|
-
|
|
1018
|
-
console.log('\n=== parseContinue: full continue file with all frontmatter fields ===');
|
|
1019
|
-
{
|
|
981
|
+
test('parseContinue: full continue file with all frontmatter fields', () => {
|
|
1020
982
|
const content = `---
|
|
1021
983
|
milestone: M001
|
|
1022
984
|
slice: S01
|
|
@@ -1051,24 +1013,23 @@ Run the full test suite with node --test.
|
|
|
1051
1013
|
const c = parseContinue(content);
|
|
1052
1014
|
|
|
1053
1015
|
// Frontmatter
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1016
|
+
assert.deepStrictEqual(c.frontmatter.milestone, 'M001', 'continue milestone');
|
|
1017
|
+
assert.deepStrictEqual(c.frontmatter.slice, 'S01', 'continue slice');
|
|
1018
|
+
assert.deepStrictEqual(c.frontmatter.task, 'T02', 'continue task');
|
|
1019
|
+
assert.deepStrictEqual(c.frontmatter.step, 3, 'continue step');
|
|
1020
|
+
assert.deepStrictEqual(c.frontmatter.totalSteps, 5, 'continue totalSteps');
|
|
1021
|
+
assert.deepStrictEqual(c.frontmatter.status, 'in_progress', 'continue status');
|
|
1022
|
+
assert.deepStrictEqual(c.frontmatter.savedAt, '2025-03-10T08:30:00Z', 'continue savedAt');
|
|
1061
1023
|
|
|
1062
1024
|
// Body sections
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
{
|
|
1025
|
+
assert.ok(c.completedWork.includes('Steps 1-3 are done'), 'completedWork content');
|
|
1026
|
+
assert.ok(c.remainingWork.includes('Steps 4-5'), 'remainingWork content');
|
|
1027
|
+
assert.ok(c.decisions.includes('manual assert pattern'), 'decisions content');
|
|
1028
|
+
assert.ok(c.context.includes('gsd-s01 worktree'), 'context content');
|
|
1029
|
+
assert.ok(c.nextAction.includes('node --test'), 'nextAction content');
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
test('parseContinue: string step/totalSteps parsed as integers', () => {
|
|
1072
1033
|
const content = `---
|
|
1073
1034
|
milestone: M002
|
|
1074
1035
|
slice: S03
|
|
@@ -1101,14 +1062,13 @@ Continue.
|
|
|
1101
1062
|
`;
|
|
1102
1063
|
|
|
1103
1064
|
const c = parseContinue(content);
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
{
|
|
1065
|
+
assert.deepStrictEqual(c.frontmatter.step, 7, 'step parsed as integer 7');
|
|
1066
|
+
assert.deepStrictEqual(c.frontmatter.totalSteps, 12, 'totalSteps parsed as integer 12');
|
|
1067
|
+
assert.deepStrictEqual(typeof c.frontmatter.step, 'number', 'step is number type');
|
|
1068
|
+
assert.deepStrictEqual(typeof c.frontmatter.totalSteps, 'number', 'totalSteps is number type');
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
test('parseContinue: NaN step values (non-numeric strings)', () => {
|
|
1112
1072
|
const content = `---
|
|
1113
1073
|
milestone: M001
|
|
1114
1074
|
slice: S01
|
|
@@ -1150,12 +1110,11 @@ Do things.
|
|
|
1150
1110
|
const totalIsNaN = Number.isNaN(c.frontmatter.totalSteps);
|
|
1151
1111
|
// The parser does parseInt which returns NaN for non-numeric strings
|
|
1152
1112
|
// There's no || 0 fallback on the parseInt path, so NaN is expected
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1113
|
+
assert.ok(stepIsNaN, 'NaN step when non-numeric string');
|
|
1114
|
+
assert.ok(totalIsNaN, 'NaN totalSteps when non-numeric string');
|
|
1115
|
+
});
|
|
1156
1116
|
|
|
1157
|
-
|
|
1158
|
-
{
|
|
1117
|
+
test('parseContinue: all three status variants', () => {
|
|
1159
1118
|
for (const status of ['in_progress', 'interrupted', 'compacted'] as const) {
|
|
1160
1119
|
const content = `---
|
|
1161
1120
|
milestone: M001
|
|
@@ -1173,12 +1132,11 @@ Work.
|
|
|
1173
1132
|
`;
|
|
1174
1133
|
|
|
1175
1134
|
const c = parseContinue(content);
|
|
1176
|
-
|
|
1135
|
+
assert.deepStrictEqual(c.frontmatter.status, status, `status variant: ${status}`);
|
|
1177
1136
|
}
|
|
1178
|
-
}
|
|
1137
|
+
});
|
|
1179
1138
|
|
|
1180
|
-
|
|
1181
|
-
{
|
|
1139
|
+
test('parseContinue: missing frontmatter', () => {
|
|
1182
1140
|
const content = `## Completed Work
|
|
1183
1141
|
|
|
1184
1142
|
Some work done.
|
|
@@ -1201,24 +1159,23 @@ Next thing.
|
|
|
1201
1159
|
`;
|
|
1202
1160
|
|
|
1203
1161
|
const c = parseContinue(content);
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1162
|
+
assert.deepStrictEqual(c.frontmatter.milestone, '', 'default milestone empty');
|
|
1163
|
+
assert.deepStrictEqual(c.frontmatter.slice, '', 'default slice empty');
|
|
1164
|
+
assert.deepStrictEqual(c.frontmatter.task, '', 'default task empty');
|
|
1165
|
+
assert.deepStrictEqual(c.frontmatter.step, 0, 'default step 0');
|
|
1166
|
+
assert.deepStrictEqual(c.frontmatter.totalSteps, 0, 'default totalSteps 0');
|
|
1167
|
+
assert.deepStrictEqual(c.frontmatter.status, 'in_progress', 'default status in_progress');
|
|
1168
|
+
assert.deepStrictEqual(c.frontmatter.savedAt, '', 'default savedAt empty');
|
|
1211
1169
|
|
|
1212
1170
|
// Body sections still parse
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
{
|
|
1171
|
+
assert.ok(c.completedWork.includes('Some work done'), 'completedWork without frontmatter');
|
|
1172
|
+
assert.ok(c.remainingWork.includes('More to do'), 'remainingWork without frontmatter');
|
|
1173
|
+
assert.ok(c.decisions.includes('A decision'), 'decisions without frontmatter');
|
|
1174
|
+
assert.ok(c.context.includes('Some context'), 'context without frontmatter');
|
|
1175
|
+
assert.ok(c.nextAction.includes('Next thing'), 'nextAction without frontmatter');
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
test('parseContinue: body section extraction', () => {
|
|
1222
1179
|
const content = `---
|
|
1223
1180
|
milestone: M001
|
|
1224
1181
|
slice: S01
|
|
@@ -1252,16 +1209,15 @@ Pick up at step 3: run the integration tests.
|
|
|
1252
1209
|
`;
|
|
1253
1210
|
|
|
1254
1211
|
const c = parseContinue(content);
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
{
|
|
1212
|
+
assert.ok(c.completedWork.includes('First paragraph'), 'completedWork first paragraph');
|
|
1213
|
+
assert.ok(c.completedWork.includes('Second paragraph'), 'completedWork second paragraph');
|
|
1214
|
+
assert.ok(c.remainingWork.includes('step 3 and step 4'), 'remainingWork detail');
|
|
1215
|
+
assert.ok(c.decisions.includes('approach A over approach B'), 'decisions detail');
|
|
1216
|
+
assert.ok(c.context.includes('Node 22 required'), 'context detail');
|
|
1217
|
+
assert.ok(c.nextAction.includes('step 3: run the integration tests'), 'nextAction detail');
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
test('parseContinue: total_steps vs totalSteps key support', () => {
|
|
1265
1221
|
// Test total_steps (snake_case) — the primary format
|
|
1266
1222
|
const content1 = `---
|
|
1267
1223
|
milestone: M001
|
|
@@ -1279,7 +1235,7 @@ Work.
|
|
|
1279
1235
|
`;
|
|
1280
1236
|
|
|
1281
1237
|
const c1 = parseContinue(content1);
|
|
1282
|
-
|
|
1238
|
+
assert.deepStrictEqual(c1.frontmatter.totalSteps, 8, 'total_steps snake_case works');
|
|
1283
1239
|
|
|
1284
1240
|
// Test totalSteps (camelCase) — the fallback
|
|
1285
1241
|
const content2 = `---
|
|
@@ -1298,15 +1254,13 @@ Work.
|
|
|
1298
1254
|
`;
|
|
1299
1255
|
|
|
1300
1256
|
const c2 = parseContinue(content2);
|
|
1301
|
-
|
|
1302
|
-
}
|
|
1257
|
+
assert.deepStrictEqual(c2.frontmatter.totalSteps, 6, 'totalSteps camelCase works');
|
|
1258
|
+
});
|
|
1303
1259
|
|
|
1304
1260
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1305
1261
|
// parseRequirementCounts tests
|
|
1306
1262
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1307
|
-
|
|
1308
|
-
console.log('\n=== parseRequirementCounts: full requirements file ===');
|
|
1309
|
-
{
|
|
1263
|
+
test('parseRequirementCounts: full requirements file', () => {
|
|
1310
1264
|
const content = `# Requirements
|
|
1311
1265
|
|
|
1312
1266
|
## Active
|
|
@@ -1343,27 +1297,25 @@ console.log('\n=== parseRequirementCounts: full requirements file ===');
|
|
|
1343
1297
|
`;
|
|
1344
1298
|
|
|
1345
1299
|
const counts = parseRequirementCounts(content);
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
{
|
|
1300
|
+
assert.deepStrictEqual(counts.active, 3, 'active count');
|
|
1301
|
+
assert.deepStrictEqual(counts.validated, 2, 'validated count');
|
|
1302
|
+
assert.deepStrictEqual(counts.deferred, 1, 'deferred count');
|
|
1303
|
+
assert.deepStrictEqual(counts.outOfScope, 2, 'outOfScope count');
|
|
1304
|
+
assert.deepStrictEqual(counts.blocked, 1, 'blocked count');
|
|
1305
|
+
assert.deepStrictEqual(counts.total, 8, 'total is sum of active+validated+deferred+outOfScope');
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
test('parseRequirementCounts: null input returns all zeros', () => {
|
|
1356
1309
|
const counts = parseRequirementCounts(null);
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
{
|
|
1310
|
+
assert.deepStrictEqual(counts.active, 0, 'null active');
|
|
1311
|
+
assert.deepStrictEqual(counts.validated, 0, 'null validated');
|
|
1312
|
+
assert.deepStrictEqual(counts.deferred, 0, 'null deferred');
|
|
1313
|
+
assert.deepStrictEqual(counts.outOfScope, 0, 'null outOfScope');
|
|
1314
|
+
assert.deepStrictEqual(counts.blocked, 0, 'null blocked');
|
|
1315
|
+
assert.deepStrictEqual(counts.total, 0, 'null total');
|
|
1316
|
+
});
|
|
1317
|
+
|
|
1318
|
+
test('parseRequirementCounts: empty sections return zero counts', () => {
|
|
1367
1319
|
const content = `# Requirements
|
|
1368
1320
|
|
|
1369
1321
|
## Active
|
|
@@ -1376,16 +1328,15 @@ console.log('\n=== parseRequirementCounts: empty sections return zero counts ===
|
|
|
1376
1328
|
`;
|
|
1377
1329
|
|
|
1378
1330
|
const counts = parseRequirementCounts(content);
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
{
|
|
1331
|
+
assert.deepStrictEqual(counts.active, 0, 'empty active');
|
|
1332
|
+
assert.deepStrictEqual(counts.validated, 0, 'empty validated');
|
|
1333
|
+
assert.deepStrictEqual(counts.deferred, 0, 'empty deferred');
|
|
1334
|
+
assert.deepStrictEqual(counts.outOfScope, 0, 'empty outOfScope');
|
|
1335
|
+
assert.deepStrictEqual(counts.blocked, 0, 'empty blocked');
|
|
1336
|
+
assert.deepStrictEqual(counts.total, 0, 'empty total');
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
test('parseRequirementCounts: blocked status counting', () => {
|
|
1389
1340
|
const content = `# Requirements
|
|
1390
1341
|
|
|
1391
1342
|
## Active
|
|
@@ -1410,13 +1361,12 @@ console.log('\n=== parseRequirementCounts: blocked status counting ===');
|
|
|
1410
1361
|
`;
|
|
1411
1362
|
|
|
1412
1363
|
const counts = parseRequirementCounts(content);
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
}
|
|
1364
|
+
assert.deepStrictEqual(counts.active, 3, 'active includes blocked items in Active section');
|
|
1365
|
+
assert.deepStrictEqual(counts.blocked, 3, 'blocked counts all blocked statuses across sections');
|
|
1366
|
+
assert.deepStrictEqual(counts.deferred, 1, 'deferred section count');
|
|
1367
|
+
});
|
|
1417
1368
|
|
|
1418
|
-
|
|
1419
|
-
{
|
|
1369
|
+
test('parseRequirementCounts: total is sum of all section counts', () => {
|
|
1420
1370
|
const content = `# Requirements
|
|
1421
1371
|
|
|
1422
1372
|
## Active
|
|
@@ -1450,20 +1400,18 @@ console.log('\n=== parseRequirementCounts: total is sum of all section counts ==
|
|
|
1450
1400
|
`;
|
|
1451
1401
|
|
|
1452
1402
|
const counts = parseRequirementCounts(content);
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
}
|
|
1403
|
+
assert.deepStrictEqual(counts.active, 1, 'one active');
|
|
1404
|
+
assert.deepStrictEqual(counts.validated, 2, 'two validated');
|
|
1405
|
+
assert.deepStrictEqual(counts.deferred, 3, 'three deferred');
|
|
1406
|
+
assert.deepStrictEqual(counts.outOfScope, 1, 'one outOfScope');
|
|
1407
|
+
assert.deepStrictEqual(counts.total, 7, 'total = 1 + 2 + 3 + 1');
|
|
1408
|
+
assert.deepStrictEqual(counts.total, counts.active + counts.validated + counts.deferred + counts.outOfScope, 'total is exact sum');
|
|
1409
|
+
});
|
|
1460
1410
|
|
|
1461
1411
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1462
1412
|
// parseSecretsManifest / formatSecretsManifest tests
|
|
1463
1413
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1464
|
-
|
|
1465
|
-
console.log('\n=== parseSecretsManifest: full manifest with 3 keys ===');
|
|
1466
|
-
{
|
|
1414
|
+
test('parseSecretsManifest: full manifest with 3 keys', () => {
|
|
1467
1415
|
const content = `# Secrets Manifest
|
|
1468
1416
|
|
|
1469
1417
|
**Milestone:** M003
|
|
@@ -1507,37 +1455,36 @@ console.log('\n=== parseSecretsManifest: full manifest with 3 keys ===');
|
|
|
1507
1455
|
|
|
1508
1456
|
const m = parseSecretsManifest(content);
|
|
1509
1457
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1458
|
+
assert.deepStrictEqual(m.milestone, 'M003', 'manifest milestone');
|
|
1459
|
+
assert.deepStrictEqual(m.generatedAt, '2025-06-15T10:00:00Z', 'manifest generatedAt');
|
|
1460
|
+
assert.deepStrictEqual(m.entries.length, 3, 'three entries');
|
|
1513
1461
|
|
|
1514
1462
|
// First entry
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1463
|
+
assert.deepStrictEqual(m.entries[0].key, 'OPENAI_API_KEY', 'entry 0 key');
|
|
1464
|
+
assert.deepStrictEqual(m.entries[0].service, 'OpenAI', 'entry 0 service');
|
|
1465
|
+
assert.deepStrictEqual(m.entries[0].dashboardUrl, 'https://platform.openai.com/api-keys', 'entry 0 dashboardUrl');
|
|
1466
|
+
assert.deepStrictEqual(m.entries[0].formatHint, 'starts with sk-', 'entry 0 formatHint');
|
|
1467
|
+
assert.deepStrictEqual(m.entries[0].status, 'pending', 'entry 0 status');
|
|
1468
|
+
assert.deepStrictEqual(m.entries[0].destination, 'dotenv', 'entry 0 destination');
|
|
1469
|
+
assert.deepStrictEqual(m.entries[0].guidance.length, 3, 'entry 0 guidance count');
|
|
1470
|
+
assert.deepStrictEqual(m.entries[0].guidance[0], 'Go to https://platform.openai.com/api-keys', 'entry 0 guidance[0]');
|
|
1471
|
+
assert.deepStrictEqual(m.entries[0].guidance[2], 'Copy the key immediately — it won\'t be shown again', 'entry 0 guidance[2]');
|
|
1524
1472
|
|
|
1525
1473
|
// Second entry
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1474
|
+
assert.deepStrictEqual(m.entries[1].key, 'STRIPE_SECRET_KEY', 'entry 1 key');
|
|
1475
|
+
assert.deepStrictEqual(m.entries[1].service, 'Stripe', 'entry 1 service');
|
|
1476
|
+
assert.deepStrictEqual(m.entries[1].status, 'collected', 'entry 1 status');
|
|
1477
|
+
assert.deepStrictEqual(m.entries[1].formatHint, 'starts with sk_test_ or sk_live_', 'entry 1 formatHint');
|
|
1478
|
+
assert.deepStrictEqual(m.entries[1].guidance.length, 3, 'entry 1 guidance count');
|
|
1531
1479
|
|
|
1532
1480
|
// Third entry
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
{
|
|
1481
|
+
assert.deepStrictEqual(m.entries[2].key, 'SUPABASE_URL', 'entry 2 key');
|
|
1482
|
+
assert.deepStrictEqual(m.entries[2].status, 'skipped', 'entry 2 status');
|
|
1483
|
+
assert.deepStrictEqual(m.entries[2].destination, 'vercel', 'entry 2 destination');
|
|
1484
|
+
assert.deepStrictEqual(m.entries[2].guidance.length, 2, 'entry 2 guidance count');
|
|
1485
|
+
});
|
|
1486
|
+
|
|
1487
|
+
test('parseSecretsManifest: single-key manifest', () => {
|
|
1541
1488
|
const content = `# Secrets Manifest
|
|
1542
1489
|
|
|
1543
1490
|
**Milestone:** M001
|
|
@@ -1556,15 +1503,14 @@ console.log('\n=== parseSecretsManifest: single-key manifest ===');
|
|
|
1556
1503
|
`;
|
|
1557
1504
|
|
|
1558
1505
|
const m = parseSecretsManifest(content);
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
{
|
|
1506
|
+
assert.deepStrictEqual(m.milestone, 'M001', 'single-key milestone');
|
|
1507
|
+
assert.deepStrictEqual(m.entries.length, 1, 'single entry');
|
|
1508
|
+
assert.deepStrictEqual(m.entries[0].key, 'DATABASE_URL', 'single entry key');
|
|
1509
|
+
assert.deepStrictEqual(m.entries[0].service, 'PostgreSQL', 'single entry service');
|
|
1510
|
+
assert.deepStrictEqual(m.entries[0].guidance.length, 2, 'single entry guidance count');
|
|
1511
|
+
});
|
|
1512
|
+
|
|
1513
|
+
test('parseSecretsManifest: empty/no-secrets manifest', () => {
|
|
1568
1514
|
const content = `# Secrets Manifest
|
|
1569
1515
|
|
|
1570
1516
|
**Milestone:** M002
|
|
@@ -1572,13 +1518,12 @@ console.log('\n=== parseSecretsManifest: empty/no-secrets manifest ===');
|
|
|
1572
1518
|
`;
|
|
1573
1519
|
|
|
1574
1520
|
const m = parseSecretsManifest(content);
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
}
|
|
1521
|
+
assert.deepStrictEqual(m.milestone, 'M002', 'empty manifest milestone');
|
|
1522
|
+
assert.deepStrictEqual(m.generatedAt, '2025-06-15T14:00:00Z', 'empty manifest generatedAt');
|
|
1523
|
+
assert.deepStrictEqual(m.entries.length, 0, 'no entries in empty manifest');
|
|
1524
|
+
});
|
|
1579
1525
|
|
|
1580
|
-
|
|
1581
|
-
{
|
|
1526
|
+
test('parseSecretsManifest: missing optional fields default correctly', () => {
|
|
1582
1527
|
const content = `# Secrets Manifest
|
|
1583
1528
|
|
|
1584
1529
|
**Milestone:** M004
|
|
@@ -1592,18 +1537,17 @@ console.log('\n=== parseSecretsManifest: missing optional fields default correct
|
|
|
1592
1537
|
`;
|
|
1593
1538
|
|
|
1594
1539
|
const m = parseSecretsManifest(content);
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
{
|
|
1540
|
+
assert.deepStrictEqual(m.entries.length, 1, 'one entry with missing fields');
|
|
1541
|
+
assert.deepStrictEqual(m.entries[0].key, 'SOME_API_KEY', 'key parsed');
|
|
1542
|
+
assert.deepStrictEqual(m.entries[0].service, 'SomeService', 'service parsed');
|
|
1543
|
+
assert.deepStrictEqual(m.entries[0].dashboardUrl, '', 'missing dashboardUrl defaults to empty string');
|
|
1544
|
+
assert.deepStrictEqual(m.entries[0].formatHint, '', 'missing formatHint defaults to empty string');
|
|
1545
|
+
assert.deepStrictEqual(m.entries[0].status, 'pending', 'missing status defaults to pending');
|
|
1546
|
+
assert.deepStrictEqual(m.entries[0].destination, 'dotenv', 'missing destination defaults to dotenv');
|
|
1547
|
+
assert.deepStrictEqual(m.entries[0].guidance.length, 1, 'guidance still parsed');
|
|
1548
|
+
});
|
|
1549
|
+
|
|
1550
|
+
test('parseSecretsManifest: all three status values parse', () => {
|
|
1607
1551
|
for (const status of ['pending', 'collected', 'skipped'] as const) {
|
|
1608
1552
|
const content = `# Secrets Manifest
|
|
1609
1553
|
|
|
@@ -1619,12 +1563,11 @@ console.log('\n=== parseSecretsManifest: all three status values parse ===');
|
|
|
1619
1563
|
`;
|
|
1620
1564
|
|
|
1621
1565
|
const m = parseSecretsManifest(content);
|
|
1622
|
-
|
|
1566
|
+
assert.deepStrictEqual(m.entries[0].status, status, `status variant: ${status}`);
|
|
1623
1567
|
}
|
|
1624
|
-
}
|
|
1568
|
+
});
|
|
1625
1569
|
|
|
1626
|
-
|
|
1627
|
-
{
|
|
1570
|
+
test('parseSecretsManifest: invalid status defaults to pending', () => {
|
|
1628
1571
|
const content = `# Secrets Manifest
|
|
1629
1572
|
|
|
1630
1573
|
**Milestone:** M006
|
|
@@ -1639,11 +1582,10 @@ console.log('\n=== parseSecretsManifest: invalid status defaults to pending ==='
|
|
|
1639
1582
|
`;
|
|
1640
1583
|
|
|
1641
1584
|
const m = parseSecretsManifest(content);
|
|
1642
|
-
|
|
1643
|
-
}
|
|
1585
|
+
assert.deepStrictEqual(m.entries[0].status, 'pending', 'invalid status defaults to pending');
|
|
1586
|
+
});
|
|
1644
1587
|
|
|
1645
|
-
|
|
1646
|
-
{
|
|
1588
|
+
test('parseSecretsManifest + formatSecretsManifest: round-trip', () => {
|
|
1647
1589
|
const original = `# Secrets Manifest
|
|
1648
1590
|
|
|
1649
1591
|
**Milestone:** M007
|
|
@@ -1678,32 +1620,30 @@ console.log('\n=== parseSecretsManifest + formatSecretsManifest: round-trip ==='
|
|
|
1678
1620
|
const parsed2 = parseSecretsManifest(formatted);
|
|
1679
1621
|
|
|
1680
1622
|
// Verify semantic equality after round-trip
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1623
|
+
assert.deepStrictEqual(parsed2.milestone, parsed1.milestone, 'round-trip milestone');
|
|
1624
|
+
assert.deepStrictEqual(parsed2.generatedAt, parsed1.generatedAt, 'round-trip generatedAt');
|
|
1625
|
+
assert.deepStrictEqual(parsed2.entries.length, parsed1.entries.length, 'round-trip entry count');
|
|
1684
1626
|
|
|
1685
1627
|
for (let i = 0; i < parsed1.entries.length; i++) {
|
|
1686
1628
|
const e1 = parsed1.entries[i];
|
|
1687
1629
|
const e2 = parsed2.entries[i];
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1630
|
+
assert.deepStrictEqual(e2.key, e1.key, `round-trip entry ${i} key`);
|
|
1631
|
+
assert.deepStrictEqual(e2.service, e1.service, `round-trip entry ${i} service`);
|
|
1632
|
+
assert.deepStrictEqual(e2.dashboardUrl, e1.dashboardUrl, `round-trip entry ${i} dashboardUrl`);
|
|
1633
|
+
assert.deepStrictEqual(e2.formatHint, e1.formatHint, `round-trip entry ${i} formatHint`);
|
|
1634
|
+
assert.deepStrictEqual(e2.status, e1.status, `round-trip entry ${i} status`);
|
|
1635
|
+
assert.deepStrictEqual(e2.destination, e1.destination, `round-trip entry ${i} destination`);
|
|
1636
|
+
assert.deepStrictEqual(e2.guidance.length, e1.guidance.length, `round-trip entry ${i} guidance length`);
|
|
1695
1637
|
for (let j = 0; j < e1.guidance.length; j++) {
|
|
1696
|
-
|
|
1638
|
+
assert.deepStrictEqual(e2.guidance[j], e1.guidance[j], `round-trip entry ${i} guidance[${j}]`);
|
|
1697
1639
|
}
|
|
1698
1640
|
}
|
|
1699
|
-
}
|
|
1641
|
+
});
|
|
1700
1642
|
|
|
1701
1643
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1702
1644
|
// LLM-style round-trip tests — realistic manifest variations
|
|
1703
1645
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1704
|
-
|
|
1705
|
-
console.log('\n=== LLM round-trip: extra whitespace ===');
|
|
1706
|
-
{
|
|
1646
|
+
test('LLM round-trip: extra whitespace', () => {
|
|
1707
1647
|
// LLMs often produce inconsistent indentation and trailing spaces
|
|
1708
1648
|
const messy = `# Secrets Manifest
|
|
1709
1649
|
|
|
@@ -1734,34 +1674,33 @@ console.log('\n=== LLM round-trip: extra whitespace ===');
|
|
|
1734
1674
|
const formatted = formatSecretsManifest(parsed1);
|
|
1735
1675
|
const parsed2 = parseSecretsManifest(formatted);
|
|
1736
1676
|
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1677
|
+
assert.deepStrictEqual(parsed2.milestone, parsed1.milestone, 'whitespace round-trip milestone');
|
|
1678
|
+
assert.deepStrictEqual(parsed2.generatedAt, parsed1.generatedAt, 'whitespace round-trip generatedAt');
|
|
1679
|
+
assert.deepStrictEqual(parsed2.entries.length, parsed1.entries.length, 'whitespace round-trip entry count');
|
|
1680
|
+
assert.deepStrictEqual(parsed2.entries.length, 2, 'whitespace: two entries parsed');
|
|
1741
1681
|
|
|
1742
1682
|
for (let i = 0; i < parsed1.entries.length; i++) {
|
|
1743
1683
|
const e1 = parsed1.entries[i];
|
|
1744
1684
|
const e2 = parsed2.entries[i];
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1685
|
+
assert.deepStrictEqual(e2.key, e1.key, `whitespace round-trip entry ${i} key`);
|
|
1686
|
+
assert.deepStrictEqual(e2.service, e1.service, `whitespace round-trip entry ${i} service`);
|
|
1687
|
+
assert.deepStrictEqual(e2.dashboardUrl, e1.dashboardUrl, `whitespace round-trip entry ${i} dashboardUrl`);
|
|
1688
|
+
assert.deepStrictEqual(e2.formatHint, e1.formatHint, `whitespace round-trip entry ${i} formatHint`);
|
|
1689
|
+
assert.deepStrictEqual(e2.status, e1.status, `whitespace round-trip entry ${i} status`);
|
|
1690
|
+
assert.deepStrictEqual(e2.destination, e1.destination, `whitespace round-trip entry ${i} destination`);
|
|
1691
|
+
assert.deepStrictEqual(e2.guidance.length, e1.guidance.length, `whitespace round-trip entry ${i} guidance length`);
|
|
1752
1692
|
for (let j = 0; j < e1.guidance.length; j++) {
|
|
1753
|
-
|
|
1693
|
+
assert.deepStrictEqual(e2.guidance[j], e1.guidance[j], `whitespace round-trip entry ${i} guidance[${j}]`);
|
|
1754
1694
|
}
|
|
1755
1695
|
}
|
|
1756
1696
|
|
|
1757
1697
|
// Verify the parser correctly stripped trailing whitespace
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
}
|
|
1698
|
+
assert.deepStrictEqual(parsed1.milestone, 'M010', 'whitespace: milestone trimmed');
|
|
1699
|
+
assert.deepStrictEqual(parsed1.entries[0].key, 'OPENAI_API_KEY', 'whitespace: key trimmed');
|
|
1700
|
+
assert.deepStrictEqual(parsed1.entries[0].service, 'OpenAI', 'whitespace: service trimmed');
|
|
1701
|
+
});
|
|
1762
1702
|
|
|
1763
|
-
|
|
1764
|
-
{
|
|
1703
|
+
test('LLM round-trip: missing optional fields', () => {
|
|
1765
1704
|
// LLMs may omit Dashboard and Format hint lines entirely
|
|
1766
1705
|
const minimal = `# Secrets Manifest
|
|
1767
1706
|
|
|
@@ -1789,32 +1728,31 @@ console.log('\n=== LLM round-trip: missing optional fields ===');
|
|
|
1789
1728
|
const parsed1 = parseSecretsManifest(minimal);
|
|
1790
1729
|
|
|
1791
1730
|
// Verify missing optional fields get defaults
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1731
|
+
assert.deepStrictEqual(parsed1.entries[0].dashboardUrl, '', 'missing-optional: no dashboard → empty string');
|
|
1732
|
+
assert.deepStrictEqual(parsed1.entries[0].formatHint, '', 'missing-optional: no format hint → empty string');
|
|
1733
|
+
assert.deepStrictEqual(parsed1.entries[1].dashboardUrl, '', 'missing-optional: entry 2 no dashboard → empty string');
|
|
1734
|
+
assert.deepStrictEqual(parsed1.entries[1].formatHint, '', 'missing-optional: entry 2 no format hint → empty string');
|
|
1796
1735
|
|
|
1797
1736
|
// Round-trip: formatter omits empty optional fields, re-parse preserves defaults
|
|
1798
1737
|
const formatted = formatSecretsManifest(parsed1);
|
|
1799
1738
|
const parsed2 = parseSecretsManifest(formatted);
|
|
1800
1739
|
|
|
1801
|
-
|
|
1740
|
+
assert.deepStrictEqual(parsed2.entries.length, parsed1.entries.length, 'missing-optional round-trip entry count');
|
|
1802
1741
|
|
|
1803
1742
|
for (let i = 0; i < parsed1.entries.length; i++) {
|
|
1804
1743
|
const e1 = parsed1.entries[i];
|
|
1805
1744
|
const e2 = parsed2.entries[i];
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1745
|
+
assert.deepStrictEqual(e2.key, e1.key, `missing-optional round-trip entry ${i} key`);
|
|
1746
|
+
assert.deepStrictEqual(e2.service, e1.service, `missing-optional round-trip entry ${i} service`);
|
|
1747
|
+
assert.deepStrictEqual(e2.dashboardUrl, e1.dashboardUrl, `missing-optional round-trip entry ${i} dashboardUrl`);
|
|
1748
|
+
assert.deepStrictEqual(e2.formatHint, e1.formatHint, `missing-optional round-trip entry ${i} formatHint`);
|
|
1749
|
+
assert.deepStrictEqual(e2.status, e1.status, `missing-optional round-trip entry ${i} status`);
|
|
1750
|
+
assert.deepStrictEqual(e2.destination, e1.destination, `missing-optional round-trip entry ${i} destination`);
|
|
1751
|
+
assert.deepStrictEqual(e2.guidance.length, e1.guidance.length, `missing-optional round-trip entry ${i} guidance length`);
|
|
1813
1752
|
}
|
|
1814
|
-
}
|
|
1753
|
+
});
|
|
1815
1754
|
|
|
1816
|
-
|
|
1817
|
-
{
|
|
1755
|
+
test('LLM round-trip: extra blank lines', () => {
|
|
1818
1756
|
// LLMs sometimes insert excessive blank lines between sections
|
|
1819
1757
|
const blanky = `# Secrets Manifest
|
|
1820
1758
|
|
|
@@ -1858,42 +1796,40 @@ console.log('\n=== LLM round-trip: extra blank lines ===');
|
|
|
1858
1796
|
|
|
1859
1797
|
const parsed1 = parseSecretsManifest(blanky);
|
|
1860
1798
|
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1799
|
+
assert.deepStrictEqual(parsed1.entries.length, 2, 'blank-lines: two entries parsed');
|
|
1800
|
+
assert.deepStrictEqual(parsed1.milestone, 'M012', 'blank-lines: milestone parsed');
|
|
1801
|
+
assert.deepStrictEqual(parsed1.entries[0].key, 'API_KEY_ONE', 'blank-lines: first key');
|
|
1802
|
+
assert.deepStrictEqual(parsed1.entries[0].guidance.length, 2, 'blank-lines: first entry guidance count');
|
|
1803
|
+
assert.deepStrictEqual(parsed1.entries[1].key, 'API_KEY_TWO', 'blank-lines: second key');
|
|
1804
|
+
assert.deepStrictEqual(parsed1.entries[1].status, 'skipped', 'blank-lines: second entry status');
|
|
1867
1805
|
|
|
1868
1806
|
// Round-trip produces clean output
|
|
1869
1807
|
const formatted = formatSecretsManifest(parsed1);
|
|
1870
1808
|
const parsed2 = parseSecretsManifest(formatted);
|
|
1871
1809
|
|
|
1872
|
-
|
|
1810
|
+
assert.deepStrictEqual(parsed2.entries.length, parsed1.entries.length, 'blank-lines round-trip entry count');
|
|
1873
1811
|
|
|
1874
1812
|
for (let i = 0; i < parsed1.entries.length; i++) {
|
|
1875
1813
|
const e1 = parsed1.entries[i];
|
|
1876
1814
|
const e2 = parsed2.entries[i];
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1815
|
+
assert.deepStrictEqual(e2.key, e1.key, `blank-lines round-trip entry ${i} key`);
|
|
1816
|
+
assert.deepStrictEqual(e2.service, e1.service, `blank-lines round-trip entry ${i} service`);
|
|
1817
|
+
assert.deepStrictEqual(e2.dashboardUrl, e1.dashboardUrl, `blank-lines round-trip entry ${i} dashboardUrl`);
|
|
1818
|
+
assert.deepStrictEqual(e2.formatHint, e1.formatHint, `blank-lines round-trip entry ${i} formatHint`);
|
|
1819
|
+
assert.deepStrictEqual(e2.status, e1.status, `blank-lines round-trip entry ${i} status`);
|
|
1820
|
+
assert.deepStrictEqual(e2.destination, e1.destination, `blank-lines round-trip entry ${i} destination`);
|
|
1821
|
+
assert.deepStrictEqual(e2.guidance.length, e1.guidance.length, `blank-lines round-trip entry ${i} guidance length`);
|
|
1884
1822
|
}
|
|
1885
1823
|
|
|
1886
1824
|
// Verify the formatted output is cleaner (fewer consecutive blank lines)
|
|
1887
1825
|
const consecutiveBlanks = formatted.match(/\n{4,}/g);
|
|
1888
|
-
|
|
1889
|
-
}
|
|
1826
|
+
assert.ok(consecutiveBlanks === null, 'blank-lines: formatted output has no 4+ consecutive newlines');
|
|
1827
|
+
});
|
|
1890
1828
|
|
|
1891
1829
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1892
1830
|
// parseRoadmap: boundary map with embedded code fences (#468)
|
|
1893
1831
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1894
|
-
|
|
1895
|
-
console.log('\n=== parseRoadmap: boundary map with code fences (#468) ===');
|
|
1896
|
-
{
|
|
1832
|
+
test('parseRoadmap: boundary map with code fences (#468)', () => {
|
|
1897
1833
|
const content = `# M001: Test
|
|
1898
1834
|
|
|
1899
1835
|
**Vision:** Test
|
|
@@ -1922,10 +1858,10 @@ Consumes: nothing
|
|
|
1922
1858
|
const r = parseRoadmap(content);
|
|
1923
1859
|
const elapsed = Date.now() - start;
|
|
1924
1860
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1861
|
+
assert.ok(elapsed < 1000, `boundary map with code fences parsed in ${elapsed}ms (should be < 1s)`);
|
|
1862
|
+
assert.deepStrictEqual(r.slices.length, 2, 'code-fence roadmap: slice count');
|
|
1927
1863
|
// Boundary map should still parse (may not capture perfectly with code fences, but must not hang)
|
|
1928
|
-
|
|
1929
|
-
}
|
|
1864
|
+
assert.ok(r.boundaryMap.length >= 0, 'code-fence roadmap: boundary map parsed without hanging');
|
|
1865
|
+
});
|
|
1930
1866
|
|
|
1931
|
-
|
|
1867
|
+
});
|