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,143 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
|
+
import { WorkflowStage } from "../bridge/types.js";
|
|
5
|
+
const RUNS_DIR = path.join(process.cwd(), ".iris/runs");
|
|
6
|
+
/**
|
|
7
|
+
* Create a new workflow run
|
|
8
|
+
*/
|
|
9
|
+
export function createRun(intent, ideId, gateMode = "manual") {
|
|
10
|
+
const runId = randomUUID();
|
|
11
|
+
const now = new Date().toISOString();
|
|
12
|
+
const runState = {
|
|
13
|
+
runId,
|
|
14
|
+
intent,
|
|
15
|
+
ideId,
|
|
16
|
+
stage: WorkflowStage.INTENT_INCEPTION,
|
|
17
|
+
gateMode,
|
|
18
|
+
artifacts: [],
|
|
19
|
+
bolts: [],
|
|
20
|
+
currentBoltIndex: 0,
|
|
21
|
+
pendingTasks: [],
|
|
22
|
+
events: [],
|
|
23
|
+
createdAt: now,
|
|
24
|
+
updatedAt: now
|
|
25
|
+
};
|
|
26
|
+
saveRun(runState);
|
|
27
|
+
return runState;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load a run by ID
|
|
31
|
+
*/
|
|
32
|
+
export function loadRun(runId) {
|
|
33
|
+
const runPath = path.join(RUNS_DIR, `${runId}.json`);
|
|
34
|
+
if (!fs.existsSync(runPath)) {
|
|
35
|
+
throw new Error(`Run not found: ${runId}`);
|
|
36
|
+
}
|
|
37
|
+
const content = fs.readFileSync(runPath, "utf8");
|
|
38
|
+
return JSON.parse(content);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Save run state
|
|
42
|
+
*/
|
|
43
|
+
export function saveRun(state) {
|
|
44
|
+
if (!fs.existsSync(RUNS_DIR)) {
|
|
45
|
+
fs.mkdirSync(RUNS_DIR, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
state.updatedAt = new Date().toISOString();
|
|
48
|
+
const runPath = path.join(RUNS_DIR, `${state.runId}.json`);
|
|
49
|
+
fs.writeFileSync(runPath, JSON.stringify(state, null, 2), "utf8");
|
|
50
|
+
// Also save a small state snapshot for quick resume
|
|
51
|
+
const statePath = path.join(RUNS_DIR, `${state.runId}.state.json`);
|
|
52
|
+
const snapshot = {
|
|
53
|
+
runId: state.runId,
|
|
54
|
+
stage: state.stage,
|
|
55
|
+
currentBoltIndex: state.currentBoltIndex,
|
|
56
|
+
updatedAt: state.updatedAt
|
|
57
|
+
};
|
|
58
|
+
fs.writeFileSync(statePath, JSON.stringify(snapshot, null, 2), "utf8");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Append an event to the run
|
|
62
|
+
*/
|
|
63
|
+
export function appendEvent(runId, event) {
|
|
64
|
+
const state = loadRun(runId);
|
|
65
|
+
const fullEvent = {
|
|
66
|
+
...event,
|
|
67
|
+
timestamp: new Date().toISOString()
|
|
68
|
+
};
|
|
69
|
+
state.events.push(fullEvent);
|
|
70
|
+
saveRun(state);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Generate a human-readable summary of the run
|
|
74
|
+
*/
|
|
75
|
+
export function generateRunSummary(runId) {
|
|
76
|
+
const state = loadRun(runId);
|
|
77
|
+
let summary = `# IRIS Workflow Run: ${runId}\n\n`;
|
|
78
|
+
summary += `**Intent:** ${state.intent}\n`;
|
|
79
|
+
summary += `**IDE:** ${state.ideId}\n`;
|
|
80
|
+
summary += `**Stage:** ${state.stage}\n`;
|
|
81
|
+
summary += `**Created:** ${state.createdAt}\n`;
|
|
82
|
+
summary += `**Updated:** ${state.updatedAt}\n\n`;
|
|
83
|
+
summary += `## Artifacts Created\n\n`;
|
|
84
|
+
if (state.artifacts.length > 0) {
|
|
85
|
+
summary += state.artifacts.map(a => `- \`${a}\``).join("\n") + "\n\n";
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
summary += "*No artifacts yet*\n\n";
|
|
89
|
+
}
|
|
90
|
+
summary += `## Bolts\n\n`;
|
|
91
|
+
if (state.bolts.length > 0) {
|
|
92
|
+
state.bolts.forEach(bolt => {
|
|
93
|
+
summary += `### ${bolt.id} (${bolt.status})\n`;
|
|
94
|
+
if (bolt.designCompleted)
|
|
95
|
+
summary += "- ✓ Design\n";
|
|
96
|
+
if (bolt.implementationCompleted)
|
|
97
|
+
summary += "- ✓ Implementation\n";
|
|
98
|
+
if (bolt.testingCompleted)
|
|
99
|
+
summary += "- ✓ Testing\n";
|
|
100
|
+
summary += "\n";
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
summary += "*No bolts yet*\n\n";
|
|
105
|
+
}
|
|
106
|
+
summary += `## Event Log\n\n`;
|
|
107
|
+
state.events.forEach(event => {
|
|
108
|
+
summary += `**${event.timestamp}** [${event.stage}] ${event.action}\n`;
|
|
109
|
+
if (event.message)
|
|
110
|
+
summary += ` ${event.message}\n`;
|
|
111
|
+
if (event.userGateDecision)
|
|
112
|
+
summary += ` Gate: ${event.userGateDecision}\n`;
|
|
113
|
+
summary += "\n";
|
|
114
|
+
});
|
|
115
|
+
return summary;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Save run summary to markdown file
|
|
119
|
+
*/
|
|
120
|
+
export function saveRunSummary(runId) {
|
|
121
|
+
const summary = generateRunSummary(runId);
|
|
122
|
+
const summaryPath = path.join(RUNS_DIR, `${runId}.md`);
|
|
123
|
+
fs.writeFileSync(summaryPath, summary, "utf8");
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* List all runs
|
|
127
|
+
*/
|
|
128
|
+
export function listRuns() {
|
|
129
|
+
if (!fs.existsSync(RUNS_DIR)) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const files = fs.readdirSync(RUNS_DIR);
|
|
133
|
+
const stateFiles = files.filter(f => f.endsWith(".state.json"));
|
|
134
|
+
return stateFiles.map(file => {
|
|
135
|
+
const content = fs.readFileSync(path.join(RUNS_DIR, file), "utf8");
|
|
136
|
+
const snapshot = JSON.parse(content);
|
|
137
|
+
return {
|
|
138
|
+
runId: snapshot.runId,
|
|
139
|
+
stage: snapshot.stage,
|
|
140
|
+
updatedAt: snapshot.updatedAt
|
|
141
|
+
};
|
|
142
|
+
}).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
143
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import { EXIT_CODES } from "../utils/exit-codes.js";
|
|
5
|
+
const STATE_FILE_PATH = path.join(process.cwd(), ".iris/state.yaml");
|
|
6
|
+
export const DEFAULT_STATE = {
|
|
7
|
+
version: 1,
|
|
8
|
+
phase: {
|
|
9
|
+
current: "inception",
|
|
10
|
+
requested: null,
|
|
11
|
+
},
|
|
12
|
+
active: {
|
|
13
|
+
intent_id: null,
|
|
14
|
+
unit_id: null,
|
|
15
|
+
bolt_id: null,
|
|
16
|
+
},
|
|
17
|
+
last_validation: {
|
|
18
|
+
at: null,
|
|
19
|
+
result: null,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export function loadState() {
|
|
23
|
+
try {
|
|
24
|
+
if (!fs.existsSync(STATE_FILE_PATH)) {
|
|
25
|
+
// Auto-create directory if it doesn't exist
|
|
26
|
+
const dir = path.dirname(STATE_FILE_PATH);
|
|
27
|
+
if (!fs.existsSync(dir)) {
|
|
28
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
saveState(DEFAULT_STATE);
|
|
31
|
+
return DEFAULT_STATE;
|
|
32
|
+
}
|
|
33
|
+
const content = fs.readFileSync(STATE_FILE_PATH, "utf8");
|
|
34
|
+
const doc = yaml.load(content);
|
|
35
|
+
const merged = { ...DEFAULT_STATE, ...doc };
|
|
36
|
+
// Migration/Fixup for legacy/bundled schema
|
|
37
|
+
if (doc.current_phase && !doc.phase) {
|
|
38
|
+
merged.phase = {
|
|
39
|
+
current: doc.current_phase.toLowerCase(), // Normalize to lowercase
|
|
40
|
+
requested: null
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Ensure nested objects exist
|
|
44
|
+
if (!merged.phase)
|
|
45
|
+
merged.phase = { ...DEFAULT_STATE.phase };
|
|
46
|
+
if (!merged.active)
|
|
47
|
+
merged.active = { ...DEFAULT_STATE.active };
|
|
48
|
+
if (!merged.last_validation || typeof merged.last_validation !== 'object') {
|
|
49
|
+
merged.last_validation = { ...DEFAULT_STATE.last_validation };
|
|
50
|
+
}
|
|
51
|
+
return merged;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error("Failed to load or create .iris/state.yaml:", error);
|
|
55
|
+
process.exit(EXIT_CODES.STATE_ERROR);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function saveState(state) {
|
|
59
|
+
try {
|
|
60
|
+
const content = yaml.dump(state);
|
|
61
|
+
fs.writeFileSync(STATE_FILE_PATH, content, "utf8");
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error("Failed to write .iris/state.yaml:", error);
|
|
65
|
+
process.exit(EXIT_CODES.STATE_ERROR);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get the selected IDE from state, or null if not set
|
|
70
|
+
*/
|
|
71
|
+
export function getSelectedIde() {
|
|
72
|
+
const state = loadState();
|
|
73
|
+
return state.ide?.id || null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Set the selected IDE in state
|
|
77
|
+
*/
|
|
78
|
+
export function setSelectedIde(ideId) {
|
|
79
|
+
const state = loadState();
|
|
80
|
+
state.ide = {
|
|
81
|
+
id: ideId,
|
|
82
|
+
selected_at: new Date().toISOString()
|
|
83
|
+
};
|
|
84
|
+
saveState(state);
|
|
85
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import inquirer from "inquirer";
|
|
4
|
+
import kleur from "kleur";
|
|
5
|
+
import { removeDir, repoRoot } from "../lib.js";
|
|
6
|
+
import { loadManifest } from "./manifest.js";
|
|
7
|
+
export async function uninstallIris(options) {
|
|
8
|
+
const root = repoRoot();
|
|
9
|
+
const manifest = loadManifest();
|
|
10
|
+
if (!manifest) {
|
|
11
|
+
console.log("No IRIS manifest found; nothing to uninstall.");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
// Step B: Remove installed tool wrapper files
|
|
15
|
+
const manifestTools = manifest.paths_installed || [];
|
|
16
|
+
let removedCount = 0;
|
|
17
|
+
let skippedCount = 0;
|
|
18
|
+
for (const entry of manifestTools) {
|
|
19
|
+
// Only remove stuff outside .iris (we delete .iris wholesale later)
|
|
20
|
+
if (entry.path.startsWith(".iris/"))
|
|
21
|
+
continue;
|
|
22
|
+
// Only remove if status was installed or overwritten
|
|
23
|
+
if (entry.status !== "installed" && entry.status !== "overwritten")
|
|
24
|
+
continue;
|
|
25
|
+
const targetPath = path.join(root, entry.path);
|
|
26
|
+
if (!fs.existsSync(targetPath))
|
|
27
|
+
continue;
|
|
28
|
+
// Check if modified by user?
|
|
29
|
+
// Method 1: Compare with mirror in .iris/tools/
|
|
30
|
+
// entry.path is like .claude/commands/foo.md
|
|
31
|
+
// We know which tool it belongs to? Not directly from installedPath.
|
|
32
|
+
// But we have tools_selected.
|
|
33
|
+
// Let's rely on finding it in one of the mirrors?
|
|
34
|
+
// Or cleaner: we know the 'source' from manifest? I didn't save 'source' in installedPath in installer.ts (oops).
|
|
35
|
+
// I saved: path, type, status.
|
|
36
|
+
// Recover source bundle path from mirror?
|
|
37
|
+
// We mirrored relevant tools to .iris/tools/<tool>.
|
|
38
|
+
// Check if file exists in ANY mirror that matches content?
|
|
39
|
+
// Or simpler: Check against ALL mirrors.
|
|
40
|
+
let matchFound = false;
|
|
41
|
+
for (const tool of manifest.tools_selected) {
|
|
42
|
+
const mirrorFile = path.join(root, ".iris/tools", tool, entry.path); // Mirror structure mimics repo root?
|
|
43
|
+
// Wrapper in installer.ts: copyDirRec(toolBundlePath, mirrorPath) -> mirrorPath = .iris/tools/<tool>
|
|
44
|
+
// And installToolWrapper copied from toolBundlePath to root.
|
|
45
|
+
// So yes, relative path inside .iris/tools/<tool> should match relative path from root?
|
|
46
|
+
// Wait. toolBundlePath might be .../tools/claude. Content is .claude/...
|
|
47
|
+
// So in mirror: .iris/tools/claude/.claude/...
|
|
48
|
+
// relative path of entry: .claude/...
|
|
49
|
+
// So yes: join(.iris/tools/<tool>, entry.path) should point to the backup.
|
|
50
|
+
if (fs.existsSync(mirrorFile)) {
|
|
51
|
+
if (areFilesEqual(targetPath, mirrorFile)) {
|
|
52
|
+
matchFound = true;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (matchFound) {
|
|
58
|
+
fs.unlinkSync(targetPath);
|
|
59
|
+
// Cleanup parent dirs if empty?
|
|
60
|
+
tryRemoveEmptyParents(targetPath, root);
|
|
61
|
+
removedCount++;
|
|
62
|
+
console.log(kleur.gray(`Removed ${entry.path}`));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log(kleur.yellow(`Modified by user; skipping: ${entry.path}`));
|
|
66
|
+
skippedCount++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Step C: Remove .iris/tools mirrors (Implicitly done by removing .iris later, but specs say remove mirrors)
|
|
70
|
+
// Actually if we remove .iris entire folder, we don't need to do C.
|
|
71
|
+
// Step D: Remove doctrine prompt
|
|
72
|
+
let removeDoctrine = false;
|
|
73
|
+
if (options.force) {
|
|
74
|
+
removeDoctrine = true;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const { confirm } = await inquirer.prompt([
|
|
78
|
+
{
|
|
79
|
+
type: "confirm",
|
|
80
|
+
name: "confirm",
|
|
81
|
+
message: "Remove IRIS doctrine (.iris/aidlc) and policy files?",
|
|
82
|
+
default: true
|
|
83
|
+
}
|
|
84
|
+
]);
|
|
85
|
+
removeDoctrine = confirm;
|
|
86
|
+
}
|
|
87
|
+
if (removeDoctrine) {
|
|
88
|
+
removeDir(path.join(root, ".iris"));
|
|
89
|
+
console.log(kleur.green("Removed .iris/ directory."));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(kleur.gray("Kept .iris/ directory."));
|
|
93
|
+
}
|
|
94
|
+
// Step E: Memory Bank
|
|
95
|
+
if (!options.keepMemory) {
|
|
96
|
+
const { confirmMemory } = await inquirer.prompt([
|
|
97
|
+
{
|
|
98
|
+
type: "confirm",
|
|
99
|
+
name: "confirmMemory",
|
|
100
|
+
message: "Keep memory-bank/ folder? (default: Yes, keep)",
|
|
101
|
+
default: true
|
|
102
|
+
}
|
|
103
|
+
]);
|
|
104
|
+
if (!confirmMemory) { // User said "No" to keeping -> Delete
|
|
105
|
+
// Check if empty or created by us
|
|
106
|
+
// Created dirs = manifest.created_dirs
|
|
107
|
+
// Safety check: contains anything we didn't create?
|
|
108
|
+
// Actually difficult to track every file in memory bank.
|
|
109
|
+
// Safe approach: Only delete if empty?
|
|
110
|
+
// Or prompt: "memory-bank contains files. Type DELETE to confirm."
|
|
111
|
+
const mbPath = path.join(root, "memory-bank");
|
|
112
|
+
if (fs.existsSync(mbPath)) {
|
|
113
|
+
const isEmpty = fs.readdirSync(mbPath).length === 0;
|
|
114
|
+
if (!isEmpty) {
|
|
115
|
+
const { typeDelete } = await inquirer.prompt([
|
|
116
|
+
{
|
|
117
|
+
type: "input",
|
|
118
|
+
name: "typeDelete",
|
|
119
|
+
message: "memory-bank contains files. Type DELETE to confirm removal:",
|
|
120
|
+
}
|
|
121
|
+
]);
|
|
122
|
+
if (typeDelete === "DELETE") {
|
|
123
|
+
removeDir(mbPath);
|
|
124
|
+
console.log(kleur.green("Removed memory-bank/."));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.log(kleur.yellow("Skipped memory-bank removal (safety)."));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
removeDir(mbPath);
|
|
132
|
+
console.log(kleur.green("Removed empty memory-bank/."));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Step F: Summary
|
|
138
|
+
console.log("");
|
|
139
|
+
console.log(kleur.bold("Uninstall Summary:"));
|
|
140
|
+
console.log(`- Removed files: ${removedCount}`);
|
|
141
|
+
console.log(`- Skipped files (modified): ${skippedCount}`);
|
|
142
|
+
console.log("");
|
|
143
|
+
}
|
|
144
|
+
function areFilesEqual(a, b) {
|
|
145
|
+
try {
|
|
146
|
+
const bufA = fs.readFileSync(a);
|
|
147
|
+
const bufB = fs.readFileSync(b);
|
|
148
|
+
return bufA.equals(bufB);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function tryRemoveEmptyParents(filePath, root) {
|
|
155
|
+
let dir = path.dirname(filePath);
|
|
156
|
+
while (dir && path.relative(root, dir) !== "" && !path.isAbsolute(path.relative(root, dir)) && fs.existsSync(dir)) {
|
|
157
|
+
const files = fs.readdirSync(dir);
|
|
158
|
+
if (files.length === 0) {
|
|
159
|
+
fs.rmdirSync(dir);
|
|
160
|
+
dir = path.dirname(dir);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|