ai-spector 0.9.21 → 0.9.24
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/contracts/bootstrap/config/prototype-screen-map.json +25 -0
- package/contracts/bootstrap/config/prototype.config.json +17 -0
- package/contracts/bootstrap/config/review-queue-pending.json +4 -0
- package/contracts/bootstrap/config/review-queue-registry.json +4 -0
- package/contracts/bootstrap/config/review.config.json +5 -0
- package/contracts/bootstrap/docs/ENTITY_REGISTRY_MIGRATION.md +192 -0
- package/contracts/bootstrap/docs/MIGRATION.md +463 -0
- package/contracts/bootstrap/docs/README.md +48 -0
- package/contracts/bootstrap/docs/adapters/README.md +32 -0
- package/contracts/bootstrap/docs/guides/COMMENTS_ENTITY_MIGRATION.md +195 -0
- package/contracts/bootstrap/docs/guides/DOCOPS_MANUAL_FALLBACK.md +177 -0
- package/contracts/bootstrap/docs/guides/PROJECT_LAYOUT.md +84 -0
- package/contracts/bootstrap/docs/modules/comments.md +77 -0
- package/contracts/bootstrap/docs/modules/generate.md +25 -0
- package/contracts/bootstrap/docs/modules/graph.md +31 -0
- package/contracts/bootstrap/docs/modules/prototype.md +26 -0
- package/contracts/bootstrap/docs/modules/review.md +25 -0
- package/contracts/bootstrap/docs/modules/translate.md +26 -0
- package/contracts/bootstrap/scripts/migrate-entity-registry.sh +55 -0
- package/contracts/bootstrap/templates/basic-design/db-design-template.md +177 -0
- package/contracts/bootstrap/templates/basic-design/detail-api-template.md +278 -0
- package/contracts/bootstrap/templates/basic-design/detail-screen-template.md +281 -0
- package/contracts/bootstrap/templates/basic-design/list-api-template.md +130 -0
- package/contracts/bootstrap/templates/basic-design/list-screen-template.md +242 -0
- package/contracts/bootstrap/templates/detail-design/common/architecture-overview-template.md +302 -0
- package/contracts/bootstrap/templates/detail-design/common/deployment-infrastructure-template.md +461 -0
- package/contracts/bootstrap/templates/detail-design/common/error-handling-patterns-template.md +460 -0
- package/contracts/bootstrap/templates/detail-design/common/integration-patterns-template.md +410 -0
- package/contracts/bootstrap/templates/detail-design/common/performance-standards-template.md +406 -0
- package/contracts/bootstrap/templates/detail-design/common/security-patterns-template.md +395 -0
- package/contracts/bootstrap/templates/detail-design/feature-detail-design-template.md +773 -0
- package/contracts/bootstrap/templates/detail-design/feature-list-template.md +39 -0
- package/contracts/bootstrap/templates/srs/1-introduction.md +58 -0
- package/contracts/bootstrap/templates/srs/2-overall-description.md +91 -0
- package/contracts/bootstrap/templates/srs/3-use-case-detail-template.md +142 -0
- package/contracts/bootstrap/templates/srs/3-use-cases.md +53 -0
- package/contracts/bootstrap/templates/srs/4-system-feature-detail-template.md +131 -0
- package/contracts/bootstrap/templates/srs/4-system-features-list-template.md +39 -0
- package/contracts/bootstrap/templates/srs/5-data-requirements.md +59 -0
- package/contracts/bootstrap/templates/srs/6-external-interfaces.md +56 -0
- package/contracts/bootstrap/templates/srs/7-quality-attributes.md +74 -0
- package/contracts/bootstrap/templates/srs/8-internationalization.md +36 -0
- package/contracts/bootstrap/templates/srs/9-other-requirements.md +46 -0
- package/contracts/examples/full-docops.config.json +54 -0
- package/contracts/examples/migrate-lifecycle.json +18 -0
- package/contracts/examples/minimal-comment-thread/comment-root.json +10 -0
- package/contracts/examples/minimal-comment-thread/meta_data.json +22 -0
- package/contracts/examples/minimal-docops.config.json +33 -0
- package/contracts/examples/minimal-lifecycle.json +17 -0
- package/contracts/examples/minimal-prototype.config.json +18 -0
- package/contracts/examples/minimal-screen-map.json +25 -0
- package/contracts/examples/registry/minimal-document.entity.json +15 -0
- package/contracts/examples/registry/minimal-manifest.json +7 -0
- package/contracts/examples/registry/minimal-screen.entity.json +11 -0
- package/contracts/examples/review/minimal-pending.json +14 -0
- package/contracts/examples/review/minimal-registry.json +31 -0
- package/contracts/examples/review/minimal-review.config.json +7 -0
- package/contracts/schemas/comments/comment_body.schema.json +19 -0
- package/contracts/schemas/comments/meta_data.schema.json +59 -0
- package/contracts/schemas/docops.config.schema.json +97 -0
- package/contracts/schemas/lifecycle.schema.json +43 -0
- package/contracts/schemas/prototype/config.schema.json +56 -0
- package/contracts/schemas/prototype/screen-map.schema.json +51 -0
- package/contracts/schemas/registry/document.entity.schema.json +38 -0
- package/contracts/schemas/registry/manifest.schema.json +18 -0
- package/contracts/schemas/registry/screen.entity.schema.json +28 -0
- package/contracts/schemas/review/registry.schema.json +72 -0
- package/contracts/schemas/review/review.config.schema.json +42 -0
- package/dist/cli.js +26 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/docops/bootstrap.d.ts +4 -1
- package/dist/core/docops/bootstrap.d.ts.map +1 -1
- package/dist/core/docops/bootstrap.js +16 -9
- package/dist/core/docops/bootstrap.js.map +1 -1
- package/dist/core/docops/guide.d.ts +78 -0
- package/dist/core/docops/guide.d.ts.map +1 -0
- package/dist/core/docops/guide.js +554 -0
- package/dist/core/docops/guide.js.map +1 -0
- package/dist/core/docops/migrate.d.ts.map +1 -1
- package/dist/core/docops/migrate.js +40 -6
- package/dist/core/docops/migrate.js.map +1 -1
- package/dist/core/graph/doc-extract.d.ts.map +1 -1
- package/dist/core/graph/doc-extract.js +12 -0
- package/dist/core/graph/doc-extract.js.map +1 -1
- package/dist/core/graph/impact.d.ts.map +1 -1
- package/dist/core/graph/impact.js +2 -1
- package/dist/core/graph/impact.js.map +1 -1
- package/dist/core/graph/link-testcases.d.ts +14 -0
- package/dist/core/graph/link-testcases.d.ts.map +1 -0
- package/dist/core/graph/link-testcases.js +46 -0
- package/dist/core/graph/link-testcases.js.map +1 -0
- package/dist/core/operations/docops.d.ts +8 -0
- package/dist/core/operations/docops.d.ts.map +1 -1
- package/dist/core/operations/docops.js +40 -0
- package/dist/core/operations/docops.js.map +1 -1
- package/dist/core/operations/index.d.ts.map +1 -1
- package/dist/core/operations/index.js +25 -0
- package/dist/core/operations/index.js.map +1 -1
- package/dist/core/operations/task-gates.d.ts +8 -1
- package/dist/core/operations/task-gates.d.ts.map +1 -1
- package/dist/core/operations/task-gates.js +14 -0
- package/dist/core/operations/task-gates.js.map +1 -1
- package/dist/core/operations/task.d.ts +2 -0
- package/dist/core/operations/task.d.ts.map +1 -1
- package/dist/core/operations/task.js +40 -2
- package/dist/core/operations/task.js.map +1 -1
- package/dist/core/sync/audit.d.ts.map +1 -1
- package/dist/core/sync/audit.js +14 -6
- package/dist/core/sync/audit.js.map +1 -1
- package/dist/core/sync/baseline.d.ts.map +1 -1
- package/dist/core/sync/baseline.js +5 -2
- package/dist/core/sync/baseline.js.map +1 -1
- package/dist/core/sync/constants.d.ts.map +1 -1
- package/dist/core/sync/constants.js +2 -1
- package/dist/core/sync/constants.js.map +1 -1
- package/dist/core/sync/gaps.d.ts.map +1 -1
- package/dist/core/sync/gaps.js +23 -2
- package/dist/core/sync/gaps.js.map +1 -1
- package/dist/core/sync/git-diff.d.ts +1 -0
- package/dist/core/sync/git-diff.d.ts.map +1 -1
- package/dist/core/sync/git-diff.js +14 -0
- package/dist/core/sync/git-diff.js.map +1 -1
- package/dist/core/sync/repo-context.d.ts +23 -0
- package/dist/core/sync/repo-context.d.ts.map +1 -0
- package/dist/core/sync/repo-context.js +28 -0
- package/dist/core/sync/repo-context.js.map +1 -0
- package/dist/core/sync/snapshot.d.ts.map +1 -1
- package/dist/core/sync/snapshot.js +24 -18
- package/dist/core/sync/snapshot.js.map +1 -1
- package/dist/core/sync/types.d.ts +7 -2
- package/dist/core/sync/types.d.ts.map +1 -1
- package/dist/core/upgrade/checklist.json +2 -2
- package/dist/interfaces/mcp/schemas.d.ts +2 -0
- package/dist/interfaces/mcp/schemas.d.ts.map +1 -1
- package/dist/interfaces/mcp/schemas.js +4 -0
- package/dist/interfaces/mcp/schemas.js.map +1 -1
- package/dist/interfaces/mcp/server.js +0 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/scaffold/claude/.claude/skills/_skill-router.md +11 -8
- package/scaffold/claude/.claude/skills/ai-spector/references/cli-failures.md +3 -3
- package/scaffold/claude/.claude/skills/ai-spector/references/docops-migrate.md +112 -0
- package/scaffold/claude/.claude/skills/ai-spector/references/help.md +1 -1
- package/scaffold/claude/.claude/skills/ai-spector/references/runbook.md +49 -26
- package/scaffold/claude/.claude/skills/ai-spector/skill.md +3 -2
- package/scaffold/cursor/skills/_skill-router.md +11 -8
- package/scaffold/cursor/skills/ai-spector/SKILL.md +3 -2
- package/scaffold/cursor/skills/ai-spector/references/cli-failures.md +3 -3
- package/scaffold/cursor/skills/ai-spector/references/docops-migrate.md +112 -0
- package/scaffold/cursor/skills/ai-spector/references/help.md +1 -1
- package/scaffold/cursor/skills/ai-spector/references/runbook.md +49 -26
- package/schemas/rules.impact.json +3 -2
- package/schemas/schema.graph.json +4 -2
- package/dist/commands/analyze.d.ts +0 -12
- package/dist/commands/analyze.d.ts.map +0 -1
- package/dist/commands/analyze.js +0 -53
- package/dist/commands/analyze.js.map +0 -1
- package/dist/commands/bootstrap.d.ts +0 -4
- package/dist/commands/bootstrap.d.ts.map +0 -1
- package/dist/commands/bootstrap.js +0 -58
- package/dist/commands/bootstrap.js.map +0 -1
- package/dist/commands/comments.d.ts +0 -44
- package/dist/commands/comments.d.ts.map +0 -1
- package/dist/commands/comments.js +0 -79
- package/dist/commands/comments.js.map +0 -1
- package/dist/commands/graph-impact.d.ts +0 -28
- package/dist/commands/graph-impact.d.ts.map +0 -1
- package/dist/commands/graph-impact.js +0 -174
- package/dist/commands/graph-impact.js.map +0 -1
- package/dist/commands/graph-merge.d.ts +0 -32
- package/dist/commands/graph-merge.d.ts.map +0 -1
- package/dist/commands/graph-merge.js +0 -101
- package/dist/commands/graph-merge.js.map +0 -1
- package/dist/commands/graph-query.d.ts +0 -10
- package/dist/commands/graph-query.d.ts.map +0 -1
- package/dist/commands/graph-query.js +0 -24
- package/dist/commands/graph-query.js.map +0 -1
- package/dist/commands/graph-report.d.ts +0 -9
- package/dist/commands/graph-report.d.ts.map +0 -1
- package/dist/commands/graph-report.js +0 -10
- package/dist/commands/graph-report.js.map +0 -1
- package/dist/commands/graph-visualize.d.ts +0 -17
- package/dist/commands/graph-visualize.d.ts.map +0 -1
- package/dist/commands/graph-visualize.js +0 -44
- package/dist/commands/graph-visualize.js.map +0 -1
- package/dist/commands/hooks-constants.d.ts +0 -2
- package/dist/commands/hooks-constants.d.ts.map +0 -1
- package/dist/commands/hooks-constants.js +0 -2
- package/dist/commands/hooks-constants.js.map +0 -1
- package/dist/commands/hooks.d.ts +0 -28
- package/dist/commands/hooks.d.ts.map +0 -1
- package/dist/commands/hooks.js +0 -187
- package/dist/commands/hooks.js.map +0 -1
- package/dist/commands/index.d.ts +0 -22
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js +0 -472
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/init.d.ts +0 -19
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -210
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/lang-queue.d.ts +0 -28
- package/dist/commands/lang-queue.d.ts.map +0 -1
- package/dist/commands/lang-queue.js +0 -49
- package/dist/commands/lang-queue.js.map +0 -1
- package/dist/commands/lang.d.ts +0 -16
- package/dist/commands/lang.d.ts.map +0 -1
- package/dist/commands/lang.js +0 -69
- package/dist/commands/lang.js.map +0 -1
- package/dist/commands/prototype.d.ts +0 -66
- package/dist/commands/prototype.d.ts.map +0 -1
- package/dist/commands/prototype.js +0 -370
- package/dist/commands/prototype.js.map +0 -1
- package/dist/commands/setup.d.ts +0 -26
- package/dist/commands/setup.d.ts.map +0 -1
- package/dist/commands/setup.js +0 -259
- package/dist/commands/setup.js.map +0 -1
- package/dist/commands/sync-cursor.d.ts +0 -10
- package/dist/commands/sync-cursor.d.ts.map +0 -1
- package/dist/commands/sync-cursor.js +0 -18
- package/dist/commands/sync-cursor.js.map +0 -1
- package/dist/commands/template-regen.d.ts +0 -24
- package/dist/commands/template-regen.d.ts.map +0 -1
- package/dist/commands/template-regen.js +0 -240
- package/dist/commands/template-regen.js.map +0 -1
- package/dist/commands/template.d.ts +0 -3
- package/dist/commands/template.d.ts.map +0 -1
- package/dist/commands/template.js +0 -1285
- package/dist/commands/template.js.map +0 -1
- package/dist/commands/validate.d.ts +0 -10
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/validate.js +0 -89
- package/dist/commands/validate.js.map +0 -1
- package/dist/comments/anchor.d.ts +0 -16
- package/dist/comments/anchor.d.ts.map +0 -1
- package/dist/comments/anchor.js +0 -59
- package/dist/comments/anchor.js.map +0 -1
- package/dist/comments/inbox.d.ts +0 -82
- package/dist/comments/inbox.d.ts.map +0 -1
- package/dist/comments/inbox.js +0 -179
- package/dist/comments/inbox.js.map +0 -1
- package/dist/comments/paths.d.ts +0 -12
- package/dist/comments/paths.d.ts.map +0 -1
- package/dist/comments/paths.js +0 -54
- package/dist/comments/paths.js.map +0 -1
- package/dist/comments/plan.d.ts +0 -20
- package/dist/comments/plan.d.ts.map +0 -1
- package/dist/comments/plan.js +0 -62
- package/dist/comments/plan.js.map +0 -1
- package/dist/comments/storage.d.ts +0 -30
- package/dist/comments/storage.d.ts.map +0 -1
- package/dist/comments/storage.js +0 -272
- package/dist/comments/storage.js.map +0 -1
- package/dist/comments/types.d.ts +0 -54
- package/dist/comments/types.d.ts.map +0 -1
- package/dist/comments/types.js +0 -2
- package/dist/comments/types.js.map +0 -1
- package/dist/config/load.d.ts +0 -47
- package/dist/config/load.d.ts.map +0 -1
- package/dist/config/load.js +0 -148
- package/dist/config/load.js.map +0 -1
- package/dist/config/types.d.ts +0 -56
- package/dist/config/types.d.ts.map +0 -1
- package/dist/config/types.js +0 -2
- package/dist/config/types.js.map +0 -1
- package/dist/core/adopt/apply.d.ts +0 -15
- package/dist/core/adopt/apply.d.ts.map +0 -1
- package/dist/core/adopt/apply.js +0 -127
- package/dist/core/adopt/apply.js.map +0 -1
- package/dist/core/adopt/bootstrap.d.ts +0 -16
- package/dist/core/adopt/bootstrap.d.ts.map +0 -1
- package/dist/core/adopt/bootstrap.js +0 -195
- package/dist/core/adopt/bootstrap.js.map +0 -1
- package/dist/core/adopt/classify.d.ts +0 -22
- package/dist/core/adopt/classify.d.ts.map +0 -1
- package/dist/core/adopt/classify.js +0 -295
- package/dist/core/adopt/classify.js.map +0 -1
- package/dist/core/adopt/paths.d.ts +0 -10
- package/dist/core/adopt/paths.d.ts.map +0 -1
- package/dist/core/adopt/paths.js +0 -16
- package/dist/core/adopt/paths.js.map +0 -1
- package/dist/core/adopt/plan.d.ts +0 -10
- package/dist/core/adopt/plan.d.ts.map +0 -1
- package/dist/core/adopt/plan.js +0 -269
- package/dist/core/adopt/plan.js.map +0 -1
- package/dist/core/adopt/scan.d.ts +0 -5
- package/dist/core/adopt/scan.d.ts.map +0 -1
- package/dist/core/adopt/scan.js +0 -169
- package/dist/core/adopt/scan.js.map +0 -1
- package/dist/core/adopt/setup.d.ts +0 -7
- package/dist/core/adopt/setup.d.ts.map +0 -1
- package/dist/core/adopt/setup.js +0 -64
- package/dist/core/adopt/setup.js.map +0 -1
- package/dist/core/adopt/tasks.d.ts +0 -9
- package/dist/core/adopt/tasks.d.ts.map +0 -1
- package/dist/core/adopt/tasks.js +0 -153
- package/dist/core/adopt/tasks.js.map +0 -1
- package/dist/core/adopt/types.d.ts +0 -75
- package/dist/core/adopt/types.d.ts.map +0 -1
- package/dist/core/adopt/types.js +0 -2
- package/dist/core/adopt/types.js.map +0 -1
- package/dist/core/adopt/validate.d.ts +0 -20
- package/dist/core/adopt/validate.d.ts.map +0 -1
- package/dist/core/adopt/validate.js +0 -129
- package/dist/core/adopt/validate.js.map +0 -1
- package/dist/core/docops/dual-write.d.ts +0 -12
- package/dist/core/docops/dual-write.d.ts.map +0 -1
- package/dist/core/docops/dual-write.js +0 -44
- package/dist/core/docops/dual-write.js.map +0 -1
- package/dist/core/engine/local-adapter.d.ts +0 -3
- package/dist/core/engine/local-adapter.d.ts.map +0 -1
- package/dist/core/engine/local-adapter.js +0 -18
- package/dist/core/engine/local-adapter.js.map +0 -1
- package/dist/core/graph/rules/default-impact.json +0 -24
- package/dist/core/operations/adopt-gates.d.ts +0 -19
- package/dist/core/operations/adopt-gates.d.ts.map +0 -1
- package/dist/core/operations/adopt-gates.js +0 -71
- package/dist/core/operations/adopt-gates.js.map +0 -1
- package/dist/core/operations/adopt-plan.d.ts +0 -15
- package/dist/core/operations/adopt-plan.d.ts.map +0 -1
- package/dist/core/operations/adopt-plan.js +0 -24
- package/dist/core/operations/adopt-plan.js.map +0 -1
- package/dist/core/operations/adopt.d.ts +0 -11
- package/dist/core/operations/adopt.d.ts.map +0 -1
- package/dist/core/operations/adopt.js +0 -150
- package/dist/core/operations/adopt.js.map +0 -1
- package/dist/core/operations/analyze.d.ts +0 -12
- package/dist/core/operations/analyze.d.ts.map +0 -1
- package/dist/core/operations/analyze.js +0 -53
- package/dist/core/operations/analyze.js.map +0 -1
- package/dist/core/prototype/spa-route-fallbacks.d.ts +0 -21
- package/dist/core/prototype/spa-route-fallbacks.d.ts.map +0 -1
- package/dist/core/prototype/spa-route-fallbacks.js +0 -93
- package/dist/core/prototype/spa-route-fallbacks.js.map +0 -1
- package/dist/graph/InMemoryGraph.d.ts +0 -2
- package/dist/graph/InMemoryGraph.d.ts.map +0 -1
- package/dist/graph/InMemoryGraph.js +0 -2
- package/dist/graph/InMemoryGraph.js.map +0 -1
- package/dist/graph/bundles.d.ts +0 -32
- package/dist/graph/bundles.d.ts.map +0 -1
- package/dist/graph/bundles.js +0 -109
- package/dist/graph/bundles.js.map +0 -1
- package/dist/graph/defaults.d.ts +0 -23
- package/dist/graph/defaults.d.ts.map +0 -1
- package/dist/graph/defaults.js +0 -25
- package/dist/graph/defaults.js.map +0 -1
- package/dist/graph/detail-sections.d.ts +0 -28
- package/dist/graph/detail-sections.d.ts.map +0 -1
- package/dist/graph/detail-sections.js +0 -220
- package/dist/graph/detail-sections.js.map +0 -1
- package/dist/graph/doc-extract.d.ts +0 -84
- package/dist/graph/doc-extract.d.ts.map +0 -1
- package/dist/graph/doc-extract.js +0 -821
- package/dist/graph/doc-extract.js.map +0 -1
- package/dist/graph/impact.d.ts +0 -5
- package/dist/graph/impact.d.ts.map +0 -1
- package/dist/graph/impact.js +0 -7
- package/dist/graph/impact.js.map +0 -1
- package/dist/graph/knowledge.d.ts +0 -11
- package/dist/graph/knowledge.d.ts.map +0 -1
- package/dist/graph/knowledge.js +0 -62
- package/dist/graph/knowledge.js.map +0 -1
- package/dist/graph/layer-audit.d.ts +0 -5
- package/dist/graph/layer-audit.d.ts.map +0 -1
- package/dist/graph/layer-audit.js +0 -29
- package/dist/graph/layer-audit.js.map +0 -1
- package/dist/graph/load.d.ts +0 -4
- package/dist/graph/load.d.ts.map +0 -1
- package/dist/graph/load.js +0 -12
- package/dist/graph/load.js.map +0 -1
- package/dist/graph/loadGraph.d.ts +0 -3
- package/dist/graph/loadGraph.d.ts.map +0 -1
- package/dist/graph/loadGraph.js +0 -7
- package/dist/graph/loadGraph.js.map +0 -1
- package/dist/graph/merge.d.ts +0 -27
- package/dist/graph/merge.d.ts.map +0 -1
- package/dist/graph/merge.js +0 -239
- package/dist/graph/merge.js.map +0 -1
- package/dist/graph/path-target-edges.d.ts +0 -2
- package/dist/graph/path-target-edges.d.ts.map +0 -1
- package/dist/graph/path-target-edges.js +0 -2
- package/dist/graph/path-target-edges.js.map +0 -1
- package/dist/graph/provenance.d.ts +0 -19
- package/dist/graph/provenance.d.ts.map +0 -1
- package/dist/graph/provenance.js +0 -199
- package/dist/graph/provenance.js.map +0 -1
- package/dist/graph/query.d.ts +0 -3
- package/dist/graph/query.d.ts.map +0 -1
- package/dist/graph/query.js +0 -2
- package/dist/graph/query.js.map +0 -1
- package/dist/graph/resolve.d.ts +0 -15
- package/dist/graph/resolve.d.ts.map +0 -1
- package/dist/graph/resolve.js +0 -117
- package/dist/graph/resolve.js.map +0 -1
- package/dist/graph/source-refs.d.ts +0 -2
- package/dist/graph/source-refs.d.ts.map +0 -1
- package/dist/graph/source-refs.js +0 -30
- package/dist/graph/source-refs.js.map +0 -1
- package/dist/graph/translation.d.ts +0 -14
- package/dist/graph/translation.d.ts.map +0 -1
- package/dist/graph/translation.js +0 -65
- package/dist/graph/translation.js.map +0 -1
- package/dist/index/doc-semantics.d.ts +0 -14
- package/dist/index/doc-semantics.d.ts.map +0 -1
- package/dist/index/doc-semantics.js +0 -101
- package/dist/index/doc-semantics.js.map +0 -1
- package/dist/index/docs-build.d.ts +0 -40
- package/dist/index/docs-build.d.ts.map +0 -1
- package/dist/index/docs-build.js +0 -136
- package/dist/index/docs-build.js.map +0 -1
- package/dist/index/docs-config.d.ts +0 -19
- package/dist/index/docs-config.d.ts.map +0 -1
- package/dist/index/docs-config.js +0 -5
- package/dist/index/docs-config.js.map +0 -1
- package/dist/interfaces/cli/format/adopt.d.ts +0 -23
- package/dist/interfaces/cli/format/adopt.d.ts.map +0 -1
- package/dist/interfaces/cli/format/adopt.js +0 -89
- package/dist/interfaces/cli/format/adopt.js.map +0 -1
- package/dist/interfaces/mcp/tools/adopt.d.ts +0 -22
- package/dist/interfaces/mcp/tools/adopt.d.ts.map +0 -1
- package/dist/interfaces/mcp/tools/adopt.js +0 -55
- package/dist/interfaces/mcp/tools/adopt.js.map +0 -1
- package/dist/lang/diff.d.ts +0 -12
- package/dist/lang/diff.d.ts.map +0 -1
- package/dist/lang/diff.js +0 -71
- package/dist/lang/diff.js.map +0 -1
- package/dist/lang/paths.d.ts +0 -16
- package/dist/lang/paths.d.ts.map +0 -1
- package/dist/lang/paths.js +0 -32
- package/dist/lang/paths.js.map +0 -1
- package/dist/lang/queue-layout.d.ts +0 -11
- package/dist/lang/queue-layout.d.ts.map +0 -1
- package/dist/lang/queue-layout.js +0 -56
- package/dist/lang/queue-layout.js.map +0 -1
- package/dist/lang/queue-store.d.ts +0 -23
- package/dist/lang/queue-store.d.ts.map +0 -1
- package/dist/lang/queue-store.js +0 -285
- package/dist/lang/queue-store.js.map +0 -1
- package/dist/lang/queue-types.d.ts +0 -122
- package/dist/lang/queue-types.d.ts.map +0 -1
- package/dist/lang/queue-types.js +0 -2
- package/dist/lang/queue-types.js.map +0 -1
- package/dist/lang/queue.d.ts +0 -17
- package/dist/lang/queue.d.ts.map +0 -1
- package/dist/lang/queue.js +0 -362
- package/dist/lang/queue.js.map +0 -1
- package/dist/lang/sections.d.ts +0 -26
- package/dist/lang/sections.d.ts.map +0 -1
- package/dist/lang/sections.js +0 -85
- package/dist/lang/sections.js.map +0 -1
- package/dist/markdown/parse.d.ts +0 -25
- package/dist/markdown/parse.d.ts.map +0 -1
- package/dist/markdown/parse.js +0 -100
- package/dist/markdown/parse.js.map +0 -1
- package/dist/prototype/build-base-path.d.ts +0 -11
- package/dist/prototype/build-base-path.d.ts.map +0 -1
- package/dist/prototype/build-base-path.js +0 -84
- package/dist/prototype/build-base-path.js.map +0 -1
- package/dist/prototype/build-manifest.d.ts +0 -21
- package/dist/prototype/build-manifest.d.ts.map +0 -1
- package/dist/prototype/build-manifest.js +0 -171
- package/dist/prototype/build-manifest.js.map +0 -1
- package/dist/prototype/config.d.ts +0 -21
- package/dist/prototype/config.d.ts.map +0 -1
- package/dist/prototype/config.js +0 -131
- package/dist/prototype/config.js.map +0 -1
- package/dist/prototype/deploy-path.d.ts +0 -9
- package/dist/prototype/deploy-path.d.ts.map +0 -1
- package/dist/prototype/deploy-path.js +0 -21
- package/dist/prototype/deploy-path.js.map +0 -1
- package/dist/prototype/htpasswd.d.ts +0 -8
- package/dist/prototype/htpasswd.d.ts.map +0 -1
- package/dist/prototype/htpasswd.js +0 -36
- package/dist/prototype/htpasswd.js.map +0 -1
- package/dist/prototype/nginx-config.d.ts +0 -16
- package/dist/prototype/nginx-config.d.ts.map +0 -1
- package/dist/prototype/nginx-config.js +0 -39
- package/dist/prototype/nginx-config.js.map +0 -1
- package/dist/prototype/parse-screen-index.d.ts +0 -10
- package/dist/prototype/parse-screen-index.d.ts.map +0 -1
- package/dist/prototype/parse-screen-index.js +0 -109
- package/dist/prototype/parse-screen-index.js.map +0 -1
- package/dist/prototype/preview-uri.d.ts +0 -8
- package/dist/prototype/preview-uri.d.ts.map +0 -1
- package/dist/prototype/preview-uri.js +0 -35
- package/dist/prototype/preview-uri.js.map +0 -1
- package/dist/prototype/relative-assets.d.ts +0 -9
- package/dist/prototype/relative-assets.d.ts.map +0 -1
- package/dist/prototype/relative-assets.js +0 -23
- package/dist/prototype/relative-assets.js.map +0 -1
- package/dist/prototype/resolve-default-screen.d.ts +0 -17
- package/dist/prototype/resolve-default-screen.d.ts.map +0 -1
- package/dist/prototype/resolve-default-screen.js +0 -31
- package/dist/prototype/resolve-default-screen.js.map +0 -1
- package/dist/prototype/rewrite-build-assets.d.ts +0 -8
- package/dist/prototype/rewrite-build-assets.d.ts.map +0 -1
- package/dist/prototype/rewrite-build-assets.js +0 -40
- package/dist/prototype/rewrite-build-assets.js.map +0 -1
- package/dist/prototype/route-defaults.d.ts +0 -43
- package/dist/prototype/route-defaults.d.ts.map +0 -1
- package/dist/prototype/route-defaults.js +0 -50
- package/dist/prototype/route-defaults.js.map +0 -1
- package/dist/prototype/screen-doc-paths.d.ts +0 -15
- package/dist/prototype/screen-doc-paths.d.ts.map +0 -1
- package/dist/prototype/screen-doc-paths.js +0 -38
- package/dist/prototype/screen-doc-paths.js.map +0 -1
- package/dist/prototype/spa-route-fallbacks.d.ts +0 -21
- package/dist/prototype/spa-route-fallbacks.d.ts.map +0 -1
- package/dist/prototype/spa-route-fallbacks.js +0 -93
- package/dist/prototype/spa-route-fallbacks.js.map +0 -1
- package/dist/prototype/theme-preview.d.ts +0 -17
- package/dist/prototype/theme-preview.d.ts.map +0 -1
- package/dist/prototype/theme-preview.js +0 -73
- package/dist/prototype/theme-preview.js.map +0 -1
- package/dist/prototype/themes.d.ts +0 -6
- package/dist/prototype/themes.d.ts.map +0 -1
- package/dist/prototype/themes.js +0 -55
- package/dist/prototype/themes.js.map +0 -1
- package/dist/prototype/types.d.ts +0 -160
- package/dist/prototype/types.d.ts.map +0 -1
- package/dist/prototype/types.js +0 -2
- package/dist/prototype/types.js.map +0 -1
- package/dist/prototype/validate.d.ts +0 -16
- package/dist/prototype/validate.d.ts.map +0 -1
- package/dist/prototype/validate.js +0 -149
- package/dist/prototype/validate.js.map +0 -1
- package/dist/registry/build.d.ts +0 -5
- package/dist/registry/build.d.ts.map +0 -1
- package/dist/registry/build.js +0 -71
- package/dist/registry/build.js.map +0 -1
- package/dist/registry/slug.d.ts +0 -4
- package/dist/registry/slug.d.ts.map +0 -1
- package/dist/registry/slug.js +0 -16
- package/dist/registry/slug.js.map +0 -1
- package/dist/registry/structure-patch.d.ts +0 -6
- package/dist/registry/structure-patch.d.ts.map +0 -1
- package/dist/registry/structure-patch.js +0 -49
- package/dist/registry/structure-patch.js.map +0 -1
- package/dist/template/scan.d.ts +0 -21
- package/dist/template/scan.d.ts.map +0 -1
- package/dist/template/scan.js +0 -84
- package/dist/template/scan.js.map +0 -1
- package/dist/template/validate.d.ts +0 -10
- package/dist/template/validate.d.ts.map +0 -1
- package/dist/template/validate.js +0 -64
- package/dist/template/validate.js.map +0 -1
- package/dist/util/fs.d.ts +0 -6
- package/dist/util/fs.d.ts.map +0 -1
- package/dist/util/fs.js +0 -24
- package/dist/util/fs.js.map +0 -1
- package/dist/util/git-diff.d.ts +0 -16
- package/dist/util/git-diff.d.ts.map +0 -1
- package/dist/util/git-diff.js +0 -93
- package/dist/util/git-diff.js.map +0 -1
- package/dist/util/gitignore.d.ts +0 -9
- package/dist/util/gitignore.d.ts.map +0 -1
- package/dist/util/gitignore.js +0 -44
- package/dist/util/gitignore.js.map +0 -1
- package/dist/util/open-browser.d.ts +0 -3
- package/dist/util/open-browser.d.ts.map +0 -1
- package/dist/util/open-browser.js +0 -24
- package/dist/util/open-browser.js.map +0 -1
- package/dist/util/paths.d.ts +0 -16
- package/dist/util/paths.d.ts.map +0 -1
- package/dist/util/paths.js +0 -19
- package/dist/util/paths.js.map +0 -1
- package/dist/util/prompt.d.ts +0 -14
- package/dist/util/prompt.d.ts.map +0 -1
- package/dist/util/prompt.js +0 -65
- package/dist/util/prompt.js.map +0 -1
- package/dist/visualize/html.d.ts +0 -13
- package/dist/visualize/html.d.ts.map +0 -1
- package/dist/visualize/html.js +0 -766
- package/dist/visualize/html.js.map +0 -1
- package/dist/visualize/stats.d.ts +0 -8
- package/dist/visualize/stats.d.ts.map +0 -1
- package/dist/visualize/stats.js +0 -9
- package/dist/visualize/stats.js.map +0 -1
|
@@ -1,1285 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { copyFile, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
4
|
-
import { findProjectRoot, loadDocflowConfig, resolveFromRoot, loadDocumentsManifest, loadBasicDesignListManifest, } from "../config/load.js";
|
|
5
|
-
import { readJson, writeJson, pathExists, copyTree } from "../util/fs.js";
|
|
6
|
-
import { buildSectionRegistry } from "../registry/build.js";
|
|
7
|
-
import { bootstrapFromRegistry } from "./bootstrap.js";
|
|
8
|
-
import { scanTemplateFolder } from "../template/scan.js";
|
|
9
|
-
import { validatePackManifest } from "../template/validate.js";
|
|
10
|
-
import { runTemplateRegen } from "./template-regen.js";
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// Helpers
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
async function loadConfigAndRoot(cwd) {
|
|
15
|
-
const root = cwd ?? findProjectRoot();
|
|
16
|
-
const { config, configFile } = await loadDocflowConfig(root);
|
|
17
|
-
return { root, config, configFile };
|
|
18
|
-
}
|
|
19
|
-
async function saveConfig(configFile, config) {
|
|
20
|
-
await writeJson(configFile, config);
|
|
21
|
-
}
|
|
22
|
-
async function rebuildRegistryAndGraph(root, config) {
|
|
23
|
-
const p = config.paths;
|
|
24
|
-
const registryPath = resolveFromRoot(root, p.registry);
|
|
25
|
-
const graphPath = resolveFromRoot(root, p.graph);
|
|
26
|
-
const registry = await buildSectionRegistry(root);
|
|
27
|
-
await writeJson(registryPath, registry);
|
|
28
|
-
const graph = bootstrapFromRegistry(registry);
|
|
29
|
-
await writeJson(graphPath, graph.toTraceabilityGraph());
|
|
30
|
-
const totalSections = registry.documents.reduce((n, d) => n + d.sections.length, 0);
|
|
31
|
-
const tg = graph.toTraceabilityGraph();
|
|
32
|
-
return {
|
|
33
|
-
documents: registry.documents.length,
|
|
34
|
-
sections: totalSections,
|
|
35
|
-
graphNodes: graph.nodesById.size,
|
|
36
|
-
graphEdges: tg.edges.length,
|
|
37
|
-
documentIds: tg.nodes.filter((n) => n.type === "document").map((n) => n.id),
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
async function listInstalledPackNames(root) {
|
|
41
|
-
const packsDir = join(root, ".ai-spector", "packs");
|
|
42
|
-
if (!(await pathExists(packsDir)))
|
|
43
|
-
return [];
|
|
44
|
-
try {
|
|
45
|
-
const entries = await readdir(packsDir, { withFileTypes: true });
|
|
46
|
-
return entries
|
|
47
|
-
.filter((e) => e.isDirectory() && e.name !== ".staging")
|
|
48
|
-
.map((e) => e.name);
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
// template list
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
async function runTemplateList(opts) {
|
|
58
|
-
const { root, config } = await loadConfigAndRoot(opts.cwd);
|
|
59
|
-
const active = config.packs?.active;
|
|
60
|
-
const installed = await listInstalledPackNames(root);
|
|
61
|
-
console.log("Template packs:");
|
|
62
|
-
// builtin entry
|
|
63
|
-
const builtinActive = !active || active === "builtin";
|
|
64
|
-
const builtinMarker = builtinActive ? "●" : "○";
|
|
65
|
-
const builtinStatus = builtinActive ? "(active)" : "(default — used when no custom pack is active)";
|
|
66
|
-
console.log(` ${builtinMarker} builtin ${builtinStatus}`);
|
|
67
|
-
// installed custom packs
|
|
68
|
-
for (const name of installed) {
|
|
69
|
-
const isActive = active === name;
|
|
70
|
-
const marker = isActive ? "●" : "○";
|
|
71
|
-
const status = isActive ? "(active)" : "(inactive)";
|
|
72
|
-
let description = "";
|
|
73
|
-
try {
|
|
74
|
-
const manifestPath = join(root, ".ai-spector", "packs", name, "manifest.json");
|
|
75
|
-
const manifest = await readJson(manifestPath);
|
|
76
|
-
description = manifest.description ? ` ${manifest.description}` : "";
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
// ignore
|
|
80
|
-
}
|
|
81
|
-
console.log(` ${marker} ${name.padEnd(16)}${status}${description}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
// DAG generation from pack manifest
|
|
86
|
-
// ---------------------------------------------------------------------------
|
|
87
|
-
/**
|
|
88
|
-
* Derive a `dag.srs.json` + `dag.srs.graph-seeds.json` pair from a pack manifest.
|
|
89
|
-
*
|
|
90
|
-
* - Non-perDomain documents → flat DAG nodes (no dependsOn; AI refines ordering).
|
|
91
|
-
* - perDomain documents → DAG nodes with `mode: "perDomainBreakout"` and the
|
|
92
|
-
* `perDomainKey` field so the generate skill knows to produce one file per domain item.
|
|
93
|
-
*/
|
|
94
|
-
function buildDagFromManifest(manifest) {
|
|
95
|
-
const packSlug = manifest.packName.replace(/[^a-z0-9]/gi, "-").toLowerCase();
|
|
96
|
-
const dagNodes = [];
|
|
97
|
-
const seedsMap = {};
|
|
98
|
-
const primaryDagIds = [];
|
|
99
|
-
// Pass 1: primary (non-perDomain) documents
|
|
100
|
-
for (const doc of manifest.documents) {
|
|
101
|
-
if (doc.perDomain)
|
|
102
|
-
continue;
|
|
103
|
-
const docSlug = doc.documentId.replace(/^doc\.[^.]+\./, "").replace(/\./g, "-");
|
|
104
|
-
const dagId = `${packSlug}.${docSlug}`;
|
|
105
|
-
const output = doc.output ?? doc.outputPattern ?? `${docSlug}.md`;
|
|
106
|
-
dagNodes.push({ id: dagId, template: doc.template, output, dependsOn: [] });
|
|
107
|
-
seedsMap[dagId] = doc.documentId;
|
|
108
|
-
primaryDagIds.push(dagId);
|
|
109
|
-
}
|
|
110
|
-
// Pass 2: perDomain breakout documents — one DAG node each, depends on primary docs
|
|
111
|
-
for (const doc of manifest.documents) {
|
|
112
|
-
if (!doc.perDomain)
|
|
113
|
-
continue;
|
|
114
|
-
const docSlug = doc.documentId.replace(/^doc\.[^.]+\./, "").replace(/\./g, "-");
|
|
115
|
-
const dagId = `${packSlug}.${docSlug}-breakout`;
|
|
116
|
-
const outputPattern = doc.outputPattern ?? doc.output ?? `${docSlug}/{id}.md`;
|
|
117
|
-
dagNodes.push({
|
|
118
|
-
id: dagId,
|
|
119
|
-
template: doc.template,
|
|
120
|
-
outputPattern,
|
|
121
|
-
dependsOn: primaryDagIds,
|
|
122
|
-
mode: "perDomainBreakout",
|
|
123
|
-
perDomainKey: doc.perDomain,
|
|
124
|
-
});
|
|
125
|
-
seedsMap[dagId] = doc.documentId;
|
|
126
|
-
}
|
|
127
|
-
const dag = { version: 1, root: "docs/srs", nodes: dagNodes };
|
|
128
|
-
// perDomain section: maps domain keys to graph node type + template doc id
|
|
129
|
-
// graphNodeType is the NodeType enum value in the graph (e.g. "requirement", "useCase")
|
|
130
|
-
const perDomainSeeds = {};
|
|
131
|
-
if (manifest.perDomainTemplates) {
|
|
132
|
-
for (const [domain, templateDocId] of Object.entries(manifest.perDomainTemplates)) {
|
|
133
|
-
if (!templateDocId)
|
|
134
|
-
continue;
|
|
135
|
-
perDomainSeeds[domain] = {
|
|
136
|
-
graphNodeType: domain,
|
|
137
|
-
templateDocId,
|
|
138
|
-
seedFromDomainId: true,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
const seeds = {
|
|
143
|
-
version: 1,
|
|
144
|
-
description: `Map dag.srs.json node ids to graph document ids for pack "${manifest.packName}"`,
|
|
145
|
-
seeds: seedsMap,
|
|
146
|
-
perDomain: perDomainSeeds,
|
|
147
|
-
};
|
|
148
|
-
return { dag, seeds };
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Write a human+AI-readable generate-hints.md into the pack directory.
|
|
152
|
-
* Describes the generation waves and breakout templates so the generate skill
|
|
153
|
-
* can drive the correct output without reading the manifest directly.
|
|
154
|
-
*/
|
|
155
|
-
async function writeGenerateHints(packDir, manifest) {
|
|
156
|
-
const primaryDocs = manifest.documents.filter((d) => !d.perDomain);
|
|
157
|
-
const breakoutDocs = manifest.documents.filter((d) => d.perDomain);
|
|
158
|
-
const lines = [
|
|
159
|
-
`# Generate hints for pack: ${manifest.packName}`,
|
|
160
|
-
"",
|
|
161
|
-
`> Auto-generated by \`template install\`. Update manually if generation order changes.`,
|
|
162
|
-
"",
|
|
163
|
-
"## Wave 0 — Primary documents",
|
|
164
|
-
"",
|
|
165
|
-
"Generate these first (no inter-document dependencies assumed):",
|
|
166
|
-
"",
|
|
167
|
-
];
|
|
168
|
-
for (const doc of primaryDocs) {
|
|
169
|
-
const output = doc.output ?? doc.outputPattern ?? "(see manifest)";
|
|
170
|
-
lines.push(`- **${doc.documentId}** → \`${output}\``);
|
|
171
|
-
lines.push(` Template: \`${doc.template}\``);
|
|
172
|
-
}
|
|
173
|
-
if (breakoutDocs.length > 0) {
|
|
174
|
-
lines.push("", "## Wave 1 — Per-domain breakout files", "", "**Start only after Wave 0 is complete and `npx ai-spector index` has run.**", "", "### Required per-item workflow", "", "Generate one item at a time — do NOT script or loop over knowledge.json:", "", "```", "1. npx ai-spector graph query <itemId> --direction both --depth 4 --edges CONTEXT --json", "2. Read projectionPaths — these are the only allowed source files", "3. Load the breakout template (listed below)", "4. Write output file with specific, verifiable content from graph context", "5. Repeat for next item", "```", "", "After all items: batch merge `rendersTo` edges → `graph validate` → `index`.", "", "For 10+ items: use sub-agents (3–5 items each). Load `generate-workflow.md` → `context-management.md`.", "", "⛔ **Anti-pattern:** do not run a script over `knowledge.json` to stamp all files at once.", "Script output passes validate/index but contains boilerplate, not real domain content.", "See `.cursor/skills/ai-spector/references/generate-graph.md §F` for the rule.", "");
|
|
175
|
-
for (const doc of breakoutDocs) {
|
|
176
|
-
const output = doc.outputPattern ?? doc.output ?? "(see manifest)";
|
|
177
|
-
lines.push(`### ${doc.perDomain} — \`${doc.documentId}\``);
|
|
178
|
-
lines.push("");
|
|
179
|
-
lines.push(`- Output pattern: \`${output}\``);
|
|
180
|
-
lines.push(`- Template: \`.ai-spector/packs/${manifest.packName}/templates/${doc.template}\``);
|
|
181
|
-
lines.push(`- Graph node type: \`${doc.perDomain}\``);
|
|
182
|
-
lines.push("");
|
|
183
|
-
lines.push("Get the item list:");
|
|
184
|
-
lines.push("```bash");
|
|
185
|
-
lines.push(`npx ai-spector graph query <any-wave0-documentId> --direction both --depth 4 --json`);
|
|
186
|
-
lines.push(`# filter: nodes where type === "${doc.perDomain}"`);
|
|
187
|
-
lines.push(`# or: npx ai-spector graph query ${doc.documentId} --json`);
|
|
188
|
-
lines.push("```");
|
|
189
|
-
lines.push("");
|
|
190
|
-
}
|
|
191
|
-
if (manifest.defaultListedIn) {
|
|
192
|
-
lines.push("### Breakout list anchors", "");
|
|
193
|
-
for (const [domain, sectionId] of Object.entries(manifest.defaultListedIn)) {
|
|
194
|
-
lines.push(`- \`${domain}\` items are listed in: \`${sectionId}\``);
|
|
195
|
-
}
|
|
196
|
-
lines.push("");
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
lines.push("", "> This pack has no per-domain breakout templates.");
|
|
201
|
-
}
|
|
202
|
-
lines.push("", "## When to pause and ask the user", "", "Stop generating and ask if:", "", "- A `{placeholder}` in a template has no matching value in the graph query result", "- The item list for a breakout domain returns 0 results or ambiguous node types", "- Two output paths could satisfy the same `outputPattern`", "- A Wave 0 document already exists and it is unclear whether to overwrite or append", "- `graph validate` fails after writing and the error is not self-explanatory", "", "⛔ **Do NOT guess. A wrong file written silently is harder to fix than a question asked upfront.**", "", "See `context-map.json` in this directory for the expected source of each `{placeholder}`.", "", "## Reference", "", `- Pack manifest: \`.ai-spector/packs/${manifest.packName}/manifest.json\``, `- Context map: \`.ai-spector/packs/${manifest.packName}/context-map.json\``, `- Inspect pack: \`npx ai-spector template inspect ${manifest.packName}\``, `- Active graph seeds: see \`.ai-spector/.docflow/config/dag.srs.graph-seeds.json\``);
|
|
203
|
-
await writeFile(join(packDir, "generate-hints.md"), lines.join("\n") + "\n", "utf8");
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Write a context-map.json into the pack directory.
|
|
207
|
-
*
|
|
208
|
-
* Lists every {placeholder} found across all templates and the suggested graph
|
|
209
|
-
* field path to resolve it from. Unknown placeholders get `"source": "TODO"` so
|
|
210
|
-
* the user (or AI) can fill them in before generating.
|
|
211
|
-
*/
|
|
212
|
-
async function writeContextMap(packDir, manifest, scanResult) {
|
|
213
|
-
// Collect all unique placeholders across all template files
|
|
214
|
-
const allPlaceholders = new Set();
|
|
215
|
-
if (scanResult) {
|
|
216
|
-
for (const f of scanResult.files) {
|
|
217
|
-
for (const p of f.placeholders) {
|
|
218
|
-
allPlaceholders.add(p);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
// Also collect from manifest-level metadata if present
|
|
223
|
-
for (const doc of manifest.documents) {
|
|
224
|
-
for (const p of [doc.output ?? "", doc.outputPattern ?? ""]) {
|
|
225
|
-
const m = p.matchAll(/\{([a-zA-Z][a-zA-Z0-9_-]*)\}/g);
|
|
226
|
-
for (const [, name] of m)
|
|
227
|
-
allPlaceholders.add(`{${name}}`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
// Build a mapping with best-guess sources
|
|
231
|
-
const KNOWN_SOURCES = {
|
|
232
|
-
"{projectName}": "graph.metadata.projectName or config.projectName",
|
|
233
|
-
"{project}": "graph.metadata.projectName or config.projectName",
|
|
234
|
-
"{domain}": "graph node id (perDomain item)",
|
|
235
|
-
"{id}": "graph node id",
|
|
236
|
-
"{title}": "graph node title or heading",
|
|
237
|
-
"{feature}": "graph node id (feature type)",
|
|
238
|
-
"{module}": "graph node id (module type)",
|
|
239
|
-
"{screen}": "graph node id (screen type)",
|
|
240
|
-
"{date}": "new Date().toISOString().slice(0, 10)",
|
|
241
|
-
"{version}": "config.version or package.json version",
|
|
242
|
-
};
|
|
243
|
-
const entries = {};
|
|
244
|
-
for (const placeholder of [...allPlaceholders].sort()) {
|
|
245
|
-
const known = KNOWN_SOURCES[placeholder];
|
|
246
|
-
entries[placeholder] = {
|
|
247
|
-
source: known ?? "TODO",
|
|
248
|
-
note: known
|
|
249
|
-
? "auto-resolved"
|
|
250
|
-
: "fill in before generating — ask user if unclear",
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
const map = {
|
|
254
|
-
_description: "Maps every {placeholder} in this pack's templates to its graph/config source. " +
|
|
255
|
-
"Entries marked TODO must be resolved before generation.",
|
|
256
|
-
packName: manifest.packName,
|
|
257
|
-
generatedAt: new Date().toISOString(),
|
|
258
|
-
placeholders: entries,
|
|
259
|
-
};
|
|
260
|
-
await writeFile(join(packDir, "context-map.json"), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Write a dedicated Cursor + Claude generate skill for the installed pack.
|
|
264
|
-
*
|
|
265
|
-
* Priority: if the AI wrote generate-skill.md to staging during the import workflow,
|
|
266
|
-
* use it directly (it's tailored to the specific template). Otherwise fall back to
|
|
267
|
-
* auto-generating a structural skeleton from the manifest.
|
|
268
|
-
*/
|
|
269
|
-
async function writePackGenerateSkill(root, manifest, stagedSkillPath) {
|
|
270
|
-
const name = manifest.packName;
|
|
271
|
-
const slug = name.replace(/[^a-z0-9]/gi, "-").toLowerCase();
|
|
272
|
-
// If the AI wrote a tailored skill during the import workflow, use it directly
|
|
273
|
-
if (stagedSkillPath && existsSync(stagedSkillPath)) {
|
|
274
|
-
const tailoredContent = await readFile(stagedSkillPath, "utf8");
|
|
275
|
-
const cursorSkillDir = join(root, ".cursor", "skills", `ai-spector-generate-${slug}`);
|
|
276
|
-
await mkdir(cursorSkillDir, { recursive: true });
|
|
277
|
-
await writeFile(join(cursorSkillDir, "SKILL.md"), tailoredContent, "utf8");
|
|
278
|
-
// Derive a minimal Claude skill from the tailored content (strip frontmatter + reformat)
|
|
279
|
-
const claudeContent = [
|
|
280
|
-
`---`,
|
|
281
|
-
`name: ai-spector-generate-${slug}`,
|
|
282
|
-
`description: "Generates documents for the ${name} template pack. Use the pack-specific Cursor skill for full details."`,
|
|
283
|
-
`---`,
|
|
284
|
-
``,
|
|
285
|
-
`# AI Spector — Generate (${name} pack)`,
|
|
286
|
-
``,
|
|
287
|
-
`## When to use`,
|
|
288
|
-
``,
|
|
289
|
-
`- Any request to generate, write, or update documents for pack \`${name}\``,
|
|
290
|
-
``,
|
|
291
|
-
`## Step 0 — Load pack context`,
|
|
292
|
-
``,
|
|
293
|
-
`Read these files before generating:`,
|
|
294
|
-
`1. \`.ai-spector/packs/${name}/generate-hints.md\``,
|
|
295
|
-
`2. \`.ai-spector/.docflow/config/dag.srs.json\``,
|
|
296
|
-
`3. \`.ai-spector/.docflow/config/dag.srs.graph-seeds.json\``,
|
|
297
|
-
``,
|
|
298
|
-
`Then follow the wave structure from \`generate-hints.md\`.`,
|
|
299
|
-
``,
|
|
300
|
-
`## Checklist`,
|
|
301
|
-
``,
|
|
302
|
-
`\`\`\``,
|
|
303
|
-
`- [ ] Loaded generate-hints.md and DAG config`,
|
|
304
|
-
`- [ ] graph validate passes before starting`,
|
|
305
|
-
`- [ ] Wave 0 primary documents generated`,
|
|
306
|
-
`- [ ] Wave 1 breakout files generated (if any)`,
|
|
307
|
-
`- [ ] npx ai-spector index run after each wave`,
|
|
308
|
-
`\`\`\``,
|
|
309
|
-
].join("\n");
|
|
310
|
-
const claudeSkillDir = join(root, ".claude", "skills", `ai-spector-generate-${slug}`);
|
|
311
|
-
await mkdir(claudeSkillDir, { recursive: true });
|
|
312
|
-
await writeFile(join(claudeSkillDir, "skill.md"), claudeContent + "\n", "utf8");
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
// Fallback: auto-generate a structural skeleton from the manifest
|
|
316
|
-
// Derive description from primary docs
|
|
317
|
-
const primaryDocs = manifest.documents.filter((d) => !d.perDomain);
|
|
318
|
-
const breakoutDocs = manifest.documents.filter((d) => d.perDomain);
|
|
319
|
-
const primaryOutputs = primaryDocs
|
|
320
|
-
.map((d) => d.output ?? d.outputPattern ?? d.template)
|
|
321
|
-
.slice(0, 3)
|
|
322
|
-
.join(", ");
|
|
323
|
-
const breakoutNote = breakoutDocs.length > 0
|
|
324
|
-
? ` Plus ${breakoutDocs.length} breakout template(s): ${breakoutDocs.map((d) => d.perDomain).join(", ")}.`
|
|
325
|
-
: "";
|
|
326
|
-
// Infer docs/ path for `paths:` frontmatter
|
|
327
|
-
const outputPaths = new Set();
|
|
328
|
-
for (const doc of manifest.documents) {
|
|
329
|
-
const p = (doc.output ?? doc.outputPattern ?? "docs/").split("/").slice(0, 2).join("/") + "/**";
|
|
330
|
-
outputPaths.add(p);
|
|
331
|
-
}
|
|
332
|
-
const pathsYaml = [...outputPaths].map((p) => ` - "${p}"`).join("\n");
|
|
333
|
-
const cursorSkillContent = [
|
|
334
|
-
`---`,
|
|
335
|
-
`name: ai-spector-generate-${slug}`,
|
|
336
|
-
`description: >-`,
|
|
337
|
-
` Generates documents for the ${name} template pack. Use when the user asks to`,
|
|
338
|
-
` generate, write, or update documents produced by this pack. Outputs: ${primaryOutputs}.${breakoutNote}`,
|
|
339
|
-
` Active pack: ${name}.`,
|
|
340
|
-
`paths:`,
|
|
341
|
-
pathsYaml,
|
|
342
|
-
`---`,
|
|
343
|
-
``,
|
|
344
|
-
`# Generate — ${name} pack`,
|
|
345
|
-
``,
|
|
346
|
-
`## Load at start (in order)`,
|
|
347
|
-
`1. \`.ai-spector/packs/${name}/generate-hints.md\``,
|
|
348
|
-
`2. \`.ai-spector/.docflow/config/dag.srs.json\``,
|
|
349
|
-
`3. \`.ai-spector/.docflow/config/dag.srs.graph-seeds.json\``,
|
|
350
|
-
`4. [\`generate-workflow.md\`](../ai-spector/references/generate-workflow.md)`,
|
|
351
|
-
``,
|
|
352
|
-
`## Pack`,
|
|
353
|
-
``,
|
|
354
|
-
`- Name: \`${name}\``,
|
|
355
|
-
`- Templates: \`.ai-spector/packs/${name}/templates/\``,
|
|
356
|
-
`- Manifest: \`.ai-spector/packs/${name}/manifest.json\``,
|
|
357
|
-
`- Context map: \`.ai-spector/packs/${name}/context-map.json\` — placeholder → graph field`,
|
|
358
|
-
``,
|
|
359
|
-
`## Before you start`,
|
|
360
|
-
``,
|
|
361
|
-
`1. Read \`context-map.json\` — check every entry marked \`TODO\` and resolve it or ask the user.`,
|
|
362
|
-
`2. Run \`npx ai-spector graph validate\` — fix any errors before writing files.`,
|
|
363
|
-
``,
|
|
364
|
-
`## Workflow`,
|
|
365
|
-
``,
|
|
366
|
-
`Follow the wave structure from \`generate-hints.md\`.`,
|
|
367
|
-
``,
|
|
368
|
-
`**Wave 0 — Primary documents (exact table):**`,
|
|
369
|
-
``,
|
|
370
|
-
`| documentId | template | output |`,
|
|
371
|
-
`|---|---|---|`,
|
|
372
|
-
...primaryDocs.map((d) => `| \`${d.documentId}\` | \`${d.template}\` | \`${d.output ?? d.outputPattern ?? "(see manifest)"}\` |`),
|
|
373
|
-
``,
|
|
374
|
-
`- Graph seed id: look up the \`documentId\` in \`dag.srs.graph-seeds.json\` to get the graph node id to query`,
|
|
375
|
-
`- **Do NOT** use builtin \`doc.srs.*\` ids — they do not exist in this pack's graph`,
|
|
376
|
-
``,
|
|
377
|
-
...(breakoutDocs.length > 0
|
|
378
|
-
? [
|
|
379
|
-
`**Wave 1 — Breakout files (${breakoutDocs.map((d) => d.perDomain).join(", ")}):**`,
|
|
380
|
-
``,
|
|
381
|
-
`Start only after Wave 0 is complete and \`npx ai-spector index\` has run.`,
|
|
382
|
-
``,
|
|
383
|
-
`**Required per-item workflow — one item at a time:**`,
|
|
384
|
-
``,
|
|
385
|
-
`\`\`\``,
|
|
386
|
-
`1. npx ai-spector graph query <itemId> --direction both --depth 4 --edges CONTEXT --json`,
|
|
387
|
-
`2. Read projectionPaths — these are the only allowed source files`,
|
|
388
|
-
`3. Write output file with specific, verifiable content from graph context`,
|
|
389
|
-
`4. Repeat for next item`,
|
|
390
|
-
`\`\`\``,
|
|
391
|
-
``,
|
|
392
|
-
`After all items are written: batch merge rendersTo edges, then validate + index.`,
|
|
393
|
-
``,
|
|
394
|
-
`⛔ **Do NOT generate breakout files via a script or loop over knowledge.json.**`,
|
|
395
|
-
`Script-generated files pass validate/index but contain boilerplate, not real content.`,
|
|
396
|
-
`See [\`generate-graph.md §F\`](../ai-spector/references/generate-graph.md) for the full rule.`,
|
|
397
|
-
``,
|
|
398
|
-
`For 10+ items: use sub-agents (3–5 items each). Load context-management.md.`,
|
|
399
|
-
``,
|
|
400
|
-
]
|
|
401
|
-
: []),
|
|
402
|
-
`## Re-generation mode (source doc changed)`,
|
|
403
|
-
``,
|
|
404
|
-
`When the user says "regenerate", "update stale docs", "source changed", or similar:`,
|
|
405
|
-
``,
|
|
406
|
-
`1. Run: \`npx ai-spector template regen --json\``,
|
|
407
|
-
`2. Read \`.ai-spector/packs/${name}/regen-plan.md\` — it lists exactly which files are stale and why`,
|
|
408
|
-
`3. Regenerate ONLY the listed files — do NOT touch others`,
|
|
409
|
-
`4. For each file: query the graph for the affected node context first`,
|
|
410
|
-
` \`npx ai-spector graph query <nodeId> --direction both --depth 4 --json\``,
|
|
411
|
-
`5. Update gen-status.json entries for regenerated files`,
|
|
412
|
-
`6. After all files: \`npx ai-spector index\` + \`npx ai-spector graph validate\``,
|
|
413
|
-
``,
|
|
414
|
-
`⛔ Do NOT regenerate files that are NOT in the regen plan. Scope is everything.`,
|
|
415
|
-
``,
|
|
416
|
-
`## After each breakout item — update gen-status.json`,
|
|
417
|
-
``,
|
|
418
|
-
`After writing each breakout file, append an entry to \`.ai-spector/packs/${name}/gen-status.json\`:`,
|
|
419
|
-
``,
|
|
420
|
-
`\`\`\`json`,
|
|
421
|
-
`{`,
|
|
422
|
-
` "packName": "${name}",`,
|
|
423
|
-
` "updatedAt": "<ISO timestamp>",`,
|
|
424
|
-
` "items": [`,
|
|
425
|
-
` { "itemId": "<graphNodeId>", "outputFile": "<relative path>", "status": "done", "generatedAt": "<ISO>" },`,
|
|
426
|
-
` { "itemId": "<id>", "status": "blocked", "blockedReason": "<why you need human input>" }`,
|
|
427
|
-
` ]`,
|
|
428
|
-
`}`,
|
|
429
|
-
`\`\`\``,
|
|
430
|
-
``,
|
|
431
|
-
`Status values: \`done\` | \`pending\` | \`blocked\`.`,
|
|
432
|
-
`Set \`blocked\` + \`blockedReason\` when you cannot proceed — user can run \`npx ai-spector template status\` to see it.`,
|
|
433
|
-
``,
|
|
434
|
-
`## After each wave`,
|
|
435
|
-
``,
|
|
436
|
-
`\`\`\`bash`,
|
|
437
|
-
`npx ai-spector graph validate`,
|
|
438
|
-
`npx ai-spector index`,
|
|
439
|
-
`\`\`\``,
|
|
440
|
-
``,
|
|
441
|
-
`## When to pause and ask the user`,
|
|
442
|
-
``,
|
|
443
|
-
`Stop and ask if:`,
|
|
444
|
-
``,
|
|
445
|
-
`- A \`{placeholder}\` in a template has no matching value in the graph result`,
|
|
446
|
-
`- The item list for a breakout domain returns 0 or ambiguous results`,
|
|
447
|
-
`- Two output paths could satisfy the same \`outputPattern\``,
|
|
448
|
-
`- A Wave 0 output file already exists and it is unclear whether to overwrite or append`,
|
|
449
|
-
`- \`graph validate\` fails after writing and the error is not self-explanatory`,
|
|
450
|
-
``,
|
|
451
|
-
`⛔ Do NOT guess. Wrong files written silently are harder to fix than a question asked upfront.`,
|
|
452
|
-
``,
|
|
453
|
-
`## On CLI failure`,
|
|
454
|
-
``,
|
|
455
|
-
`Load [\`cli-failures.md\`](../ai-spector/references/cli-failures.md).`,
|
|
456
|
-
``,
|
|
457
|
-
`"generate ${name}", "write docs", "generate documents" → this skill.`,
|
|
458
|
-
].join("\n");
|
|
459
|
-
const claudeSkillContent = [
|
|
460
|
-
`---`,
|
|
461
|
-
`name: ai-spector-generate-${slug}`,
|
|
462
|
-
`description: "Generates documents for the ${name} template pack. Use when the user asks to generate, write, or update documents. Outputs: ${primaryOutputs}.${breakoutNote}"`,
|
|
463
|
-
`---`,
|
|
464
|
-
``,
|
|
465
|
-
`# AI Spector — Generate (${name} pack)`,
|
|
466
|
-
``,
|
|
467
|
-
`## When to use`,
|
|
468
|
-
``,
|
|
469
|
-
`- "generate ${name}", "write docs", "generate documents" when pack \`${name}\` is active`,
|
|
470
|
-
``,
|
|
471
|
-
`## Step 0 — Load pack context`,
|
|
472
|
-
``,
|
|
473
|
-
`Read these files before generating anything:`,
|
|
474
|
-
`1. \`.ai-spector/packs/${name}/generate-hints.md\` — wave structure`,
|
|
475
|
-
`2. \`.ai-spector/.docflow/config/dag.srs.json\` — DAG node order`,
|
|
476
|
-
`3. \`.ai-spector/.docflow/config/dag.srs.graph-seeds.json\` — graph seed ids`,
|
|
477
|
-
``,
|
|
478
|
-
`## Workflow`,
|
|
479
|
-
``,
|
|
480
|
-
`Follow wave structure from \`generate-hints.md\`.`,
|
|
481
|
-
``,
|
|
482
|
-
`\`\`\``,
|
|
483
|
-
`1. Load generate-hints.md + DAG config`,
|
|
484
|
-
`2. Wave 0: generate primary documents (use seed ids from dag.srs.graph-seeds.json)`,
|
|
485
|
-
...(breakoutDocs.length > 0
|
|
486
|
-
? [`3. Wave 1: generate breakout files (one per graph node of perDomainKey type)`]
|
|
487
|
-
: []),
|
|
488
|
-
`${breakoutDocs.length > 0 ? "4" : "3"}. After each wave: npx ai-spector index`,
|
|
489
|
-
`${breakoutDocs.length > 0 ? "5" : "4"}. Final: npx ai-spector graph validate`,
|
|
490
|
-
`\`\`\``,
|
|
491
|
-
``,
|
|
492
|
-
`**Important:** Use graph seed ids from \`dag.srs.graph-seeds.json\`, NOT builtin \`doc.srs.*\` ids.`,
|
|
493
|
-
``,
|
|
494
|
-
`Before generating: read \`context-map.json\` and resolve any entry marked \`TODO\` — ask the user if unclear.`,
|
|
495
|
-
``,
|
|
496
|
-
`## When to pause and ask the user`,
|
|
497
|
-
``,
|
|
498
|
-
`Stop and ask if:`,
|
|
499
|
-
``,
|
|
500
|
-
`- A \`{placeholder}\` has no matching value in the graph result`,
|
|
501
|
-
`- The breakout item list returns 0 or ambiguous results`,
|
|
502
|
-
`- An output file already exists and it is unclear whether to overwrite`,
|
|
503
|
-
`- \`graph validate\` fails and the error is not self-explanatory`,
|
|
504
|
-
``,
|
|
505
|
-
`⛔ Do NOT guess. Ask instead.`,
|
|
506
|
-
``,
|
|
507
|
-
`## Re-generation mode`,
|
|
508
|
-
``,
|
|
509
|
-
`When user says "regenerate", "update stale docs", or "source changed":`,
|
|
510
|
-
``,
|
|
511
|
-
`\`\`\``,
|
|
512
|
-
`1. npx ai-spector template regen --json`,
|
|
513
|
-
`2. Read regen-plan.md — regenerate ONLY listed files`,
|
|
514
|
-
`3. Query graph context for each affected node before rewriting`,
|
|
515
|
-
`4. Update gen-status.json`,
|
|
516
|
-
`5. npx ai-spector index + graph validate`,
|
|
517
|
-
`\`\`\``,
|
|
518
|
-
``,
|
|
519
|
-
...(breakoutDocs.length > 0
|
|
520
|
-
? [
|
|
521
|
-
`## Tracking breakout progress (gen-status.json)`,
|
|
522
|
-
``,
|
|
523
|
-
`After each breakout item, update \`.ai-spector/packs/${name}/gen-status.json\`:`,
|
|
524
|
-
`- \`"status": "done"\` + \`outputFile\` + \`generatedAt\` when written successfully`,
|
|
525
|
-
`- \`"status": "blocked"\` + \`blockedReason\` when you need human input`,
|
|
526
|
-
``,
|
|
527
|
-
`User can run \`npx ai-spector template status\` to see what's done/blocked.`,
|
|
528
|
-
``,
|
|
529
|
-
]
|
|
530
|
-
: []),
|
|
531
|
-
`## Checklist`,
|
|
532
|
-
``,
|
|
533
|
-
`\`\`\``,
|
|
534
|
-
`- [ ] Loaded generate-hints.md and DAG config`,
|
|
535
|
-
`- [ ] graph validate passes before starting`,
|
|
536
|
-
`- [ ] Wave 0 primary documents generated`,
|
|
537
|
-
...(breakoutDocs.length > 0 ? [`- [ ] Wave 1 breakout files generated`, `- [ ] gen-status.json updated for every item`] : []),
|
|
538
|
-
`- [ ] npx ai-spector index run after each wave`,
|
|
539
|
-
`- [ ] npx ai-spector graph validate run at end`,
|
|
540
|
-
`\`\`\``,
|
|
541
|
-
].join("\n");
|
|
542
|
-
// Write Cursor skill
|
|
543
|
-
const cursorSkillDir = join(root, ".cursor", "skills", `ai-spector-generate-${slug}`);
|
|
544
|
-
await mkdir(cursorSkillDir, { recursive: true });
|
|
545
|
-
await writeFile(join(cursorSkillDir, "SKILL.md"), cursorSkillContent + "\n", "utf8");
|
|
546
|
-
// Write Claude skill
|
|
547
|
-
const claudeSkillDir = join(root, ".claude", "skills", `ai-spector-generate-${slug}`);
|
|
548
|
-
await mkdir(claudeSkillDir, { recursive: true });
|
|
549
|
-
await writeFile(join(claudeSkillDir, "skill.md"), claudeSkillContent + "\n", "utf8");
|
|
550
|
-
}
|
|
551
|
-
async function writeDagFiles(root, dag, seeds) {
|
|
552
|
-
const dagDir = join(root, ".ai-spector", ".docflow", "config");
|
|
553
|
-
await mkdir(dagDir, { recursive: true });
|
|
554
|
-
await writeJson(join(dagDir, "dag.srs.json"), dag);
|
|
555
|
-
await writeJson(join(dagDir, "dag.srs.graph-seeds.json"), seeds);
|
|
556
|
-
}
|
|
557
|
-
async function restoreBuiltinDagFiles(root) {
|
|
558
|
-
const { scaffoldBundleRoot } = await import("../config/load.js");
|
|
559
|
-
const srcDir = join(scaffoldBundleRoot(), ".ai-spector", ".docflow", "config");
|
|
560
|
-
const destDir = join(root, ".ai-spector", ".docflow", "config");
|
|
561
|
-
await mkdir(destDir, { recursive: true });
|
|
562
|
-
for (const name of ["dag.srs.json", "dag.srs.graph-seeds.json"]) {
|
|
563
|
-
const src = join(srcDir, name);
|
|
564
|
-
const dest = join(destDir, name);
|
|
565
|
-
if (existsSync(src)) {
|
|
566
|
-
await copyFile(src, dest);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
// ---------------------------------------------------------------------------
|
|
571
|
-
// template use <name>
|
|
572
|
-
// ---------------------------------------------------------------------------
|
|
573
|
-
async function runTemplateUse(name, opts) {
|
|
574
|
-
const { root, config, configFile } = await loadConfigAndRoot(opts.cwd);
|
|
575
|
-
if (name === "builtin" || name === "default") {
|
|
576
|
-
if (config.packs) {
|
|
577
|
-
delete config.packs;
|
|
578
|
-
}
|
|
579
|
-
await saveConfig(configFile, config);
|
|
580
|
-
console.log("Switched to builtin templates. Restoring builtin DAG config...");
|
|
581
|
-
await restoreBuiltinDagFiles(root);
|
|
582
|
-
}
|
|
583
|
-
else {
|
|
584
|
-
const manifestPath = join(root, ".ai-spector", "packs", name, "manifest.json");
|
|
585
|
-
if (!existsSync(manifestPath)) {
|
|
586
|
-
console.error(`Pack "${name}" not found. Expected manifest at: ${manifestPath}`);
|
|
587
|
-
process.exitCode = 1;
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
const manifest = await readJson(manifestPath);
|
|
591
|
-
config.packs = { active: name };
|
|
592
|
-
await saveConfig(configFile, config);
|
|
593
|
-
console.log(`Switched to pack "${name}". Rebuilding registry, graph, and DAG config...`);
|
|
594
|
-
// Write pack-derived DAG files, generate-hints.md, and dedicated generate skill
|
|
595
|
-
const packDir = join(root, ".ai-spector", "packs", name);
|
|
596
|
-
const { dag, seeds } = buildDagFromManifest(manifest);
|
|
597
|
-
await writeDagFiles(root, dag, seeds);
|
|
598
|
-
await writeGenerateHints(packDir, manifest);
|
|
599
|
-
// Scan installed templates so context-map captures real placeholders
|
|
600
|
-
const templatesDir = join(packDir, "templates");
|
|
601
|
-
const scanForContextMap = (await pathExists(templatesDir))
|
|
602
|
-
? await scanTemplateFolder(templatesDir, templatesDir)
|
|
603
|
-
: undefined;
|
|
604
|
-
await writeContextMap(packDir, manifest, scanForContextMap);
|
|
605
|
-
await writePackGenerateSkill(root, manifest);
|
|
606
|
-
}
|
|
607
|
-
const stats = await rebuildRegistryAndGraph(root, config);
|
|
608
|
-
console.log(`Done. ${stats.documents} documents, ${stats.sections} sections, ` +
|
|
609
|
-
`${stats.graphNodes} graph nodes, ${stats.graphEdges} edges.`);
|
|
610
|
-
const activePack = config.packs?.active ?? "builtin";
|
|
611
|
-
const docIdList = stats.documentIds?.map((id) => ` - ${id}`).join("\n") ??
|
|
612
|
-
` (run \`npx ai-spector template inspect ${activePack}\` to list them)`;
|
|
613
|
-
console.log(`Active graph document ids (valid query seeds):\n${docIdList}`);
|
|
614
|
-
if (activePack !== "builtin") {
|
|
615
|
-
const activeManifestPath = join(root, ".ai-spector", "packs", activePack, "manifest.json");
|
|
616
|
-
if (existsSync(activeManifestPath)) {
|
|
617
|
-
const activeManifest = await readJson(activeManifestPath);
|
|
618
|
-
const breakoutDocs = activeManifest.documents.filter((d) => d.perDomain);
|
|
619
|
-
if (breakoutDocs.length > 0) {
|
|
620
|
-
console.log(`\n⚠ Pack has ${breakoutDocs.length} breakout template(s) — generate primary docs first, then ask:\n` +
|
|
621
|
-
breakoutDocs.map((d) => ` "generate breakout ${d.perDomain} files from the graph"`).join("\n") +
|
|
622
|
-
`\n See: .ai-spector/packs/${activePack}/generate-hints.md`);
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
console.log(`\nNote: .cursor/skills/ai-spector-generate-srs/references/runbook.md still references` +
|
|
626
|
-
`\nbuiltin ids — update it or ask the agent to use the ids above when querying the graph.`);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
// ---------------------------------------------------------------------------
|
|
630
|
-
// template inspect <name>
|
|
631
|
-
// ---------------------------------------------------------------------------
|
|
632
|
-
async function runTemplateInspect(name, opts) {
|
|
633
|
-
const { root } = await loadConfigAndRoot(opts.cwd);
|
|
634
|
-
let manifests;
|
|
635
|
-
if (name === "builtin") {
|
|
636
|
-
const { loadDocumentsManifest, loadBasicDesignListManifest } = await import("../config/load.js");
|
|
637
|
-
const { manifest: srs } = await loadDocumentsManifest();
|
|
638
|
-
const bd = await loadBasicDesignListManifest();
|
|
639
|
-
manifests = [srs, bd];
|
|
640
|
-
}
|
|
641
|
-
else {
|
|
642
|
-
const manifestPath = join(root, ".ai-spector", "packs", name, "manifest.json");
|
|
643
|
-
if (!existsSync(manifestPath)) {
|
|
644
|
-
console.error(`Pack "${name}" not found. Expected manifest at: ${manifestPath}`);
|
|
645
|
-
process.exitCode = 1;
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
const manifest = await readJson(manifestPath);
|
|
649
|
-
manifests = [manifest];
|
|
650
|
-
}
|
|
651
|
-
// Print table header
|
|
652
|
-
const header = "documentId".padEnd(40) +
|
|
653
|
-
"template".padEnd(40) +
|
|
654
|
-
"output / outputPattern".padEnd(50) +
|
|
655
|
-
"perDomain";
|
|
656
|
-
const separator = "-".repeat(header.length);
|
|
657
|
-
console.log(header);
|
|
658
|
-
console.log(separator);
|
|
659
|
-
for (const manifest of manifests) {
|
|
660
|
-
for (const doc of manifest.documents) {
|
|
661
|
-
const col1 = doc.documentId.padEnd(40);
|
|
662
|
-
const col2 = doc.template.padEnd(40);
|
|
663
|
-
const outputVal = doc.outputPattern ?? doc.output ?? "";
|
|
664
|
-
const col3 = outputVal.padEnd(50);
|
|
665
|
-
const col4 = doc.perDomain ?? "";
|
|
666
|
-
console.log(`${col1}${col2}${col3}${col4}`);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
// ---------------------------------------------------------------------------
|
|
671
|
-
// template scan <path>
|
|
672
|
-
// ---------------------------------------------------------------------------
|
|
673
|
-
async function runTemplateScan(sourcePath, opts) {
|
|
674
|
-
const resolvedSource = resolve(opts.cwd ?? process.cwd(), sourcePath);
|
|
675
|
-
// Validate source is a directory
|
|
676
|
-
if (!existsSync(resolvedSource)) {
|
|
677
|
-
console.error(`Error: path does not exist: ${resolvedSource}`);
|
|
678
|
-
process.exitCode = 1;
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
const { statSync } = await import("node:fs");
|
|
682
|
-
if (!statSync(resolvedSource).isDirectory()) {
|
|
683
|
-
console.error(`Error: path is not a directory: ${resolvedSource}`);
|
|
684
|
-
process.exitCode = 1;
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
const { root } = await loadConfigAndRoot(opts.cwd);
|
|
688
|
-
const stagingDir = join(root, ".ai-spector", "packs", ".staging");
|
|
689
|
-
// Clear staging
|
|
690
|
-
if (await pathExists(stagingDir)) {
|
|
691
|
-
await rm(stagingDir, { recursive: true, force: true });
|
|
692
|
-
}
|
|
693
|
-
await mkdir(stagingDir, { recursive: true });
|
|
694
|
-
// Scan
|
|
695
|
-
const result = await scanTemplateFolder(resolvedSource, stagingDir);
|
|
696
|
-
// Write scan-result.json
|
|
697
|
-
const scanResultPath = join(stagingDir, "scan-result.json");
|
|
698
|
-
await writeJson(scanResultPath, result);
|
|
699
|
-
// Print summary
|
|
700
|
-
console.log(`\nScanned ${resolvedSource}:`);
|
|
701
|
-
console.log(` ${result.files.length} template file${result.files.length === 1 ? "" : "s"} found`);
|
|
702
|
-
console.log();
|
|
703
|
-
if (result.files.length > 0) {
|
|
704
|
-
const fileCol = 32;
|
|
705
|
-
const headCol = 10;
|
|
706
|
-
const header = "File".padEnd(fileCol) + "Headings".padEnd(headCol) + "Placeholders";
|
|
707
|
-
const divider = "─".repeat(header.length + 10);
|
|
708
|
-
console.log(` ${header}`);
|
|
709
|
-
console.log(` ${divider}`);
|
|
710
|
-
for (const f of result.files) {
|
|
711
|
-
const name = f.relativePath.padEnd(fileCol);
|
|
712
|
-
const headings = String(f.headings.length).padEnd(headCol);
|
|
713
|
-
const placeholders = f.placeholders.join(", ") || "(none)";
|
|
714
|
-
console.log(` ${name}${headings}${placeholders}`);
|
|
715
|
-
}
|
|
716
|
-
console.log();
|
|
717
|
-
}
|
|
718
|
-
const relStaging = join(".ai-spector", "packs", ".staging", "scan-result.json");
|
|
719
|
-
console.log(`Scan saved → ${relStaging}`);
|
|
720
|
-
console.log();
|
|
721
|
-
console.log(`Next step: open your AI IDE and ask:`);
|
|
722
|
-
console.log(` "set up template pack"`);
|
|
723
|
-
console.log(`The AI will read the scan result, ask you questions, refine`);
|
|
724
|
-
console.log(`the templates, and write them to staging for you to review.`);
|
|
725
|
-
}
|
|
726
|
-
// ---------------------------------------------------------------------------
|
|
727
|
-
// template install [--name <name>] [--dry-run]
|
|
728
|
-
// ---------------------------------------------------------------------------
|
|
729
|
-
async function runTemplateInstall(opts) {
|
|
730
|
-
const { root, config, configFile } = await loadConfigAndRoot(opts.cwd);
|
|
731
|
-
const stagingDir = join(root, ".ai-spector", "packs", ".staging");
|
|
732
|
-
// Check scan-result.json exists
|
|
733
|
-
const scanResultPath = join(stagingDir, "scan-result.json");
|
|
734
|
-
if (!(await pathExists(scanResultPath))) {
|
|
735
|
-
console.error('Error: scan-result.json not found in staging. Run `template scan` first.');
|
|
736
|
-
process.exitCode = 1;
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
// Check staleness (> 24h)
|
|
740
|
-
const scanResult = await readJson(scanResultPath);
|
|
741
|
-
const scannedAt = new Date(scanResult.scannedAt);
|
|
742
|
-
const ageMs = Date.now() - scannedAt.getTime();
|
|
743
|
-
const ageHours = Math.floor(ageMs / (1000 * 60 * 60));
|
|
744
|
-
if (ageMs > 24 * 60 * 60 * 1000) {
|
|
745
|
-
console.warn(`Warning: scan is ${ageHours} hours old — the AI may have worked with stale context.`);
|
|
746
|
-
}
|
|
747
|
-
// Check manifest.json exists
|
|
748
|
-
const stagingManifestPath = join(stagingDir, "manifest.json");
|
|
749
|
-
if (!(await pathExists(stagingManifestPath))) {
|
|
750
|
-
console.error('Error: manifest.json not found in staging. Ask the AI to complete the setup workflow.');
|
|
751
|
-
process.exitCode = 1;
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
// Load + validate manifest
|
|
755
|
-
const rawManifest = await readJson(stagingManifestPath);
|
|
756
|
-
const { valid, errors } = validatePackManifest(rawManifest);
|
|
757
|
-
if (!valid) {
|
|
758
|
-
console.error("manifest.json validation failed:");
|
|
759
|
-
for (const err of errors) {
|
|
760
|
-
console.error(` - ${err}`);
|
|
761
|
-
}
|
|
762
|
-
process.exitCode = 1;
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
const manifest = rawManifest;
|
|
766
|
-
// Determine pack name
|
|
767
|
-
const packName = opts.name ?? manifest.packName;
|
|
768
|
-
// Check all template files exist in staging/templates/
|
|
769
|
-
const stagingTemplatesDir = join(stagingDir, manifest.templatesDir ?? "templates");
|
|
770
|
-
const missing = [];
|
|
771
|
-
for (const doc of manifest.documents) {
|
|
772
|
-
const tplPath = join(stagingTemplatesDir, doc.template);
|
|
773
|
-
if (!(await pathExists(tplPath))) {
|
|
774
|
-
missing.push(doc.template);
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
if (missing.length > 0) {
|
|
778
|
-
console.error("Error: the following template files are missing from staging:");
|
|
779
|
-
for (const f of missing) {
|
|
780
|
-
console.error(` - ${f}`);
|
|
781
|
-
}
|
|
782
|
-
process.exitCode = 1;
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
// Dry run — stop here
|
|
786
|
-
if (opts.dryRun) {
|
|
787
|
-
console.log(`Dry run complete — all checks passed. Pack: ${packName}`);
|
|
788
|
-
return;
|
|
789
|
-
}
|
|
790
|
-
// Destination
|
|
791
|
-
const destDir = join(root, ".ai-spector", "packs", packName);
|
|
792
|
-
// Remove existing if present
|
|
793
|
-
if (await pathExists(destDir)) {
|
|
794
|
-
await rm(destDir, { recursive: true, force: true });
|
|
795
|
-
}
|
|
796
|
-
// Copy templates
|
|
797
|
-
const destTemplatesDir = join(destDir, "templates");
|
|
798
|
-
await copyTree(stagingTemplatesDir, destTemplatesDir);
|
|
799
|
-
// Write manifest to destination (with resolved packName)
|
|
800
|
-
const finalManifest = { ...manifest, packName };
|
|
801
|
-
await writeJson(join(destDir, "manifest.json"), finalManifest);
|
|
802
|
-
// Patch config FIRST
|
|
803
|
-
const previousPacks = config.packs;
|
|
804
|
-
config.packs = { active: packName };
|
|
805
|
-
await saveConfig(configFile, config);
|
|
806
|
-
// Build registry + graph, roll back on failure
|
|
807
|
-
let stats;
|
|
808
|
-
try {
|
|
809
|
-
stats = await rebuildRegistryAndGraph(root, config);
|
|
810
|
-
}
|
|
811
|
-
catch (err) {
|
|
812
|
-
// Roll back config
|
|
813
|
-
if (previousPacks === undefined) {
|
|
814
|
-
delete config.packs;
|
|
815
|
-
}
|
|
816
|
-
else {
|
|
817
|
-
config.packs = previousPacks;
|
|
818
|
-
}
|
|
819
|
-
await saveConfig(configFile, config);
|
|
820
|
-
throw err;
|
|
821
|
-
}
|
|
822
|
-
// Write pack-derived DAG files, generate-hints.md, and dedicated generate skill
|
|
823
|
-
const { dag, seeds } = buildDagFromManifest(finalManifest);
|
|
824
|
-
await writeDagFiles(root, dag, seeds);
|
|
825
|
-
await writeGenerateHints(destDir, finalManifest);
|
|
826
|
-
await writeContextMap(destDir, finalManifest, scanResult);
|
|
827
|
-
const stagedSkillPath = join(stagingDir, "generate-skill.md");
|
|
828
|
-
await writePackGenerateSkill(root, finalManifest, stagedSkillPath);
|
|
829
|
-
// skill-hints.md — if the AI wrote one in staging, copy to pack dir
|
|
830
|
-
const skillHintsPath = join(stagingDir, "skill-hints.md");
|
|
831
|
-
if (await pathExists(skillHintsPath)) {
|
|
832
|
-
await copyFile(skillHintsPath, join(destDir, "skill-hints.md"));
|
|
833
|
-
}
|
|
834
|
-
// Clear staging
|
|
835
|
-
await rm(stagingDir, { recursive: true, force: true });
|
|
836
|
-
// Print summary
|
|
837
|
-
console.log(`\n✓ Pack '${packName}' installed and activated.\n`);
|
|
838
|
-
console.log(` Documents : ${stats.documents}`);
|
|
839
|
-
console.log(` Sections : ${stats.sections}`);
|
|
840
|
-
console.log(` Graph : ${stats.graphNodes} nodes, ${stats.graphEdges} edges`);
|
|
841
|
-
console.log();
|
|
842
|
-
const usedAiSkill = existsSync(stagedSkillPath);
|
|
843
|
-
console.log(` Templates → .ai-spector/packs/${packName}/templates/`);
|
|
844
|
-
console.log(` Active → docflow.config.json updated`);
|
|
845
|
-
console.log(` DAG → .ai-spector/.docflow/config/dag.srs.json updated`);
|
|
846
|
-
console.log(` Skill → .cursor/skills/ai-spector-generate-${packName}/SKILL.md ${usedAiSkill ? "(AI-written)" : "(auto-generated from manifest)"}`);
|
|
847
|
-
console.log(` → .claude/skills/ai-spector-generate-${packName}/skill.md`);
|
|
848
|
-
const breakoutDocs = finalManifest.documents.filter((d) => d.perDomain);
|
|
849
|
-
if (breakoutDocs.length > 0) {
|
|
850
|
-
console.log();
|
|
851
|
-
console.log(` ⚠ This pack includes ${breakoutDocs.length} per-domain breakout template(s):`);
|
|
852
|
-
for (const d of breakoutDocs) {
|
|
853
|
-
console.log(` ${d.documentId} (perDomain: ${d.perDomain})`);
|
|
854
|
-
console.log(` output: ${d.outputPattern ?? d.output ?? "(see manifest)"}`);
|
|
855
|
-
}
|
|
856
|
-
console.log();
|
|
857
|
-
console.log(` Breakout files are NOT generated automatically.`);
|
|
858
|
-
console.log(` After "generate SRS", ask your AI IDE:`);
|
|
859
|
-
console.log(` "generate breakout ${breakoutDocs[0]?.perDomain ?? "requirement"} files from the graph"`);
|
|
860
|
-
console.log(` Reference: .ai-spector/packs/${packName}/generate-hints.md`);
|
|
861
|
-
}
|
|
862
|
-
console.log();
|
|
863
|
-
console.log(`Next: ask your AI to "generate <document>" to use the new template.`);
|
|
864
|
-
}
|
|
865
|
-
// ---------------------------------------------------------------------------
|
|
866
|
-
// template verify <name>
|
|
867
|
-
// ---------------------------------------------------------------------------
|
|
868
|
-
/**
|
|
869
|
-
* Verify a pack is correctly wired:
|
|
870
|
-
* - All template files referenced in manifest exist on disk
|
|
871
|
-
* - context-map.json has no TODO entries (warns, not error)
|
|
872
|
-
* - generate-hints.md and dag files exist
|
|
873
|
-
* - All output paths / outputPatterns are non-empty
|
|
874
|
-
*/
|
|
875
|
-
async function runTemplateVerify(name, opts) {
|
|
876
|
-
const { root, config } = await loadConfigAndRoot(opts.cwd);
|
|
877
|
-
const resolvedName = name === "active" ? (config.packs?.active ?? "builtin") : name;
|
|
878
|
-
if (resolvedName === "builtin") {
|
|
879
|
-
console.log("Builtin pack — no verification needed.");
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
const packDir = join(root, ".ai-spector", "packs", resolvedName);
|
|
883
|
-
const manifestPath = join(packDir, "manifest.json");
|
|
884
|
-
if (!(await pathExists(manifestPath))) {
|
|
885
|
-
console.error(`Error: pack "${resolvedName}" not found at ${packDir}`);
|
|
886
|
-
process.exitCode = 1;
|
|
887
|
-
return;
|
|
888
|
-
}
|
|
889
|
-
const manifest = await readJson(manifestPath);
|
|
890
|
-
const errors = [];
|
|
891
|
-
const warnings = [];
|
|
892
|
-
// 1. Template files
|
|
893
|
-
for (const doc of manifest.documents) {
|
|
894
|
-
const tplPath = join(packDir, "templates", doc.template);
|
|
895
|
-
if (!(await pathExists(tplPath))) {
|
|
896
|
-
errors.push(`Missing template file: templates/${doc.template} (documentId: ${doc.documentId})`);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
// 2. Output paths defined
|
|
900
|
-
for (const doc of manifest.documents) {
|
|
901
|
-
const hasOutput = typeof doc.output === "string" && doc.output.length > 0;
|
|
902
|
-
const hasPattern = typeof doc.outputPattern === "string" && doc.outputPattern.length > 0;
|
|
903
|
-
if (!hasOutput && !hasPattern) {
|
|
904
|
-
errors.push(`Document "${doc.documentId}" has no output or outputPattern`);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
// 3. generate-hints.md and DAG files
|
|
908
|
-
for (const f of ["generate-hints.md", "context-map.json"]) {
|
|
909
|
-
if (!(await pathExists(join(packDir, f)))) {
|
|
910
|
-
warnings.push(`${f} is missing — run \`template use ${resolvedName}\` to regenerate`);
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
const dagDir = join(root, ".ai-spector", ".docflow", "config");
|
|
914
|
-
if (config.packs?.active === resolvedName) {
|
|
915
|
-
for (const f of ["dag.srs.json", "dag.srs.graph-seeds.json"]) {
|
|
916
|
-
if (!(await pathExists(join(dagDir, f)))) {
|
|
917
|
-
warnings.push(`${f} is missing — run \`template use ${resolvedName}\` to regenerate`);
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
// 4. context-map TODO entries
|
|
922
|
-
const contextMapPath = join(packDir, "context-map.json");
|
|
923
|
-
if (await pathExists(contextMapPath)) {
|
|
924
|
-
try {
|
|
925
|
-
const cm = await readJson(contextMapPath);
|
|
926
|
-
const todos = Object.entries(cm.placeholders ?? {})
|
|
927
|
-
.filter(([, v]) => v.source === "TODO")
|
|
928
|
-
.map(([k]) => k);
|
|
929
|
-
if (todos.length > 0) {
|
|
930
|
-
warnings.push(`context-map.json has ${todos.length} unresolved placeholder(s): ${todos.join(", ")}` +
|
|
931
|
-
`\n Edit .ai-spector/packs/${resolvedName}/context-map.json or ask the AI to fill them in.`);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
catch {
|
|
935
|
-
warnings.push("context-map.json could not be parsed");
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
// 5. gen-status.json — show missing breakout items if present
|
|
939
|
-
const genStatusPath = join(packDir, "gen-status.json");
|
|
940
|
-
if (await pathExists(genStatusPath)) {
|
|
941
|
-
try {
|
|
942
|
-
const gs = await readJson(genStatusPath);
|
|
943
|
-
const blocked = (gs.items ?? []).filter((i) => i.status === "blocked");
|
|
944
|
-
const pending = (gs.items ?? []).filter((i) => i.status === "pending");
|
|
945
|
-
if (blocked.length > 0) {
|
|
946
|
-
warnings.push(`${blocked.length} breakout item(s) are blocked:\n` +
|
|
947
|
-
blocked.map((i) => ` ${i.itemId}: ${i.blockedReason ?? "no reason given"}`).join("\n"));
|
|
948
|
-
}
|
|
949
|
-
if (pending.length > 0) {
|
|
950
|
-
warnings.push(`${pending.length} breakout item(s) still pending: ${pending.map((i) => i.itemId).join(", ")}`);
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
catch {
|
|
954
|
-
warnings.push("gen-status.json could not be parsed");
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
// Report
|
|
958
|
-
if (errors.length === 0 && warnings.length === 0) {
|
|
959
|
-
console.log(`✓ Pack "${resolvedName}" verified — no issues found.`);
|
|
960
|
-
return;
|
|
961
|
-
}
|
|
962
|
-
if (warnings.length > 0) {
|
|
963
|
-
console.log(`Warnings for pack "${resolvedName}":`);
|
|
964
|
-
for (const w of warnings)
|
|
965
|
-
console.warn(` ⚠ ${w}`);
|
|
966
|
-
}
|
|
967
|
-
if (errors.length > 0) {
|
|
968
|
-
console.log(`\nErrors for pack "${resolvedName}":`);
|
|
969
|
-
for (const e of errors)
|
|
970
|
-
console.error(` ✗ ${e}`);
|
|
971
|
-
process.exitCode = 1;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
// ---------------------------------------------------------------------------
|
|
975
|
-
// template status [name]
|
|
976
|
-
// ---------------------------------------------------------------------------
|
|
977
|
-
/**
|
|
978
|
-
* Show generation progress for a pack's breakout items.
|
|
979
|
-
* Reads gen-status.json if present; otherwise reports no tracking data.
|
|
980
|
-
*/
|
|
981
|
-
async function runTemplateStatus(name, opts) {
|
|
982
|
-
const { root, config } = await loadConfigAndRoot(opts.cwd);
|
|
983
|
-
const resolvedName = name ?? config.packs?.active ?? "builtin";
|
|
984
|
-
if (resolvedName === "builtin") {
|
|
985
|
-
console.log("Builtin pack has no breakout items to track.");
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
const packDir = join(root, ".ai-spector", "packs", resolvedName);
|
|
989
|
-
const manifest = await readJson(join(packDir, "manifest.json")).catch(() => null);
|
|
990
|
-
if (!manifest) {
|
|
991
|
-
console.error(`Error: pack "${resolvedName}" not found.`);
|
|
992
|
-
process.exitCode = 1;
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
|
-
const breakoutDocs = manifest.documents.filter((d) => d.perDomain);
|
|
996
|
-
if (breakoutDocs.length === 0) {
|
|
997
|
-
console.log(`Pack "${resolvedName}" has no breakout templates — nothing to track.`);
|
|
998
|
-
return;
|
|
999
|
-
}
|
|
1000
|
-
const genStatusPath = join(packDir, "gen-status.json");
|
|
1001
|
-
if (!(await pathExists(genStatusPath))) {
|
|
1002
|
-
console.log(`No gen-status.json found for pack "${resolvedName}".`);
|
|
1003
|
-
console.log(`This file is written by the AI as it generates breakout items.`);
|
|
1004
|
-
console.log(`Run \`template verify ${resolvedName}\` after generation to check for issues.`);
|
|
1005
|
-
return;
|
|
1006
|
-
}
|
|
1007
|
-
const gs = await readJson(genStatusPath);
|
|
1008
|
-
const items = gs.items ?? [];
|
|
1009
|
-
const done = items.filter((i) => i.status === "done" && !i.staleSince);
|
|
1010
|
-
const stale = items.filter((i) => i.status === "done" && i.staleSince);
|
|
1011
|
-
const pending = items.filter((i) => i.status === "pending");
|
|
1012
|
-
const blocked = items.filter((i) => i.status === "blocked");
|
|
1013
|
-
console.log(`\nGeneration status for pack "${resolvedName}"\n`);
|
|
1014
|
-
console.log(` Total : ${items.length}`);
|
|
1015
|
-
console.log(` Done : ${done.length}`);
|
|
1016
|
-
console.log(` Stale : ${stale.length}${stale.length > 0 ? " ← run \`template regen\` to see details" : ""}`);
|
|
1017
|
-
console.log(` Pending : ${pending.length}`);
|
|
1018
|
-
console.log(` Blocked : ${blocked.length}`);
|
|
1019
|
-
if (stale.length > 0) {
|
|
1020
|
-
console.log(`\n⚠ Stale (source changed since generation):`);
|
|
1021
|
-
for (const i of stale) {
|
|
1022
|
-
console.log(` ${i.itemId} → ${i.outputFile ?? "(no output path)"} (stale since ${i.staleSince.slice(0, 10)})`);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
if (done.length > 0) {
|
|
1026
|
-
console.log(`\n✓ Done:`);
|
|
1027
|
-
for (const i of done) {
|
|
1028
|
-
console.log(` ${i.itemId} → ${i.outputFile ?? "(no output path)"}${i.generatedAt ? ` (${i.generatedAt.slice(0, 10)})` : ""}`);
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
if (pending.length > 0) {
|
|
1032
|
-
console.log(`\n○ Pending:`);
|
|
1033
|
-
for (const i of pending)
|
|
1034
|
-
console.log(` ${i.itemId}`);
|
|
1035
|
-
}
|
|
1036
|
-
if (blocked.length > 0) {
|
|
1037
|
-
console.log(`\n⚠ Blocked (needs human input):`);
|
|
1038
|
-
for (const i of blocked)
|
|
1039
|
-
console.log(` ${i.itemId}: ${i.blockedReason ?? "no reason given"}`);
|
|
1040
|
-
}
|
|
1041
|
-
if (blocked.length > 0 || pending.length > 0) {
|
|
1042
|
-
console.log(`\nTo resume: ask your AI to continue generating breakout items for pack "${resolvedName}".`);
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
// ---------------------------------------------------------------------------
|
|
1046
|
-
// template remove <name>
|
|
1047
|
-
// ---------------------------------------------------------------------------
|
|
1048
|
-
async function runTemplateRemove(name, opts) {
|
|
1049
|
-
const { root, config } = await loadConfigAndRoot(opts.cwd);
|
|
1050
|
-
const packDir = join(root, ".ai-spector", "packs", name);
|
|
1051
|
-
if (!(await pathExists(packDir))) {
|
|
1052
|
-
console.error(`Error: pack "${name}" is not installed.`);
|
|
1053
|
-
process.exitCode = 1;
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
if (config.packs?.active === name) {
|
|
1057
|
-
console.error(`Error: pack '${name}' is currently active. Run \`template use builtin\` first.`);
|
|
1058
|
-
process.exitCode = 1;
|
|
1059
|
-
return;
|
|
1060
|
-
}
|
|
1061
|
-
await rm(packDir, { recursive: true, force: true });
|
|
1062
|
-
console.log(`Removed pack '${name}'.`);
|
|
1063
|
-
}
|
|
1064
|
-
// ---------------------------------------------------------------------------
|
|
1065
|
-
// template export <output-path>
|
|
1066
|
-
// ---------------------------------------------------------------------------
|
|
1067
|
-
async function runTemplateExport(outputPath, opts) {
|
|
1068
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
1069
|
-
const { root, config } = await loadConfigAndRoot(cwd);
|
|
1070
|
-
// Determine pack name to export
|
|
1071
|
-
let packName = opts.pack ?? config.packs?.active ?? "builtin";
|
|
1072
|
-
if (!packName || packName === "default")
|
|
1073
|
-
packName = "builtin";
|
|
1074
|
-
// Resolve output path
|
|
1075
|
-
const outputAbs = resolve(cwd, outputPath);
|
|
1076
|
-
// Check if output exists
|
|
1077
|
-
if (await pathExists(outputAbs)) {
|
|
1078
|
-
if (!opts.overwrite) {
|
|
1079
|
-
console.error("Output path already exists. Use --overwrite to replace it.");
|
|
1080
|
-
process.exitCode = 1;
|
|
1081
|
-
return;
|
|
1082
|
-
}
|
|
1083
|
-
await rm(outputAbs, { recursive: true, force: true });
|
|
1084
|
-
}
|
|
1085
|
-
// Create output dirs
|
|
1086
|
-
const outputTemplatesDir = join(outputAbs, "templates");
|
|
1087
|
-
await mkdir(outputTemplatesDir, { recursive: true });
|
|
1088
|
-
let manifest;
|
|
1089
|
-
let templateCount = 0;
|
|
1090
|
-
if (packName === "builtin") {
|
|
1091
|
-
// Load both builtin manifests
|
|
1092
|
-
const { bundleRoot, manifest: srsManifest } = await loadDocumentsManifest();
|
|
1093
|
-
const bdManifest = await loadBasicDesignListManifest();
|
|
1094
|
-
// Merge documents, copying templates while preserving subdirectory structure
|
|
1095
|
-
const allDocs = [];
|
|
1096
|
-
for (const srcManifest of [srsManifest, bdManifest]) {
|
|
1097
|
-
const srcTemplatesDir = join(bundleRoot, srcManifest.templatesDir);
|
|
1098
|
-
for (const doc of srcManifest.documents) {
|
|
1099
|
-
// srcManifest.templatesDir is e.g. "templates/srs" — extract the subdir
|
|
1100
|
-
// relative to the root "templates/" folder
|
|
1101
|
-
const subdir = srcManifest.templatesDir.replace(/^templates\/?/, "");
|
|
1102
|
-
const relPath = subdir ? join(subdir, doc.template) : doc.template;
|
|
1103
|
-
const srcFile = join(srcTemplatesDir, doc.template);
|
|
1104
|
-
const destFile = join(outputTemplatesDir, relPath);
|
|
1105
|
-
// Ensure subdirectory exists
|
|
1106
|
-
await mkdir(join(outputTemplatesDir, subdir || "."), { recursive: true });
|
|
1107
|
-
if (await pathExists(srcFile)) {
|
|
1108
|
-
await copyFile(srcFile, destFile);
|
|
1109
|
-
templateCount++;
|
|
1110
|
-
}
|
|
1111
|
-
allDocs.push({ ...doc, template: relPath });
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
manifest = {
|
|
1115
|
-
version: srsManifest.version,
|
|
1116
|
-
name: "builtin",
|
|
1117
|
-
packName: "builtin",
|
|
1118
|
-
templatesDir: "templates",
|
|
1119
|
-
nodePrefix: "doc.srs",
|
|
1120
|
-
perDomainTemplates: {
|
|
1121
|
-
useCase: "doc.srs.use-case-detail",
|
|
1122
|
-
feature: "doc.srs.system-feature-detail",
|
|
1123
|
-
},
|
|
1124
|
-
defaultListedIn: {
|
|
1125
|
-
useCase: "doc.srs.use-cases",
|
|
1126
|
-
feature: "doc.srs.system-features-list",
|
|
1127
|
-
actor: "doc.srs.use-cases",
|
|
1128
|
-
},
|
|
1129
|
-
documents: allDocs,
|
|
1130
|
-
};
|
|
1131
|
-
}
|
|
1132
|
-
else {
|
|
1133
|
-
// Custom pack
|
|
1134
|
-
const packDir = join(root, ".ai-spector", "packs", packName);
|
|
1135
|
-
const manifestPath = join(packDir, "manifest.json");
|
|
1136
|
-
if (!(await pathExists(manifestPath))) {
|
|
1137
|
-
console.error(`Pack "${packName}" not found. Expected manifest at: ${manifestPath}`);
|
|
1138
|
-
process.exitCode = 1;
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
manifest = await readJson(manifestPath);
|
|
1142
|
-
const srcTemplatesDir = join(packDir, manifest.templatesDir ?? "templates");
|
|
1143
|
-
// Copy templates preserving structure
|
|
1144
|
-
if (await pathExists(srcTemplatesDir)) {
|
|
1145
|
-
await copyTree(srcTemplatesDir, outputTemplatesDir);
|
|
1146
|
-
// Count template files
|
|
1147
|
-
const countFiles = async (dir) => {
|
|
1148
|
-
let count = 0;
|
|
1149
|
-
try {
|
|
1150
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
1151
|
-
for (const entry of entries) {
|
|
1152
|
-
if (entry.isDirectory()) {
|
|
1153
|
-
count += await countFiles(join(dir, entry.name));
|
|
1154
|
-
}
|
|
1155
|
-
else if (entry.name.endsWith(".md")) {
|
|
1156
|
-
count++;
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
catch { /* ignore */ }
|
|
1161
|
-
return count;
|
|
1162
|
-
};
|
|
1163
|
-
templateCount = await countFiles(srcTemplatesDir);
|
|
1164
|
-
}
|
|
1165
|
-
// Override templatesDir to "templates" in output
|
|
1166
|
-
manifest = { ...manifest, templatesDir: "templates" };
|
|
1167
|
-
// Copy skill-hints.md if present
|
|
1168
|
-
const skillHintsSrc = join(packDir, "skill-hints.md");
|
|
1169
|
-
if (await pathExists(skillHintsSrc)) {
|
|
1170
|
-
await copyFile(skillHintsSrc, join(outputAbs, "skill-hints.md"));
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
// Write manifest.json
|
|
1174
|
-
await writeJson(join(outputAbs, "manifest.json"), manifest);
|
|
1175
|
-
// Print summary
|
|
1176
|
-
const relOutput = outputPath.startsWith("/") ? outputPath : `./${outputPath}`;
|
|
1177
|
-
console.log(`\n✓ Exported pack '${packName}' to ${relOutput}/\n`);
|
|
1178
|
-
console.log(` Templates : ${templateCount} files`);
|
|
1179
|
-
console.log(` Manifest : ${join(outputPath, "manifest.json")}`);
|
|
1180
|
-
console.log();
|
|
1181
|
-
console.log("To use this template in another project:");
|
|
1182
|
-
console.log(" 1. Copy the folder to the other project's workspace");
|
|
1183
|
-
console.log(` 2. Run: npx ai-spector template scan ./${outputPath}`);
|
|
1184
|
-
console.log(' 3. Ask your AI: "set up template pack"');
|
|
1185
|
-
}
|
|
1186
|
-
// ---------------------------------------------------------------------------
|
|
1187
|
-
// Command group registration
|
|
1188
|
-
// ---------------------------------------------------------------------------
|
|
1189
|
-
export function registerTemplateCommand(program) {
|
|
1190
|
-
const template = program
|
|
1191
|
-
.command("template")
|
|
1192
|
-
.description("Template pack management: list, use, inspect, scan, install, remove");
|
|
1193
|
-
template
|
|
1194
|
-
.command("list")
|
|
1195
|
-
.description("List installed template packs and show which is active")
|
|
1196
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1197
|
-
.action(async (opts) => {
|
|
1198
|
-
await runTemplateList({ cwd: resolve(opts.cwd ?? process.cwd()) });
|
|
1199
|
-
});
|
|
1200
|
-
template
|
|
1201
|
-
.command("use <name>")
|
|
1202
|
-
.description('Switch to a template pack (use "builtin" to revert to default). Re-indexes graph automatically.')
|
|
1203
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1204
|
-
.action(async (name, opts) => {
|
|
1205
|
-
await runTemplateUse(name, { cwd: resolve(opts.cwd ?? process.cwd()) });
|
|
1206
|
-
});
|
|
1207
|
-
template
|
|
1208
|
-
.command("inspect <name>")
|
|
1209
|
-
.description('Inspect a pack manifest as a table (use "builtin" to inspect the builtin templates).')
|
|
1210
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1211
|
-
.action(async (name, opts) => {
|
|
1212
|
-
await runTemplateInspect(name, { cwd: resolve(opts.cwd ?? process.cwd()) });
|
|
1213
|
-
});
|
|
1214
|
-
template
|
|
1215
|
-
.command("scan <path>")
|
|
1216
|
-
.description("Walk a folder of .md template files, extract headings + placeholders, write scan-result.json to staging.")
|
|
1217
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1218
|
-
.action(async (sourcePath, opts) => {
|
|
1219
|
-
await runTemplateScan(sourcePath, { cwd: resolve(opts.cwd ?? process.cwd()) });
|
|
1220
|
-
});
|
|
1221
|
-
template
|
|
1222
|
-
.command("install")
|
|
1223
|
-
.description("Install a template pack from staging (manifest.json + templates/ written by AI).")
|
|
1224
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1225
|
-
.option("--name <name>", "Override pack name from manifest")
|
|
1226
|
-
.option("--dry-run", "Validate and print without writing anything")
|
|
1227
|
-
.action(async (opts) => {
|
|
1228
|
-
await runTemplateInstall({
|
|
1229
|
-
cwd: resolve(opts.cwd ?? process.cwd()),
|
|
1230
|
-
name: opts.name,
|
|
1231
|
-
dryRun: Boolean(opts.dryRun),
|
|
1232
|
-
});
|
|
1233
|
-
});
|
|
1234
|
-
template
|
|
1235
|
-
.command("verify [name]")
|
|
1236
|
-
.description('Verify a pack is correctly wired: templates exist, output paths defined, context-map TODOs flagged. Use "active" or omit to check the active pack.')
|
|
1237
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1238
|
-
.action(async (name, opts) => {
|
|
1239
|
-
await runTemplateVerify(name ?? "active", { cwd: resolve(opts.cwd ?? process.cwd()) });
|
|
1240
|
-
});
|
|
1241
|
-
template
|
|
1242
|
-
.command("status [name]")
|
|
1243
|
-
.description("Show breakout-item generation progress for a pack (reads gen-status.json written by the AI during generation).")
|
|
1244
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1245
|
-
.action(async (name, opts) => {
|
|
1246
|
-
await runTemplateStatus(name, { cwd: resolve(opts.cwd ?? process.cwd()) });
|
|
1247
|
-
});
|
|
1248
|
-
template
|
|
1249
|
-
.command("regen [pack]")
|
|
1250
|
-
.description("Detect which pack output files are stale after source changes, write regen-plan.md, and update gen-status.json. Uses git diff by default.")
|
|
1251
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1252
|
-
.option("--origin <nodeId>", "Use a specific graph node as impact origin instead of git diff")
|
|
1253
|
-
.option("--json", "Output regen plan as JSON")
|
|
1254
|
-
.option("--dry-run", "Print stale files without writing regen-plan.md")
|
|
1255
|
-
.action(async (pack, opts) => {
|
|
1256
|
-
await runTemplateRegen({
|
|
1257
|
-
cwd: resolve(opts.cwd ?? process.cwd()),
|
|
1258
|
-
pack,
|
|
1259
|
-
origin: opts.origin,
|
|
1260
|
-
json: Boolean(opts.json),
|
|
1261
|
-
dryRun: Boolean(opts.dryRun),
|
|
1262
|
-
});
|
|
1263
|
-
});
|
|
1264
|
-
template
|
|
1265
|
-
.command("remove <name>")
|
|
1266
|
-
.description("Remove an installed template pack by name.")
|
|
1267
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1268
|
-
.action(async (name, opts) => {
|
|
1269
|
-
await runTemplateRemove(name, { cwd: resolve(opts.cwd ?? process.cwd()) });
|
|
1270
|
-
});
|
|
1271
|
-
template
|
|
1272
|
-
.command("export <output-path>")
|
|
1273
|
-
.description("Export the active (or specified) template pack as a portable folder for use in another project.")
|
|
1274
|
-
.option("-C, --cwd <path>", "Project root", process.cwd())
|
|
1275
|
-
.option("--pack <name>", "Pack to export (default: active pack)")
|
|
1276
|
-
.option("--overwrite", "Replace output path if it already exists")
|
|
1277
|
-
.action(async (outputPath, opts) => {
|
|
1278
|
-
await runTemplateExport(outputPath, {
|
|
1279
|
-
cwd: resolve(opts.cwd ?? process.cwd()),
|
|
1280
|
-
pack: opts.pack,
|
|
1281
|
-
overwrite: Boolean(opts.overwrite),
|
|
1282
|
-
});
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
//# sourceMappingURL=template.js.map
|