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.
@@ -1,90 +1,24 @@
1
1
  import { Command } from "commander";
2
- import kleur from "kleur";
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
- const BRIDGE_DIR = path.join(process.cwd(), ".iris/bridge");
9
- const INBOX_DIR = path.join(BRIDGE_DIR, "inbox");
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
- // navi bridge run
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
- console.log(kleur.bold("IRIS Bridge Helper"));
21
- console.log(kleur.dim("Watching for workflow tasks...\n"));
22
- const ideId = getSelectedIde();
23
- if (!ideId) {
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
- // navi bridge status
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: navi install\n"));
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: navi bridge run"));
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
- }
@@ -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, saveRunSummary } from "../iris/run-state.js";
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 { executeIntentInception } from "../workflows/intent-inception.js";
9
- import { executeBoltPlan } from "../workflows/bolt-plan.js";
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 (Intent → Bolt Plan → Execution)")
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
- // Get intent
26
- const intent = intentArgs.join(" ");
27
- if (!intent) {
28
- console.error(kleur.red("Error: Intent is required"));
29
- console.log("Usage: navi develop \"Build a dashboard UI...\"");
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 Workflow"));
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
- console.log(kleur.cyan(`Run ID: ${runState.runId}`));
56
- console.log(kleur.dim(`Resume with: navi develop --resume ${runState.runId}`));
57
- console.log("");
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
- console.log(kleur.bold("Workflow Execution"));
77
- console.log(kleur.gray(`Using ${connector.displayName} connector`));
78
- console.log("");
79
- // Check connector availability
80
- const available = await connector.isAvailable();
81
- if (!available) {
82
- console.log(kleur.yellow("⚠ Connector not fully available, using fallback mode"));
83
- }
84
- // Ensure bridge is running
85
- if (connector.ensureRunning) {
86
- await connector.ensureRunning();
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
- // Execute stages based on current stage
89
- while (runState.stage !== WorkflowStage.DONE) {
90
- console.log(kleur.bold().cyan(`\n=== Stage: ${runState.stage} ===\n`));
91
- switch (runState.stage) {
92
- case WorkflowStage.INTENT_INCEPTION:
93
- await executeIntentInception(runState, connector, options);
94
- break;
95
- case WorkflowStage.BOLT_PLAN:
96
- await executeBoltPlan(runState, connector, options);
97
- break;
98
- case WorkflowStage.BOLT_EXECUTION:
99
- await executeBoltExecution(runState, connector, options);
100
- break;
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
- console.log("");
106
- console.log(kleur.bold().green("✓ Workflow Complete!"));
107
- console.log(kleur.gray(`Summary: .iris/runs/${runState.runId}.md`));
140
+ return bolts;
108
141
  }
@@ -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 navi")
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 naviLinkPath = globalBinDir !== "unknown" ? path.join(globalBinDir, "navi") : "unknown";
44
- const naviLinkExists = naviLinkPath !== "unknown" && fs.existsSync(naviLinkPath);
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(naviLinkExists)} navi link: ${naviLinkExists ? `${naviLinkPath} → exists` : `${naviLinkPath} → NOT FOUND`}`);
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 || !naviLinkExists;
71
+ const hasIssues = !cliExists || !binInPath || !irisLinkExists;
72
72
  if (hasIssues) {
73
73
  console.log(kleur.yellow("⚠ Action Required:"));
74
74
  console.log("");
75
- if (!naviLinkExists && cliExists) {
76
- console.log(" Link navi globally:");
77
- console.log(kleur.cyan(" npm run link:navi"));
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 navi globally:");
93
- console.log(kleur.cyan(" navi --version"));
94
- console.log(kleur.cyan(" navi validate"));
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 navi' to see which navi resolves"));
100
- console.log(kleur.dim(" - Use 'npm run navi -- <command>' for repo-local execution"));
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
  });
@@ -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
- // If defaults empty, fallback to Claude + Cursor
14
- const defaultSelection = defaults.length > 0 ? defaults : ["claude", "cursor"];
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 navi develop
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 'navi develop'?",
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
@@ -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-pack.<agent>.<phase>.md)")
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)
@@ -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("navi validate --apply") + " to commit this transition.");
36
+ console.log(kleur.bold("Next: ") + "Run " + kleur.green("iris validate --apply") + " to commit this transition.");
37
37
  console.log("");
38
38
  });
@@ -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 `navi validate` for full details."));
91
+ console.log(kleur.dim("Hint: Run `iris validate` for full details."));
92
92
  }
93
93
  else {
94
94
  console.log("");
@@ -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 navi validate"
8
- // If we run 'navi run', it probably SHOULD update the last_validation timestamp to show we checked.
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 navi validate for details"));
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);
@@ -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-pack.${agent}.${phase}.md`);
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
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "project-iris",
3
- "version": "0.0.06",
3
+ "version": "0.0.07",
4
4
  "type": "module",
5
5
  "bin": {
6
- "iris": "dist/cli.js"
6
+ "iris": "dist/cli.js",
7
+ "project-iris": "dist/cli.js"
7
8
  },
8
9
  "files": [
9
10
  "dist/**",
@@ -1,6 +0,0 @@
1
- # IRIS (Doctrine)
2
-
3
- IRIS doctrine is stored in `.iris/aidlc/`.
4
- Use the workflow entrypoints under `.antigravity/workflows/` to run the agents.
5
-
6
- Always load `.iris/aidlc/context/load-order.md` first.