@rryando/arcs 3.1.1 → 3.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.
- package/README.md +65 -40
- package/dist/cli/arcs-orchestrate.d.ts +1 -1
- package/dist/cli/arcs-orchestrate.d.ts.map +1 -1
- package/dist/cli/arcs-orchestrate.js +4 -2
- package/dist/cli/arcs-orchestrate.js.map +1 -1
- package/dist/cli/arg-parser.d.ts +3 -3
- package/dist/cli/arg-parser.d.ts.map +1 -1
- package/dist/cli/arg-parser.js +1 -1
- package/dist/cli/arg-parser.js.map +1 -1
- package/dist/cli/bundle-installer.d.ts +11 -0
- package/dist/cli/bundle-installer.d.ts.map +1 -1
- package/dist/cli/bundle-installer.js +14 -4
- package/dist/cli/bundle-installer.js.map +1 -1
- package/dist/cli/command-registry.d.ts +61 -6
- package/dist/cli/command-registry.d.ts.map +1 -1
- package/dist/cli/command-registry.js.map +1 -1
- package/dist/cli/commands/batch.js +17 -16
- package/dist/cli/commands/batch.js.map +1 -1
- package/dist/cli/commands/brief.js +71 -74
- package/dist/cli/commands/brief.js.map +1 -1
- package/dist/cli/commands/bundle.js +79 -31
- package/dist/cli/commands/bundle.js.map +1 -1
- package/dist/cli/commands/dependency.js +47 -43
- package/dist/cli/commands/dependency.js.map +1 -1
- package/dist/cli/commands/diagnostics.js.map +1 -1
- package/dist/cli/commands/diagram.js +39 -55
- package/dist/cli/commands/diagram.js.map +1 -1
- package/dist/cli/commands/done.js +19 -18
- package/dist/cli/commands/done.js.map +1 -1
- package/dist/cli/commands/graph.js +12 -10
- package/dist/cli/commands/graph.js.map +1 -1
- package/dist/cli/commands/knowledge-search.js +11 -12
- package/dist/cli/commands/knowledge-search.js.map +1 -1
- package/dist/cli/commands/knowledge.js +63 -56
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/loop.js +40 -36
- package/dist/cli/commands/loop.js.map +1 -1
- package/dist/cli/commands/maintenance.js +16 -13
- package/dist/cli/commands/maintenance.js.map +1 -1
- package/dist/cli/commands/next.js +9 -8
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/plan.js +61 -55
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/cli/commands/project-updates.js +37 -33
- package/dist/cli/commands/project-updates.js.map +1 -1
- package/dist/cli/commands/project.js +21 -17
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/remember.js +6 -6
- package/dist/cli/commands/remember.js.map +1 -1
- package/dist/cli/commands/status.js +9 -8
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/task.d.ts +1 -5
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +180 -248
- package/dist/cli/commands/task.js.map +1 -1
- package/dist/cli/commands/utility.d.ts.map +1 -1
- package/dist/cli/commands/utility.js +39 -35
- package/dist/cli/commands/utility.js.map +1 -1
- package/dist/cli/dag-commands.js.map +1 -1
- package/dist/cli/help-generator.d.ts +3 -3
- package/dist/cli/help-generator.d.ts.map +1 -1
- package/dist/cli/help-generator.js +1 -1
- package/dist/cli/help-generator.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +235 -113
- package/dist/cli/setup.js.map +1 -1
- package/dist/utils/diagram-generator.d.ts +3 -1
- package/dist/utils/diagram-generator.d.ts.map +1 -1
- package/dist/utils/diagram-generator.js +4 -4
- package/dist/utils/diagram-generator.js.map +1 -1
- package/dist/utils/diagram-store.d.ts +45 -0
- package/dist/utils/diagram-store.d.ts.map +1 -0
- package/dist/utils/diagram-store.js +99 -0
- package/dist/utils/diagram-store.js.map +1 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/json-schemas.d.ts +7 -0
- package/dist/utils/json-schemas.d.ts.map +1 -1
- package/dist/utils/json-schemas.js +1 -0
- package/dist/utils/json-schemas.js.map +1 -1
- package/dist/utils/storage-utils.d.ts +1 -1
- package/dist/utils/storage-utils.d.ts.map +1 -1
- package/dist/utils/storage-utils.js +1 -1
- package/dist/utils/storage-utils.js.map +1 -1
- package/dist/utils/task-store.d.ts.map +1 -1
- package/dist/utils/task-store.js +6 -5
- package/dist/utils/task-store.js.map +1 -1
- package/dist/utils/toposort.d.ts +1 -1
- package/dist/utils/toposort.d.ts.map +1 -1
- package/dist/utils/toposort.js +5 -4
- package/dist/utils/toposort.js.map +1 -1
- package/dist/utils/workflow-policy.d.ts +1 -1
- package/dist/utils/workflow-policy.d.ts.map +1 -1
- package/dist/utils/workflow-policy.js +1 -1
- package/dist/utils/workflow-policy.js.map +1 -1
- package/opencode/arcs/manifest.json +51 -53
- package/opencode/arcs/prompts/arcs-orchestrate-caveman.txt +4 -2
- package/opencode/arcs/prompts/arcs-orchestrate.txt +4 -2
- package/package.json +1 -1
- package/scripts/deploy-claudecode-bundle.mjs +414 -0
|
@@ -36,16 +36,27 @@
|
|
|
36
36
|
"value": {
|
|
37
37
|
"external_directory": {
|
|
38
38
|
"/tmp": "allow",
|
|
39
|
+
"/tmp/**": "allow",
|
|
39
40
|
"~/.arcs": "allow",
|
|
40
|
-
"~/.
|
|
41
|
+
"~/.arcs/**": "allow",
|
|
42
|
+
"~/.config/opencode": "allow",
|
|
43
|
+
"~/.config/opencode/**": "allow"
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
46
|
},
|
|
47
|
+
{
|
|
48
|
+
"path": [
|
|
49
|
+
"lsp"
|
|
50
|
+
],
|
|
51
|
+
"value": true,
|
|
52
|
+
"mode": "if-absent"
|
|
53
|
+
},
|
|
44
54
|
{
|
|
45
55
|
"path": [
|
|
46
56
|
"model"
|
|
47
57
|
],
|
|
48
|
-
"value": "github-copilot/claude-sonnet-4.6"
|
|
58
|
+
"value": "github-copilot/claude-sonnet-4.6",
|
|
59
|
+
"mode": "if-absent"
|
|
49
60
|
},
|
|
50
61
|
{
|
|
51
62
|
"path": [
|
|
@@ -53,7 +64,8 @@
|
|
|
53
64
|
"build",
|
|
54
65
|
"model"
|
|
55
66
|
],
|
|
56
|
-
"value": "github-copilot/claude-sonnet-4.6"
|
|
67
|
+
"value": "github-copilot/claude-sonnet-4.6",
|
|
68
|
+
"mode": "if-absent"
|
|
57
69
|
},
|
|
58
70
|
{
|
|
59
71
|
"path": [
|
|
@@ -61,7 +73,8 @@
|
|
|
61
73
|
"plan",
|
|
62
74
|
"model"
|
|
63
75
|
],
|
|
64
|
-
"value": "github-copilot/claude-opus-4.6"
|
|
76
|
+
"value": "github-copilot/claude-opus-4.6",
|
|
77
|
+
"mode": "if-absent"
|
|
65
78
|
},
|
|
66
79
|
{
|
|
67
80
|
"path": [
|
|
@@ -69,7 +82,8 @@
|
|
|
69
82
|
"general",
|
|
70
83
|
"model"
|
|
71
84
|
],
|
|
72
|
-
"value": "github-copilot/claude-opus-4.6"
|
|
85
|
+
"value": "github-copilot/claude-opus-4.6",
|
|
86
|
+
"mode": "if-absent"
|
|
73
87
|
},
|
|
74
88
|
{
|
|
75
89
|
"path": [
|
|
@@ -77,7 +91,8 @@
|
|
|
77
91
|
"explore",
|
|
78
92
|
"model"
|
|
79
93
|
],
|
|
80
|
-
"value": "github-copilot/claude-haiku-4.5"
|
|
94
|
+
"value": "github-copilot/claude-haiku-4.5",
|
|
95
|
+
"mode": "if-absent"
|
|
81
96
|
},
|
|
82
97
|
{
|
|
83
98
|
"path": [
|
|
@@ -90,9 +105,11 @@
|
|
|
90
105
|
"model": "github-copilot/claude-haiku-4.5",
|
|
91
106
|
"prompt": "{file:./prompts/code-reviewer.txt}",
|
|
92
107
|
"permission": {
|
|
93
|
-
"edit": "
|
|
108
|
+
"edit": "allow",
|
|
94
109
|
"bash": "allow",
|
|
95
|
-
"webfetch": "
|
|
110
|
+
"webfetch": "allow",
|
|
111
|
+
"mcp": "allow",
|
|
112
|
+
"external_directory": { "*": "allow" }
|
|
96
113
|
}
|
|
97
114
|
}
|
|
98
115
|
},
|
|
@@ -106,13 +123,11 @@
|
|
|
106
123
|
"mode": "subagent",
|
|
107
124
|
"model": "github-copilot/claude-opus-4.6",
|
|
108
125
|
"permission": {
|
|
109
|
-
"edit": "
|
|
110
|
-
"bash": "
|
|
126
|
+
"edit": "allow",
|
|
127
|
+
"bash": "allow",
|
|
111
128
|
"webfetch": "allow",
|
|
112
|
-
"
|
|
113
|
-
|
|
114
|
-
"~/.arcs": "allow"
|
|
115
|
-
}
|
|
129
|
+
"mcp": "allow",
|
|
130
|
+
"external_directory": { "*": "allow" }
|
|
116
131
|
},
|
|
117
132
|
"prompt": "{file:./prompts/docs-researcher.txt}"
|
|
118
133
|
}
|
|
@@ -127,12 +142,11 @@
|
|
|
127
142
|
"mode": "subagent",
|
|
128
143
|
"model": "github-copilot/claude-haiku-4.5",
|
|
129
144
|
"permission": {
|
|
130
|
-
"edit": "
|
|
131
|
-
"bash": "
|
|
132
|
-
"webfetch": "
|
|
133
|
-
"
|
|
134
|
-
|
|
135
|
-
}
|
|
145
|
+
"edit": "allow",
|
|
146
|
+
"bash": "allow",
|
|
147
|
+
"webfetch": "allow",
|
|
148
|
+
"mcp": "allow",
|
|
149
|
+
"external_directory": { "*": "allow" }
|
|
136
150
|
},
|
|
137
151
|
"prompt": "{file:./prompts/tech-architect.txt}"
|
|
138
152
|
}
|
|
@@ -149,12 +163,9 @@
|
|
|
149
163
|
"permission": {
|
|
150
164
|
"edit": "allow",
|
|
151
165
|
"bash": "allow",
|
|
152
|
-
"webfetch": "
|
|
166
|
+
"webfetch": "allow",
|
|
153
167
|
"mcp": "allow",
|
|
154
|
-
"external_directory": {
|
|
155
|
-
"/tmp": "allow",
|
|
156
|
-
"~/.arcs": "allow"
|
|
157
|
-
}
|
|
168
|
+
"external_directory": { "*": "allow" }
|
|
158
169
|
},
|
|
159
170
|
"prompt": "{file:./prompts/software-engineer.txt}"
|
|
160
171
|
}
|
|
@@ -171,13 +182,9 @@
|
|
|
171
182
|
"permission": {
|
|
172
183
|
"edit": "allow",
|
|
173
184
|
"bash": "allow",
|
|
174
|
-
"webfetch": "
|
|
185
|
+
"webfetch": "allow",
|
|
175
186
|
"mcp": "allow",
|
|
176
|
-
"external_directory": {
|
|
177
|
-
"/tmp": "allow",
|
|
178
|
-
"~/.arcs": "allow",
|
|
179
|
-
"~/.config/opencode": "allow"
|
|
180
|
-
}
|
|
187
|
+
"external_directory": { "*": "allow" }
|
|
181
188
|
},
|
|
182
189
|
"prompt": "{file:./prompts/arcs-docs.txt}"
|
|
183
190
|
}
|
|
@@ -192,13 +199,11 @@
|
|
|
192
199
|
"mode": "subagent",
|
|
193
200
|
"model": "github-copilot/claude-haiku-4.5",
|
|
194
201
|
"permission": {
|
|
195
|
-
"edit": "
|
|
202
|
+
"edit": "allow",
|
|
196
203
|
"bash": "allow",
|
|
197
|
-
"webfetch": "
|
|
204
|
+
"webfetch": "allow",
|
|
198
205
|
"mcp": "allow",
|
|
199
|
-
"external_directory": {
|
|
200
|
-
"~/.arcs": "allow"
|
|
201
|
-
}
|
|
206
|
+
"external_directory": { "*": "allow" }
|
|
202
207
|
},
|
|
203
208
|
"prompt": "{file:./prompts/qa-analyst.txt}"
|
|
204
209
|
}
|
|
@@ -213,14 +218,11 @@
|
|
|
213
218
|
"mode": "subagent",
|
|
214
219
|
"model": "github-copilot/claude-sonnet-4.6",
|
|
215
220
|
"permission": {
|
|
216
|
-
"edit": "
|
|
221
|
+
"edit": "allow",
|
|
217
222
|
"bash": "allow",
|
|
218
|
-
"webfetch": "
|
|
223
|
+
"webfetch": "allow",
|
|
219
224
|
"mcp": "allow",
|
|
220
|
-
"external_directory": {
|
|
221
|
-
"/tmp": "allow",
|
|
222
|
-
"~/.arcs": "allow"
|
|
223
|
-
}
|
|
225
|
+
"external_directory": { "*": "allow" }
|
|
224
226
|
},
|
|
225
227
|
"prompt": "{file:./prompts/devil-advocate.txt}"
|
|
226
228
|
}
|
|
@@ -235,14 +237,11 @@
|
|
|
235
237
|
"mode": "subagent",
|
|
236
238
|
"model": "github-copilot/claude-opus-4.6",
|
|
237
239
|
"permission": {
|
|
238
|
-
"edit": "
|
|
240
|
+
"edit": "allow",
|
|
239
241
|
"bash": "allow",
|
|
240
|
-
"webfetch": "
|
|
242
|
+
"webfetch": "allow",
|
|
241
243
|
"mcp": "allow",
|
|
242
|
-
"external_directory": {
|
|
243
|
-
"/tmp": "allow",
|
|
244
|
-
"~/.arcs": "allow"
|
|
245
|
-
}
|
|
244
|
+
"external_directory": { "*": "allow" }
|
|
246
245
|
},
|
|
247
246
|
"prompt": "{file:./prompts/oncall-ops.txt}"
|
|
248
247
|
}
|
|
@@ -257,13 +256,11 @@
|
|
|
257
256
|
"mode": "subagent",
|
|
258
257
|
"model": "github-copilot/claude-opus-4.6",
|
|
259
258
|
"permission": {
|
|
260
|
-
"edit": "
|
|
259
|
+
"edit": "allow",
|
|
261
260
|
"bash": "allow",
|
|
262
261
|
"webfetch": "allow",
|
|
263
262
|
"mcp": "allow",
|
|
264
|
-
"external_directory": {
|
|
265
|
-
"~/.arcs": "allow"
|
|
266
|
-
}
|
|
263
|
+
"external_directory": { "*": "allow" }
|
|
267
264
|
},
|
|
268
265
|
"prompt": "{file:./prompts/system-architect.txt}"
|
|
269
266
|
}
|
|
@@ -272,7 +269,8 @@
|
|
|
272
269
|
"path": [
|
|
273
270
|
"small_model"
|
|
274
271
|
],
|
|
275
|
-
"value": "github-copilot/claude-haiku-4.5"
|
|
272
|
+
"value": "github-copilot/claude-haiku-4.5",
|
|
273
|
+
"mode": "if-absent"
|
|
276
274
|
}
|
|
277
275
|
]
|
|
278
276
|
}
|
|
@@ -412,11 +412,13 @@ CLI:
|
|
|
412
412
|
4. When scope survives challenge → dispatch scoping sub-agent with minimal framing
|
|
413
413
|
5. Present plan + diagram → user confirms
|
|
414
414
|
6. Dispatch `devil-advocate` PHASE: brainstorm with proposed plan → handle verdict
|
|
415
|
-
7. On PASS: `arcs plan create` → `arcs task create × N` (
|
|
415
|
+
7. On PASS: `arcs plan create` → `arcs task create × N` (**ALWAYS** pass `--dependsOn=<earlierTaskId>,...` for chained tasks — diagram edges are derived from this field, never from titles) → `arcs diagram init <slug> <planId> --json`
|
|
416
416
|
|
|
417
417
|
**Constraints:**
|
|
418
418
|
- Every diagram node gets a Task record (`planId` set, `status: backlog`, priority by depth)
|
|
419
|
-
- Diagram uses `flowchart TD`, stable IDs (T001+), rich per-node metadata
|
|
419
|
+
- Diagram uses `flowchart TD`, stable IDs (T001+ in task.id order), rich per-node metadata
|
|
420
|
+
- **Never embed `T###` ordinals in task titles** — node IDs are derived from sorted task.id at `diagram init` time. Embedding ordinals breaks regeneration.
|
|
421
|
+
- **`--dependsOn` is the only way to encode execution order.** A diagram with no edges = tasks created without `--dependsOn`. Fix the data, not the diagram.
|
|
420
422
|
- Silently load the `to-diagram` skill before generating diagrams
|
|
421
423
|
- Never write to DAG before user confirms summary
|
|
422
424
|
- **YAGNI before scope**: propose the minimal version, let user opt into more
|
|
@@ -320,11 +320,13 @@ CLI:
|
|
|
320
320
|
4. When scope survives challenge → dispatch scoping sub-agent with minimal framing
|
|
321
321
|
5. Present plan + diagram → user confirms
|
|
322
322
|
6. Dispatch `devil-advocate` PHASE: brainstorm with proposed plan → handle verdict
|
|
323
|
-
7. On PASS: `arcs plan create` → `arcs task create × N` (
|
|
323
|
+
7. On PASS: `arcs plan create` → `arcs task create × N` (**ALWAYS** pass `--dependsOn=<earlierTaskId>,...` for chained tasks — diagram edges are derived from this field, never from titles) → `arcs diagram init <slug> <planId> --json`
|
|
324
324
|
|
|
325
325
|
**Constraints:**
|
|
326
326
|
- Every diagram node gets a Task record (`planId` set, `status: backlog`, priority by depth)
|
|
327
|
-
- Diagram uses `flowchart TD`, stable IDs (T001+), rich per-node metadata
|
|
327
|
+
- Diagram uses `flowchart TD`, stable IDs (T001+ in task.id order), rich per-node metadata
|
|
328
|
+
- **Never embed `T###` ordinals in task titles** — node IDs are derived from sorted task.id at `diagram init` time. Embedding ordinals breaks regeneration.
|
|
329
|
+
- **`--dependsOn` is the only way to encode execution order.** A diagram with no edges = tasks created without `--dependsOn`. Fix the data, not the diagram.
|
|
328
330
|
- Silently load the `to-diagram` skill before generating diagrams
|
|
329
331
|
- Never write to DAG before user confirms summary
|
|
330
332
|
- **YAGNI before scope**: propose the minimal version, let user opt into more
|
package/package.json
CHANGED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Deploy claudecode ARCS bundle from repo to Claude Code config.
|
|
3
|
+
//
|
|
4
|
+
// Direction: repo → config ONLY. Never writes config → repo.
|
|
5
|
+
//
|
|
6
|
+
// What gets deployed:
|
|
7
|
+
// 1. Sub-agent prompts → <destination>/agents/<stem>.md
|
|
8
|
+
// 2. Skills tree → <destination>/skills/arcs-<name>/...
|
|
9
|
+
// (Claude Code skills must be flat under skills/; we use the `arcs-`
|
|
10
|
+
// prefix as a namespace so orphan pruning never touches user skills.)
|
|
11
|
+
// 3. Default agent → settings.json `agent` field set to "arcs-orchestrate"
|
|
12
|
+
//
|
|
13
|
+
// Env vars:
|
|
14
|
+
// DEPLOY_BUNDLE_ROOT — override bundle root (default: opencode/arcs)
|
|
15
|
+
// DEPLOY_CONFIG_ROOT — override config root (default: ~/.claude)
|
|
16
|
+
// DEPLOY_PROJECT_ROOT — override project root (default: repository root)
|
|
17
|
+
// DEPLOY_SCOPE — `global` or `project` (default: `global`)
|
|
18
|
+
// DEPLOY_DRY_RUN — "false" to actually write; anything else = dry-run (default: dry-run)
|
|
19
|
+
//
|
|
20
|
+
// Outputs JSON to stdout: DeployResult
|
|
21
|
+
// Exit code: 0 on success, 1 on error.
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
existsSync,
|
|
25
|
+
mkdirSync,
|
|
26
|
+
readdirSync,
|
|
27
|
+
readFileSync,
|
|
28
|
+
rmSync,
|
|
29
|
+
statSync,
|
|
30
|
+
writeFileSync,
|
|
31
|
+
} from "node:fs";
|
|
32
|
+
import { homedir } from "node:os";
|
|
33
|
+
import { dirname, relative, resolve } from "node:path";
|
|
34
|
+
|
|
35
|
+
const repoRoot = resolve(import.meta.dirname, "..");
|
|
36
|
+
const defaultBundleRoot = resolve(repoRoot, "opencode/arcs");
|
|
37
|
+
const defaultConfigRoot = resolve(homedir(), ".claude");
|
|
38
|
+
const defaultProjectRoot = repoRoot;
|
|
39
|
+
|
|
40
|
+
const bundleRoot = process.env.DEPLOY_BUNDLE_ROOT
|
|
41
|
+
? resolve(repoRoot, process.env.DEPLOY_BUNDLE_ROOT)
|
|
42
|
+
: defaultBundleRoot;
|
|
43
|
+
|
|
44
|
+
let configRoot;
|
|
45
|
+
if (process.env.DEPLOY_CONFIG_ROOT) {
|
|
46
|
+
const rawPath = process.env.DEPLOY_CONFIG_ROOT;
|
|
47
|
+
if (rawPath.startsWith("~/")) {
|
|
48
|
+
configRoot = resolve(homedir(), rawPath.slice(2));
|
|
49
|
+
} else if (rawPath === "~") {
|
|
50
|
+
configRoot = homedir();
|
|
51
|
+
} else {
|
|
52
|
+
configRoot = resolve(homedir(), rawPath);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
configRoot = defaultConfigRoot;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const projectRoot = process.env.DEPLOY_PROJECT_ROOT
|
|
59
|
+
? resolve(repoRoot, process.env.DEPLOY_PROJECT_ROOT)
|
|
60
|
+
: defaultProjectRoot;
|
|
61
|
+
|
|
62
|
+
const scope = process.env.DEPLOY_SCOPE === "project" ? "project" : "global";
|
|
63
|
+
const dryRun = process.env.DEPLOY_DRY_RUN !== "false";
|
|
64
|
+
|
|
65
|
+
const destination = scope === "project" ? projectRoot : configRoot;
|
|
66
|
+
|
|
67
|
+
const DEFAULT_AGENT = "arcs-orchestrate";
|
|
68
|
+
const SKILL_PREFIX = "arcs-";
|
|
69
|
+
|
|
70
|
+
const agentMetadata = {
|
|
71
|
+
"software-engineer": {
|
|
72
|
+
name: "Software Engineer",
|
|
73
|
+
description: "Implementation specialist. Writes code, runs tests, and ships features.",
|
|
74
|
+
tools: "Read, Write, Edit, Glob, Grep, Bash",
|
|
75
|
+
model: "inherit",
|
|
76
|
+
},
|
|
77
|
+
"devil-advocate": {
|
|
78
|
+
name: "Devil's Advocate",
|
|
79
|
+
description: "Adversarial phase-gate agent. Checks work using KISS/YAGNI/DRY principles.",
|
|
80
|
+
tools: "Read, Glob, Grep, Bash",
|
|
81
|
+
model: "inherit",
|
|
82
|
+
},
|
|
83
|
+
"tech-architect": {
|
|
84
|
+
name: "Technical Architect",
|
|
85
|
+
description:
|
|
86
|
+
"Architecture and analysis specialist. Deep structural reasoning and trade-off evaluation.",
|
|
87
|
+
tools: "Read, Glob, Grep, Bash",
|
|
88
|
+
model: "inherit",
|
|
89
|
+
},
|
|
90
|
+
"code-reviewer": {
|
|
91
|
+
name: "Code Reviewer",
|
|
92
|
+
description: "Reviews code changes for correctness, maintainability, and testing issues.",
|
|
93
|
+
tools: "Read, Glob, Grep, Bash",
|
|
94
|
+
model: "inherit",
|
|
95
|
+
},
|
|
96
|
+
"qa-analyst": {
|
|
97
|
+
name: "QA Analyst",
|
|
98
|
+
description: "Quality enforcement specialist. Proactive code audits and convention compliance.",
|
|
99
|
+
tools: "Read, Glob, Grep, Bash",
|
|
100
|
+
model: "inherit",
|
|
101
|
+
},
|
|
102
|
+
"system-architect": {
|
|
103
|
+
name: "System Architect",
|
|
104
|
+
description:
|
|
105
|
+
"Architecture and design specialist. Module boundaries, dependency graphs, and design decisions.",
|
|
106
|
+
tools: "Read, Glob, Grep, Bash",
|
|
107
|
+
model: "inherit",
|
|
108
|
+
},
|
|
109
|
+
"arcs-docs": {
|
|
110
|
+
name: "ARCS Docs Specialist",
|
|
111
|
+
description:
|
|
112
|
+
"Documentation specialist. Manages plans, knowledge entries, diagrams, and DAG health.",
|
|
113
|
+
tools: "Read, Write, Edit, Glob, Grep, Bash",
|
|
114
|
+
model: "inherit",
|
|
115
|
+
},
|
|
116
|
+
"docs-researcher": {
|
|
117
|
+
name: "Docs Researcher",
|
|
118
|
+
description: "Handles documentation writing, research synthesis, and document-heavy analysis.",
|
|
119
|
+
tools: "Read, Write, Edit, Glob, Grep, Bash",
|
|
120
|
+
model: "inherit",
|
|
121
|
+
},
|
|
122
|
+
"oncall-ops": {
|
|
123
|
+
name: "On-Call Ops",
|
|
124
|
+
description:
|
|
125
|
+
"Debugging and diagnosis specialist. Finds root causes through systematic investigation.",
|
|
126
|
+
tools: "Read, Write, Edit, Glob, Grep, Bash",
|
|
127
|
+
model: "inherit",
|
|
128
|
+
},
|
|
129
|
+
"arcs-orchestrate": {
|
|
130
|
+
name: "ARCS Orchestrator",
|
|
131
|
+
description:
|
|
132
|
+
"The central coordinator for executing plans, managing agent dispatch, and handling DAG workflows.",
|
|
133
|
+
tools: "Read, Write, Edit, Glob, Grep, Bash",
|
|
134
|
+
model: "inherit",
|
|
135
|
+
},
|
|
136
|
+
"arcs-orchestrate-caveman": {
|
|
137
|
+
name: "ARCS Orchestrator (Caveman)",
|
|
138
|
+
description:
|
|
139
|
+
"A terse, high-efficiency orchestrator that drives tasks without extra commentary.",
|
|
140
|
+
tools: "Read, Write, Edit, Glob, Grep, Bash",
|
|
141
|
+
model: "inherit",
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
function ensureParentDir(filePath) {
|
|
146
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function configRelativePath(suffix) {
|
|
150
|
+
return scope === "project" ? `.claude/${suffix}` : suffix;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function walkFiles(dir) {
|
|
154
|
+
const out = [];
|
|
155
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
156
|
+
const full = resolve(dir, entry.name);
|
|
157
|
+
if (entry.isDirectory()) {
|
|
158
|
+
out.push(...walkFiles(full));
|
|
159
|
+
} else if (entry.isFile()) {
|
|
160
|
+
out.push(full);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// 1. Compile sub-agent prompts
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
function buildAgentSources() {
|
|
171
|
+
const promptsDir = resolve(bundleRoot, "prompts");
|
|
172
|
+
if (!existsSync(promptsDir)) {
|
|
173
|
+
throw new Error(`Prompts directory not found at ${promptsDir}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const promptFiles = readdirSync(promptsDir).filter((f) => f.endsWith(".txt"));
|
|
177
|
+
const sources = [];
|
|
178
|
+
|
|
179
|
+
for (const file of promptFiles) {
|
|
180
|
+
const stem = file.slice(0, -4);
|
|
181
|
+
const sourcePath = resolve(promptsDir, file);
|
|
182
|
+
const promptContent = readFileSync(sourcePath, "utf-8");
|
|
183
|
+
|
|
184
|
+
const meta = agentMetadata[stem] || {
|
|
185
|
+
name: stem
|
|
186
|
+
.split("-")
|
|
187
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
188
|
+
.join(" "),
|
|
189
|
+
description: `ARCS ${stem} agent.`,
|
|
190
|
+
tools: "Read, Write, Edit, Glob, Grep, Bash",
|
|
191
|
+
model: "inherit",
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const toolsArray = meta.tools.split(",").map((t) => t.trim());
|
|
195
|
+
const toolsYaml = `[${toolsArray.map((t) => `"${t}"`).join(", ")}]`;
|
|
196
|
+
|
|
197
|
+
const compiledContent = [
|
|
198
|
+
"---",
|
|
199
|
+
`name: ${meta.name}`,
|
|
200
|
+
`description: ${meta.description}`,
|
|
201
|
+
`model: ${meta.model}`,
|
|
202
|
+
`tools: ${toolsYaml}`,
|
|
203
|
+
"---",
|
|
204
|
+
"",
|
|
205
|
+
promptContent,
|
|
206
|
+
].join("\n");
|
|
207
|
+
|
|
208
|
+
sources.push({
|
|
209
|
+
stem,
|
|
210
|
+
compiledContent,
|
|
211
|
+
configRelative: configRelativePath(`agents/${stem}.md`),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return sources;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// 2. Walk skills tree
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
function buildSkillSources() {
|
|
223
|
+
const skillsDir = resolve(bundleRoot, "skills");
|
|
224
|
+
if (!existsSync(skillsDir)) return { skillNames: [], files: [] };
|
|
225
|
+
|
|
226
|
+
const skillNames = readdirSync(skillsDir, { withFileTypes: true })
|
|
227
|
+
.filter((e) => e.isDirectory())
|
|
228
|
+
.map((e) => e.name);
|
|
229
|
+
|
|
230
|
+
const files = [];
|
|
231
|
+
for (const skillName of skillNames) {
|
|
232
|
+
const skillRoot = resolve(skillsDir, skillName);
|
|
233
|
+
for (const absPath of walkFiles(skillRoot)) {
|
|
234
|
+
const rel = relative(skillRoot, absPath).split(/[\\/]/).join("/");
|
|
235
|
+
files.push({
|
|
236
|
+
skillName,
|
|
237
|
+
absSource: absPath,
|
|
238
|
+
configRelative: configRelativePath(`skills/${SKILL_PREFIX}${skillName}/${rel}`),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { skillNames, files };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
// 3. settings.json merge — set default agent
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
function planSettingsUpdate() {
|
|
251
|
+
const settingsConfigRelative = configRelativePath("settings.json");
|
|
252
|
+
const settingsAbsolute = resolve(destination, settingsConfigRelative);
|
|
253
|
+
|
|
254
|
+
let existing = null;
|
|
255
|
+
let parsed = {};
|
|
256
|
+
if (existsSync(settingsAbsolute)) {
|
|
257
|
+
existing = readFileSync(settingsAbsolute, "utf-8");
|
|
258
|
+
try {
|
|
259
|
+
parsed = JSON.parse(existing);
|
|
260
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
261
|
+
parsed = {};
|
|
262
|
+
}
|
|
263
|
+
} catch {
|
|
264
|
+
// Malformed JSON — preserve as-is by skipping the merge.
|
|
265
|
+
return { settingsConfigRelative, settingsAbsolute, status: "skipped-malformed" };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const merged = { ...parsed, agent: DEFAULT_AGENT };
|
|
270
|
+
const serialized = `${JSON.stringify(merged, null, 2)}\n`;
|
|
271
|
+
|
|
272
|
+
let status;
|
|
273
|
+
if (existing === null) {
|
|
274
|
+
status = "added";
|
|
275
|
+
} else if (existing === serialized) {
|
|
276
|
+
status = "unchanged";
|
|
277
|
+
} else {
|
|
278
|
+
status = "changed";
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return { settingsConfigRelative, settingsAbsolute, status, serialized };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
// Main
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
|
|
288
|
+
function main() {
|
|
289
|
+
const agentSources = buildAgentSources();
|
|
290
|
+
const skillSources = buildSkillSources();
|
|
291
|
+
const settingsPlan = planSettingsUpdate();
|
|
292
|
+
|
|
293
|
+
// Pass 1: Classify add/change/unchanged for every planned write
|
|
294
|
+
const filesAdded = [];
|
|
295
|
+
const filesChanged = [];
|
|
296
|
+
const filesUnchanged = [];
|
|
297
|
+
|
|
298
|
+
for (const { compiledContent, configRelative } of agentSources) {
|
|
299
|
+
const abs = resolve(destination, configRelative);
|
|
300
|
+
if (!existsSync(abs)) {
|
|
301
|
+
filesAdded.push(configRelative);
|
|
302
|
+
} else if (readFileSync(abs, "utf-8") !== compiledContent) {
|
|
303
|
+
filesChanged.push(configRelative);
|
|
304
|
+
} else {
|
|
305
|
+
filesUnchanged.push(configRelative);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
for (const { absSource, configRelative } of skillSources.files) {
|
|
310
|
+
const abs = resolve(destination, configRelative);
|
|
311
|
+
const sourceContent = readFileSync(absSource);
|
|
312
|
+
if (!existsSync(abs)) {
|
|
313
|
+
filesAdded.push(configRelative);
|
|
314
|
+
} else {
|
|
315
|
+
const existing = readFileSync(abs);
|
|
316
|
+
if (Buffer.compare(existing, sourceContent) !== 0) {
|
|
317
|
+
filesChanged.push(configRelative);
|
|
318
|
+
} else {
|
|
319
|
+
filesUnchanged.push(configRelative);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (settingsPlan.status === "added") {
|
|
325
|
+
filesAdded.push(settingsPlan.settingsConfigRelative);
|
|
326
|
+
} else if (settingsPlan.status === "changed") {
|
|
327
|
+
filesChanged.push(settingsPlan.settingsConfigRelative);
|
|
328
|
+
} else if (settingsPlan.status === "unchanged") {
|
|
329
|
+
filesUnchanged.push(settingsPlan.settingsConfigRelative);
|
|
330
|
+
}
|
|
331
|
+
// status "skipped-malformed" intentionally omitted from all lists
|
|
332
|
+
|
|
333
|
+
// Orphans
|
|
334
|
+
const filesRemoved = [];
|
|
335
|
+
|
|
336
|
+
// Agent orphans — only `.md` files whose stem we recognize from agentMetadata
|
|
337
|
+
const agentsTargetDir = resolve(destination, configRelativePath("agents"));
|
|
338
|
+
const activeAgentStems = new Set(agentSources.map((f) => f.stem));
|
|
339
|
+
if (existsSync(agentsTargetDir)) {
|
|
340
|
+
for (const entry of readdirSync(agentsTargetDir, { withFileTypes: true })) {
|
|
341
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
342
|
+
const stem = entry.name.slice(0, -3);
|
|
343
|
+
if (agentMetadata[stem] && !activeAgentStems.has(stem)) {
|
|
344
|
+
filesRemoved.push(configRelativePath(`agents/${entry.name}`));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Skill orphans — only `arcs-*/` directories that aren't in current source.
|
|
351
|
+
// The prefix gives us a safe namespace; foreign user skills are never touched.
|
|
352
|
+
const skillsTargetDir = resolve(destination, configRelativePath("skills"));
|
|
353
|
+
const activeSkillDirs = new Set(skillSources.skillNames.map((n) => `${SKILL_PREFIX}${n}`));
|
|
354
|
+
const orphanSkillDirs = [];
|
|
355
|
+
if (existsSync(skillsTargetDir)) {
|
|
356
|
+
for (const entry of readdirSync(skillsTargetDir, { withFileTypes: true })) {
|
|
357
|
+
if (entry.isDirectory() && entry.name.startsWith(SKILL_PREFIX)) {
|
|
358
|
+
if (!activeSkillDirs.has(entry.name)) {
|
|
359
|
+
orphanSkillDirs.push(entry.name);
|
|
360
|
+
filesRemoved.push(configRelativePath(`skills/${entry.name}`));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Pass 2: Write if not dry-run
|
|
367
|
+
if (!dryRun) {
|
|
368
|
+
for (const { compiledContent, configRelative } of agentSources) {
|
|
369
|
+
const abs = resolve(destination, configRelative);
|
|
370
|
+
ensureParentDir(abs);
|
|
371
|
+
writeFileSync(abs, compiledContent, "utf-8");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
for (const { absSource, configRelative } of skillSources.files) {
|
|
375
|
+
const abs = resolve(destination, configRelative);
|
|
376
|
+
ensureParentDir(abs);
|
|
377
|
+
writeFileSync(abs, readFileSync(absSource));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (settingsPlan.serialized) {
|
|
381
|
+
ensureParentDir(settingsPlan.settingsAbsolute);
|
|
382
|
+
writeFileSync(settingsPlan.settingsAbsolute, settingsPlan.serialized, "utf-8");
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
for (const fileToRemove of filesRemoved) {
|
|
386
|
+
const abs = resolve(destination, fileToRemove);
|
|
387
|
+
if (existsSync(abs)) {
|
|
388
|
+
// Could be a file (agent orphan) or directory (skill orphan)
|
|
389
|
+
const isDir = statSync(abs).isDirectory();
|
|
390
|
+
rmSync(abs, { recursive: isDir, force: true });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const result = {
|
|
396
|
+
dryRun,
|
|
397
|
+
source: bundleRoot,
|
|
398
|
+
destination,
|
|
399
|
+
filesAdded,
|
|
400
|
+
filesChanged,
|
|
401
|
+
filesRemoved,
|
|
402
|
+
filesUnchanged,
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
main();
|
|
410
|
+
} catch (error) {
|
|
411
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
412
|
+
process.stderr.write(`${message}\n`);
|
|
413
|
+
process.exitCode = 1;
|
|
414
|
+
}
|