odd-studio 3.7.5 → 3.7.7

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 (36) hide show
  1. package/README.md +0 -3
  2. package/bin/commands/init.js +3 -41
  3. package/bin/commands/status.js +0 -3
  4. package/bin/odd-studio.js +1 -1
  5. package/codex-plugin/.codex-plugin/plugin.json +1 -1
  6. package/codex-plugin/hooks.json +25 -7
  7. package/hooks/odd-studio.sh +0 -31
  8. package/package.json +1 -2
  9. package/plugins/plugin-gates.js +104 -79
  10. package/scripts/command-definitions.js +7 -47
  11. package/scripts/hook-definitions.js +137 -0
  12. package/scripts/install-codex-commands.js +11 -3
  13. package/scripts/install-commands.js +13 -9
  14. package/scripts/setup-codex-plugin.js +7 -0
  15. package/scripts/setup-hooks.js +6 -130
  16. package/scripts/state-schema.js +2 -4
  17. package/skill/SKILL.md +50 -675
  18. package/skill/docs/build/build-protocol.md +1 -5
  19. package/skill/docs/build/confirm-protocol.md +41 -0
  20. package/skill/docs/build/export-protocol.md +45 -0
  21. package/skill/docs/chapters/chapter-10.md +2 -2
  22. package/skill/docs/chapters/chapter-11.md +3 -5
  23. package/skill/docs/chapters/chapter-12.md +2 -2
  24. package/skill/docs/chapters/chapter-14.md +8 -12
  25. package/skill/docs/runtime/build-entry.md +64 -0
  26. package/skill/docs/runtime/shared-commands.md +65 -0
  27. package/skill/docs/runtime/status-protocol.md +11 -0
  28. package/skill/docs/startup/startup-protocol.md +90 -0
  29. package/skill/odd-build/SKILL.md +6 -4
  30. package/skill/odd-debug/SKILL.md +3 -5
  31. package/skill/odd-deploy/SKILL.md +8 -3
  32. package/skill/odd-plan/SKILL.md +10 -3
  33. package/skill/odd-status/SKILL.md +3 -3
  34. package/skill/odd-swarm/SKILL.md +5 -4
  35. package/templates/.odd/state.json +1 -3
  36. package/templates/AGENTS.md +52 -190
package/README.md CHANGED
@@ -72,7 +72,6 @@ That single command:
72
72
  - Scaffolds your project structure (`docs/`, `.odd/`, instruction files)
73
73
  - Installs the ODD harness into the matching project-local agent surface (`.claude/`, `.opencode/`, or `plugins/odd-studio/`)
74
74
  - Configures odd-flow MCP server for cross-session memory in the matching agent config
