devloom 1.0.0 → 1.2.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.
Files changed (142) hide show
  1. package/.ai/core.dsl +44 -0
  2. package/.ai/skills.dsl +39 -0
  3. package/.ai/verify.dsl +97 -0
  4. package/.ai/workflow.dsl +81 -0
  5. package/.opencode/themes/devloom-night-owl.json +103 -0
  6. package/GUIDE.md +572 -115
  7. package/README.md +583 -109
  8. package/SECURITY.md +93 -0
  9. package/agents/devloom-developer-flash.md +31 -0
  10. package/agents/devloom-developer-senior.md +32 -0
  11. package/agents/devloom-developer.md +21 -55
  12. package/agents/devloom-documenter-flash.md +28 -0
  13. package/agents/devloom-documenter.md +17 -56
  14. package/agents/devloom-orchestrator.md +236 -356
  15. package/agents/devloom-planner-flash.md +34 -0
  16. package/agents/devloom-planner-senior.md +35 -0
  17. package/agents/devloom-planner.md +36 -0
  18. package/agents/devloom-qa-flash.md +32 -0
  19. package/agents/devloom-qa.md +21 -83
  20. package/agents/devloom-security-senior.md +35 -0
  21. package/agents/devloom-security.md +34 -0
  22. package/agents/devloom-verifier.md +33 -0
  23. package/agents/devloom-vision.md +113 -0
  24. package/agents/devloom-visual-critic.md +102 -0
  25. package/agents/devloom-visual-director.md +84 -0
  26. package/commands/devloom-agents.md +49 -0
  27. package/commands/devloom-auto.md +11 -0
  28. package/commands/devloom-context.md +82 -0
  29. package/commands/devloom-deepseek-mimo.md +11 -0
  30. package/commands/devloom-deepseek.md +11 -0
  31. package/commands/devloom-free.md +11 -0
  32. package/commands/devloom-go-economy.md +11 -0
  33. package/commands/devloom-go-flash.md +11 -0
  34. package/commands/devloom-go.md +11 -0
  35. package/commands/devloom-init.md +28 -50
  36. package/commands/devloom-loop-status.md +25 -0
  37. package/commands/devloom-loop.md +36 -0
  38. package/commands/devloom-mimo.md +11 -0
  39. package/commands/devloom-plan.md +13 -0
  40. package/commands/devloom-refresh.md +22 -0
  41. package/commands/devloom-resume.md +30 -49
  42. package/commands/devloom-save.md +69 -0
  43. package/commands/devloom-status.md +9 -18
  44. package/commands/devloom.md +99 -67
  45. package/dist/agents.d.ts +30 -0
  46. package/dist/agents.d.ts.map +1 -0
  47. package/dist/agents.js +139 -0
  48. package/dist/agents.js.map +1 -0
  49. package/dist/bootstrap.d.ts +56 -0
  50. package/dist/bootstrap.d.ts.map +1 -0
  51. package/dist/bootstrap.js +116 -0
  52. package/dist/bootstrap.js.map +1 -0
  53. package/dist/constraints.d.ts +5 -0
  54. package/dist/constraints.d.ts.map +1 -0
  55. package/dist/constraints.js +45 -0
  56. package/dist/constraints.js.map +1 -0
  57. package/dist/context.d.ts +42 -0
  58. package/dist/context.d.ts.map +1 -0
  59. package/dist/context.js +636 -0
  60. package/dist/context.js.map +1 -0
  61. package/dist/guard.d.ts +19 -0
  62. package/dist/guard.d.ts.map +1 -0
  63. package/dist/guard.js +418 -0
  64. package/dist/guard.js.map +1 -0
  65. package/dist/loop.d.ts +64 -0
  66. package/dist/loop.d.ts.map +1 -0
  67. package/dist/loop.js +164 -0
  68. package/dist/loop.js.map +1 -0
  69. package/dist/plugin.d.ts.map +1 -1
  70. package/dist/plugin.js +101 -1
  71. package/dist/plugin.js.map +1 -1
  72. package/dist/tui-agents.d.ts +61 -0
  73. package/dist/tui-agents.d.ts.map +1 -0
  74. package/dist/tui-agents.js +89 -0
  75. package/dist/tui-agents.js.map +1 -0
  76. package/dist/tui.d.ts +6 -0
  77. package/dist/tui.d.ts.map +1 -0
  78. package/dist/tui.js +92 -0
  79. package/dist/tui.js.map +1 -0
  80. package/dist/worktree.d.ts +46 -0
  81. package/dist/worktree.d.ts.map +1 -0
  82. package/dist/worktree.js +272 -0
  83. package/dist/worktree.js.map +1 -0
  84. package/package.json +46 -6
  85. package/patterns/changelog-drafter.md +38 -0
  86. package/patterns/ci-sweeper.md +40 -0
  87. package/patterns/daily-triage.md +37 -0
  88. package/patterns/dependency-sweeper.md +40 -0
  89. package/patterns/design-audit.md +63 -0
  90. package/patterns/issue-triage.md +37 -0
  91. package/patterns/post-merge-cleanup.md +34 -0
  92. package/patterns/pr-babysitter.md +37 -0
  93. package/patterns/registry.yaml +52 -0
  94. package/postinstall.mjs +264 -64
  95. package/project/README.md +29 -0
  96. package/protocol/agent-contracts.md +27 -0
  97. package/protocol/artifact-system.md +53 -0
  98. package/protocol/model-routing.md +194 -0
  99. package/protocol/orchestrator-core.md +43 -0
  100. package/protocol/project-system.md +45 -0
  101. package/protocol/rules.md +18 -0
  102. package/protocol/verification-policy.md +48 -0
  103. package/scripts/briefing.mjs +192 -0
  104. package/scripts/loop-run.mjs +205 -0
  105. package/scripts/model-capabilities.mjs +194 -0
  106. package/scripts/plugin-cache.mjs +269 -0
  107. package/scripts/profile.mjs +755 -0
  108. package/scripts/visual-benchmark.mjs +149 -0
  109. package/scripts/worktree.mjs +445 -0
  110. package/skills/build/development.md +33 -0
  111. package/skills/build/live-docs.md +38 -0
  112. package/skills/build/simplify.md +50 -0
  113. package/skills/build/vision-analysis.md +98 -0
  114. package/skills/design/app-design.md +31 -0
  115. package/skills/design/design-grounding.md +30 -0
  116. package/skills/design/design-system.md +27 -0
  117. package/skills/design/game-design.md +35 -0
  118. package/skills/design/motion-design.md +34 -0
  119. package/skills/design/visual-direction.md +32 -0
  120. package/skills/design/web-design.md +33 -0
  121. package/skills/loop/changelog-drafter.md +32 -0
  122. package/skills/loop/ci-sweeper.md +24 -0
  123. package/skills/loop/constraints.md +21 -0
  124. package/skills/loop/dependency-sweeper.md +24 -0
  125. package/skills/loop/design-audit.md +67 -0
  126. package/skills/loop/issue-triage.md +27 -0
  127. package/skills/loop/post-merge-cleanup.md +24 -0
  128. package/skills/loop/pr-babysitter.md +26 -0
  129. package/skills/loop/triage.md +25 -0
  130. package/skills/loop/verifier.md +20 -0
  131. package/skills/meta/skill-discovery.md +23 -0
  132. package/skills/plan/planning.md +27 -0
  133. package/skills/plan/verification-planning.md +40 -0
  134. package/skills/review/security-review.md +63 -0
  135. package/skills/ship/documentation.md +11 -0
  136. package/skills/verify/app-verification.md +19 -0
  137. package/skills/verify/quality-assurance.md +17 -0
  138. package/skills/verify/visual-critique.md +30 -0
  139. package/skills/verify/visual-quality-gate.md +29 -0
  140. package/skills/verify/visual-regression.md +25 -0
  141. package/agents/devloom-analyst.md +0 -83
  142. package/agents/devloom-architect.md +0 -88
