@atlashub/smartstack-cli 3.7.0 → 3.9.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.
Files changed (133) hide show
  1. package/dist/index.js +365 -2
  2. package/dist/index.js.map +1 -1
  3. package/package.json +4 -2
  4. package/templates/agents/action.md +1 -0
  5. package/templates/agents/ba-writer.md +33 -0
  6. package/templates/agents/explore-codebase.md +1 -0
  7. package/templates/agents/explore-docs.md +1 -0
  8. package/templates/agents/fix-grammar.md +1 -0
  9. package/templates/agents/snipper.md +1 -0
  10. package/templates/skills/admin/SKILL.md +6 -0
  11. package/templates/skills/ai-prompt/SKILL.md +32 -136
  12. package/templates/skills/ai-prompt/steps/step-01-implementation.md +122 -0
  13. package/templates/skills/apex/SKILL.md +120 -0
  14. package/templates/skills/apex/_shared.md +86 -0
  15. package/templates/skills/apex/references/agent-teams-protocol.md +164 -0
  16. package/templates/skills/apex/references/smartstack-layers.md +173 -0
  17. package/templates/skills/apex/steps/step-00-init.md +156 -0
  18. package/templates/skills/apex/steps/step-01-analyze.md +169 -0
  19. package/templates/skills/apex/steps/step-02-plan.md +160 -0
  20. package/templates/skills/apex/steps/step-03-execute.md +166 -0
  21. package/templates/skills/apex/steps/step-04-validate.md +138 -0
  22. package/templates/skills/apex/steps/step-05-examine.md +124 -0
  23. package/templates/skills/apex/steps/step-06-resolve.md +105 -0
  24. package/templates/skills/apex/steps/step-07-tests.md +130 -0
  25. package/templates/skills/apex/steps/step-08-run-tests.md +115 -0
  26. package/templates/skills/application/SKILL.md +10 -0
  27. package/templates/skills/application/references/backend-controller-hierarchy.md +58 -0
  28. package/templates/skills/application/references/backend-entity-seeding.md +72 -0
  29. package/templates/skills/application/references/backend-verification.md +88 -0
  30. package/templates/skills/application/references/frontend-verification.md +111 -0
  31. package/templates/skills/application/references/nav-fallback-procedure.md +200 -0
  32. package/templates/skills/application/references/provider-template.md +134 -0
  33. package/templates/skills/application/references/test-frontend.md +73 -0
  34. package/templates/skills/application/references/test-prerequisites.md +72 -0
  35. package/templates/skills/application/steps/step-01-navigation.md +7 -198
  36. package/templates/skills/application/steps/step-03b-provider.md +4 -128
  37. package/templates/skills/application/steps/step-04-backend.md +20 -350
  38. package/templates/skills/application/steps/step-05-frontend.md +12 -101
  39. package/templates/skills/application/steps/step-07-tests.md +12 -132
  40. package/templates/skills/business-analyse/SKILL.md +11 -2
  41. package/templates/skills/business-analyse/html/ba-interactive.html +3214 -2246
  42. package/templates/skills/business-analyse/html/build-html.js +77 -0
  43. package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +130 -0
  44. package/templates/skills/business-analyse/html/src/scripts/02-navigation.js +22 -0
  45. package/templates/skills/business-analyse/html/src/scripts/03-render-cadrage.js +208 -0
  46. package/templates/skills/business-analyse/html/src/scripts/04-render-modules.js +211 -0
  47. package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +554 -0
  48. package/templates/skills/business-analyse/html/src/scripts/06-render-consolidation.js +110 -0
  49. package/templates/skills/business-analyse/html/src/scripts/07-render-handoff.js +90 -0
  50. package/templates/skills/business-analyse/html/src/scripts/08-editing.js +45 -0
  51. package/templates/skills/business-analyse/html/src/scripts/09-export.js +168 -0
  52. package/templates/skills/business-analyse/html/src/scripts/10-comments.js +171 -0
  53. package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +161 -0
  54. package/templates/skills/business-analyse/html/src/styles/01-variables.css +38 -0
  55. package/templates/skills/business-analyse/html/src/styles/02-layout.css +101 -0
  56. package/templates/skills/business-analyse/html/src/styles/03-navigation.css +62 -0
  57. package/templates/skills/business-analyse/html/src/styles/04-cards.css +196 -0
  58. package/templates/skills/business-analyse/html/src/styles/05-modules.css +325 -0
  59. package/templates/skills/business-analyse/html/src/styles/06-wireframes.css +230 -0
  60. package/templates/skills/business-analyse/html/src/styles/07-comments.css +184 -0
  61. package/templates/skills/business-analyse/html/src/styles/08-review-panel.css +241 -0
  62. package/templates/skills/business-analyse/html/src/template.html +623 -0
  63. package/templates/skills/business-analyse/references/cadrage-structure-cards.md +78 -0
  64. package/templates/skills/business-analyse/references/cadrage-vibe-coding.md +97 -0
  65. package/templates/skills/business-analyse/references/consolidation-structural-checks.md +92 -0
  66. package/templates/skills/business-analyse/references/deploy-data-build.md +121 -0
  67. package/templates/skills/business-analyse/references/deploy-modes.md +49 -0
  68. package/templates/skills/business-analyse/references/handoff-file-templates.md +119 -0
  69. package/templates/skills/business-analyse/references/handoff-mappings.md +81 -0
  70. package/templates/skills/business-analyse/references/html-data-mapping.md +10 -2
  71. package/templates/skills/business-analyse/references/init-schema-deployment.md +65 -0
  72. package/templates/skills/business-analyse/references/review-data-mapping.md +363 -0
  73. package/templates/skills/business-analyse/references/spec-auto-inference.md +57 -0
  74. package/templates/skills/business-analyse/references/ui-dashboard-spec.md +85 -0
  75. package/templates/skills/business-analyse/references/ui-resource-cards.md +110 -0
  76. package/templates/skills/business-analyse/references/validate-incremental-html.md +55 -0
  77. package/templates/skills/business-analyse/steps/step-00-init.md +35 -68
  78. package/templates/skills/business-analyse/steps/step-01-cadrage.md +5 -194
  79. package/templates/skills/business-analyse/steps/step-03a-data.md +6 -49
  80. package/templates/skills/business-analyse/steps/step-03b-ui.md +12 -178
  81. package/templates/skills/business-analyse/steps/step-03d-validate.md +3 -48
  82. package/templates/skills/business-analyse/steps/step-04-consolidation.md +9 -104
  83. package/templates/skills/business-analyse/steps/step-05a-handoff.md +25 -441
  84. package/templates/skills/business-analyse/steps/step-05b-deploy.md +19 -187
  85. package/templates/skills/business-analyse/steps/step-06-review.md +277 -0
  86. package/templates/skills/cc-agent/references/agent-behavior-patterns.md +95 -0
  87. package/templates/skills/cc-agent/steps/step-02-generate.md +5 -78
  88. package/templates/skills/check-version/SKILL.md +7 -0
  89. package/templates/skills/controller/references/controller-code-templates.md +159 -0
  90. package/templates/skills/controller/references/permission-sync-templates.md +152 -0
  91. package/templates/skills/controller/steps/step-03-generate.md +6 -158
  92. package/templates/skills/controller/steps/step-04-perms.md +5 -144
  93. package/templates/skills/debug/SKILL.md +7 -0
  94. package/templates/skills/explore/SKILL.md +6 -0
  95. package/templates/skills/feature-full/SKILL.md +39 -142
  96. package/templates/skills/feature-full/steps/step-01-implementation.md +120 -0
  97. package/templates/skills/gitflow/references/init-config-template.md +135 -0
  98. package/templates/skills/gitflow/references/init-name-normalization.md +103 -0
  99. package/templates/skills/gitflow/references/plan-template.md +69 -0
  100. package/templates/skills/gitflow/references/start-efcore-preflight.md +70 -0
  101. package/templates/skills/gitflow/references/start-local-config.md +110 -0
  102. package/templates/skills/gitflow/steps/step-init.md +18 -289
  103. package/templates/skills/gitflow/steps/step-plan.md +6 -63
  104. package/templates/skills/gitflow/steps/step-start.md +16 -126
  105. package/templates/skills/mcp/SKILL.md +9 -213
  106. package/templates/skills/mcp/steps/step-01-healthcheck.md +108 -0
  107. package/templates/skills/mcp/steps/step-02-tools.md +73 -0
  108. package/templates/skills/notification/SKILL.md +7 -0
  109. package/templates/skills/quick-search/SKILL.md +5 -0
  110. package/templates/skills/ralph-loop/SKILL.md +99 -381
  111. package/templates/skills/ralph-loop/references/category-rules.md +259 -0
  112. package/templates/skills/ralph-loop/references/compact-loop.md +182 -0
  113. package/templates/skills/ralph-loop/references/task-transform-legacy.md +259 -0
  114. package/templates/skills/ralph-loop/references/team-orchestration.md +189 -0
  115. package/templates/skills/ralph-loop/steps/step-00-init.md +111 -383
  116. package/templates/skills/ralph-loop/steps/step-01-task.md +79 -896
  117. package/templates/skills/ralph-loop/steps/step-02-execute.md +68 -680
  118. package/templates/skills/ralph-loop/steps/step-03-commit.md +47 -277
  119. package/templates/skills/ralph-loop/steps/step-04-check.md +124 -607
  120. package/templates/skills/ralph-loop/steps/step-05-report.md +68 -367
  121. package/templates/skills/refactor/SKILL.md +12 -176
  122. package/templates/skills/refactor/steps/step-01-discover.md +60 -0
  123. package/templates/skills/refactor/steps/step-02-execute.md +67 -0
  124. package/templates/skills/review-code/SKILL.md +19 -257
  125. package/templates/skills/review-code/steps/step-01-smartstack.md +96 -0
  126. package/templates/skills/review-code/steps/step-02-detailed-review.md +80 -0
  127. package/templates/skills/review-code/steps/step-03-react.md +44 -0
  128. package/templates/skills/ui-components/SKILL.md +7 -0
  129. package/templates/skills/utils/SKILL.md +6 -0
  130. package/templates/skills/validate/SKILL.md +6 -0
  131. package/templates/skills/validate-feature/SKILL.md +8 -0
  132. package/templates/skills/workflow/SKILL.md +40 -118
  133. package/templates/skills/workflow/steps/step-01-implementation.md +84 -0
