@planu/cli 5.7.4 → 5.7.6

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 (208) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +3 -3
  3. package/dist/.planu-build.json +1 -1
  4. package/dist/cli/commands/package-handoff.js +30 -5
  5. package/dist/config/compliance-profiles.json +36 -3
  6. package/dist/config/environment-schema.json +21 -0
  7. package/dist/config/hook-templates/planu-session-safeguard.sh +22 -15
  8. package/dist/config/official-sdd-tools.d.ts +1 -1
  9. package/dist/config/official-sdd-tools.js +11 -24
  10. package/dist/config/registries/hosts/codex.json +14 -30
  11. package/dist/config/registries/hosts/opencode.json +1 -6
  12. package/dist/config/registries/legacy-guidance.json +26 -0
  13. package/dist/config/server-instructions.js +0 -21
  14. package/dist/config/skill-templates/planu-context-assets.md +3 -6
  15. package/dist/config/skill-templates/planu-implement.md +2 -2
  16. package/dist/config/skill-templates/planu-multi-teammate-review.md +1 -1
  17. package/dist/config/skill-templates/planu-release.md +2 -7
  18. package/dist/config/skill-templates/planu-validate.md +3 -3
  19. package/dist/config/spec-templates/crud-rest-api/template.json +5 -1
  20. package/dist/config/spec-templates/file-upload-s3/template.json +5 -1
  21. package/dist/config/spec-templates/stripe-payments/template.json +5 -1
  22. package/dist/config/spec-templates/webhook-system/template.json +5 -1
  23. package/dist/config/subagent-templates/planu-challenger.md +4 -9
  24. package/dist/config/subagent-templates/planu-spec-implementer.md +2 -4
  25. package/dist/config/subagent-templates/planu-validator.md +5 -8
  26. package/dist/config/tool-groups.json +6 -144
  27. package/dist/engine/ambiguity-scorer.js +40 -1
  28. package/dist/engine/autopilot/bootstrap.js +1 -1
  29. package/dist/engine/code-impact-analyzer.js +2 -6
  30. package/dist/engine/context-orchestrator/index.js +2 -2
  31. package/dist/engine/convention-scanner/codebase-scanner.js +2 -2
  32. package/dist/engine/dogfooding/runtime-gap-detector.js +2 -2
  33. package/dist/engine/evidence-gates/artifact-reader.d.ts +1 -0
  34. package/dist/engine/evidence-gates/artifact-reader.js +1 -1
  35. package/dist/engine/evidence-gates/evidence-autofill.d.ts +13 -5
  36. package/dist/engine/evidence-gates/evidence-autofill.js +106 -43
  37. package/dist/engine/evidence-gates/lifecycle-gate.js +7 -4
  38. package/dist/engine/evidence-index/index-builder.js +21 -4
  39. package/dist/engine/execution/job-runtime.js +1 -1
  40. package/dist/engine/execution/package-handoff-job-executor.d.ts +22 -0
  41. package/dist/engine/execution/package-handoff-job-executor.js +490 -0
  42. package/dist/engine/execution/validate-job-executor.js +20 -8
  43. package/dist/engine/git/exec-git.d.ts +4 -0
  44. package/dist/engine/git/exec-git.js +14 -0
  45. package/dist/engine/handoff-artifacts/implementation-review-reader.js +15 -2
  46. package/dist/engine/handoff-artifacts/schemas.d.ts +2 -0
  47. package/dist/engine/handoff-artifacts/schemas.js +1 -0
  48. package/dist/engine/handoff-format.d.ts +36 -0
  49. package/dist/engine/handoff-format.js +314 -0
  50. package/dist/engine/handoff-packager.d.ts +5 -3
  51. package/dist/engine/handoff-packager.js +89 -32
  52. package/dist/engine/host-tool-filter.js +20 -49
  53. package/dist/engine/human-summary.js +14 -3
  54. package/dist/engine/implementation-contract/common.d.ts +8 -3
  55. package/dist/engine/implementation-contract/common.js +9 -6
  56. package/dist/engine/implementation-contract/evaluator.js +4 -6
  57. package/dist/engine/implementation-contract/renderer.js +18 -23
  58. package/dist/engine/project-health-checker.js +2 -2
  59. package/dist/engine/readiness-checker.js +27 -0
  60. package/dist/engine/reconcile/apply-changes.js +4 -0
  61. package/dist/engine/reconcile/propagate-mirrors.d.ts +3 -0
  62. package/dist/engine/reconcile/propagate-mirrors.js +311 -0
  63. package/dist/engine/self-healing/healer.js +12 -5
  64. package/dist/engine/self-healing/strategies/lint-fix.d.ts +1 -1
  65. package/dist/engine/self-healing/strategies/lint-fix.js +4 -1
  66. package/dist/engine/self-healing/strategies/test-fix.d.ts +4 -3
  67. package/dist/engine/self-healing/strategies/test-fix.js +32 -14
  68. package/dist/engine/self-healing/strategies/typescript-fix.d.ts +1 -1
  69. package/dist/engine/self-healing/strategies/typescript-fix.js +28 -10
  70. package/dist/engine/session/checkpoint-writer.js +8 -4
  71. package/dist/engine/session/session-tracker.d.ts +5 -16
  72. package/dist/engine/session/session-tracker.js +29 -26
  73. package/dist/engine/session-state/writer.js +3 -11
  74. package/dist/engine/skill-generator/workflow-skill-generator.d.ts +4 -2
  75. package/dist/engine/skill-generator/workflow-skill-generator.js +113 -38
  76. package/dist/engine/spec-format/bdd-parser.d.ts +1 -11
  77. package/dist/engine/spec-format/bdd-parser.js +40 -11
  78. package/dist/engine/spec-format/lean-spec-generator.js +30 -10
  79. package/dist/engine/spec-format/retired-scaffold.d.ts +3 -0
  80. package/dist/engine/spec-format/retired-scaffold.js +5 -0
  81. package/dist/engine/spec-format/technical-md-populator.d.ts +1 -0
  82. package/dist/engine/spec-format/technical-md-populator.js +27 -0
  83. package/dist/engine/spec-format/unified-spec-builder.d.ts +1 -0
  84. package/dist/engine/spec-format/unified-spec-builder.js +30 -20
  85. package/dist/engine/spec-grounding/contract.js +115 -1
  86. package/dist/engine/spec-migrator/fold-technical.js +60 -19
  87. package/dist/engine/spec-migrator/placeholder-technical.d.ts +2 -0
  88. package/dist/engine/spec-migrator/placeholder-technical.js +18 -0
  89. package/dist/engine/spec-quality/generic-output-gate.js +37 -3
  90. package/dist/engine/technical-enricher/index.js +4 -1
  91. package/dist/engine/type-safety-gate.js +3 -11
  92. package/dist/engine/universal-rules/catalog.js +0 -2
  93. package/dist/engine/universal-rules/rules/planu-workflow.js +1 -1
  94. package/dist/engine/validation/durable-validation.d.ts +3 -2
  95. package/dist/engine/validation/durable-validation.js +106 -40
  96. package/dist/engine/validation/validation-freshness.d.ts +3 -2
  97. package/dist/engine/validation/validation-freshness.js +22 -16
  98. package/dist/engine/validation/validation-worktree.js +34 -0
  99. package/dist/hosts/claude-code/ux/mcp-prompts.js +8 -8
  100. package/dist/index.js +3 -17
  101. package/dist/resources/process.js +38 -65
  102. package/dist/storage/session-state-store.js +2 -0
  103. package/dist/tools/challenge-spec/scenarios-utils.js +2 -2
  104. package/dist/tools/configure-checkpoint-policy.js +3 -3
  105. package/dist/tools/create-spec-helpers.js +2 -5
  106. package/dist/tools/create-spec.js +21 -4
  107. package/dist/tools/facilitate.js +3 -6
  108. package/dist/tools/git/branch-ops.js +16 -4
  109. package/dist/tools/github-release-handler.js +1 -20
  110. package/dist/tools/heal-spec-docs.js +1 -13
  111. package/dist/tools/init-project/agents-md-writer.js +6 -6
  112. package/dist/tools/init-project/claude-md-generator.d.ts +0 -1
  113. package/dist/tools/init-project/claude-md-generator.js +0 -35
  114. package/dist/tools/init-project/handler.js +24 -49
  115. package/dist/tools/init-project/legacy-guidance-scanner.d.ts +12 -0
  116. package/dist/tools/init-project/legacy-guidance-scanner.js +176 -0
  117. package/dist/tools/init-project/per-client-files-writer.js +5 -5
  118. package/dist/tools/init-project/planu-workflow-generator.js +2 -13
  119. package/dist/tools/init-project/portable-index-reconciler.d.ts +21 -1
  120. package/dist/tools/init-project/portable-index-reconciler.js +66 -0
  121. package/dist/tools/init-project/rules-generator.d.ts +0 -11
  122. package/dist/tools/init-project/rules-generator.js +4 -85
  123. package/dist/tools/init-project/scaffold-writer.d.ts +0 -1
  124. package/dist/tools/init-project/scaffold-writer.js +1 -12
  125. package/dist/tools/jobs/handlers.d.ts +0 -6
  126. package/dist/tools/jobs/handlers.js +13 -40
  127. package/dist/tools/list-specs.js +6 -21
  128. package/dist/tools/package-handoff.d.ts +3 -2
  129. package/dist/tools/package-handoff.js +153 -427
  130. package/dist/tools/reconcile-session-safeguard-hook.js +22 -15
  131. package/dist/tools/register-sdd-tools.js +0 -23
  132. package/dist/tools/register-spec-tools/core-spec-tools.js +18 -27
  133. package/dist/tools/schemas/index.d.ts +1 -1
  134. package/dist/tools/schemas/index.js +1 -1
  135. package/dist/tools/schemas/output-schemas.d.ts +1 -18
  136. package/dist/tools/schemas/output-schemas.js +1 -11
  137. package/dist/tools/schemas/package-handoff-output-schema.d.ts +40 -0
  138. package/dist/tools/schemas/package-handoff-output-schema.js +68 -0
  139. package/dist/tools/schemas/validate-output-schema.d.ts +1 -2
  140. package/dist/tools/schemas/validate-output-schema.js +3 -2
  141. package/dist/tools/session-checkpoint.js +2 -2
  142. package/dist/tools/skill-registry/index.d.ts +0 -1
  143. package/dist/tools/skill-registry/index.js +0 -1
  144. package/dist/tools/sync-spec-state-handler.d.ts +2 -2
  145. package/dist/tools/sync-spec-state-handler.js +23 -3
  146. package/dist/tools/tool-registry/core-tools.js +1 -140
  147. package/dist/tools/tool-registry/group-infra.js +2 -39
  148. package/dist/tools/tool-registry/group-quality-compliance.js +9 -1
  149. package/dist/tools/update-status/batch.js +17 -0
  150. package/dist/tools/update-status/dod-gates.js +14 -1
  151. package/dist/tools/update-status/done-receipt-verifier.d.ts +22 -0
  152. package/dist/tools/update-status/done-receipt-verifier.js +56 -26
  153. package/dist/tools/update-status/evidence-gate.js +10 -21
  154. package/dist/tools/update-status/file-sync.d.ts +1 -0
  155. package/dist/tools/update-status/file-sync.js +6 -0
  156. package/dist/tools/update-status/index.d.ts +6 -0
  157. package/dist/tools/update-status/index.js +39 -44
  158. package/dist/tools/update-status-actions.js +2 -7
  159. package/dist/tools/validate.js +34 -28
  160. package/dist/transports/oauth-validator.js +12 -1
  161. package/dist/transports/transport-factory.d.ts +2 -1
  162. package/dist/transports/transport-factory.js +19 -0
  163. package/dist/types/common/primitives.d.ts +2 -0
  164. package/dist/types/durable-job.d.ts +45 -0
  165. package/dist/types/durable-validation.d.ts +1 -1
  166. package/dist/types/evidence-autofill.d.ts +13 -1
  167. package/dist/types/handoff-artifacts.d.ts +2 -1
  168. package/dist/types/index.d.ts +1 -0
  169. package/dist/types/index.js +1 -0
  170. package/dist/types/legacy-guidance.d.ts +20 -0
  171. package/dist/types/legacy-guidance.js +3 -0
  172. package/dist/types/readiness.d.ts +8 -1
  173. package/dist/types/reconcile.d.ts +6 -0
  174. package/dist/types/skill-registry.d.ts +26 -1
  175. package/dist/types/spec/core.d.ts +5 -0
  176. package/dist/types/spec/inputs.d.ts +2 -2
  177. package/dist/types/spec-format.d.ts +11 -1
  178. package/dist/types/spec-grounding.d.ts +12 -0
  179. package/dist/types/ssr-migration.d.ts +1 -1
  180. package/dist/types/transport.d.ts +1 -0
  181. package/dist/types/validation-receipt.d.ts +27 -0
  182. package/package.json +3 -2
  183. package/planu-plugin.json +13 -26
  184. package/src/i18n/messages/en.json +1 -1
  185. package/src/i18n/messages/es.json +1 -1
  186. package/src/i18n/messages/pt.json +1 -1
  187. package/dist/engine/skill-generator/conventions-hasher.d.ts +0 -7
  188. package/dist/engine/skill-generator/conventions-hasher.js +0 -24
  189. package/dist/engine/universal-rules/rules/agent-teams.d.ts +0 -3
  190. package/dist/engine/universal-rules/rules/agent-teams.js +0 -63
  191. package/dist/tools/clarify-requirements/multiple-choice.d.ts +0 -30
  192. package/dist/tools/clarify-requirements/multiple-choice.js +0 -306
  193. package/dist/tools/clarify-requirements/questions-context.d.ts +0 -9
  194. package/dist/tools/clarify-requirements/questions-context.js +0 -74
  195. package/dist/tools/clarify-requirements/questions.d.ts +0 -16
  196. package/dist/tools/clarify-requirements/questions.js +0 -96
  197. package/dist/tools/clarify-requirements.d.ts +0 -4
  198. package/dist/tools/clarify-requirements.js +0 -314
  199. package/dist/tools/code-graph-handler.d.ts +0 -6
  200. package/dist/tools/code-graph-handler.js +0 -72
  201. package/dist/tools/create-rule.d.ts +0 -4
  202. package/dist/tools/create-rule.js +0 -107
  203. package/dist/tools/feedback-handler.d.ts +0 -7
  204. package/dist/tools/feedback-handler.js +0 -150
  205. package/dist/tools/semantic-search-handler.d.ts +0 -7
  206. package/dist/tools/semantic-search-handler.js +0 -71
  207. package/dist/tools/skill-registry/search.d.ts +0 -11
  208. package/dist/tools/skill-registry/search.js +0 -159