75
- - Optionally installs Checkpoint security scanning (you'll be asked)
76
75
  - Initialises git with an initial commit
77
76
 
78
77
  **Then open your project in your AI coding agent and start ODD:**
@@ -150,9 +149,7 @@ ODD Studio installs safety gates that run automatically throughout your build. T
150
149
  | Brief gate | Before agent spawning | Blocks build agents until the session brief is confirmed |
151
150
  | Swarm write gate | Before file writes | Blocks writes during swarm builds unless from an assigned agent |
152
151
  | Verify gate | Before state edits | Blocks premature outcome confirmation |
153
- | Checkpoint gate | Before confirm/commit | Blocks verification and commits until a fresh security scan clears the latest build changes |
154
152
  | odd-flow build gate | Before agent spawning | Blocks builds without odd-flow sync |
155
- | odd-flow commit gate | Before git commit | Blocks commits during build without odd-flow sync |
156
153
  | Outcome quality | After writing outcomes | Checks all 6 fields present; flags banned technical vocabulary |
157
154
  | Persona quality | After writing personas | Checks all 7 dimensions are present |
158
155
  | Code elegance | After writing source files | Checks file length against ODD limits |
@@ -29,7 +29,7 @@ export function registerInit(program, deps) {
29
29
  const targetDir = projectName ? path.resolve(process.cwd(), projectName) : process.cwd();
30
30
  const resolvedName = projectName || path.basename(process.cwd());
31
31
  const agentLabel = getAgentLabel(agent);
32
- const totalSteps = 5;
32
+ const totalSteps = 4;
33
33
 
34
34
  console.log(chalk.bold(` Setting up: ${chalk.cyan(resolvedName)}`) + chalk.dim(` (${agentLabel})\n`));
35
35
 
@@ -47,7 +47,6 @@ export function registerInit(program, deps) {
47
47
 
48
48
  await installSkills({ PACKAGE_ROOT, isClaude, isOpenCode, options, print, targetDir, totalSteps });
49
49
  await installHooks({ PACKAGE_ROOT, isClaude, isOpenCode, isCodex, options, print, targetDir, totalSteps });
50
- await maybeInstallCheckpoint({ options, print, targetDir, totalSteps });
51
50
  await configureMcp({ agent, options, print, targetDir, totalSteps });
52
51
 
53
52
  print.blank();
@@ -142,51 +141,14 @@ async function installHooks(context) {
142
141
  }
143
142
  }
144
143
 
145
- async function maybeInstallCheckpoint({ options, print, targetDir, totalSteps }) {
146
- let installCheckpoint = false;
147
- if (!options.yes) {
148
- const { default: inquirer } = await import('inquirer');
149
- const answer = await inquirer.prompt([{
150
- type: 'confirm',
151
- name: 'checkpoint',
152
- message: 'Install Checkpoint security scanning into this project?',
153
- default: false,
154
- }]);
155
- installCheckpoint = answer.checkpoint;
156
- }
157
-
158
- if (!installCheckpoint) {
159
- print.step(4, totalSteps, 'Skipping Checkpoint security scanning');
160
- print.info('Enable later: npx @darrenjcoxon/vibeguard --install-tools');
161
- return;
162
- }
163
-
164
- print.step(4, totalSteps, 'Installing Checkpoint security scanning...');
165
- const spinner = ora({ text: '', indent: 4 }).start();
166
- try {
167
- const { execFileSync } = await import('child_process');
168
- execFileSync('npx', ['-y', '@darrenjcoxon/vibeguard', '--install-tools'], {
169
- cwd: targetDir,
170
- stdio: 'inherit',
171
- timeout: 60000,
172
- });
173
- spinner.stop();
174
- print.ok('Checkpoint security tools installed');
175
- } catch {
176
- spinner.stop();
177
- print.warn('Checkpoint tools could not be installed');
178
- print.info('Run: npx @darrenjcoxon/vibeguard --install-tools to enable later');
179
- }
180
- }
181
-
182
144
  async function configureMcp({ agent, options, print, targetDir, totalSteps }) {
183
145
  if (options.skipMcp) {
184
- print.step(5, totalSteps, 'Skipping odd-flow MCP server (--skip-mcp)');
146
+ print.step(4, totalSteps, 'Skipping odd-flow MCP server (--skip-mcp)');
185
147
  print.info('odd-flow swarm coordination will not be available.');
186
148
  return;
187
149
  }
188
150
 
189
- print.step(5, totalSteps, 'Configuring odd-flow memory server...');
151
+ print.step(4, totalSteps, 'Configuring odd-flow memory server...');
190
152
  const spinner = ora({ text: '', indent: 4 }).start();
191
153
  try {
192
154
  const { default: setupMcp } = await import('../../scripts/setup-mcp.js');
@@ -28,9 +28,6 @@ export function registerStatus(program, deps) {
28
28
  console.log(chalk.bold(' Debug: ') + (state.debugStrategy || 'strategy not set'));
29
29
  console.log(chalk.bold(' Target: ') + (state.debugTarget || 'target not set'));
30
30
  }
31
- if (state.currentPhase === 'build') {
32
- console.log(chalk.bold(' Checkpoint: ') + (state.checkpointStatus || 'unknown'));
33
- }
34
31
  print.blank();
35
32
 
36
33
  if (state.personas?.length) {
package/bin/odd-studio.js CHANGED
@@ -15,7 +15,7 @@ import { registerUninstall } from './commands/uninstall.js';
15
15
  const __filename = fileURLToPath(import.meta.url);
16
16
  const __dirname = path.dirname(__filename);
17
17
  const require = createRequire(import.meta.url);
18
- const pkg = require('../package.json'); // v3.6.0
18
+ const pkg = require('../package.json');
19
19
 
20
20
  const PACKAGE_ROOT = path.resolve(__dirname, '..');
21
21
  const deps = { PACKAGE_ROOT, print };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "odd-studio",
3
- "version": "3.6.0",
3
+ "version": "3.7.7",
4
4
  "description": "Outcome-Driven Development planning and build harness for domain experts building serious software with AI.",
5
5
  "author": {
6
6
  "name": "ODD Studio"
@@ -15,7 +15,7 @@
15
15
  ]
16
16
  },
17
17
  {
18
- "matcher": "Write|Edit",
18
+ "matcher": "Write",
19
19
  "hooks": [
20
20
  {
21
21
  "type": "command",
@@ -32,15 +32,19 @@
32
32
  ]
33
33
  },
34
34
  {
35
- "matcher": "Bash",
35
+ "matcher": "Edit",
36
36
  "hooks": [
37
37
  {
38
38
  "type": "command",
39
- "command": "./hooks/odd-studio.sh checkpoint-gate"
39
+ "command": "./hooks/odd-studio.sh swarm-write"
40
40
  },
41
41
  {
42
42
  "type": "command",
43
- "command": "./hooks/odd-studio.sh commit-gate"
43
+ "command": "./hooks/odd-studio.sh verify-gate"
44
+ },
45
+ {
46
+ "type": "command",
47
+ "command": "./hooks/odd-studio.sh confirm-gate"
44
48
  }
45
49
  ]
46
50
  }
@@ -57,12 +61,26 @@
57
61
  ],
58
62
  "PostToolUse": [
59
63
  {
60
- "matcher": "Bash",
64
+ "matcher": "Write",
61
65
  "hooks": [
62
66
  {
63
67
  "type": "command",
64
- "command": "./hooks/odd-studio.sh checkpoint-validate"
65
- },
68
+ "command": "./hooks/odd-studio.sh plan-complete-gate"
69
+ }
70
+ ]
71
+ },
72
+ {
73
+ "matcher": "Edit",
74
+ "hooks": [
75
+ {
76
+ "type": "command",
77
+ "command": "./hooks/odd-studio.sh plan-complete-gate"
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "matcher": "Bash",
83
+ "hooks": [
66
84
  {
67
85
  "type": "command",
68
86
  "command": "./hooks/odd-studio.sh session-save"
@@ -239,8 +239,6 @@ verify-gate)
239
239
  exit 2
240
240
  fi
241
241
 
242
- # Checkpoint pre-check removed in v3.7.3 (see checkpoint-gate note below).
243
-
244
242
  VERIFIED_CONFIRMED=$(get_state_field "verificationConfirmed")
245
243
  if [ "$VERIFIED_CONFIRMED" != "true" ]; then
246
244
  echo "ODD STUDIO [verify-gate]: Cannot mark NEW outcomes as verified." >&2
@@ -250,14 +248,6 @@ verify-gate)
250
248
  exit 0
251
249
  ;;
252
250
 
253
- # ─────────────────────────────────────────────────────────────────────────────
254
- # checkpoint-gate — REMOVED in v3.7.3
255
- # Vibeguard/Checkpoint was treating dependency-lockfile CVEs as equivalent to
256
- # code vulnerabilities, making every commit impossible while any transitive
257
- # dep had an open advisory. Re-introduce only with a scoped scanner that
258
- # ignores lockfile-only findings.
259
- # ─────────────────────────────────────────────────────────────────────────────
260
-
261
251
  # ─────────────────────────────────────────────────────────────────────────────
262
252
  # PreToolUse: Edit|Write — blocks briefConfirmed without odd-flow store
263
253
  # ─────────────────────────────────────────────────────────────────────────────
@@ -276,14 +266,6 @@ confirm-gate)
276
266
  exit 0
277
267
  ;;
278
268
 
279
- # ─────────────────────────────────────────────────────────────────────────────
280
- # commit-gate — REMOVED in v3.7.4
281
- # The dirty/ready marker treadmill was creating more friction than value.
282
- # State persistence to odd-flow is now the orchestrator's responsibility at
283
- # genuine persistence points (session end, outcome verified) rather than a
284
- # hard-block on every commit.
285
- # ─────────────────────────────────────────────────────────────────────────────
286
-
287
269
  # ─────────────────────────────────────────────────────────────────────────────
288
270
  # UserPromptSubmit — warns every turn if build phase without swarm
289
271
  # ─────────────────────────────────────────────────────────────────────────────
@@ -344,10 +326,6 @@ session-save)
344
326
  exit 0
345
327
  ;;
346
328
 
347
- # ─────────────────────────────────────────────────────────────────────────────
348
- # checkpoint-validate — REMOVED in v3.7.3 (see checkpoint-gate note above)
349
- # ─────────────────────────────────────────────────────────────────────────────
350
-
351
329
  # ─────────────────────────────────────────────────────────────────────────────
352
330
  # PostToolUse: Write|Edit state.json — blocks phase transition without Steps 9, 9b, 9d
353
331
  # ─────────────────────────────────────────────────────────────────────────────
@@ -389,17 +367,9 @@ plan-complete-gate)
389
367
  exit 0
390
368
  ;;
391
369
 
392
- # ─────────────────────────────────────────────────────────────────────────────
393
- # state-dirty-mark — REMOVED in v3.7.4 (dirty/ready treadmill eliminated)
394
- # ─────────────────────────────────────────────────────────────────────────────
395
-
396
370
  # ─────────────────────────────────────────────────────────────────────────────
397
371
  # PostToolUse: mcp__odd-flow__memory_store — brief-stored marker only
398
372
  # ─────────────────────────────────────────────────────────────────────────────
399
- # v3.7.4: the state-ready / dirty treadmill has been removed. This hook now
400
- # only creates the brief-stored marker after a session brief is persisted.
401
- # odd-project-state stores are logged but do not touch any marker — state
402
- # persistence is no longer gated by a marker state machine.
403
373
  store-validate)
404
374
  [ "$TOOL_NAME" = "mcp__odd-flow__memory_store" ] || exit 0
405
375
  [ "$CURRENT_PHASE" = "build" ] || exit 0
@@ -502,7 +472,6 @@ code-quality)
502
472
 
503
473
  # ─────────────────────────────────────────────────────────────────────────────
504
474
  # PostToolUse: Write|Edit — security baseline warnings (stderr, non-blocking)
505
- # Checkpoint dirty-marking was removed in v3.7.3 along with checkpoint-gate.
506
475
  # ─────────────────────────────────────────────────────────────────────────────
507
476
  security-quality)
508
477
  [ "$TOOL_NAME" = "Write" ] || [ "$TOOL_NAME" = "Edit" ] || exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "odd-studio",
3
- "version": "3.7.5",
3
+ "version": "3.7.7",
4
4
  "description": "Outcome-Driven Development for AI coding agents — a planning and build harness for domain experts building serious software with AI. Works with Claude Code, OpenCode, and Codex.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -47,7 +47,6 @@
47
47
  "chalk": "^5.3.0",
48
48
  "commander": "^12.0.0",
49
49
  "fs-extra": "^11.2.0",
50
- "inquirer": "^10.0.0",
51
50
  "ora": "^8.0.1"
52
51
  },
