@edupia-tutor/spec-driven-docs 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/build.js +230 -0
- package/bin/index.js +598 -0
- package/commands/debug.md +830 -0
- package/commands/debug.tmpl +257 -0
- package/commands/define-product.md +652 -0
- package/commands/define-product.tmpl +158 -0
- package/commands/dev-gen-test.md +1010 -0
- package/commands/dev-gen-test.tmpl +490 -0
- package/commands/dev-run-test.md +744 -0
- package/commands/dev-run-test.tmpl +224 -0
- package/commands/dev-smoke-test.md +711 -0
- package/commands/dev-smoke-test.tmpl +217 -0
- package/commands/fix-bug.md +744 -0
- package/commands/fix-bug.tmpl +171 -0
- package/commands/generate-bdd.md +1054 -0
- package/commands/generate-bdd.tmpl +534 -0
- package/commands/generate-code.md +869 -0
- package/commands/generate-code.tmpl +349 -0
- package/commands/generate-design-spec.md +958 -0
- package/commands/generate-design-spec.tmpl +464 -0
- package/commands/generate-prd.md +748 -0
- package/commands/generate-prd.tmpl +254 -0
- package/commands/generate-spec-manifest.md +658 -0
- package/commands/generate-spec-manifest.tmpl +164 -0
- package/commands/generate-tech-docs.md +849 -0
- package/commands/generate-tech-docs.tmpl +355 -0
- package/commands/learn.md +636 -0
- package/commands/learn.tmpl +63 -0
- package/commands/map-testids.md +575 -0
- package/commands/map-testids.tmpl +81 -0
- package/commands/propose-scenario.md +623 -0
- package/commands/propose-scenario.tmpl +129 -0
- package/commands/qc-analyze.md +580 -0
- package/commands/qc-analyze.tmpl +86 -0
- package/commands/qc-design-test.md +562 -0
- package/commands/qc-design-test.tmpl +68 -0
- package/commands/qc-plan.md +543 -0
- package/commands/qc-plan.tmpl +49 -0
- package/commands/qc-report.md +554 -0
- package/commands/qc-report.tmpl +60 -0
- package/commands/qc-review.md +547 -0
- package/commands/qc-review.tmpl +53 -0
- package/commands/qc-run-test.md +604 -0
- package/commands/qc-run-test.tmpl +84 -0
- package/commands/refine-prd.md +772 -0
- package/commands/refine-prd.tmpl +140 -0
- package/commands/report-bug.md +639 -0
- package/commands/report-bug.tmpl +145 -0
- package/commands/review-code.md +677 -0
- package/commands/review-code.tmpl +104 -0
- package/commands/review-context.md +1047 -0
- package/commands/review-context.tmpl +415 -0
- package/commands/review-tech-docs.md +811 -0
- package/commands/review-tech-docs.tmpl +317 -0
- package/commands/setup-ai-first.md +545 -0
- package/commands/setup-ai-first.tmpl +358 -0
- package/commands/sync.md +451 -0
- package/commands/sync.tmpl +351 -0
- package/commands/update-framework.md +251 -0
- package/commands/update-framework.tmpl +151 -0
- package/commands/validate-traces.md +842 -0
- package/commands/validate-traces.tmpl +348 -0
- package/core/FRAMEWORK_VERSION +1 -0
- package/core/commands/debug.md +830 -0
- package/core/commands/define-product.md +652 -0
- package/core/commands/dev-gen-test.md +1010 -0
- package/core/commands/dev-run-test.md +744 -0
- package/core/commands/dev-smoke-test.md +711 -0
- package/core/commands/fix-bug.md +744 -0
- package/core/commands/generate-bdd.md +1054 -0
- package/core/commands/generate-code.md +869 -0
- package/core/commands/generate-design-spec.md +958 -0
- package/core/commands/generate-prd.md +748 -0
- package/core/commands/generate-spec-manifest.md +658 -0
- package/core/commands/generate-tech-docs.md +849 -0
- package/core/commands/learn.md +636 -0
- package/core/commands/map-testids.md +575 -0
- package/core/commands/propose-scenario.md +623 -0
- package/core/commands/qc-analyze.md +580 -0
- package/core/commands/qc-design-test.md +562 -0
- package/core/commands/qc-plan.md +543 -0
- package/core/commands/qc-report.md +554 -0
- package/core/commands/qc-review.md +547 -0
- package/core/commands/qc-run-test.md +604 -0
- package/core/commands/refine-prd.md +772 -0
- package/core/commands/report-bug.md +639 -0
- package/core/commands/review-code.md +677 -0
- package/core/commands/review-context.md +1047 -0
- package/core/commands/review-tech-docs.md +811 -0
- package/core/commands/setup-ai-first.md +545 -0
- package/core/commands/sync.md +451 -0
- package/core/commands/update-framework.md +251 -0
- package/core/commands/validate-traces.md +842 -0
- package/core/hooks/data-guard.js +141 -0
- package/core/hooks/settings.json +18 -0
- package/core/modules/android-compose/module.yaml +13 -0
- package/core/modules/android-compose/stack-profile.yaml +57 -0
- package/core/modules/angular/architecture-snippets/component-patterns.md +187 -0
- package/core/modules/angular/module.yaml +6 -0
- package/core/modules/angular/stack-profile.yaml +38 -0
- package/core/modules/context-engineering/architecture-snippets/context-design.md +119 -0
- package/core/modules/context-engineering/module.yaml +9 -0
- package/core/modules/context-engineering/stack-profile.yaml +61 -0
- package/core/modules/dotnet/architecture-snippets/clean-arch.md +160 -0
- package/core/modules/dotnet/module.yaml +6 -0
- package/core/modules/dotnet/stack-profile.yaml +50 -0
- package/core/modules/flutter/module.yaml +14 -0
- package/core/modules/flutter/stack-profile.yaml +59 -0
- package/core/modules/golang/architecture-snippets/domain-layout.md +283 -0
- package/core/modules/golang/module.yaml +6 -0
- package/core/modules/golang/stack-profile.yaml +40 -0
- package/core/modules/ios-swiftui/module.yaml +13 -0
- package/core/modules/ios-swiftui/stack-profile.yaml +55 -0
- package/core/modules/java-spring/architecture-snippets/layered-arch.md +201 -0
- package/core/modules/java-spring/module.yaml +15 -0
- package/core/modules/java-spring/stack-profile.yaml +28 -0
- package/core/modules/nextjs/architecture-snippets/app-router-patterns.md +269 -0
- package/core/modules/nextjs/module.yaml +14 -0
- package/core/modules/nextjs/stack-profile.yaml +74 -0
- package/core/modules/nuxt/module.yaml +14 -0
- package/core/modules/nuxt/stack-profile.yaml +58 -0
- package/core/modules/php-laravel/architecture-snippets/service-repository.md +302 -0
- package/core/modules/php-laravel/module.yaml +15 -0
- package/core/modules/php-laravel/stack-profile.yaml +56 -0
- package/core/modules/qc-playwright/stack-profile.yaml +66 -0
- package/core/modules/react/architecture-snippets/hooks-query-patterns.md +254 -0
- package/core/modules/react/module.yaml +14 -0
- package/core/modules/react/stack-profile.yaml +63 -0
- package/core/modules/react-native/module.yaml +14 -0
- package/core/modules/react-native/stack-profile.yaml +56 -0
- package/core/modules/vue/module.yaml +14 -0
- package/core/modules/vue/stack-profile.yaml +65 -0
- package/core/rules/data-protection.md +80 -0
- package/core/rules/workflow.md +44 -0
- package/core/skills/code/SKILL.md +770 -0
- package/core/skills/debug/SKILL.md +869 -0
- package/core/skills/design-spec/SKILL.md +589 -0
- package/core/skills/discovery/SKILL.md +554 -0
- package/core/skills/prd/SKILL.md +562 -0
- package/core/skills/qc/qa-analyst/DOC_GAPS.template.md +63 -0
- package/core/skills/qc/qa-analyst/acceptance-criteria.md +60 -0
- package/core/skills/qc/qa-analyst/business-rules.md +59 -0
- package/core/skills/qc/qa-analyst/data-flow.md +64 -0
- package/core/skills/qc/qa-analyst/spec-breakdown.md +61 -0
- package/core/skills/qc/qa-designer/e2e/journey.md +41 -0
- package/core/skills/qc/qa-designer/exploratory/charter.md +68 -0
- package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +43 -0
- package/core/skills/qc/qa-designer/functional/api.md +45 -0
- package/core/skills/qc/qa-designer/functional/gui-feature.md +46 -0
- package/core/skills/qc/qa-designer/functional/gui-screen.md +52 -0
- package/core/skills/qc/qa-designer/integration/api.md +42 -0
- package/core/skills/qc/qa-designer/integration/db.md +39 -0
- package/core/skills/qc/qa-designer/integration/gui.md +40 -0
- package/core/skills/qc/qa-designer/integration/kafka.md +40 -0
- package/core/skills/qc/qa-designer/non-functional.md +40 -0
- package/core/skills/qc/qa-planner/test-plan.md +120 -0
- package/core/skills/qc/qa-reviewer/script/e2e.md +87 -0
- package/core/skills/qc/qa-reviewer/script/exploratory.md +45 -0
- package/core/skills/qc/qa-reviewer/script/functional.md +101 -0
- package/core/skills/qc/qa-reviewer/script/integration.md +91 -0
- package/core/skills/qc/qa-reviewer/script/non-functional.md +126 -0
- package/core/skills/qc/qa-reviewer/test-case/e2e.md +73 -0
- package/core/skills/qc/qa-reviewer/test-case/exploratory.md +43 -0
- package/core/skills/qc/qa-reviewer/test-case/functional.md +76 -0
- package/core/skills/qc/qa-reviewer/test-case/integration.md +69 -0
- package/core/skills/qc/qa-reviewer/test-case/non-functional.md +73 -0
- package/core/skills/qc/qa-runner/e2e.md +49 -0
- package/core/skills/qc/qa-runner/exploratory/session.md +36 -0
- package/core/skills/qc/qa-runner/functional/api.md +35 -0
- package/core/skills/qc/qa-runner/functional/gui-feature.md +51 -0
- package/core/skills/qc/qa-runner/functional/gui-screen.md +55 -0
- package/core/skills/qc/qa-runner/integration.md +47 -0
- package/core/skills/qc/qa-runner/non-functional.md +49 -0
- package/core/skills/qc/qa-runner/report/report.md +37 -0
- package/core/skills/setup-ai-first/SKILL.md +216 -0
- package/core/skills/spec/SKILL.md +461 -0
- package/core/skills/test/SKILL.md +1297 -0
- package/core/steps/capture-lesson.md +79 -0
- package/core/steps/context-loader.md +307 -0
- package/core/steps/gate.md +87 -0
- package/core/steps/report-footer.md +100 -0
- package/core/steps/review-fanout.md +138 -0
- package/core/steps/spawn-agent.md +124 -0
- package/core/steps/trace-mirror.md +26 -0
- package/core/templates/architecture.template.md +113 -0
- package/core/templates/design-spec.template.md +217 -0
- package/core/templates/feature.template +259 -0
- package/core/templates/platform-guide.template.md +145 -0
- package/core/templates/prd.template.md +327 -0
- package/core/templates/product-definition.template.md +168 -0
- package/core/templates/project-context.yaml +161 -0
- package/docs/01-getting-started/README.md +19 -0
- package/docs/01-getting-started/core-concepts.md +102 -0
- package/docs/01-getting-started/installation.md +156 -0
- package/docs/01-getting-started/quickstart.md +85 -0
- package/docs/02-guides/README.md +26 -0
- package/docs/02-guides/developer/README.md +46 -0
- package/docs/02-guides/developer/bdd-and-trace.md +125 -0
- package/docs/02-guides/developer/commands.md +76 -0
- package/docs/02-guides/developer/pr-checklist.md +15 -0
- package/docs/02-guides/developer/scenarios.md +460 -0
- package/docs/02-guides/developer/workflow.md +121 -0
- package/docs/02-guides/product-owner/README.md +79 -0
- package/docs/02-guides/product-owner/commands.md +30 -0
- package/docs/02-guides/product-owner/handoff-checklist.md +42 -0
- package/docs/02-guides/product-owner/prd-writing-rules.md +45 -0
- package/docs/02-guides/product-owner/scenarios.md +436 -0
- package/docs/02-guides/tester/README.md +75 -0
- package/docs/02-guides/tester/bug-reporting.md +117 -0
- package/docs/02-guides/tester/qc-automation.md +165 -0
- package/docs/02-guides/tester/reading-specs.md +79 -0
- package/docs/02-guides/tester/scenarios.md +186 -0
- package/docs/02-guides/tester/spec-manifest.md +130 -0
- package/docs/02-guides/tester/test-checklist.md +31 -0
- package/docs/02-guides/tester/workflow.md +77 -0
- package/docs/03-concepts/README.md +19 -0
- package/docs/03-concepts/architecture.md +248 -0
- package/docs/03-concepts/pipeline.md +274 -0
- package/docs/03-concepts/traceability.md +149 -0
- package/docs/04-operations/README.md +33 -0
- package/docs/04-operations/bug-flow.md +362 -0
- package/docs/04-operations/publishing.md +137 -0
- package/docs/04-operations/sync-and-update.md +522 -0
- package/docs/05-reference/README.md +32 -0
- package/docs/05-reference/command-cheatsheet.md +147 -0
- package/docs/05-reference/commands.md +232 -0
- package/docs/05-reference/modules.md +110 -0
- package/docs/05-reference/trace-schema.md +153 -0
- package/docs/README.md +49 -0
- package/hooks/data-guard.js +141 -0
- package/hooks/settings.json +18 -0
- package/modules/android-compose/module.yaml +13 -0
- package/modules/android-compose/stack-profile.yaml +57 -0
- package/modules/angular/architecture-snippets/component-patterns.md +187 -0
- package/modules/angular/module.yaml +6 -0
- package/modules/angular/stack-profile.yaml +38 -0
- package/modules/context-engineering/architecture-snippets/context-design.md +119 -0
- package/modules/context-engineering/module.yaml +9 -0
- package/modules/context-engineering/stack-profile.yaml +61 -0
- package/modules/dotnet/architecture-snippets/clean-arch.md +160 -0
- package/modules/dotnet/module.yaml +6 -0
- package/modules/dotnet/stack-profile.yaml +50 -0
- package/modules/flutter/module.yaml +14 -0
- package/modules/flutter/stack-profile.yaml +59 -0
- package/modules/golang/architecture-snippets/domain-layout.md +283 -0
- package/modules/golang/module.yaml +6 -0
- package/modules/golang/stack-profile.yaml +40 -0
- package/modules/ios-swiftui/module.yaml +13 -0
- package/modules/ios-swiftui/stack-profile.yaml +55 -0
- package/modules/java-spring/architecture-snippets/layered-arch.md +201 -0
- package/modules/java-spring/module.yaml +15 -0
- package/modules/java-spring/stack-profile.yaml +28 -0
- package/modules/nextjs/architecture-snippets/app-router-patterns.md +269 -0
- package/modules/nextjs/module.yaml +14 -0
- package/modules/nextjs/stack-profile.yaml +74 -0
- package/modules/nuxt/module.yaml +14 -0
- package/modules/nuxt/stack-profile.yaml +58 -0
- package/modules/php-laravel/architecture-snippets/service-repository.md +302 -0
- package/modules/php-laravel/module.yaml +15 -0
- package/modules/php-laravel/stack-profile.yaml +56 -0
- package/modules/qc-playwright/stack-profile.yaml +66 -0
- package/modules/react/architecture-snippets/hooks-query-patterns.md +254 -0
- package/modules/react/module.yaml +14 -0
- package/modules/react/stack-profile.yaml +63 -0
- package/modules/react-native/module.yaml +14 -0
- package/modules/react-native/stack-profile.yaml +56 -0
- package/modules/vue/module.yaml +14 -0
- package/modules/vue/stack-profile.yaml +65 -0
- package/package.json +49 -0
- package/rules/data-protection.md +80 -0
- package/rules/workflow.md +44 -0
- package/scripts/init.sh +49 -0
- package/scripts/migrate-specs.js +256 -0
- package/scripts/upgrade.sh +94 -0
- package/skills/code/SKILL.md +770 -0
- package/skills/code/SKILL.tmpl +176 -0
- package/skills/debug/SKILL.md +869 -0
- package/skills/debug/SKILL.tmpl +262 -0
- package/skills/design-spec/SKILL.md +589 -0
- package/skills/design-spec/SKILL.tmpl +95 -0
- package/skills/discovery/SKILL.md +554 -0
- package/skills/discovery/SKILL.tmpl +147 -0
- package/skills/prd/SKILL.md +562 -0
- package/skills/prd/SKILL.tmpl +188 -0
- package/skills/qc/qa-analyst/DOC_GAPS.template.md +63 -0
- package/skills/qc/qa-analyst/acceptance-criteria.md +60 -0
- package/skills/qc/qa-analyst/business-rules.md +59 -0
- package/skills/qc/qa-analyst/data-flow.md +64 -0
- package/skills/qc/qa-analyst/spec-breakdown.md +61 -0
- package/skills/qc/qa-designer/e2e/journey.md +41 -0
- package/skills/qc/qa-designer/exploratory/charter.md +68 -0
- package/skills/qc/qa-designer/exploratory/explore-to-functional.md +43 -0
- package/skills/qc/qa-designer/functional/api.md +45 -0
- package/skills/qc/qa-designer/functional/gui-feature.md +46 -0
- package/skills/qc/qa-designer/functional/gui-screen.md +52 -0
- package/skills/qc/qa-designer/integration/api.md +42 -0
- package/skills/qc/qa-designer/integration/db.md +39 -0
- package/skills/qc/qa-designer/integration/gui.md +40 -0
- package/skills/qc/qa-designer/integration/kafka.md +40 -0
- package/skills/qc/qa-designer/non-functional.md +40 -0
- package/skills/qc/qa-planner/test-plan.md +120 -0
- package/skills/qc/qa-reviewer/script/e2e.md +87 -0
- package/skills/qc/qa-reviewer/script/exploratory.md +45 -0
- package/skills/qc/qa-reviewer/script/functional.md +101 -0
- package/skills/qc/qa-reviewer/script/integration.md +91 -0
- package/skills/qc/qa-reviewer/script/non-functional.md +126 -0
- package/skills/qc/qa-reviewer/test-case/e2e.md +73 -0
- package/skills/qc/qa-reviewer/test-case/exploratory.md +43 -0
- package/skills/qc/qa-reviewer/test-case/functional.md +76 -0
- package/skills/qc/qa-reviewer/test-case/integration.md +69 -0
- package/skills/qc/qa-reviewer/test-case/non-functional.md +73 -0
- package/skills/qc/qa-runner/e2e.md +49 -0
- package/skills/qc/qa-runner/exploratory/session.md +36 -0
- package/skills/qc/qa-runner/functional/api.md +35 -0
- package/skills/qc/qa-runner/functional/gui-feature.md +51 -0
- package/skills/qc/qa-runner/functional/gui-screen.md +55 -0
- package/skills/qc/qa-runner/integration.md +47 -0
- package/skills/qc/qa-runner/non-functional.md +49 -0
- package/skills/qc/qa-runner/report/report.md +37 -0
- package/skills/setup-ai-first/SKILL.md +216 -0
- package/skills/setup-ai-first/SKILL.tmpl +116 -0
- package/skills/spec/SKILL.md +461 -0
- package/skills/spec/SKILL.tmpl +174 -0
- package/skills/test/SKILL.md +1297 -0
- package/skills/test/SKILL.tmpl +296 -0
- package/steps/capture-lesson.md +79 -0
- package/steps/context-loader.md +307 -0
- package/steps/gate.md +87 -0
- package/steps/report-footer.md +100 -0
- package/steps/review-fanout.md +138 -0
- package/steps/spawn-agent.md +124 -0
- package/steps/trace-mirror.md +26 -0
- package/templates/architecture.template.md +113 -0
- package/templates/design-spec.template.md +217 -0
- package/templates/feature.template +259 -0
- package/templates/platform-guide.template.md +145 -0
- package/templates/prd.template.md +327 -0
- package/templates/product-definition.template.md +168 -0
- package/templates/project-context.yaml +161 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* migrate-specs.js — migrate a consumer project's specs from the LEGACY
|
|
5
|
+
* artifact-type-first layout to the FEATURE-PACKAGE layout.
|
|
6
|
+
*
|
|
7
|
+
* OLD NEW
|
|
8
|
+
* specs/prd/{domain}/{slug}.md specs/{domain}/{slug}/prd.md
|
|
9
|
+
* specs/bdd/{domain}/{rest} specs/{domain}/{prd-slug}/bdd/{rest}
|
|
10
|
+
* specs/tech-docs/{domain}/{file} specs/{domain}/{prd-slug}/tech-docs/{file}
|
|
11
|
+
* specs/design-spec/{domain}/{file} specs/{domain}/{prd-slug}/design-spec/{file}
|
|
12
|
+
* .trace/{UC-ID}.tsv .trace/{domain}/{prd-slug}/{UC-ID}.tsv
|
|
13
|
+
*
|
|
14
|
+
* The {prd-slug} for a BDD / tech-doc / design-spec / trace file is resolved by
|
|
15
|
+
* reading its `@trace.source` header (the PRD it derives from) or its `@trace.uc`
|
|
16
|
+
* tag (mapped to a PRD via the .feature index). Files that cannot be resolved are
|
|
17
|
+
* LEFT IN PLACE and listed in the report — never moved to a guessed location.
|
|
18
|
+
*
|
|
19
|
+
* DRY-RUN by default (prints the plan, changes nothing). Pass --apply to execute.
|
|
20
|
+
* Tracked files are moved with `git mv` to preserve history; the rest with fs.rename.
|
|
21
|
+
* After moving, internal `@trace.source` / path references inside the moved files
|
|
22
|
+
* are rewritten to the new locations.
|
|
23
|
+
*
|
|
24
|
+
* Usage (from the consumer project root):
|
|
25
|
+
* node scripts/migrate-specs.js # dry-run, prints plan
|
|
26
|
+
* node scripts/migrate-specs.js --apply # execute the migration
|
|
27
|
+
* node scripts/migrate-specs.js --specs specs --trace .trace --root .
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const fs = require('fs');
|
|
31
|
+
const path = require('path');
|
|
32
|
+
const { execSync } = require('child_process');
|
|
33
|
+
|
|
34
|
+
// ── args ────────────────────────────────────────────────────────────────────
|
|
35
|
+
const argv = process.argv.slice(2);
|
|
36
|
+
const has = f => argv.includes(f);
|
|
37
|
+
const flag = (f, d) => { const i = argv.indexOf(f); return i !== -1 ? argv[i + 1] : d; };
|
|
38
|
+
|
|
39
|
+
const APPLY = has('--apply');
|
|
40
|
+
const ROOT = path.resolve(flag('--root', '.'));
|
|
41
|
+
const SPECS = flag('--specs', 'specs');
|
|
42
|
+
const TRACE = flag('--trace', '.trace');
|
|
43
|
+
|
|
44
|
+
const specsAbs = path.join(ROOT, SPECS);
|
|
45
|
+
const traceAbs = path.join(ROOT, TRACE);
|
|
46
|
+
|
|
47
|
+
// ── git tracked set (for `git mv`) ────────────────────────────────────────────
|
|
48
|
+
let tracked = null;
|
|
49
|
+
try {
|
|
50
|
+
const out = execSync('git ls-files', { cwd: ROOT, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
51
|
+
tracked = new Set(out.split('\n').filter(Boolean).map(p => p.replace(/\\/g, '/')));
|
|
52
|
+
} catch { tracked = null; }
|
|
53
|
+
|
|
54
|
+
const rel = abs => path.relative(ROOT, abs).replace(/\\/g, '/');
|
|
55
|
+
const isTracked = abs => !!(tracked && tracked.has(rel(abs)));
|
|
56
|
+
|
|
57
|
+
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
58
|
+
function walk(dir) {
|
|
59
|
+
const out = [];
|
|
60
|
+
if (!fs.existsSync(dir)) return out;
|
|
61
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
62
|
+
const p = path.join(dir, e.name);
|
|
63
|
+
if (e.isDirectory()) out.push(...walk(p));
|
|
64
|
+
else out.push(p);
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
const head = (file, n = 4096) => { try { return fs.readFileSync(file, 'utf8').slice(0, n); } catch { return ''; } };
|
|
69
|
+
|
|
70
|
+
const reSource = /@trace\.source\s*[=:]\s*([^\s)'"]+)/;
|
|
71
|
+
const reUc = /@trace\.(?:uc|id)\s*[=:]\s*([A-Za-z0-9_-]+)/;
|
|
72
|
+
const reUcName = /\b([A-Za-z][A-Za-z0-9]*-UC\d+)\b/; // e.g. PAY-UC1, FT-UC3
|
|
73
|
+
// extract {domain}/{slug} from a legacy PRD ref like specs/prd/payment/create-invoice.md
|
|
74
|
+
const slugFromPrdRef = ref => { const m = ref && ref.match(/prd\/([^/]+)\/([^/]+)\.md/); return m ? { domain: m[1], slug: m[2] } : null; };
|
|
75
|
+
|
|
76
|
+
// ── plan state ─────────────────────────────────────────────────────────────────
|
|
77
|
+
const moves = []; // { from(abs), to(abs), type }
|
|
78
|
+
const unresolved = []; // { file(rel), type, reason }
|
|
79
|
+
const prdByDomain = {}; // domain -> Set(slug)
|
|
80
|
+
const ucToSlug = {}; // `${domain}/${UC}` -> slug
|
|
81
|
+
const ticketToSlug = {}; // `${domain}/${TICKET}` -> slug (from PRD metadata)
|
|
82
|
+
|
|
83
|
+
const addSlug = (domain, slug) => { (prdByDomain[domain] = prdByDomain[domain] || new Set()).add(slug); };
|
|
84
|
+
const onlySlug = domain => { const s = prdByDomain[domain]; return s && s.size === 1 ? [...s][0] : null; };
|
|
85
|
+
|
|
86
|
+
// ── Phase 1 — PRDs: specs/prd/{domain}/{slug}.md → specs/{domain}/{slug}/prd.md ──
|
|
87
|
+
const prdRoot = path.join(specsAbs, 'prd');
|
|
88
|
+
for (const file of walk(prdRoot).filter(f => f.endsWith('.md'))) {
|
|
89
|
+
const parts = rel(file).split('/'); // [SPECS, 'prd', domain, ...rest, name.md]
|
|
90
|
+
const after = parts.slice(parts.indexOf('prd') + 1); // [domain, ...rest, name.md]
|
|
91
|
+
if (after.length !== 2) { unresolved.push({ file: rel(file), type: 'prd', reason: `expected specs/prd/{domain}/{slug}.md (got depth ${after.length})` }); continue; }
|
|
92
|
+
const [domain, name] = after;
|
|
93
|
+
const slug = name.replace(/\.md$/, '');
|
|
94
|
+
addSlug(domain, slug);
|
|
95
|
+
// map this PRD's Ticket + UC ids → slug (fallback resolution for other artifacts)
|
|
96
|
+
const body = head(file, 16384);
|
|
97
|
+
const ticket = (body.match(/\|\s*\*\*(?:PRD ID|Ticket)\*\*\s*\|\s*([A-Za-z0-9_-]+)/) || [])[1];
|
|
98
|
+
if (ticket) ticketToSlug[`${domain}/${ticket}`] = slug;
|
|
99
|
+
let m; const reUcG = new RegExp(reUcName.source, 'g');
|
|
100
|
+
while ((m = reUcG.exec(body))) ucToSlug[`${domain}/${m[1]}`] = ucToSlug[`${domain}/${m[1]}`] || slug;
|
|
101
|
+
moves.push({ from: file, to: path.join(specsAbs, domain, slug, 'prd.md'), type: 'prd' });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ── Phase 2 — BDD: specs/bdd/{domain}/{rest} → specs/{domain}/{prd-slug}/bdd/{rest}
|
|
105
|
+
const bddRoot = path.join(specsAbs, 'bdd');
|
|
106
|
+
for (const file of walk(bddRoot).filter(f => f.endsWith('.feature'))) {
|
|
107
|
+
const after = rel(file).split('/').slice(rel(file).split('/').indexOf('bdd') + 1); // [domain, ...rest]
|
|
108
|
+
const domain = after[0];
|
|
109
|
+
const rest = after.slice(1).join('/'); // e.g. system/PAY-UC1.feature OR PAY-UC1.feature
|
|
110
|
+
const h = head(file);
|
|
111
|
+
let resolved = slugFromPrdRef((h.match(reSource) || [])[1]);
|
|
112
|
+
const uc = (h.match(reUc) || [])[1] || (path.basename(file).match(reUcName) || [])[1];
|
|
113
|
+
let slug = resolved && resolved.domain === domain ? resolved.slug : null;
|
|
114
|
+
if (!slug && uc) slug = ucToSlug[`${domain}/${uc}`];
|
|
115
|
+
if (!slug) slug = onlySlug(domain);
|
|
116
|
+
if (!slug) { unresolved.push({ file: rel(file), type: 'bdd', reason: 'cannot resolve prd-slug (no @trace.source / @trace.uc match, and domain has multiple PRDs)' }); continue; }
|
|
117
|
+
if (uc) ucToSlug[`${domain}/${uc}`] = ucToSlug[`${domain}/${uc}`] || slug;
|
|
118
|
+
moves.push({ from: file, to: path.join(specsAbs, domain, slug, 'bdd', ...rest.split('/')), type: 'bdd' });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Phase 3 — tech-docs → specs/{domain}/{prd-slug}/tech-docs/{file} ────────────
|
|
122
|
+
const techRoot = path.join(specsAbs, 'tech-docs');
|
|
123
|
+
for (const file of walk(techRoot).filter(f => f.endsWith('.md'))) {
|
|
124
|
+
const after = rel(file).split('/').slice(rel(file).split('/').indexOf('tech-docs') + 1);
|
|
125
|
+
const domain = after[0];
|
|
126
|
+
const rest = after.slice(1).join('/');
|
|
127
|
+
const h = head(file);
|
|
128
|
+
const src = (h.match(reSource) || [])[1];
|
|
129
|
+
let slug = (slugFromPrdRef(src) || {}).slug; // when @trace.source points at the PRD
|
|
130
|
+
const uc = (h.match(reUc) || [])[1] || (path.basename(file).match(reUcName) || [])[1];
|
|
131
|
+
if (!slug && uc) slug = ucToSlug[`${domain}/${uc}`];
|
|
132
|
+
if (!slug && src) { const m = src.match(/bdd\/(?:.*\/)?([A-Za-z][A-Za-z0-9]*-UC\d+)/); if (m) slug = ucToSlug[`${domain}/${m[1]}`]; }
|
|
133
|
+
if (!slug) slug = onlySlug(domain);
|
|
134
|
+
if (!slug) { unresolved.push({ file: rel(file), type: 'tech-docs', reason: 'cannot resolve prd-slug from @trace.source / UC-ID' }); continue; }
|
|
135
|
+
moves.push({ from: file, to: path.join(specsAbs, domain, slug, 'tech-docs', ...rest.split('/')), type: 'tech-docs' });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── Phase 4 — design-spec → specs/{domain}/{prd-slug}/design-spec/{file} ────────
|
|
139
|
+
const dsRoot = path.join(specsAbs, 'design-spec');
|
|
140
|
+
for (const file of walk(dsRoot).filter(f => f.endsWith('.md'))) {
|
|
141
|
+
const after = rel(file).split('/').slice(rel(file).split('/').indexOf('design-spec') + 1);
|
|
142
|
+
const domain = after[0];
|
|
143
|
+
const rest = after.slice(1).join('/');
|
|
144
|
+
const h = head(file);
|
|
145
|
+
let slug = (slugFromPrdRef((h.match(reSource) || [])[1]) || {}).slug;
|
|
146
|
+
const ticket = (path.basename(file).match(/^([A-Za-z][A-Za-z0-9]*-\d+|[A-Za-z]+-UC\d+)/) || [])[1];
|
|
147
|
+
if (!slug && ticket) slug = ticketToSlug[`${domain}/${ticket}`] || ucToSlug[`${domain}/${ticket}`];
|
|
148
|
+
if (!slug) slug = onlySlug(domain);
|
|
149
|
+
if (!slug) { unresolved.push({ file: rel(file), type: 'design-spec', reason: 'cannot resolve prd-slug (no @trace.source / ticket match)' }); continue; }
|
|
150
|
+
moves.push({ from: file, to: path.join(specsAbs, domain, slug, 'design-spec', ...rest.split('/')), type: 'design-spec' });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── Phase 5 — trace: flat .trace/{UC-ID}.tsv → .trace/{domain}/{prd-slug}/{UC}.tsv
|
|
154
|
+
if (fs.existsSync(traceAbs)) {
|
|
155
|
+
for (const file of fs.readdirSync(traceAbs).filter(f => f.endsWith('.tsv'))) {
|
|
156
|
+
const uc = file.replace(/\.tsv$/, '');
|
|
157
|
+
const keys = Object.keys(ucToSlug).filter(k => k.endsWith(`/${uc}`));
|
|
158
|
+
if (keys.length === 0) { unresolved.push({ file: `${TRACE}/${file}`, type: 'trace', reason: `no PRD/feature maps UC ${uc}` }); continue; }
|
|
159
|
+
if (keys.length > 1) { unresolved.push({ file: `${TRACE}/${file}`, type: 'trace', reason: `UC ${uc} is ambiguous across domains: ${keys.join(', ')}` }); continue; }
|
|
160
|
+
const [domain] = keys[0].split('/');
|
|
161
|
+
const slug = ucToSlug[keys[0]];
|
|
162
|
+
moves.push({ from: path.join(traceAbs, file), to: path.join(traceAbs, domain, slug, file), type: 'trace' });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── build old→new ref map (repo-root-relative, forward slashes) ────────────────
|
|
167
|
+
const refMap = new Map();
|
|
168
|
+
for (const m of moves) refMap.set(rel(m.from), rel(m.to));
|
|
169
|
+
|
|
170
|
+
// ── report ──────────────────────────────────────────────────────────────────────
|
|
171
|
+
const byType = moves.reduce((a, m) => ((a[m.type] = (a[m.type] || 0) + 1), a), {});
|
|
172
|
+
console.log('');
|
|
173
|
+
console.log('╔════════════════════════════════════════════╗');
|
|
174
|
+
console.log(`║ migrate-specs — ${APPLY ? 'APPLY' : 'DRY RUN'}${' '.repeat(APPLY ? 20 : 18)}║`);
|
|
175
|
+
console.log('╚════════════════════════════════════════════╝');
|
|
176
|
+
console.log(`Root : ${ROOT}`);
|
|
177
|
+
console.log(`Specs : ${SPECS}/ Trace: ${TRACE}/ Git: ${tracked ? 'yes (git mv)' : 'no (fs move)'}`);
|
|
178
|
+
console.log('');
|
|
179
|
+
if (moves.length === 0 && unresolved.length === 0) {
|
|
180
|
+
console.log('Nothing to migrate — no legacy specs/prd, specs/bdd, specs/tech-docs, specs/design-spec, or flat .trace/*.tsv found.');
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
console.log(`Planned moves: ${moves.length} (${Object.entries(byType).map(([k, v]) => `${k}:${v}`).join(' ')})`);
|
|
184
|
+
console.log('');
|
|
185
|
+
for (const m of moves) console.log(` ${rel(m.from)}\n → ${rel(m.to)}`);
|
|
186
|
+
if (unresolved.length) {
|
|
187
|
+
console.log('');
|
|
188
|
+
console.log(`⚠️ Unresolved (LEFT IN PLACE — fix the file's @trace.source/@trace.uc or move manually):`);
|
|
189
|
+
for (const u of unresolved) console.log(` [${u.type}] ${u.file}\n ${u.reason}`);
|
|
190
|
+
}
|
|
191
|
+
console.log('');
|
|
192
|
+
|
|
193
|
+
// ── apply ────────────────────────────────────────────────────────────────────────
|
|
194
|
+
function gitMv(from, to) {
|
|
195
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
196
|
+
execSync(`git mv -k "${rel(from)}" "${rel(to)}"`, { cwd: ROOT, stdio: ['ignore', 'ignore', 'pipe'] });
|
|
197
|
+
}
|
|
198
|
+
function fsMv(from, to) {
|
|
199
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
200
|
+
fs.renameSync(from, to);
|
|
201
|
+
}
|
|
202
|
+
function rewriteRefs(absFile) {
|
|
203
|
+
if (!/\.(md|feature|tsv|ya?ml)$/.test(absFile)) return 0;
|
|
204
|
+
let txt; try { txt = fs.readFileSync(absFile, 'utf8'); } catch { return 0; }
|
|
205
|
+
let n = 0, out = txt;
|
|
206
|
+
for (const [oldRef, newRef] of refMap) {
|
|
207
|
+
if (out.includes(oldRef)) { out = out.split(oldRef).join(newRef); n++; }
|
|
208
|
+
}
|
|
209
|
+
if (n) fs.writeFileSync(absFile, out, 'utf8');
|
|
210
|
+
return n;
|
|
211
|
+
}
|
|
212
|
+
function pruneEmptyDirs(root) {
|
|
213
|
+
if (!fs.existsSync(root)) return;
|
|
214
|
+
for (const e of fs.readdirSync(root, { withFileTypes: true })) {
|
|
215
|
+
if (e.isDirectory()) pruneEmptyDirs(path.join(root, e.name));
|
|
216
|
+
}
|
|
217
|
+
try { if (fs.readdirSync(root).length === 0) fs.rmdirSync(root); } catch {}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (!APPLY) {
|
|
221
|
+
console.log('DRY RUN — nothing changed. Re-run with --apply to execute.');
|
|
222
|
+
console.log('After applying, grep your CODE for stale spec refs the move did not touch:');
|
|
223
|
+
console.log(' @trace.source=specs/bdd/... and specs/{prd,bdd,tech-docs,design-spec}/');
|
|
224
|
+
process.exit(0);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
let moved = 0, failed = 0;
|
|
228
|
+
for (const m of moves) {
|
|
229
|
+
try {
|
|
230
|
+
if (fs.existsSync(m.to)) throw new Error('target already exists');
|
|
231
|
+
(isTracked(m.from) ? gitMv : fsMv)(m.from, m.to);
|
|
232
|
+
moved++;
|
|
233
|
+
} catch (e) {
|
|
234
|
+
failed++;
|
|
235
|
+
console.log(` ❌ ${rel(m.from)} — ${e.message}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// rewrite internal references in every moved file (now at its new path)
|
|
239
|
+
let rewritten = 0;
|
|
240
|
+
for (const m of moves) if (fs.existsSync(m.to)) rewritten += rewriteRefs(m.to) > 0 ? 1 : 0;
|
|
241
|
+
|
|
242
|
+
// remove now-empty legacy roots
|
|
243
|
+
[prdRoot, bddRoot, techRoot, dsRoot].forEach(pruneEmptyDirs);
|
|
244
|
+
|
|
245
|
+
console.log('');
|
|
246
|
+
console.log(`✅ Moved ${moved}/${moves.length} files${failed ? ` (${failed} failed)` : ''}.`);
|
|
247
|
+
console.log(`✅ Rewrote internal references in ${rewritten} file(s).`);
|
|
248
|
+
if (unresolved.length) console.log(`⚠️ ${unresolved.length} file(s) left in place (see list above).`);
|
|
249
|
+
console.log('');
|
|
250
|
+
console.log('Next:');
|
|
251
|
+
console.log(' 1. Review the moves (git status / git diff).');
|
|
252
|
+
console.log(' 2. Grep CODE for stale refs the move did not touch:');
|
|
253
|
+
console.log(' @trace.source=specs/bdd/... and specs/{prd,bdd,tech-docs,design-spec}/');
|
|
254
|
+
console.log(' 3. Run /validate-traces to confirm coverage still resolves.');
|
|
255
|
+
console.log(' 4. Commit.');
|
|
256
|
+
console.log('');
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# upgrade.sh — Upgrade Spec-Driven Docs framework in an existing project.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash scripts/upgrade.sh
|
|
6
|
+
# bash scripts/upgrade.sh --module java-spring # also upgrade a stack module
|
|
7
|
+
#
|
|
8
|
+
# What it does:
|
|
9
|
+
# 1. Reads current installed version from .agent/FRAMEWORK_VERSION
|
|
10
|
+
# 2. Checks npm registry for the latest published version
|
|
11
|
+
# 3. If newer: runs npx @edupia-tutor/spec-driven-docs@latest --init to update .agent/
|
|
12
|
+
# 4. Reports what changed so you can review before committing
|
|
13
|
+
#
|
|
14
|
+
# Requirements:
|
|
15
|
+
# - Project was set up with init.sh (or npx ... --init)
|
|
16
|
+
# - .agent/FRAMEWORK_VERSION exists
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
AGENT_DIR=".agent"
|
|
21
|
+
VERSION_FILE="${AGENT_DIR}/FRAMEWORK_VERSION"
|
|
22
|
+
|
|
23
|
+
echo ""
|
|
24
|
+
echo "╔══════════════════════════════════════════╗"
|
|
25
|
+
echo "║ Spec-Driven Docs — Upgrade ║"
|
|
26
|
+
echo "╚══════════════════════════════════════════╝"
|
|
27
|
+
echo ""
|
|
28
|
+
|
|
29
|
+
# ── Prerequisite checks ───────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
if ! command -v node &> /dev/null; then
|
|
32
|
+
echo "❌ Node.js is required. Install from https://nodejs.org"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
if ! command -v npm &> /dev/null; then
|
|
37
|
+
echo "❌ npm is required. Install Node.js from https://nodejs.org"
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
if [ ! -f "$VERSION_FILE" ]; then
|
|
42
|
+
echo "❌ .agent/FRAMEWORK_VERSION not found."
|
|
43
|
+
echo ""
|
|
44
|
+
echo " This project was not set up with --init."
|
|
45
|
+
echo " To set up the new structure:"
|
|
46
|
+
echo ""
|
|
47
|
+
echo " bash scripts/init.sh"
|
|
48
|
+
echo " or:"
|
|
49
|
+
echo " npx @edupia-tutor/spec-driven-docs --init"
|
|
50
|
+
echo ""
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# ── Version comparison ────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
CURRENT=$(cat "$VERSION_FILE" | tr -d '[:space:]')
|
|
57
|
+
echo "Checking npm registry ..."
|
|
58
|
+
|
|
59
|
+
LATEST=$(npm view @edupia-tutor/spec-driven-docs version 2>/dev/null || echo "unknown")
|
|
60
|
+
|
|
61
|
+
echo ""
|
|
62
|
+
echo " Installed : v${CURRENT}"
|
|
63
|
+
echo " Latest : v${LATEST}"
|
|
64
|
+
echo ""
|
|
65
|
+
|
|
66
|
+
if [ "$LATEST" = "unknown" ]; then
|
|
67
|
+
echo "⚠️ Could not reach npm registry. Check your internet connection."
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ "$CURRENT" = "$LATEST" ]; then
|
|
72
|
+
echo "✅ Already up to date (v${CURRENT}). Nothing to do."
|
|
73
|
+
echo ""
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ── Upgrade ───────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
echo "Upgrading v${CURRENT} → v${LATEST} ..."
|
|
80
|
+
echo ""
|
|
81
|
+
|
|
82
|
+
npx -y @edupia-tutor/spec-driven-docs@latest --init "$@"
|
|
83
|
+
|
|
84
|
+
# ── Post-upgrade guidance ─────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
echo ""
|
|
87
|
+
echo "✅ Upgraded to v${LATEST}!"
|
|
88
|
+
echo ""
|
|
89
|
+
echo "Review what changed in .agent/ before committing:"
|
|
90
|
+
echo ""
|
|
91
|
+
echo " git diff .agent/"
|
|
92
|
+
echo " git add .agent/"
|
|
93
|
+
echo " git commit -m 'chore: upgrade spec-driven-docs v${CURRENT} → v${LATEST}'"
|
|
94
|
+
echo ""
|