@@ -0,0 +1,176 @@
1
+ // tools/init-project/legacy-guidance-scanner.ts — SPEC-1716
2
+ // Neutralizes stale Planu-installed guidance that instructs agents to create
3
+ // legacy standalone technical.md/progress.md files. Fail-closed by tiers:
4
+ // Tier 1 — delimited Planu block markers: rewrite only the block content.
5
+ // Tier 2 — composite fingerprint (allowlisted legacy filename + managed-by
6
+ // header): replace the whole file, backing up the prior content.
7
+ // Tier 3 — contradictory phrase alone: warning only, zero mutation.
8
+ // Malformed markers (opening without closing) also fail closed: warning only.
9
+ import { readFile, readdir } from 'node:fs/promises';
10
+ import { join } from 'node:path';
11
+ import { atomicWriteFile } from '../../engine/safety/atomic-write-file.js';
12
+ import { resolveContainedProjectFile } from '../../engine/safety/contained-project-file.js';
13
+ import legacyGuidanceRegistry from '../../config/registries/legacy-guidance.json' with { type: 'json' };
14
+ const REGISTRY = legacyGuidanceRegistry;
15
+ async function resolveScanSurface(projectPath) {
16
+ const files = [...REGISTRY.hostSurfaceFiles];
17
+ for (const { dir, extension } of REGISTRY.hostSurfaceDirectories) {
18
+ let entries;
19
+ try {
20
+ entries = await readdir(join(projectPath, dir), { withFileTypes: true });
21
+ }
22
+ catch {
23
+ continue;
24
+ }
25
+ for (const entry of entries) {
26
+ if (entry.isFile() && entry.name.endsWith(extension)) {
27
+ files.push(`${dir}/${entry.name}`);
28
+ }
29
+ }
30
+ }
31
+ return files;
32
+ }
33
+ function containsContradictoryPhrase(text) {
34
+ return REGISTRY.contradictoryPhrases.find((phrase) => text.includes(phrase));
35
+ }
36
+ const OPEN_MARKER_RE = /<!--\s*(planu[\w:-]*)\s*-->/g;
37
+ /**
38
+ * Locate delimited `<!-- planu... --> ... <!-- /planu... -->` (or repeated-tag,
39
+ * as emitted by claude-md-generator.ts wrap()) blocks. Returns `null` when an
40
+ * opening marker has no matching close — the malformed-marker fail-closed case.
41
+ *
42
+ * Uses its own cursor (a fresh RegExp with `re.lastIndex` advanced past each
43
+ * close marker) instead of `matchAll` over the whole string — a repeated-tag
44
+ * close is itself a valid open-marker match, so scanning the string without
45
+ * skipping past it would misread that close as the next block's opener.
46
+ */
47
+ function findPlanuBlocks(content) {
48
+ const blocks = [];
49
+ const re = new RegExp(OPEN_MARKER_RE.source, 'g');
50
+ let match;
51
+ while ((match = re.exec(content)) !== null) {
52
+ const id = match[1];
53
+ if (id === undefined) {
54
+ continue;
55
+ }
56
+ const openEnd = match.index + match[0].length;
57
+ const closeTag = `<!-- /${id} -->`;
58
+ const repeatTag = match[0];
59
+ const closeTagStart = content.indexOf(closeTag, openEnd);
60
+ const repeatTagStart = content.indexOf(repeatTag, openEnd);
61
+ const closeStart = [closeTagStart, repeatTagStart]
62
+ .filter((idx) => idx !== -1)
63
+ .sort((a, b) => a - b)[0];
64
+ if (closeStart === undefined) {
65
+ return null;
66
+ }
67
+ const closeLength = closeStart === closeTagStart ? closeTag.length : repeatTag.length;
68
+ blocks.push({ id, openEnd, closeStart });
69
+ re.lastIndex = closeStart + closeLength;
70
+ }
71
+ return blocks;
72
+ }
73
+ async function scanTier1(absPath, relPath, content) {
74
+ const blocks = findPlanuBlocks(content);
75
+ if (blocks === null) {
76
+ return {
77
+ kind: 'warning',
78
+ file: relPath,
79
+ reason: 'malformed Planu block marker: an opening marker has no matching closing marker',
80
+ };
81
+ }
82
+ const dirty = blocks.some(({ openEnd, closeStart }) => containsContradictoryPhrase(content.slice(openEnd, closeStart)));
83
+ if (!dirty) {
84
+ return { kind: 'clean' };
85
+ }
86
+ let rewritten = '';
87
+ let cursor = 0;
88
+ for (const { openEnd, closeStart } of blocks) {
89
+ const body = content.slice(openEnd, closeStart);
90
+ rewritten += content.slice(cursor, openEnd);
91
+ rewritten += containsContradictoryPhrase(body) ? `\n${REGISTRY.replacementGuidance}\n` : body;
92
+ cursor = closeStart;
93
+ }
94
+ rewritten += content.slice(cursor);
95
+ await atomicWriteFile(absPath, rewritten);
96
+ return { kind: 'healed', file: relPath };
97
+ }
98
+ async function scanTier2Or3(absPath, relPath, content) {
99
+ const phrase = containsContradictoryPhrase(content);
100
+ if (!phrase) {
101
+ return { kind: 'clean' };
102
+ }
103
+ const strongFingerprint = REGISTRY.legacyFilenameAllowlist.includes(relPath) &&
104
+ REGISTRY.managedByMarkers.some((marker) => content.includes(marker));
105
+ if (!strongFingerprint) {
106
+ return {
107
+ kind: 'warning',
108
+ file: relPath,
109
+ reason: `contains contradictory legacy guidance phrase: "${phrase}"`,
110
+ };
111
+ }
112
+ await atomicWriteFile(`${absPath}.planu-backup`, content);
113
+ await atomicWriteFile(absPath, `${REGISTRY.replacementGuidance}\n`);
114
+ return { kind: 'healed', file: relPath };
115
+ }
116
+ async function scanFile(projectPath, relPath) {
117
+ const absPath = join(projectPath, relPath);
118
+ let content;
119
+ try {
120
+ content = await readFile(absPath, 'utf-8');
121
+ }
122
+ catch {
123
+ return { kind: 'clean' };
124
+ }
125
+ // Fail closed if the surface entry (or an ancestor directory, e.g. a
126
+ // symlinked .claude/rules) resolves outside the project root — never let a
127
+ // mutation follow a symlink off the repo.
128
+ try {
129
+ await resolveContainedProjectFile(projectPath, absPath);
130
+ }
131
+ catch {
132
+ return {
133
+ kind: 'warning',
134
+ file: relPath,
135
+ reason: 'path escapes the project directory (symlink containment check failed)',
136
+ };
137
+ }
138
+ const hasMarkers = OPEN_MARKER_RE.test(content);
139
+ OPEN_MARKER_RE.lastIndex = 0;
140
+ return hasMarkers
141
+ ? scanTier1(absPath, relPath, content)
142
+ : scanTier2Or3(absPath, relPath, content);
143
+ }
144
+ /**
145
+ * Scan the bounded set of Planu-installed host guidance surfaces for stale
146
+ * directives that contradict the current spec.md-only contract, and
147
+ * neutralize what can be neutralized fail-closed. Never touches files
148
+ * outside the inventoried scan surface (no repo-wide walk).
149
+ */
150
+ export async function scanLegacyGuidance(projectPath) {
151
+ const files = await resolveScanSurface(projectPath);
152
+ const healed = [];
153
+ const warnings = [];
154
+ for (const relPath of files) {
155
+ const outcome = await scanFile(projectPath, relPath);
156
+ if (outcome.kind === 'healed') {
157
+ healed.push(outcome.file);
158
+ }
159
+ else if (outcome.kind === 'warning') {
160
+ warnings.push({ file: outcome.file, reason: outcome.reason });
161
+ }
162
+ }
163
+ return { healed, warnings };
164
+ }
165
+ /** Records a legacy-guidance scan outcome onto an init_project autopilot summary. */
166
+ export function summarizeLegacyGuidanceScan(collector, scan) {
167
+ if (scan.healed.length > 0) {
168
+ collector.pushOk('legacy-guidance-healed', `Neutralized stale Planu guidance in: ${scan.healed.join(', ')}`);
169
+ }
170
+ if (scan.warnings.length > 0) {
171
+ collector.pushSkipped('legacy-guidance-warnings', `Left untouched (weak fingerprint, needs manual review): ${scan.warnings
172
+ .map((w) => w.file)
173
+ .join(', ')}`);
174
+ }
175
+ }
176
+ //# sourceMappingURL=legacy-guidance-scanner.js.map
@@ -18,13 +18,13 @@ alwaysApply: true
18
18
  \`\`\`
19
19
  facilitate("what to build") → create_spec → challenge_spec → check_readiness
20
20
  → update_status(review) → approve → update_status(approved) → implement
21
- → validate (ack) → watch_job (completed) → inspect job.result → update_status(done)
21
+ → validate (ack) → get_job (poll until completed) → inspect job.result → update_status(done)
22
22
  \`\`\`
23
23
 
24
24
  ## Durable Validation Gate
25
25
 
26
- 1. Call \`validate\` and capture \`operationId\`, \`projectId\`, \`workspaceId\`, and \`cursor\`
27
- 2. Call \`watch_job\` with the latest cursor until terminal
26
+ 1. Call \`validate\` and capture \`operationId\`, \`projectId\`, and \`workspaceId\`
27
+ 2. Poll \`get_job\` with those identifiers until terminal
28
28
  3. Require \`completed\` and inspect \`job.result.structuredContent\`; stop on \`failed\`, \`dead-letter\`, or \`cancelled\`
29
29
  4. Mark done only when the completed result reports \`ready: true\`, \`status: "passing"\`, and passing quality gates
30
30
 
@@ -54,7 +54,7 @@ Planu enforces Spec Driven Development — spec first, then implement, then vali
54
54
 
55
55
  - Starting a new feature: use \`facilitate()\` or \`create_spec()\`
56
56
  - Resuming work: use \`planu_status\` to see what's in progress
57
- - Finishing work: submit \`validate()\`, await \`watch_job()\`, inspect \`job.result.structuredContent\`, then use \`update_status(done)\`
57
+ - Finishing work: submit \`validate()\`, poll \`get_job()\`, inspect \`job.result.structuredContent\`, then use \`update_status(done)\`
58
58
 
59
59
  ## Mandatory Workflow
60
60
 
@@ -64,7 +64,7 @@ Planu enforces Spec Driven Development — spec first, then implement, then vali
64
64
  4. \`update_status(specId, "approved")\` — lock the spec
65
65
  5. Implement the code
66
66
  6. \`validate(specId)\` — submit validation and capture the durable job acknowledgement
67
- 7. \`watch_job(operationId, projectId, workspaceId, cursor)\` — repeat until terminal
67
+ 7. \`get_job(operationId, projectId, workspaceId)\` — poll and repeat until terminal
68
68
  8. Require \`completed\` and inspect \`job.result.structuredContent\`
69
69
  9. \`update_status(specId, "done")\` — mark complete only after a completed passing result
70
70
 
@@ -10,7 +10,7 @@ const PLANU_WORKFLOW_CONTENT = `<!-- planu-sdd-workflow -->
10
10
  ### Session Start
