project-iris 0.0.12 → 0.0.14

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 (189) hide show
  1. package/README.md +214 -323
  2. package/bin/cli.js +21 -0
  3. package/flows/aidlc/README.md +372 -0
  4. package/flows/aidlc/agents/construction-agent.md +79 -0
  5. package/flows/aidlc/agents/inception-agent.md +97 -0
  6. package/flows/aidlc/agents/master-agent.md +61 -0
  7. package/flows/aidlc/agents/operations-agent.md +89 -0
  8. package/flows/aidlc/commands/construction-agent.md +63 -0
  9. package/flows/aidlc/commands/inception-agent.md +55 -0
  10. package/flows/aidlc/commands/master-agent.md +47 -0
  11. package/flows/aidlc/commands/operations-agent.md +77 -0
  12. package/flows/aidlc/context-config.yaml +67 -0
  13. package/flows/aidlc/memory-bank.yaml +104 -0
  14. package/flows/aidlc/quick-start.md +322 -0
  15. package/flows/aidlc/skills/construction/bolt-list.md +163 -0
  16. package/flows/aidlc/skills/construction/bolt-replan.md +345 -0
  17. package/flows/aidlc/skills/construction/bolt-start.md +442 -0
  18. package/flows/aidlc/skills/construction/bolt-status.md +185 -0
  19. package/flows/aidlc/skills/construction/navigator.md +196 -0
  20. package/flows/aidlc/skills/inception/bolt-plan.md +372 -0
  21. package/flows/aidlc/skills/inception/context.md +171 -0
  22. package/flows/aidlc/skills/inception/intent-create.md +211 -0
  23. package/flows/aidlc/skills/inception/intent-list.md +124 -0
  24. package/flows/aidlc/skills/inception/navigator.md +207 -0
  25. package/flows/aidlc/skills/inception/requirements.md +227 -0
  26. package/flows/aidlc/skills/inception/review.md +248 -0
  27. package/flows/aidlc/skills/inception/story-create.md +304 -0
  28. package/flows/aidlc/skills/inception/units.md +278 -0
  29. package/flows/aidlc/skills/master/analyze-context.md +239 -0
  30. package/flows/aidlc/skills/master/answer-question.md +141 -0
  31. package/flows/aidlc/skills/master/explain-flow.md +158 -0
  32. package/flows/aidlc/skills/master/project-init.md +281 -0
  33. package/flows/aidlc/skills/master/route-request.md +126 -0
  34. package/flows/aidlc/skills/operations/build.md +237 -0
  35. package/flows/aidlc/skills/operations/deploy.md +259 -0
  36. package/flows/aidlc/skills/operations/monitor.md +265 -0
  37. package/flows/aidlc/skills/operations/navigator.md +209 -0
  38. package/flows/aidlc/skills/operations/verify.md +224 -0
  39. package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt.md +3 -3
  40. package/{dist → flows/aidlc}/templates/construction/bolt-types/spike-bolt.md +2 -2
  41. package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
  42. package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
  43. package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
  44. package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
  45. package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
  46. package/flows/aidlc/templates/inception/project/README.md +55 -0
  47. package/flows/aidlc/templates/standards/catalog.yaml +345 -0
  48. package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
  49. package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
  50. package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
  51. package/lib/InstallerFactory.js +36 -0
  52. package/lib/analytics/env-detector.js +92 -0
  53. package/lib/analytics/index.js +22 -0
  54. package/lib/analytics/machine-id.js +33 -0
  55. package/lib/analytics/tracker.js +232 -0
  56. package/lib/cli-utils.js +342 -0
  57. package/lib/constants.js +32 -0
  58. package/lib/installer.js +402 -0
  59. package/lib/installers/AntigravityInstaller.js +22 -0
  60. package/lib/installers/ClaudeInstaller.js +85 -0
  61. package/lib/installers/ClineInstaller.js +21 -0
  62. package/lib/installers/CodexInstaller.js +21 -0
  63. package/lib/installers/CopilotInstaller.js +113 -0
  64. package/lib/installers/CursorInstaller.js +63 -0
  65. package/lib/installers/GeminiInstaller.js +75 -0
  66. package/lib/installers/KiroInstaller.js +22 -0
  67. package/lib/installers/OpenCodeInstaller.js +22 -0
  68. package/lib/installers/RooInstaller.js +22 -0
  69. package/lib/installers/ToolInstaller.js +73 -0
  70. package/lib/installers/WindsurfInstaller.js +22 -0
  71. package/lib/markdown-validator.ts +175 -0
  72. package/lib/yaml-validator.ts +99 -0
  73. package/package.json +105 -32
  74. package/scripts/artifact-validator.js +594 -0
  75. package/scripts/bolt-complete.js +606 -0
  76. package/scripts/status-integrity.js +598 -0
  77. package/dist/bridge/agent-runner.js +0 -190
  78. package/dist/bridge/connector-factory.js +0 -31
  79. package/dist/bridge/connectors/antigravity-connector.js +0 -18
  80. package/dist/bridge/connectors/cursor-connector.js +0 -31
  81. package/dist/bridge/connectors/in-process-connector.js +0 -29
  82. package/dist/bridge/connectors/vscode-connector.js +0 -31
  83. package/dist/bridge/connectors/windsurf-connector.js +0 -23
  84. package/dist/bridge/filesystem-connector.js +0 -110
  85. package/dist/bridge/helper.js +0 -203
  86. package/dist/bridge/types.js +0 -10
  87. package/dist/cli.js +0 -40
  88. package/dist/commands/ask.js +0 -259
  89. package/dist/commands/bridge.js +0 -88
  90. package/dist/commands/create.js +0 -25
  91. package/dist/commands/develop.js +0 -141
  92. package/dist/commands/doctor.js +0 -102
  93. package/dist/commands/flow.js +0 -301
  94. package/dist/commands/framework.js +0 -273
  95. package/dist/commands/generate.js +0 -59
  96. package/dist/commands/install.js +0 -100
  97. package/dist/commands/pack.js +0 -33
  98. package/dist/commands/phase.js +0 -38
  99. package/dist/commands/run.js +0 -199
  100. package/dist/commands/status.js +0 -114
  101. package/dist/commands/uninstall.js +0 -14
  102. package/dist/commands/use.js +0 -20
  103. package/dist/commands/validate.js +0 -102
  104. package/dist/framework/framework-loader.js +0 -97
  105. package/dist/framework/framework-paths.js +0 -48
  106. package/dist/framework/framework-types.js +0 -15
  107. package/dist/iris/artifact-checker.js +0 -78
  108. package/dist/iris/artifacts/config.js +0 -68
  109. package/dist/iris/artifacts/generator.js +0 -88
  110. package/dist/iris/artifacts/types.js +0 -1
  111. package/dist/iris/bundle.js +0 -44
  112. package/dist/iris/doctrine/collector.js +0 -124
  113. package/dist/iris/fixer.js +0 -149
  114. package/dist/iris/flows/manifest.js +0 -124
  115. package/dist/iris/framework-context.js +0 -49
  116. package/dist/iris/framework-manager.js +0 -215
  117. package/dist/iris/fs/atomic.js +0 -22
  118. package/dist/iris/guard.js +0 -38
  119. package/dist/iris/importers/index.js +0 -9
  120. package/dist/iris/importers/types.js +0 -8
  121. package/dist/iris/importers/writer.js +0 -139
  122. package/dist/iris/include.js +0 -49
  123. package/dist/iris/installer.js +0 -334
  124. package/dist/iris/interactive/env.js +0 -21
  125. package/dist/iris/interactive/intent-interview.js +0 -345
  126. package/dist/iris/interactive/intent-schema.js +0 -28
  127. package/dist/iris/interactive/interview-io.js +0 -22
  128. package/dist/iris/interview/config.js +0 -71
  129. package/dist/iris/interview/types.js +0 -16
  130. package/dist/iris/interview/utils.js +0 -38
  131. package/dist/iris/manifest.js +0 -54
  132. package/dist/iris/packer.js +0 -325
  133. package/dist/iris/parsers/unit-parser.js +0 -43
  134. package/dist/iris/paths.js +0 -18
  135. package/dist/iris/policy.js +0 -133
  136. package/dist/iris/proc.js +0 -56
  137. package/dist/iris/report.js +0 -53
  138. package/dist/iris/resolver.js +0 -66
  139. package/dist/iris/router.js +0 -114
  140. package/dist/iris/routes.js +0 -189
  141. package/dist/iris/run-state.js +0 -146
  142. package/dist/iris/state.js +0 -113
  143. package/dist/iris/templates.js +0 -70
  144. package/dist/iris/tmp.js +0 -24
  145. package/dist/iris/uninstaller.js +0 -181
  146. package/dist/iris/utils/interpolate.js +0 -42
  147. package/dist/iris/validator.js +0 -391
  148. package/dist/iris/workflow/config.js +0 -51
  149. package/dist/iris/workflow/engine.js +0 -129
  150. package/dist/iris/workflow/steps.js +0 -448
  151. package/dist/iris/workflow/types.js +0 -1
  152. package/dist/iris_bundle/frameworks/iris-core/framework.yaml +0 -9
  153. package/dist/iris_bundle/frameworks/iris-core/memory/memory-bank.yaml +0 -1
  154. package/dist/iris_bundle/frameworks/iris-core/policy.yaml +0 -7
  155. package/dist/iris_bundle/frameworks/iris-core/templates/config/memory-bank.yaml +0 -1
  156. package/dist/iris_bundle/frameworks/iris-core/templates/construction/bolt-types/spike-bolt.md +0 -240
  157. package/dist/lib.js +0 -96
  158. package/dist/templates/construction/bolt-template.md +0 -226
  159. package/dist/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -49
  160. package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -55
  161. package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -67
  162. package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -62
  163. package/dist/templates/construction/bolt-types/ddd-construction-bolt.md +0 -528
  164. package/dist/templates/construction/bolt-types/simple-construction-bolt.md +0 -347
  165. package/dist/templates/inception/requirements-template.md +0 -144
  166. package/dist/templates/inception/stories-template.md +0 -38
  167. package/dist/templates/inception/story-template.md +0 -147
  168. package/dist/templates/inception/system-context-template.md +0 -29
  169. package/dist/templates/inception/unit-brief-template.md +0 -177
  170. package/dist/templates/inception/units-template.md +0 -52
  171. package/dist/utils/exit-codes.js +0 -7
  172. package/dist/utils/logo.js +0 -17
  173. package/dist/workflows/bolt-execution.js +0 -238
  174. package/dist/workflows/bolt-plan.js +0 -221
  175. package/dist/workflows/intent-inception.js +0 -285
  176. package/dist/workflows/memory-bank-generator.js +0 -180
  177. package/dist/workflows/reporting.js +0 -74
  178. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-template.md +0 -0
  179. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -0
  180. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -0
  181. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -0
  182. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -0
  183. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/simple-construction-bolt.md +0 -0
  184. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/requirements-template.md +0 -0
  185. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/stories-template.md +0 -0
  186. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/story-template.md +0 -0
  187. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/system-context-template.md +0 -0
  188. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/unit-brief-template.md +0 -0
  189. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/units-template.md +0 -0
