5-phase-workflow 1.9.1 → 1.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -141,6 +141,7 @@ Claude Code exposes the workflow under the `/5:` namespace. Codex exposes the sa
141
141
  | `/5:quick-implement` or `$5-quick-implement` | Fast | Streamlined workflow for small tasks |
142
142
  | `/5:eject` or `$5-eject` | Utility | Permanently remove update infrastructure |
143
143
  | `/5:unlock` or `$5-unlock` | Utility | Remove planning guard lock |
144
+ | `/5:synchronize-agents` or `$5-synchronize-agents` | Utility | Sync user content between Claude Code and Codex runtimes |
144
145
 
145
146
  ## Configuration
146
147
 
@@ -278,7 +279,8 @@ After installation, your `.claude/` directory will contain:
278
279
  │ ├── quick-implement.md
279
280
  │ ├── configure.md
280
281
  │ ├── eject.md
281
- └── unlock.md
282
+ ├── unlock.md
283
+ │ └── synchronize-agents.md
282
284
  ├── skills/ # Atomic operations
283
285
  │ ├── build-project/
284
286
  │ ├── run-tests/
@@ -398,6 +400,26 @@ This permanently removes the update infrastructure:
398
400
 
399
401
  All other workflow files remain untouched. **This is irreversible.** To restore update functionality, reinstall with `npx 5-phase-workflow`.
400
402
 
403
+ ### Synchronizing Runtimes
404
+
405
+ If you have both Claude Code and Codex installed, user-generated content (project-specific skills, custom commands, rules) only exists in the runtime where it was created. To sync this content bidirectionally:
406
+
407
+ ```bash
408
+ # Claude Code
409
+ /5:synchronize-agents
410
+
411
+ # Codex
412
+ $5-synchronize-agents
413
+ ```
414
+
415
+ This will:
416
+ - Sync project-specific skills (e.g., `create-controller`, `run-tests`) between `.claude/skills/` and `.codex/skills/` with appropriate format conversion
417
+ - Convert custom Claude commands to Codex skills
418
+ - Append `.claude/rules/` content to `.codex/instructions.md`
419
+ - Sync Codex-only skills back to Claude
420
+
421
+ Workflow-managed files and shared data (`.5/`) are not affected — those are handled by the installer.
422
+
401
423
  ## Development
402
424
 
403
425
  ### Running Tests