11
11
  1. Call \`planu_status\` — load active specs and git state
12
12
  2. If spec is \`implementing\` → resume it before starting new work
13
- 3. Call \`semantic_search\` before starting any new task
13
+ 3. Call \`list_specs\` before starting any new task to find related work
14
14
 
15
15
  ### For ANY task > 5 minutes
16
16
  NEVER write production code without an approved spec.
@@ -21,22 +21,11 @@ NEVER write production code without an approved spec.
21
21
  ### Session End
22
22
  Call \`session_checkpoint\` to save context for the next session.
23
23
 
24
- ### For Multiple Specs (3+ parallel work)
25
- When implementing multiple independent specs simultaneously, use Agent Teams:
26
- 1. Call \`plan_team_distribution\` with spec IDs → auto-assigns file ownership
27
- 2. Call \`generate_teammate_prompt\` for each teammate → isolated context per agent
28
- 3. Spawn N Claude Code agents with the generated prompts
29
- 4. Call \`validate_team_results\` after completion → verify merged code
30
-
31
- Use teams when: 3+ specs ready, estimated effort >8h, monorepo with clear module boundaries.
32
- Quick ref: parallel work → \`plan_team_distribution\` → teammates → \`validate_team_results\`
33
-
34
24
  ### Quick reference
35
25
  - New feature → \`facilitate("description")\`
36
26
  - Check progress → \`planu_status\`
37
- - Find related work → \`semantic_search("keywords")\`
27
+ - Find related work → \`list_specs\` and scan titles
38
28
  - List pending → \`list_specs\` with status filter
39
- - Parallel work → \`plan_team_distribution\`
40
29
  <!-- /planu-sdd-workflow -->`;
