devloom 1.0.0

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.
@@ -0,0 +1,94 @@
1
+ ---
2
+ mode: subagent
3
+ model: opencode/deepseek-v4-flash-free
4
+ hidden: true
5
+ permission:
6
+ edit: allow
7
+ bash: allow
8
+ ---
9
+
10
+ # DevLoom QA – Quality Inspector
11
+
12
+ ## Skill Auto-Detection
13
+
14
+ Read the relevant domain skill file(s) from disk based on the task type:
15
+ - FE task -> cat ~/.config/opencode/skills/build/frontend-development.md
16
+ - BE task -> cat ~/.config/opencode/skills/build/backend-development.md + cat ~/.config/opencode/skills/build/api-design.md
17
+ - API design -> cat ~/.config/opencode/skills/build/api-design.md
18
+ - Testing -> cat ~/.config/opencode/skills/build/test-driven-development.md + cat ~/.config/opencode/skills/verify/quality-assurance.md
19
+ - Security -> cat ~/.config/opencode/skills/review/security-review.md
20
+ - Performance -> cat ~/.config/opencode/skills/review/performance-review.md
21
+ - Debugging -> cat ~/.config/opencode/skills/verify/debugging.md
22
+ - Documentation -> cat ~/.config/opencode/skills/ship/documentation.md
23
+ - Requirements -> cat ~/.config/opencode/skills/define/requirements-analysis.md
24
+ - Planning -> cat ~/.config/opencode/skills/plan/architecture-planning.md
25
+
26
+ At minimum, always read:
27
+ cat ~/.config/opencode/skills/verify/quality-assurance.md
28
+
29
+ You are a QA engineer in the DevLoom weaving pipeline.
30
+ Verify that a single completed task meets its acceptance criteria, passes all
31
+ tests, and introduces no regressions.
32
+
33
+ ## Instructions
34
+
35
+ 1. Read the task to verify from your prompt. Then read its spec in `.opencode/devloom/plan.md`:
36
+ ```bash
37
+ cat .opencode/devloom/plan.md
38
+ ```
39
+
40
+ 2. Read the files that were modified (reported by the developer in their completion message).
41
+
42
+ 3. Write tests if they do not already exist:
43
+ - **Unit tests**: cover every new function, method, or class.
44
+ - **Integration tests**: cover API endpoints or cross-module interactions.
45
+ - **Edge cases**: null/undefined inputs, empty collections, boundary values, error paths.
46
+ - Place test files adjacent to source files following the project's convention
47
+ (e.g., `*.spec.ts`, `*_test.py`, `*_test.go`).
48
+
49
+ 4. Run the linter and capture output — do NOT suppress failures with `|| true`,
50
+ as lint errors must be included in a `QA_FAIL` report:
51
+ ```bash
52
+ LINT_RESULT=0
53
+ LINT_OUTPUT=$(npm run lint 2>&1) || LINT_RESULT=$?
54
+ if [ $LINT_RESULT -ne 0 ]; then
55
+ LINT_OUTPUT=$(npx eslint src --ext .ts,.js 2>&1) || LINT_RESULT=$?
56
+ fi
57
+ echo "$LINT_OUTPUT"
58
+ # LINT_RESULT is non-zero if linting failed — factor this into your verdict.
59
+ ```
60
+
61
+ 5. Run the full test suite and capture the result:
62
+ ```bash
63
+ TEST_RESULT=0
64
+ TEST_OUTPUT=$(npm test 2>&1) || { TEST_RESULT=$?; TEST_OUTPUT=$(python -m pytest 2>&1) || { TEST_RESULT=$?; TEST_OUTPUT=$(go test ./... 2>&1) || TEST_RESULT=$?; }; }
65
+ echo "$TEST_OUTPUT"
66
+ # TEST_RESULT is non-zero if the test suite failed — factor this into your verdict.
67
+ ```
68
+
69
+ 6. Verify each acceptance criterion from `.opencode/devloom/plan.md` for this task is met.
70
+ If any criterion is unmet, it is a failure.
71
+
72
+ 7. **Report the verdict exactly once** with **exactly one** of these strings:
73
+
74
+ **On success:**
75
+ ```
76
+ QA_PASS: [task title]
77
+ Tests written: [list of test files]
78
+ Criteria verified: [list of AC IDs that were checked]
79
+ ```
80
+
81
+ **On failure:**
82
+ ```
83
+ QA_FAIL: [task title]
84
+ Failures:
85
+ - [specific failure 1: file, line, error message or criterion unmet]
86
+ - [specific failure 2: ...]
87
+ Suggested fixes:
88
+ - [concrete fix suggestion 1]
89
+ - [concrete fix suggestion 2]
90
+ ```
91
+
92
+ **CRITICAL**: Be precise in failure reports — the developer will use them directly to fix the code.
93
+ Do NOT attempt to fix the code yourself or iterate. Report once and stop. The orchestrator
94
+ will determine if the developer retries or the task is skipped after 3 failures.
@@ -0,0 +1,60 @@
1
+ ---
2
+ description: "DevLoom Init: start fresh execution, clearing any prior state"
3
+ agent: devloom-orchestrator
4
+ subtask: false
5
+ ---
6
+
7
+ # Force Fresh Start
8
+
9
+ ```bash
10
+ # Create .opencode/devloom directory if not exists
11
+ mkdir -p .opencode/devloom
12
+
13
+ # Load project config — local config.json overrides global agent models
14
+ # ALL models MUST use opencode/ API prefix (e.g. opencode/deepseek-v4-flash-free)
15
+ if [ -f ".opencode/devloom/config.json" ]; then
16
+ echo "📋 Applying local model config..."
17
+ node -e "
18
+ const c = JSON.parse(require('fs').readFileSync('.opencode/devloom/config.json','utf8'));
19
+ const m = c.models || {};
20
+ for (const [agent, model] of Object.entries(m)) {
21
+ let finalModel = model.trim();
22
+ if (!finalModel.startsWith('opencode/') && !finalModel.startsWith('opencode-go/')) {
23
+ finalModel = 'opencode/' + finalModel;
24
+ console.log(' ⚠️ Added opencode/ prefix to ' + agent + ': ' + model + ' -> ' + finalModel);
25
+ }
26
+ const f = require('os').homedir() + '/.config/opencode/agents/devloom-' + agent + '.md';
27
+ try {
28
+ const fs = require('fs');
29
+ let content = fs.readFileSync(f, 'utf8');
30
+ content = content.replace(/^model:.*/m, 'model: ' + finalModel);
31
+ fs.writeFileSync(f, content);
32
+ console.log(' ' + agent + ' -> ' + finalModel);
33
+ } catch(e) { console.error(' Failed ' + agent + ': ' + e.message); }
34
+ }
35
+ "
36
+ fi
37
+
38
+ # Remove any existing state to force fresh start
39
+ rm -f .opencode/devloom/state.json
40
+ rm -f .opencode/devloom/errors.md
41
+
42
+ # Clear requirement/plan files if they exist
43
+ rm -f .opencode/devloom/requirements.md
44
+ rm -f .opencode/devloom/plan.md
45
+
46
+ echo "🔄 Clearing prior state - starting fresh"
47
+ ```
48
+
49
+ # Fresh DevLoom Orchestration
50
+
51
+ $ARGUMENTS
52
+
53
+ IMPORTANT: Run full workflow from PHASE 0.
54
+
55
+ 1. Run PHASE 0 — detect models, ask user preference, update agent files
56
+ 2. Invoke @devloom-analyst to create requirements
57
+ 3. Invoke @devloom-architect to create plan
58
+ 4. Loop through every task: developer → QA → (fix if needed) → mark [x]
59
+ 5. Invoke @devloom-documenter and run final quality gate
60
+ 6. Output DEVLOOM_DONE only when all tasks are [x] and build passes
@@ -0,0 +1,58 @@
1
+ ---
2
+ description: "Resume DevLoom from last execution point"
3
+ agent: devloom-orchestrator
4
+ subtask: false
5
+ ---
6
+
7
+ # Initialize & Dashboard Setup
8
+
9
+ ```bash
10
+ # Load project config — local config.json overrides global agent models
11
+ # ALL models MUST use opencode/ API prefix (e.g. opencode/deepseek-v4-flash-free)
12
+ if [ -f ".opencode/devloom/config.json" ]; then
13
+ echo "📋 Applying local model config..."
14
+ node -e "
15
+ const c = JSON.parse(require('fs').readFileSync('.opencode/devloom/config.json','utf8'));
16
+ const m = c.models || {};
17
+ for (const [agent, model] of Object.entries(m)) {
18
+ let finalModel = model.trim();
19
+ if (!finalModel.startsWith('opencode/') && !finalModel.startsWith('opencode-go/')) {
20
+ finalModel = 'opencode/' + finalModel;
21
+ console.log(' ⚠️ Added opencode/ prefix to ' + agent + ': ' + model + ' -> ' + finalModel);
22
+ }
23
+ const f = require('os').homedir() + '/.config/opencode/agents/devloom-' + agent + '.md';
24
+ try {
25
+ const fs = require('fs');
26
+ let content = fs.readFileSync(f, 'utf8');
27
+ content = content.replace(/^model:.*/m, 'model: ' + finalModel);
28
+ fs.writeFileSync(f, content);
29
+ console.log(' ' + agent + ' -> ' + finalModel);
30
+ } catch(e) { console.error(' Failed ' + agent + ': ' + e.message); }
31
+ }
32
+ "
33
+ fi
34
+
35
+ # Check state file
36
+ STATE_FILE=".opencode/devloom/state.json"
37
+
38
+ if [ -f "$STATE_FILE" ]; then
39
+ PHASE=$(grep -o '"phase":"[^"]*"' "$STATE_FILE" | cut -d'"' -f4)
40
+ echo "Resuming from $PHASE"
41
+ else
42
+ echo "No execution state found. Use /devloom [prompt] to start new execution."
43
+ exit 1
44
+ fi
45
+ ```
46
+
47
+ # Resume DevLoom Orchestration
48
+
49
+ RESUME MODE ACTIVE: Execute PRE-PHASE 0 resume detection.
50
+
51
+ 1. Check for existing `.opencode/devloom/state.json`
52
+ 2. If found: Load phase, completedPhases, tasks.completed, requirements, plan
53
+ 3. Skip all completed phases
54
+ 4. Jump to next pending phase
55
+ 5. Resume execution from that point with existing models and configuration
56
+ 6. Do NOT run PHASE 0 (model selection) during resume
57
+
58
+ Execute all remaining tasks until complete, then PHASE 3 delivery and quality gate.
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: "DevLoom: show the current weaving progress — tasks done, in progress, and any errors"
3
+ agent: devloom-orchestrator
4
+ subtask: false
5
+ ---
6
+
7
+ # Setup
8
+
9
+ ```bash
10
+ mkdir -p .opencode/devloom
11
+ ```
12
+
13
+ # Status Report
14
+
15
+ Read `.opencode/devloom/plan.md` and `.opencode/devloom/errors.md` (if they exist) and report:
16
+
17
+ 1. **Progress**: total tasks, completed ([x]), pending ([ ]), completion percentage
18
+ 2. **Current task**: the first unchecked task in `.opencode/devloom/plan.md` (if any)
19
+ 3. **Errors**: any failed or skipped tasks listed in `.opencode/devloom/errors.md`
20
+ 4. **Phase**: which phase the weave is in (Planning / Weaving / Documenting / Done)
21
+
22
+ Format the output as a clean status report. If `.opencode/devloom/plan.md` does not exist,
23
+ report that no weaving session has been started yet.
@@ -0,0 +1,81 @@
1
+ ---
2
+ description: "DevLoom: weave a full software feature from a single prompt — requirements, code, tests, and docs"
3
+ agent: devloom-orchestrator
4
+ subtask: false
5
+ ---
6
+
7
+ # Smart Resume Detection
8
+
9
+ ```bash
10
+ # Create .opencode/devloom directory if not exists
11
+ mkdir -p .opencode/devloom
12
+
13
+ # Load project config — local config.json overrides global agent models
14
+ # ALL models MUST use opencode/ API prefix (e.g. opencode/deepseek-v4-flash-free)
15
+ if [ -f ".opencode/devloom/config.json" ]; then
16
+ echo "📋 Applying local model config..."
17
+ node -e "
18
+ const c = JSON.parse(require('fs').readFileSync('.opencode/devloom/config.json','utf8'));
19
+ const m = c.models || {};
20
+ for (const [agent, model] of Object.entries(m)) {
21
+ // Ensure model has opencode/ or opencode-go/ prefix
22
+ let finalModel = model.trim();
23
+ if (!finalModel.startsWith('opencode/') && !finalModel.startsWith('opencode-go/')) {
24
+ finalModel = 'opencode/' + finalModel;
25
+ console.log(' ⚠️ Added opencode/ prefix to ' + agent + ': ' + model + ' -> ' + finalModel);
26
+ }
27
+ const f = require('os').homedir() + '/.config/opencode/agents/devloom-' + agent + '.md';
28
+ try {
29
+ const fs = require('fs');
30
+ let content = fs.readFileSync(f, 'utf8');
31
+ content = content.replace(/^model:.*/m, 'model: ' + finalModel);
32
+ fs.writeFileSync(f, content);
33
+ console.log(' ' + agent + ' -> ' + finalModel);
34
+ } catch(e) { console.error(' Failed ' + agent + ': ' + e.message); }
35
+ }
36
+ "
37
+ fi
38
+
39
+ # Check if state exists
40
+ if [ -f ".opencode/devloom/state.json" ]; then
41
+ PHASE=$(grep -o '"phase":"[^"]*"' .opencode/devloom/state.json | cut -d'"' -f4)
42
+ COMPLETED=$(grep -o '"completed":\[' .opencode/devloom/state.json | wc -l)
43
+
44
+ # If no arguments provided, RESUME existing execution
45
+ if [ -z "$ARGUMENTS" ]; then
46
+ echo "📋 Found existing execution at phase: $PHASE"
47
+ echo "🔄 Resuming from saved state..."
48
+
49
+ # Delegate to orchestrator resume logic (PRE-PHASE 0)
50
+ echo "RESUMING_FROM_STATE"
51
+ else
52
+ # If prompt provided, treat as new execution with same project
53
+ echo "ℹ️ Existing state found. Starting fresh execution with new prompt..."
54
+ fi
55
+ else
56
+ # No prior state
57
+ if [ -z "$ARGUMENTS" ]; then
58
+ echo "❌ No prior execution found. Use: /devloom [description]"
59
+ echo " Or use: /devloom init [description] to start fresh"
60
+ exit 1
61
+ fi
62
+
63
+ echo "✨ Starting fresh DevLoom execution..."
64
+ fi
65
+ ```
66
+
67
+ # Orchestrator Direction
68
+
69
+ $ARGUMENTS
70
+
71
+ IMPORTANT:
72
+ - If RESUMING_FROM_STATE: Use PRE-PHASE 0 resume detection. Skip completed phases.
73
+ - Otherwise: Run full PHASE 0 → PHASE 1 → PHASE 2 → PHASE 3 workflow.
74
+
75
+ Full workflow:
76
+ 1. Run PHASE 0 — detect models, ask user preference, update agent files
77
+ 2. Invoke @devloom-analyst to create requirements
78
+ 3. Invoke @devloom-architect to create plan
79
+ 4. Loop through every task: developer → QA → (fix if needed) → mark [x]
80
+ 5. Invoke @devloom-documenter and run final quality gate
81
+ 6. Output DEVLOOM_DONE only when all tasks are [x] and build passes
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ export declare const DevLoomPlugin: Plugin;
3
+ export default DevLoomPlugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAGjD,eAAO,MAAM,aAAa,EAAE,MAE3B,CAAA;AAED,eAAe,aAAa,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { createLifecycleHooks } from "./plugin.js";
2
+ export const DevLoomPlugin = async (ctx) => {
3
+ return createLifecycleHooks(ctx);
4
+ };
5
+ export default DevLoomPlugin;
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,CAAC,MAAM,aAAa,GAAW,KAAK,EAAE,GAAG,EAAE,EAAE;IACjD,OAAO,oBAAoB,CAAC,GAAG,CAAC,CAAA;AAClC,CAAC,CAAA;AAED,eAAe,aAAa,CAAA"}
@@ -0,0 +1,4 @@
1
+ import type { Hooks, PluginInput } from "@opencode-ai/plugin";
2
+ declare function createLifecycleHooks(_ctx: PluginInput): Hooks;
3
+ export { createLifecycleHooks };
4
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAI7D,iBAAS,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,KAAK,CAkCtD;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAA"}
package/dist/plugin.js ADDED
@@ -0,0 +1,31 @@
1
+ import { env } from "node:process";
2
+ const PLUGIN_NAME = "devloom";
3
+ function createLifecycleHooks(_ctx) {
4
+ const debug = env.DEVLOOM_DEBUG === "1";
5
+ const log = (message, data) => {
6
+ if (debug) {
7
+ console.log(`[${new Date().toISOString()}] [${PLUGIN_NAME}]`, message, data ? JSON.stringify(data, null, 2) : "");
8
+ }
9
+ };
10
+ return {
11
+ event: async ({ event }) => {
12
+ log("Event received", {
13
+ type: event.type,
14
+ properties: Object.keys(event.properties ?? {}),
15
+ });
16
+ },
17
+ "tool.execute.before": async ({ tool, sessionID }) => {
18
+ log("Tool executing", { tool, sessionID });
19
+ },
20
+ "tool.execute.after": async ({ tool, sessionID }, output) => {
21
+ log("Tool completed", {
22
+ tool,
23
+ sessionID,
24
+ title: output?.title,
25
+ outputLength: output?.output?.length ?? 0,
26
+ });
27
+ },
28
+ };
29
+ }
30
+ export { createLifecycleHooks };
31
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAGlC,MAAM,WAAW,GAAG,SAAS,CAAA;AAE7B,SAAS,oBAAoB,CAAC,IAAiB;IAC7C,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,KAAK,GAAG,CAAA;IAEvC,MAAM,GAAG,GAAG,CAAC,OAAe,EAAE,IAA8B,EAAE,EAAE;QAC9D,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,GAAG,CACT,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,WAAW,GAAG,EAChD,OAAO,EACP,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAC1C,CAAA;QACH,CAAC;IACH,CAAC,CAAA;IAED,OAAO;QACL,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YACzB,GAAG,CAAC,gBAAgB,EAAE;gBACpB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC;aAChD,CAAC,CAAA;QACJ,CAAC;QAED,qBAAqB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;YACnD,GAAG,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA;QAC5C,CAAC;QAED,oBAAoB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE;YAC1D,GAAG,CAAC,gBAAgB,EAAE;gBACpB,IAAI;gBACJ,SAAS;gBACT,KAAK,EAAE,MAAM,EAAE,KAAK;gBACpB,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;aAC1C,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAA"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "devloom",
3
+ "version": "1.0.0",
4
+ "description": "DevLoom – Autonomous Development Weaver for OpenCode. Transforms a single prompt into fully tested, documented software.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "agents",
11
+ "commands",
12
+ "postinstall.mjs",
13
+ "README.md",
14
+ "GUIDE.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsc --watch",
19
+ "postinstall": "node postinstall.mjs"
20
+ },
21
+ "peerDependencies": {
22
+ "@opencode-ai/plugin": ">=0.6.0"
23
+ },
24
+
25
+ "devDependencies": {
26
+ "@opencode-ai/plugin": "^1.15.7",
27
+ "@types/node": "^25.9.1",
28
+ "typescript": "^5.4.0"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/nsrau/devloom.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/nsrau/devloom/issues"
36
+ },
37
+ "homepage": "https://github.com/nsrau/devloom#readme",
38
+ "keywords": [
39
+ "opencode",
40
+ "opencode-plugin",
41
+ "devloom",
42
+ "autonomous",
43
+ "agent-loop",
44
+ "weaver",
45
+ "code-generation"
46
+ ],
47
+ "license": "MIT"
48
+ }
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ // postinstall.mjs — Installs DevLoom agents, commands, and skills into OpenCode's global config dirs.
3
+
4
+ import { copyFileSync, mkdirSync, existsSync, readdirSync, statSync } from "fs"
5
+ import { resolve, dirname, join, relative } from "path"
6
+ import { fileURLToPath } from "url"
7
+ import { homedir, platform } from "os"
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url))
10
+
11
+ // ── Config dir resolution (cross-platform) ────────────────────────────────────
12
+ function getConfigDir() {
13
+ const os = platform()
14
+ if (os === "win32") {
15
+ return resolve(process.env.APPDATA ?? homedir(), "opencode")
16
+ }
17
+ if (os === "darwin") {
18
+ return resolve(homedir(), "Library", "Application Support", "opencode")
19
+ }
20
+ return resolve(
21
+ process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"),
22
+ "opencode"
23
+ )
24
+ }
25
+
26
+ const CONFIG_DIR = getConfigDir()
27
+ const AGENTS_DIR = resolve(CONFIG_DIR, "agents")
28
+ const COMMANDS_DIR = resolve(CONFIG_DIR, "commands")
29
+ const SKILLS_DIR = resolve(CONFIG_DIR, "skills")
30
+
31
+ const AGENTS = [
32
+ "devloom-orchestrator",
33
+ "devloom-analyst",
34
+ "devloom-architect",
35
+ "devloom-developer",
36
+ "devloom-qa",
37
+ "devloom-documenter",
38
+ ]
39
+
40
+ const COMMANDS = ["devloom", "devloom-status", "devloom-resume", "devloom-init"]
41
+
42
+ // ── Helpers ───────────────────────────────────────────────────────────────────
43
+ function ensureDir(dir) {
44
+ if (!existsSync(dir)) {
45
+ mkdirSync(dir, { recursive: true })
46
+ console.log(` Created: ${dir}`)
47
+ }
48
+ }
49
+
50
+ let installFailed = false
51
+
52
+ function installFile(src, dest, label) {
53
+ if (!existsSync(src)) {
54
+ console.error(` Source file not found: ${src}`)
55
+ installFailed = true
56
+ return
57
+ }
58
+ try {
59
+ copyFileSync(src, dest)
60
+ console.log(` ${label}`)
61
+ } catch (err) {
62
+ console.error(` Failed -- ${label}: ${err.message}`)
63
+ installFailed = true
64
+ }
65
+ }
66
+
67
+ function installDirRecursive(srcDir, destDir, labelPrefix) {
68
+ if (!existsSync(srcDir)) {
69
+ console.error(` Source dir not found: ${srcDir}`)
70
+ installFailed = true
71
+ return
72
+ }
73
+ const entries = readdirSync(srcDir)
74
+ ensureDir(destDir)
75
+ for (const entry of entries) {
76
+ const srcPath = join(srcDir, entry)
77
+ const destPath = join(destDir, entry)
78
+ const stat = statSync(srcPath)
79
+ if (stat.isDirectory()) {
80
+ installDirRecursive(srcPath, destPath, `${labelPrefix}/${entry}`)
81
+ } else {
82
+ installFile(srcPath, destPath, `${labelPrefix}/${entry}`)
83
+ }
84
+ }
85
+ }
86
+
87
+ // ── Main ──────────────────────────────────────────────────────────────────────
88
+ console.log("\nDevLoom -- post-install\n")
89
+ console.log(` Config dir : ${CONFIG_DIR}`)
90
+ console.log(` Agents dir : ${AGENTS_DIR}`)
91
+ console.log(` Commands dir: ${COMMANDS_DIR}`)
92
+ console.log(` Skills dir : ${SKILLS_DIR}\n`)
93
+
94
+ ensureDir(AGENTS_DIR)
95
+ ensureDir(COMMANDS_DIR)
96
+
97
+
98
+ console.log("Installing agents:")
99
+ for (const name of AGENTS) {
100
+ installFile(
101
+ resolve(__dirname, "agents", `${name}.md`),
102
+ resolve(AGENTS_DIR, `${name}.md`),
103
+ `Agent: ${name}`
104
+ )
105
+ }
106
+
107
+ console.log("\nInstalling commands:")
108
+ for (const name of COMMANDS) {
109
+ installFile(
110
+ resolve(__dirname, "commands", `${name}.md`),
111
+ resolve(COMMANDS_DIR, `${name}.md`),
112
+ `Command: /${name}`
113
+ )
114
+ }
115
+
116
+ console.log("\nInstalling skills:")
117
+ installDirRecursive(
118
+ resolve(__dirname, "skills"),
119
+ SKILLS_DIR,
120
+ "Skill"
121
+ )
122
+
123
+ if (!installFailed) {
124
+ console.log(`
125
+ DevLoom installed successfully!
126
+
127
+ Start weaving:
128
+ /devloom Build a REST API for user management with JWT auth
129
+ /devloom-status
130
+
131
+ Debug mode:
132
+ DEVLOOM_DEBUG=1 opencode @devloom-orchestrator
133
+ `)
134
+ } else {
135
+ console.error("\nSome files could not be installed. See errors above.")
136
+ process.exit(1)
137
+ }