@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.
Files changed (61) hide show
  1. package/README.md +33 -0
  2. package/agentic/code/providers/capability-matrix.yaml +511 -0
  3. package/agentic/code/providers/model-capabilities.v1.json +120 -0
  4. package/agentic/code/providers/model-catalog.v1.json +96 -0
  5. package/agentic/code/providers/model-policy-evaluations.v1.json +50 -0
  6. package/agentic/code/providers/premium-model-allowlist.v1.json +36 -0
  7. package/bin/aiwg.mjs +14 -10
  8. package/dist/src/api/index.d.ts +1 -0
  9. package/dist/src/api/index.js +1 -0
  10. package/dist/src/artifacts/cli.js +2 -0
  11. package/dist/src/artifacts/types.js +4 -0
  12. package/dist/src/auth/client.js +209 -0
  13. package/dist/src/auth/config.js +38 -0
  14. package/dist/src/auth/credential-store.js +141 -0
  15. package/dist/src/auth/resource-credentials.js +25 -0
  16. package/dist/src/auth/types.js +2 -0
  17. package/dist/src/channel/manager.mjs +5 -5
  18. package/dist/src/cli/handlers/auth.js +125 -0
  19. package/dist/src/cli/handlers/help.js +1 -0
  20. package/dist/src/cli/handlers/index.js +3 -1
  21. package/dist/src/cli/handlers/resource-versions.js +2 -0
  22. package/dist/src/cli/handlers/sessions.js +23 -5
  23. package/dist/src/cli/handlers/subcommands.js +10 -1
  24. package/dist/src/cli/handlers/use.js +342 -43
  25. package/dist/src/config/gitignore.js +1 -0
  26. package/dist/src/extensions/commands/definitions.js +19 -0
  27. package/dist/src/memory/canonical-context.js +342 -0
  28. package/dist/src/memory/context-pack.js +282 -0
  29. package/dist/src/memory/index.js +4 -0
  30. package/dist/src/memory/intake.js +118 -0
  31. package/dist/src/resources/resolver.js +1 -0
  32. package/dist/src/resources/web-release.d.ts +3 -1
  33. package/dist/src/resources/web-release.js +14 -6
  34. package/dist/src/serve/agentic-sandbox-fleet-client.js +213 -0
  35. package/dist/src/serve/fleet-mission-conductor.js +293 -0
  36. package/dist/src/sessions/index.js +1 -0
  37. package/dist/src/sessions/output-registration.js +338 -0
  38. package/dist/src/sessions/promotion.js +73 -2
  39. package/dist/src/sessions/repository.js +2 -1
  40. package/dist/src/update/notifier.mjs +13 -2
  41. package/package.json +8 -1
  42. package/tools/_resolve-impl.mjs +74 -0
  43. package/tools/agents/deploy-agents.mjs +962 -0
  44. package/tools/agents/providers/base.mjs +2954 -0
  45. package/tools/agents/providers/claude.mjs +711 -0
  46. package/tools/agents/providers/codex.mjs +699 -0
  47. package/tools/agents/providers/copilot.mjs +659 -0
  48. package/tools/agents/providers/cursor.mjs +714 -0
  49. package/tools/agents/providers/factory.mjs +1130 -0
  50. package/tools/agents/providers/hermes.mjs +663 -0
  51. package/tools/agents/providers/hook-capabilities.mjs +85 -0
  52. package/tools/agents/providers/model-role.mjs +56 -0
  53. package/tools/agents/providers/openclaw-translator.mjs +348 -0
  54. package/tools/agents/providers/openclaw.mjs +680 -0
  55. package/tools/agents/providers/opencode.mjs +675 -0
  56. package/tools/agents/providers/openhuman.mjs +292 -0
  57. package/tools/agents/providers/warp.mjs +413 -0
  58. package/tools/agents/providers/windsurf.mjs +748 -0
  59. package/tools/commands/deploy-prompts-codex.mjs +336 -0
  60. package/tools/plugin/package-plugins.mjs +1013 -0
  61. 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
+ };