41
30
  /**
42
31
  * Generate the Planu SDD section for CLAUDE.md.
@@ -1,10 +1,30 @@
1
- import type { FilesystemImportDeps, FilesystemImportFailure, PortableIndexReconciliationResult, SkippedLegacyContract } from '../../types/index.js';
1
+ import type { WorkflowSkillGenerationResult } from '../../types/skill-registry.js';
2
+ import type { FilesystemImportDeps, FilesystemImportFailure, LegacyGuidanceScanResult, PortableIndexReconciliationResult, SkippedLegacyContract } from '../../types/index.js';
3
+ import type { ToolResult } from '../../types/common/primitives.js';
2
4
  /** Autopilot-first summary of fatal reconciliation failures: what failed, why, and the next action. */
3
5
  export declare function describeReconciliationFailures(failures: FilesystemImportFailure[]): string;
4
6
  /** Autopilot-first summary of a successful reconciliation, including any legacy skips. */
5
7
  export declare function describeReconciliationSuccess(repositoryFilesChanged: string[], skippedLegacy: SkippedLegacyContract[], strayRepoRemovals?: string[]): string;
6
8
  /** Autopilot-first message for a reconciliation outcome, success or fatal-failure. */
7
9
  export declare function describeReconciliationOutcome(reconciliation: PortableIndexReconciliationResult, repositoryFilesChanged: string[]): string;
