project-iris 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -323
- package/bin/cli.js +21 -0
- package/flows/aidlc/README.md +372 -0
- package/flows/aidlc/agents/construction-agent.md +79 -0
- package/flows/aidlc/agents/inception-agent.md +97 -0
- package/flows/aidlc/agents/master-agent.md +61 -0
- package/flows/aidlc/agents/operations-agent.md +89 -0
- package/flows/aidlc/commands/construction-agent.md +63 -0
- package/flows/aidlc/commands/inception-agent.md +55 -0
- package/flows/aidlc/commands/master-agent.md +47 -0
- package/flows/aidlc/commands/operations-agent.md +77 -0
- package/flows/aidlc/context-config.yaml +67 -0
- package/flows/aidlc/memory-bank.yaml +104 -0
- package/flows/aidlc/quick-start.md +322 -0
- package/flows/aidlc/skills/construction/bolt-list.md +163 -0
- package/flows/aidlc/skills/construction/bolt-replan.md +345 -0
- package/flows/aidlc/skills/construction/bolt-start.md +442 -0
- package/flows/aidlc/skills/construction/bolt-status.md +185 -0
- package/flows/aidlc/skills/construction/navigator.md +196 -0
- package/flows/aidlc/skills/inception/bolt-plan.md +372 -0
- package/flows/aidlc/skills/inception/context.md +171 -0
- package/flows/aidlc/skills/inception/intent-create.md +211 -0
- package/flows/aidlc/skills/inception/intent-list.md +124 -0
- package/flows/aidlc/skills/inception/navigator.md +207 -0
- package/flows/aidlc/skills/inception/requirements.md +227 -0
- package/flows/aidlc/skills/inception/review.md +248 -0
- package/flows/aidlc/skills/inception/story-create.md +304 -0
- package/flows/aidlc/skills/inception/units.md +278 -0
- package/flows/aidlc/skills/master/analyze-context.md +239 -0
- package/flows/aidlc/skills/master/answer-question.md +141 -0
- package/flows/aidlc/skills/master/explain-flow.md +158 -0
- package/flows/aidlc/skills/master/project-init.md +281 -0
- package/flows/aidlc/skills/master/route-request.md +126 -0
- package/flows/aidlc/skills/operations/build.md +237 -0
- package/flows/aidlc/skills/operations/deploy.md +259 -0
- package/flows/aidlc/skills/operations/monitor.md +265 -0
- package/flows/aidlc/skills/operations/navigator.md +209 -0
- package/flows/aidlc/skills/operations/verify.md +224 -0
- package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt.md +3 -3
- package/{dist → flows/aidlc}/templates/construction/bolt-types/spike-bolt.md +2 -2
- package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
- package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
- package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
- package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
- package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
- package/flows/aidlc/templates/inception/project/README.md +55 -0
- package/flows/aidlc/templates/standards/catalog.yaml +345 -0
- package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
- package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
- package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
- package/lib/InstallerFactory.js +36 -0
- package/lib/analytics/env-detector.js +92 -0
- package/lib/analytics/index.js +22 -0
- package/lib/analytics/machine-id.js +33 -0
- package/lib/analytics/tracker.js +232 -0
- package/lib/cli-utils.js +342 -0
- package/lib/constants.js +32 -0
- package/lib/installer.js +402 -0
- package/lib/installers/AntigravityInstaller.js +22 -0
- package/lib/installers/ClaudeInstaller.js +85 -0
- package/lib/installers/ClineInstaller.js +21 -0
- package/lib/installers/CodexInstaller.js +21 -0
- package/lib/installers/CopilotInstaller.js +113 -0
- package/lib/installers/CursorInstaller.js +63 -0
- package/lib/installers/GeminiInstaller.js +75 -0
- package/lib/installers/KiroInstaller.js +22 -0
- package/lib/installers/OpenCodeInstaller.js +22 -0
- package/lib/installers/RooInstaller.js +22 -0
- package/lib/installers/ToolInstaller.js +73 -0
- package/lib/installers/WindsurfInstaller.js +22 -0
- package/lib/markdown-validator.ts +175 -0
- package/lib/yaml-validator.ts +99 -0
- package/package.json +105 -32
- package/scripts/artifact-validator.js +594 -0
- package/scripts/bolt-complete.js +606 -0
- package/scripts/status-integrity.js +598 -0
- package/dist/bridge/agent-runner.js +0 -190
- package/dist/bridge/connector-factory.js +0 -31
- package/dist/bridge/connectors/antigravity-connector.js +0 -18
- package/dist/bridge/connectors/cursor-connector.js +0 -31
- package/dist/bridge/connectors/in-process-connector.js +0 -29
- package/dist/bridge/connectors/vscode-connector.js +0 -31
- package/dist/bridge/connectors/windsurf-connector.js +0 -23
- package/dist/bridge/filesystem-connector.js +0 -110
- package/dist/bridge/helper.js +0 -203
- package/dist/bridge/types.js +0 -10
- package/dist/cli.js +0 -40
- package/dist/commands/ask.js +0 -259
- package/dist/commands/bridge.js +0 -88
- package/dist/commands/create.js +0 -25
- package/dist/commands/develop.js +0 -141
- package/dist/commands/doctor.js +0 -102
- package/dist/commands/flow.js +0 -301
- package/dist/commands/framework.js +0 -273
- package/dist/commands/generate.js +0 -59
- package/dist/commands/install.js +0 -100
- package/dist/commands/pack.js +0 -33
- package/dist/commands/phase.js +0 -38
- package/dist/commands/run.js +0 -199
- package/dist/commands/status.js +0 -114
- package/dist/commands/uninstall.js +0 -14
- package/dist/commands/use.js +0 -20
- package/dist/commands/validate.js +0 -102
- package/dist/framework/framework-loader.js +0 -97
- package/dist/framework/framework-paths.js +0 -48
- package/dist/framework/framework-types.js +0 -15
- package/dist/iris/artifact-checker.js +0 -78
- package/dist/iris/artifacts/config.js +0 -68
- package/dist/iris/artifacts/generator.js +0 -88
- package/dist/iris/artifacts/types.js +0 -1
- package/dist/iris/bundle.js +0 -44
- package/dist/iris/doctrine/collector.js +0 -124
- package/dist/iris/fixer.js +0 -149
- package/dist/iris/flows/manifest.js +0 -124
- package/dist/iris/framework-context.js +0 -49
- package/dist/iris/framework-manager.js +0 -215
- package/dist/iris/fs/atomic.js +0 -22
- package/dist/iris/guard.js +0 -38
- package/dist/iris/importers/index.js +0 -9
- package/dist/iris/importers/types.js +0 -8
- package/dist/iris/importers/writer.js +0 -139
- package/dist/iris/include.js +0 -49
- package/dist/iris/installer.js +0 -334
- package/dist/iris/interactive/env.js +0 -21
- package/dist/iris/interactive/intent-interview.js +0 -345
- package/dist/iris/interactive/intent-schema.js +0 -28
- package/dist/iris/interactive/interview-io.js +0 -22
- package/dist/iris/interview/config.js +0 -71
- package/dist/iris/interview/types.js +0 -16
- package/dist/iris/interview/utils.js +0 -38
- package/dist/iris/manifest.js +0 -54
- package/dist/iris/packer.js +0 -325
- package/dist/iris/parsers/unit-parser.js +0 -43
- package/dist/iris/paths.js +0 -18
- package/dist/iris/policy.js +0 -133
- package/dist/iris/proc.js +0 -56
- package/dist/iris/report.js +0 -53
- package/dist/iris/resolver.js +0 -66
- package/dist/iris/router.js +0 -114
- package/dist/iris/routes.js +0 -189
- package/dist/iris/run-state.js +0 -146
- package/dist/iris/state.js +0 -113
- package/dist/iris/templates.js +0 -70
- package/dist/iris/tmp.js +0 -24
- package/dist/iris/uninstaller.js +0 -181
- package/dist/iris/utils/interpolate.js +0 -42
- package/dist/iris/validator.js +0 -391
- package/dist/iris/workflow/config.js +0 -51
- package/dist/iris/workflow/engine.js +0 -129
- package/dist/iris/workflow/steps.js +0 -448
- package/dist/iris/workflow/types.js +0 -1
- package/dist/iris_bundle/frameworks/iris-core/framework.yaml +0 -9
- package/dist/iris_bundle/frameworks/iris-core/memory/memory-bank.yaml +0 -1
- package/dist/iris_bundle/frameworks/iris-core/policy.yaml +0 -7
- package/dist/iris_bundle/frameworks/iris-core/templates/config/memory-bank.yaml +0 -1
- package/dist/iris_bundle/frameworks/iris-core/templates/construction/bolt-types/spike-bolt.md +0 -240
- package/dist/lib.js +0 -96
- package/dist/templates/construction/bolt-template.md +0 -226
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -49
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -55
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -67
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -62
- package/dist/templates/construction/bolt-types/ddd-construction-bolt.md +0 -528
- package/dist/templates/construction/bolt-types/simple-construction-bolt.md +0 -347
- package/dist/templates/inception/requirements-template.md +0 -144
- package/dist/templates/inception/stories-template.md +0 -38
- package/dist/templates/inception/story-template.md +0 -147
- package/dist/templates/inception/system-context-template.md +0 -29
- package/dist/templates/inception/unit-brief-template.md +0 -177
- package/dist/templates/inception/units-template.md +0 -52
- package/dist/utils/exit-codes.js +0 -7
- package/dist/utils/logo.js +0 -17
- package/dist/workflows/bolt-execution.js +0 -238
- package/dist/workflows/bolt-plan.js +0 -221
- package/dist/workflows/intent-inception.js +0 -285
- package/dist/workflows/memory-bank-generator.js +0 -180
- package/dist/workflows/reporting.js +0 -74
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/simple-construction-bolt.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/requirements-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/stories-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/story-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/system-context-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/unit-brief-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/units-template.md +0 -0
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
// Safe extensions to import
|
|
4
|
-
const SAFE_EXTENSIONS = ['.md', '.txt', '.yml', '.yaml', '.json'];
|
|
5
|
-
const IGNORED_DIRS = ['node_modules', '.git', 'dist', 'build', 'coverage'];
|
|
6
|
-
export function ensureDoctrineOnlyFlowInstalled(opts) {
|
|
7
|
-
const result = {
|
|
8
|
-
flowId: opts.flowId,
|
|
9
|
-
installedDir: '', // set below
|
|
10
|
-
copiedFiles: 0,
|
|
11
|
-
skippedFiles: 0,
|
|
12
|
-
overwrittenFiles: 0,
|
|
13
|
-
};
|
|
14
|
-
const flowDir = path.join(opts.repoRoot, '.iris', 'flows', opts.flowId);
|
|
15
|
-
result.installedDir = flowDir;
|
|
16
|
-
if (opts.verbose) {
|
|
17
|
-
console.log(`Preparing to install flow '${opts.flowId}' into ${flowDir}`);
|
|
18
|
-
}
|
|
19
|
-
// 1. Check if flow already exists
|
|
20
|
-
if (fs.existsSync(flowDir) && !opts.force) {
|
|
21
|
-
// If not creating/updating, we might still want to report what happened,
|
|
22
|
-
// but typically we'd expect the caller to handle "already exists" checks if they wanted to abort early.
|
|
23
|
-
// However, the requirement is "overwrite if force", which implies "skip/merge if not force".
|
|
24
|
-
// For simplicity, if it exists and !force, we might just return early if we consider the flow "installed".
|
|
25
|
-
// But let's proceed to copy templates with "skip existing" semantics.
|
|
26
|
-
}
|
|
27
|
-
if (opts.dryRun) {
|
|
28
|
-
console.log(`[Dry Run] Would create/update flow directory: ${flowDir}`);
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
fs.mkdirSync(flowDir, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
// 2. Ensure flow.yaml exists
|
|
34
|
-
const flowYamlPath = path.join(flowDir, 'flow.yaml');
|
|
35
|
-
if (opts.dryRun) {
|
|
36
|
-
if (!fs.existsSync(flowYamlPath) || opts.force) {
|
|
37
|
-
console.log(`[Dry Run] Would write flow.yaml for ${opts.flowId}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
if (!fs.existsSync(flowYamlPath) || opts.force) {
|
|
42
|
-
const flowYamlContent = `# Generated by iris flow import
|
|
43
|
-
id: ${opts.flowId}
|
|
44
|
-
name: ${opts.flowId}
|
|
45
|
-
description: Imported from ${opts.displayName}
|
|
46
|
-
version: imported
|
|
47
|
-
`;
|
|
48
|
-
fs.writeFileSync(flowYamlPath, flowYamlContent);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// 3. Ensure overlays exist (empty)
|
|
52
|
-
const overlays = ['policy.overlay.yaml', 'routes.overlay.yaml'];
|
|
53
|
-
for (const file of overlays) {
|
|
54
|
-
const filePath = path.join(flowDir, file);
|
|
55
|
-
if (opts.dryRun) {
|
|
56
|
-
if (!fs.existsSync(filePath) || opts.force) {
|
|
57
|
-
console.log(`[Dry Run] Would ensure empty ${file}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
if (!fs.existsSync(filePath) || opts.force) {
|
|
62
|
-
fs.writeFileSync(filePath, '{}\n');
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// 4. Copy templates
|
|
67
|
-
// Destination: .iris/flows/<id>/doctrine/templates/<id>/...
|
|
68
|
-
// We nest under <id> again inside templates/ to avoid collision if multiple flows share generic names,
|
|
69
|
-
// and to keep it namespaced.
|
|
70
|
-
const destTemplateRoot = path.join(flowDir, 'doctrine', 'templates', opts.flowId);
|
|
71
|
-
if (opts.dryRun) {
|
|
72
|
-
console.log(`[Dry Run] Would copy templates to ${destTemplateRoot}`);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
fs.mkdirSync(destTemplateRoot, { recursive: true });
|
|
76
|
-
}
|
|
77
|
-
for (const srcDir of opts.sourceTemplateDirs) {
|
|
78
|
-
copyRecursive(srcDir, destTemplateRoot, opts, result);
|
|
79
|
-
}
|
|
80
|
-
return result;
|
|
81
|
-
}
|
|
82
|
-
function copyRecursive(src, dest, opts, result, relPath = '') {
|
|
83
|
-
if (!fs.existsSync(src))
|
|
84
|
-
return;
|
|
85
|
-
const stats = fs.statSync(src);
|
|
86
|
-
if (stats.isDirectory()) {
|
|
87
|
-
// Check ignore list
|
|
88
|
-
const dirName = path.basename(src);
|
|
89
|
-
if (IGNORED_DIRS.includes(dirName))
|
|
90
|
-
return;
|
|
91
|
-
if (!opts.dryRun) {
|
|
92
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
93
|
-
}
|
|
94
|
-
const children = fs.readdirSync(src);
|
|
95
|
-
for (const child of children) {
|
|
96
|
-
copyRecursive(path.join(src, child), path.join(dest, child), opts, result, path.join(relPath, child));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
else if (stats.isFile()) {
|
|
100
|
-
// Filter extensions
|
|
101
|
-
const ext = path.extname(src).toLowerCase();
|
|
102
|
-
if (!SAFE_EXTENSIONS.includes(ext)) {
|
|
103
|
-
if (opts.verbose)
|
|
104
|
-
console.log(`Skipping unsafe/irrelevant file type: ${src}`);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (opts.dryRun) {
|
|
108
|
-
// Just log what would happen
|
|
109
|
-
if (fs.existsSync(dest)) {
|
|
110
|
-
if (opts.force) {
|
|
111
|
-
console.log(`[Dry Run] Would overwrite ${relPath}`);
|
|
112
|
-
result.overwrittenFiles++;
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
console.log(`[Dry Run] Would skip existing ${relPath}`);
|
|
116
|
-
result.skippedFiles++;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
console.log(`[Dry Run] Would copy ${relPath}`);
|
|
121
|
-
result.copiedFiles++;
|
|
122
|
-
}
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (fs.existsSync(dest)) {
|
|
126
|
-
if (opts.force) {
|
|
127
|
-
fs.copyFileSync(src, dest);
|
|
128
|
-
result.overwrittenFiles++;
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
result.skippedFiles++;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
fs.copyFileSync(src, dest);
|
|
136
|
-
result.copiedFiles++;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
package/dist/iris/include.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import fg from "fast-glob";
|
|
3
|
-
import { repoRoot } from "../lib.js";
|
|
4
|
-
import { resolveArtifactPath } from "./resolver.js";
|
|
5
|
-
export const DEFAULT_EXCLUDES = [
|
|
6
|
-
"**/node_modules/**",
|
|
7
|
-
"**/.git/**",
|
|
8
|
-
"**/dist/**",
|
|
9
|
-
"**/build/**",
|
|
10
|
-
"**/.iris/cache/**",
|
|
11
|
-
"**/.DS_Store"
|
|
12
|
-
];
|
|
13
|
-
export async function resolveIncludes(includeGlobs, excludeGlobs = []) {
|
|
14
|
-
const root = repoRoot();
|
|
15
|
-
if (includeGlobs.length === 0)
|
|
16
|
-
return [];
|
|
17
|
-
const entries = await fg(includeGlobs, {
|
|
18
|
-
cwd: root,
|
|
19
|
-
ignore: [...DEFAULT_EXCLUDES, ...excludeGlobs],
|
|
20
|
-
dot: true,
|
|
21
|
-
absolute: false,
|
|
22
|
-
onlyFiles: true
|
|
23
|
-
});
|
|
24
|
-
// Sort for determinism
|
|
25
|
-
return entries.sort();
|
|
26
|
-
}
|
|
27
|
-
export function isBinary(buffer) {
|
|
28
|
-
const chunk = buffer.subarray(0, Math.min(buffer.length, 8000));
|
|
29
|
-
return chunk.includes(0);
|
|
30
|
-
}
|
|
31
|
-
const MAX_INDIVIDUAL_FILE_SIZE = 1_000_000;
|
|
32
|
-
export function getFileContent(relPath) {
|
|
33
|
-
const root = repoRoot();
|
|
34
|
-
const absPath = resolveArtifactPath(root, relPath);
|
|
35
|
-
try {
|
|
36
|
-
const stats = fs.statSync(absPath);
|
|
37
|
-
if (stats.size > MAX_INDIVIDUAL_FILE_SIZE) {
|
|
38
|
-
return { path: relPath, size: stats.size, skipped: "too_large" };
|
|
39
|
-
}
|
|
40
|
-
const buffer = fs.readFileSync(absPath);
|
|
41
|
-
if (isBinary(buffer)) {
|
|
42
|
-
return { path: relPath, size: stats.size, skipped: "binary" };
|
|
43
|
-
}
|
|
44
|
-
return { path: relPath, size: stats.size, content: buffer.toString("utf8") };
|
|
45
|
-
}
|
|
46
|
-
catch (e) {
|
|
47
|
-
return { path: relPath, size: 0, skipped: "read_error" };
|
|
48
|
-
}
|
|
49
|
-
}
|
package/dist/iris/installer.js
DELETED
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import kleur from "kleur";
|
|
4
|
-
import inquirer from "inquirer";
|
|
5
|
-
import { ensureDir, sanitizeFolderName, spawnAsync } from "../lib.js";
|
|
6
|
-
import { updateManifest } from "./manifest.js";
|
|
7
|
-
import { getBundleRoot, getBundledFrameworksDir } from "./bundle.js";
|
|
8
|
-
export const TOOLS = ["claude", "cursor", "gemini", "antigravity", "codex", "auto"];
|
|
9
|
-
const BUNDLE_ROOT = getBundleRoot();
|
|
10
|
-
/**
|
|
11
|
-
* Ensure package.json exists in the target directory.
|
|
12
|
-
* If missing, create a minimal one.
|
|
13
|
-
*/
|
|
14
|
-
function ensurePackageJson(root) {
|
|
15
|
-
const pkgPath = path.join(root, "package.json");
|
|
16
|
-
if (fs.existsSync(pkgPath)) {
|
|
17
|
-
console.log(kleur.gray("✓ package.json already exists"));
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const folderName = path.basename(root);
|
|
21
|
-
const sanitizedName = sanitizeFolderName(folderName);
|
|
22
|
-
const minimalPackage = {
|
|
23
|
-
name: sanitizedName,
|
|
24
|
-
version: "0.0.0",
|
|
25
|
-
private: true,
|
|
26
|
-
description: "Project using IRIS framework",
|
|
27
|
-
scripts: {}
|
|
28
|
-
};
|
|
29
|
-
fs.writeFileSync(pkgPath, JSON.stringify(minimalPackage, null, 2) + "\n", "utf8");
|
|
30
|
-
console.log(kleur.green(`✓ Created package.json (name: ${sanitizedName})`));
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Run npm install to generate package-lock.json and install any dependencies
|
|
34
|
-
*/
|
|
35
|
-
async function runNpmInstall(root) {
|
|
36
|
-
console.log("");
|
|
37
|
-
console.log(kleur.bold("Running npm install to generate lockfile..."));
|
|
38
|
-
try {
|
|
39
|
-
const result = await spawnAsync("npm", ["install"], root);
|
|
40
|
-
if (result.code === 0) {
|
|
41
|
-
console.log(kleur.green("✓ npm install completed successfully"));
|
|
42
|
-
// Verify lockfile was created
|
|
43
|
-
const lockPath = path.join(root, "package-lock.json");
|
|
44
|
-
if (fs.existsSync(lockPath)) {
|
|
45
|
-
console.log(kleur.green("✓ package-lock.json generated"));
|
|
46
|
-
}
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
console.error(kleur.red(`✗ npm install failed with code ${result.code}`));
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
console.error(kleur.red("✗ Failed to run npm install"));
|
|
56
|
-
console.error(kleur.gray(` Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
export function detectTools(root) {
|
|
61
|
-
const checks = {
|
|
62
|
-
claude: fs.existsSync(path.join(root, ".claude")),
|
|
63
|
-
cursor: fs.existsSync(path.join(root, ".cursor")),
|
|
64
|
-
gemini: fs.existsSync(path.join(root, ".gemini")),
|
|
65
|
-
antigravity: fs.existsSync(path.join(root, ".antigravity")),
|
|
66
|
-
codex: fs.existsSync(path.join(root, "AGENTS.md")),
|
|
67
|
-
auto: false,
|
|
68
|
-
};
|
|
69
|
-
return TOOLS.filter(t => checks[t]);
|
|
70
|
-
}
|
|
71
|
-
export async function installIris(root, tools, options = {}) {
|
|
72
|
-
// Step 0: Ensure package.json exists (for empty folder support)
|
|
73
|
-
ensurePackageJson(root);
|
|
74
|
-
const installedPaths = [];
|
|
75
|
-
const createdDirs = [];
|
|
76
|
-
// Bundle locations
|
|
77
|
-
const bundleIris = path.join(BUNDLE_ROOT, ".iris");
|
|
78
|
-
const bundleDoctrine = path.join(bundleIris, "aidlc");
|
|
79
|
-
const bundleTools = path.join(bundleIris, "tools");
|
|
80
|
-
// Target locations
|
|
81
|
-
const targetIris = path.join(root, ".iris");
|
|
82
|
-
const targetDoctrine = path.join(targetIris, "aidlc");
|
|
83
|
-
const targetTools = path.join(targetIris, "tools");
|
|
84
|
-
// Step C: Framework & Core Configuration
|
|
85
|
-
// New (Step 3): Install iris-core framework as the Single Source of Truth
|
|
86
|
-
// Then populate legacy mirrors (.iris/aidlc, policy.yaml, etc) from it.
|
|
87
|
-
const bundledFrameworksDir = getBundledFrameworksDir();
|
|
88
|
-
const sourceIrisCore = path.join(bundledFrameworksDir, "iris-core");
|
|
89
|
-
const frameworksDir = path.join(targetIris, "frameworks");
|
|
90
|
-
const targetIrisCore = path.join(frameworksDir, "iris-core");
|
|
91
|
-
ensureDir(frameworksDir);
|
|
92
|
-
// 1. Install Framework (Idempotent: Skip if exists unless --force)
|
|
93
|
-
let installedFramework = false;
|
|
94
|
-
if (fs.existsSync(sourceIrisCore)) {
|
|
95
|
-
if (fs.existsSync(targetIrisCore) && !options.force) {
|
|
96
|
-
// Skip
|
|
97
|
-
// console.log(kleur.gray("Skipped iris-core (exists)"));
|
|
98
|
-
// Check for version mismatch (UX enhancement)
|
|
99
|
-
try {
|
|
100
|
-
const targetManifestPath = path.join(targetIrisCore, "framework.yaml");
|
|
101
|
-
const sourceManifestPath = path.join(sourceIrisCore, "framework.yaml");
|
|
102
|
-
if (fs.existsSync(targetManifestPath) && fs.existsSync(sourceManifestPath)) {
|
|
103
|
-
const targetManifest = JSON.parse(fs.readFileSync(targetManifestPath, 'utf8')); // Yaml is subset of json? No, need yaml parser or simple regex for version.
|
|
104
|
-
// We don't have yaml parser imported here easily (maybe?) inquirer or kleur doesn't help.
|
|
105
|
-
// Let's use simple regex since we control the manifest format mostly.
|
|
106
|
-
const getVer = (content) => {
|
|
107
|
-
const m = content.match(/version:\s*["']?([\w.-]+)["']?/);
|
|
108
|
-
return m ? m[1] : null;
|
|
109
|
-
};
|
|
110
|
-
const targetVer = getVer(fs.readFileSync(targetManifestPath, 'utf8'));
|
|
111
|
-
const sourceVer = getVer(fs.readFileSync(sourceManifestPath, 'utf8'));
|
|
112
|
-
if (targetVer && sourceVer && targetVer !== sourceVer) {
|
|
113
|
-
console.log(kleur.yellow(`Warning: Installed iris-core is v${targetVer}, but bundled is v${sourceVer}.`));
|
|
114
|
-
console.log(kleur.yellow(`Run 'iris install --force' to update (will overwrite changes).`));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
catch (e) {
|
|
119
|
-
// ignore
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
// Install / Overwrite
|
|
124
|
-
if (fs.existsSync(targetIrisCore)) {
|
|
125
|
-
// Force overwrite implies we can clobber.
|
|
126
|
-
// Simple recursive copy over top works for updating files.
|
|
127
|
-
}
|
|
128
|
-
ensureDir(targetIrisCore);
|
|
129
|
-
copyDirRec(sourceIrisCore, targetIrisCore, [], root, true);
|
|
130
|
-
installedFramework = true;
|
|
131
|
-
installedPaths.push({
|
|
132
|
-
path: ".iris/frameworks/iris-core",
|
|
133
|
-
type: "directory",
|
|
134
|
-
status: "installed"
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
// 2. Compatibility Mirror: .iris/aidlc
|
|
139
|
-
// Populated FROM the valid target iris-core framework
|
|
140
|
-
// This ensures legacy tools see the same templates as the framework.
|
|
141
|
-
const targetAidlc = path.join(targetIris, "aidlc");
|
|
142
|
-
ensureDir(targetAidlc);
|
|
143
|
-
// Mirror Templates
|
|
144
|
-
const frameworkTemplates = path.join(targetIrisCore, "templates");
|
|
145
|
-
const aidlcTemplates = path.join(targetAidlc, "templates");
|
|
146
|
-
// Only mirror if framework templates exist
|
|
147
|
-
if (fs.existsSync(frameworkTemplates)) {
|
|
148
|
-
// If aidlc/templates exists, only overwrite if force?
|
|
149
|
-
// Or always align mirror to framework?
|
|
150
|
-
// "Keep it read-only in spirit". Aligning is safer.
|
|
151
|
-
// But if user modified aidlc, we clobber?
|
|
152
|
-
// Let's protect user edits in aidlc too unless force.
|
|
153
|
-
if (!fs.existsSync(aidlcTemplates) || options.force) {
|
|
154
|
-
ensureDir(aidlcTemplates);
|
|
155
|
-
copyDirRec(frameworkTemplates, aidlcTemplates, [], root, true);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
// Legacy Config Mirrors (policy.yaml, routes.yaml)
|
|
159
|
-
// Copy from framework -> .iris/ root IF MISSING (don't overwrite config by default)
|
|
160
|
-
// We do NOT use --force to overwrite these configs usually, as they are user-owned once installed.
|
|
161
|
-
// Spec: "Default: Skip if exists (preserve user edits). If --force: Overwrite."
|
|
162
|
-
// So we respect that for these files too if we consider them part of "install".
|
|
163
|
-
const frameworkPolicy = path.join(targetIrisCore, "policy.yaml");
|
|
164
|
-
const targetPolicy = path.join(targetIris, "policy.yaml");
|
|
165
|
-
if (fs.existsSync(frameworkPolicy) && (!fs.existsSync(targetPolicy) || options.force)) {
|
|
166
|
-
fs.copyFileSync(frameworkPolicy, targetPolicy);
|
|
167
|
-
installedPaths.push({ path: ".iris/policy.yaml", type: "file", status: "installed" });
|
|
168
|
-
}
|
|
169
|
-
const frameworkRoutes = path.join(targetIrisCore, "routes.yaml");
|
|
170
|
-
const targetRoutes = path.join(targetIris, "routes.yaml");
|
|
171
|
-
if (fs.existsSync(frameworkRoutes) && (!fs.existsSync(targetRoutes) || options.force)) {
|
|
172
|
-
fs.copyFileSync(frameworkRoutes, targetRoutes);
|
|
173
|
-
installedPaths.push({ path: ".iris/routes.yaml", type: "file", status: "installed" });
|
|
174
|
-
}
|
|
175
|
-
// Add README to aidlc
|
|
176
|
-
const mirrorReadme = path.join(targetAidlc, "README.md");
|
|
177
|
-
if (!fs.existsSync(mirrorReadme)) {
|
|
178
|
-
fs.writeFileSync(mirrorReadme, "# Legacy Mirror\n\nThis directory is a compatibility mirror of `.iris/frameworks/iris-core`.\nDo not edit files here; they may be overwritten by system updates.\nEdit the framework files instead.\n");
|
|
179
|
-
}
|
|
180
|
-
// 3. State (only if missing)
|
|
181
|
-
if (!fs.existsSync(path.join(targetIris, "state.yaml"))) {
|
|
182
|
-
if (fs.existsSync(path.join(bundleIris, "state.yaml"))) {
|
|
183
|
-
fs.copyFileSync(path.join(bundleIris, "state.yaml"), path.join(targetIris, "state.yaml"));
|
|
184
|
-
installedPaths.push({
|
|
185
|
-
path: ".iris/state.yaml",
|
|
186
|
-
type: "file",
|
|
187
|
-
status: "installed"
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
// Step D: Memory Bank Skeleton
|
|
192
|
-
const memoryDirs = ["intents", "units", "bolts", "standards", "logs"];
|
|
193
|
-
for (const d of memoryDirs) {
|
|
194
|
-
const p = path.join(root, "memory-bank", d);
|
|
195
|
-
if (!fs.existsSync(p)) {
|
|
196
|
-
ensureDir(p);
|
|
197
|
-
createdDirs.push(path.relative(root, p));
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
// Step E: Install Tool Wrappers & F: Mirror
|
|
201
|
-
for (const t of tools) {
|
|
202
|
-
const toolBundlePath = path.join(bundleTools, t);
|
|
203
|
-
if (!fs.existsSync(toolBundlePath)) {
|
|
204
|
-
console.warn(kleur.yellow(`Warning: No bundle found for tool '${t}' at ${toolBundlePath}`));
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
// Mirror first (always safe/overwrite in .iris/tools)
|
|
208
|
-
const mirrorPath = path.join(targetTools, t);
|
|
209
|
-
ensureDir(mirrorPath);
|
|
210
|
-
// We copy the WHOLE bundles content for that tool into mirror.
|
|
211
|
-
// Actually, we should mirror what we *attempt* to install.
|
|
212
|
-
// Spec: "mirror exactly what was installed (and skipped) into .iris/tools/<tool>"
|
|
213
|
-
// "still mirror the bundle version ... and record skipped_existing"
|
|
214
|
-
// So: copy bundle -> mirror.
|
|
215
|
-
copyDirRec(toolBundlePath, mirrorPath, [], root, true); // Don't track mirror files in manifest individually? Or do we?
|
|
216
|
-
// "mirror installed tool payloads... create but only filled for selected tools"
|
|
217
|
-
// Uninstall: "Remove .iris/tools/<tool> mirrors ... Always safe"
|
|
218
|
-
// So we don't strictly need to track every file inside mirror in manifest, just the tool selection implies it.
|
|
219
|
-
// Install to Repo Root
|
|
220
|
-
await installToolWrapper(t, toolBundlePath, root, installedPaths);
|
|
221
|
-
}
|
|
222
|
-
// Step G: Update Manifest
|
|
223
|
-
updateManifest({
|
|
224
|
-
tools_selected: tools,
|
|
225
|
-
paths_installed: installedPaths,
|
|
226
|
-
created_dirs: createdDirs,
|
|
227
|
-
doctrine_path: ".iris/aidlc",
|
|
228
|
-
policy_path: ".iris/policy.yaml"
|
|
229
|
-
});
|
|
230
|
-
// Step H: Run npm install to generate lockfile
|
|
231
|
-
await runNpmInstall(root);
|
|
232
|
-
}
|
|
233
|
-
async function installToolWrapper(tool, srcDir, root, installedPaths) {
|
|
234
|
-
// We need to walk the srcDir and copy to root, respecting collisions.
|
|
235
|
-
// For Claude: srcDir is .../tools/claude. contains .claude/...
|
|
236
|
-
// So we just copy * recursively from srcDir to root.
|
|
237
|
-
// Check for collisions first?
|
|
238
|
-
// Recursive walker needed.
|
|
239
|
-
const entries = getAllFiles(srcDir);
|
|
240
|
-
for (const entry of entries) {
|
|
241
|
-
const relPath = path.relative(srcDir, entry);
|
|
242
|
-
const targetPath = path.join(root, relPath);
|
|
243
|
-
if (fs.existsSync(targetPath)) {
|
|
244
|
-
// Prompt? "Prompt per tool: File exists... Overwrite?"
|
|
245
|
-
// Actually prompting check-per-file might be spammy.
|
|
246
|
-
// "Prompt per tool: File exists... Overwrite?" -> implies one prompt per file clash?
|
|
247
|
-
// "If a target file exists... Prompt per tool (maybe once?)... No, spec says: 'Prompt per tool: "File exists: <path>. Overwrite? (y/N)"'"
|
|
248
|
-
// This looks like per-file.
|
|
249
|
-
// Let's implement per-file prompt for safety.
|
|
250
|
-
// Check content difference!
|
|
251
|
-
if (areFilesEqual(entry, targetPath)) {
|
|
252
|
-
// Identical, skip prompt, mark installed (or skipped? effectively same)
|
|
253
|
-
installedPaths.push({ path: relPath, type: "file", status: "installed" });
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
const { overwrite } = await inquirer.prompt([
|
|
257
|
-
{
|
|
258
|
-
type: "confirm",
|
|
259
|
-
name: "overwrite",
|
|
260
|
-
message: `File exists: ${relPath}. Overwrite?`,
|
|
261
|
-
default: false
|
|
262
|
-
}
|
|
263
|
-
]);
|
|
264
|
-
if (overwrite) {
|
|
265
|
-
ensureDir(path.dirname(targetPath));
|
|
266
|
-
fs.copyFileSync(entry, targetPath);
|
|
267
|
-
installedPaths.push({ path: relPath, type: "file", status: "overwritten" });
|
|
268
|
-
console.log(kleur.green(`Overwrote ${relPath}`));
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
console.log(kleur.gray(`Skipped ${relPath}`));
|
|
272
|
-
installedPaths.push({ path: relPath, type: "file", status: "skipped_existing" });
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
ensureDir(path.dirname(targetPath));
|
|
277
|
-
fs.copyFileSync(entry, targetPath);
|
|
278
|
-
installedPaths.push({ path: relPath, type: "file", status: "installed" });
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
// Helpers
|
|
283
|
-
function getAllFiles(dir, fileList = []) {
|
|
284
|
-
const files = fs.readdirSync(dir);
|
|
285
|
-
files.forEach(file => {
|
|
286
|
-
const filePath = path.join(dir, file);
|
|
287
|
-
if (fs.statSync(filePath).isDirectory()) {
|
|
288
|
-
getAllFiles(filePath, fileList);
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
fileList.push(filePath);
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
return fileList;
|
|
295
|
-
}
|
|
296
|
-
function copyDirRec(src, dest, trackList, root, silentTrack = false) {
|
|
297
|
-
ensureDir(dest);
|
|
298
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
299
|
-
for (const entry of entries) {
|
|
300
|
-
const srcPath = path.join(src, entry.name);
|
|
301
|
-
const destPath = path.join(dest, entry.name);
|
|
302
|
-
if (entry.isDirectory()) {
|
|
303
|
-
copyDirRec(srcPath, destPath, trackList, root, silentTrack);
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
fs.copyFileSync(srcPath, destPath);
|
|
307
|
-
if (!silentTrack) {
|
|
308
|
-
trackList.push({
|
|
309
|
-
path: path.relative(root, destPath),
|
|
310
|
-
type: "file",
|
|
311
|
-
status: "installed"
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
function safeCopy(src, dest, trackList, root) {
|
|
318
|
-
if (fs.existsSync(dest))
|
|
319
|
-
return; // Skip if exists
|
|
320
|
-
if (!fs.existsSync(src))
|
|
321
|
-
return; // Skip if source missing
|
|
322
|
-
ensureDir(path.dirname(dest));
|
|
323
|
-
fs.copyFileSync(src, dest);
|
|
324
|
-
trackList.push({
|
|
325
|
-
path: path.relative(root, dest),
|
|
326
|
-
type: "file",
|
|
327
|
-
status: "installed"
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
function areFilesEqual(a, b) {
|
|
331
|
-
const bufA = fs.readFileSync(a);
|
|
332
|
-
const bufB = fs.readFileSync(b);
|
|
333
|
-
return bufA.equals(bufB);
|
|
334
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export function checkInteractiveMode(options) {
|
|
2
|
-
// 1. Force Interactive overrides all
|
|
3
|
-
if (options.forceInteractive) {
|
|
4
|
-
return { isInteractive: true, reason: "Forced by user flag" };
|
|
5
|
-
}
|
|
6
|
-
// 2. Explicit Non-Interactive
|
|
7
|
-
if (options.nonInteractive) {
|
|
8
|
-
return { isInteractive: false, reason: "User requested non-interactive mode" };
|
|
9
|
-
}
|
|
10
|
-
// 3. CI Detection
|
|
11
|
-
// Common CI env vars: CI, GITHUB_ACTIONS, GITLAB_CI, CIRCLECI, TRAVIS
|
|
12
|
-
if (process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI) {
|
|
13
|
-
return { isInteractive: false, reason: "CI environment detected" };
|
|
14
|
-
}
|
|
15
|
-
// 4. TTY Detection
|
|
16
|
-
// process.stdin.isTTY is undefined or false if piped/redirected
|
|
17
|
-
if (!process.stdin.isTTY) {
|
|
18
|
-
return { isInteractive: false, reason: "No TTY detected (piped input)" };
|
|
19
|
-
}
|
|
20
|
-
return { isInteractive: true };
|
|
21
|
-
}
|