@@ -0,0 +1,598 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * iris Status Integrity Script
5
+ *
6
+ * Checks and fixes status inconsistencies across the artifact hierarchy.
7
+ * Status must cascade correctly: Bolt complete → Stories complete → Unit complete → Intent complete
8
+ *
9
+ * Usage:
10
+ * node .iris/scripts/status-integrity.js
11
+ * node .iris/scripts/status-integrity.js --fix
12
+ *
13
+ * Cross-platform: Works on Linux, macOS, Windows via Node.js
14
+ */
15
+
16
+ const fs = require('fs-extra');
17
+ const path = require('path');
18
+ const yaml = require('js-yaml');
19
+
20
+ // Theme colors for output
21
+ const colors = {
22
+ reset: '\x1b[0m',
23
+ green: '\x1b[32m',
24
+ red: '\x1b[31m',
25
+ yellow: '\x1b[33m',
26
+ blue: '\x1b[34m',
27
+ dim: '\x1b[90m',
28
+ bright: '\x1b[1m'
29
+ };
30
+
31
+ // Memory bank paths (relative to project root)
32
+ const MEMORY_BANK_DIR = 'memory-bank';
33
+ const BOLTS_DIR = path.join(MEMORY_BANK_DIR, 'bolts');
34
+ const INTENTS_DIR = path.join(MEMORY_BANK_DIR, 'intents');
35
+ const MAINTENANCE_LOG = path.join(MEMORY_BANK_DIR, 'maintenance-log.md');
36
+
37
+ /**
38
+ * Extract frontmatter from a markdown file
39
+ */
40
+ function extractFrontmatter(content) {
41
+ const match = content.match(/^---\n([\s\S]+?)\n---/);
42
+ if (!match) return null;
43
+
44
+ try {
45
+ return yaml.load(match[1]);
46
+ } catch (error) {
47
+ console.error(`${colors.red}Error parsing YAML frontmatter:${colors.reset}`, error.message);
48
+ return null;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Update frontmatter in a markdown file
54
+ */
55
+ function updateFrontmatter(content, newFrontmatter) {
56
+ const match = content.match(/^---\n([\s\S]+?)\n---/);
57
+ if (!match) return null;
58
+
59
+ const newYaml = yaml.dump(newFrontmatter, {
60
+ lineWidth: -1,
61
+ noRefs: true,
62
+ quotingType: '"',
63
+ forceQuotes: false,
64
+ sortKeys: false
65
+ }).trim();
66
+
67
+ return `---\n${newYaml}\n---${content.slice(match[0].length)}`;
68
+ }
69
+
70
+ /**
71
+ * Format timestamp as ISO 8601
72
+ * Format: YYYY-MM-DDTHH:MM:SSZ (no milliseconds, per memory-bank.yaml convention)
73
+ */
74
+ function getTimestamp() {
75
+ const date = new Date();
76
+ // Format to ISO 8601 without milliseconds
77
+ return date.toISOString().replace(/\.\d+Z$/, 'Z');
78
+ }
79
+
80
+ /**
81
+ * Read a bolt file and extract metadata
82
+ */
83
+ async function readBolt(boltDir) {
84
+ const boltPath = path.join(BOLTS_DIR, boltDir, 'bolt.md');
85
+
86
+ if (!await fs.pathExists(boltPath)) {
87
+ return null;
88
+ }
89
+
90
+ const content = await fs.readFile(boltPath, 'utf8');
91
+ const frontmatter = extractFrontmatter(content);
92
+
93
+ if (!frontmatter) {
94
+ return null;
95
+ }
96
+
97
+ return {
98
+ id: frontmatter.id || boltDir.replace(/\/$/, ''),
99
+ dir: boltDir,
100
+ path: boltPath,
101
+ content,
102
+ frontmatter
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Find a story file by ID
108
+ */
109
+ async function findStoryFile(intent, unit, storyId) {
110
+ let storyPath = path.join(INTENTS_DIR, intent, 'units', unit, 'stories', `${storyId}.md`);
111
+
112
+ if (await fs.pathExists(storyPath)) {
113
+ return storyPath;
114
+ }
115
+
116
+ storyPath = path.join(INTENTS_DIR, intent, 'units', unit, 'stories', storyId);
117
+ if (await fs.pathExists(storyPath)) {
118
+ return storyPath;
119
+ }
120
+
121
+ return null;
122
+ }
123
+
124
+ /**
125
+ * Check story status consistency
126
+ */
127
+ async function checkStoryStatus(bolt) {
128
+ const stories = bolt.frontmatter.stories || [];
129
+ const intent = bolt.frontmatter.intent;
130
+ const unit = bolt.frontmatter.unit;
131
+ const boltComplete = bolt.frontmatter.status === 'complete';
132
+
133
+ const inconsistencies = [];
134
+
135
+ if (!boltComplete) {
136
+ return inconsistencies; // Only check if bolt is complete
137
+ }
138
+
139
+ for (const storyId of stories) {
140
+ const storyPath = await findStoryFile(intent, unit, storyId);
141
+
142
+ if (!storyPath) {
143
+ inconsistencies.push({
144
+ type: 'story',
145
+ path: `{intent}/${unit}/stories/${storyId}.md`,
146
+ current: 'file_not_found',
147
+ expected: 'complete',
148
+ reason: `Bolt ${bolt.id} is complete but story file not found`
149
+ });
150
+ continue;
151
+ }
152
+
153
+ const content = await fs.readFile(storyPath, 'utf8');
154
+ const frontmatter = extractFrontmatter(content);
155
+
156
+ if (!frontmatter) {
157
+ inconsistencies.push({
158
+ type: 'story',
159
+ path: storyPath,
160
+ current: 'invalid_frontmatter',
161
+ expected: 'complete',
162
+ reason: `Bolt ${bolt.id} is complete but story has no frontmatter`
163
+ });
164
+ continue;
165
+ }
166
+
167
+ const status = frontmatter.status || 'draft';
168
+ const implemented = frontmatter.implemented || false;
169
+
170
+ if (status !== 'complete' || !implemented) {
171
+ inconsistencies.push({
172
+ type: 'story',
173
+ path: storyPath,
174
+ current: `${status}${implemented ? '' : ' (not implemented)'}`,
175
+ expected: 'complete, implemented: true',
176
+ reason: `Bolt ${bolt.id} is complete but story is not marked complete`,
177
+ storyPath,
178
+ content
179
+ });
180
+ }
181
+ }
182
+
183
+ return inconsistencies;
184
+ }
185
+
186
+ /**
187
+ * Check unit status consistency
188
+ */
189
+ async function checkUnitStatus(unit, intent) {
190
+ const inconsistencies = [];
191
+ const unitBriefPath = path.join(INTENTS_DIR, intent, 'units', unit, 'unit-brief.md');
192
+
193
+ if (!await fs.pathExists(unitBriefPath)) {
194
+ return [];
195
+ }
196
+
197
+ const unitBriefContent = await fs.readFile(unitBriefPath, 'utf8');
198
+ const unitBriefFrontmatter = extractFrontmatter(unitBriefContent);
199
+
200
+ if (!unitBriefFrontmatter) {
201
+ return [];
202
+ }
203
+
204
+ const currentStatus = unitBriefFrontmatter.status || 'unknown';
205
+
206
+ // Find all bolts for this unit
207
+ const boltDirs = await fs.readdir(BOLTS_DIR).catch(() => []);
208
+ const unitBolts = [];
209
+
210
+ for (const boltDir of boltDirs) {
211
+ const boltPath = path.join(BOLTS_DIR, boltDir, 'bolt.md');
212
+ if (await fs.pathExists(boltPath)) {
213
+ const content = await fs.readFile(boltPath, 'utf8');
214
+ const frontmatter = extractFrontmatter(content);
215
+ if (frontmatter && frontmatter.unit === unit) {
216
+ unitBolts.push({
217
+ id: frontmatter.id || boltDir,
218
+ status: frontmatter.status || 'planned'
219
+ });
220
+ }
221
+ }
222
+ }
223
+
224
+ if (unitBolts.length === 0) {
225
+ return []; // No bolts, can't determine expected status
226
+ }
227
+
228
+ // Determine expected status
229
+ let expectedStatus;
230
+ const allComplete = unitBolts.every(b => b.status === 'complete');
231
+ const anyInProgress = unitBolts.some(b => b.status === 'in-progress');
232
+ const allPlanned = unitBolts.every(b => b.status === 'planned');
233
+
234
+ if (allComplete) {
235
+ expectedStatus = 'complete';
236
+ } else if (anyInProgress) {
237
+ expectedStatus = 'in-progress';
238
+ } else if (allPlanned) {
239
+ expectedStatus = 'stories-defined';
240
+ } else {
241
+ expectedStatus = 'stories-defined';
242
+ }
243
+
244
+ if (currentStatus !== expectedStatus) {
245
+ inconsistencies.push({
246
+ type: 'unit',
247
+ path: unitBriefPath,
248
+ current: currentStatus,
249
+ expected: expectedStatus,
250
+ reason: `Unit has ${unitBolts.length} bolts (${unitBolts.filter(b => b.status === 'complete').length}/${unitBolts.length} complete)`,
251
+ unitBriefContent
252
+ });
253
+ }
254
+
255
+ return inconsistencies;
256
+ }
257
+
258
+ /**
259
+ * Check intent status consistency
260
+ */
261
+ async function checkIntentStatus(intent) {
262
+ const inconsistencies = [];
263
+ const requirementsPath = path.join(INTENTS_DIR, intent, 'requirements.md');
264
+
265
+ if (!await fs.pathExists(requirementsPath)) {
266
+ return [];
267
+ }
268
+
269
+ const requirementsContent = await fs.readFile(requirementsPath, 'utf8');
270
+ const requirementsFrontmatter = extractFrontmatter(requirementsContent);
271
+
272
+ if (!requirementsFrontmatter) {
273
+ return [];
274
+ }
275
+
276
+ const currentStatus = requirementsFrontmatter.status || 'unknown';
277
+
278
+ // Find all units for this intent
279
+ const unitsDir = path.join(INTENTS_DIR, intent, 'units');
280
+ if (!await fs.pathExists(unitsDir)) {
281
+ return [];
282
+ }
283
+
284
+ const unitDirs = await fs.readdir(unitsDir).catch(() => []);
285
+ const unitStatuses = [];
286
+
287
+ for (const unitDir of unitDirs) {
288
+ const unitBriefPath = path.join(unitsDir, unitDir, 'unit-brief.md');
289
+ if (await fs.pathExists(unitBriefPath)) {
290
+ const content = await fs.readFile(unitBriefPath, 'utf8');
291
+ const frontmatter = extractFrontmatter(content);
292
+ if (frontmatter) {
293
+ unitStatuses.push({
294
+ unit: unitDir,
295
+ status: frontmatter.status || 'unknown'
296
+ });
297
+ }
298
+ }
299
+ }
300
+
301
+ if (unitStatuses.length === 0) {
302
+ return []; // No units, can't determine expected status
303
+ }
304
+
305
+ // Determine expected status
306
+ let expectedStatus;
307
+ const allComplete = unitStatuses.every(u => u.status === 'complete');
308
+ const anyInProgress = unitStatuses.some(u => u.status === 'in-progress');
309
+
310
+ if (allComplete) {
311
+ expectedStatus = 'complete';
312
+ } else if (anyInProgress) {
313
+ expectedStatus = 'construction';
314
+ } else {
315
+ expectedStatus = 'units-defined';
316
+ }
317
+
318
+ if (currentStatus !== expectedStatus) {
319
+ inconsistencies.push({
320
+ type: 'intent',
321
+ path: requirementsPath,
322
+ current: currentStatus,
323
+ expected: expectedStatus,
324
+ reason: `Intent has ${unitStatuses.length} units (${unitStatuses.filter(u => u.status === 'complete').length}/${unitStatuses.length} complete)`,
325
+ requirementsContent
326
+ });
327
+ }
328
+
329
+ return inconsistencies;
330
+ }
331
+
332
+ /**
333
+ * Fix a story status inconsistency
334
+ */
335
+ async function fixStoryStatus(inconsistency) {
336
+ const { storyPath, content } = inconsistency;
337
+
338
+ if (!storyPath || !content) {
339
+ return false;
340
+ }
341
+
342
+ const frontmatter = extractFrontmatter(content);
343
+ if (!frontmatter) {
344
+ return false;
345
+ }
346
+
347
+ const newFrontmatter = {
348
+ ...frontmatter,
349
+ status: 'complete',
350
+ implemented: true
351
+ };
352
+
353
+ const newContent = updateFrontmatter(content, newFrontmatter);
354
+ if (!newContent) {
355
+ return false;
356
+ }
357
+
358
+ await fs.writeFile(storyPath, newContent, 'utf8');
359
+ return true;
360
+ }
361
+
362
+ /**
363
+ * Fix a unit status inconsistency
364
+ */
365
+ async function fixUnitStatus(inconsistency) {
366
+ const { path: unitBriefPath, unitBriefContent, expected } = inconsistency;
367
+
368
+ if (!unitBriefPath || !unitBriefContent) {
369
+ return false;
370
+ }
371
+
372
+ const frontmatter = extractFrontmatter(unitBriefContent);
373
+ if (!frontmatter) {
374
+ return false;
375
+ }
376
+
377
+ const newFrontmatter = {
378
+ ...frontmatter,
379
+ status: expected
380
+ };
381
+
382
+ const newContent = updateFrontmatter(unitBriefContent, newFrontmatter);
383
+ if (!newContent) {
384
+ return false;
385
+ }
386
+
387
+ await fs.writeFile(unitBriefPath, newContent, 'utf8');
388
+ return true;
389
+ }
390
+
391
+ /**
392
+ * Fix an intent status inconsistency
393
+ */
394
+ async function fixIntentStatus(inconsistency) {
395
+ const { path: requirementsPath, requirementsContent, expected } = inconsistency;
396
+
397
+ if (!requirementsPath || !requirementsContent) {
398
+ return false;
399
+ }
400
+
401
+ const frontmatter = extractFrontmatter(requirementsContent);
402
+ if (!frontmatter) {
403
+ return false;
404
+ }
405
+
406
+ const newFrontmatter = {
407
+ ...frontmatter,
408
+ status: expected
409
+ };
410
+
411
+ const newContent = updateFrontmatter(requirementsContent, newFrontmatter);
412
+ if (!newContent) {
413
+ return false;
414
+ }
415
+
416
+ await fs.writeFile(requirementsPath, newContent, 'utf8');
417
+ return true;
418
+ }
419
+
420
+ /**
421
+ * Log fixes to maintenance log
422
+ */
423
+ async function logToMaintenanceLog(fixedItems) {
424
+ if (fixedItems.length === 0) {
425
+ return;
426
+ }
427
+
428
+ const timestamp = new Date().toISOString();
429
+
430
+ let logContent = '';
431
+ if (await fs.pathExists(MAINTENANCE_LOG)) {
432
+ logContent = await fs.readFile(MAINTENANCE_LOG, 'utf8');
433
+ }
434
+
435
+ const logEntry = `
436
+ ## ${timestamp} - Status Sync
437
+
438
+ **Triggered by**: status-integrity script
439
+
440
+ | Artifact | Old Status | New Status | Reason |
441
+ |----------|------------|------------|--------|
442
+ ${fixedItems.map(item => {
443
+ const shortPath = item.path.replace(/^memory-bank\//, '');
444
+ return `| ${shortPath} | ${item.current} | ${item.expected} | ${item.reason} |`;
445
+ }).join('\n')}
446
+
447
+ ---
448
+ `;
449
+
450
+ await fs.writeFile(MAINTENANCE_LOG, logContent + logEntry, 'utf8');
451
+ }
452
+
453
+ /**
454
+ * Main: Check and fix status inconsistencies
455
+ */
456
+ async function statusIntegrity(fix = false) {
457
+ console.log(`${colors.bright}${colors.blue}════════════════════════════════════════${colors.reset}`);
458
+ console.log(`${colors.bright}${colors.blue}Status Integrity Check${colors.reset}`);
459
+ console.log(`${colors.bright}${colors.blue}════════════════════════════════════════${colors.reset}\n`);
460
+
461
+ const inconsistencies = [];
462
+ const bolts = [];
463
+ const intents = [];
464
+
465
+ // Step 1: Scan all bolts
466
+ if (await fs.pathExists(BOLTS_DIR)) {
467
+ const boltDirs = await fs.readdir(BOLTS_DIR).catch(() => []);
468
+
469
+ for (const boltDir of boltDirs) {
470
+ const bolt = await readBolt(boltDir);
471
+ if (bolt) {
472
+ bolts.push(bolt);
473
+
474
+ // Track unique intents
475
+ if (bolt.frontmatter.intent && !intents.includes(bolt.frontmatter.intent)) {
476
+ intents.push(bolt.frontmatter.intent);
477
+ }
478
+ }
479
+ }
480
+ }
481
+
482
+ // Step 2: Check story status
483
+ console.log(`${colors.dim}[1/3] Checking story status...${colors.reset}`);
484
+ for (const bolt of bolts) {
485
+ const storyIssues = await checkStoryStatus(bolt);
486
+ inconsistencies.push(...storyIssues);
487
+ }
488
+
489
+ // Step 3: Check unit status
490
+ console.log(`${colors.dim}[2/3] Checking unit status...${colors.reset}`);
491
+ for (const intent of intents) {
492
+ if (await fs.pathExists(path.join(INTENTS_DIR, intent, 'units'))) {
493
+ const unitDirs = await fs.readdir(path.join(INTENTS_DIR, intent, 'units')).catch(() => []);
494
+
495
+ for (const unit of unitDirs) {
496
+ const unitIssues = await checkUnitStatus(unit, intent);
497
+ inconsistencies.push(...unitIssues);
498
+ }
499
+ }
500
+ }
501
+
502
+ // Step 4: Check intent status
503
+ console.log(`${colors.dim}[3/3] Checking intent status...${colors.reset}`);
504
+ for (const intent of intents) {
505
+ const intentIssues = await checkIntentStatus(intent);
506
+ inconsistencies.push(...intentIssues);
507
+ }
508
+
509
+ // Display results
510
+ console.log();
511
+ if (inconsistencies.length === 0) {
512
+ console.log(`${colors.green}✓${colors.reset} All statuses are consistent!\n`);
513
+ } else {
514
+ console.log(`${colors.bright}${colors.yellow}⚠️ ${inconsistencies.length} inconsistencies found${colors.reset}\n`);
515
+
516
+ console.log(`${colors.bright}Status Inconsistencies:${colors.reset}\n`);
517
+ console.log('| Artifact | Current | Expected | Reason |');
518
+ console.log('|----------|---------|----------|--------|');
519
+
520
+ for (const issue of inconsistencies) {
521
+ const shortPath = issue.path.replace(/^memory-bank\//, '').replace(/^.*\/intents\//, '');
522
+ console.log(`| ${shortPath} | ${issue.current} | ${issue.expected} | ${issue.reason} |`);
523
+ }
524
+
525
+ console.log();
526
+
527
+ if (fix) {
528
+ console.log(`${colors.bright}Fixing inconsistencies...${colors.reset}\n`);
529
+
530
+ const fixedItems = [];
531
+
532
+ for (const issue of inconsistencies) {
533
+ let fixed = false;
534
+
535
+ if (issue.type === 'story') {
536
+ fixed = await fixStoryStatus(issue);
537
+ } else if (issue.type === 'unit') {
538
+ fixed = await fixUnitStatus(issue);
539
+ } else if (issue.type === 'intent') {
540
+ fixed = await fixIntentStatus(issue);
541
+ }
542
+
543
+ if (fixed) {
544
+ console.log(` ${colors.green}✓${colors.reset} Fixed: ${issue.path.replace(/^memory-bank\//, '')}`);
545
+ fixedItems.push(issue);
546
+ } else {
547
+ console.log(` ${colors.red}✗${colors.reset} Failed: ${issue.path.replace(/^memory-bank\//, '')}`);
548
+ }
549
+ }
550
+
551
+ if (fixedItems.length > 0) {
552
+ await logToMaintenanceLog(fixedItems);
553
+ console.log(`\n${colors.dim}Logged to: ${MAINTENANCE_LOG}${colors.reset}`);
554
+ }
555
+
556
+ console.log();
557
+ console.log(`${colors.green}Fixed ${fixedItems.length} of ${inconsistencies.length} inconsistencies${colors.reset}\n`);
558
+ } else {
559
+ console.log(`${colors.dim}Run with --fix to automatically correct these issues.${colors.reset}\n`);
560
+ }
561
+ }
562
+
563
+ // Summary
564
+ console.log(`${colors.bright}${colors.blue}════════════════════════════════════════${colors.reset}`);
565
+ console.log(`${colors.bright}${colors.blue}Summary${colors.reset}`);
566
+ console.log(`${colors.bright}${colors.blue}════════════════════════════════════════${colors.reset}\n`);
567
+ console.log(`Bolts scanned: ${bolts.length}`);
568
+ console.log(`Intents scanned: ${intents.length}`);
569
+ console.log(`Inconsistencies: ${colors.red}${inconsistencies.length}${colors.reset}\n`);
570
+
571
+ return inconsistencies.length;
572
+ }
573
+
574
+ // CLI entry point
575
+ const args = process.argv.slice(2);
576
+ const shouldFix = args.includes('--fix') || args.includes('-f');
577
+
578
+ if (args.includes('--help') || args.includes('-h')) {
579
+ console.log(`
580
+ Usage: node status-integrity.js [options]
581
+
582
+ Options:
583
+ --fix, -f Automatically fix status inconsistencies
584
+ --help, -h Show this help message
585
+
586
+ Examples:
587
+ node .iris/scripts/status-integrity.js
588
+ node .iris/scripts/status-integrity.js --fix
589
+ `);
590
+ process.exit(0);
591
+ }
592
+
593
+ statusIntegrity(shouldFix)
594
+ .then(count => process.exit(count > 0 && !shouldFix ? 1 : 0))
595
+ .catch(error => {
596
+ console.error(`\n${colors.red}Error:${colors.reset}`, error.message);
597
+ process.exit(1);
598
+ });