@@ -0,0 +1,69 @@
1
+ ---
2
+ description: "DevLoom: persist current project state and pause"
3
+ agent: devloom-orchestrator
4
+ subtask: false
5
+ ---
6
+
7
+ # Save
8
+
9
+ ```bash
10
+ mkdir -p .opencode/devloom/project/{stories,tasks,bugs,decisions,reports}
11
+
12
+ node -e "
13
+ const fs = require('fs');
14
+ const p = '.opencode/devloom/project';
15
+ const readJson = (f, fb) => {
16
+ try { return JSON.parse(fs.readFileSync(f, 'utf8')); } catch { return fb; }
17
+ };
18
+ const cfg = readJson(p + '/config.json', {});
19
+ const board = readJson(p + '/board.json', {});
20
+ const state = readJson(p + '/state.json', {});
21
+ const now = new Date().toISOString();
22
+ const next = {
23
+ v: 1,
24
+ phase: state.phase || 'paused',
25
+ prompt: state.prompt || '',
26
+ ticket: state.ticket || board.active || '',
27
+ next: 'user-command',
28
+ updatedAt: now,
29
+ notes: Array.isArray(state.notes) ? state.notes : []
30
+ };
31
+ const normalizedCfg = {
32
+ v: 1,
33
+ lang: 'en',
34
+ tracker: cfg.tracker === 'github' ? 'github' : 'local',
35
+ gh: {
36
+ enabled: !!cfg.gh?.enabled,
37
+ owner: cfg.gh?.owner || '',
38
+ repo: cfg.gh?.repo || '',
39
+ project: cfg.gh?.project || ''
40
+ },
41
+ rules: { flow: ['analysis','documentation','implementation','verification','regression','done'], tests: 'required', regression: 'required', queue: 'single', docs: 'official', delegation: 'required', skills: 'required', memory: 'load', save: 'always', promptTask: 'append-last' }
42
+ };
43
+ fs.writeFileSync(p + '/config.json', JSON.stringify(normalizedCfg));
44
+ fs.writeFileSync(p + '/board.json', JSON.stringify({
45
+ v: 1,
46
+ tracker: normalizedCfg.tracker,
47
+ active: board.active || next.ticket || '',
48
+ cols: {
49
+ backlog: Array.isArray(board.cols?.backlog) ? board.cols.backlog : [],
50
+ ready: Array.isArray(board.cols?.ready) ? board.cols.ready : [],
51
+ doing: Array.isArray(board.cols?.doing) ? board.cols.doing.slice(0, 1) : (board.active ? [board.active] : []),
52
+ review: Array.isArray(board.cols?.review) ? board.cols.review : [],
53
+ blocked: Array.isArray(board.cols?.blocked) ? board.cols.blocked : [],
54
+ done: Array.isArray(board.cols?.done) ? board.cols.done : []
55
+ },
56
+ updatedAt: now
57
+ }));
58
+ fs.writeFileSync(p + '/state.json', JSON.stringify(next));
59
+ fs.writeFileSync(p + '/README.md', '# DevLoom Project Workspace\\nAll artifacts in this workspace must be written in English.\\nAI-only state files use minified JSON to reduce token usage.\\n');
60
+ "
61
+ ```
62
+
63
+ MODE: Pause
64
+ LOAD: `.opencode/devloom/project/config.json|board.json|state.json`
65
+ OUT:
66
+ - state persisted
67
+ - ticket/todo/plan context preserved for the next prompt
68
+ - next=user-command
69
+ - resume with `/devloom-resume` or new `/devloom ...` prompt
@@ -1,23 +1,14 @@
1
1
  ---