package/bin/install.js CHANGED
@@ -847,6 +847,7 @@ function showCommandsHelp(isGlobal) {
847
847
  log.info(' $5-reconfigure - Refresh docs/skills (no Q&A)');
848
848
  log.info(' $5-eject - Eject from update mechanism');
849
849
  log.info(' $5-unlock - Remove planning guard lock');
850
+ log.info(' $5-synchronize-agents - Sync user content between runtimes');
850
851
  } else {
851
852
  log.info('Available commands:');
852
853
  log.info(' /5:plan-feature - Start feature planning (Phase 1)');
@@ -859,6 +860,7 @@ function showCommandsHelp(isGlobal) {
859
860
  log.info(' /5:reconfigure - Refresh docs/skills (no Q&A)');
860
861
  log.info(' /5:eject - Eject from update mechanism');
861
862
  log.info(' /5:unlock - Remove planning guard lock');
863
+ log.info(' /5:synchronize-agents - Sync user content between runtimes');
862
864
  }
863
865
  log.info('');
864
866
  log.info(`Config file: ${path.join(getDataPath(isGlobal), 'config.json')}`);
@@ -0,0 +1,639 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ // ANSI colors for terminal output
7
+ const colors = {
8
+ reset: '\x1b[0m',
9
+ bright: '\x1b[1m',
10
+ green: '\x1b[32m',
11
+ yellow: '\x1b[33m',
12
+ blue: '\x1b[34m',
13
+ red: '\x1b[31m',
14
+ dim: '\x1b[2m'
15
+ };
16
+
17
+ const log = {
18
+ info: (msg) => console.log(`${colors.blue}i${colors.reset} ${msg}`),
19
+ success: (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`),
20
+ warn: (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`),
21
+ error: (msg) => console.log(`${colors.red}✗${colors.reset} ${msg}`),
22
+ header: (msg) => console.log(`\n${colors.bright}${msg}${colors.reset}\n`),
23
+ dim: (msg) => console.log(` ${colors.dim}${msg}${colors.reset}`)
24
+ };
25
+
26
+ // ── Workflow-managed file lists (must match install.js) ────────────────────
27
+
28
+ const WORKFLOW_MANAGED_SKILLS = new Set([
29
+ 'configure-docs-index',
30
+ 'configure-project',
31
+ 'configure-skills',
32
+ 'generate-readme'
33
+ ]);
34
+
35
+ const WORKFLOW_MANAGED_AGENTS = new Set([
36
+ 'component-executor.md'
37
+ ]);
38
+
39
+ const RULES_SYNC_START = '<!-- 5-sync:rules-start -->';
40
+ const RULES_SYNC_END = '<!-- 5-sync:rules-end -->';
41
+ const AGENTS_SYNC_START = '<!-- 5-sync:agents-start -->';
42
+ const AGENTS_SYNC_END = '<!-- 5-sync:agents-end -->';
43
+
44
+ // ── Conversion functions (mirrors install.js logic) ────────────────────────
45
+
46
+ function extractFrontmatterAndBody(content) {
47
+ const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
48
+ if (!match) return { frontmatter: null, body: content };
49
+ return { frontmatter: match[1], body: match[2] };
50
+ }
51
+
52
+ function extractFrontmatterField(frontmatter, field) {
53
+ if (!frontmatter) return null;
54
+ const match = frontmatter.match(new RegExp(`^${field}:\\s*(.+)$`, 'm'));
55
+ return match ? match[1].trim() : null;
56
+ }
57
+
58
+ function claudeToCodexContent(content) {
59
+ return content
60
+ .replace(/\/5:([a-z0-9-]+)/g, (_, name) => `$5-${name}`)
61
+ .replace(/\.claude\//g, '.codex/');
62
+ }
63
+
64
+ function codexToClaudeContent(content) {
65
+ return content
66
+ .replace(/\$5-([a-z0-9-]+)/g, (_, name) => `/5:${name}`)
67
+ .replace(/\.codex\//g, '.claude/')
68
+ .replace(/<codex_skill_adapter>[\s\S]*?<\/codex_skill_adapter>\n*/g, '');
69
+ }
70
+
71
+ function getCodexSkillAdapterHeader(skillName) {
72
+ const invocation = `$${skillName}`;
73
+ return `<codex_skill_adapter>
74
+ ## Skill Invocation
75
+ - This skill is invoked by mentioning \`${invocation}\`.
76
+ - Treat all user text after \`${invocation}\` as the skill argument.
77
+
78
+ ## Tool Mapping (Claude Code → Codex)
79
+ This skill was authored for Claude Code. Map these tool references:
80
+
81
+ | Claude Code | Codex Equivalent |
82
+ |-------------|------------------|
83
+ | \`AskUserQuestion\` | Ask the user directly in conversation |
84
+ | \`Task(subagent_type="Explore")\` | Research the codebase yourself using available tools |
85
+ | \`Task(prompt="...")\` | \`spawn_agent(message="...")\` |
86
+ | \`Read\` | \`read_file\` |
87
+ | \`Write\` | \`write_file\` |
88
+ | \`Edit\` | \`patch\` |
89
+ | \`Bash\` | \`shell\` |
90
+ | \`Glob\` | \`glob\` / \`list_directory\` |
91
+ | \`Grep\` | \`grep\` / \`search\` |
92
+ | \`TaskCreate/TaskUpdate\` | Track progress internally |
93
+ | \`EnterPlanMode\` | Not available — use structured output instead |
94
+ </codex_skill_adapter>`;
95
+ }
96
+
97
+ function convertClaudeCommandToCodexSkill(content, skillName) {
98
+ const converted = claudeToCodexContent(content);
99
+ const { frontmatter, body } = extractFrontmatterAndBody(converted);
100
+
101
+ let description = `Custom command: ${skillName}`;
102
+ if (frontmatter) {
103
+ const maybeDesc = extractFrontmatterField(frontmatter, 'description');
104
+ if (maybeDesc) description = maybeDesc;
105
+ }
106
+
107
+ const shortDesc = description.length > 180 ? `${description.slice(0, 177)}...` : description;
108
+ const adapter = getCodexSkillAdapterHeader(skillName);
109
+
110
+ return `---
111
+ name: ${skillName}
112
+ description: ${description}
113
+ metadata:
114
+ short-description: ${shortDesc}
115
+ ---
116
+
117
+ ${adapter}
118
+
119
+ ${body.trimStart()}`;
120
+ }
121
+
122
+ function convertClaudeSkillToCodex(content) {
123
+ return claudeToCodexContent(content);
124
+ }
125
+
126
+ function convertCodexSkillToClaude(content) {
127
+ return codexToClaudeContent(content);
128
+ }
129
+
130
+ // ── File helpers ────────────────────────────────────────────────────────────
131
+
132
+ function copyDirContents(src, dest, transformMd) {
133
+ if (!fs.existsSync(dest)) {
134
+ fs.mkdirSync(dest, { recursive: true });
135
+ }
136
+ const entries = fs.readdirSync(src, { withFileTypes: true });
137
+ for (const entry of entries) {
138
+ const srcPath = path.join(src, entry.name);
139
+ const destPath = path.join(dest, entry.name);
140
+ if (entry.isDirectory()) {
141
+ copyDirContents(srcPath, destPath, transformMd);
142
+ } else if (transformMd && entry.name.endsWith('.md')) {
143
+ const content = fs.readFileSync(srcPath, 'utf8');
144
+ fs.writeFileSync(destPath, transformMd(content));
145
+ } else {
146
+ fs.copyFileSync(srcPath, destPath);
147
+ }
148
+ }
149
+ }
150
+
151
+ function dirContentsEqual(dirA, dirB, transformFn) {
152
+ if (!fs.existsSync(dirA) || !fs.existsSync(dirB)) return false;
153
+
154
+ const filesA = fs.readdirSync(dirA).sort();
155
+ const filesB = fs.readdirSync(dirB).sort();
156
+ if (filesA.length !== filesB.length) return false;
157
+
158
+ for (const file of filesA) {
159
+ if (!filesB.includes(file)) return false;
160
+ const pathA = path.join(dirA, file);
161
+ const pathB = path.join(dirB, file);
162
+ const statA = fs.statSync(pathA);
163
+ const statB = fs.statSync(pathB);
164
+ if (statA.isDirectory() !== statB.isDirectory()) return false;
165
+ if (statA.isDirectory()) {
166
+ if (!dirContentsEqual(pathA, pathB, transformFn)) return false;
167
+ } else {
168
+ let contentA = fs.readFileSync(pathA, 'utf8');
169
+ const contentB = fs.readFileSync(pathB, 'utf8');
170
+ if (file.endsWith('.md') && transformFn) {
171
+ contentA = transformFn(contentA);
172
+ }
173
+ if (contentA !== contentB) return false;
174
+ }
175
+ }
176
+ return true;
177
+ }
178
+
179
+ // ── Inventory ──────────────────────────────────────────────────────────────
180
+
181
+ function findProjectRoot() {
182
+ // Walk up from cwd to find .5/ or .claude/ or .codex/
183
+ let dir = process.cwd();
184
+ while (dir !== path.dirname(dir)) {
185
+ if (fs.existsSync(path.join(dir, '.5')) ||
186
+ fs.existsSync(path.join(dir, '.claude')) ||
187
+ fs.existsSync(path.join(dir, '.codex'))) {
188
+ return dir;
189
+ }
190
+ dir = path.dirname(dir);
191
+ }
192
+ return process.cwd();
193
+ }
194
+
195
+ function isClaudeInstalled(root) {
196
+ return fs.existsSync(path.join(root, '.claude', 'commands', '5', 'plan-feature.md'));
197
+ }
198
+
199
+ function isCodexInstalled(root) {
200
+ return fs.existsSync(path.join(root, '.codex', 'skills', '5-plan-feature', 'SKILL.md'));
201
+ }
202
+
203
+ function getClaudeUserSkills(root) {
204
+ const skillsDir = path.join(root, '.claude', 'skills');
205
+ if (!fs.existsSync(skillsDir)) return [];
206
+ return fs.readdirSync(skillsDir, { withFileTypes: true })
207
+ .filter(d => d.isDirectory())
208
+ .map(d => d.name)
209
+ .filter(name => !WORKFLOW_MANAGED_SKILLS.has(name));
210
+ }
211
+
212
+ function getCodexUserSkills(root) {
213
+ const skillsDir = path.join(root, '.codex', 'skills');
214
+ if (!fs.existsSync(skillsDir)) return [];
215
+ return fs.readdirSync(skillsDir, { withFileTypes: true })
216
+ .filter(d => d.isDirectory())
217
+ .map(d => d.name)
218
+ .filter(name => !name.startsWith('5-') && !WORKFLOW_MANAGED_SKILLS.has(name));
219
+ }
220
+
221
+ function getClaudeCustomCommands(root) {
222
+ const commandsDir = path.join(root, '.claude', 'commands');
223
+ if (!fs.existsSync(commandsDir)) return [];
224
+ const namespaces = fs.readdirSync(commandsDir, { withFileTypes: true })
225
+ .filter(d => d.isDirectory() && d.name !== '5')
226
+ .map(d => d.name);
227
+
228
+ const commands = [];
229
+ for (const ns of namespaces) {
230
+ const nsDir = path.join(commandsDir, ns);
231
+ const files = fs.readdirSync(nsDir).filter(f => f.endsWith('.md'));
232
+ for (const file of files) {
233
+ commands.push({ namespace: ns, name: file.replace('.md', ''), file });
234
+ }
235
+ }
236
+ return commands;
237
+ }
238
+
239
+ function getClaudeCustomAgents(root) {
240
+ const agentsDir = path.join(root, '.claude', 'agents');
241
+ if (!fs.existsSync(agentsDir)) return [];
242
+ return fs.readdirSync(agentsDir)
243
+ .filter(f => f.endsWith('.md') && !WORKFLOW_MANAGED_AGENTS.has(f));
244
+ }
245
+
246
+ function getClaudeRules(root) {
247
+ const rulesDir = path.join(root, '.claude', 'rules');
248
+ if (!fs.existsSync(rulesDir)) return [];
249
+ return fs.readdirSync(rulesDir).filter(f => f.endsWith('.md'));
250
+ }
251
+
252
+ // ── Sync action classification ─────────────────────────────────────────────
253
+
254
+ function classifySkillActions(root) {
255
+ const claudeSkills = getClaudeUserSkills(root);
256
+ const codexSkills = getCodexUserSkills(root);
257
+ const claudeSet = new Set(claudeSkills);
258
+ const codexSet = new Set(codexSkills);
259
+
260
+ const actions = [];
261
+
262
+ // Claude → Codex
263
+ for (const skill of claudeSkills) {
264
+ const claudeDir = path.join(root, '.claude', 'skills', skill);
265
+ const codexDir = path.join(root, '.codex', 'skills', skill);
266
+ if (!codexSet.has(skill)) {
267
+ actions.push({ type: 'new', direction: 'claude-to-codex', category: 'skill', name: skill });
268
+ } else if (!dirContentsEqual(claudeDir, codexDir, convertClaudeSkillToCodex)) {
269
+ actions.push({ type: 'updated', direction: 'claude-to-codex', category: 'skill', name: skill });
270
+ } else {
271
+ actions.push({ type: 'in-sync', direction: 'both', category: 'skill', name: skill });
272
+ }
273
+ }
274
+
275
+ // Codex → Claude
276
+ for (const skill of codexSkills) {
277
+ if (claudeSet.has(skill)) continue; // Already handled above
278
+ const claudeDir = path.join(root, '.claude', 'skills', skill);
279
+ if (!fs.existsSync(claudeDir)) {
280
+ actions.push({ type: 'new', direction: 'codex-to-claude', category: 'skill', name: skill });
281
+ }
282
+ }
283
+
284
+ return actions;
285
+ }
286
+
287
+ function classifyCommandActions(root) {
288
+ const commands = getClaudeCustomCommands(root);
289
+ const actions = [];
290
+
291
+ for (const cmd of commands) {
292
+ const skillName = `${cmd.namespace}-${cmd.name}`;
293
+ const codexSkillDir = path.join(root, '.codex', 'skills', skillName);
294
+ if (!fs.existsSync(codexSkillDir)) {
295
+ actions.push({ type: 'new', direction: 'claude-to-codex', category: 'command', name: `${cmd.namespace}/${cmd.name}`, skillName });
296
+ } else {
297
+ // Check if content changed
298
+ const claudeContent = fs.readFileSync(path.join(root, '.claude', 'commands', cmd.namespace, cmd.file), 'utf8');
299
+ const converted = convertClaudeCommandToCodexSkill(claudeContent, skillName);
300
+ const existing = fs.readFileSync(path.join(codexSkillDir, 'SKILL.md'), 'utf8');
301
+ if (converted !== existing) {
302
+ actions.push({ type: 'updated', direction: 'claude-to-codex', category: 'command', name: `${cmd.namespace}/${cmd.name}`, skillName });
303
+ } else {
304
+ actions.push({ type: 'in-sync', direction: 'both', category: 'command', name: `${cmd.namespace}/${cmd.name}` });
305
+ }
306
+ }
307
+ }
308
+
309
+ return actions;
310
+ }
311
+
312
+ function classifyRulesActions(root) {
313
+ const rules = getClaudeRules(root);
314
+ if (rules.length === 0) return [];
315
+
316
+ const instructionsPath = path.join(root, '.codex', 'instructions.md');
317
+ if (!fs.existsSync(instructionsPath)) {
318
+ return [{ type: 'new', direction: 'claude-to-codex', category: 'rules', name: `${rules.length} rule file(s)` }];
319
+ }
320
+
321
+ const instructions = fs.readFileSync(instructionsPath, 'utf8');
322
+ const newSection = buildRulesSection(root, rules);
323
+
324
+ // Extract existing section
325
+ const startIdx = instructions.indexOf(RULES_SYNC_START);
326
+ const endIdx = instructions.indexOf(RULES_SYNC_END);
327
+ if (startIdx === -1 || endIdx === -1) {
328
+ return [{ type: 'new', direction: 'claude-to-codex', category: 'rules', name: `${rules.length} rule file(s)` }];
329
+ }
330
+
331
+ const existing = instructions.substring(startIdx, endIdx + RULES_SYNC_END.length);
332
+ if (existing === newSection) {
333
+ return [{ type: 'in-sync', direction: 'both', category: 'rules', name: `${rules.length} rule file(s)` }];
334
+ }
335
+ return [{ type: 'updated', direction: 'claude-to-codex', category: 'rules', name: `${rules.length} rule file(s)` }];
336
+ }
337
+
338
+ function classifyAgentActions(root) {
339
+ const agents = getClaudeCustomAgents(root);
340
+ if (agents.length === 0) return [];
341
+
342
+ const instructionsPath = path.join(root, '.codex', 'instructions.md');
343
+ if (!fs.existsSync(instructionsPath)) {
344
+ return [{ type: 'new', direction: 'claude-to-codex', category: 'agents', name: `${agents.length} agent(s)` }];
345
+ }
346
+
347
+ const instructions = fs.readFileSync(instructionsPath, 'utf8');
348
+ const newSection = buildAgentsSection(root, agents);
349
+
350
+ const startIdx = instructions.indexOf(AGENTS_SYNC_START);
351
+ const endIdx = instructions.indexOf(AGENTS_SYNC_END);
352
+ if (startIdx === -1 || endIdx === -1) {
353
+ return [{ type: 'new', direction: 'claude-to-codex', category: 'agents', name: `${agents.length} agent(s)` }];
354
+ }
355
+
356
+ const existing = instructions.substring(startIdx, endIdx + AGENTS_SYNC_END.length);
357
+ if (existing === newSection) {
358
+ return [{ type: 'in-sync', direction: 'both', category: 'agents', name: `${agents.length} agent(s)` }];
359
+ }
360
+ return [{ type: 'updated', direction: 'claude-to-codex', category: 'agents', name: `${agents.length} agent(s)` }];
361
+ }
362
+
363
+ // ── Build sync content ─────────────────────────────────────────────────────
364
+
365
+ function buildRulesSection(root, ruleFiles) {
366
+ const rulesDir = path.join(root, '.claude', 'rules');
367
+ let section = `${RULES_SYNC_START}\n## Project Rules\n\n`;
368
+ section += '> Synced from .claude/rules/ — do not edit this section manually.\n';
369
+ section += '> Re-run /5:synchronize-agents (or $5-synchronize-agents) to update.\n\n';
370
+
371
+ for (const file of ruleFiles) {
372
+ const content = fs.readFileSync(path.join(rulesDir, file), 'utf8');
373
+ const { body } = extractFrontmatterAndBody(content);
374
+ const name = file.replace('.md', '');
375
+ section += `### ${name}\n\n${body.trim()}\n\n`;
376
+ }
377
+
378
+ section += RULES_SYNC_END;
379
+ return section;
380
+ }
381
+
382
+ function buildAgentsSection(root, agentFiles) {
383
+ const agentsDir = path.join(root, '.claude', 'agents');
384
+ let section = `${AGENTS_SYNC_START}\n## Custom Agent References\n\n`;
385
+ section += '> Synced from .claude/agents/ — do not edit this section manually.\n';
386
+ section += '> In Codex, use `spawn_agent()` with equivalent instructions.\n\n';
387
+
388
+ for (const file of agentFiles) {
389
+ const content = fs.readFileSync(path.join(agentsDir, file), 'utf8');
390
+ const converted = claudeToCodexContent(content);
391
+ const name = file.replace('.md', '');
392
+ section += `### ${name}\n\n${converted.trim()}\n\n`;
393
+ }
394
+
395
+ section += AGENTS_SYNC_END;
396
+ return section;
397
+ }
398
+
399
+ function updateInstructionsSection(instructionsContent, startMarker, endMarker, newSection) {
400
+ const startIdx = instructionsContent.indexOf(startMarker);
401
+ const endIdx = instructionsContent.indexOf(endMarker);
402
+
403
+ if (startIdx !== -1 && endIdx !== -1) {
404
+ // Replace existing section
405
+ return instructionsContent.substring(0, startIdx) +
406
+ newSection +
407
+ instructionsContent.substring(endIdx + endMarker.length);
408
+ }
409
+ // Append
410
+ return instructionsContent.trimEnd() + '\n\n' + newSection + '\n';
411
+ }
412
+
413
+ // ── Execute sync ───────────────────────────────────────────────────────────
414
+
415
+ function executeSync(root, actions) {
416
+ const counts = { 'claude-to-codex': { new: 0, updated: 0 }, 'codex-to-claude': { new: 0, updated: 0 }, synced: 0 };
417
+
418
+ for (const action of actions) {
419
+ if (action.type === 'in-sync') {
420
+ counts.synced++;
421
+ continue;
422
+ }
423
+
424
+ if (action.category === 'skill') {
425
+ if (action.direction === 'claude-to-codex') {
426
+ syncSkillClaudeToCodex(root, action.name);
427
+ } else {
428
+ syncSkillCodexToClaude(root, action.name);
429
+ }
430
+ counts[action.direction][action.type]++;
431
+ } else if (action.category === 'command') {
432
+ syncCommandClaudeToCodex(root, action);
433
+ counts['claude-to-codex'][action.type]++;
434
+ }
435
+ // rules and agents are handled separately in batch
436
+ }
437
+
438
+ // Sync rules to instructions.md
439
+ const ruleActions = actions.filter(a => a.category === 'rules' && a.type !== 'in-sync');
440
+ if (ruleActions.length > 0) {
441
+ syncRulesToInstructions(root);
442
+ for (const a of ruleActions) counts['claude-to-codex'][a.type]++;
443
+ }
444
+
445
+ // Sync agents to instructions.md
446
+ const agentActions = actions.filter(a => a.category === 'agents' && a.type !== 'in-sync');
447
+ if (agentActions.length > 0) {
448
+ syncAgentsToInstructions(root);
449
+ for (const a of agentActions) counts['claude-to-codex'][a.type]++;
450
+ }
451
+
452
+ return counts;
453
+ }
454
+
455
+ function syncSkillClaudeToCodex(root, skillName) {
456
+ const src = path.join(root, '.claude', 'skills', skillName);
457
+ const dest = path.join(root, '.codex', 'skills', skillName);
458
+ copyDirContents(src, dest, convertClaudeSkillToCodex);
459
+ log.dim(`skill: ${skillName} → .codex/skills/${skillName}/`);
460
+ }
461
+
462
+ function syncSkillCodexToClaude(root, skillName) {
463
+ const src = path.join(root, '.codex', 'skills', skillName);
464
+ const dest = path.join(root, '.claude', 'skills', skillName);
465
+ copyDirContents(src, dest, convertCodexSkillToClaude);
466
+ log.dim(`skill: ${skillName} → .claude/skills/${skillName}/`);
467
+ }
468
+
469
+ function syncCommandClaudeToCodex(root, action) {
470
+ const [ns, name] = action.name.split('/');
471
+ const srcFile = path.join(root, '.claude', 'commands', ns, `${name}.md`);
472
+ const content = fs.readFileSync(srcFile, 'utf8');
473
+ const converted = convertClaudeCommandToCodexSkill(content, action.skillName);
474
+
475
+ const destDir = path.join(root, '.codex', 'skills', action.skillName);
476
+ if (!fs.existsSync(destDir)) {
477
+ fs.mkdirSync(destDir, { recursive: true });
478
+ }
479
+ fs.writeFileSync(path.join(destDir, 'SKILL.md'), converted);
480
+ log.dim(`command: ${action.name} → .codex/skills/${action.skillName}/`);
481
+ }
482
+
483
+ function syncRulesToInstructions(root) {
484
+ const rules = getClaudeRules(root);
485
+ const section = buildRulesSection(root, rules);
486
+ const instructionsPath = path.join(root, '.codex', 'instructions.md');
487
+ let content = fs.existsSync(instructionsPath) ? fs.readFileSync(instructionsPath, 'utf8') : '';
488
+ content = updateInstructionsSection(content, RULES_SYNC_START, RULES_SYNC_END, section);
489
+ fs.writeFileSync(instructionsPath, content);
490
+ log.dim(`rules: ${rules.length} file(s) → .codex/instructions.md`);
491
+ }
492
+
493
+ function syncAgentsToInstructions(root) {
494
+ const agents = getClaudeCustomAgents(root);
495
+ const section = buildAgentsSection(root, agents);
496
+ const instructionsPath = path.join(root, '.codex', 'instructions.md');
497
+ let content = fs.existsSync(instructionsPath) ? fs.readFileSync(instructionsPath, 'utf8') : '';
498
+ content = updateInstructionsSection(content, AGENTS_SYNC_START, AGENTS_SYNC_END, section);
499
+ fs.writeFileSync(instructionsPath, content);
500
+ log.dim(`agents: ${agents.length} file(s) → .codex/instructions.md`);
501
+ }
502
+
503
+ // ── Display ────────────────────────────────────────────────────────────────
504
+
505
+ function formatActionLabel(type) {
506
+ if (type === 'new') return `${colors.green}NEW${colors.reset}`;
507
+ if (type === 'updated') return `${colors.yellow}UPD${colors.reset}`;
508
+ return `${colors.dim}OK${colors.reset}`;
509
+ }
510
+
511
+ function printSummary(actions) {
512
+ const claudeToCodex = actions.filter(a => a.direction === 'claude-to-codex');
513
+ const codexToClaude = actions.filter(a => a.direction === 'codex-to-claude');
514
+ const inSync = actions.filter(a => a.type === 'in-sync');
515
+
516
+ if (claudeToCodex.length > 0) {
517
+ console.log(' Claude → Codex:');
518
+ for (const a of claudeToCodex) {
519
+ console.log(` [${formatActionLabel(a.type)}] ${a.category}: ${a.name}`);
520
+ }
521
+ }
522
+
523
+ if (codexToClaude.length > 0) {
524
+ console.log(' Codex → Claude:');
525
+ for (const a of codexToClaude) {
526
+ console.log(` [${formatActionLabel(a.type)}] ${a.category}: ${a.name}`);
527
+ }
528
+ }
529
+
530
+ if (inSync.length > 0) {
531
+ console.log(` ${colors.dim}Already in sync: ${inSync.length} item(s)${colors.reset}`);
532
+ }
533
+ }
534
+
535
+ function printResults(counts) {
536
+ const c2c = counts['claude-to-codex'];
537
+ const c2cl = counts['codex-to-claude'];
538
+ const total = c2c.new + c2c.updated + c2cl.new + c2cl.updated;
539
+
540
+ if (total === 0 && counts.synced > 0) {
541
+ log.success(`Everything is already in sync (${counts.synced} item(s))`);
542
+ return;
543
+ }
544
+
545
+ console.log('');
546
+ if (c2c.new + c2c.updated > 0) {
547
+ console.log(` Claude → Codex: ${c2c.new} new, ${c2c.updated} updated`);
548
+ }
549
+ if (c2cl.new + c2cl.updated > 0) {
550
+ console.log(` Codex → Claude: ${c2cl.new} new, ${c2cl.updated} updated`);
551
+ }
552
+ if (counts.synced > 0) {
553
+ console.log(` ${colors.dim}Skipped: ${counts.synced} (already in sync)${colors.reset}`);
554
+ }
555
+ }
556
+
557
+ // ── Main ───────────────────────────────────────────────────────────────────
558
+
559
+ function main() {
560
+ const args = process.argv.slice(2);
561
+ const dryRun = args.includes('--dry-run');
562
+ const quiet = args.includes('--quiet');
563
+
564
+ const root = findProjectRoot();
565
+
566
+ if (!quiet) {
567
+ log.header('Synchronize Agent Runtimes');
568
+ }
569
+
570
+ // Step 1: Detect runtimes
571
+ const hasClaude = isClaudeInstalled(root);
572
+ const hasCodex = isCodexInstalled(root);
573
+
574
+ if (!hasClaude && !hasCodex) {
575
+ log.error('No runtime installations found.');
576
+ log.info('Install Claude Code: npx 5-phase-workflow');
577
+ log.info('Install Codex: npx 5-phase-workflow --codex');
578
+ process.exit(1);
579
+ }
580
+
581
+ if (!hasClaude) {
582
+ log.error('Claude Code runtime not installed.');
583
+ log.info('Install with: npx 5-phase-workflow');
584
+ process.exit(1);
585
+ }
586
+
587
+ if (!hasCodex) {
588
+ log.error('Codex runtime not installed.');
589
+ log.info('Install with: npx 5-phase-workflow --codex');
590
+ process.exit(1);
591
+ }
592
+
593
+ if (!quiet) {
594
+ log.success('Both runtimes detected');
595
+ }
596
+
597
+ // Step 2-3: Inventory and classify
598
+ const actions = [
599
+ ...classifySkillActions(root),
600
+ ...classifyCommandActions(root),
601
+ ...classifyRulesActions(root),
602
+ ...classifyAgentActions(root)
603
+ ];
604
+
605
+ if (actions.length === 0) {
606
+ log.info('No user-generated content found to synchronize.');
607
+ process.exit(0);
608
+ }
609
+
610
+ const actionable = actions.filter(a => a.type !== 'in-sync');
611
+
612
+ if (actionable.length === 0) {
613
+ log.success(`Everything is already in sync (${actions.length} item(s))`);
614
+ process.exit(0);
615
+ }
616
+
617
+ // Step 4: Show summary
618
+ if (!quiet) {
619
+ console.log('');
620
+ printSummary(actions);
621
+ console.log('');
622
+ }
623
+
624
+ if (dryRun) {
625
+ log.info('Dry run — no changes made.');
626
+ process.exit(0);
627
+ }
628
+
629
+ // Step 5: Execute
630
+ const counts = executeSync(root, actions);
631
+
632
+ // Step 6: Report
633
+ if (!quiet) {
634
+ log.header('Synchronization Complete');
635
+ printResults(counts);
636
+ }
637
+ }
638
+
639
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "5-phase-workflow",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "description": "A 5-phase feature development workflow for Claude Code",
5
5
  "bin": {
6
6
  "5-phase-workflow": "bin/install.js"
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: 5:analyze-feature
3
+ description: Analyze any feature, dataflow, or domain concept in the codebase and generate comprehensive documentation with mermaid diagrams. Use when you need to understand how a feature works end-to-end, trace a dataflow, or document a domain area.
4
+ allowed-tools: Read, Write, Glob, Grep, Task, AskUserQuestion
5
+ user-invocable: true
6
+ ---
7
+
8
+ <role>
9
+ You are a Codebase Analyst. Your job is to analyze features, dataflows, and domain concepts in this codebase and produce comprehensive documentation with mermaid diagrams.
10
+ You do NOT write code. You do NOT refactor. You only read, analyze, and document.
11
+ </role>
12
+
13
+ # Analyze Feature
14
+
15
+ Generate comprehensive documentation for any feature, dataflow, or domain concept by analyzing the codebase.
16
+
17
+ ## Arguments
18
+
19
+ - `$ARGUMENTS`: Description of what to analyze (e.g., "user authentication flow", "how orders get processed", "payment integration")
20
+
21
+ ## Step 0: Validate Input
22
+
23
+ If `$ARGUMENTS` is empty, vague, or ambiguous (e.g., "analyze this", "the thing", or a single word without clear context), use AskUserQuestion to clarify:
24
+ - What specific feature, dataflow, or domain concept should be analyzed?
25
+ - Optionally: which modules or layers are most relevant?
26
+
27
+ Do NOT proceed until you have a clear understanding of what to analyze.
28
+
29
+ ## Step 1: Determine Scope and Output Name
30
+
31
+ 1. Derive a short kebab-case name from the analysis subject (e.g., `user-auth`, `order-processing`, `payment-integration`). This becomes the filename: `{name}-analysis.md`.
32
+
33
+ 2. Identify the relevant modules and layers to analyze. If `.5/index/` exists, read the index files for a quick structural overview. Otherwise, use Glob to understand the project layout.
34
+
35
+ 3. Use Glob and Grep to locate the relevant source files. Search for key classes, interfaces, functions, and types related to the analysis subject.
36
+
37
+ ## Step 2: Analyze the Codebase
38
+
39
+ Spawn Explore agents (one or more in parallel depending on scope) to thoroughly read all relevant files. Tailor the agents to the analysis subject:
40
+
41
+ ### For Domain/Feature Analysis
42
+
43
+ Read across the relevant layers following the project's dependency flow (e.g., models -> services -> controllers -> routes, or entities -> repositories -> handlers -> endpoints). Identify the layer structure from the codebase scan.
44
+
45
+ ### For Dataflow Analysis
46
+
47
+ Trace the data path through all involved components. Follow inputs from entry points (API endpoints, event handlers, CLI commands) through processing layers to outputs (database writes, API responses, events published).
48
+
49
+ ### For Cross-Cutting Analysis
50
+
51
+ Examine shared concerns as relevant: validation, authentication, error handling, logging, caching, event publishing.
52
+
53
+ Request structured output from each agent covering the entities, flows, relationships, and patterns found.
54
+
55
+ ## Step 3: Generate Documentation
56
+
57
+ Using the analysis results, create a comprehensive markdown document.
58
+
59
+ **The document MUST follow this structure** (omit sections that don't apply to the analysis subject):
60
+
61
+ ```markdown
62
+ # {Analysis Title}
63
+
64
+ {1-3 sentence summary of what this feature/dataflow does and why it exists}
65
+
66
+ ---
67
+
68
+ ## Table of Contents
69
+ {auto-generated links to sections below}
70
+
71
+ ---
72
+
73
+ ## Overview
74
+ {High-level description of the feature/concept, its purpose, and where it fits in the system}
75
+ {Mention the involved modules and their roles}
76
+
77
+ ---
78
+
79
+ ## Data Flow
80
+ {For each major operation, create a mermaid sequence diagram}
81
+ {Show the path from entry point through processing layers to output}
82
+ {Include relevant function/method names and payload types}
83
+
84
+ ---
85
+
86
+ ## Domain Model
87
+ {mermaid classDiagram or erDiagram showing entities and their relationships}
88
+ {Include: key fields, types, relationships with cardinality}
89
+ {Show: value objects, enums, and aggregate boundaries where relevant}
90
+
91
+ ---
92
+
93
+ ## Operations
94
+
95
+ ### Writes (Commands/Mutations)
96
+ {Table: Operation | Handler/Service | Description | Key Validation}
97
+
98
+ ### Reads (Queries)
99
+ {Table: Query | Handler/Service | Description | Return Type}
100
+
101
+ ---
102
+
103
+ ## API / Entry Points
104
+ {Table: Method | Path/Topic/Command | Handler | Description}
105
+
106
+ ---
107
+
108
+ ## Event Flow
109
+ {If async events are involved (Kafka, RabbitMQ, webhooks, etc.)}
110
+ {mermaid sequence diagram showing event production/consumption}
111
+ {Include: event names, topics, consumer handlers}
112
+
113
+ ---
114
+
115
+ ## Module Dependencies
116
+ {mermaid graph showing which modules depend on which}
117
+ {Use subgraphs to group by domain or layer}
118
+
119
+ ---
120
+
121
+ ## Key Implementation Details
122
+ {Notable patterns, edge cases, business rules worth highlighting}
123
+ {Reference specific classes/functions and file paths}
124
+ ```
125
+
126
+ ### Mermaid Diagram Guidelines
127
+
128
+ - **Sequence diagrams** (`sequenceDiagram`): For request/response flows across layers
129
+ - **Class diagrams** (`classDiagram`): For domain model relationships and aggregate structure
130
+ - **ER diagrams** (`erDiagram`): For persistence model relationships
131
+ - **Flowcharts** (`flowchart TD`): For decision trees, validation flows, state machines
132
+ - **Graph diagrams** (`graph LR` or `graph TD`): For module dependencies, component trees
133
+ - Keep node labels short but descriptive
134
+ - Use subgraphs to group related nodes by module or layer
135
+ - Use dotted lines (`-.->`) for optional/indirect relationships
136
+
137
+ ## Step 4: Write File
138
+
139
+ 1. Write the documentation using the Write tool to `.5/analysis/{name}-analysis.md`.
140
+
141
+ 2. Report to the user:
142
+ ```
143
+ Analysis saved to .5/analysis/{name}-analysis.md
144
+
145
+ Sections included:
146
+ - {list of sections that were generated}
147
+ - {N} mermaid diagrams
148
+
149
+ Modules analyzed: {list of modules examined}
150
+ ```
151
+
152
+ ## Important Notes
153
+
154
+ - Read ALL relevant files thoroughly before writing -- do not guess or assume
155
+ - Every mermaid diagram must be syntactically valid
156
+ - Reference specific class/function names and file paths so the reader can navigate to the source
157
+ - Focus on the specific feature/dataflow requested, not the entire codebase
158
+ - Follow the data through all layers from entry point to output
159
+ - Document both the happy path and notable error/validation paths
@@ -14,20 +14,22 @@ After creating the spec, you are FINISHED. You do not continue. You do not offer
14
14
  </role>
15
15
 
16
16
  <constraints>
17
- HARD CONSTRAINTS — violations waste tokens and get blocked by plan-guard:
18
- - NEVER write code, pseudo-code, or implementation snippets in any output
19
- - NEVER describe HOW something will be implemented (file contents, signatures, class structures)
20
- - NEVER create an implementation plan, file list, component breakdown, or step-by-step build guide that is Phase 2's job
21
- - NEVER suggest "shall I continue with implementation planning?" or "let me create the plan" — you are DONE after feature.md
22
- - NEVER offer to proceed to the next phase the user will invoke `/5:plan-implementation` themselves
23
- - NEVER spawn Task agents with subagent_type other than Explore
24
- - NEVER write to any file except .5/.planning-active, .5/features/{name}/codebase-scan.md, and .5/features/{name}/feature.md
25
- - NEVER call EnterPlanMode the workflow has its own planning process
26
- - NEVER use Bash to create, write, or modify files — this bypasses the plan-guard and is a constraint violation
27
- - NEVER continue past the completion message — when you output "Feature spec created at...", you are FINISHED
28
- - The feature spec describes WHAT and WHY, never HOW
29
- - If you feel the urge to plan implementation or write code, STOP — ask a clarifying question instead
30
- - Your output is a SPECIFICATION, not a design document. No code. No file layouts. No API shapes. No implementation plans.
17
+ HARD CONSTRAINTS:
18
+ - Do NOT write code or pseudo-code describe behavior and data shapes in natural language or tables
19
+ - Do NOT create implementation plans, file lists, or step-by-step build guides — that is Phase 2's job
20
+ - Do NOT offer to proceed to the next phase the user will invoke `/5:plan-implementation` themselves
21
+ - Do NOT spawn Task agents with subagent_type other than Explore
22
+ - Do NOT write to any file except .5/.planning-active, .5/features/{name}/codebase-scan.md, and .5/features/{name}/feature.md
23
+ - Do NOT call EnterPlanMode the workflow has its own planning process
24
+ - Do NOT use Bash to create, write, or modify files — this bypasses the plan-guard
25
+ - Do NOT continue past the completion message when you output "Feature spec created at...", you are FINISHED
26
+
27
+ WHAT IS ALLOWED:
28
+ - Name existing classes, modules, services, and patterns
29
+ - Describe entity fields with domain types
30
+ - Reference existing patterns as models
31
+ - Mention affected methods or APIs by name
32
+ - Include data shape tables with field names and types — these are part of the requirement
31
33
  </constraints>
32
34
 
33
35
  <write-rules>
@@ -42,11 +44,10 @@ Any other Write target WILL be blocked by the plan-guard hook. Do not attempt it
42
44
  Use the template structure from `.claude/templates/workflow/FEATURE-SPEC.md`.
43
45
 
44
46
  **Content rules for feature.md:**
45
- - Requirements use natural language ("The system shall..."), NOT code
46
- - Affected Components lists module/domain names, NOT file paths
47
- - NO code snippets, NO pseudo-code, NO type definitions
48
- - Entity definitions describe data CONCEPTS, not DB schemas or TypeScript interfaces
49
- - Acceptance criteria describe observable behavior, NOT test code
47
+ - Write naturally reference existing classes, modules, and patterns by name for precision
48
+ - Entity definitions include field names and domain types — these define the requirement
49
+ - Acceptance criteria describe observable behavior
50
+ - No code blocks, no pseudo-code, no class hierarchy designs
50
51
  </output-format>
51
52
 
52
53
  <collaboration-strategy>
@@ -193,7 +194,7 @@ Targeted exploration for feature planning.
193
194
 
194
195
  ### Step 4: Create Feature Specification
195
196
 
196
- > **ROLE CHECK:** You are writing a SPECIFICATION (WHAT/WHY), not a design document (HOW). Zero code, zero file paths to create, zero signatures. After writing feature.md you are DONE — do NOT proceed to implementation planning or coding.
197
+ > **ROLE CHECK:** You are writing a FEATURE SPECIFICATION. After writing feature.md you are DONE — do NOT proceed to implementation planning or coding.
197
198
 
198
199
  **Extract ticket ID from git branch:**
199
200
  - The Explore agent from Step 2 already ran `git branch --show-current` — find the branch name in its results
@@ -209,25 +210,14 @@ Write to `.5/features/{name}/feature.md` using Write tool, where `{name}` is eit
209
210
 
210
211
  Use the template structure from `.claude/templates/workflow/FEATURE-SPEC.md`.
211
212
 
212
- Populate all sections:
213
- - Ticket ID & Summary
214
- - Problem Statement
215
- - Visual Overview (optional mermaid diagramssee below)
216
- - Requirements (functional and non-functional)
217
- - Constraints
218
- - Affected Components (from exploration)
219
- - Acceptance Criteria
220
- - Alternatives Considered
221
- - Decisions (from the conversation) — label each with **[DECIDED]**, **[FLEXIBLE]**, or **[DEFERRED]**
222
-
223
- **Visual Overview (optional mermaid diagrams):**
224
- Include mermaid diagrams in the spec when they add clarity. Use your judgment:
225
- - **Flow diagrams**: When the feature involves a multi-step process or state transitions
226
- - **Entity relationship diagrams**: When new data concepts relate to existing ones
227
- - **Component interaction diagrams**: When multiple modules/services communicate
228
- - **Sequence diagrams**: When the order of operations between actors matters
229
-
230
- Simple features (single-component changes, straightforward CRUD) typically do not need diagrams. Do not add diagrams for the sake of having them. Diagrams describe WHAT happens, not HOW it is implemented. No class diagrams, no file-level architecture diagrams, no code-level sequence diagrams.
213
+ Populate the sections from the template. Key guidance:
214
+ - **Overview**: Write a short narrative (3-5 sentences) merging the problem and the solution
215
+ - **What Changes**: Group by logical concern, not by module layer. Name existing classes and patterns. Use entity tables where new data models are introduced
216
+ - **Existing Patterns to Follow**: Be specific these are the highest-value pointers for Phase 2
217
+ - **Scope**: Be explicit about what's in and what's out
218
+ - **Decisions**: Label each from the conversation
219
+ - **Diagrams**: Include only when they add clarity. Simple features don't need them
220
+ - **Alternatives**: Only include if genuinely discussed and the reasoning matters. Delete if empty
231
221
 
232
222
  **Decision labeling rules:**
233
223
  - **[DECIDED]**: The user gave a clear, specific answer → Phase 2 planner and Phase 3 agents MUST honor exactly
@@ -110,11 +110,11 @@ This activates (or refreshes) the plan-guard hook which prevents accidental sour
110
110
 
111
111
  ### Step 1: Load Feature Spec *(skip if live context)*
112
112
 
113
- **If live context:** You already have the feature spec discussion in your conversation history. Extract ticket ID, requirements, acceptance criteria, affected components, and decisions from what was discussed. Output `✓ Step 1 skipped (live context)` and proceed to Step 1b.
113
+ **If live context:** You already have the feature spec discussion in your conversation history. Extract ticket ID, what changes (by logical concern), acceptance criteria, existing patterns to follow, scope, and decisions from what was discussed. Output `✓ Step 1 skipped (live context)` and proceed to Step 1b.
114
114
 
115
115
  **If no live context:** Read `.5/features/{feature-name}/feature.md` (where `{feature-name}` is the argument provided).
116
116
 
117
- Extract: Ticket ID, requirements (functional and non-functional), acceptance criteria, affected components, and **decisions**.
117
+ Extract: Ticket ID, overview, what changes (each logical concern), existing patterns to follow, constraints, scope, acceptance criteria, and **decisions**.
118
118
 
119
119
  **Decision labels from feature spec:**
120
120
  - **[DECIDED]** items are locked — your plan MUST honor them exactly. Do not override or reinterpret.
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: 5:synchronize-agents
3
+ description: Synchronize user-generated skills, rules, and custom content between Claude Code and Codex runtimes
4
+ allowed-tools: Bash, Read, AskUserQuestion
5
+ user-invocable: true
6
+ model: haiku
7
+ context: fork
8
+ ---
9
+
10
+ <role>
11
+ You are a Runtime Synchronizer. You run the sync script and report results.
12
+ You do NOT modify files manually. After reporting, you are DONE.
13
+ </role>
14
+
15
+ # Synchronize Agent Runtimes
16
+
17
+ Synchronizes user-generated content (skills, commands, agents, rules) between the Claude Code (`.claude/`) and Codex (`.codex/`) runtimes.
18
+
19
+ ## Step 1: Locate the Sync Script
20
+
21
+ The script is `bin/sync-agents.js` in the workflow package. Find it by checking these paths in order:
22
+
23
+ 1. `./bin/sync-agents.js` (development checkout / project root)
24
+ 2. `./node_modules/5-phase-workflow/bin/sync-agents.js` (local npm install)
25
+
26
+ Read `.5/version.json` if available — its location confirms the project root.
27
+
28
+ Store the resolved path for the following steps.
29
+
30
+ If the script cannot be found, tell the user: "Sync script not found. Update the workflow first: `npx 5-phase-workflow --upgrade`" and **stop**.
31
+
32
+ ## Step 2: Dry Run
33
+
34
+ Run the script in dry-run mode to preview what will be synced:
35
+
36
+ ```bash
37
+ node {script-path} --dry-run
38
+ ```
39
+
40
+ If the script exits with a non-zero code (missing runtime, no content), show the output and **stop**.
41
+
42
+ If there are no actionable changes (everything in sync), report that and **stop**.
43
+
44
+ ## Step 3: Confirm with User
45
+
46
+ Show the dry-run output. Ask: "Proceed with synchronization?"
47
+
48
+ If the user declines, stop.
49
+
50
+ ## Step 4: Execute Sync
51
+
52
+ ```bash
53
+ node {script-path}
54
+ ```
55
+
56
+ ## Step 5: Report
57
+
58
+ Show the script output. Summarize what was synced.
59
+
60
+ Note: This command works identically from both Claude Code (`/5:synchronize-agents`) and Codex (`$5-synchronize-agents`). The script auto-detects the project root.
@@ -1,135 +1,100 @@
1
1
  <!-- TEMPLATE RULES:
2
- - Requirements use natural language, not code
3
- - Affected Components lists module/domain names, not file paths
4
- - Entity definitions describe data concepts, not schemas or interfaces
5
- - Acceptance criteria describe observable behavior, not test assertions
6
- - NO code, pseudo-code, or implementation details anywhere in this document
7
- - Visual Overview section is OPTIONAL delete it if the feature doesn't benefit from diagrams
8
- - Mermaid diagrams describe WHAT happens, not HOW it is implemented
2
+ - Write naturally
3
+ - Reference existing classes, modules, and patterns by name for precision
4
+ - Entity definitions include field names and domain types
5
+ - Acceptance criteria describe observable behavior
6
+ - No code blocks, no pseudo-code, no class hierarchy designs
7
+ - Delete any OPTIONAL section that doesn't add value for this feature
9
8
  -->
10
9
 
11
10
  # Feature: {TICKET-ID} - {Title}
12
11
 
13
- ## Ticket ID
14
- {TICKET-ID}
12
+ **Ticket:** {TICKET-ID}
15
13
 
16
- ## Summary
17
- {1-2 sentence overview: what capability is being added and who benefits}
14
+ ## Overview
18
15
 
19
- ## Problem Statement
20
- {Describe the current pain point or gap. Who experiences it and what is the impact?}
16
+ {What is the problem or gap, and what capability is being added? Write this as a short narrative
17
+ that covers who is affected, why the change matters, and what the end result looks like.}
21
18
 
22
- ## Visual Overview
23
- <!-- OPTIONAL: Include mermaid diagrams only when they add clarity to the feature.
24
- Delete this entire section if the feature is simple enough to understand without diagrams.
25
- Use whichever diagram types are relevant — you don't need all of them. -->
19
+ <!-- OPTIONAL: Include mermaid diagrams only when they add clarity.
20
+ Delete this section for simple features. Use whichever diagram type fits. -->
26
21
 
27
- <!-- Process Flow — use when the feature involves a multi-step process or state transitions -->
28
22
  ```mermaid
29
23
  flowchart TD
30
- A[Start state] --> B[Step 1]
31
- B --> C{Decision point}
32
- C -->|Yes| D[Outcome 1]
33
- C -->|No| E[Outcome 2]
24
+ A[Current state] --> B[Change]
25
+ B --> C[New capability]
34
26
  ```
35
27
 
36
- <!-- Entity Relationships — use when new data concepts relate to existing ones -->
37
- ```mermaid
38
- erDiagram
39
- ENTITY-A ||--o{ ENTITY-B : "relationship"
40
- ENTITY-B }|--|| ENTITY-C : "relationship"
41
- ```
28
+ ## What Changes
42
29
 
43
- <!-- Component Interactions use when multiple modules or services communicate -->
44
- ```mermaid
45
- sequenceDiagram
46
- actor User
47
- participant ComponentA
48
- participant ComponentB
49
- User->>ComponentA: action
50
- ComponentA->>ComponentB: interaction
51
- ComponentB-->>User: result
52
- ```
30
+ {Describe what the feature does in natural, flowing prose. Name existing classes, modules, and patterns
31
+ where relevant. Group by logical concern, not by module layer. Each subsection should tell the reader
32
+ what happens and which parts of the codebase are involved.}
53
33
 
54
- ## Requirements
34
+ ### {Logical concern 1, e.g., "New data model"}
55
35
 
56
- ### Functional Requirements
57
- - {The system shall... [describe observable behavior]}
58
- - ...
36
+ {Describe the change. Name affected modules and classes. If a new entity is introduced, describe its
37
+ fields and types in a table:}
59
38
 
60
- ### Non-Functional Requirements
61
- - {Performance, reliability, scalability, or compatibility expectations}
62
- - ...
39
+ | Field | Type | Required | Description |
40
+ |-------|------|----------|-------------|
41
+ | ... | ... | ... | ... |
63
42
 
64
- ## Constraints
65
- - {Business rules that limit the solution space}
66
- - {Technical boundaries from existing architecture}
67
- - {Timeline or resource limitations}
43
+ ### {Logical concern 2, e.g., "CRUD operations"}
68
44
 
69
- ## Affected Components
70
- - **{component/module-1}** - {What changes here}
71
- - **{component/module-2}** - {What changes here}
72
- - **{component/module-3}** - {What changes here}
73
- - ...
45
+ {Describe the behavior. Reference existing patterns to follow, e.g.,
46
+ "Follow the same approach as the UserService update flow."}
74
47
 
75
- ## Entity/Component Definitions
48
+ ### {Logical concern 3, e.g., "API exposure"}
76
49
 
77
- ### {EntityName} (if applicable)
78
- <!-- Describe the data CONCEPT, not the implementation. No TypeScript, no SQL. -->
79
- | Field | Type | Required | Description |
80
- |-------|------|----------|-------------|
81
- | id | {Entity}Id | Yes | Unique identifier |
82
- | name | String | Yes | Entity name |
83
- | ... | ... | ... | ... |
50
+ {Describe what gets exposed and how.}
84
51
 
85
52
  ### Business Rules
53
+
86
54
  - {Rule 1}
87
55
  - {Rule 2}
88
- - ...
89
56
 
90
- ## Acceptance Criteria
91
- - [ ] {Observable behavior that proves this requirement is met}
92
- - [ ] {Observable behavior that proves this requirement is met}
93
- - [ ] {Observable behavior that proves this requirement is met}
57
+ ## Existing Patterns to Follow
58
+
59
+ {List concrete patterns from the codebase that the implementation should mirror.
60
+ These are high-value pointers for Phase 2 -- be specific.}
61
+
62
+ - **{Pattern name}** -- {where it lives and what to reuse from it}
94
63
  - ...
95
64
 
96
- ## Alternatives Considered
65
+ ## Constraints
66
+
67
+ - {Business rules that limit the solution space}
68
+ - {Technical boundaries from existing architecture}
69
+
70
+ ## Scope
71
+
72
+ **In scope:**
73
+ - {What is included}
97
74
 
98
- ### Option 1: {Alternative approach}
99
- **Pros:** {Benefits}
100
- **Cons:** {Drawbacks}
101
- **Decision:** Rejected because {reason}
75
+ **Out of scope:**
76
+ - {What is explicitly excluded and why}
102
77
 
103
- ### Option 2: {Another alternative}
104
- **Pros:** {Benefits}
105
- **Cons:** {Drawbacks}
106
- **Decision:** Rejected because {reason}
78
+ ## Acceptance Criteria
107
79
 
108
- ### Chosen Approach: {Selected approach}
109
- **Rationale:** {Why this approach was chosen}
80
+ - [ ] {Observable behavior that proves a requirement is met}
81
+ - [ ] ...
110
82
 
111
83
  ## Decisions
112
84
 
113
- <!-- Record key decisions from the spec discussion.
114
- Tag each with exactly one of: [DECIDED], [FLEXIBLE], [DEFERRED]
115
- - [DECIDED]: Locked — Phase 2 planner and Phase 3 agents MUST honor exactly
116
- - [FLEXIBLE]: Claude's discretion — planner chooses the best approach
117
- - [DEFERRED]: Out of scope — planner MUST NOT include in the plan
118
- -->
85
+ <!-- Tag each: [DECIDED] = locked, [FLEXIBLE] = planner chooses, [DEFERRED] = not in this iteration -->
119
86
 
120
- ### {Topic: brief description of what was decided}
121
- **Context:** {Why this decision came up}
122
- **Decision:** {What was decided} **[DECIDED]**
87
+ - **{Topic}:** {What was decided and why} **[DECIDED]**
88
+ - **{Topic}:** {General direction, planner chooses specifics} **[FLEXIBLE]**
89
+ - **{Topic}:** {Not addressing now -- reason} **[DEFERRED]**
123
90
 
124
- ### {Topic: area left to implementer's discretion}
125
- **Context:** {What was discussed}
126
- **Decision:** {General direction, implementer chooses specifics} **[FLEXIBLE]**
91
+ <!-- OPTIONAL: Only include if alternatives were genuinely discussed and the reasoning matters -->
92
+ ## Alternatives Considered
127
93
 
128
- ### {Topic: explicitly deferred item}
129
- **Context:** {Why it was raised}
130
- **Decision:** {Not addressing now — reason} **[DEFERRED]**
94
+ - **{Alternative}:** Rejected because {reason}
95
+ - **{Chosen approach}:** Selected because {reason}
131
96
 
132
97
  ## Next Steps
133
- After approval:
134
- 1. Run `/clear` to reset context
98
+
99
+ 1. Run `/clear` to reset context (optional)
135
100
  2. Run `/5:plan-implementation {TICKET-ID}-{description}`