53
52
  "devDependencies": {
@@ -1,7 +1,56 @@
1
+ import { existsSync, readFileSync } from 'fs';
1
2
  import { resolve } from 'path';
2
- import { checkBriefQuality, checkCodeElegance, checkOutcomeQuality, checkPersonaQuality, checkSecurityBaseline } from './plugin-quality-checks.js';
3
- import { isBriefFile, isOutcomeFile, isPersonaFile, isSourceCodePath, isSrcFile } from './plugin-paths.js';
4
- import { markerExists, markerValid, removeMarker, touchMarker } from './plugin-markers.js';
3
+ import {
4
+ checkBriefQuality,
5
+ checkCodeElegance,
6
+ checkOutcomeQuality,
7
+ checkPersonaQuality,
8
+ checkSecurityBaseline,
9
+ } from './plugin-quality-checks.js';
10
+ import {
11
+ isBriefFile,
12
+ isOutcomeFile,
13
+ isPersonaFile,
14
+ isSourceCodePath,
15
+ isSrcFile,
16
+ } from './plugin-paths.js';
17
+ import { markerExists, markerValid, touchMarker } from './plugin-markers.js';
18
+
19
+ const HALF_HOUR = 1800000;
20
+ const TWO_HOURS = 7200000;
21
+ const ONE_DAY = 86400000;
22
+ const ONE_HOUR = 3600000;
23
+ const TWO_MINUTES = 120000;
24
+
25
+ function readMarker(name) {
26
+ return resolve(process.cwd(), '.odd', name);
27
+ }
28
+
29
+ function isVerifiedUpgrade(event, filePath) {
30
+ if (!filePath.includes('state.json')) return false;
31
+
32
+ if (event.tool?.name === 'Write') {
33
+ const next = event.input?.content || '';
34
+ const nextCount = (next.match(/"buildStatus"\s*:\s*"verified"/g) || []).length;
35
+ const prevCount = existsSync(filePath)
36
+ ? (readFileSync(filePath, 'utf8').match(/"buildStatus"\s*:\s*"verified"/g) || []).length
37
+ : 0;
38
+ return nextCount > prevCount;
39
+ }
40
+
41
+ const previous = event.input?.old_string || '';
42
+ const next = event.input?.new_string || '';
43
+ return (next.match(/"verified"/g) || []).length > (previous.match(/"verified"/g) || []).length;
44
+ }
45
+
46
+ function blocksBuildPhaseTransition(newContent) {
47
+ if (!/"currentPhase"\s*:\s*"build"/.test(newContent)) return false;
48
+ return (
49
+ !/"techStackDecided"\s*:\s*true/.test(newContent) ||
50
+ !/"designApproachDecided"\s*:\s*true/.test(newContent) ||
51
+ !/"architectureDocGenerated"\s*:\s*true/.test(newContent)
52
+ );
53
+ }
5
54
 
6
55
  export function createBeforeHook(readState) {
7
56
  return (event) => {
@@ -12,63 +61,58 @@ export function createBeforeHook(readState) {
12
61
  const filePath = event.input?.file_path || event.input?.path || '';
13
62
 
14
63
  if (toolName === 'Agent' && state.currentPhase === 'build') {
15
- if (!state.briefConfirmed) {
16
- const prompt = (event.input?.prompt || '').slice(0, 200).toLowerCase();
17
- if (!prompt.match(/session.brief|session-brief|generate.*brief|write.*brief/)) {
18
- return { blocked: true, message: 'ODD BRIEF GATE: Build agents blocked. The session brief has not been confirmed. Run /odd-sync first.' };
19
- }
64
+ const prompt = (event.input?.prompt || '').slice(0, 300).toLowerCase();
65
+
66
+ if (!state.briefConfirmed && !prompt.match(/session.brief|session-brief|generate.*brief|write.*brief/)) {
67
+ return { blocked: true, message: 'ODD BRIEF GATE: Build agents blocked session brief not confirmed. Generate and confirm the brief first.' };
20
68
  }
21
69
 
22
- if (state.briefConfirmed && !markerValid(resolve(process.cwd(), '.odd', '.odd-flow-phase-synced'), 1800000)) {
70
+ if (state.briefConfirmed && !markerValid(readMarker('.odd-flow-phase-synced'), HALF_HOUR)) {
23
71
  return { blocked: true, message: 'ODD BUILD GATE: Brief confirmed but odd-flow not synced. Run /odd-sync first.' };
24
72
  }
25
73
 
26
- if (state.briefConfirmed && !markerValid(resolve(process.cwd(), '.odd', '.odd-flow-agents-ready'), 7200000)) {
27
- const prompt = (event.input?.prompt || '').slice(0, 300).toLowerCase();
28
- if (!prompt.match(/session.brief|session-brief|generate.*brief|odd-sync|fix.*hook|update.*hook/)) {
29
- return { blocked: true, message: 'ODD BUILD GATE: Task agents blocked — odd-flow swarm not initialised. Run the full swarm init sequence.' };
30
- }
74
+ if (markerValid(readMarker('.odd-quick-fix'), ONE_HOUR)) return;
75
+
76
+ if (
77
+ state.briefConfirmed &&
78
+ !markerValid(readMarker('.odd-flow-agents-ready'), TWO_HOURS) &&
79
+ !prompt.match(/session.brief|session-brief|generate.*brief|odd-sync|fix.*hook|update.*hook/)
80
+ ) {
81
+ return { blocked: true, message: 'ODD BUILD GATE: Task agents blocked — odd-flow swarm not initialised. Run the full swarm init sequence.' };
31
82
  }
32
83
  }
33
84
 
34
85
  if ((toolName === 'Write' || toolName === 'Edit') && state.currentPhase === 'build' && isSourceCodePath(filePath)) {
35
- if (!markerValid(resolve(process.cwd(), '.odd', '.odd-flow-swarm-active'), 3600000)) {
86
+ if (!markerValid(readMarker('.odd-flow-swarm-active'), ONE_DAY)) {
36
87
  return { blocked: true, message: `ODD SWARM WRITE GATE: Source writes blocked — no active build session. Run *build first. File: ${filePath}` };
37
88
  }
38
- if (!markerValid(resolve(process.cwd(), '.odd', '.odd-flow-agent-token'), 120000)) {
39
- return { blocked: true, message: `ODD SWARM WRITE GATE: Source writes blocked — no agent write token. Only Task agents can write source code, not the orchestrator. Dispatch work to agents instead. File: ${filePath}` };
89
+
90
+ if (state.debugSession || markerValid(readMarker('.odd-quick-fix'), ONE_HOUR)) return;
91
+
92
+ if (!markerValid(readMarker('.odd-flow-agent-token'), TWO_MINUTES)) {
93
+ return { blocked: true, message: `ODD SWARM WRITE GATE: Source writes blocked — no agent write token. Only Task agents can write source code, not the orchestrator. File: ${filePath}` };
40
94
  }
41
95
  }
42
96
 
43
97
  if ((toolName === 'Edit' || toolName === 'Write') && filePath.includes('state.json')) {
44
98
  const newContent = event.input?.new_string || event.input?.content || '';
45
- if (newContent.includes('"verified"') && state.buildMode === 'debug') {
46
- return { blocked: true, message: 'ODD VERIFY GATE: Cannot mark outcomes as verified while debug mode is active. Return buildMode to verify first.' };
47
- }
48
- if (newContent.includes('"verified"') && (markerExists('.checkpoint-dirty') || !markerExists('.checkpoint-clear'))) {
49
- return { blocked: true, message: 'ODD CHECKPOINT GATE: Cannot mark outcomes as verified until a fresh Checkpoint scan clears the latest source changes.' };
50
- }
51
- if (newContent.includes('"verified"') && !state.verificationConfirmed) {
52
- return { blocked: true, message: 'ODD VERIFY GATE: Cannot mark outcomes as verified. Walk through the verification checklist first.' };
53
- }
54
- if (/"briefConfirmed"\s*:\s*true/.test(newContent) && !markerExists('.odd-flow-brief-stored')) {
55
- return { blocked: true, message: 'ODD CONFIRM GATE: Brief not stored in odd-flow memory. Store it first.' };
99
+
100
+ if (blocksBuildPhaseTransition(newContent)) {
101
+ return { blocked: true, message: 'ODD PLAN COMPLETE GATE: Finish technical architecture, design approach, and architecture docs before entering build phase.' };
56
102
  }
57
- }
58
103
 
59
- if (toolName === 'Bash' && state.currentPhase === 'build') {
60
- const cmd = event.input?.command || '';
61
- if (/git\s+commit/.test(cmd)) {
104
+ if (isVerifiedUpgrade(event, filePath)) {
62
105
  if (state.buildMode === 'debug') {
63
- return { blocked: true, message: 'ODD DEBUG GATE: Commits are blocked while debug mode is active. Finish the fix and return to verification first.' };
64
- }
65
- if (markerExists('.checkpoint-dirty') || !markerExists('.checkpoint-clear')) {
66
- return { blocked: true, message: 'ODD CHECKPOINT GATE: Commit blocked — run Checkpoint and clear the latest source changes first.' };
106
+ return { blocked: true, message: 'ODD VERIFY GATE: Cannot mark outcomes as verified while debug mode is active. Return to verification first.' };
67
107
  }
68
- if (!markerExists('.odd-flow-state-ready')) {
69
- return { blocked: true, message: 'ODD COMMIT GATE: Commit blocked — odd-flow state not stored. Call mcp__odd-flow__memory_store key=odd-project-state first.' };
108
+
109
+ if (!state.verificationConfirmed) {
110
+ return { blocked: true, message: 'ODD VERIFY GATE: Cannot mark outcomes as verified. Walk through the verification checklist first.' };
70
111
  }
71
- removeMarker('.odd-flow-state-ready');
112
+ }
113
+
114
+ if (/"briefConfirmed"\s*:\s*true/.test(newContent) && !markerExists('.odd-flow-brief-stored')) {
115
+ return { blocked: true, message: 'ODD CONFIRM GATE: Brief not stored in odd-flow memory. Store it first.' };
72
116
  }
73
117
  }
74
118
  };
@@ -89,44 +133,26 @@ export function createAfterHook(readState, readLastCommit, writeState) {
89
133
  if (toolName === 'Write' && isBriefFile(filePath)) warnings.push(...checkBriefQuality(filePath));
90
134
 
91
135
  const state = readState();
92
- if ((toolName === 'Write' || toolName === 'Edit') && isSrcFile(filePath) && state?.currentPhase === 'build') {
93
- touchMarker('.checkpoint-dirty');
94
- removeMarker('.checkpoint-clear');
95
- }
96
136
 
97
137
  if (toolName === 'Bash') {
98
138
  const cmd = event.input?.command || '';
99
- const state = readState();
100
- if (state?.currentPhase === 'build' && /@darrenjcoxon\/vibeguard|vibeguard/.test(cmd) && event.exitCode === 0) {
101
- touchMarker('.checkpoint-clear');
102
- removeMarker('.checkpoint-dirty');
103
- }
104
- if (/git\s+commit/.test(cmd) && event.exitCode === 0) {
105
- if (state) {
106
- state.lastCommit = readLastCommit();
107
- state.lastSaved = new Date().toISOString();
108
- state.lastCommitAt = state.lastSaved;
109
- writeState(state);
110
- touchMarker('.odd-flow-phase-synced');
111
- touchMarker('.odd-flow-state-dirty');
112
- }
139
+ if (/git\s+commit/.test(cmd) && event.exitCode === 0 && state) {
140
+ state.lastCommit = readLastCommit();
141
+ state.lastSaved = new Date().toISOString();
142
+ state.lastCommitAt = state.lastSaved;
143
+ writeState(state);
144
+ touchMarker('.odd-flow-phase-synced');
113
145
  }
114
146
  }
115
147
 
116
- if (toolName === 'mcp__odd-flow__memory_store') {
117
- const state = readState();
118
- if (state?.currentPhase === 'build' && event.input?.key === 'odd-project-state') {
119
- touchMarker('.odd-flow-state-ready');
120
- removeMarker('.odd-flow-state-dirty');
121
- }
148
+ if (toolName === 'mcp__odd-flow__memory_store' && state?.currentPhase === 'build') {
149
+ if (event.input?.key?.startsWith('odd-session-brief-')) touchMarker('.odd-flow-brief-stored');
122
150
  }
123
151
 
124
- if (toolName === 'mcp__odd-flow__coordination_sync') {
125
- const state = readState();
126
- if (state?.currentPhase === 'build') {
127
- touchMarker('.odd-flow-agents-ready');
128
- touchMarker('.odd-flow-phase-synced');
129
- }
152
+ if (toolName === 'mcp__odd-flow__coordination_sync' && state?.currentPhase === 'build') {
153
+ touchMarker('.odd-flow-swarm-active');
154
+ touchMarker('.odd-flow-agents-ready');
155
+ touchMarker('.odd-flow-phase-synced');
130
156
  }
131
157
 
132
158
  if (warnings.length > 0) return { warnings };
@@ -137,20 +163,19 @@ export function createChatHook(readState) {
137
163
  return () => {
138
164
  const state = readState();
139
165
  if (!state || state.currentPhase !== 'build') return;
166
+
140
167
  if (state.buildMode === 'debug') {
141
- return { warnings: [`ODD DEBUG MODE: ${state.debugStrategy || 'Choose a strategy'} — keep the work inside the active outcome and return to verification when the fix is ready.`] };
142
- }
143
- if (markerExists('.odd-flow-state-dirty')) {
144
- return { warnings: ['ODD STATE NOT SAVED: A git commit was made but state was not stored to odd-flow. Store it now.'] };
145
- }
146
- if (markerExists('.checkpoint-dirty')) {
147
- return { warnings: ['ODD CHECKPOINT DIRTY: Source changes have not passed a fresh security scan yet. Run Checkpoint before verification or commit.'] };
168
+ return {
169
+ warnings: [
170
+ `ODD DEBUG MODE: ${state.debugStrategy || 'Choose a strategy'} — keep the fix inside the active outcome and return to verification when ready.`,
171
+ ],
172
+ };
148
173
  }
149
174
 
150
- const swarmPath = resolve(process.cwd(), '.odd', '.odd-flow-swarm-active');
151
- if (!markerValid(swarmPath, 3600000)) {
175
+ const swarmPath = readMarker('.odd-flow-swarm-active');
176
+ if (!markerValid(swarmPath, ONE_DAY)) {
152
177
  const stale = markerExists('.odd-flow-swarm-active') ? ' (marker expired — re-init required)' : '';
153
- return { warnings: [`ODD SWARM GUARD: You are in build phase. The odd-flow swarm must be initialised before any build action.${stale}`] };
178
+ return { warnings: [`ODD SWARM GUARD: You are in build phase. The odd-flow swarm must be initialised before build work.${stale}`] };
154
179
  }
155
180
  };
156
181
  }
@@ -10,76 +10,36 @@ export const COMMANDS = [
10
10
  {
11
11
  name: 'odd-plan',
12
12
  description: 'Start or continue the planning phase — personas, outcomes, contracts, and Master Implementation Plan',
13
- body: 'You are executing the ODD Studio `*plan` command.\n\nRead this file now:\n- `.opencode/odd/SKILL.md` — the full ODD Studio coach and planning protocol\n\nThen execute the `*plan` protocol exactly as documented, starting from the state check.',
13
+ body: 'You are executing the ODD Studio `*plan` command.\n\nRead this file now:\n- `.opencode/odd/odd-plan/SKILL.md` — the planning command guide\n\nThen execute the `*plan` protocol exactly as documented, starting from the state check.',
14
14
  },
15
15
  {
16
16
  name: 'odd-build',
17
17
  description: 'Start or continue a build session — reads project state and executes the build protocol',
18
- body: 'You are executing the ODD Studio `*build` command.\n\nRead these two files now:\n1. `.opencode/odd/SKILL.md` — the full ODD Studio coach and build protocol\n2. `.opencode/odd/docs/build/build-protocol.md` — the Build Protocol detail\n\nThen execute the `*build` protocol exactly as documented in those files, starting from the state check.',
18
+ body: 'You are executing the ODD Studio `*build` command.\n\nRead this file now:\n- `.opencode/odd/odd-build/SKILL.md` — the build command guide\n\nThen execute the `*build` protocol exactly as documented, starting from the state check.',
19
19
  },
20
20
  {
21
21
  name: 'odd-debug',
22
22
  description: 'Start or continue an in-flow ODD debugging session — selects the correct debug approach and returns to verification',
23
- body: 'You are executing the ODD Studio `*debug` command.\n\nRead these two files now:\n1. `.opencode/odd/SKILL.md` — the full ODD Studio coach and build protocol\n2. `.opencode/odd/docs/build/debug-protocol.md` the Debug Protocol detail\n\nBefore you inspect code or propose a fix, classify the failure into exactly one strategy:\n- `ui-behaviour` for visible interface-only failures\n- `full-stack` for browser-to-route-to-service-to-data failures\n- `auth-security` for auth, permissions, trust boundaries, uploads, or webhooks\n- `integration-contract` for producer-consumer or contract mismatches\n- `background-process` for jobs, queues, webhooks, or async delivery\n- `performance-state` for stale data, races, caching, or timing-sensitive defects\n\nIf the evidence is mixed, gather more evidence first and then choose the narrowest matching strategy. Do not guess. Do not apply a quick fix. Then execute the `*debug` protocol exactly as documented in those files, starting from the state check and recording the chosen strategy in `.odd/state.json` before any fix is attempted.',
23
+ body: 'You are executing the ODD Studio `*debug` command.\n\nRead this file now:\n- `.opencode/odd/odd-debug/SKILL.md` — the debug command guide\n\nThen execute the `*debug` protocol exactly as documented, starting from the state check.',
24
24
  },
25
25
  {
26
26
  name: 'odd-swarm',
27
27
  description: 'Build all independent outcomes in the current phase simultaneously using odd-flow parallel agents',
28
- body: 'You are executing the ODD Studio `*swarm` command.\n\nRead these two files now:\n1. `.opencode/odd/SKILL.md` — the full ODD Studio coach and build protocol\n2. `.opencode/odd/docs/build/build-protocol.md` — the Build Protocol detail\n\nThen execute the `*swarm` protocol exactly as documented in those files, starting from the state check.',
28
+ body: 'You are executing the ODD Studio `*swarm` command.\n\nRead this file now:\n- `.opencode/odd/odd-swarm/SKILL.md` — the swarm command guide\n\nThen execute the `*swarm` protocol exactly as documented, starting from the state check.',
29
29
  },
30
30
  {
31
31
  name: 'odd-status',
32
32
  description: 'Show full project state, phase progress, and what comes next',
33
- body: 'You are executing the ODD Studio `*status` command.\n\nRead this file now:\n- `.opencode/odd/SKILL.md` — the full ODD Studio coach\n\nThen execute the `*status` protocol exactly as documented, starting from the state check.',
33
+ body: 'You are executing the ODD Studio `*status` command.\n\nRead this file now:\n- `.opencode/odd/odd-status/SKILL.md` — the status command guide\n\nThen execute the `*status` protocol exactly as documented, starting from the state check.',
34
34
  },
35
35
  {
36
36
  name: 'odd-deploy',
37
37
  description: 'Verify all outcomes are confirmed, then deploy the current phase to production',
38
- body: 'You are executing the ODD Studio `*deploy` command.\n\nRead this file now:\n- `.opencode/odd/SKILL.md` — the full ODD Studio coach\n\nThen execute the `*deploy` protocol exactly as documented, starting from the state check.',
38
+ body: 'You are executing the ODD Studio `*deploy` command.\n\nRead this file now:\n- `.opencode/odd/odd-deploy/SKILL.md` — the deploy command guide\n\nThen execute the `*deploy` protocol exactly as documented, starting from the state check.',
39
39
  },
40
40
  {
41
41
  name: 'odd-sync',
42
42
  description: 'Sync ODD project state to odd-flow memory. MUST be run before building.',
43
- body: `You are syncing ODD project state to odd-flow memory. Follow these steps exactly:
44
-
45
- ## Step 1 — Read project state
46
-
47
- Read \`.odd/state.json\` and store the full contents in a variable.
48
-
49
- ## Step 2 — Store state in odd-flow
50
-
51
- Call \`mcp__odd-flow__memory_store\` with:
52
- - **key**: \`odd-project-state\`
53
- - **namespace**: \`odd-project\`
54
- - **value**: the full contents of \`.odd/state.json\`
55
-
56
- ## Step 3 — Read and store the current session brief
57
-
58
- Read \`sessionBriefCount\` from state.json. If it is greater than 0, read the file \`docs/session-brief-[N].md\` where N equals the sessionBriefCount value.
59
-
60
- If the brief file exists, call \`mcp__odd-flow__memory_store\` with:
61
- - **key**: \`odd-session-brief-[N]\` (replace [N] with the actual number)
62
- - **namespace**: \`odd-project\`
63
- - **value**: the full contents of the session brief file
64
-
65
- ## Step 4 — Create marker files
66
-
67
- Run via shell:
68
- \`\`\`bash
69
- touch .odd/.odd-flow-phase-synced
70
- \`\`\`
71
-
72
- If a session brief was stored in Step 3, also run:
73
- \`\`\`bash
74
- touch .odd/.odd-flow-brief-stored
75
- \`\`\`
76
-
77
- ## Step 5 — Confirm
78
-
79
- Report to the user:
80
-
81
- > odd-flow synced. Phase [X] state and brief [N] stored. Build agents unlocked.
82
-
83
- Replace [X] with \`currentBuildPhase\` from state.json and [N] with the session brief number.`,
43
+ body: 'You are executing the ODD Studio `*sync` command.\n\nRead this file now:\n- `.opencode/odd/odd-sync/SKILL.md` the sync command guide\n\nThen execute the sync steps exactly as documented.',
84
44
  },
85
45
  ];