2
- description: "DevLoom: show the current weaving progress — tasks done, in progress, and any errors"
2
+ description: "DevLoom: show progress, active ticket, and phase"
3
3
  agent: devloom-orchestrator
4
4
  subtask: false
5
5
  ---
6
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.
7
+ LOAD: `.opencode/devloom/plan.md|.opencode/devloom/errors.md|.opencode/devloom/project/board.json|.opencode/devloom/project/state.json`
8
+ REPORT:
9
+ - progress: total|done|pending|pct
10
+ - active: current ticket (in doing)
11
+ - queued: count of items in backlog (waiting behind active work)
12
+ - blocked: blocked tickets
13
+ - errors: failed/skipped work
14
+ - phase: analysis|documentation|implementation|verification|regression|queued|done
@@ -1,81 +1,113 @@
1
1
  ---
2
- description: "DevLoom: weave a full software feature from a single prompt — requirements, code, tests, and docs"
2
+ description: "DevLoom: autonomous delivery from one prompt"
3
3
  agent: devloom-orchestrator
4
4
  subtask: false
5
5
  ---
6
6
 
7
- # Smart Resume Detection
7
+ # Boot
8
8
 
9
9
  ```bash
10
- # Create .opencode/devloom directory if not exists
11
- mkdir -p .opencode/devloom
10
+ mkdir -p .opencode/devloom/project/{stories,tasks,bugs,decisions,reports}
12
11
 
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
12
+ export SANITIZED_PROMPT="$(node -e "
13
+ let p = process.argv[1] || '';
14
+ p = p.slice(0, 4000).replace(/[\x00-\x08\x0E-\x1F\x7F]/g, '');
15
+ process.stdout.write(p);
16
+ " -- "$ARGUMENTS")"
38
17
 
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..."
18
+ if [ -z "$SANITIZED_PROMPT" ] && [ -n "$ARGUMENTS" ]; then
19
+ SANITIZED_PROMPT="$ARGUMENTS"
64
20
  fi
65
- ```
66
21
 
67
- # Orchestrator Direction
22
+ DEVLOOM_PROMPT="$SANITIZED_PROMPT" node -e "
23
+ const fs = require('fs');
24
+ const p = '.opencode/devloom/project';
25
+ const readJson = (f, fb) => {
26
+ try { return JSON.parse(fs.readFileSync(f, 'utf8')); } catch { return fb; }
27
+ };
28
+ const prompt = (process.env.DEVLOOM_PROMPT || '').trim();
29
+ const cfg = readJson(p + '/config.json', {});
30
+ const board = readJson(p + '/board.json', {});
31
+ const state = readJson(p + '/state.json', {});
32
+ const now = new Date().toISOString();
33
+ const taskId = prompt ? 'PROMPT-' + now.replace(/[-:.TZ]/g, '').slice(0, 14) : '';
34
+ const todoPath = p + '/tasks/TODO.md';
35
+ const normalizedCfg = {
36
+ v: 1,
37
+ lang: 'en',
38
+ tracker: cfg.tracker === 'github' ? 'github' : 'local',
39
+ gh: {
40
+ enabled: !!cfg.gh?.enabled,
41
+ owner: cfg.gh?.owner || '',
42
+ repo: cfg.gh?.repo || '',
43
+ project: cfg.gh?.project || ''
44
+ },
45
+ rules: { flow: ['analysis','documentation','implementation','verification','regression','done'], tests: 'required', regression: 'required', queue: 'single', docs: 'official', delegation: 'required', skills: 'required', memory: 'load', save: 'always', promptTask: 'append-last' }
46
+ };
47
+ const active = typeof board.active === 'string' ? board.active : '';
48
+ const backlog = Array.isArray(board.cols?.backlog) ? board.cols.backlog : [];
49
+ const nextBacklog = taskId ? [...backlog, taskId] : backlog;
50
+ const todoHeader = '# DevLoom Prompt Tasks\nEvery /devloom prompt is appended here as the last todo before execution routing.\n\n';
51
+ if (!fs.existsSync(todoPath)) fs.writeFileSync(todoPath, todoHeader);
52
+ if (taskId) fs.appendFileSync(todoPath, '- [ ] ' + taskId + ' :: ' + prompt.replace(/\\s+/g, ' ') + '\n');
53
+ const normalizedBoard = {
54
+ v: 1,
55
+ tracker: normalizedCfg.tracker,
56
+ active,
57
+ cols: {
58
+ backlog: nextBacklog,
59
+ ready: Array.isArray(board.cols?.ready) ? board.cols.ready : [],
60
+ doing: Array.isArray(board.cols?.doing) ? board.cols.doing.slice(0, 1) : (active ? [active] : []),
61
+ review: Array.isArray(board.cols?.review) ? board.cols.review : [],
62
+ blocked: Array.isArray(board.cols?.blocked) ? board.cols.blocked : [],
63
+ done: Array.isArray(board.cols?.done) ? board.cols.done : []
64
+ },
65
+ updatedAt: now
66
+ };
67
+ const normalizedState = {
68
+ v: 1,
69
+ phase: typeof state.phase === 'string' ? state.phase : 'idle',
70
+ prompt: prompt || (typeof state.prompt === 'string' ? state.prompt : ''),
71
+ ticket: typeof state.ticket === 'string' ? state.ticket : active,
72
+ next: typeof state.next === 'string' ? state.next : 'analysis',
73
+ updatedAt: now,
74
+ notes: Array.isArray(state.notes) ? state.notes : []
75
+ };
76
+ fs.writeFileSync(p + '/README.md', '# DevLoom Project Workspace\\nAll artifacts in this workspace must be written in English.\\nAI-only state files use minified JSON to reduce token usage.\\n');
77
+ fs.writeFileSync(p + '/config.json', JSON.stringify(normalizedCfg));
78
+ fs.writeFileSync(p + '/board.json', JSON.stringify(normalizedBoard));
79
+ fs.writeFileSync(p + '/state.json', JSON.stringify(normalizedState));
80
+ "
81
+
82
+ if [ -f ".opencode/devloom/config.json" ]; then
83
+ echo "Applying local model config..."
68
84
 