10
+ /**
11
+ * SPEC-1716: Build the already-initialized `init_project` result, folding the
12
+ * legacy-guidance scan's healed paths into the reported repository file changes.
13
+ */
14
+ export declare function buildAlreadyInitializedResult(projectId: string, reconciliation: PortableIndexReconciliationResult, legacyMigratedPaths: readonly string[], refreshedAssets: readonly string[], legacyGuidanceScan: LegacyGuidanceScanResult, projectPath: string, skillSync?: WorkflowSkillGenerationResult | null): ToolResult;
15
+ /** The subset of `MigrationRunResult` (migration-runner.ts) needed to report an authorized migration. */
16
+ interface AuthorizedMigrationSummary {
17
+ criticalMigrationFailures: unknown[];
18
+ changedPaths?: string[];
19
+ migrationReportPath: string | null;
20
+ }
21
+ /**
22
+ * SPEC-1716: Build the authorized-migration `init_project` result, folding the
23
+ * legacy-guidance scan's healed paths into the reported repository file changes
24
+ * the same way the already-initialized fast path does.
25
+ */
26
+ export declare function buildAuthorizedMigrationResult(projectId: string, projectPath: string, authorizedMigrations: readonly string[], migrationResult: AuthorizedMigrationSummary, legacyGuidanceScan: LegacyGuidanceScanResult): ToolResult;
8
27
  /** Rebuild the mutable external index from the repository-owned portable contracts. */
9
28
  export declare function reconcilePortableSpecIndex(projectPath: string, projectId: string, deps?: FilesystemImportDeps): Promise<PortableIndexReconciliationResult>;
29
+ export {};
10
30
  //# sourceMappingURL=portable-index-reconciler.d.ts.map
