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.
- package/README.md +214 -323
- package/bin/cli.js +21 -0
- package/flows/aidlc/README.md +372 -0
- package/flows/aidlc/agents/construction-agent.md +79 -0
- package/flows/aidlc/agents/inception-agent.md +97 -0
- package/flows/aidlc/agents/master-agent.md +61 -0
- package/flows/aidlc/agents/operations-agent.md +89 -0
- package/flows/aidlc/commands/construction-agent.md +63 -0
- package/flows/aidlc/commands/inception-agent.md +55 -0
- package/flows/aidlc/commands/master-agent.md +47 -0
- package/flows/aidlc/commands/operations-agent.md +77 -0
- package/flows/aidlc/context-config.yaml +67 -0
- package/flows/aidlc/memory-bank.yaml +104 -0
- package/flows/aidlc/quick-start.md +322 -0
- package/flows/aidlc/skills/construction/bolt-list.md +163 -0
- package/flows/aidlc/skills/construction/bolt-replan.md +345 -0
- package/flows/aidlc/skills/construction/bolt-start.md +442 -0
- package/flows/aidlc/skills/construction/bolt-status.md +185 -0
- package/flows/aidlc/skills/construction/navigator.md +196 -0
- package/flows/aidlc/skills/inception/bolt-plan.md +372 -0
- package/flows/aidlc/skills/inception/context.md +171 -0
- package/flows/aidlc/skills/inception/intent-create.md +211 -0
- package/flows/aidlc/skills/inception/intent-list.md +124 -0
- package/flows/aidlc/skills/inception/navigator.md +207 -0
- package/flows/aidlc/skills/inception/requirements.md +227 -0
- package/flows/aidlc/skills/inception/review.md +248 -0
- package/flows/aidlc/skills/inception/story-create.md +304 -0
- package/flows/aidlc/skills/inception/units.md +278 -0
- package/flows/aidlc/skills/master/analyze-context.md +239 -0
- package/flows/aidlc/skills/master/answer-question.md +141 -0
- package/flows/aidlc/skills/master/explain-flow.md +158 -0
- package/flows/aidlc/skills/master/project-init.md +281 -0
- package/flows/aidlc/skills/master/route-request.md +126 -0
- package/flows/aidlc/skills/operations/build.md +237 -0
- package/flows/aidlc/skills/operations/deploy.md +259 -0
- package/flows/aidlc/skills/operations/monitor.md +265 -0
- package/flows/aidlc/skills/operations/navigator.md +209 -0
- package/flows/aidlc/skills/operations/verify.md +224 -0
- package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt.md +3 -3
- package/{dist → flows/aidlc}/templates/construction/bolt-types/spike-bolt.md +2 -2
- package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
- package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
- package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
- package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
- package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
- package/flows/aidlc/templates/inception/project/README.md +55 -0
- package/flows/aidlc/templates/standards/catalog.yaml +345 -0
- package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
- package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
- package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
- package/lib/InstallerFactory.js +36 -0
- package/lib/analytics/env-detector.js +92 -0
- package/lib/analytics/index.js +22 -0
- package/lib/analytics/machine-id.js +33 -0
- package/lib/analytics/tracker.js +232 -0
- package/lib/cli-utils.js +342 -0
- package/lib/constants.js +32 -0
- package/lib/installer.js +402 -0
- package/lib/installers/AntigravityInstaller.js +22 -0
- package/lib/installers/ClaudeInstaller.js +85 -0
- package/lib/installers/ClineInstaller.js +21 -0
- package/lib/installers/CodexInstaller.js +21 -0
- package/lib/installers/CopilotInstaller.js +113 -0
- package/lib/installers/CursorInstaller.js +63 -0
- package/lib/installers/GeminiInstaller.js +75 -0
- package/lib/installers/KiroInstaller.js +22 -0
- package/lib/installers/OpenCodeInstaller.js +22 -0
- package/lib/installers/RooInstaller.js +22 -0
- package/lib/installers/ToolInstaller.js +73 -0
- package/lib/installers/WindsurfInstaller.js +22 -0
- package/lib/markdown-validator.ts +175 -0
- package/lib/yaml-validator.ts +99 -0
- package/package.json +105 -32
- package/scripts/artifact-validator.js +594 -0
- package/scripts/bolt-complete.js +606 -0
- package/scripts/status-integrity.js +598 -0
- package/dist/bridge/agent-runner.js +0 -190
- package/dist/bridge/connector-factory.js +0 -31
- package/dist/bridge/connectors/antigravity-connector.js +0 -18
- package/dist/bridge/connectors/cursor-connector.js +0 -31
- package/dist/bridge/connectors/in-process-connector.js +0 -29
- package/dist/bridge/connectors/vscode-connector.js +0 -31
- package/dist/bridge/connectors/windsurf-connector.js +0 -23
- package/dist/bridge/filesystem-connector.js +0 -110
- package/dist/bridge/helper.js +0 -203
- package/dist/bridge/types.js +0 -10
- package/dist/cli.js +0 -40
- package/dist/commands/ask.js +0 -259
- package/dist/commands/bridge.js +0 -88
- package/dist/commands/create.js +0 -25
- package/dist/commands/develop.js +0 -141
- package/dist/commands/doctor.js +0 -102
- package/dist/commands/flow.js +0 -301
- package/dist/commands/framework.js +0 -273
- package/dist/commands/generate.js +0 -59
- package/dist/commands/install.js +0 -100
- package/dist/commands/pack.js +0 -33
- package/dist/commands/phase.js +0 -38
- package/dist/commands/run.js +0 -199
- package/dist/commands/status.js +0 -114
- package/dist/commands/uninstall.js +0 -14
- package/dist/commands/use.js +0 -20
- package/dist/commands/validate.js +0 -102
- package/dist/framework/framework-loader.js +0 -97
- package/dist/framework/framework-paths.js +0 -48
- package/dist/framework/framework-types.js +0 -15
- package/dist/iris/artifact-checker.js +0 -78
- package/dist/iris/artifacts/config.js +0 -68
- package/dist/iris/artifacts/generator.js +0 -88
- package/dist/iris/artifacts/types.js +0 -1
- package/dist/iris/bundle.js +0 -44
- package/dist/iris/doctrine/collector.js +0 -124
- package/dist/iris/fixer.js +0 -149
- package/dist/iris/flows/manifest.js +0 -124
- package/dist/iris/framework-context.js +0 -49
- package/dist/iris/framework-manager.js +0 -215
- package/dist/iris/fs/atomic.js +0 -22
- package/dist/iris/guard.js +0 -38
- package/dist/iris/importers/index.js +0 -9
- package/dist/iris/importers/types.js +0 -8
- package/dist/iris/importers/writer.js +0 -139
- package/dist/iris/include.js +0 -49
- package/dist/iris/installer.js +0 -334
- package/dist/iris/interactive/env.js +0 -21
- package/dist/iris/interactive/intent-interview.js +0 -345
- package/dist/iris/interactive/intent-schema.js +0 -28
- package/dist/iris/interactive/interview-io.js +0 -22
- package/dist/iris/interview/config.js +0 -71
- package/dist/iris/interview/types.js +0 -16
- package/dist/iris/interview/utils.js +0 -38
- package/dist/iris/manifest.js +0 -54
- package/dist/iris/packer.js +0 -325
- package/dist/iris/parsers/unit-parser.js +0 -43
- package/dist/iris/paths.js +0 -18
- package/dist/iris/policy.js +0 -133
- package/dist/iris/proc.js +0 -56
- package/dist/iris/report.js +0 -53
- package/dist/iris/resolver.js +0 -66
- package/dist/iris/router.js +0 -114
- package/dist/iris/routes.js +0 -189
- package/dist/iris/run-state.js +0 -146
- package/dist/iris/state.js +0 -113
- package/dist/iris/templates.js +0 -70
- package/dist/iris/tmp.js +0 -24
- package/dist/iris/uninstaller.js +0 -181
- package/dist/iris/utils/interpolate.js +0 -42
- package/dist/iris/validator.js +0 -391
- package/dist/iris/workflow/config.js +0 -51
- package/dist/iris/workflow/engine.js +0 -129
- package/dist/iris/workflow/steps.js +0 -448
- package/dist/iris/workflow/types.js +0 -1
- package/dist/iris_bundle/frameworks/iris-core/framework.yaml +0 -9
- package/dist/iris_bundle/frameworks/iris-core/memory/memory-bank.yaml +0 -1
- package/dist/iris_bundle/frameworks/iris-core/policy.yaml +0 -7
- package/dist/iris_bundle/frameworks/iris-core/templates/config/memory-bank.yaml +0 -1
- package/dist/iris_bundle/frameworks/iris-core/templates/construction/bolt-types/spike-bolt.md +0 -240
- package/dist/lib.js +0 -96
- package/dist/templates/construction/bolt-template.md +0 -226
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -49
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -55
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -67
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -62
- package/dist/templates/construction/bolt-types/ddd-construction-bolt.md +0 -528
- package/dist/templates/construction/bolt-types/simple-construction-bolt.md +0 -347
- package/dist/templates/inception/requirements-template.md +0 -144
- package/dist/templates/inception/stories-template.md +0 -38
- package/dist/templates/inception/story-template.md +0 -147
- package/dist/templates/inception/system-context-template.md +0 -29
- package/dist/templates/inception/unit-brief-template.md +0 -177
- package/dist/templates/inception/units-template.md +0 -52
- package/dist/utils/exit-codes.js +0 -7
- package/dist/utils/logo.js +0 -17
- package/dist/workflows/bolt-execution.js +0 -238
- package/dist/workflows/bolt-plan.js +0 -221
- package/dist/workflows/intent-inception.js +0 -285
- package/dist/workflows/memory-bank-generator.js +0 -180
- package/dist/workflows/reporting.js +0 -74
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/simple-construction-bolt.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/requirements-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/stories-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/story-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/system-context-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/unit-brief-template.md +0 -0
- /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
|
+
});
|