@aiwg/cli 2026.8.0 → 2026.8.1
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 +33 -0
- package/agentic/code/providers/capability-matrix.yaml +511 -0
- package/agentic/code/providers/model-capabilities.v1.json +120 -0
- package/agentic/code/providers/model-catalog.v1.json +96 -0
- package/agentic/code/providers/model-policy-evaluations.v1.json +50 -0
- package/agentic/code/providers/premium-model-allowlist.v1.json +36 -0
- package/bin/aiwg.mjs +14 -10
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/cli.js +2 -0
- package/dist/src/artifacts/types.js +4 -0
- package/dist/src/auth/client.js +209 -0
- package/dist/src/auth/config.js +38 -0
- package/dist/src/auth/credential-store.js +141 -0
- package/dist/src/auth/resource-credentials.js +25 -0
- package/dist/src/auth/types.js +2 -0
- package/dist/src/channel/manager.mjs +5 -5
- package/dist/src/cli/handlers/auth.js +125 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/resource-versions.js +2 -0
- package/dist/src/cli/handlers/sessions.js +23 -5
- package/dist/src/cli/handlers/subcommands.js +10 -1
- package/dist/src/cli/handlers/use.js +342 -43
- package/dist/src/config/gitignore.js +1 -0
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/memory/canonical-context.js +342 -0
- package/dist/src/memory/context-pack.js +282 -0
- package/dist/src/memory/index.js +4 -0
- package/dist/src/memory/intake.js +118 -0
- package/dist/src/resources/resolver.js +1 -0
- package/dist/src/resources/web-release.d.ts +3 -1
- package/dist/src/resources/web-release.js +14 -6
- package/dist/src/serve/agentic-sandbox-fleet-client.js +213 -0
- package/dist/src/serve/fleet-mission-conductor.js +293 -0
- package/dist/src/sessions/index.js +1 -0
- package/dist/src/sessions/output-registration.js +338 -0
- package/dist/src/sessions/promotion.js +73 -2
- package/dist/src/sessions/repository.js +2 -1
- package/dist/src/update/notifier.mjs +13 -2
- package/package.json +8 -1
- package/tools/_resolve-impl.mjs +74 -0
- package/tools/agents/deploy-agents.mjs +962 -0
- package/tools/agents/providers/base.mjs +2954 -0
- package/tools/agents/providers/claude.mjs +711 -0
- package/tools/agents/providers/codex.mjs +699 -0
- package/tools/agents/providers/copilot.mjs +659 -0
- package/tools/agents/providers/cursor.mjs +714 -0
- package/tools/agents/providers/factory.mjs +1130 -0
- package/tools/agents/providers/hermes.mjs +663 -0
- package/tools/agents/providers/hook-capabilities.mjs +85 -0
- package/tools/agents/providers/model-role.mjs +56 -0
- package/tools/agents/providers/openclaw-translator.mjs +348 -0
- package/tools/agents/providers/openclaw.mjs +680 -0
- package/tools/agents/providers/opencode.mjs +675 -0
- package/tools/agents/providers/openhuman.mjs +292 -0
- package/tools/agents/providers/warp.mjs +413 -0
- package/tools/agents/providers/windsurf.mjs +748 -0
- package/tools/commands/deploy-prompts-codex.mjs +336 -0
- package/tools/plugin/package-plugins.mjs +1013 -0
- package/tools/skills/deploy-skills-codex.mjs +571 -0
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Codex Provider
|
|
3
|
+
*
|
|
4
|
+
* Deploys agents and commands for OpenAI Codex CLI. Commands are transformed
|
|
5
|
+
* to prompts format via external script.
|
|
6
|
+
*
|
|
7
|
+
* Deployment paths:
|
|
8
|
+
* - Agents: <project>/.codex/agents/ (project-local)
|
|
9
|
+
* - Commands: ~/.codex/prompts/ (home directory, NOT project)
|
|
10
|
+
* - Skills: <project>/.agents/skills/ (project-local, cross-provider canonical)
|
|
11
|
+
* - Rules: <project>/.codex/rules/ (project-local, conventional)
|
|
12
|
+
*
|
|
13
|
+
* Skill path note (#766 regression fix):
|
|
14
|
+
* Codex (codex-rs/core-skills/src/loader.rs) scans the project-local
|
|
15
|
+
* `.agents/skills/` directory — the industry-standard, cross-provider path
|
|
16
|
+
* shared with OpenClaw, Warp, Copilot, and OpenCode. The legacy home-dir
|
|
17
|
+
* path `~/.codex/skills/` is deprecated. Earlier versions wrote BOTH, and
|
|
18
|
+
* because codex-rs scans both, every kernel skill appeared twice in the
|
|
19
|
+
* slash-command list (e.g. `/aiwg-regenerate` listed twice). We now write
|
|
20
|
+
* `.agents/skills/` only and prune the stale legacy home dir on deploy.
|
|
21
|
+
*
|
|
22
|
+
* Special features:
|
|
23
|
+
* - Model replacement (opus/sonnet/haiku -> gpt-5.4/gpt-5.5/gpt-5.4-mini)
|
|
24
|
+
* - --as-agents-md aggregation option
|
|
25
|
+
* - Delegates commands to deploy-prompts-codex.mjs (deploys to ~/.codex/prompts/)
|
|
26
|
+
* - Delegates skills to deploy-skills-codex.mjs (deploys to .agents/skills/)
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import realFs from 'fs';
|
|
30
|
+
import { createRequire } from 'module';
|
|
31
|
+
const _require = createRequire(import.meta.url);
|
|
32
|
+
let fs;
|
|
33
|
+
try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
|
|
34
|
+
const staticModelCatalog = _require('../../../agentic/code/providers/model-catalog.v1.json');
|
|
35
|
+
import path from 'path';
|
|
36
|
+
import os from 'os';
|
|
37
|
+
import { spawn } from 'child_process';
|
|
38
|
+
import { load as loadYaml } from 'js-yaml';
|
|
39
|
+
import { classifyModelRole, modelForRole } from './model-role.mjs';
|
|
40
|
+
import {
|
|
41
|
+
ensureDir,
|
|
42
|
+
listMdFiles,
|
|
43
|
+
listMdFilesRecursive,
|
|
44
|
+
writeFile,
|
|
45
|
+
deployFiles,
|
|
46
|
+
createAgentsMdFromTemplate,
|
|
47
|
+
initializeFrameworkWorkspace,
|
|
48
|
+
getAddonAgentFiles,
|
|
49
|
+
getAddonCommandFiles,
|
|
50
|
+
getAddonSkillDirs,
|
|
51
|
+
getAddonRuleFiles,
|
|
52
|
+
listSkillDirs,
|
|
53
|
+
loadRuntimeModelCatalog,
|
|
54
|
+
deploySkillDir,
|
|
55
|
+
deploySkillsWithKernelRouting,
|
|
56
|
+
getFrameworksForMode,
|
|
57
|
+
normalizeDeploymentMode,
|
|
58
|
+
getRulesIndexPath,
|
|
59
|
+
cleanupOldRuleFiles,
|
|
60
|
+
filterCommandsAgainstSkills,
|
|
61
|
+
collectFrameworkArtifacts,
|
|
62
|
+
listOnDemandRuleFiles,
|
|
63
|
+
writeOnDemandRuleIndex,
|
|
64
|
+
deploySoulCompanions,
|
|
65
|
+
parseFrontmatter,
|
|
66
|
+
resolveAiwgRoot
|
|
67
|
+
} from './base.mjs';
|
|
68
|
+
const modelCatalog = loadRuntimeModelCatalog(staticModelCatalog);
|
|
69
|
+
|
|
70
|
+
function resolveCodexHelperRoot(srcRoot) {
|
|
71
|
+
const resolved = resolveAiwgRoot(srcRoot);
|
|
72
|
+
if (resolved) return resolved;
|
|
73
|
+
return path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..', '..');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// Provider Configuration
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
export const name = 'codex';
|
|
81
|
+
export const aliases = ['openai'];
|
|
82
|
+
|
|
83
|
+
export const paths = {
|
|
84
|
+
agents: '.codex/agents/',
|
|
85
|
+
commands: '.codex/commands/', // Project-local mirror for conventional deployment
|
|
86
|
+
// Skills sequestered under .codex/.aiwg/skills/ — index-driven discovery (#1212).
|
|
87
|
+
skills: '.codex/.aiwg/skills/',
|
|
88
|
+
rules: '.codex/rules/'
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Kernel skills (always-loaded) deploy to the project-local `.agents/skills/`
|
|
92
|
+
// directory — the cross-provider canonical path codex-rs natively scans. This
|
|
93
|
+
// is project-relative (joined with the deploy target), matching the other
|
|
94
|
+
// providers' kernel paths. The legacy home-dir path `~/.codex/skills/` is
|
|
95
|
+
// deprecated and pruned on deploy (#766 regression fix). The standard tier
|
|
96
|
+
// (when `--copy-all` is passed) lands alongside kernel skills; the
|
|
97
|
+
// deploy-skills-codex.mjs script filters non-kernel skills out by default (#1217).
|
|
98
|
+
export const kernelSkillsPath = '.agents/skills/';
|
|
99
|
+
|
|
100
|
+
// Legacy home-dir skills location written by AIWG versions prior to the #766
|
|
101
|
+
// regression fix. Pruned on every codex skill deploy so codex-rs stops listing
|
|
102
|
+
// each AIWG skill twice. Never touches non-AIWG (unmarked) skills.
|
|
103
|
+
const legacyHomeSkillsDir = path.join(os.homedir(), '.codex', 'skills');
|
|
104
|
+
|
|
105
|
+
export const support = {
|
|
106
|
+
agents: 'native',
|
|
107
|
+
commands: 'native',
|
|
108
|
+
skills: 'native',
|
|
109
|
+
rules: 'conventional'
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const capabilities = {
|
|
113
|
+
skills: true, // But deployed to home dir
|
|
114
|
+
rules: true,
|
|
115
|
+
aggregatedOutput: true, // --as-agents-md
|
|
116
|
+
yamlFormat: false
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// Model Mapping
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Map model shorthand to OpenAI/GPT format
|
|
125
|
+
*/
|
|
126
|
+
export function mapModel(originalModel, modelCfg, modelsConfig) {
|
|
127
|
+
const gptModels = {
|
|
128
|
+
'opus': modelCatalog.providers.codex.roles.reasoning.id,
|
|
129
|
+
'sonnet': modelCatalog.providers.codex.roles.coding.id,
|
|
130
|
+
'haiku': modelCatalog.providers.codex.roles.efficiency.id
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Handle override models first
|
|
134
|
+
if (modelCfg.reasoningModel || modelCfg.codingModel || modelCfg.efficiencyModel) {
|
|
135
|
+
const mapped = modelForRole(originalModel, {
|
|
136
|
+
reasoning: modelCfg.reasoningModel || gptModels.opus,
|
|
137
|
+
coding: modelCfg.codingModel || gptModels.sonnet,
|
|
138
|
+
efficiency: modelCfg.efficiencyModel || gptModels.haiku,
|
|
139
|
+
}, { defaultRole: 'coding' });
|
|
140
|
+
return mapped ?? originalModel;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return modelForRole(originalModel, {
|
|
144
|
+
reasoning: gptModels.opus,
|
|
145
|
+
coding: gptModels.sonnet,
|
|
146
|
+
efficiency: gptModels.haiku,
|
|
147
|
+
}, { defaultRole: 'coding' }) ?? originalModel;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function cleanYamlScalar(value) {
|
|
151
|
+
return String(value || '').trim().replace(/^['"]|['"]$/g, '');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function tomlString(value) {
|
|
155
|
+
return JSON.stringify(String(value));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Render a standalone Codex custom-agent TOML file.
|
|
160
|
+
*
|
|
161
|
+
* Required fields follow the current Codex custom-agent contract:
|
|
162
|
+
* name, description, and developer_instructions. Model controls are native
|
|
163
|
+
* config.toml keys and inherit only when omitted.
|
|
164
|
+
*
|
|
165
|
+
* @implements #1802
|
|
166
|
+
*/
|
|
167
|
+
export function renderAgentToml(srcPath, content, models) {
|
|
168
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
169
|
+
if (!frontmatter) {
|
|
170
|
+
throw new Error(`Codex agent ${srcPath} is missing YAML frontmatter`);
|
|
171
|
+
}
|
|
172
|
+
const metadata = loadYaml(frontmatter) || {};
|
|
173
|
+
|
|
174
|
+
const name = cleanYamlScalar(metadata.name) || path.basename(srcPath, '.md');
|
|
175
|
+
const description = cleanYamlScalar(metadata.description);
|
|
176
|
+
const instructions = body.trim();
|
|
177
|
+
if (!description) throw new Error(`Codex agent ${srcPath} is missing description`);
|
|
178
|
+
if (!instructions) throw new Error(`Codex agent ${srcPath} has no developer instructions`);
|
|
179
|
+
|
|
180
|
+
const role = classifyModelRole(metadata.model, { defaultRole: 'coding' });
|
|
181
|
+
const model = role === 'unknown' ? cleanYamlScalar(metadata.model) : models[role];
|
|
182
|
+
const effortMatch = frontmatter.match(/^model-effort:\s*([^\n]+)$/m);
|
|
183
|
+
const effort = effortMatch
|
|
184
|
+
? cleanYamlScalar(effortMatch[1])
|
|
185
|
+
: { reasoning: 'high', coding: 'medium', efficiency: 'low' }[role];
|
|
186
|
+
|
|
187
|
+
const lines = [
|
|
188
|
+
`name = ${tomlString(name)}`,
|
|
189
|
+
`description = ${tomlString(description)}`,
|
|
190
|
+
`developer_instructions = ${tomlString(instructions)}`,
|
|
191
|
+
];
|
|
192
|
+
if (model) lines.push(`model = ${tomlString(model)}`);
|
|
193
|
+
if (effort) lines.push(`model_reasoning_effort = ${tomlString(effort)}`);
|
|
194
|
+
return `${lines.join('\n')}\n`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// Content Transformation
|
|
199
|
+
// ============================================================================
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Transform agent content for Codex
|
|
203
|
+
*/
|
|
204
|
+
export function transformAgent(srcPath, content, opts) {
|
|
205
|
+
const { reasoningModel, codingModel, efficiencyModel } = opts;
|
|
206
|
+
const catalogModels = modelCatalog.providers.codex.roles;
|
|
207
|
+
|
|
208
|
+
const models = {
|
|
209
|
+
reasoning: reasoningModel || catalogModels.reasoning.id,
|
|
210
|
+
coding: codingModel || catalogModels.coding.id,
|
|
211
|
+
efficiency: efficiencyModel || catalogModels.efficiency.id
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
return renderAgentToml(srcPath, content, models);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Transform command content for Codex
|
|
219
|
+
*/
|
|
220
|
+
export function transformCommand(srcPath, content, opts) {
|
|
221
|
+
return content;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ============================================================================
|
|
225
|
+
// Deployment Functions
|
|
226
|
+
// ============================================================================
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Deploy agents to .codex/agents/
|
|
230
|
+
*/
|
|
231
|
+
export function deployAgents(agentFiles, targetDir, opts) {
|
|
232
|
+
const destDir = path.join(targetDir, paths.agents);
|
|
233
|
+
ensureDir(destDir, opts.dryRun);
|
|
234
|
+
return deployFiles(agentFiles, destDir, {
|
|
235
|
+
...opts,
|
|
236
|
+
fileExtension: '.toml',
|
|
237
|
+
injectPlatform: false,
|
|
238
|
+
}, transformAgent);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Deploy commands via external script
|
|
243
|
+
*
|
|
244
|
+
* NOTE: Codex prompts/commands go to ~/.codex/prompts/ (home directory)
|
|
245
|
+
* not to the project directory. We do NOT pass --target to let the
|
|
246
|
+
* script use its default home directory location.
|
|
247
|
+
*/
|
|
248
|
+
export async function deployCommands(targetDir, srcRoot, opts) {
|
|
249
|
+
const helperRoot = resolveCodexHelperRoot(srcRoot);
|
|
250
|
+
const scriptPath = path.join(helperRoot, 'tools', 'commands', 'deploy-prompts-codex.mjs');
|
|
251
|
+
|
|
252
|
+
if (!fs.existsSync(scriptPath)) {
|
|
253
|
+
console.warn(`Codex prompts deployment script not found at ${scriptPath}`);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
console.log('Delegating command deployment to deploy-prompts-codex.mjs (~/.codex/prompts/)...');
|
|
258
|
+
|
|
259
|
+
return new Promise((resolve, reject) => {
|
|
260
|
+
// NOTE: Do NOT pass --target - Codex prompts belong in ~/.codex/prompts/ (home)
|
|
261
|
+
const args = ['--source', srcRoot];
|
|
262
|
+
if (opts.dryRun) args.push('--dry-run');
|
|
263
|
+
if (opts.force) args.push('--force');
|
|
264
|
+
if (opts.mode) args.push('--mode', opts.mode);
|
|
265
|
+
if (opts.copyStandardSkills === true) args.push('--copy-all');
|
|
266
|
+
|
|
267
|
+
const child = spawn('node', [scriptPath, ...args], {
|
|
268
|
+
stdio: 'inherit',
|
|
269
|
+
cwd: helperRoot
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
child.on('close', (code) => {
|
|
273
|
+
if (code === 0) resolve();
|
|
274
|
+
else reject(new Error(`deploy-prompts-codex.mjs exited with code ${code}`));
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
child.on('error', reject);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Deploy skills via external script
|
|
283
|
+
*/
|
|
284
|
+
export async function deploySkills(targetDir, srcRoot, opts) {
|
|
285
|
+
const helperRoot = resolveCodexHelperRoot(srcRoot);
|
|
286
|
+
const scriptPath = path.join(helperRoot, 'tools', 'skills', 'deploy-skills-codex.mjs');
|
|
287
|
+
|
|
288
|
+
if (!fs.existsSync(scriptPath)) {
|
|
289
|
+
console.warn(`Codex skills deployment script not found at ${scriptPath}`);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
console.log('Delegating skill deployment to deploy-skills-codex.mjs...');
|
|
294
|
+
|
|
295
|
+
// Deploy to the project-local .agents/skills/ — the SINGLE codex-scanned
|
|
296
|
+
// target (industry-standard cross-provider path). Writing only here avoids
|
|
297
|
+
// the duplicate slash-command bug that occurred when skills were ALSO
|
|
298
|
+
// written to the legacy ~/.codex/skills/ home dir: codex-rs scans both, so
|
|
299
|
+
// every kernel skill was listed twice (e.g. `/aiwg-regenerate`). See #766.
|
|
300
|
+
const crossAgentSkillsDir = path.join(targetDir, '.agents', 'skills');
|
|
301
|
+
console.log(`Deploying skills to ${crossAgentSkillsDir} (.agents/skills — codex-scanned path)...`);
|
|
302
|
+
|
|
303
|
+
await new Promise((resolve, reject) => {
|
|
304
|
+
const args = ['--source', srcRoot, '--target', crossAgentSkillsDir];
|
|
305
|
+
if (opts.dryRun) args.push('--dry-run');
|
|
306
|
+
if (opts.force) args.push('--force');
|
|
307
|
+
if (opts.mode) args.push('--mode', opts.mode);
|
|
308
|
+
if (opts.copyStandardSkills === true) args.push('--copy-all');
|
|
309
|
+
|
|
310
|
+
const child = spawn('node', [scriptPath, ...args], {
|
|
311
|
+
stdio: 'inherit',
|
|
312
|
+
cwd: helperRoot
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
child.on('close', (code) => {
|
|
316
|
+
if (code === 0) resolve();
|
|
317
|
+
else reject(new Error(`deploy-skills-codex.mjs exited with code ${code}`));
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
child.on('error', reject);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Self-heal: prune AIWG-managed skill dirs left behind in the legacy
|
|
324
|
+
// ~/.codex/skills/ home location by pre-fix versions, so codex-rs stops
|
|
325
|
+
// listing each skill twice.
|
|
326
|
+
pruneLegacyCodexSkills(opts);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Remove AIWG-managed skill directories from the legacy ~/.codex/skills/ home
|
|
331
|
+
* location. Earlier AIWG versions deployed kernel skills there in addition to
|
|
332
|
+
* .agents/skills/; since codex-rs scans both, this produced duplicate
|
|
333
|
+
* slash-command entries (#766 half-fix regression). Only directories carrying
|
|
334
|
+
* the `.aiwg-managed` marker are removed — user-authored skills are never
|
|
335
|
+
* touched. The now-empty legacy dir is removed if AIWG owned everything in it.
|
|
336
|
+
*/
|
|
337
|
+
export function pruneLegacyCodexSkills(opts = {}, legacyDir = legacyHomeSkillsDir) {
|
|
338
|
+
let entries;
|
|
339
|
+
try {
|
|
340
|
+
entries = fs.readdirSync(legacyDir, { withFileTypes: true });
|
|
341
|
+
} catch {
|
|
342
|
+
return 0; // legacy dir absent — nothing to prune
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
let pruned = 0;
|
|
346
|
+
for (const ent of entries) {
|
|
347
|
+
if (!ent.isDirectory()) continue;
|
|
348
|
+
const skillDir = path.join(legacyDir, ent.name);
|
|
349
|
+
// `aiwg-mcp` was deployed before marker files existed and its malformed
|
|
350
|
+
// pre-fix SKILL.md is rejected by Codex before AIWG can self-heal. The
|
|
351
|
+
// exact retired name is safe to claim; all other unmarked skills remain
|
|
352
|
+
// user-owned.
|
|
353
|
+
const isKnownPreMarkerLegacySkill = ent.name === 'aiwg-mcp';
|
|
354
|
+
if (
|
|
355
|
+
!isKnownPreMarkerLegacySkill &&
|
|
356
|
+
!fs.existsSync(path.join(skillDir, '.aiwg-managed'))
|
|
357
|
+
) continue; // leave user skills alone
|
|
358
|
+
if (opts.dryRun) {
|
|
359
|
+
console.log(`[dry-run] would prune legacy AIWG skill ${skillDir}`);
|
|
360
|
+
} else {
|
|
361
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
362
|
+
}
|
|
363
|
+
pruned++;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (pruned > 0) {
|
|
367
|
+
console.log(`Pruned ${pruned} AIWG-managed skill${pruned === 1 ? '' : 's'} from legacy ~/.codex/skills/ (now deployed to .agents/skills/).`);
|
|
368
|
+
if (!opts.dryRun) {
|
|
369
|
+
// Remove the legacy dir only if AIWG owned everything in it.
|
|
370
|
+
try {
|
|
371
|
+
if (fs.readdirSync(legacyDir).length === 0) fs.rmdirSync(legacyDir);
|
|
372
|
+
} catch { /* non-empty (user skills remain) — keep it */ }
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return pruned;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Deploy rules to .codex/rules/
|
|
380
|
+
*/
|
|
381
|
+
export function deployRules(ruleFiles, targetDir, opts) {
|
|
382
|
+
const destDir = path.join(targetDir, paths.rules);
|
|
383
|
+
ensureDir(destDir, opts.dryRun);
|
|
384
|
+
cleanupOldRuleFiles(destDir, opts);
|
|
385
|
+
return deployFiles(ruleFiles, destDir, opts, transformCommand);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Aggregate agents to single AGENTS.md file
|
|
390
|
+
*/
|
|
391
|
+
export function aggregateToAgentsMd(agentFiles, destPath, opts) {
|
|
392
|
+
const blocks = [];
|
|
393
|
+
for (const f of agentFiles) {
|
|
394
|
+
let content = fs.readFileSync(f, 'utf8');
|
|
395
|
+
content = transformAgent(f, content, opts);
|
|
396
|
+
if (!content.endsWith('\n')) content += '\n';
|
|
397
|
+
blocks.push(content);
|
|
398
|
+
}
|
|
399
|
+
const out = blocks.join('\n');
|
|
400
|
+
if (opts.dryRun) console.log(`[dry-run] write ${destPath}`);
|
|
401
|
+
else fs.writeFileSync(destPath, out, 'utf8');
|
|
402
|
+
console.log(`wrote ${path.relative(process.cwd(), destPath)} with ${agentFiles.length} agents`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ============================================================================
|
|
406
|
+
// AGENTS.md
|
|
407
|
+
// ============================================================================
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Create/update AGENTS.md from Codex template
|
|
411
|
+
*/
|
|
412
|
+
export function createAgentsMd(target, srcRoot, dryRun) {
|
|
413
|
+
createAgentsMdFromTemplate(target, srcRoot, 'codex/AGENTS.md.aiwg-template', dryRun);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// ============================================================================
|
|
417
|
+
// Plugin Bundle Generator
|
|
418
|
+
// ============================================================================
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Generate a Codex plugin bundle for AIWG SDLC.
|
|
422
|
+
*
|
|
423
|
+
* Creates:
|
|
424
|
+
* <targetDir>/agentic/code/plugins/sdlc/.codex-plugin/plugin.json — Codex plugin manifest
|
|
425
|
+
* <targetDir>/.agents/plugins/marketplace.json — Repo marketplace entry
|
|
426
|
+
*
|
|
427
|
+
* @param {string} targetDir - Root directory where bundle is written
|
|
428
|
+
* @param {{ dryRun?: boolean, srcRoot?: string, version?: string }} opts
|
|
429
|
+
*/
|
|
430
|
+
export function generatePluginBundle(targetDir, opts = {}) {
|
|
431
|
+
const { dryRun = false, srcRoot = process.cwd(), version: overrideVersion } = opts;
|
|
432
|
+
|
|
433
|
+
// Resolve version: opts.version > package.json > 'unknown'
|
|
434
|
+
let version = overrideVersion;
|
|
435
|
+
if (!version) {
|
|
436
|
+
try {
|
|
437
|
+
const pkgPath = path.join(srcRoot, 'package.json');
|
|
438
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
439
|
+
// Strip pre-release suffix so it stays CalVer-compliant
|
|
440
|
+
version = (pkg.version || 'unknown').replace(/-.*$/, '');
|
|
441
|
+
} catch {
|
|
442
|
+
version = 'unknown';
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// ---- plugin.json --------------------------------------------------------
|
|
447
|
+
const pluginManifest = {
|
|
448
|
+
name: 'aiwg-sdlc',
|
|
449
|
+
version,
|
|
450
|
+
description:
|
|
451
|
+
'Complete Software Development Lifecycle framework with 180+ specialized agents for requirements, architecture, security, testing, and deployment.',
|
|
452
|
+
author: 'AIWG',
|
|
453
|
+
homepage: 'https://aiwg.io',
|
|
454
|
+
repository: 'https://github.com/jmagly/aiwg',
|
|
455
|
+
license: 'MIT',
|
|
456
|
+
skills: './skills/',
|
|
457
|
+
keywords: ['sdlc', 'aiwg', 'agents', 'architecture', 'security', 'testing', 'deployment']
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const pluginJsonDir = path.join(targetDir, 'agentic', 'code', 'plugins', 'sdlc', '.codex-plugin');
|
|
461
|
+
const pluginJsonPath = path.join(pluginJsonDir, 'plugin.json');
|
|
462
|
+
|
|
463
|
+
if (dryRun) {
|
|
464
|
+
console.log(`[dry-run] would write ${pluginJsonPath}`);
|
|
465
|
+
} else {
|
|
466
|
+
fs.mkdirSync(pluginJsonDir, { recursive: true });
|
|
467
|
+
fs.writeFileSync(pluginJsonPath, JSON.stringify(pluginManifest, null, 2) + '\n', 'utf8');
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// ---- marketplace.json ---------------------------------------------------
|
|
471
|
+
const marketplace = {
|
|
472
|
+
name: 'aiwg-local',
|
|
473
|
+
interface: {
|
|
474
|
+
displayName: 'AIWG Plugins'
|
|
475
|
+
},
|
|
476
|
+
plugins: [
|
|
477
|
+
{
|
|
478
|
+
name: 'aiwg-sdlc',
|
|
479
|
+
source: {
|
|
480
|
+
path: './agentic/code/plugins/sdlc',
|
|
481
|
+
source: 'local'
|
|
482
|
+
},
|
|
483
|
+
policy: {
|
|
484
|
+
installation: 'AVAILABLE'
|
|
485
|
+
},
|
|
486
|
+
category: 'Development'
|
|
487
|
+
}
|
|
488
|
+
]
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const marketplaceDir = path.join(targetDir, '.agents', 'plugins');
|
|
492
|
+
const marketplacePath = path.join(marketplaceDir, 'marketplace.json');
|
|
493
|
+
|
|
494
|
+
if (dryRun) {
|
|
495
|
+
console.log(`[dry-run] would write ${marketplacePath}`);
|
|
496
|
+
} else {
|
|
497
|
+
fs.mkdirSync(marketplaceDir, { recursive: true });
|
|
498
|
+
fs.writeFileSync(marketplacePath, JSON.stringify(marketplace, null, 2) + '\n', 'utf8');
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// ============================================================================
|
|
503
|
+
// Post-Deployment
|
|
504
|
+
// ============================================================================
|
|
505
|
+
|
|
506
|
+
export async function postDeploy(targetDir, opts) {
|
|
507
|
+
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
508
|
+
|
|
509
|
+
if (opts.createAgentsMd) {
|
|
510
|
+
createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// ============================================================================
|
|
515
|
+
// File Extension
|
|
516
|
+
// ============================================================================
|
|
517
|
+
|
|
518
|
+
export function getFileExtension(type) {
|
|
519
|
+
return '.md';
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// ============================================================================
|
|
523
|
+
// Main Deploy Function
|
|
524
|
+
// ============================================================================
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Main deployment function for Codex provider
|
|
528
|
+
*/
|
|
529
|
+
export async function deploy(opts) {
|
|
530
|
+
const {
|
|
531
|
+
srcRoot,
|
|
532
|
+
target,
|
|
533
|
+
mode,
|
|
534
|
+
deployCommands: shouldDeployCommands,
|
|
535
|
+
deploySkills: shouldDeploySkills,
|
|
536
|
+
deployRules: shouldDeployRules,
|
|
537
|
+
commandsOnly,
|
|
538
|
+
skillsOnly,
|
|
539
|
+
rulesOnly,
|
|
540
|
+
dryRun,
|
|
541
|
+
asAgentsMd,
|
|
542
|
+
asPlugin,
|
|
543
|
+
createAgentsMd: shouldCreateAgentsMd
|
|
544
|
+
} = opts;
|
|
545
|
+
|
|
546
|
+
console.log(`\n=== OpenAI Codex Provider ===`);
|
|
547
|
+
console.log(`Target: ${target}`);
|
|
548
|
+
console.log(`Mode: ${mode}`);
|
|
549
|
+
|
|
550
|
+
// Collect source files based on mode
|
|
551
|
+
const agentFiles = [];
|
|
552
|
+
const ruleFiles = [];
|
|
553
|
+
const normalizedMode = normalizeDeploymentMode(mode);
|
|
554
|
+
|
|
555
|
+
// Check for addon-style directory structure (direct agents/ and rules/
|
|
556
|
+
// subdirs). Handles deployment when --source points at a project-local
|
|
557
|
+
// bundle (.aiwg/extensions/<name>/) rather than $AIWG_ROOT. Mirrors the
|
|
558
|
+
// reference implementation in claude.mjs (#124). Commands and skills are
|
|
559
|
+
// resolved from srcRoot inside deployCommands/deploySkills, so only agents
|
|
560
|
+
// and rules need the explicit short-circuit here.
|
|
561
|
+
const isAddonSource = fs.existsSync(path.join(srcRoot, 'agents')) ||
|
|
562
|
+
fs.existsSync(path.join(srcRoot, 'commands')) ||
|
|
563
|
+
fs.existsSync(path.join(srcRoot, 'skills')) ||
|
|
564
|
+
fs.existsSync(path.join(srcRoot, 'rules'));
|
|
565
|
+
|
|
566
|
+
if (isAddonSource) {
|
|
567
|
+
const addonAgentsDir = path.join(srcRoot, 'agents');
|
|
568
|
+
if (fs.existsSync(addonAgentsDir)) {
|
|
569
|
+
agentFiles.push(...listMdFiles(addonAgentsDir));
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (shouldDeployRules || rulesOnly) {
|
|
573
|
+
const addonRulesDir = path.join(srcRoot, 'rules');
|
|
574
|
+
if (fs.existsSync(addonRulesDir)) {
|
|
575
|
+
ruleFiles.push(...listMdFiles(addonRulesDir));
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Frameworks discovered from manifests/directory structure
|
|
581
|
+
const frameworks = getFrameworksForMode(srcRoot, normalizedMode);
|
|
582
|
+
for (const framework of frameworks) {
|
|
583
|
+
if (framework.components.agents.exists) {
|
|
584
|
+
agentFiles.push(...listMdFiles(framework.components.agents.path));
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (framework.id === 'sdlc-complete' && framework.components.rules.exists) {
|
|
588
|
+
// Use consolidated RULES-INDEX.md for SDLC rules when available.
|
|
589
|
+
const indexPath = getRulesIndexPath(srcRoot);
|
|
590
|
+
if (indexPath) {
|
|
591
|
+
ruleFiles.push(indexPath);
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (framework.components.rules.exists) {
|
|
597
|
+
ruleFiles.push(...listMdFiles(framework.components.rules.path));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// All addons (dynamically discovered)
|
|
602
|
+
if (normalizedMode === 'general' || normalizedMode === 'sdlc' || normalizedMode === 'both' || normalizedMode === 'all') {
|
|
603
|
+
agentFiles.push(...getAddonAgentFiles(srcRoot));
|
|
604
|
+
ruleFiles.push(...getAddonRuleFiles(srcRoot));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Collect soul companion files
|
|
608
|
+
const soulArtifacts = collectFrameworkArtifacts(srcRoot, normalizedMode, {
|
|
609
|
+
includeAgents: false,
|
|
610
|
+
includeCommands: false,
|
|
611
|
+
includeSkills: false,
|
|
612
|
+
includeRules: false
|
|
613
|
+
});
|
|
614
|
+
const soulFiles = [...(soulArtifacts.souls || [])];
|
|
615
|
+
|
|
616
|
+
// Deploy based on flags
|
|
617
|
+
if (!commandsOnly && !skillsOnly && !rulesOnly) {
|
|
618
|
+
if (asAgentsMd) {
|
|
619
|
+
// Aggregate to single AGENTS.md
|
|
620
|
+
const destPath = path.join(target, 'AGENTS.md');
|
|
621
|
+
console.log(`\nAggregating ${agentFiles.length} agents to AGENTS.md...`);
|
|
622
|
+
aggregateToAgentsMd(agentFiles, destPath, opts);
|
|
623
|
+
} else {
|
|
624
|
+
console.log(`\nDeploying ${agentFiles.length} agents...`);
|
|
625
|
+
deployAgents(agentFiles, target, opts);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Deploy soul companion files alongside agents
|
|
629
|
+
if (soulFiles.length > 0) {
|
|
630
|
+
const destDir = path.join(target, paths.agents);
|
|
631
|
+
ensureDir(destDir, opts.dryRun);
|
|
632
|
+
console.log(`\nDeploying ${soulFiles.length} soul files...`);
|
|
633
|
+
deploySoulCompanions(soulFiles, destDir, opts);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (shouldDeployCommands || commandsOnly) {
|
|
638
|
+
console.log(`\nDeploying commands...`);
|
|
639
|
+
await deployCommands(target, srcRoot, opts);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (shouldDeploySkills || skillsOnly) {
|
|
643
|
+
console.log(`\nDeploying skills to .agents/skills/...`);
|
|
644
|
+
await deploySkills(target, srcRoot, opts);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (shouldDeployRules || rulesOnly) {
|
|
648
|
+
console.log(`\nDeploying ${ruleFiles.length} rules...`);
|
|
649
|
+
deployRules(ruleFiles, target, opts);
|
|
650
|
+
|
|
651
|
+
// On-demand index (#1675): list the MEDIUM/LOW rules tier-gated out of the
|
|
652
|
+
// always-on set so agents can fetch them via `aiwg show rule`.
|
|
653
|
+
const onDemandCount = writeOnDemandRuleIndex(
|
|
654
|
+
path.join(target, paths.rules),
|
|
655
|
+
listOnDemandRuleFiles(srcRoot),
|
|
656
|
+
opts,
|
|
657
|
+
);
|
|
658
|
+
if (onDemandCount > 0) {
|
|
659
|
+
console.log(` On-demand rules (not inlined): ${onDemandCount} → RULES-ONDEMAND.md`);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Post-deployment
|
|
664
|
+
await postDeploy(target, { ...opts, createAgentsMd: shouldCreateAgentsMd });
|
|
665
|
+
|
|
666
|
+
// Plugin bundle (opt-in via --as-plugin)
|
|
667
|
+
if (asPlugin) {
|
|
668
|
+
console.log('\nGenerating Codex plugin bundle...');
|
|
669
|
+
generatePluginBundle(target, { dryRun, srcRoot });
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
console.log('\n=== Codex deployment complete ===\n');
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// ============================================================================
|
|
676
|
+
// Default Export
|
|
677
|
+
// ============================================================================
|
|
678
|
+
|
|
679
|
+
export default {
|
|
680
|
+
name,
|
|
681
|
+
aliases,
|
|
682
|
+
paths,
|
|
683
|
+
kernelSkillsPath,
|
|
684
|
+
support,
|
|
685
|
+
capabilities,
|
|
686
|
+
transformAgent,
|
|
687
|
+
transformCommand,
|
|
688
|
+
mapModel,
|
|
689
|
+
deployAgents,
|
|
690
|
+
deployCommands,
|
|
691
|
+
deploySkills,
|
|
692
|
+
deployRules,
|
|
693
|
+
aggregateToAgentsMd,
|
|
694
|
+
createAgentsMd,
|
|
695
|
+
postDeploy,
|
|
696
|
+
getFileExtension,
|
|
697
|
+
generatePluginBundle,
|
|
698
|
+
deploy
|
|
699
|
+
};
|