@@ -156,6 +156,72 @@ export function describeReconciliationOutcome(reconciliation, repositoryFilesCha
156
156
  ? describeReconciliationSuccess(repositoryFilesChanged, reconciliation.skippedLegacy, reconciliation.strayRepoRemovals)
157
157
  : describeReconciliationFailures(reconciliation.failures);
158
158
  }
159
+ /**
160
+ * SPEC-1716: Build the already-initialized `init_project` result, folding the
161
+ * legacy-guidance scan's healed paths into the reported repository file changes.
162
+ */
163
+ export function buildAlreadyInitializedResult(projectId, reconciliation, legacyMigratedPaths, refreshedAssets, legacyGuidanceScan, projectPath, skillSync = null) {
164
+ const repositoryFilesChanged = [...refreshedAssets, ...legacyGuidanceScan.healed];
165
+ const baseText = describeReconciliationOutcome(reconciliation, [...refreshedAssets]);
166
+ const healedGuidanceSuffix = legacyGuidanceScan.healed.length === 0
167
+ ? ''
168
+ : ` Healed stale legacy guidance in: ${legacyGuidanceScan.healed.join(', ')}.`;
169
+ const skillSyncSuffix = skillSync === null
170
+ ? ''
171
+ : ` Refreshed ${String(skillSync.written.length)} workflow skill(s), left ${String(skillSync.userModified.length)} user-modified skill(s) untouched.`;
172
+ return {
173
+ content: [{ type: 'text', text: baseText + healedGuidanceSuffix + skillSyncSuffix }],
174
+ structuredContent: {
175
+ projectId,
176
+ ...(reconciliation.failures.length === 0 ? { projectPath } : {}),
177
+ isUpdate: true,
178
+ repositoryFilesChanged,
179
+ authorizedMigrations: [],
180
+ importedSpecIds: reconciliation.importedSpecIds,
181
+ failures: reconciliation.failures,
182
+ skippedLegacy: reconciliation.skippedLegacy,
183
+ strayRepoRemovals: reconciliation.strayRepoRemovals,
184
+ migratedLegacyPaths: legacyMigratedPaths,
185
+ legacyGuidanceHealed: legacyGuidanceScan.healed,
186
+ legacyGuidanceWarnings: legacyGuidanceScan.warnings,
187
+ skillsWritten: skillSync?.written.length ?? 0,
188
+ skillsUserModified: skillSync?.userModified.length ?? 0,
189
+ },
190
+ ...(reconciliation.failures.length > 0 ? { isError: true } : {}),
191
+ };
192
+ }
193
+ /**
194
+ * SPEC-1716: Build the authorized-migration `init_project` result, folding the
195
+ * legacy-guidance scan's healed paths into the reported repository file changes
196
+ * the same way the already-initialized fast path does.
197
+ */
198
+ export function buildAuthorizedMigrationResult(projectId, projectPath, authorizedMigrations, migrationResult, legacyGuidanceScan) {
199
+ const hasCriticalFailures = migrationResult.criticalMigrationFailures.length > 0;
200
+ const healedGuidanceSuffix = legacyGuidanceScan.healed.length === 0
201
+ ? ''
202
+ : ` Healed stale legacy guidance in: ${legacyGuidanceScan.healed.join(', ')}.`;
203
+ const baseText = hasCriticalFailures
204
+ ? `Authorized migration completed with ${String(migrationResult.criticalMigrationFailures.length)} blocking issue(s).`
205
+ : 'Authorized planu-spec-format-v1 migration completed.';
206
+ return {
207
+ content: [{ type: 'text', text: baseText + healedGuidanceSuffix }],
208
+ structuredContent: {
209
+ projectId,
210
+ projectPath,
211
+ isUpdate: true,
212
+ authorizedMigrations,
213
+ repositoryFilesChanged: [
214
+ ...(migrationResult.changedPaths ?? []),
215
+ ...legacyGuidanceScan.healed,
216
+ ],
217
+ migrationReportPath: migrationResult.migrationReportPath,
218
+ criticalMigrationFailures: migrationResult.criticalMigrationFailures,
219
+ legacyGuidanceHealed: legacyGuidanceScan.healed,
220
+ legacyGuidanceWarnings: legacyGuidanceScan.warnings,
221
+ },
222
+ ...(hasCriticalFailures ? { isError: true } : {}),
223
+ };
224
+ }
159
225
  /** Rebuild the mutable external index from the repository-owned portable contracts. */