package/dist/index.js CHANGED
@@ -126749,6 +126749,334 @@ mcpCommand.command("remove").description("Unregister MCP servers from CLI and VS
126749
126749
  var import_fs_extra9 = __toESM(require_lib());
126750
126750
  var import_path11 = require("path");
126751
126751
 
126752
+ // src/utils/task-generator.ts
126753
+ var LAYER_ORDER = [
126754
+ { key: "domain", category: "domain" },
126755
+ { key: "infrastructure", category: "infrastructure" },
126756
+ { key: "application", category: "application" },
126757
+ { key: "api", category: "api" },
126758
+ { key: "frontend", category: "frontend" },
126759
+ { key: "seedData", category: "infrastructure" },
126760
+ { key: "tests", category: "test" }
126761
+ ];
126762
+ function createTask(ctx, category, description, deps, acceptanceCriteria, filesCreated = [], meta) {
126763
+ const task = {
126764
+ id: ctx.nextId++,
126765
+ description,
126766
+ status: "pending",
126767
+ category,
126768
+ dependencies: [...new Set(deps)],
126769
+ acceptance_criteria: acceptanceCriteria,
126770
+ started_at: null,
126771
+ completed_at: null,
126772
+ iteration: null,
126773
+ commit_hash: null,
126774
+ files_changed: { created: filesCreated, modified: [] },
126775
+ validation: null,
126776
+ error: null,
126777
+ module: ctx.moduleCode,
126778
+ ...meta ?? {}
126779
+ };
126780
+ ctx.lastIdByCategory[category] = task.id;
126781
+ return task;
126782
+ }
126783
+ function generateFilesTasks(prd, ctx, frontendFiles, i18nFiles, testFiles) {
126784
+ const tasks = [];
126785
+ const filesToCreate = prd.implementation?.filesToCreate ?? {};
126786
+ const allFRs = prd.requirements?.functionalRequirements ?? [];
126787
+ const permMatrix = prd.architecture?.permissionMatrix;
126788
+ for (const layer of LAYER_ORDER) {
126789
+ const files = filesToCreate[layer.key] ?? [];
126790
+ if (layer.category === "frontend") {
126791
+ frontendFiles.push(...files);
126792
+ continue;
126793
+ }
126794
+ if (layer.category === "test") {
126795
+ testFiles.push(...files);
126796
+ continue;
126797
+ }
126798
+ for (const fileSpec of files) {
126799
+ if (fileSpec.path?.includes("i18n/") || fileSpec.path?.includes("/locales/")) {
126800
+ i18nFiles.push(fileSpec);
126801
+ continue;
126802
+ }
126803
+ const depIds = [];
126804
+ if (ctx.lastIdByCategory[layer.category]) depIds.push(ctx.lastIdByCategory[layer.category]);
126805
+ if (layer.category === "api" && ctx.lastIdByCategory["application"]) depIds.push(ctx.lastIdByCategory["application"]);
126806
+ const linkedFRs = (fileSpec.linkedFRs ?? []).map(
126807
+ (frId) => allFRs.find((f) => f.id === frId)?.statement ?? frId
126808
+ );
126809
+ let criteria = linkedFRs.length > 0 ? `Implements: ${linkedFRs.join("; ")}` : `File ${fileSpec.path} created and compiles`;
126810
+ if (layer.category === "api" && (permMatrix?.permissions?.length ?? 0) > 0) {
126811
+ criteria += "; MANDATORY: [RequirePermission] on every endpoint (NOT [Authorize])";
126812
+ }
126813
+ const description = fileSpec.description ? `[${layer.category}] ${fileSpec.description} \u2192 ${fileSpec.path}` : `[${layer.category}] Create ${fileSpec.type}: ${fileSpec.path}`;
126814
+ tasks.push(createTask(ctx, layer.category, description, depIds, criteria, [fileSpec.path]));
126815
+ }
126816
+ }
126817
+ return tasks;
126818
+ }
126819
+ function generateFrontendTask(prd, ctx, frontendFiles) {
126820
+ if (frontendFiles.length === 0) return [];
126821
+ const apiDepId = ctx.lastIdByCategory["api"] ?? ctx.lastIdByCategory["application"];
126822
+ const ctxName = capitalize(prd.project?.application ? "Business" : "Business");
126823
+ const app = prd.project?.application ?? ctx.moduleCode;
126824
+ const wireframes = [...new Set(frontendFiles.flatMap((f) => f.linkedWireframes ?? []))];
126825
+ return [createTask(
126826
+ ctx,
126827
+ "frontend",
126828
+ `[frontend] Generate COMPLETE frontend for module ${ctx.moduleCode} via MCP (${frontendFiles.length} files)`,
126829
+ apiDepId ? [apiDepId] : [],
126830
+ `Pages in src/pages/${ctxName}/${app}/${ctx.moduleCode}/; MCP scaffold_api_client + scaffold_routes; SmartTable for lists; CSS variables ONLY; 4-language i18n; npm run typecheck passes`,
126831
+ frontendFiles.map((f) => f.path),
126832
+ {
126833
+ _frontendMeta: {
126834
+ wireframes,
126835
+ filesToCreate: frontendFiles.map((f) => ({ path: f.path, type: f.type }))
126836
+ }
126837
+ }
126838
+ )];
126839
+ }
126840
+ function generateI18nTask(ctx, i18nFiles) {
126841
+ if (i18nFiles.length === 0) return [];
126842
+ const frontendDepId = ctx.lastIdByCategory["frontend"];
126843
+ return [createTask(
126844
+ ctx,
126845
+ "i18n",
126846
+ `[i18n] Generate i18n for module ${ctx.moduleCode} (4 languages)`,
126847
+ frontendDepId ? [frontendDepId] : [],
126848
+ "4 JSON files (fr, en, it, de) with identical keys",
126849
+ i18nFiles.map((f) => f.path)
126850
+ )];
126851
+ }
126852
+ function generateTestTasks(prd, ctx, testFiles) {
126853
+ const tasks = [];
126854
+ const allFRs = prd.requirements?.functionalRequirements ?? [];
126855
+ for (const fileSpec of testFiles) {
126856
+ const depIds = [];
126857
+ if (ctx.lastIdByCategory["test"]) depIds.push(ctx.lastIdByCategory["test"]);
126858
+ const migDepId = ctx.lastIdByCategory["migration"] ?? ctx.lastIdByCategory["api"] ?? ctx.lastIdByCategory["application"];
126859
+ if (migDepId) depIds.push(migDepId);
126860
+ const linkedFRs = (fileSpec.linkedFRs ?? []).map(
126861
+ (frId) => allFRs.find((f) => f.id === frId)?.statement ?? frId
126862
+ );
126863
+ const criteria = linkedFRs.length > 0 ? `Implements: ${linkedFRs.join("; ")}` : `File ${fileSpec.path} created and compiles`;
126864
+ const description = fileSpec.description ? `[test] ${fileSpec.description} \u2192 ${fileSpec.path}` : `[test] Create ${fileSpec.type}: ${fileSpec.path}`;
126865
+ tasks.push(createTask(ctx, "test", description, depIds, criteria, [fileSpec.path]));
126866
+ }
126867
+ return tasks;
126868
+ }
126869
+ function applyGuardrails(prd, ctx, tasks, frontendFiles, i18nFiles) {
126870
+ const injected = [];
126871
+ const filesToCreate = prd.implementation?.filesToCreate ?? {};
126872
+ const testFileSpecs = filesToCreate["tests"] ?? [];
126873
+ const entities = prd.architecture?.entities ?? [];
126874
+ const apiEndpoints = prd.architecture?.apiEndpoints ?? [];
126875
+ const sections = prd.architecture?.sections ?? [];
126876
+ const permMatrix = prd.architecture?.permissionMatrix;
126877
+ if (!tasks.some((t) => t.category === "infrastructure") && entities.length > 0) {
126878
+ injected.push(createTask(
126879
+ ctx,
126880
+ "infrastructure",
126881
+ `[infrastructure] Create EF Core configs and seed data for ${ctx.moduleCode} (${entities.length} entities)`,
126882
+ ctx.lastIdByCategory["domain"] ? [ctx.lastIdByCategory["domain"]] : [],
126883
+ `EF configs for ${entities.length} entities; DbSet in ExtensionsDbContext; DI registration`
126884
+ ));
126885
+ }
126886
+ if (!tasks.some((t) => t.category === "api") && apiEndpoints.length > 0) {
126887
+ const depId = ctx.lastIdByCategory["application"] ?? ctx.lastIdByCategory["infrastructure"];
126888
+ const permCriteria = (permMatrix?.permissions?.length ?? 0) > 0 ? "[RequirePermission] on every endpoint" : "Authorization";
126889
+ injected.push(createTask(
126890
+ ctx,
126891
+ "api",
126892
+ `[api] Create controllers for ${ctx.moduleCode} (${apiEndpoints.length} endpoints)`,
126893
+ depId ? [depId] : [],
126894
+ `${apiEndpoints.length} endpoints; ${permCriteria}; Swagger`
126895
+ ));
126896
+ }
126897
+ if (frontendFiles.length === 0 && !tasks.some((t) => t.category === "frontend") && (sections.length > 0 || apiEndpoints.length > 0)) {
126898
+ const ctxName = "Business";
126899
+ const app = prd.project?.application ?? ctx.moduleCode;
126900
+ const derivedFiles = sections.map((s) => ({
126901
+ path: `web/src/pages/${ctxName}/${app}/${ctx.moduleCode}/${s.code}.tsx`,
126902
+ type: "Page"
126903
+ }));
126904
+ if (apiEndpoints.length > 0) {
126905
+ derivedFiles.push({ path: `web/src/services/api/${ctx.moduleCode}Api.ts`, type: "ApiService" });
126906
+ }
126907
+ const depId = ctx.lastIdByCategory["api"] ?? ctx.lastIdByCategory["application"];
126908
+ injected.push(createTask(
126909
+ ctx,
126910
+ "frontend",
126911
+ `[frontend] Generate COMPLETE frontend for ${ctx.moduleCode} via MCP (${derivedFiles.length} files)`,
126912
+ depId ? [depId] : [],
126913
+ `Pages in src/pages/${ctxName}/${app}/${ctx.moduleCode}/; SmartTable for lists; CSS variables ONLY; 4-language i18n; npm run typecheck passes`,
126914
+ derivedFiles.map((f) => f.path),
126915
+ {
126916
+ _frontendMeta: {
126917
+ wireframes: sections.map((s) => s.code),
126918
+ filesToCreate: derivedFiles,
126919
+ source: "guardrail-derived"
126920
+ }
126921
+ }
126922
+ ));
126923
+ if (i18nFiles.length === 0 && !tasks.some((t) => t.category === "i18n")) {
126924
+ injected.push(createTask(
126925
+ ctx,
126926
+ "i18n",
126927
+ `[i18n] Generate i18n for ${ctx.moduleCode} (4 languages)`,
126928
+ [ctx.lastIdByCategory["frontend"]],
126929
+ "4 JSON files (fr, en, it, de) with identical keys"
126930
+ ));
126931
+ }
126932
+ }
126933
+ if (testFileSpecs.length === 0 && !tasks.some((t) => t.category === "test") && !injected.some((t) => t.category === "test") && entities.length > 0) {
126934
+ ctx.lastIdByCategory["_needsGuardrailTest"] = 1;
126935
+ }
126936
+ const coreSeedData = prd.seedData?.core;
126937
+ const hasSeedDataInTasks = [...tasks, ...injected].some(
126938
+ (t) => t.description?.includes("SeedData") || t.description?.includes("seed data")
126939
+ );
126940
+ if (coreSeedData && coreSeedData.length > 0 && !hasSeedDataInTasks) {
126941
+ const infraDepId = ctx.lastIdByCategory["infrastructure"] ?? ctx.lastIdByCategory["domain"];
126942
+ const navCount = coreSeedData.filter((s) => s.entity === "NavigationModule").reduce((n, s) => n + (s.count ?? 1), 0);
126943
+ const permCount = coreSeedData.filter((s) => s.entity === "Permission").reduce((n, s) => n + (s.count ?? 1), 0);
126944
+ const roleCount = coreSeedData.filter((s) => s.entity === "RolePermission").reduce((n, s) => n + (s.count ?? 1), 0);
126945
+ const navRoute = `business.${(prd.project?.application ?? "app").toLowerCase()}.${ctx.moduleCode.toLowerCase()}`;
126946
+ injected.push(createTask(
126947
+ ctx,
126948
+ "infrastructure",
126949
+ `[infrastructure] Create core seed data for ${ctx.moduleCode}: NavigationModuleSeedData, PermissionsSeedData, RolesSeedData`,
126950
+ infraDepId ? [infraDepId] : [],
126951
+ `NavigationModuleSeedData (${navCount} nav); PermissionsSeedData (${permCount} perms); RolesSeedData (${roleCount} roles); SeedConstants; dotnet build passes`,
126952
+ [],
126953
+ {
126954
+ _seedDataMeta: {
126955
+ source: "guardrail-derived",
126956
+ coreSeedData,
126957
+ navRoute,
126958
+ contextCode: "business",
126959
+ appCode: prd.project?.application,
126960
+ appLabels: null
126961
+ }
126962
+ }
126963
+ ));
126964
+ }
126965
+ const hasProvider = [...tasks, ...injected].some(
126966
+ (t) => t.description?.includes("IClientSeedDataProvider") || t.description?.includes("SeedDataProvider")
126967
+ );
126968
+ const seedDataFiles = filesToCreate["seedData"] ?? [];
126969
+ if (!hasProvider && (seedDataFiles.length > 0 || coreSeedData && coreSeedData.length > 0)) {
126970
+ const depId = ctx.lastIdByCategory["infrastructure"] ?? ctx.lastIdByCategory["domain"];
126971
+ const navRoute = `business.${(prd.project?.application ?? "app").toLowerCase()}.${ctx.moduleCode.toLowerCase()}`;
126972
+ injected.push(createTask(
126973
+ ctx,
126974
+ "infrastructure",
126975
+ "[infrastructure] Create IClientSeedDataProvider for core seed data injection",
126976
+ depId ? [depId] : [],
126977
+ "SeedNavigationAsync + SeedPermissionsAsync + SeedRolePermissionsAsync; DI registered; idempotent",
126978
+ [],
126979
+ {
126980
+ _providerMeta: {
126981
+ navRoute,
126982
+ contextCode: "business",
126983
+ appCode: prd.project?.application
126984
+ }
126985
+ }
126986
+ ));
126987
+ }
126988
+ const allTasks = [...tasks, ...injected];
126989
+ const hasEntities = entities.length > 0 || allTasks.some((t) => t.category === "domain");
126990
+ const hasMigration = allTasks.some((t) => t.description?.toLowerCase().includes("migration"));
126991
+ if (hasEntities && !hasMigration) {
126992
+ const depId = ctx.lastIdByCategory["infrastructure"];
126993
+ injected.push(createTask(
126994
+ ctx,
126995
+ "infrastructure",
126996
+ `[infrastructure] Create EF Core migration for ${ctx.moduleCode}`,
126997
+ depId ? [depId] : [],
126998
+ "MCP suggest_migration \u2192 name; dotnet ef migrations add; dotnet ef database update; dotnet build passes",
126999
+ [],
127000
+ {
127001
+ _migrationMeta: {
127002
+ entities: entities.map((e) => e.name),
127003
+ moduleCode: ctx.moduleCode
127004
+ }
127005
+ }
127006
+ ));
127007
+ ctx.lastIdByCategory["migration"] = ctx.nextId - 1;
127008
+ }
127009
+ if (ctx.lastIdByCategory["_needsGuardrailTest"]) {
127010
+ delete ctx.lastIdByCategory["_needsGuardrailTest"];
127011
+ const migDepId = ctx.lastIdByCategory["migration"] ?? ctx.lastIdByCategory["api"] ?? ctx.lastIdByCategory["application"];
127012
+ injected.push(createTask(
127013
+ ctx,
127014
+ "test",
127015
+ `[test] Create unit and integration tests for ${ctx.moduleCode}`,
127016
+ migDepId ? [migDepId] : [],
127017
+ "Domain + service + controller tests; dotnet test passes; coverage >= 80%"
127018
+ ));
127019
+ }
127020
+ return injected;
127021
+ }
127022
+ function generateValidationTask(ctx) {
127023
+ const allCategoryTails = Object.values(ctx.lastIdByCategory).filter((id) => id != null);
127024
+ return createTask(
127025
+ ctx,
127026
+ "validation",
127027
+ `[validation] Build, test, and MCP validate module ${ctx.moduleCode}`,
127028
+ allCategoryTails,
127029
+ "dotnet build succeeds; dotnet test passes; MCP validate_conventions clean"
127030
+ );
127031
+ }
127032
+ function generateTasks(prd) {
127033
+ const moduleCode = prd.project.module;
127034
+ const ctx = {
127035
+ moduleCode,
127036
+ lastIdByCategory: {},
127037
+ nextId: 1
127038
+ };
127039
+ const frontendFiles = [];
127040
+ const i18nFiles = [];
127041
+ const testFiles = [];
127042
+ const fileTasks = generateFilesTasks(prd, ctx, frontendFiles, i18nFiles, testFiles);
127043
+ const frontendTask = generateFrontendTask(prd, ctx, frontendFiles);
127044
+ const i18nTask = generateI18nTask(ctx, i18nFiles);
127045
+ const coreTasks = [...fileTasks, ...frontendTask, ...i18nTask];
127046
+ const guardrailTasks = applyGuardrails(prd, ctx, coreTasks, frontendFiles, i18nFiles);
127047
+ const testTasks = generateTestTasks(prd, ctx, testFiles);
127048
+ const allTasks = [...coreTasks, ...guardrailTasks, ...testTasks];
127049
+ const validationTask = generateValidationTask(ctx);
127050
+ return [...allTasks, validationTask];
127051
+ }
127052
+ function validateTaskIntegrity(tasks) {
127053
+ const errors = [];
127054
+ const taskIds = new Set(tasks.map((t) => t.id));
127055
+ for (const task of tasks) {
127056
+ for (const depId of task.dependencies) {
127057
+ if (!taskIds.has(depId)) {
127058
+ errors.push(`Task ${task.id}: dependency ${depId} does not exist`);
127059
+ }
127060
+ }
127061
+ for (const depId of task.dependencies) {
127062
+ if (depId >= task.id) {
127063
+ errors.push(`Task ${task.id}: forward dependency to ${depId}`);
127064
+ }
127065
+ }
127066
+ if (!task.description) errors.push(`Task ${task.id}: missing description`);
127067
+ if (!task.category) errors.push(`Task ${task.id}: missing category`);
127068
+ if (!task.acceptance_criteria) errors.push(`Task ${task.id}: missing acceptance_criteria`);
127069
+ if (!task.module) errors.push(`Task ${task.id}: missing module`);
127070
+ }
127071
+ if (tasks.length < 2) {
127072
+ errors.push(`Only ${tasks.length} task(s) generated (minimum 2 expected)`);
127073
+ }
127074
+ return errors;
127075
+ }
127076
+ function capitalize(s) {
127077
+ return s.charAt(0).toUpperCase() + s.slice(1);
127078
+ }
127079
+
126752
127080
  // src/utils/prd-extractor.ts
126753
127081
  function extractPrd(moduleFeature, featureJsonPath, namespace) {
126754
127082
  const { metadata, analysis, specification, handoff } = moduleFeature;
@@ -126938,6 +127266,26 @@ function validatePrdCompleteness(prd, feature) {
126938
127266
  }
126939
127267
  return warnings;
126940
127268
  }
127269
+ function extractUnifiedPrd(moduleFeature, featureJsonPath, namespace, cliVersion) {
127270
+ const prdJson = extractPrd(moduleFeature, featureJsonPath, namespace);
127271
+ const tasks = generateTasks(prdJson);
127272
+ return {
127273
+ $version: "3.0.0",
127274
+ source: prdJson.source,
127275
+ project: prdJson.project,
127276
+ requirements: prdJson.requirements,
127277
+ businessRules: prdJson.businessRules,
127278
+ architecture: prdJson.architecture,
127279
+ implementation: prdJson.implementation,
127280
+ seedData: prdJson.seedData,
127281
+ tasks,
127282
+ metadata: {
127283
+ generatedBy: "ss-derive-prd",
127284
+ cliVersion: cliVersion ?? "unknown",
127285
+ moduleCode: prdJson.project.module
127286
+ }
127287
+ };
127288
+ }
126941
127289
  function getPrdFileCounts(prd) {
126942
127290
  const ftc = prd.implementation.filesToCreate;
126943
127291
  return {
@@ -127028,7 +127376,14 @@ Processing module: ${source_default.bold(moduleName)}`));
127028
127376
  continue;
127029
127377
  }
127030
127378
  }
127379
+ let cliVersion = "unknown";
127380
+ try {
127381
+ const pkg2 = import_fs_extra9.default.readJsonSync((0, import_path11.join)(__dirname, "..", "..", "package.json"));
127382
+ cliVersion = pkg2.version ?? "unknown";
127383
+ } catch {
127384
+ }
127031
127385
  const relativePath = (0, import_path11.relative)(process.cwd(), entry.path);
127386
+ const unified = extractUnifiedPrd(featureJson, relativePath, namespace, cliVersion);
127032
127387
  const prd = extractPrd(featureJson, relativePath, namespace);
127033
127388
  let outputPath;
127034
127389
  if (options.output && !entry.isApplicationMode) {
@@ -127036,7 +127391,7 @@ Processing module: ${source_default.bold(moduleName)}`));
127036
127391
  } else {
127037
127392
  outputPath = (0, import_path11.join)(ralphDir, `prd-${moduleName}.json`);
127038
127393
  }
