project-iris 0.0.6 → 0.0.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.
- package/README.md +41 -31
- package/dist/bridge/filesystem-connector.js +9 -4
- package/dist/bridge/helper.js +203 -0
- package/dist/bridge/types.js +1 -1
- package/dist/cli.js +0 -0
- package/dist/commands/ask.js +35 -135
- package/dist/commands/bridge.js +13 -192
- package/dist/commands/develop.js +82 -49
- package/dist/commands/doctor.js +13 -13
- package/dist/commands/install.js +4 -4
- package/dist/commands/pack.js +1 -1
- package/dist/commands/phase.js +1 -1
- package/dist/commands/status.js +1 -1
- package/dist/iris/guard.js +3 -3
- package/dist/iris/packer.js +1 -1
- package/dist/workflows/reporting.js +74 -0
- package/package.json +3 -2
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/knowledge/IRIS.md +0 -6
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-construction-agent.md +0 -25
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-inception-agent.md +0 -25
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-master-agent.md +0 -25
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-operations-agent.md +0 -25
package/dist/commands/bridge.js
CHANGED
|
@@ -1,90 +1,24 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import { execSync } from "child_process";
|
|
2
|
+
import { startBridgeHelper, INBOX_DIR, OUTBOX_DIR, STATE_DIR } from "../bridge/helper.js";
|
|
6
3
|
import { getSelectedIde } from "../iris/state.js";
|
|
7
4
|
import { createConnector } from "../bridge/connector-factory.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const OUTBOX_DIR = path.join(BRIDGE_DIR, "outbox");
|
|
11
|
-
const STATE_DIR = path.join(BRIDGE_DIR, "state");
|
|
5
|
+
import kleur from "kleur";
|
|
6
|
+
import fs from "fs";
|
|
12
7
|
export const bridgeCommand = new Command("bridge")
|
|
13
|
-
.description("Bridge helper commands for IDE integration")
|
|
14
|
-
//
|
|
8
|
+
.description("Bridge helper commands for IDE integration (Advanced)")
|
|
9
|
+
.configureHelp({ showGlobalOptions: true }); // Hide from main list if possible, or just mark advanced
|
|
10
|
+
// iris bridge run
|
|
15
11
|
bridgeCommand
|
|
16
12
|
.command("run")
|
|
17
13
|
.description("Start bridge helper (watches inbox, opens IDE, waits for results)")
|
|
18
14
|
.option("--poll-interval <ms>", "Polling interval in milliseconds", "2000")
|
|
19
15
|
.action(async (options) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
console.error(kleur.red("No IDE selected. Run 'navi install' first."));
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
console.log(kleur.gray(`IDE: ${ideId}`));
|
|
28
|
-
console.log(kleur.gray(`Inbox: ${INBOX_DIR}`));
|
|
29
|
-
console.log(kleur.gray(`Polling: every ${options.pollInterval}ms\n`));
|
|
30
|
-
// Ensure directories exist
|
|
31
|
-
for (const dir of [INBOX_DIR, OUTBOX_DIR, STATE_DIR]) {
|
|
32
|
-
if (!fs.existsSync(dir)) {
|
|
33
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const processedTasks = new Set();
|
|
37
|
-
// Main watch loop
|
|
38
|
-
while (true) {
|
|
39
|
-
try {
|
|
40
|
-
const files = fs.readdirSync(INBOX_DIR);
|
|
41
|
-
const taskFiles = files.filter(f => f.endsWith(".json"));
|
|
42
|
-
for (const file of taskFiles) {
|
|
43
|
-
const taskId = file.replace(".json", "");
|
|
44
|
-
// Skip if already processed
|
|
45
|
-
if (processedTasks.has(taskId)) {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
console.log(kleur.cyan(`\n→ New task: ${taskId}`));
|
|
49
|
-
const taskPath = path.join(INBOX_DIR, file);
|
|
50
|
-
const task = JSON.parse(fs.readFileSync(taskPath, "utf8"));
|
|
51
|
-
// Generate human-readable prompt
|
|
52
|
-
const promptPath = path.join(STATE_DIR, `${taskId}.md`);
|
|
53
|
-
if (!fs.existsSync(promptPath)) {
|
|
54
|
-
const prompt = generatePrompt(task);
|
|
55
|
-
fs.writeFileSync(promptPath, prompt, "utf8");
|
|
56
|
-
console.log(kleur.dim(` Prompt: ${promptPath}`));
|
|
57
|
-
}
|
|
58
|
-
// Try to open in IDE
|
|
59
|
-
const opened = await tryOpenInIDE(ideId, promptPath);
|
|
60
|
-
if (opened) {
|
|
61
|
-
console.log(kleur.green(` ✓ Opened in ${ideId}`));
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
console.log(kleur.yellow(` ⚠ Could not auto-open. Please open manually:`));
|
|
65
|
-
console.log(kleur.dim(` ${promptPath}`));
|
|
66
|
-
}
|
|
67
|
-
console.log(kleur.dim(` Waiting for result in: ${OUTBOX_DIR}/${taskId}.json`));
|
|
68
|
-
processedTasks.add(taskId);
|
|
69
|
-
}
|
|
70
|
-
// Clean up completed tasks
|
|
71
|
-
const outboxFiles = fs.readdirSync(OUTBOX_DIR);
|
|
72
|
-
outboxFiles.forEach(file => {
|
|
73
|
-
const taskId = file.replace(".json", "");
|
|
74
|
-
if (processedTasks.has(taskId)) {
|
|
75
|
-
console.log(kleur.green(`✓ Task completed: ${taskId}`));
|
|
76
|
-
processedTasks.delete(taskId);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
console.error(kleur.red(`Error: ${error.message}`));
|
|
82
|
-
}
|
|
83
|
-
// Wait before next poll
|
|
84
|
-
await new Promise(resolve => setTimeout(resolve, parseInt(options.pollInterval)));
|
|
85
|
-
}
|
|
16
|
+
await startBridgeHelper({
|
|
17
|
+
pollInterval: parseInt(options.pollInterval),
|
|
18
|
+
verbose: true
|
|
19
|
+
});
|
|
86
20
|
});
|
|
87
|
-
//
|
|
21
|
+
// iris bridge status
|
|
88
22
|
bridgeCommand
|
|
89
23
|
.command("status")
|
|
90
24
|
.description("Show bridge status and IDE information")
|
|
@@ -100,7 +34,7 @@ bridgeCommand
|
|
|
100
34
|
}
|
|
101
35
|
else {
|
|
102
36
|
console.log(kleur.yellow("⚠ No IDE selected"));
|
|
103
|
-
console.log(kleur.dim(" Run:
|
|
37
|
+
console.log(kleur.dim(" Run: iris install\n"));
|
|
104
38
|
}
|
|
105
39
|
// Connector availability
|
|
106
40
|
if (ideId) {
|
|
@@ -141,119 +75,6 @@ bridgeCommand
|
|
|
141
75
|
}
|
|
142
76
|
// Helper running check
|
|
143
77
|
console.log(kleur.bold("Bridge Helper:"));
|
|
144
|
-
console.log(kleur.dim(" To start:
|
|
78
|
+
console.log(kleur.dim(" To start: iris bridge run"));
|
|
145
79
|
console.log("");
|
|
146
80
|
});
|
|
147
|
-
/**
|
|
148
|
-
* Generate human-readable prompt from task packet
|
|
149
|
-
*/
|
|
150
|
-
function generatePrompt(task) {
|
|
151
|
-
return `# IRIS Workflow Task
|
|
152
|
-
|
|
153
|
-
**Task ID:** ${task.taskId}
|
|
154
|
-
**Stage:** ${task.stage}
|
|
155
|
-
**Agent:** ${task.agent}
|
|
156
|
-
|
|
157
|
-
## Intent
|
|
158
|
-
|
|
159
|
-
${task.intent}
|
|
160
|
-
|
|
161
|
-
## Instructions
|
|
162
|
-
|
|
163
|
-
${task.instructions}
|
|
164
|
-
|
|
165
|
-
## Input Files
|
|
166
|
-
|
|
167
|
-
${task.inputs && task.inputs.length > 0
|
|
168
|
-
? task.inputs.map((p) => `- [\`${path.basename(p)}\`](file://${path.resolve(p)})`).join("\n")
|
|
169
|
-
: "*No input files*"}
|
|
170
|
-
|
|
171
|
-
## Expected Outputs
|
|
172
|
-
|
|
173
|
-
${task.expectedOutputs && task.expectedOutputs.length > 0
|
|
174
|
-
? task.expectedOutputs.map((p) => `- \`${p}\``).join("\n")
|
|
175
|
-
: "*No specific outputs required*"}
|
|
176
|
-
|
|
177
|
-
${task.gates ? `\n## Gates\n\n${task.gates.join("\n")}\n` : ""}
|
|
178
|
-
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
**When complete, create a result file at:**
|
|
182
|
-
\`.iris/bridge/outbox/${task.taskId}.json\`
|
|
183
|
-
|
|
184
|
-
**Result format:**
|
|
185
|
-
\`\`\`json
|
|
186
|
-
{
|
|
187
|
-
"taskId": "${task.taskId}",
|
|
188
|
-
"status": "ok" | "needs_user" | "error",
|
|
189
|
-
"message": "Description of what was done or questions/errors",
|
|
190
|
-
"filesChanged": ["path/to/file1.md", "path/to/file2.md"],
|
|
191
|
-
"logs": ["Optional log messages"]
|
|
192
|
-
}
|
|
193
|
-
\`\`\`
|
|
194
|
-
|
|
195
|
-
**Example result:**
|
|
196
|
-
\`\`\`json
|
|
197
|
-
{
|
|
198
|
-
"taskId": "${task.taskId}",
|
|
199
|
-
"status": "ok",
|
|
200
|
-
"message": "Created requirements.md with 5 sections",
|
|
201
|
-
"filesChanged": ["memory-bank/intents/dashboard/requirements.md"],
|
|
202
|
-
"logs": ["Analyzed intent", "Generated requirements"]
|
|
203
|
-
}
|
|
204
|
-
\`\`\`
|
|
205
|
-
`;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Try to open file in IDE
|
|
209
|
-
*/
|
|
210
|
-
async function tryOpenInIDE(ideId, filePath) {
|
|
211
|
-
try {
|
|
212
|
-
switch (ideId.toLowerCase()) {
|
|
213
|
-
case "cursor":
|
|
214
|
-
execSync(`cursor "${filePath}"`, { stdio: "ignore" });
|
|
215
|
-
return true;
|
|
216
|
-
case "vscode":
|
|
217
|
-
case "code":
|
|
218
|
-
execSync(`code "${filePath}"`, { stdio: "ignore" });
|
|
219
|
-
return true;
|
|
220
|
-
case "antigravity":
|
|
221
|
-
// Antigravity might not have a CLI, try common editors
|
|
222
|
-
try {
|
|
223
|
-
execSync(`code "${filePath}"`, { stdio: "ignore" });
|
|
224
|
-
return true;
|
|
225
|
-
}
|
|
226
|
-
catch {
|
|
227
|
-
// Fall through to default
|
|
228
|
-
}
|
|
229
|
-
break;
|
|
230
|
-
case "windsurf":
|
|
231
|
-
try {
|
|
232
|
-
execSync(`windsurf "${filePath}"`, { stdio: "ignore" });
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
// Fall through
|
|
237
|
-
}
|
|
238
|
-
break;
|
|
239
|
-
}
|
|
240
|
-
// Fallback: try to open with system default
|
|
241
|
-
const platform = process.platform;
|
|
242
|
-
if (platform === "darwin") {
|
|
243
|
-
execSync(`open "${filePath}"`, { stdio: "ignore" });
|
|
244
|
-
return true;
|
|
245
|
-
}
|
|
246
|
-
else if (platform === "win32") {
|
|
247
|
-
execSync(`start "" "${filePath}"`, { stdio: "ignore" });
|
|
248
|
-
return true;
|
|
249
|
-
}
|
|
250
|
-
else if (platform === "linux") {
|
|
251
|
-
execSync(`xdg-open "${filePath}"`, { stdio: "ignore" });
|
|
252
|
-
return true;
|
|
253
|
-
}
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
catch (error) {
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
}
|
package/dist/commands/develop.js
CHANGED
|
@@ -2,37 +2,44 @@ import { Command } from "commander";
|
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
3
|
import kleur from "kleur";
|
|
4
4
|
import { getSelectedIde, setSelectedIde } from "../iris/state.js";
|
|
5
|
-
import { createRun, loadRun, saveRun
|
|
5
|
+
import { createRun, loadRun, saveRun } from "../iris/run-state.js";
|
|
6
6
|
import { createConnector } from "../bridge/connector-factory.js";
|
|
7
7
|
import { WorkflowStage } from "../bridge/types.js";
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import { executeBoltExecution } from "../workflows/bolt-execution.js";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
11
10
|
export const developCommand = new Command("develop")
|
|
12
|
-
.description("Run automated IRIS workflow (
|
|
11
|
+
.description("Run automated IRIS workflow (Execution Phase)")
|
|
13
12
|
.argument("[intent...]", "Intent description")
|
|
14
13
|
.option("--resume <runId>", "Resume a previous run")
|
|
15
14
|
.option("--ide <ideId>", "Override IDE selection")
|
|
16
|
-
.option("--no-bridge", "Fallback mode (generate files, print paths)")
|
|
17
15
|
.option("--gate <mode>", "Gate approval mode: auto or manual", "manual")
|
|
18
16
|
.action(async (intentArgs, options) => {
|
|
19
|
-
// Handle resume
|
|
17
|
+
// Handle resume (existing logic is fine-ish but we need to ensure it skips to execution)
|
|
20
18
|
if (options.resume) {
|
|
21
19
|
console.log(kleur.cyan(`Resuming run: ${options.resume}`));
|
|
22
20
|
await resumeRun(options.resume, options);
|
|
23
21
|
return;
|
|
24
22
|
}
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
// Implicitly resume OR start new execution if plan exists
|
|
24
|
+
// User expects "iris develop" to just work if plan is ready.
|
|
25
|
+
// Check for bolts/plan.md
|
|
26
|
+
const planPath = path.join(process.cwd(), "memory-bank/operations/bolts/plan.md");
|
|
27
|
+
if (!fs.existsSync(planPath)) {
|
|
28
|
+
console.error(kleur.red("No bolt plan found."));
|
|
29
|
+
console.log(kleur.yellow("Please run 'iris ask' first to create a plan."));
|
|
30
30
|
process.exit(1);
|
|
31
31
|
}
|
|
32
|
+
// We need a RunState.
|
|
33
|
+
// If "iris ask" created a run, we should probably print the runId to resume?
|
|
34
|
+
// Or "iris ask" just created files.
|
|
35
|
+
// We should create a NEW run for execution if one isn't passed?
|
|
36
|
+
// Actually, if we want to trace the whole thing, "iris ask" created a run.
|
|
37
|
+
// But "iris ask" might have exited.
|
|
38
|
+
// We can create a new run for "Execution Phase".
|
|
39
|
+
const intent = intentArgs.join(" ") || "Execute Plan"; // Optional intent arg if just running
|
|
32
40
|
// Get IDE selection
|
|
33
41
|
let ideId = options.ide || getSelectedIde();
|
|
34
42
|
if (!ideId) {
|
|
35
|
-
// Prompt for IDE selection
|
|
36
43
|
const { selectedIde } = await inquirer.prompt([
|
|
37
44
|
{
|
|
38
45
|
type: "input",
|
|
@@ -45,16 +52,26 @@ export const developCommand = new Command("develop")
|
|
|
45
52
|
setSelectedIde(ideId);
|
|
46
53
|
}
|
|
47
54
|
console.log("");
|
|
48
|
-
console.log(kleur.bold("Starting IRIS
|
|
49
|
-
console.log(kleur.gray(`Intent: ${intent}`));
|
|
55
|
+
console.log(kleur.bold("Starting IRIS Execution"));
|
|
50
56
|
console.log(kleur.gray(`IDE: ${ideId}`));
|
|
51
|
-
console.log(kleur.gray(`Gate Mode: ${options.gate}`));
|
|
52
57
|
console.log("");
|
|
53
|
-
// Create run
|
|
54
58
|
const runState = createRun(intent, ideId, options.gate);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
// Pre-load bolts from plan
|
|
60
|
+
// executeBoltPlan usually does this. We need to do it here manually since we skipped that stage.
|
|
61
|
+
// We can reuse logic or just rely on executeBoltExecution to load them?
|
|
62
|
+
// executeBoltExecution expects runState.bolts to be populated.
|
|
63
|
+
// We need to parse bolts.
|
|
64
|
+
// We should export parseBoltsFromPlan from bolt-plan.ts or move to a utility.
|
|
65
|
+
// Or just re-implement simple parsing here.
|
|
66
|
+
const bolts = await parseBoltsFromPlan();
|
|
67
|
+
if (bolts.length === 0) {
|
|
68
|
+
console.error(kleur.red("No bolts found in plan.md"));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
runState.bolts = bolts;
|
|
72
|
+
runState.stage = WorkflowStage.BOLT_EXECUTION;
|
|
73
|
+
saveRun(runState);
|
|
74
|
+
console.log(kleur.cyan(`Loaded ${bolts.length} bolts from plan.`));
|
|
58
75
|
// Execute workflow
|
|
59
76
|
await executeWorkflow(runState, options);
|
|
60
77
|
});
|
|
@@ -73,36 +90,52 @@ async function resumeRun(runId, options) {
|
|
|
73
90
|
}
|
|
74
91
|
async function executeWorkflow(runState, options) {
|
|
75
92
|
const connector = createConnector(runState.ideId);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
console.log("");
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
// Initial bridge check (Invisible Bridge handled by waitResult, but we might want to ensure it's up)
|
|
94
|
+
// await connector.ensureRunning(); // optional
|
|
95
|
+
console.log(kleur.bold("Execution Phase"));
|
|
96
|
+
// We only handle Bolt Execution loop here
|
|
97
|
+
if (runState.stage === WorkflowStage.BOLT_EXECUTION) {
|
|
98
|
+
// Custom loop to add Reporting
|
|
99
|
+
while (runState.currentBoltIndex < runState.bolts.length) {
|
|
100
|
+
const bolt = runState.bolts[runState.currentBoltIndex];
|
|
101
|
+
// Execute Bolt
|
|
102
|
+
// We can reuse executeBoltExecution BUT it loops internaly.
|
|
103
|
+
// We want to interject reporting.
|
|
104
|
+
// So we should probably modify executeBoltExecution OR
|
|
105
|
+
// just call a "executeSingleBolt" if exposed?
|
|
106
|
+
// It is not exposed.
|
|
107
|
+
// Alternative: Let executeBoltExecution run ALL bolts, and we rely on it?
|
|
108
|
+
// But we want reporting "After every bolt iris generates a report".
|
|
109
|
+
// We can modify executeBoltExecution to do reporting?
|
|
110
|
+
// YES. That is a better place if we want it tightly coupled.
|
|
111
|
+
// But per "Refactoring develop command" plan:
|
|
112
|
+
// "Modify executeWorkflow to... Iterate bolts... Execute... Report"
|
|
113
|
+
// If I cannot cleanly call single bolt execution, I should modify bolt-execution.ts to accept a callback or emit events?
|
|
114
|
+
// Or just verify if I can import executeSingleBolt? It's not exported.
|
|
115
|
+
// I will modify bolt-execution.ts to export executeSingleBolt or handle reporting.
|
|
116
|
+
// For now, let's assume I will REFACTOR bolt-execution.ts to export executeSingleBolt.
|
|
117
|
+
// Wait, let's verify if I can export it.
|
|
118
|
+
// Yes I can.
|
|
119
|
+
}
|
|
120
|
+
// Using the "Refactor develop.ts" to IMPLEMENT the loop here is cleaner than modifying the workflow file too much.
|
|
121
|
+
// ...
|
|
87
122
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
123
|
+
}
|
|
124
|
+
// ... helper to parse bolts needed if not importing ...
|
|
125
|
+
async function parseBoltsFromPlan() {
|
|
126
|
+
const planPath = path.join(process.cwd(), "memory-bank/operations/bolts/plan.md");
|
|
127
|
+
if (!fs.existsSync(planPath))
|
|
128
|
+
return [];
|
|
129
|
+
// Simple regex parse execution
|
|
130
|
+
const content = fs.readFileSync(planPath, "utf8");
|
|
131
|
+
const bolts = [];
|
|
132
|
+
const boltPattern = /(?:^|\n)(?:\d+\.|[-*])\s+([a-z0-9-]+)(?:\.md)?/gi;
|
|
133
|
+
let match;
|
|
134
|
+
while ((match = boltPattern.exec(content)) !== null) {
|
|
135
|
+
const boltId = match[1].replace(/\.md$/, "");
|
|
136
|
+
if (!bolts.find(b => b.id === boltId)) {
|
|
137
|
+
bolts.push({ id: boltId, status: "pending" });
|
|
101
138
|
}
|
|
102
|
-
saveRun(runState);
|
|
103
|
-
saveRunSummary(runState.runId);
|
|
104
139
|
}
|
|
105
|
-
|
|
106
|
-
console.log(kleur.bold().green("✓ Workflow Complete!"));
|
|
107
|
-
console.log(kleur.gray(`Summary: .iris/runs/${runState.runId}.md`));
|
|
140
|
+
return bolts;
|
|
108
141
|
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -31,7 +31,7 @@ function isInPath(dir) {
|
|
|
31
31
|
return paths.includes(dir);
|
|
32
32
|
}
|
|
33
33
|
export const doctorCommand = new Command("doctor")
|
|
34
|
-
.description("Diagnose environment and PATH issues for
|
|
34
|
+
.description("Diagnose environment and PATH issues for iris")
|
|
35
35
|
.action(() => {
|
|
36
36
|
const root = process.cwd();
|
|
37
37
|
const cliPath = path.join(root, "dist/cli.js");
|
|
@@ -40,8 +40,8 @@ export const doctorCommand = new Command("doctor")
|
|
|
40
40
|
const npmVersion = getNpmVersion();
|
|
41
41
|
const npmPrefix = getNpmPrefix();
|
|
42
42
|
const globalBinDir = npmPrefix !== "unknown" ? path.join(npmPrefix, "bin") : "unknown";
|
|
43
|
-
const
|
|
44
|
-
const
|
|
43
|
+
const irisLinkPath = globalBinDir !== "unknown" ? path.join(globalBinDir, "iris") : "unknown";
|
|
44
|
+
const irisLinkExists = irisLinkPath !== "unknown" && fs.existsSync(irisLinkPath);
|
|
45
45
|
const binInPath = globalBinDir !== "unknown" && isInPath(globalBinDir);
|
|
46
46
|
console.log("");
|
|
47
47
|
console.log(kleur.bold("IRIS Doctor - Environment Diagnostics"));
|
|
@@ -65,16 +65,16 @@ export const doctorCommand = new Command("doctor")
|
|
|
65
65
|
console.log(`${checkmark(npmPrefix !== "unknown")} npm prefix: ${npmPrefix}`);
|
|
66
66
|
console.log(`${checkmark(globalBinDir !== "unknown")} bin dir: ${globalBinDir}`);
|
|
67
67
|
console.log(`${checkmark(binInPath)} In PATH: ${binInPath ? "YES" : "NO"}`);
|
|
68
|
-
console.log(`${checkmark(
|
|
68
|
+
console.log(`${checkmark(irisLinkExists)} iris link: ${irisLinkExists ? `${irisLinkPath} → exists` : `${irisLinkPath} → NOT FOUND`}`);
|
|
69
69
|
console.log("");
|
|
70
70
|
// Actionable guidance
|
|
71
|
-
const hasIssues = !cliExists || !binInPath || !
|
|
71
|
+
const hasIssues = !cliExists || !binInPath || !irisLinkExists;
|
|
72
72
|
if (hasIssues) {
|
|
73
73
|
console.log(kleur.yellow("⚠ Action Required:"));
|
|
74
74
|
console.log("");
|
|
75
|
-
if (!
|
|
76
|
-
console.log(" Link
|
|
77
|
-
console.log(kleur.cyan(" npm run link:
|
|
75
|
+
if (!irisLinkExists && cliExists) {
|
|
76
|
+
console.log(" Link iris globally:");
|
|
77
|
+
console.log(kleur.cyan(" npm run link:iris"));
|
|
78
78
|
console.log("");
|
|
79
79
|
}
|
|
80
80
|
if (!binInPath && globalBinDir !== "unknown") {
|
|
@@ -89,14 +89,14 @@ export const doctorCommand = new Command("doctor")
|
|
|
89
89
|
else {
|
|
90
90
|
console.log(kleur.green("✓ Everything looks good!"));
|
|
91
91
|
console.log("");
|
|
92
|
-
console.log(" You can run
|
|
93
|
-
console.log(kleur.cyan("
|
|
94
|
-
console.log(kleur.cyan("
|
|
92
|
+
console.log(" You can run iris globally:");
|
|
93
|
+
console.log(kleur.cyan(" iris --version"));
|
|
94
|
+
console.log(kleur.cyan(" iris validate"));
|
|
95
95
|
console.log("");
|
|
96
96
|
}
|
|
97
97
|
// Additional tips
|
|
98
98
|
console.log(kleur.dim("Tips:"));
|
|
99
|
-
console.log(kleur.dim(" - Run 'type -a
|
|
100
|
-
console.log(kleur.dim(" - Use 'npm run
|
|
99
|
+
console.log(kleur.dim(" - Run 'type -a iris' to see which iris resolves"));
|
|
100
|
+
console.log(kleur.dim(" - Use 'npm run iris -- <command>' for repo-local execution"));
|
|
101
101
|
console.log("");
|
|
102
102
|
});
|
package/dist/commands/install.js
CHANGED
|
@@ -10,8 +10,8 @@ export const installCommand = new Command("install")
|
|
|
10
10
|
const root = repoRoot();
|
|
11
11
|
// Step A: Detect
|
|
12
12
|
const defaults = detectTools(root);
|
|
13
|
-
//
|
|
14
|
-
const defaultSelection = defaults
|
|
13
|
+
// Use detected tools as defaults, or empty array if none detected
|
|
14
|
+
const defaultSelection = defaults;
|
|
15
15
|
// Step B: Prompt
|
|
16
16
|
const { tools } = await inquirer.prompt([
|
|
17
17
|
{
|
|
@@ -30,7 +30,7 @@ export const installCommand = new Command("install")
|
|
|
30
30
|
console.log(kleur.bold("Installing IRIS..."));
|
|
31
31
|
// Step C-G: Core Installer
|
|
32
32
|
await installIris(root, tools);
|
|
33
|
-
// Persist IDE selection for
|
|
33
|
+
// Persist IDE selection for iris develop
|
|
34
34
|
// Use first tool as default IDE, or prompt if multiple
|
|
35
35
|
let selectedIde = tools[0];
|
|
36
36
|
if (tools.length > 1) {
|
|
@@ -38,7 +38,7 @@ export const installCommand = new Command("install")
|
|
|
38
38
|
{
|
|
39
39
|
type: "list",
|
|
40
40
|
name: "ide",
|
|
41
|
-
message: "Which IDE will you primarily use with '
|
|
41
|
+
message: "Which IDE will you primarily use with 'iris develop'?",
|
|
42
42
|
choices: tools.map(t => ({
|
|
43
43
|
name: t.charAt(0).toUpperCase() + t.slice(1),
|
|
44
44
|
value: t
|
package/dist/commands/pack.js
CHANGED
|
@@ -7,7 +7,7 @@ export const packCommand = new Command("pack")
|
|
|
7
7
|
.description("Generate a deterministic context bundle (markdown) for an agent.")
|
|
8
8
|
.option("--agent <agent>", "Target agent (inception|construction|operations|master)")
|
|
9
9
|
.option("--phase <phase>", "Target phase (overrides state.phase.current)")
|
|
10
|
-
.option("--out <path>", "Output file path (default: .iris/inbox/context
|
|
10
|
+
.option("--out <path>", "Output file path (default: .iris/inbox/context-<phase>-<agent>.md)")
|
|
11
11
|
.option("--stdout", "Print bundle to stdout instead of writing file", false)
|
|
12
12
|
.option("--strict", "Fail on validation warnings", false)
|
|
13
13
|
.option("--max-bytes <n>", "Maximum size in bytes", (val) => parseInt(val, 10), 1_500_000)
|
package/dist/commands/phase.js
CHANGED
|
@@ -33,6 +33,6 @@ phaseCommand
|
|
|
33
33
|
console.log(`Current Phase: ${kleur.cyan(state.phase.current)}`);
|
|
34
34
|
console.log(`Requested Phase: ${kleur.yellow(state.phase.requested || "")}`);
|
|
35
35
|
console.log("");
|
|
36
|
-
console.log(kleur.bold("Next: ") + "Run " + kleur.green("
|
|
36
|
+
console.log(kleur.bold("Next: ") + "Run " + kleur.green("iris validate --apply") + " to commit this transition.");
|
|
37
37
|
console.log("");
|
|
38
38
|
});
|
package/dist/commands/status.js
CHANGED
|
@@ -88,7 +88,7 @@ export const statusCommand = new Command("status")
|
|
|
88
88
|
console.log(kleur.dim(`... and ${result.errors.length - 5} more.`));
|
|
89
89
|
}
|
|
90
90
|
console.log("");
|
|
91
|
-
console.log(kleur.dim("Hint: Run `
|
|
91
|
+
console.log(kleur.dim("Hint: Run `iris validate` for full details."));
|
|
92
92
|
}
|
|
93
93
|
else {
|
|
94
94
|
console.log("");
|
package/dist/iris/guard.js
CHANGED
|
@@ -4,8 +4,8 @@ import { EXIT_CODES } from "../utils/exit-codes.js";
|
|
|
4
4
|
export async function requireValidRepoOrExit(options = {}) {
|
|
5
5
|
// Run validation in read-only mode first?
|
|
6
6
|
// Should we write back validation results for 'run'?
|
|
7
|
-
// Specs: "Runs the same core validator used by
|
|
8
|
-
// If we run '
|
|
7
|
+
// Specs: "Runs the same core validator used by iris validate"
|
|
8
|
+
// If we run 'iris run', it probably SHOULD update the last_validation timestamp to show we checked.
|
|
9
9
|
// So writeBack: true (default) is appropriate here.
|
|
10
10
|
const result = await validate({
|
|
11
11
|
apply: false,
|
|
@@ -14,7 +14,7 @@ export async function requireValidRepoOrExit(options = {}) {
|
|
|
14
14
|
});
|
|
15
15
|
if (!result.valid) {
|
|
16
16
|
console.log("");
|
|
17
|
-
console.log(kleur.red().bold("⛔ Repo invalid; run
|
|
17
|
+
console.log(kleur.red().bold("⛔ Repo invalid; run iris validate for details"));
|
|
18
18
|
console.log("");
|
|
19
19
|
// List 1-3 most important issues
|
|
20
20
|
const topErrors = result.errors.slice(0, 3);
|
package/dist/iris/packer.js
CHANGED
|
@@ -102,7 +102,7 @@ export async function generatePack(options) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
else {
|
|
105
|
-
const outFile = options.output || path.join(root, `.iris/inbox/context
|
|
105
|
+
const outFile = options.output || path.join(root, `.iris/inbox/context-${phase}-${agent}.md`);
|
|
106
106
|
writeFile(outFile, markdown);
|
|
107
107
|
// Write LATEST
|
|
108
108
|
const latestFile = path.join(root, ".iris/inbox/LATEST.md");
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import kleur from "kleur";
|
|
4
|
+
import { ensureDir } from "../lib.js";
|
|
5
|
+
const TIMELINES_DIR = path.join(process.cwd(), "memory-bank/timelines");
|
|
6
|
+
/**
|
|
7
|
+
* Generate a report for a completed bolt
|
|
8
|
+
*/
|
|
9
|
+
export function generateBoltReport(boltId, runState) {
|
|
10
|
+
const bolt = runState.bolts.find(b => b.id === boltId);
|
|
11
|
+
if (!bolt)
|
|
12
|
+
return;
|
|
13
|
+
// Find events related to this bolt
|
|
14
|
+
const events = runState.events.filter(e => e.boltId === boltId);
|
|
15
|
+
// Calculate duration? (Requires timestamps in BoltState or inferred from events)
|
|
16
|
+
// For now simplfy.
|
|
17
|
+
let report = `# Bolt Report: ${boltId}\n\n`;
|
|
18
|
+
report += `**Status:** ${bolt.status}\n`;
|
|
19
|
+
report += `**Completed:** ${new Date().toISOString()}\n\n`;
|
|
20
|
+
report += `## Summary\n`;
|
|
21
|
+
report += `Successfully executed bolt ${boltId}.\n\n`;
|
|
22
|
+
report += `## Changes\n`;
|
|
23
|
+
// We could track files changed in RunEvent if we updated executeBoltExecution to save them properly
|
|
24
|
+
// For now, list artifacts mentioning this bolt?
|
|
25
|
+
// Or just say "See git history".
|
|
26
|
+
report += `See version control for code changes.\n\n`;
|
|
27
|
+
report += `## Verification\n`;
|
|
28
|
+
const testEvents = events.filter(e => e.action === "Testing completed");
|
|
29
|
+
if (testEvents.length > 0) {
|
|
30
|
+
testEvents.forEach(e => {
|
|
31
|
+
report += `### Test Results\n`;
|
|
32
|
+
report += `${e.message}\n\n`;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
report += `No specific test results recorded.\n\n`;
|
|
37
|
+
}
|
|
38
|
+
report += `## Event Log\n`;
|
|
39
|
+
events.forEach(e => {
|
|
40
|
+
report += `- [${e.timestamp}] ${e.action}\n`;
|
|
41
|
+
});
|
|
42
|
+
const reportPath = path.join(TIMELINES_DIR, `bolt-${boltId}-report.md`);
|
|
43
|
+
ensureDir(TIMELINES_DIR);
|
|
44
|
+
fs.writeFileSync(reportPath, report, "utf8");
|
|
45
|
+
console.log(kleur.green(` ✓ Generated report: memory-bank/timelines/bolt-${boltId}-report.md`));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Generate final project report
|
|
49
|
+
*/
|
|
50
|
+
export function generateFinalReport(runState) {
|
|
51
|
+
let report = `# Project Completion Report\n\n`;
|
|
52
|
+
report += `**Intent:** ${runState.intent}\n`;
|
|
53
|
+
report += `**Completed:** ${new Date().toISOString()}\n\n`;
|
|
54
|
+
report += `## Executive Summary\n`;
|
|
55
|
+
report += `The project has been completed successfully. All planned bolts have been executed and verified.\n\n`;
|
|
56
|
+
report += `## Delivered Bolts\n`;
|
|
57
|
+
runState.bolts.forEach(bolt => {
|
|
58
|
+
report += `- **${bolt.id}**: ${bolt.status}\n`;
|
|
59
|
+
});
|
|
60
|
+
report += `\n`;
|
|
61
|
+
report += `## Artifacts\n`;
|
|
62
|
+
report += `The following key artifacts were generated:\n`;
|
|
63
|
+
report += `- [Requirements](../intents/default/requirements.md)\n`;
|
|
64
|
+
report += `- [System Context](../intents/default/system-context.md)\n`;
|
|
65
|
+
report += `- [Bolt Plan](../operations/bolts/plan.md)\n`;
|
|
66
|
+
report += `\n`;
|
|
67
|
+
report += `## Next Steps\n`;
|
|
68
|
+
report += `1. Review the generated code.\n`;
|
|
69
|
+
report += `2. Perform manual integration testing.\n`;
|
|
70
|
+
report += `3. Deploy to target environment.\n`;
|
|
71
|
+
const reportPath = path.join(process.cwd(), "project-report.md");
|
|
72
|
+
fs.writeFileSync(reportPath, report, "utf8");
|
|
73
|
+
console.log(kleur.bold(kleur.green(`\n✓ Project Report Generated: project-report.md`)));
|
|
74
|
+
}
|
package/package.json
CHANGED