160
226
  export async function reconcilePortableSpecIndex(projectPath, projectId, deps = {
161
227
  listSpecs: specStore.listSpecs,
@@ -1,14 +1,3 @@
1
- export declare function generateAgentTeamsRulesContent(): string;
2
- /**
3
- * Write .claude/rules/agent-teams.md to the target project.
4
- * Always overwrites to keep the content up to date.
5
- */
6
- export declare function generateAgentTeamsRules(projectPath: string): Promise<void>;
7
- /**
8
- * Write .claude/rules/agent-teams.md only if it does not exist yet.
9
- * @returns true if the file was created, false if it already existed.
10
- */
11
- export declare function generateAgentTeamsRulesIfMissing(projectPath: string): Promise<boolean>;
12
1
  /**
13
2
  * Write .claude/rules/planu-workflow.md to the target project.
14
3
  * Always overwrites to keep the content up to date.
@@ -18,12 +18,12 @@ Auto-generated by \`init_project\`. Do not edit manually.
18
18
  | implementing | work in progress |
19
19
  | done | implemented + validated |
20
20
 
21
- Flow: \`facilitate → create_spec → challenge_spec → check_readiness → approve → implement → validate (ack) → watch_job (completed) → inspect job.result → done\`
21
+ Flow: \`facilitate → create_spec → challenge_spec → check_readiness → approve → implement → validate (ack) → get_job (poll until completed) → inspect job.result → done\`
22
22
 
23
23
  ## Durable Validation Gate
24
24
 
25
- 1. Call \`validate\` and capture \`operationId\`, \`projectId\`, \`workspaceId\`, and \`cursor\` from the acknowledgement.
26
- 2. Call \`watch_job\` with those identifiers and the latest cursor. Repeat while state is non-terminal.
25
+ 1. Call \`validate\` and capture \`operationId\`, \`projectId\`, and \`workspaceId\` from the acknowledgement.
26
+ 2. Poll \`get_job\` with those identifiers. Repeat while state is non-terminal.
27
27
  3. Require state \`completed\`, then inspect \`job.result.structuredContent\`. Stop and report \`failed\`, \`dead-letter\`, or \`cancelled\`.
28
28
  4. Call \`update_status(done)\` only after the completed result reports \`ready: true\`, \`status: "passing"\`, and passing quality gates.
29
29
 
@@ -60,7 +60,7 @@ Any new feature, bug fix, refactor, integration, or task > 5 min.
60
60
  |------|------|
61
61
  | New feature | \`facilitate("what to build")\` |
62
62
  | Project status | \`planu_status\` |
63
- | Find related specs | \`semantic_search("keywords")\` |
63
+ | Find related specs | \`list_specs\` and scan titles |
64
64
  | List approved work | \`list_specs\` with \`status: approved\` |
65
65
  | Mark done | \`update_status\` → \`done\` |
66
66
  | Verify | \`validate\` |
@@ -102,87 +102,6 @@ Set \`CLAUDE_CODE_SUBAGENT_MODEL=haiku\` in \`.env\` for automatic haiku on all
102
102
  - Keep output < 500 tokens for read operations, < 200 tokens for write confirmations
103
103
  - After every tool call: explain what happened in simple terms
104
104
  `;
105
- const AGENT_TEAMS_RULES_PATH = '.claude/rules/agent-teams.md';
106
- export function generateAgentTeamsRulesContent() {
107
- return `# Agent Teams (MANDATORY when 3+ specs in parallel)
108
-
109
- This rule file is auto-generated by Planu \`init_project\`. Do not edit manually — re-run \`init_project\` to update.
110
-
111
- ## When to Use Agent Teams
112
-
113
- Use Agent Teams when ALL of the following apply:
114
- - 3+ independent specs are ready to implement simultaneously
115
- - Estimated total effort > 8h
116
- - Specs touch different modules/packages (clear file ownership boundaries)
117
- - Monorepo or multi-layer architecture with well-defined interfaces
118
-
119
- ## File Ownership (MANDATORY)
120
-
121
- **Two teammates MUST NEVER modify the same file.** This is non-negotiable.
122
-
123
- - \`plan_team_distribution\` auto-assigns exclusive file ownership per agent
124
- - Review the distribution before spawning — resolve any conflicts first
125
- - If two specs require the same shared file, implement them sequentially, not in parallel
126
-
127
- ## Workflow
128
-
129
- \`\`\`
130
- 1. plan_team_distribution # input: spec IDs → output: file ownership map
131
- 2. generate_teammate_prompt # input: teammate assignment → output: isolated prompt
132
- 3. spawn N agents # claude -p "<prompt>" for each teammate (run in parallel)
133
- 4. validate_team_results # input: projectPath → verifies merged code integrity
134
- \`\`\`
135
-
136
- ## Available Team Patterns
137
-
138
- - **implement-and-test** — one agent implements, another writes tests (zero shared files)
139
- - **full-stack** — frontend and backend agents with a contract-first API boundary
140
- - **research-then-build** — researcher gathers context first, builder implements after
141
- - **parallel-specs** — N agents implement N fully independent specs simultaneously
142
-
143
- ## Quick Reference
144
-
145
- | Goal | Tool |
146
- |------|------|
147
- | Plan team distribution | \`plan_team_distribution\` with spec IDs array |
148
- | Get teammate context | \`generate_teammate_prompt\` with assignment object |
149
- | Validate merged result | \`validate_team_results\` after all agents complete |
150
-
151
- ## Prohibited Patterns
152
-
153
- - NEVER spawn teammates without running \`plan_team_distribution\` first
154
- - NEVER assign the same file to two different teammates
155
- - NEVER skip \`validate_team_results\` after parallel implementation
156
- - NEVER use Agent Teams for 1-2 specs — sequential is faster and safer
157
- `;
158
- }
159
- /**
160
- * Write .claude/rules/agent-teams.md to the target project.
161
- * Always overwrites to keep the content up to date.
162
- */
163
- export async function generateAgentTeamsRules(projectPath) {
164
- const rulesPath = join(projectPath, AGENT_TEAMS_RULES_PATH);
165
- const rulesDir = join(projectPath, '.claude/rules');
166
- await mkdir(rulesDir, { recursive: true });
167
- const content = generateAgentTeamsRulesContent();
168
- assertEnglishOnlyArtifactText(content, 'rule');
169
- await writeFile(rulesPath, content, 'utf-8');
170
- }
171
- /**
172
- * Write .claude/rules/agent-teams.md only if it does not exist yet.
173
- * @returns true if the file was created, false if it already existed.
174
- */
175
- export async function generateAgentTeamsRulesIfMissing(projectPath) {
176
- const rulesPath = join(projectPath, AGENT_TEAMS_RULES_PATH);
177
- try {
178
- await access(rulesPath);
179
- return false; // already exists
180
- }
181
- catch {
182
- await generateAgentTeamsRules(projectPath);
183
- return true;
184
- }
185
- }
186
105
  /**
187
106
  * Write .claude/rules/planu-workflow.md to the target project.
188
107
  * Always overwrites to keep the content up to date.
@@ -22,7 +22,6 @@ export interface ScaffoldWriteResult {
22
22
  planuWorkflowInjected: boolean;
23
23
  planuHooksConfigured: boolean;
24
24
  planuRulesWritten: boolean;
25
- agentTeamsRulesWritten: boolean;
26
25
  modeRulesWritten: boolean;
27
26
  responseStyleRulesWritten: boolean;
28
27
  compactSkillWritten: boolean;
@@ -8,7 +8,7 @@ import { runGitSetup } from './git-setup.js';
8
8
  import { generateAndWriteRules } from './lifecycle-helpers.js';
9
9
  import { generatePlanuSection, injectPlanuSection } from './planu-workflow-generator.js';
10
10
  import { configurePlanuHooks } from './hooks-configurator.js';
11
- import { generateWorkflowRulesIfMissing, generateAgentTeamsRulesIfMissing, generateModeRulesIfMissing, generateResponseStyleRulesIfMissing, } from './rules-generator.js';
11
+ import { generateWorkflowRulesIfMissing, generateModeRulesIfMissing, generateResponseStyleRulesIfMissing, } from './rules-generator.js';
12
12
  import { generateCompactSkillIfMissing } from './skills-writer.js';
13
13
  import { generateFindSkillsIfMissing } from './find-skills-writer.js';
14
14
  import { injectPlanuAutoStage } from '../../engine/git-hook-injector.js';
@@ -120,16 +120,6 @@ export async function runScaffoldWriter(projectPath, projectId, knowledge, recom
120
120
  /* best-effort */
121
121
  }
122
122
  }
