project-iris 0.0.6
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 +384 -0
- package/dist/bridge/connector-factory.js +27 -0
- package/dist/bridge/connectors/antigravity-connector.js +18 -0
- package/dist/bridge/connectors/cursor-connector.js +31 -0
- package/dist/bridge/connectors/vscode-connector.js +31 -0
- package/dist/bridge/connectors/windsurf-connector.js +23 -0
- package/dist/bridge/filesystem-connector.js +100 -0
- package/dist/bridge/types.js +10 -0
- package/dist/cli.js +30 -0
- package/dist/commands/ask.js +232 -0
- package/dist/commands/bridge.js +259 -0
- package/dist/commands/develop.js +108 -0
- package/dist/commands/doctor.js +102 -0
- package/dist/commands/install.js +57 -0
- package/dist/commands/pack.js +27 -0
- package/dist/commands/phase.js +38 -0
- package/dist/commands/run.js +17 -0
- package/dist/commands/status.js +105 -0
- package/dist/commands/uninstall.js +12 -0
- package/dist/commands/validate.js +87 -0
- package/dist/iris/artifact-checker.js +78 -0
- package/dist/iris/fixer.js +143 -0
- package/dist/iris/guard.js +38 -0
- package/dist/iris/include.js +49 -0
- package/dist/iris/installer.js +269 -0
- package/dist/iris/manifest.js +54 -0
- package/dist/iris/packer.js +303 -0
- package/dist/iris/policy.js +28 -0
- package/dist/iris/report.js +53 -0
- package/dist/iris/resolver.js +63 -0
- package/dist/iris/router.js +114 -0
- package/dist/iris/routes.js +20 -0
- package/dist/iris/run-state.js +143 -0
- package/dist/iris/state.js +85 -0
- package/dist/iris/uninstaller.js +166 -0
- package/dist/iris/validator.js +329 -0
- package/dist/lib.js +96 -0
- package/dist/utils/exit-codes.js +7 -0
- package/dist/workflows/bolt-execution.js +238 -0
- package/dist/workflows/bolt-plan.js +192 -0
- package/dist/workflows/intent-inception.js +188 -0
- package/package.json +41 -0
- package/src/iris_bundle/.iris/aidlc/README.md +16 -0
- package/src/iris_bundle/.iris/aidlc/agents/iris-construction-agent.md +35 -0
- package/src/iris_bundle/.iris/aidlc/agents/iris-inception-agent.md +30 -0
- package/src/iris_bundle/.iris/aidlc/agents/iris-master-agent.md +35 -0
- package/src/iris_bundle/.iris/aidlc/agents/iris-operations-agent.md +29 -0
- package/src/iris_bundle/.iris/aidlc/commands/iris-construction-agent.md +18 -0
- package/src/iris_bundle/.iris/aidlc/commands/iris-inception-agent.md +18 -0
- package/src/iris_bundle/.iris/aidlc/commands/iris-master-agent.md +18 -0
- package/src/iris_bundle/.iris/aidlc/commands/iris-operations-agent.md +18 -0
- package/src/iris_bundle/.iris/aidlc/context/context-map.md +25 -0
- package/src/iris_bundle/.iris/aidlc/context/exclusion-rules.md +13 -0
- package/src/iris_bundle/.iris/aidlc/context/load-order.md +25 -0
- package/src/iris_bundle/.iris/aidlc/memory/intent-rules.md +9 -0
- package/src/iris_bundle/.iris/aidlc/memory/log-rules.md +5 -0
- package/src/iris_bundle/.iris/aidlc/memory/memory-bank.yaml +39 -0
- package/src/iris_bundle/.iris/aidlc/memory/unit-rules.md +9 -0
- package/src/iris_bundle/.iris/aidlc/quick-start.md +24 -0
- package/src/iris_bundle/.iris/aidlc/skills/execution/implementation.md +14 -0
- package/src/iris_bundle/.iris/aidlc/skills/execution/refactoring.md +13 -0
- package/src/iris_bundle/.iris/aidlc/skills/execution/scaffold-generation.md +15 -0
- package/src/iris_bundle/.iris/aidlc/skills/governance/escalation.md +13 -0
- package/src/iris_bundle/.iris/aidlc/skills/governance/quality-gates.md +14 -0
- package/src/iris_bundle/.iris/aidlc/skills/governance/stop-conditions.md +11 -0
- package/src/iris_bundle/.iris/aidlc/skills/reasoning/decomposition.md +23 -0
- package/src/iris_bundle/.iris/aidlc/skills/reasoning/risk-analysis.md +14 -0
- package/src/iris_bundle/.iris/aidlc/skills/reasoning/verification.md +21 -0
- package/src/iris_bundle/.iris/aidlc/standards/artifacts-registry.md +38 -0
- package/src/iris_bundle/.iris/aidlc/standards/decision-logging.md +16 -0
- package/src/iris_bundle/.iris/aidlc/standards/doctrine-structure.md +31 -0
- package/src/iris_bundle/.iris/aidlc/standards/documentation-rules.md +15 -0
- package/src/iris_bundle/.iris/aidlc/standards/file-structure.md +21 -0
- package/src/iris_bundle/.iris/aidlc/standards/naming-conventions.md +18 -0
- package/src/iris_bundle/.iris/aidlc/standards/phases-and-gates.md +25 -0
- package/src/iris_bundle/.iris/aidlc/standards/routes-and-routing.md +35 -0
- package/src/iris_bundle/.iris/aidlc/standards/tool-wrappers.md +32 -0
- package/src/iris_bundle/.iris/aidlc/templates/bolt.md +23 -0
- package/src/iris_bundle/.iris/aidlc/templates/doctrine-doc-template.md +33 -0
- package/src/iris_bundle/.iris/aidlc/templates/intent.md +23 -0
- package/src/iris_bundle/.iris/aidlc/templates/log.md +24 -0
- package/src/iris_bundle/.iris/aidlc/templates/review.md +21 -0
- package/src/iris_bundle/.iris/aidlc/templates/unit.md +31 -0
- package/src/iris_bundle/.iris/aidlc/validation/failure-modes.md +16 -0
- package/src/iris_bundle/.iris/aidlc/validation/phase-preconditions.md +21 -0
- package/src/iris_bundle/.iris/aidlc/validation/quality-checklist.md +20 -0
- package/src/iris_bundle/.iris/policy.yaml +27 -0
- package/src/iris_bundle/.iris/routes.yaml +98 -0
- package/src/iris_bundle/.iris/state.yaml +7 -0
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/knowledge/IRIS.md +6 -0
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-construction-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-inception-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-master-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-operations-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/claude/.claude/claude.md +9 -0
- package/src/iris_bundle/.iris/tools/claude/.claude/commands/compare-specs.md +203 -0
- package/src/iris_bundle/.iris/tools/claude/.claude/commands/iris-construction-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/claude/.claude/commands/iris-inception-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/claude/.claude/commands/iris-master-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/claude/.claude/commands/iris-operations-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/codex/AGENTS.md +15 -0
- package/src/iris_bundle/.iris/tools/cursor/.cursor/commands/iris-construction-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/cursor/.cursor/commands/iris-inception-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/cursor/.cursor/commands/iris-master-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/cursor/.cursor/commands/iris-operations-agent.md +25 -0
- package/src/iris_bundle/.iris/tools/gemini/.gemini/commands/iris-construction-agent.toml +29 -0
- package/src/iris_bundle/.iris/tools/gemini/.gemini/commands/iris-inception-agent.toml +29 -0
- package/src/iris_bundle/.iris/tools/gemini/.gemini/commands/iris-master-agent.toml +29 -0
- package/src/iris_bundle/.iris/tools/gemini/.gemini/commands/iris-operations-agent.toml +29 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import inquirer from "inquirer";
|
|
3
|
+
import kleur from "kleur";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import { repoRoot, writeFile, ensureDir } from "../lib.js";
|
|
7
|
+
import { getIrisDebugInfo } from "../iris/resolver.js";
|
|
8
|
+
import { loadRoutes } from "../iris/routes.js";
|
|
9
|
+
import { routeIntent } from "../iris/router.js";
|
|
10
|
+
import { generatePack } from "../iris/packer.js";
|
|
11
|
+
import { checkArtifact } from "../iris/artifact-checker.js";
|
|
12
|
+
import { loadPolicy } from "../iris/policy.js";
|
|
13
|
+
import { loadState } from "../iris/state.js";
|
|
14
|
+
import { EXIT_CODES } from "../utils/exit-codes.js";
|
|
15
|
+
export const askCommand = new Command("ask")
|
|
16
|
+
.description("Route intent to an agent and generate context pack")
|
|
17
|
+
.argument("[intent...]", "Natural language request intent")
|
|
18
|
+
.option("--agent <agent>", "Override target agent")
|
|
19
|
+
.option("--phase <phase>", "Override target phase")
|
|
20
|
+
.option("--out <path>", "Explicit output path for pack")
|
|
21
|
+
.option("--stdout", "Print pack to stdout", false)
|
|
22
|
+
.option("--json", "Output structured JSON result", false)
|
|
23
|
+
.option("--dry-run", "Simulate routing without writing files", false)
|
|
24
|
+
.option("--debug", "Show IRIS authority and artifact resolution info", false)
|
|
25
|
+
.action(async (intentParts, opts) => {
|
|
26
|
+
const root = repoRoot();
|
|
27
|
+
if (opts.debug) {
|
|
28
|
+
const debug = getIrisDebugInfo(root);
|
|
29
|
+
console.log(kleur.bold("IRIS Authority Debug"));
|
|
30
|
+
console.log(`- Repo Root: ${debug.repoRoot}`);
|
|
31
|
+
console.log(`- IRIS Root: ${debug.irisRoot}`);
|
|
32
|
+
console.log(`- Source: ${kleur.magenta(debug.source.toUpperCase())}`);
|
|
33
|
+
console.log(`- Bundled Path:${debug.bundledPath}`);
|
|
34
|
+
console.log("");
|
|
35
|
+
// Show artifact validation details
|
|
36
|
+
const policy = loadPolicy();
|
|
37
|
+
const state = loadState();
|
|
38
|
+
const phase = state.phase.current;
|
|
39
|
+
const requirements = policy.phases[phase]?.requires || [];
|
|
40
|
+
if (requirements.length > 0) {
|
|
41
|
+
console.log(kleur.bold("Required Artifacts Debug"));
|
|
42
|
+
for (const req of requirements) {
|
|
43
|
+
const check = checkArtifact(root, req.path, req.type);
|
|
44
|
+
console.log(`\n- Artifact: ${req.type}`);
|
|
45
|
+
console.log(` Raw: ${JSON.stringify(req.path)}`);
|
|
46
|
+
console.log(` Normalized: ${check.normalized}`);
|
|
47
|
+
console.log(` Absolute: ${check.absolutePath}`);
|
|
48
|
+
console.log(` Inferred: ${check.inferredKind}`);
|
|
49
|
+
console.log(` Exists: ${check.exists ? kleur.green("YES") : kleur.red("NO")}`);
|
|
50
|
+
if (check.exists) {
|
|
51
|
+
console.log(` Type: ${check.isDirectory ? "directory" : check.isFile ? "file" : "unknown"}`);
|
|
52
|
+
}
|
|
53
|
+
if (check.hasControlChars) {
|
|
54
|
+
console.log(` ${kleur.yellow("⚠ WARNING: Raw path contains control characters (\\r, \\n, etc.)")}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
console.log("");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// 1. Get Intent
|
|
61
|
+
let intent = intentParts.join(" ");
|
|
62
|
+
if (!intent) {
|
|
63
|
+
if (process.stdin.isTTY) {
|
|
64
|
+
const { input } = await inquirer.prompt([
|
|
65
|
+
{ type: "input", name: "input", message: "What do you want to do?" }
|
|
66
|
+
]);
|
|
67
|
+
intent = input;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
intent = fs.readFileSync(0, "utf-8").trim();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!intent) {
|
|
74
|
+
console.error(kleur.red("No intent provided."));
|
|
75
|
+
process.exit(EXIT_CODES.INVALID);
|
|
76
|
+
}
|
|
77
|
+
// 2. Load Routes
|
|
78
|
+
const routesConfig = loadRoutes();
|
|
79
|
+
if (!routesConfig) {
|
|
80
|
+
console.error(kleur.red("Routes not found. Run `navi install` or create .iris/routes.yaml"));
|
|
81
|
+
process.exit(EXIT_CODES.POLICY_ERROR);
|
|
82
|
+
}
|
|
83
|
+
// 3. Route
|
|
84
|
+
const routing = routeIntent(intent, routesConfig);
|
|
85
|
+
const routeId = routing.route === "default" ? "default" : routing.route.route_id;
|
|
86
|
+
// 4. Decide Context
|
|
87
|
+
let agent = opts.agent;
|
|
88
|
+
let phase = opts.phase;
|
|
89
|
+
let command = "";
|
|
90
|
+
if (routing.route === "default") {
|
|
91
|
+
if (!agent)
|
|
92
|
+
agent = routesConfig.default.agent;
|
|
93
|
+
if (!phase)
|
|
94
|
+
phase = routesConfig.default.phase;
|
|
95
|
+
command = routesConfig.default.command;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const selectedRoute = routing.route;
|
|
99
|
+
// If agent missing in route, try to infer or fallback
|
|
100
|
+
if (!agent)
|
|
101
|
+
agent = selectedRoute.agent || inferAgentFromPhase(selectedRoute.phase);
|
|
102
|
+
if (!phase)
|
|
103
|
+
phase = selectedRoute.phase ? selectedRoute.phase.toLowerCase() : "";
|
|
104
|
+
command = selectedRoute.command;
|
|
105
|
+
}
|
|
106
|
+
// Fallback inference if agent is still missing (e.g. default route missing fields)
|
|
107
|
+
if (!agent && command) {
|
|
108
|
+
if (command.includes("inception"))
|
|
109
|
+
agent = "inception";
|
|
110
|
+
else if (command.includes("construction"))
|
|
111
|
+
agent = "construction";
|
|
112
|
+
else if (command.includes("operations"))
|
|
113
|
+
agent = "operations";
|
|
114
|
+
else if (command.includes("master"))
|
|
115
|
+
agent = "master";
|
|
116
|
+
}
|
|
117
|
+
// Override command logic if agent/phase changed via flags
|
|
118
|
+
if (opts.agent) {
|
|
119
|
+
switch (opts.agent) {
|
|
120
|
+
case "master":
|
|
121
|
+
command = "/iris-master-agent";
|
|
122
|
+
break;
|
|
123
|
+
case "inception":
|
|
124
|
+
command = "/iris-inception-agent";
|
|
125
|
+
break;
|
|
126
|
+
case "construction":
|
|
127
|
+
command = "/iris-construction-agent";
|
|
128
|
+
break;
|
|
129
|
+
case "operations":
|
|
130
|
+
command = "/iris-operations-agent";
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 5. Pack
|
|
135
|
+
let packPath = "";
|
|
136
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
137
|
+
if (opts.out) {
|
|
138
|
+
packPath = opts.out;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
packPath = path.join(root, `.iris/inbox/context-pack.${agent}.${phase}.${timestamp}.md`);
|
|
142
|
+
}
|
|
143
|
+
if (!opts.dryRun) {
|
|
144
|
+
try {
|
|
145
|
+
await generatePack({
|
|
146
|
+
agent,
|
|
147
|
+
phase,
|
|
148
|
+
output: opts.stdout ? undefined : packPath,
|
|
149
|
+
stdout: opts.stdout,
|
|
150
|
+
strict: false
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
console.error("Packing failed:", e);
|
|
155
|
+
process.exit(EXIT_CODES.INVALID);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// 6. Write Next Action
|
|
159
|
+
const nextPath = path.join(root, ".iris/inbox/next.md");
|
|
160
|
+
const nextHistoryPath = path.join(root, `.iris/inbox/next.${timestamp}.md`);
|
|
161
|
+
const nextContent = `# IRIS Next Action
|
|
162
|
+
|
|
163
|
+
## Intent
|
|
164
|
+
${intent}
|
|
165
|
+
|
|
166
|
+
## Routing Decision
|
|
167
|
+
- Route: ${routeId}
|
|
168
|
+
- Agent: ${agent}
|
|
169
|
+
- Phase: ${phase}
|
|
170
|
+
- Command: ${command}
|
|
171
|
+
- Score: ${routing.score}
|
|
172
|
+
- Matched: ${routing.matches.join(", ")}
|
|
173
|
+
${routing.excludedBy ? `- Excluded By: ${routing.excludedBy}` : ""}
|
|
174
|
+
|
|
175
|
+
## Context Pack
|
|
176
|
+
- Path: ${opts.stdout ? "(stdout)" : path.relative(root, packPath)}
|
|
177
|
+
- Generated: ${new Date().toISOString()}
|
|
178
|
+
|
|
179
|
+
## Do this now
|
|
180
|
+
1) Open your IDE agent tool
|
|
181
|
+
2) Run: \`${command}\`
|
|
182
|
+
3) Provide the context pack at: \`${opts.stdout ? "(stdout)" : path.relative(root, packPath)}\`
|
|
183
|
+
|
|
184
|
+
## Notes
|
|
185
|
+
- If validation fails, run: \`navi validate\`
|
|
186
|
+
`;
|
|
187
|
+
if (!opts.dryRun) {
|
|
188
|
+
ensureDir(path.dirname(nextPath));
|
|
189
|
+
writeFile(nextPath, nextContent);
|
|
190
|
+
writeFile(nextHistoryPath, nextContent);
|
|
191
|
+
}
|
|
192
|
+
// 7. Output
|
|
193
|
+
if (opts.json) {
|
|
194
|
+
console.log(JSON.stringify({
|
|
195
|
+
intent,
|
|
196
|
+
route: routeId,
|
|
197
|
+
agent,
|
|
198
|
+
phase,
|
|
199
|
+
command,
|
|
200
|
+
score: routing.score,
|
|
201
|
+
matches: routing.matches,
|
|
202
|
+
pack_path: opts.stdout ? null : packPath,
|
|
203
|
+
next_path: nextPath
|
|
204
|
+
}, null, 2));
|
|
205
|
+
}
|
|
206
|
+
else if (!opts.stdout) {
|
|
207
|
+
console.log("");
|
|
208
|
+
console.log(kleur.bold("Routing: ") + kleur.cyan(routeId) + kleur.dim(` (conf: ${routing.score})`));
|
|
209
|
+
console.log(kleur.bold("Command: ") + kleur.green(command));
|
|
210
|
+
if (opts.dryRun) {
|
|
211
|
+
console.log(kleur.yellow("[DRY RUN] No files written."));
|
|
212
|
+
console.log(`Would satisfy intent '${intent}' with agent ${agent}.`);
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
console.log(kleur.bold("Context: ") + kleur.gray(path.relative(root, packPath)));
|
|
216
|
+
console.log(kleur.bold("Next: ") + kleur.gray(path.relative(root, nextPath)));
|
|
217
|
+
}
|
|
218
|
+
console.log("");
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
function inferAgentFromPhase(phase) {
|
|
222
|
+
if (!phase)
|
|
223
|
+
return "master";
|
|
224
|
+
const p = phase.toLowerCase();
|
|
225
|
+
if (p === "inception")
|
|
226
|
+
return "inception";
|
|
227
|
+
if (p === "construction")
|
|
228
|
+
return "construction";
|
|
229
|
+
if (p === "operations")
|
|
230
|
+
return "operations";
|
|
231
|
+
return "master";
|
|
232
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
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";
|
|
6
|
+
import { getSelectedIde } from "../iris/state.js";
|
|
7
|
+
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");
|
|
12
|
+
export const bridgeCommand = new Command("bridge")
|
|
13
|
+
.description("Bridge helper commands for IDE integration");
|
|
14
|
+
// navi bridge run
|
|
15
|
+
bridgeCommand
|
|
16
|
+
.command("run")
|
|
17
|
+
.description("Start bridge helper (watches inbox, opens IDE, waits for results)")
|
|
18
|
+
.option("--poll-interval <ms>", "Polling interval in milliseconds", "2000")
|
|
19
|
+
.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
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// navi bridge status
|
|
88
|
+
bridgeCommand
|
|
89
|
+
.command("status")
|
|
90
|
+
.description("Show bridge status and IDE information")
|
|
91
|
+
.action(() => {
|
|
92
|
+
console.log("");
|
|
93
|
+
console.log(kleur.bold("IRIS Bridge Status"));
|
|
94
|
+
console.log("===================\n");
|
|
95
|
+
// IDE selection
|
|
96
|
+
const ideId = getSelectedIde();
|
|
97
|
+
if (ideId) {
|
|
98
|
+
console.log(kleur.bold("Selected IDE:"));
|
|
99
|
+
console.log(` ${kleur.cyan(ideId)}\n`);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.log(kleur.yellow("⚠ No IDE selected"));
|
|
103
|
+
console.log(kleur.dim(" Run: navi install\n"));
|
|
104
|
+
}
|
|
105
|
+
// Connector availability
|
|
106
|
+
if (ideId) {
|
|
107
|
+
const connector = createConnector(ideId);
|
|
108
|
+
console.log(kleur.bold("Connector:"));
|
|
109
|
+
console.log(` Type: ${connector.displayName}`);
|
|
110
|
+
console.log(` ID: ${connector.id}\n`);
|
|
111
|
+
}
|
|
112
|
+
// Bridge directories
|
|
113
|
+
console.log(kleur.bold("Bridge Directories:"));
|
|
114
|
+
const dirs = [
|
|
115
|
+
{ name: "Inbox", path: INBOX_DIR },
|
|
116
|
+
{ name: "Outbox", path: OUTBOX_DIR },
|
|
117
|
+
{ name: "State", path: STATE_DIR }
|
|
118
|
+
];
|
|
119
|
+
dirs.forEach(({ name, path: dirPath }) => {
|
|
120
|
+
const exists = fs.existsSync(dirPath);
|
|
121
|
+
const status = exists ? kleur.green("✓") : kleur.red("✗");
|
|
122
|
+
console.log(` ${status} ${name}: ${dirPath}`);
|
|
123
|
+
if (exists) {
|
|
124
|
+
const files = fs.readdirSync(dirPath);
|
|
125
|
+
if (files.length > 0) {
|
|
126
|
+
console.log(kleur.dim(` ${files.length} file(s)`));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
console.log("");
|
|
131
|
+
// Pending tasks
|
|
132
|
+
if (fs.existsSync(INBOX_DIR)) {
|
|
133
|
+
const tasks = fs.readdirSync(INBOX_DIR).filter(f => f.endsWith(".json"));
|
|
134
|
+
if (tasks.length > 0) {
|
|
135
|
+
console.log(kleur.bold("Pending Tasks:"));
|
|
136
|
+
tasks.forEach(task => {
|
|
137
|
+
console.log(kleur.yellow(` - ${task.replace(".json", "")}`));
|
|
138
|
+
});
|
|
139
|
+
console.log("");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Helper running check
|
|
143
|
+
console.log(kleur.bold("Bridge Helper:"));
|
|
144
|
+
console.log(kleur.dim(" To start: navi bridge run"));
|
|
145
|
+
console.log("");
|
|
146
|
+
});
|
|
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
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import inquirer from "inquirer";
|
|
3
|
+
import kleur from "kleur";
|
|
4
|
+
import { getSelectedIde, setSelectedIde } from "../iris/state.js";
|
|
5
|
+
import { createRun, loadRun, saveRun, saveRunSummary } from "../iris/run-state.js";
|
|
6
|
+
import { createConnector } from "../bridge/connector-factory.js";
|
|
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";
|
|
11
|
+
export const developCommand = new Command("develop")
|
|
12
|
+
.description("Run automated IRIS workflow (Intent → Bolt Plan → Execution)")
|
|
13
|
+
.argument("[intent...]", "Intent description")
|
|
14
|
+
.option("--resume <runId>", "Resume a previous run")
|
|
15
|
+
.option("--ide <ideId>", "Override IDE selection")
|
|
16
|
+
.option("--no-bridge", "Fallback mode (generate files, print paths)")
|
|
17
|
+
.option("--gate <mode>", "Gate approval mode: auto or manual", "manual")
|
|
18
|
+
.action(async (intentArgs, options) => {
|
|
19
|
+
// Handle resume
|
|
20
|
+
if (options.resume) {
|
|
21
|
+
console.log(kleur.cyan(`Resuming run: ${options.resume}`));
|
|
22
|
+
await resumeRun(options.resume, options);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
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...\"");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
// Get IDE selection
|
|
33
|
+
let ideId = options.ide || getSelectedIde();
|
|
34
|
+
if (!ideId) {
|
|
35
|
+
// Prompt for IDE selection
|
|
36
|
+
const { selectedIde } = await inquirer.prompt([
|
|
37
|
+
{
|
|
38
|
+
type: "input",
|
|
39
|
+
name: "selectedIde",
|
|
40
|
+
message: "Which IDE will you use? (e.g., cursor, vscode, antigravity)",
|
|
41
|
+
default: "antigravity"
|
|
42
|
+
}
|
|
43
|
+
]);
|
|
44
|
+
ideId = selectedIde;
|
|
45
|
+
setSelectedIde(ideId);
|
|
46
|
+
}
|
|
47
|
+
console.log("");
|
|
48
|
+
console.log(kleur.bold("Starting IRIS Workflow"));
|
|
49
|
+
console.log(kleur.gray(`Intent: ${intent}`));
|
|
50
|
+
console.log(kleur.gray(`IDE: ${ideId}`));
|
|
51
|
+
console.log(kleur.gray(`Gate Mode: ${options.gate}`));
|
|
52
|
+
console.log("");
|
|
53
|
+
// Create run
|
|
54
|
+
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("");
|
|
58
|
+
// Execute workflow
|
|
59
|
+
await executeWorkflow(runState, options);
|
|
60
|
+
});
|
|
61
|
+
async function resumeRun(runId, options) {
|
|
62
|
+
try {
|
|
63
|
+
const runState = loadRun(runId);
|
|
64
|
+
console.log(kleur.gray(`Intent: ${runState.intent}`));
|
|
65
|
+
console.log(kleur.gray(`Current Stage: ${runState.stage}`));
|
|
66
|
+
console.log("");
|
|
67
|
+
await executeWorkflow(runState, options);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error(kleur.red(`Failed to resume run: ${error.message}`));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function executeWorkflow(runState, options) {
|
|
75
|
+
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();
|
|
87
|
+
}
|
|
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;
|
|
101
|
+
}
|
|
102
|
+
saveRun(runState);
|
|
103
|
+
saveRunSummary(runState.runId);
|
|
104
|
+
}
|
|
105
|
+
console.log("");
|
|
106
|
+
console.log(kleur.bold().green("✓ Workflow Complete!"));
|
|
107
|
+
console.log(kleur.gray(`Summary: .iris/runs/${runState.runId}.md`));
|
|
108
|
+
}
|