69
- $ARGUMENTS
85
+ PROFILE_MJS="$(node -e 'const fs=require("fs"),p=require("path");console.log(p.join(p.dirname(fs.realpathSync(process.argv[1])),"profile.mjs"))' "$0" 2>/dev/null)"
86
+ [ -f "$PROFILE_MJS" ] || PROFILE_MJS="$HOME/.config/opencode/devloom-scripts/profile.mjs"
87
+ [ -f "$PROFILE_MJS" ] || PROFILE_MJS="$HOME/.config/opencode/commands/profile.mjs"
88
+ [ -f "$PROFILE_MJS" ] || { echo "profile.mjs not found - reinstall DevLoom"; exit 1; }
89
+
90
+ INIT_PROFILE="$(node -e "const c=require('./.opencode/devloom/config.json'); process.stdout.write(c.profile||'auto')" 2>/dev/null || echo auto)"
91
+
92
+ node "$PROFILE_MJS" set "$INIT_PROFILE" 2>&1
93
+ node "$PROFILE_MJS" apply 2>&1
94
+ fi
95
+ ```
70
96
 
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.
97
+ # Run
74
98
 
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
99
+ PROMPT: `$SANITIZED_PROMPT`
100
+ LOAD: `.opencode/devloom/project/config.json|.opencode/devloom/project/board.json|.opencode/devloom/project/state.json`
101
+ RULES:
102
+ - English-only artifacts
103
+ - normalize existing project files to canonical JSONM/English format before execution
104
+ - load memory and relevant skills before planning new work
105
+ - append every prompt as the last task/todo in `.opencode/devloom/project/tasks/TODO.md`
106
+ - auto-create or update tickets, todos, and plan entries for the prompt
107
+ - persist board+state every phase
108
+ - IF board.cols.doing is non-empty: queue the prompt to backlog, do NOT start new work — the orchestrator will see the queue and exit
109
+ - continue pending work first (only when doing is empty) unless user reprioritizes
110
+ - one active ticket by default — new prompts are queued when doing is occupied
111
+ - delegate specialist work to DevLoom subagents by default
112
+ - triage first: pick the minimal agent chain for the prompt intent (see workflow.dsl CHAINS); never run the full pipeline by default
113
+ - DEVLOOM_DONE only when the chosen chain's gates pass
@@ -0,0 +1,30 @@
1
+ export interface ActiveProfile {
2
+ profile?: string;
3
+ resolvedProfile?: string;
4
+ models?: Record<string, string>;
5
+ tier?: string | null;
6
+ }
7
+ export declare function readActiveProfile(rootDir: string): ActiveProfile | null;
8
+ export declare function profileLabel(profile: ActiveProfile | null): string;
9
+ export declare function agentVariantVisible(_profile: ActiveProfile | null, variant: string): boolean;
10
+ export interface DevloomAgentMeta {
11
+ name: string;
12
+ role: string;
13
+ description: string;
14
+ mode: "subagent" | "primary" | "all";
15
+ color: string;
16
+ model: string;
17
+ }
18
+ export declare function listDevloomAgents(profile: ActiveProfile | null): DevloomAgentMeta[];
19
+ export interface DevloomAgentConfig {
20
+ description?: string;
21
+ mode?: "subagent" | "primary" | "all";
22
+ model?: string;
23
+ color?: string;
24
+ hidden?: boolean;
25
+ }
26
+ export declare function buildAgentConfigs(rootDir: string): Record<string, DevloomAgentConfig>;
27
+ export declare function injectDevloomAgents(opencodeConfig: {
28
+ agent?: Record<string, DevloomAgentConfig | undefined>;
29
+ }, rootDir: string): void;
30
+ //# sourceMappingURL=agents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAQvE;AAKD,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAKlE;AAQD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5F;AAeD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,KAAK,CAAA;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAgDD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,gBAAgB,EAAE,CAmBnF;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,KAAK,CAAA;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAiBrF;AAED,wBAAgB,mBAAmB,CACjC,cAAc,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,SAAS,CAAC,CAAA;CAAE,EAC1E,OAAO,EAAE,MAAM,GACd,IAAI,CAUN"}
package/dist/agents.js ADDED
@@ -0,0 +1,139 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export function readActiveProfile(rootDir) {
4
+ const p = join(rootDir, ".opencode", "devloom", "config.json");
5
+ if (!existsSync(p))
6
+ return null;
7
+ try {
8
+ return JSON.parse(readFileSync(p, "utf8"));
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ }
14
+ // Human-readable label shown in the sidebar so the current DevLoom profile is
15
+ // visible at a glance (e.g. "DevLoom Orchestrator: ... (profile: go-flash)").
16
+ // With a senior tier override the label is extended to "(profile: go, tier: senior)".
17
+ export function profileLabel(profile) {
18
+ const name = profile?.resolvedProfile ?? profile?.profile;
19
+ if (!name)
20
+ return "";
21
+ const tier = profile?.tier === "senior" ? ", tier: senior" : "";
22
+ return ` (profile: ${name}${tier})`;
23
+ }
24
+ // Whether a given agent variant should be surfaced in the sidebar / @-menu.
25
+ // Only base and non-tiered variants (orchestrator, developer, planner, qa,
26
+ // security, verifier, documenter, vision) are shown — flash and senior variants
27
+ // are always hidden regardless of the active profile. Non-matching variants
28
+ // stay registered so orchestrator task() routing keeps working; they are only
29
+ // hidden from the UI. Pure function, no fs access.
30
+ export function agentVariantVisible(_profile, variant) {
31
+ return !variant.endsWith("-flash") && !variant.endsWith("-senior");
32
+ }
33
+ const AGENT_VARIANTS = {
34
+ developer: ["developer", "developer-flash", "developer-senior"],
35
+ planner: ["planner", "planner-flash", "planner-senior"],
36
+ qa: ["qa", "qa-flash"],
37
+ verifier: ["verifier"],
38
+ security: ["security", "security-senior"],
39
+ documenter: ["documenter", "documenter-flash"],
40
+ orchestrator: ["orchestrator"],
41
+ vision: ["vision"],
42
+ "visual-director": ["visual-director"],
43
+ "visual-critic": ["visual-critic"],
44
+ };
45
+ const ROLE_COLORS = {
46
+ orchestrator: "#7fdbca",
47
+ planner: "#c792ea",
48
+ developer: "#addb67",
49
+ qa: "#ecc48d",
50
+ verifier: "#82aaff",
51
+ security: "#ff5874",
52
+ documenter: "#f07178",
53
+ vision: "#f78c6c",
54
+ "visual-director": "#c3e88d",
55
+ "visual-critic": "#89ddff",
56
+ };
57
+ const DESCRIPTIONS = {
58
+ "devloom-orchestrator": "DevLoom Orchestrator: autonomous multi-agent delivery",
59
+ "devloom-planner": "DevLoom Planner: callable by the orchestrator for requirements and architecture planning",
60
+ "devloom-planner-senior": "DevLoom Planner Senior: callable by the orchestrator for complex requirements and architecture planning",
61
+ "devloom-planner-flash": "DevLoom Planner Flash: callable by the orchestrator for lightweight requirements and planning",
62
+ "devloom-developer": "DevLoom Developer: callable by the orchestrator for ticket implementation and defect fixes",
63
+ "devloom-developer-senior": "DevLoom Developer Senior: callable by the orchestrator for complex ticket implementation and defect fixes",
64
+ "devloom-developer-flash": "DevLoom Developer Flash: callable by the orchestrator for simple ticket implementation and defect fixes",
65
+ "devloom-qa": "DevLoom QA: callable by the orchestrator for verification, code review, and regression",
66
+ "devloom-qa-flash": "DevLoom QA Flash: callable by the orchestrator for lightweight verification, code review, and regression",
67
+ "devloom-verifier": "DevLoom Verifier: callable by the orchestrator for runtime app verification across scopes",
68
+ "devloom-security": "DevLoom Security: callable by the orchestrator for CRUD endpoint and exposure-surface review",
69
+ "devloom-security-senior": "DevLoom Security Senior: callable by the orchestrator for deep CRUD endpoint and exposure-surface review",
70
+ "devloom-documenter": "DevLoom Documenter: callable by the orchestrator for documentation and state updates",
71
+ "devloom-documenter-flash": "DevLoom Documenter Flash: callable by the orchestrator for lightweight documentation and state updates",
72
+ "devloom-vision": "DevLoom Vision: read-only image description specialist. Takes an image, returns structured description. Nothing else.",
73
+ "devloom-visual-director": "DevLoom Visual Director: read-only design brief writer. Emits DESIGN_FILE spec for UI work.",
74
+ "devloom-visual-critic": "DevLoom Visual Critic: read-only visual reviewer. Scores screenshots against the design contract and issues a delta verdict.",
75
+ };
76
+ const DEFAULT_MODELS = {
77
+ orchestrator: "opencode-go/deepseek-v4-flash",
78
+ planner: "opencode-go/qwen3.7-max",
79
+ developer: "opencode-go/kimi-k2.7-code",
80
+ qa: "opencode-go/deepseek-v4-flash",
81
+ verifier: "opencode-go/deepseek-v4-flash",
82
+ security: "opencode-go/deepseek-v4-flash",
83
+ documenter: "opencode-go/deepseek-v4-flash",
84
+ vision: "opencode-go/qwen3.6-plus",
85
+ "visual-director": "opencode-go/qwen3.6-plus",
86
+ "visual-critic": "opencode-go/qwen3.6-plus",
87
+ };
88
+ export function listDevloomAgents(profile) {
89
+ const agents = [];
90
+ const models = profile?.models ?? {};
91
+ for (const [role, variants] of Object.entries(AGENT_VARIANTS)) {
92
+ const color = ROLE_COLORS[role] ?? "#7fdbca";
93
+ for (const variant of variants) {
94
+ const name = `devloom-${variant}`;
95
+ const isOrchestrator = name === "devloom-orchestrator";
96
+ agents.push({
97
+ name,
98
+ role,
99
+ description: `${DESCRIPTIONS[name] ?? `DevLoom ${variant}`}${isOrchestrator ? profileLabel(profile) : ""}`,
100
+ mode: isOrchestrator ? "all" : "subagent",
101
+ color,
102
+ model: models[role] ?? DEFAULT_MODELS[role] ?? "",
103
+ });
104
+ }
105
+ }
106
+ return agents;
107
+ }
108
+ export function buildAgentConfigs(rootDir) {
109
+ const profile = readActiveProfile(rootDir);
110
+ if (!profile || !profile.models)
111
+ return {};
112
+ const configs = {};
113
+ for (const agent of listDevloomAgents(profile)) {
114
+ // listDevloomAgents names agents `devloom-${variant}`; recover the variant
115
+ // so the visibility rule applies to the same string unit tests use.
116
+ const variant = agent.name.startsWith("devloom-") ? agent.name.slice("devloom-".length) : agent.name;
117
+ configs[agent.name] = {
118
+ description: agent.description,
119
+ mode: agent.mode,
120
+ model: agent.model,
121
+ color: agent.color,
122
+ hidden: !agentVariantVisible(profile, variant),
123
+ };
124
+ }
125
+ return configs;
126
+ }
127
+ export function injectDevloomAgents(opencodeConfig, rootDir) {
128
+ const configs = buildAgentConfigs(rootDir);
129
+ if (Object.keys(configs).length === 0)
130
+ return;
131
+ opencodeConfig.agent = opencodeConfig.agent ?? {};
132
+ for (const [name, agent] of Object.entries(configs)) {
133
+ const existing = opencodeConfig.agent[name];
134
+ opencodeConfig.agent[name] = existing
135
+ ? { ...existing, ...agent }
136
+ : agent;
137
+ }
138
+ }
139
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAShC,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC,CAAA;IAC9D,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAkB,CAAA;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,8EAA8E;AAC9E,sFAAsF;AACtF,MAAM,UAAU,YAAY,CAAC,OAA6B;IACxD,MAAM,IAAI,GAAG,OAAO,EAAE,eAAe,IAAI,OAAO,EAAE,OAAO,CAAA;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IACpB,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAA;IAC/D,OAAO,cAAc,IAAI,GAAG,IAAI,GAAG,CAAA;AACrC,CAAC;AAED,4EAA4E;AAC5E,2EAA2E;AAC3E,gFAAgF;AAChF,4EAA4E;AAC5E,8EAA8E;AAC9E,mDAAmD;AACnD,MAAM,UAAU,mBAAmB,CAAC,QAA8B,EAAE,OAAe;IACjF,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;AACpE,CAAC;AAED,MAAM,cAAc,GAA6B;IAC/C,SAAS,EAAE,CAAC,WAAW,EAAE,iBAAiB,EAAE,kBAAkB,CAAC;IAC/D,OAAO,EAAE,CAAC,SAAS,EAAE,eAAe,EAAE,gBAAgB,CAAC;IACvD,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,CAAC,UAAU,CAAC;IACtB,QAAQ,EAAE,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACzC,UAAU,EAAE,CAAC,YAAY,EAAE,kBAAkB,CAAC;IAC9C,YAAY,EAAE,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,CAAC,QAAQ,CAAC;IAClB,iBAAiB,EAAE,CAAC,iBAAiB,CAAC;IACtC,eAAe,EAAE,CAAC,eAAe,CAAC;CACnC,CAAA;AAWD,MAAM,WAAW,GAA2B;IAC1C,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE,SAAS;IAClB,SAAS,EAAE,SAAS;IACpB,EAAE,EAAE,SAAS;IACb,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,SAAS;IACnB,UAAU,EAAE,SAAS;IACrB,MAAM,EAAE,SAAS;IACjB,iBAAiB,EAAE,SAAS;IAC5B,eAAe,EAAE,SAAS;CAC3B,CAAA;AAED,MAAM,YAAY,GAA2B;IAC3C,sBAAsB,EAAE,uDAAuD;IAC/E,iBAAiB,EAAE,0FAA0F;IAC7G,wBAAwB,EAAE,yGAAyG;IACnI,uBAAuB,EAAE,+FAA+F;IACxH,mBAAmB,EAAE,4FAA4F;IACjH,0BAA0B,EAAE,2GAA2G;IACvI,yBAAyB,EAAE,yGAAyG;IACpI,YAAY,EAAE,wFAAwF;IACtG,kBAAkB,EAAE,0GAA0G;IAC9H,kBAAkB,EAAE,2FAA2F;IAC/G,kBAAkB,EAAE,8FAA8F;IAClH,yBAAyB,EAAE,0GAA0G;IACrI,oBAAoB,EAAE,sFAAsF;IAC5G,0BAA0B,EAAE,wGAAwG;IACpI,gBAAgB,EAAE,uHAAuH;IACzI,yBAAyB,EAAE,6FAA6F;IACxH,uBAAuB,EAAE,8HAA8H;CACxJ,CAAA;AAED,MAAM,cAAc,GAA2B;IAC7C,YAAY,EAAE,+BAA+B;IAC7C,OAAO,EAAE,yBAAyB;IAClC,SAAS,EAAE,4BAA4B;IACvC,EAAE,EAAE,+BAA+B;IACnC,QAAQ,EAAE,+BAA+B;IACzC,QAAQ,EAAE,+BAA+B;IACzC,UAAU,EAAE,+BAA+B;IAC3C,MAAM,EAAE,0BAA0B;IAClC,iBAAiB,EAAE,0BAA0B;IAC7C,eAAe,EAAE,0BAA0B;CAC5C,CAAA;AAED,MAAM,UAAU,iBAAiB,CAAC,OAA6B;IAC7D,MAAM,MAAM,GAAuB,EAAE,CAAA;IACrC,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,EAAE,CAAA;IACpC,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,SAAS,CAAA;QAC5C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,WAAW,OAAO,EAAE,CAAA;YACjC,MAAM,cAAc,GAAG,IAAI,KAAK,sBAAsB,CAAA;YACtD,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI;gBACJ,IAAI;gBACJ,WAAW,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,WAAW,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC1G,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU;gBACzC,KAAK;gBACL,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE;aAClD,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAUD,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAC1C,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,EAAE,CAAA;IAC1C,MAAM,OAAO,GAAuC,EAAE,CAAA;IACtD,KAAK,MAAM,KAAK,IAAI,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,2EAA2E;QAC3E,oEAAoE;QACpE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAA;QACpG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG;YACpB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC;SAC/C,CAAA;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,cAA0E,EAC1E,OAAe;IAEf,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAC1C,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IAC7C,cAAc,CAAC,KAAK,GAAG,cAAc,CAAC,KAAK,IAAI,EAAE,CAAA;IACjD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC3C,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ;YACnC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAE;YAC3B,CAAC,CAAC,KAAK,CAAA;IACX,CAAC;AACH,CAAC"}
@@ -0,0 +1,56 @@
1
+ type ProjectState = {
2
+ v: number;
3
+ phase: string;
4
+ prompt: string;
5
+ ticket: string;
6
+ next: string;
7
+ updatedAt: string;
8
+ notes: string[];
9
+ sessions: Record<string, string>;
10
+ degraded: boolean;
11
+ };
12
+ type ProjectBoard = {
13
+ v: number;
14
+ tracker: "local" | "github";
15
+ active: string;
16
+ cols: {
17
+ backlog: string[];
18
+ ready: string[];
19
+ doing: string[];
20
+ review: string[];
21
+ blocked: string[];
22
+ done: string[];
23
+ };
24
+ updatedAt: string;
25
+ };
26
+ type ProjectConfig = {
27
+ v: number;
28
+ lang: "en";
29
+ tracker: "local" | "github";
30
+ gh: {
31
+ enabled: boolean;
32
+ owner: string;
33
+ repo: string;
34
+ project: string;
35
+ };
36
+ rules: {
37
+ flow: string[];
38
+ tests: "required";
39
+ regression: "required";
40
+ queue: "single";
41
+ docs: "official";
42
+ delegation: "required";
43
+ skills: "required";
44
+ memory: "load";
45
+ save: "always";
46
+ promptTask: "append-last";
47
+ };
48
+ };
49
+ export declare function ensureProjectWorkspace(rootDir: string): {
50
+ config: ProjectConfig;
51
+ board: ProjectBoard;
52
+ state: ProjectState;
53
+ projectRoot: string;
54
+ };
55
+ export {};
56
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAIA,KAAK,YAAY,GAAG;IAClB,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,CAAC,EAAE,MAAM,CAAA;IACT,OAAO,EAAE,OAAO,GAAG,QAAQ,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,EAAE,CAAA;QACjB,KAAK,EAAE,MAAM,EAAE,CAAA;QACf,KAAK,EAAE,MAAM,EAAE,CAAA;QACf,MAAM,EAAE,MAAM,EAAE,CAAA;QAChB,OAAO,EAAE,MAAM,EAAE,CAAA;QACjB,IAAI,EAAE,MAAM,EAAE,CAAA;KACf,CAAA;IACD,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,KAAK,aAAa,GAAG;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,OAAO,GAAG,QAAQ,CAAA;IAC3B,EAAE,EAAE;QACF,OAAO,EAAE,OAAO,CAAA;QAChB,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;IACD,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,EAAE,CAAA;QACd,KAAK,EAAE,UAAU,CAAA;QACjB,UAAU,EAAE,UAAU,CAAA;QACtB,KAAK,EAAE,QAAQ,CAAA;QACf,IAAI,EAAE,UAAU,CAAA;QAChB,UAAU,EAAE,UAAU,CAAA;QACtB,MAAM,EAAE,UAAU,CAAA;QAClB,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,EAAE,QAAQ,CAAA;QACd,UAAU,EAAE,aAAa,CAAA;KAC1B,CAAA;CACF,CAAA;AA8FD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM;;;;;EA+BrD"}
@@ -0,0 +1,116 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { ensureLoopWorkspace } from "./loop.js";
4
+ const PROJECT_DIRS = ["stories", "tasks", "bugs", "decisions", "reports"];
5
+ const WORKSPACE_README = [
6
+ "# DevLoom Project Workspace",
7
+ "All artifacts in this workspace must be written in English.",
8
+ "AI-only state files use minified JSON to reduce token usage.",
9
+ "Every new /devloom prompt must be appended as the last task in project/tasks/TODO.md before execution continues.",
10
+ "Default workflow: load state, inspect memory, use relevant skills, delegate to subagents, keep tickets/todos/plan updated, and save state at every phase boundary.",
11
+ "",
12
+ ].join("\n");
13
+ function ensureDir(path) {
14
+ if (!existsSync(path))
15
+ mkdirSync(path, { recursive: true });
16
+ }
17
+ function readJson(path, fallback) {
18
+ try {
19
+ return JSON.parse(readFileSync(path, "utf8"));
20
+ }
21
+ catch {
22
+ return fallback;
23
+ }
24
+ }
25
+ function normalizeStringArray(value) {
26
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
27
+ }
28
+ function normalizeTracker(value) {
29
+ return value === "github" ? "github" : "local";
30
+ }
31
+ function normalizeConfig(value) {
32
+ const cfg = typeof value === "object" && value ? value : {};
33
+ return {
34
+ v: 1,
35
+ lang: "en",
36
+ tracker: normalizeTracker(cfg.tracker),
37
+ gh: {
38
+ enabled: Boolean(cfg.gh?.enabled),
39
+ owner: typeof cfg.gh?.owner === "string" ? cfg.gh.owner : "",
40
+ repo: typeof cfg.gh?.repo === "string" ? cfg.gh.repo : "",
41
+ project: typeof cfg.gh?.project === "string" ? cfg.gh.project : "",
42
+ },
43
+ rules: {
44
+ flow: ["analysis", "documentation", "implementation", "verification", "regression", "done"],
45
+ tests: "required",
46
+ regression: "required",
47
+ queue: "single",
48
+ docs: "official",
49
+ delegation: "required",
50
+ skills: "required",
51
+ memory: "load",
52
+ save: "always",
53
+ promptTask: "append-last",
54
+ },
55
+ };
56
+ }
57
+ function normalizeBoard(value, tracker, now) {
58
+ const board = typeof value === "object" && value ? value : {};
59
+ const active = typeof board.active === "string" ? board.active : "";
60
+ return {
61
+ v: 1,
62
+ tracker,
63
+ active,
64
+ cols: {
65
+ backlog: normalizeStringArray(board.cols?.backlog),
66
+ ready: normalizeStringArray(board.cols?.ready),
67
+ doing: normalizeStringArray(board.cols?.doing).slice(0, 1),
68
+ review: normalizeStringArray(board.cols?.review),
69
+ blocked: normalizeStringArray(board.cols?.blocked),
70
+ done: normalizeStringArray(board.cols?.done),
71
+ },
72
+ updatedAt: now,
73
+ };
74
+ }
75
+ function normalizeState(value, board, now) {
76
+ const state = typeof value === "object" && value ? value : {};
77
+ return {
78
+ v: 1,
79
+ phase: typeof state.phase === "string" ? state.phase : "idle",
80
+ prompt: typeof state.prompt === "string" ? state.prompt : "",
81
+ ticket: typeof state.ticket === "string" ? state.ticket : board.active,
82
+ next: typeof state.next === "string" ? state.next : "analysis",
83
+ updatedAt: now,
84
+ notes: normalizeStringArray(state.notes),
85
+ sessions: typeof state.sessions === "object" && state.sessions ? state.sessions : {},
86
+ degraded: typeof state.degraded === "boolean" ? state.degraded : false,
87
+ };
88
+ }
89
+ export function ensureProjectWorkspace(rootDir) {
90
+ const projectRoot = join(rootDir, ".opencode", "devloom", "project");
91
+ const devloomRoot = join(rootDir, ".opencode", "devloom");
92
+ const now = new Date().toISOString();
93
+ ensureDir(projectRoot);
94
+ ensureDir(join(devloomRoot, ".tmp"));
95
+ for (const dir of PROJECT_DIRS)
96
+ ensureDir(join(projectRoot, dir));
97
+ const configPath = join(projectRoot, "config.json");
98
+ const boardPath = join(projectRoot, "board.json");
99
+ const statePath = join(projectRoot, "state.json");
100
+ const readmePath = join(projectRoot, "README.md");
101
+ const config = normalizeConfig(readJson(configPath, {}));
102
+ const board = normalizeBoard(readJson(boardPath, {}), config.tracker, now);
103
+ const state = normalizeState(readJson(statePath, {}), board, now);
104
+ writeFileSync(readmePath, WORKSPACE_README);
105
+ writeFileSync(configPath, JSON.stringify(config));
106
+ writeFileSync(boardPath, JSON.stringify(board));
107
+ writeFileSync(statePath, JSON.stringify(state));
108
+ ensureLoopWorkspace(rootDir);
109
+ return {
110
+ config,
111
+ board,
112
+ state,
113
+ projectRoot,
114
+ };
115
+ }
116
+ //# sourceMappingURL=bootstrap.js.map