127039
- await import_fs_extra9.default.writeJson(outputPath, prd, { spaces: 2 });
127394
+ await import_fs_extra9.default.writeJson(outputPath, unified, { spaces: 2 });
127040
127395
  totalGenerated++;
127041
127396
  const completenessWarnings = validatePrdCompleteness(prd, featureJson);
127042
127397
  if (completenessWarnings.length > 0) {
@@ -127050,13 +127405,21 @@ Processing module: ${source_default.bold(moduleName)}`));
127050
127405
  continue;
127051
127406
  }
127052
127407
  }
127408
+ const taskErrors = validateTaskIntegrity(unified.tasks);
127409
+ if (taskErrors.length > 0) {
127410
+ console.log(source_default.yellow(` Task integrity warnings:`));
127411
+ for (const te of taskErrors) {
127412
+ console.log(source_default.yellow(` - ${te}`));
127413
+ }
127414
+ }
127053
127415
  const relOutput = (0, import_path11.relative)(process.cwd(), outputPath);
127054
- console.log(source_default.green(` Generated: ${relOutput}`));
127416
+ console.log(source_default.green(` Generated: ${relOutput} (unified v3)`));
127055
127417
  console.log(source_default.gray(` UCs: ${prd.requirements.useCases.length} | FRs: ${prd.requirements.functionalRequirements.length} | BRs: ${prd.businessRules.length}`));
127056
127418
  console.log(source_default.gray(` Endpoints: ${prd.architecture.apiEndpoints.length} | Sections: ${prd.architecture.sections.length}`));
127057
127419
  const counts = getPrdFileCounts(prd);
127058
127420
  const totalFiles = Object.values(counts).reduce((s, n) => s + n, 0);
127059
127421
  console.log(source_default.gray(` Files to create: ${totalFiles} (domain: ${counts.domain}, app: ${counts.application}, infra: ${counts.infrastructure}, api: ${counts.api}, frontend: ${counts.frontend}, seed: ${counts.seedData}, tests: ${counts.tests})`));
127422
+ console.log(source_default.gray(` Tasks: ${unified.tasks.length} (${unified.tasks.filter((t) => t.category === "validation").length} validation)`));
127060
127423
  console.log(source_default.gray(` BR mappings: ${prd.implementation.brToCodeMapping.length}`));
127061
127424
  }
127062
127425
  console.log();