@aiwg/cli 2026.9.15 → 2026.9.16
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/agentic/code/providers/capability-matrix.yaml +42 -0
- package/agentic/code/providers/model-capabilities.v1.json +119 -34
- package/agentic/code/providers/model-catalog.v1.json +259 -56
- package/dist/src/cli/handlers/sessions.js +159 -45
- package/dist/src/cli/handlers/use.js +20 -1
- package/dist/src/cli/scope-resolver.js +13 -0
- package/dist/src/cli/services/deployment-verification.js +33 -2
- package/dist/src/config/aiwg-config.js +39 -0
- package/dist/src/config/user-registry.js +9 -0
- package/dist/src/extensions/manifest.js +1 -0
- package/dist/src/models/model-capabilities.v1.json +119 -34
- package/dist/src/models/model-catalog.v1.json +259 -56
- package/dist/src/models/model-discovery.js +7 -0
- package/dist/src/models/provider-policy.js +1 -1
- package/dist/src/plugin/skill-command-translator.js +1 -0
- package/dist/src/providers/capability-matrix.yaml +42 -0
- package/dist/src/providers/grokbot-paths.js +103 -0
- package/dist/src/providers/provider-definitions.js +85 -1
- package/dist/src/providers/provider-inventory.js +1 -0
- package/dist/src/sessions/adapters/grokbot.js +49 -0
- package/dist/src/sessions/adapters/hermes.js +17 -9
- package/dist/src/sessions/adapters/opencode.js +12 -1
- package/dist/src/sessions/contracts.js +1 -1
- package/dist/src/sessions/export-plan.js +130 -4
- package/dist/src/sessions/fortemi-export-mapping.js +10 -6
- package/dist/src/sessions/fortemi-shard-blobs.js +78 -0
- package/dist/src/sessions/index.js +2 -0
- package/dist/src/sessions/workspace-discovery.js +1 -1
- package/dist/src/skills/deployer.js +17 -0
- package/dist/src/smiths/skillsmith/collision-detector.js +1 -0
- package/package.json +1 -1
- package/tools/agents/deploy-agents.mjs +3 -2
- package/tools/agents/providers/grokbot.mjs +285 -0
|
@@ -6,59 +6,150 @@
|
|
|
6
6
|
"providers": {
|
|
7
7
|
"antigravity": {
|
|
8
8
|
"roles": {
|
|
9
|
-
"reasoning": {
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
"reasoning": {
|
|
10
|
+
"id": "configured/reasoning",
|
|
11
|
+
"status": "unverified",
|
|
12
|
+
"observed": false
|
|
13
|
+
},
|
|
14
|
+
"coding": {
|
|
15
|
+
"id": "configured/coding",
|
|
16
|
+
"status": "unverified",
|
|
17
|
+
"observed": false
|
|
18
|
+
},
|
|
19
|
+
"efficiency": {
|
|
20
|
+
"id": "configured/efficiency",
|
|
21
|
+
"status": "unverified",
|
|
22
|
+
"observed": false
|
|
23
|
+
}
|
|
12
24
|
},
|
|
13
|
-
"sourceUrl": "https://antigravity.google/docs/cli/overview/",
|
|
25
|
+
"sourceUrl": "https://antigravity.google/docs/cli/overview/",
|
|
26
|
+
"verifiedAt": "2026-09-04"
|
|
14
27
|
},
|
|
15
28
|
"claude": {
|
|
16
29
|
"roles": {
|
|
17
|
-
"reasoning": {
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
"reasoning": {
|
|
31
|
+
"id": "claude-opus-4-7",
|
|
32
|
+
"status": "active",
|
|
33
|
+
"observed": false
|
|
34
|
+
},
|
|
35
|
+
"coding": {
|
|
36
|
+
"id": "claude-sonnet-4-6",
|
|
37
|
+
"status": "active",
|
|
38
|
+
"observed": false
|
|
39
|
+
},
|
|
40
|
+
"efficiency": {
|
|
41
|
+
"id": "claude-haiku-4-5",
|
|
42
|
+
"status": "active",
|
|
43
|
+
"observed": false
|
|
44
|
+
}
|
|
20
45
|
},
|
|
21
|
-
"sourceUrl": "https://docs.anthropic.com/en/docs/about-claude/models/overview",
|
|
46
|
+
"sourceUrl": "https://docs.anthropic.com/en/docs/about-claude/models/overview",
|
|
47
|
+
"verifiedAt": "2026-07-20"
|
|
22
48
|
},
|
|
23
49
|
"codex": {
|
|
24
50
|
"roles": {
|
|
25
|
-
"reasoning": {
|
|
26
|
-
|
|
27
|
-
|
|
51
|
+
"reasoning": {
|
|
52
|
+
"id": "gpt-5.4",
|
|
53
|
+
"status": "active",
|
|
54
|
+
"observed": true
|
|
55
|
+
},
|
|
56
|
+
"coding": {
|
|
57
|
+
"id": "gpt-5.5",
|
|
58
|
+
"status": "active",
|
|
59
|
+
"observed": true
|
|
60
|
+
},
|
|
61
|
+
"efficiency": {
|
|
62
|
+
"id": "gpt-5.4-mini",
|
|
63
|
+
"status": "active",
|
|
64
|
+
"observed": true
|
|
65
|
+
}
|
|
28
66
|
},
|
|
29
|
-
"sourceUrl": "https://learn.chatgpt.com/docs/agent-configuration/subagents",
|
|
67
|
+
"sourceUrl": "https://learn.chatgpt.com/docs/agent-configuration/subagents",
|
|
68
|
+
"verifiedAt": "2026-07-20"
|
|
30
69
|
},
|
|
31
70
|
"copilot": {
|
|
32
71
|
"roles": {
|
|
33
|
-
"reasoning": {
|
|
34
|
-
|
|
35
|
-
|
|
72
|
+
"reasoning": {
|
|
73
|
+
"id": "claude-opus-4.6",
|
|
74
|
+
"status": "unverified",
|
|
75
|
+
"observed": false
|
|
76
|
+
},
|
|
77
|
+
"coding": {
|
|
78
|
+
"id": "claude-sonnet-4.6",
|
|
79
|
+
"status": "unverified",
|
|
80
|
+
"observed": false
|
|
81
|
+
},
|
|
82
|
+
"efficiency": {
|
|
83
|
+
"id": "gpt-5.1-codex-mini",
|
|
84
|
+
"status": "unverified",
|
|
85
|
+
"observed": false
|
|
86
|
+
}
|
|
36
87
|
},
|
|
37
|
-
"sourceUrl": "https://docs.github.com/en/copilot/reference/ai-models/supported-models",
|
|
88
|
+
"sourceUrl": "https://docs.github.com/en/copilot/reference/ai-models/supported-models",
|
|
89
|
+
"verifiedAt": "2026-07-20"
|
|
38
90
|
},
|
|
39
91
|
"cursor": {
|
|
40
92
|
"roles": {
|
|
41
|
-
"reasoning": {
|
|
42
|
-
|
|
43
|
-
|
|
93
|
+
"reasoning": {
|
|
94
|
+
"id": "claude-4.6-opus-high-thinking",
|
|
95
|
+
"status": "unverified",
|
|
96
|
+
"observed": false
|
|
97
|
+
},
|
|
98
|
+
"coding": {
|
|
99
|
+
"id": "claude-4.6-sonnet",
|
|
100
|
+
"status": "unverified",
|
|
101
|
+
"observed": false
|
|
102
|
+
},
|
|
103
|
+
"efficiency": {
|
|
104
|
+
"id": "auto",
|
|
105
|
+
"status": "unverified",
|
|
106
|
+
"observed": false
|
|
107
|
+
}
|
|
44
108
|
},
|
|
45
|
-
"sourceUrl": "https://cursor.com/docs/models",
|
|
109
|
+
"sourceUrl": "https://cursor.com/docs/models",
|
|
110
|
+
"verifiedAt": "2026-07-20"
|
|
46
111
|
},
|
|
47
112
|
"factory": {
|
|
48
113
|
"roles": {
|
|
49
|
-
"reasoning": {
|
|
50
|
-
|
|
51
|
-
|
|
114
|
+
"reasoning": {
|
|
115
|
+
"id": "heavy",
|
|
116
|
+
"status": "active",
|
|
117
|
+
"observed": false
|
|
118
|
+
},
|
|
119
|
+
"coding": {
|
|
120
|
+
"id": "medium",
|
|
121
|
+
"status": "active",
|
|
122
|
+
"observed": false
|
|
123
|
+
},
|
|
124
|
+
"efficiency": {
|
|
125
|
+
"id": "light",
|
|
126
|
+
"status": "active",
|
|
127
|
+
"observed": false
|
|
128
|
+
}
|
|
52
129
|
},
|
|
53
|
-
"sourceUrl": "https://docs.factory.ai/cli/user-guides/choosing-your-model",
|
|
130
|
+
"sourceUrl": "https://docs.factory.ai/cli/user-guides/choosing-your-model",
|
|
131
|
+
"verifiedAt": "2026-07-20"
|
|
54
132
|
},
|
|
55
133
|
"opencode": {
|
|
56
134
|
"roles": {
|
|
57
|
-
"reasoning": {
|
|
58
|
-
|
|
59
|
-
|
|
135
|
+
"reasoning": {
|
|
136
|
+
"id": "anthropic/claude-opus-4-7",
|
|
137
|
+
"status": "unverified",
|
|
138
|
+
"observed": false
|
|
139
|
+
},
|
|
140
|
+
"coding": {
|
|
141
|
+
"id": "anthropic/claude-sonnet-4-6",
|
|
142
|
+
"status": "unverified",
|
|
143
|
+
"observed": false
|
|
144
|
+
},
|
|
145
|
+
"efficiency": {
|
|
146
|
+
"id": "anthropic/claude-haiku-4-5",
|
|
147
|
+
"status": "unverified",
|
|
148
|
+
"observed": false
|
|
149
|
+
}
|
|
60
150
|
},
|
|
61
|
-
"sourceUrl": "https://opencode.ai/docs/models",
|
|
151
|
+
"sourceUrl": "https://opencode.ai/docs/models",
|
|
152
|
+
"verifiedAt": "2026-07-20"
|
|
62
153
|
},
|
|
63
154
|
"omp": {
|
|
64
155
|
"roles": {
|
|
@@ -83,59 +174,171 @@
|
|
|
83
174
|
},
|
|
84
175
|
"pi": {
|
|
85
176
|
"roles": {
|
|
86
|
-
"reasoning": {
|
|
87
|
-
|
|
88
|
-
|
|
177
|
+
"reasoning": {
|
|
178
|
+
"id": "configured/reasoning",
|
|
179
|
+
"status": "unverified",
|
|
180
|
+
"observed": false
|
|
181
|
+
},
|
|
182
|
+
"coding": {
|
|
183
|
+
"id": "configured/coding",
|
|
184
|
+
"status": "unverified",
|
|
185
|
+
"observed": false
|
|
186
|
+
},
|
|
187
|
+
"efficiency": {
|
|
188
|
+
"id": "configured/efficiency",
|
|
189
|
+
"status": "unverified",
|
|
190
|
+
"observed": false
|
|
191
|
+
}
|
|
89
192
|
},
|
|
90
|
-
"sourceUrl": "https://github.com/earendil-works/pi/tree/main/packages/coding-agent#providers--models",
|
|
193
|
+
"sourceUrl": "https://github.com/earendil-works/pi/tree/main/packages/coding-agent#providers--models",
|
|
194
|
+
"verifiedAt": "2026-09-04"
|
|
91
195
|
},
|
|
92
196
|
"deepseek-harness": {
|
|
93
197
|
"roles": {
|
|
94
|
-
"reasoning": {
|
|
95
|
-
|
|
96
|
-
|
|
198
|
+
"reasoning": {
|
|
199
|
+
"id": "configured/reasoning",
|
|
200
|
+
"status": "unverified",
|
|
201
|
+
"observed": false
|
|
202
|
+
},
|
|
203
|
+
"coding": {
|
|
204
|
+
"id": "configured/coding",
|
|
205
|
+
"status": "unverified",
|
|
206
|
+
"observed": false
|
|
207
|
+
},
|
|
208
|
+
"efficiency": {
|
|
209
|
+
"id": "configured/efficiency",
|
|
210
|
+
"status": "unverified",
|
|
211
|
+
"observed": false
|
|
212
|
+
}
|
|
97
213
|
},
|
|
98
|
-
"sourceUrl": "https://github.com/deepseek-ai/deepseek-harness",
|
|
214
|
+
"sourceUrl": "https://github.com/deepseek-ai/deepseek-harness",
|
|
215
|
+
"verifiedAt": "2026-09-05"
|
|
99
216
|
},
|
|
100
217
|
"warp": {
|
|
101
218
|
"roles": {
|
|
102
|
-
"reasoning": {
|
|
103
|
-
|
|
104
|
-
|
|
219
|
+
"reasoning": {
|
|
220
|
+
"id": "profile-selected",
|
|
221
|
+
"status": "unverified",
|
|
222
|
+
"observed": false
|
|
223
|
+
},
|
|
224
|
+
"coding": {
|
|
225
|
+
"id": "profile-selected",
|
|
226
|
+
"status": "unverified",
|
|
227
|
+
"observed": false
|
|
228
|
+
},
|
|
229
|
+
"efficiency": {
|
|
230
|
+
"id": "profile-selected",
|
|
231
|
+
"status": "unverified",
|
|
232
|
+
"observed": false
|
|
233
|
+
}
|
|
105
234
|
},
|
|
106
|
-
"sourceUrl": "https://docs.warp.dev/agent-platform/capabilities/agent-profiles-permissions",
|
|
235
|
+
"sourceUrl": "https://docs.warp.dev/agent-platform/capabilities/agent-profiles-permissions",
|
|
236
|
+
"verifiedAt": "2026-07-20"
|
|
107
237
|
},
|
|
108
238
|
"windsurf": {
|
|
109
239
|
"roles": {
|
|
110
|
-
"reasoning": {
|
|
111
|
-
|
|
112
|
-
|
|
240
|
+
"reasoning": {
|
|
241
|
+
"id": "inherited",
|
|
242
|
+
"status": "unverified",
|
|
243
|
+
"observed": false
|
|
244
|
+
},
|
|
245
|
+
"coding": {
|
|
246
|
+
"id": "inherited",
|
|
247
|
+
"status": "unverified",
|
|
248
|
+
"observed": false
|
|
249
|
+
},
|
|
250
|
+
"efficiency": {
|
|
251
|
+
"id": "inherited",
|
|
252
|
+
"status": "unverified",
|
|
253
|
+
"observed": false
|
|
254
|
+
}
|
|
113
255
|
},
|
|
114
|
-
"sourceUrl": "https://docs.devin.ai/desktop/cascade/models",
|
|
256
|
+
"sourceUrl": "https://docs.devin.ai/desktop/cascade/models",
|
|
257
|
+
"verifiedAt": "2026-07-20"
|
|
115
258
|
},
|
|
116
259
|
"openclaw": {
|
|
117
260
|
"roles": {
|
|
118
|
-
"reasoning": {
|
|
119
|
-
|
|
120
|
-
|
|
261
|
+
"reasoning": {
|
|
262
|
+
"id": "configured/reasoning",
|
|
263
|
+
"status": "unverified",
|
|
264
|
+
"observed": false
|
|
265
|
+
},
|
|
266
|
+
"coding": {
|
|
267
|
+
"id": "configured/coding",
|
|
268
|
+
"status": "unverified",
|
|
269
|
+
"observed": false
|
|
270
|
+
},
|
|
271
|
+
"efficiency": {
|
|
272
|
+
"id": "configured/efficiency",
|
|
273
|
+
"status": "unverified",
|
|
274
|
+
"observed": false
|
|
275
|
+
}
|
|
121
276
|
},
|
|
122
|
-
"sourceUrl": "https://docs.openclaw.ai/tools/subagents",
|
|
277
|
+
"sourceUrl": "https://docs.openclaw.ai/tools/subagents",
|
|
278
|
+
"verifiedAt": "2026-07-20"
|
|
279
|
+
},
|
|
280
|
+
"grokbot": {
|
|
281
|
+
"roles": {
|
|
282
|
+
"reasoning": {
|
|
283
|
+
"id": "global-delegation-model",
|
|
284
|
+
"status": "unverified",
|
|
285
|
+
"observed": false
|
|
286
|
+
},
|
|
287
|
+
"coding": {
|
|
288
|
+
"id": "global-delegation-model",
|
|
289
|
+
"status": "unverified",
|
|
290
|
+
"observed": false
|
|
291
|
+
},
|
|
292
|
+
"efficiency": {
|
|
293
|
+
"id": "global-delegation-model",
|
|
294
|
+
"status": "unverified",
|
|
295
|
+
"observed": false
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
"sourceUrl": "https://github.com/jmagly/aiwg/blob/main/docs/architecture/adr-grokbot-provider-target.md",
|
|
299
|
+
"verifiedAt": "2026-09-15"
|
|
123
300
|
},
|
|
124
301
|
"hermes": {
|
|
125
302
|
"roles": {
|
|
126
|
-
"reasoning": {
|
|
127
|
-
|
|
128
|
-
|
|
303
|
+
"reasoning": {
|
|
304
|
+
"id": "global-delegation-model",
|
|
305
|
+
"status": "unverified",
|
|
306
|
+
"observed": false
|
|
307
|
+
},
|
|
308
|
+
"coding": {
|
|
309
|
+
"id": "global-delegation-model",
|
|
310
|
+
"status": "unverified",
|
|
311
|
+
"observed": false
|
|
312
|
+
},
|
|
313
|
+
"efficiency": {
|
|
314
|
+
"id": "global-delegation-model",
|
|
315
|
+
"status": "unverified",
|
|
316
|
+
"observed": false
|
|
317
|
+
}
|
|
129
318
|
},
|
|
130
|
-
"sourceUrl": "https://hermes-agent.nousresearch.com/docs/user-guide/features/delegation",
|
|
319
|
+
"sourceUrl": "https://hermes-agent.nousresearch.com/docs/user-guide/features/delegation",
|
|
320
|
+
"verifiedAt": "2026-07-20"
|
|
131
321
|
},
|
|
132
322
|
"openhuman": {
|
|
133
323
|
"roles": {
|
|
134
|
-
"reasoning": {
|
|
135
|
-
|
|
136
|
-
|
|
324
|
+
"reasoning": {
|
|
325
|
+
"id": "Hint(reasoning)",
|
|
326
|
+
"status": "active",
|
|
327
|
+
"observed": false
|
|
328
|
+
},
|
|
329
|
+
"coding": {
|
|
330
|
+
"id": "Hint(coding)",
|
|
331
|
+
"status": "active",
|
|
332
|
+
"observed": false
|
|
333
|
+
},
|
|
334
|
+
"efficiency": {
|
|
335
|
+
"id": "Hint(efficiency)",
|
|
336
|
+
"status": "active",
|
|
337
|
+
"observed": false
|
|
338
|
+
}
|
|
137
339
|
},
|
|
138
|
-
"sourceUrl": "https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/harness/definition.rs",
|
|
340
|
+
"sourceUrl": "https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/harness/definition.rs",
|
|
341
|
+
"verifiedAt": "2026-07-20"
|
|
139
342
|
}
|
|
140
343
|
}
|
|
141
344
|
}
|
|
@@ -39,6 +39,13 @@ export const PROVIDER_DISCOVERY_DECISIONS = {
|
|
|
39
39
|
reason: 'Droid exposes model selection through policy and interactive surfaces, but no documented non-interactive model-list command.',
|
|
40
40
|
documentation: 'https://docs.factory.ai/cli/configuration/models',
|
|
41
41
|
},
|
|
42
|
+
grokbot: {
|
|
43
|
+
provider: 'grokbot',
|
|
44
|
+
status: 'unsupported',
|
|
45
|
+
interface: null,
|
|
46
|
+
reason: 'Grok Bot is a desktop multi-agent runtime with no verified non-interactive model-list command; AIWG does not invent xAI/Grok API enumeration as Grok Bot entitlement.',
|
|
47
|
+
documentation: 'https://github.com/jmagly/aiwg/blob/main/docs/architecture/adr-grokbot-provider-target.md',
|
|
48
|
+
},
|
|
42
49
|
hermes: {
|
|
43
50
|
provider: 'hermes',
|
|
44
51
|
status: 'unsupported',
|
|
@@ -26,7 +26,7 @@ function requireModelResource(filename) {
|
|
|
26
26
|
const capabilityData = requireModelResource('model-capabilities.v1.json');
|
|
27
27
|
const catalogData = requireModelResource('model-catalog.v1.json');
|
|
28
28
|
const ProviderSchema = z.enum([
|
|
29
|
-
'antigravity', 'claude', 'codex', 'copilot', 'cursor', 'deepseek-harness', 'factory', 'hermes',
|
|
29
|
+
'antigravity', 'claude', 'codex', 'copilot', 'cursor', 'deepseek-harness', 'factory', 'grokbot', 'hermes',
|
|
30
30
|
'opencode', 'openclaw', 'openhuman', 'omp', 'pi', 'warp', 'windsurf',
|
|
31
31
|
]);
|
|
32
32
|
const OutcomeSchema = z.enum([
|
|
@@ -359,6 +359,48 @@ providers:
|
|
|
359
359
|
deploy_target: project
|
|
360
360
|
aggregated_output: true
|
|
361
361
|
|
|
362
|
+
grokbot:
|
|
363
|
+
display_name: Grok Bot
|
|
364
|
+
status: stable
|
|
365
|
+
# Multi-agent desktop assistant runtime. Distinct from Cursor IDE and from
|
|
366
|
+
# xAI Grok Build / API. Native skill root is operator-configured via
|
|
367
|
+
# AIWG_GROKBOT_SKILLS_DIR (fail-closed; no invented ~/.grokbot).
|
|
368
|
+
# ADR: docs/architecture/adr-grokbot-provider-target.md · epic #196.
|
|
369
|
+
# Optional natives (routines/CreateAgent/connectors) deferred #209 — does not block stable.
|
|
370
|
+
# #210: stable via Linux PUW + security + migration + maintainer waiver (no macOS/Windows gate).
|
|
371
|
+
daemon_tier: unsupported
|
|
372
|
+
daemon_pty_adapter: false
|
|
373
|
+
artifact_paths:
|
|
374
|
+
agents: "Indexed: aiwg discover / aiwg show agent" # native CreateAgent teammates are Grok-owned (#209)
|
|
375
|
+
commands: "Indexed: aiwg discover / aiwg show command"
|
|
376
|
+
skills: "AIWG_GROKBOT_SKILLS_DIR when configured (fail-closed)"
|
|
377
|
+
skills_cross_agent: "AIWG_GROKBOT_SKILLS_DIR/.aiwg/skills with --copy-all"
|
|
378
|
+
rules: "Indexed: aiwg show rule + AGENTS.md discover-first bridge"
|
|
379
|
+
behaviors: "Unsupported as automatic AIWG artifact; map manually to Grok routines (#209)"
|
|
380
|
+
native_features:
|
|
381
|
+
cron: true # Grok routines — capability present; AIWG does not own the clock
|
|
382
|
+
agent_teams: true # CreateAgent/teammates — Grok-owned orchestration
|
|
383
|
+
tasks: false # no verified separate task contract
|
|
384
|
+
mcp: true # connectors/dynamic tools — capability; AIWG MCP install deferred #209
|
|
385
|
+
behaviors: false
|
|
386
|
+
mission_control: false
|
|
387
|
+
daemon: false
|
|
388
|
+
emulation:
|
|
389
|
+
cron: null
|
|
390
|
+
agent_teams: null
|
|
391
|
+
tasks: aiwg-mc
|
|
392
|
+
mcp: null
|
|
393
|
+
behaviors: aiwg-mc
|
|
394
|
+
mission_control: aiwg-mc
|
|
395
|
+
daemon: null
|
|
396
|
+
hook_wiring:
|
|
397
|
+
at_link_support: false
|
|
398
|
+
hook_file: null
|
|
399
|
+
fallback: full-inject
|
|
400
|
+
context_file: AGENTS.md
|
|
401
|
+
deploy_target: mixed
|
|
402
|
+
aggregated_output: false
|
|
403
|
+
|
|
362
404
|
hermes:
|
|
363
405
|
display_name: Hermes
|
|
364
406
|
status: stable
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grok Bot skill-root resolution — fail-closed until explicitly configured.
|
|
3
|
+
*
|
|
4
|
+
* Unlike Hermes (HERMES_HOME with a documented default) or Pi
|
|
5
|
+
* (PI_CODING_AGENT_DIR with ~/.pi/agent fallback), Grok Bot's native skill
|
|
6
|
+
* filesystem root is not yet verified in AIWG. This module therefore:
|
|
7
|
+
*
|
|
8
|
+
* 1. Honors AIWG_GROKBOT_SKILLS_DIR when set to an absolute path
|
|
9
|
+
* 2. Rejects relative paths, `~`-only values that would invent a home layout,
|
|
10
|
+
* and path segments that attempt traversal after resolution
|
|
11
|
+
* 3. Returns null when unset — callers MUST fail closed for user-scope writes
|
|
12
|
+
*
|
|
13
|
+
* Never invent ~/.grokbot or ~/.cursor as a fallback.
|
|
14
|
+
*
|
|
15
|
+
* @see docs/architecture/adr-grokbot-provider-target.md
|
|
16
|
+
* @issue #205 #207
|
|
17
|
+
*/
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
import * as path from 'node:path';
|
|
20
|
+
export const GROKBOT_SKILLS_DIR_ENV = 'AIWG_GROKBOT_SKILLS_DIR';
|
|
21
|
+
/**
|
|
22
|
+
* Expand a leading `~/` against the operator home directory.
|
|
23
|
+
* Bare `~` is rejected — it would invent a home skill root.
|
|
24
|
+
*/
|
|
25
|
+
function expandHomePrefix(raw, userHome = homedir()) {
|
|
26
|
+
if (raw === '~')
|
|
27
|
+
return raw;
|
|
28
|
+
if (raw.startsWith('~/'))
|
|
29
|
+
return path.join(userHome, raw.slice(2));
|
|
30
|
+
return raw;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate and resolve the configured Grok Bot skills directory.
|
|
34
|
+
* Returns a structured result so CLI/doctor can print actionable remediation.
|
|
35
|
+
*/
|
|
36
|
+
export function resolveGrokbotSkillsDirResult(env = process.env, userHome = homedir()) {
|
|
37
|
+
const raw = (env[GROKBOT_SKILLS_DIR_ENV] || '').trim();
|
|
38
|
+
if (!raw) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
reason: 'unset',
|
|
42
|
+
message: `Grok Bot user-scope skill root is not configured. Set ${GROKBOT_SKILLS_DIR_ENV} to the absolute path of the Grok Bot skills/workflows directory before using --scope user or --global. AIWG does not invent ~/.grokbot.`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const expanded = expandHomePrefix(raw, userHome);
|
|
46
|
+
if (expanded === '~' || expanded.startsWith('~')) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
reason: 'not-absolute',
|
|
50
|
+
message: `${GROKBOT_SKILLS_DIR_ENV} must be an absolute path (got '${raw}'). Bare '~' is rejected so AIWG never invents a Grok home layout.`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (!path.isAbsolute(expanded)) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
reason: 'not-absolute',
|
|
57
|
+
message: `${GROKBOT_SKILLS_DIR_ENV} must be an absolute path (got '${raw}'). Relative values are rejected.`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const resolved = path.resolve(expanded);
|
|
61
|
+
// After resolve(), reject any attempt to escape via .. that still looks suspicious
|
|
62
|
+
// relative to a normalized absolute root — path.resolve already collapses '..',
|
|
63
|
+
// so the main remaining check is that we did not start relative.
|
|
64
|
+
if (raw.includes('\0') || resolved.includes('\0')) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
reason: 'traversal',
|
|
68
|
+
message: `${GROKBOT_SKILLS_DIR_ENV} contains an invalid path character.`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Defensive: if the pre-resolve value contained '..' segments that would
|
|
72
|
+
// escape a declared parent, surface traversal. Absolute resolve is fine;
|
|
73
|
+
// we still refuse paths that are only '..' noise without a real root.
|
|
74
|
+
const normalizedInput = path.normalize(expanded);
|
|
75
|
+
if (normalizedInput === path.sep || normalizedInput === '') {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
reason: 'empty',
|
|
79
|
+
message: `${GROKBOT_SKILLS_DIR_ENV} resolves to an empty or root-only path; refuse to deploy there.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { ok: true, path: resolved, source: 'env' };
|
|
83
|
+
}
|
|
84
|
+
/** Absolute skills dir when configured; otherwise null (fail closed). */
|
|
85
|
+
export function resolveGrokbotSkillsDir(env = process.env, userHome = homedir()) {
|
|
86
|
+
const result = resolveGrokbotSkillsDirResult(env, userHome);
|
|
87
|
+
return result.ok ? result.path : null;
|
|
88
|
+
}
|
|
89
|
+
/** True when user-scope skill writes are allowed. */
|
|
90
|
+
export function isGrokbotUserScopeConfigured(env = process.env, userHome = homedir()) {
|
|
91
|
+
return resolveGrokbotSkillsDir(env, userHome) !== null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* User-facing remediation for missing/invalid Grok Bot skill root.
|
|
95
|
+
* Shared by `aiwg use`, doctor, and the provider writer.
|
|
96
|
+
*/
|
|
97
|
+
export function grokbotMissingRootRemediation(env = process.env, userHome = homedir()) {
|
|
98
|
+
const result = resolveGrokbotSkillsDirResult(env, userHome);
|
|
99
|
+
if (result.ok)
|
|
100
|
+
return '';
|
|
101
|
+
return result.message;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=grokbot-paths.js.map
|