123
- // SPEC-291: Write .claude/rules/agent-teams.md if missing (best-effort)
124
- let agentTeamsRulesWritten = false;
125
- if (shouldWriteClaudeAssets) {
126
- try {
127
- agentTeamsRulesWritten = await generateAgentTeamsRulesIfMissing(projectPath);
128
- }
129
- catch {
130
- /* best-effort */
131
- }
132
- }
133
123
  // SPEC-494: Write .claude/rules/planu-modes.md if missing (best-effort)
134
124
  let modeRulesWritten = false;
135
125
  if (shouldWriteClaudeAssets) {
@@ -394,7 +384,6 @@ export async function runScaffoldWriter(projectPath, projectId, knowledge, recom
394
384
  planuWorkflowInjected,
395
385
  planuHooksConfigured,
396
386
  planuRulesWritten,
397
- agentTeamsRulesWritten,
398
387
  modeRulesWritten,
399
388
  responseStyleRulesWritten,
400
389
  compactSkillWritten,
@@ -4,12 +4,6 @@ export interface JobToolIdentityInput {
4
4
  projectId: string;
5
5
  workspaceId?: string;
6
6
  }
7
- export interface WatchJobToolInput extends JobToolIdentityInput {
8
- afterCursor?: string;
9
- timeoutMs?: number;
10
- }
11
7
  export declare function handleGetJob(input: JobToolIdentityInput): Promise<ToolResult>;
12
- export declare function handleWatchJob(input: WatchJobToolInput): Promise<ToolResult>;
13
- export declare function handleCancelJob(input: JobToolIdentityInput): Promise<ToolResult>;
14
8
  export declare function handleRestartJob(input: JobToolIdentityInput): Promise<ToolResult>;
15
9
  //# sourceMappingURL=handlers.d.ts.map
@@ -52,6 +52,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
52
52
  });
53
53
  import { DurableJobRuntime, publicJobCursor } from '../../engine/execution/job-runtime.js';
54
54
  import { scheduleValidateJobExecution } from '../../engine/execution/validate-job-executor.js';
55
+ import { schedulePackageHandoffJobExecution } from '../../engine/execution/package-handoff-job-executor.js';
55
56
  import { isValidationReceiptPendingIntent, parseValidationReceiptPendingCheckpoint, } from '../../types/durable-validation.js';
56
57
  import { redactText } from '../../security/redactor.js';
57
58
  import { getRuntimePolicy } from '../../engine/runtime-policy.js';
@@ -97,62 +98,34 @@ export function handleGetJob(input) {
97
98
  __disposeResources(env_1);
98
99
  }
99
100
  }
100
- export async function handleWatchJob(input) {
101
+ export function handleRestartJob(input) {
101
102
  const env_2 = { stack: [], error: void 0, hasError: false };
102
103
  try {
103
104
  const runtime = __addDisposableResource(env_2, new DurableJobRuntime(), false);
104
- const job = await runtime.watch(input);
105
- const observation = publicJob(runtime, job);
106
- return result({ schemaVersion: 1, job: observation, cursor: publicJobCursor(observation) });
107
- }
108
- catch (e_2) {
109
- env_2.error = e_2;
110
- env_2.hasError = true;
111
- }
112
- finally {
113
- __disposeResources(env_2);
114
- }
115
- }
116
- export function handleCancelJob(input) {
117
- const env_3 = { stack: [], error: void 0, hasError: false };
118
- try {
119
- const runtime = __addDisposableResource(env_3, new DurableJobRuntime(), false);
120
- const job = runtime.cancel(input);
121
- const observation = publicJob(runtime, job);
122
- return Promise.resolve(result({ schemaVersion: 1, job: observation, cursor: publicJobCursor(observation) }));
123
- }
124
- catch (e_3) {
125
- env_3.error = e_3;
126
- env_3.hasError = true;
127
- }
128
- finally {
129
- __disposeResources(env_3);
130
- }
131
- }
132
- export function handleRestartJob(input) {
133
- const env_4 = { stack: [], error: void 0, hasError: false };
134
- try {
135
- const runtime = __addDisposableResource(env_4, new DurableJobRuntime(), false);
136
105
  const current = runtime.get(input);
137
106
  const pending = parseValidationReceiptPendingCheckpoint(current.checkpoint, checkpointLimits());
138
107
  if (!pending && isValidationReceiptPendingIntent(current.checkpoint)) {
139
108
  throw new Error('[Planu] Invalid validation receipt checkpoint');
140
109
  }
141
110
  const job = pending ? runtime.restartReceiptPublication(input) : runtime.restart(input);
142
- if (job.payload &&
143
- typeof job.payload === 'object' &&
144
- job.payload.kind === 'validate') {
111
+ const kind = job.payload && typeof job.payload === 'object'
112
+ ? job.payload.kind
113
+ : undefined;
114
+ if (kind === 'validate') {
145
115
  scheduleValidateJobExecution(job.projectId, job.workspaceId);
146
116
  }
117
+ else if (kind === 'package-handoff') {
118
+ schedulePackageHandoffJobExecution(job.projectId, job.workspaceId);
119
+ }
147
120
  const observation = publicJob(runtime, job);
148
121
  return Promise.resolve(result({ schemaVersion: 1, job: observation, cursor: publicJobCursor(observation) }));
149
122
  }
150
- catch (e_4) {
151
- env_4.error = e_4;
152
- env_4.hasError = true;
123
+ catch (e_2) {
124
+ env_2.error = e_2;
125
+ env_2.hasError = true;
153
126
  }
154
127
  finally {
155
- __disposeResources(env_4);
128
+ __disposeResources(env_2);
156
129
  }
157
130
  }
158
131
  //# sourceMappingURL=handlers.js.map