auriga-cli 1.10.3 → 1.11.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 CHANGED
@@ -11,9 +11,9 @@ This repo itself is a fully configured harness project. You can clone it to see
11
11
  | Module | Description |
12
12
  |---|---|
13
13
  | **Workflow** | `CLAUDE.md` auriga workflow: requirement clarification -> TDD -> Review, Harness principles, Subagent usage guide |
14
- | **Skills** | Development process + orchestration skills — brainstorming, systematic-debugging, TDD, verification, planning, playwright, deep-review, test-designer, parallel-implementation |
14
+ | **Skills** | Development process + orchestration skills — brainstorming, systematic-debugging, TDD, verification, planning, playwright, test-designer, parallel-implementation |
15
15
  | **Recommended Skills** | Optional utility skills (e.g. `codex-agent`, `claude-code-agent`) you can add on top of the workflow skills |
16
- | **Plugins** | Recommended Claude Code and Codex plugins — skill-creator, claude-md-management, codex, auriga-go, auriga-pr-guards, session-instructions-loader |
16
+ | **Plugins** | Recommended Claude Code and Codex plugins — skill-creator, claude-md-management, codex, auriga-go, auriga-pr-guards, session-instructions-loader, deep-review |
17
17
  | **Hooks** | Claude Code hooks: `notify` (macOS notification, focus-aware sound-only when terminal is frontmost — **opt-in**: not installed by `install --all`, requires `install hooks --hook notify`) |
18
18
 
19
19
  ## Quick Start
@@ -90,7 +90,6 @@ Installs selected skills via `npx skills add`, targeting both Claude Code and Co
90
90
  | verification-before-completion | [obra/superpowers](https://github.com/obra/superpowers) | Pre-completion verification — evidence before assertions |
91
91
  | planning-with-files | [OthmanAdi/planning-with-files](https://github.com/OthmanAdi/planning-with-files) | File-based task planning and progress tracking |
92
92
  | playwright-cli | [microsoft/playwright-cli](https://github.com/microsoft/playwright-cli) | Browser automation and testing |
93
- | deep-review | [Ben2pc/g-claude-code-plugins](https://github.com/Ben2pc/g-claude-code-plugins) | Multi-dimensional PR review orchestrator (required + conditional reviewers + punch list) |
94
93
  | test-designer | [Ben2pc/g-claude-code-plugins](https://github.com/Ben2pc/g-claude-code-plugins) | Independent-Evaluation test designer for TDD red phase |
95
94
  | parallel-implementation | [Ben2pc/g-claude-code-plugins](https://github.com/Ben2pc/g-claude-code-plugins) | Slice planner for parallel multi-subagent code writing |
96
95
 
@@ -126,6 +125,7 @@ npx -y auriga-cli install plugins --agent both --plugin auriga-pr-guards
126
125
  | auriga-go | Claude Code / Codex | Workflow autopilot for the auriga workflow. Reminder-based navigation across the `CLAUDE.md` phases with an Experimental hook-backed `ship` mode. Bundles a skill (description-based NL trigger + `/auriga-go`) plus a plugin-level Stop hook for ship mode. |
127
126
  | auriga-pr-guards | Claude Code / Codex | Two PR-workflow guardrails packaged as a dual-Agent plugin: `pr-create-guard` (PostToolUse for `gh pr create` → fetch the new PR's body via `gh pr view` and inject headings + TODO counts as `additionalContext` so the Agent can self-verify scope / acceptance / risks / TODO) and `pr-ready-guard` (PreToolUse for `gh pr ready` → block on stray planning docs at `findings.md` / `progress.md` / `task_plan.md` / `docs/superpowers/specs/*.md`, unfinalized active specs in `docs/specs/*.md`, or unpushed commits; otherwise inject the body snapshot). Codex currently fails open on the `additionalContext` field for PreToolUse but enforces blocks identically. |
128
127
  | session-instructions-loader | Codex | Codex-only SessionStart plugin that injects ancestor `AGENTS.md` files plus repo-configured extra instruction files. |
128
+ | deep-review | Claude Code / Codex | Multi-dimensional PR review orchestrator from [Ben2pc/g-claude-code-plugins](https://github.com/Ben2pc/g-claude-code-plugins) — dispatches parallel reviewers (spec-conformance, correctness, test-quality, docs-sync, plus conditional robustness/UX/performance/structure/code-quality/skill-plugin-quality) and synthesizes findings into an actionable punch list. Bundles a companion `reviewer-creator` skill for scaffolding project-level custom reviewers under `docs/rules/review/`. Drives the formal-review step in `CLAUDE.md` step 10. |
129
129
 
130
130
  ### Hooks
131
131
 
package/README.zh-CN.md CHANGED
@@ -11,9 +11,9 @@
11
11
  | 模块 | 说明 |
12
12
  |---|---|
13
13
  | **Workflow** | `CLAUDE.md` 里的 auriga 工作流:需求澄清 → TDD → Review,Harness 原则,Subagent 使用指南 |
14
- | **Skills** | 开发流程 + 编排类 skills —— brainstorming、systematic-debugging、TDD、verification、planning、playwright、deep-review、test-designer、parallel-implementation |
14
+ | **Skills** | 开发流程 + 编排类 skills —— brainstorming、systematic-debugging、TDD、verification、planning、playwright、test-designer、parallel-implementation |
15
15
  | **Recommended Skills** | 可选的工具类 skills(如 `codex-agent`、`claude-code-agent`),在 workflow skills 之外按需追加 |
16
- | **Plugins** | 推荐的 Claude Code 和 Codex 插件 —— skill-creator、claude-md-management、codex、auriga-go、auriga-pr-guards、session-instructions-loader |
16
+ | **Plugins** | 推荐的 Claude Code 和 Codex 插件 —— skill-creator、claude-md-management、codex、auriga-go、auriga-pr-guards、session-instructions-loader、deep-review |
17
17
  | **Hooks** | Claude Code hooks:`notify`(macOS 通知,终端在焦点时仅放声不弹横幅 —— **opt-in**:`install --all` 不装,需要 `install hooks --hook notify`) |
18
18
 
19
19
  ## 快速开始
@@ -90,7 +90,6 @@ npx auriga-cli
90
90
  | verification-before-completion | [obra/superpowers](https://github.com/obra/superpowers) | 完成前验证,用证据说话 |
91
91
  | planning-with-files | [OthmanAdi/planning-with-files](https://github.com/OthmanAdi/planning-with-files) | 文件化任务计划与进度跟踪 |
92
92
  | playwright-cli | [microsoft/playwright-cli](https://github.com/microsoft/playwright-cli) | 浏览器自动化与测试 |
93
- | deep-review | [Ben2pc/g-claude-code-plugins](https://github.com/Ben2pc/g-claude-code-plugins) | 多维度 PR review 编排器(必选 + 条件 + punch list 汇总) |
94
93
  | test-designer | [Ben2pc/g-claude-code-plugins](https://github.com/Ben2pc/g-claude-code-plugins) | TDD 红灯阶段的 Independent Evaluation 测试设计器 |
95
94
  | parallel-implementation | [Ben2pc/g-claude-code-plugins](https://github.com/Ben2pc/g-claude-code-plugins) | 多 subagent 并行写代码时的切片计划器 |
96
95
 
@@ -126,6 +125,7 @@ npx -y auriga-cli install plugins --agent both --plugin auriga-pr-guards
126
125
  | auriga-go | Claude Code / Codex | auriga 工作流的自动驾驶:按 `CLAUDE.md` 的 phase 做 reminder-based 导航;包含 Experimental 的 hook-backed `ship` 模式。内置一个 skill(按 description 的自然语言触发 + `/auriga-go` slash command)和一个 plugin 层面的 Stop hook。 |
127
126
  | auriga-pr-guards | Claude Code / Codex | 两个 PR-workflow guardrail:`pr-create-guard`(`gh pr create` 的 PostToolUse —— 通过 `gh pr view` 拉真实 PR body,扫 `^##` / `^###` headings 并统计 `- [ ]` / `- [x]` 注入 `additionalContext`,让 Agent 对照范围 / 验收 / 风险 / 剩余 TODO 四要素)+ `pr-ready-guard`(`gh pr ready` 的 PreToolUse —— 仅按结构信号拦截:游离 `findings.md` / `progress.md` / `task_plan.md` / `docs/superpowers/specs/*.md`、`docs/specs/*.md` 内未结案的活跃 spec、未 push commits;放行时注入 body 快照)。Codex 当前对 PreToolUse 的 `additionalContext` 字段 fail-open(解析但不生效),block 路径两边一致。 |
128
127
  | session-instructions-loader | Codex | Codex-only SessionStart 插件,注入上层目录的 `AGENTS.md` 和仓库配置的额外 instruction 文件。 |
128
+ | deep-review | Claude Code / Codex | 来自 [Ben2pc/g-claude-code-plugins](https://github.com/Ben2pc/g-claude-code-plugins) 的多维度 PR review 编排器 —— 并行派发各维度 reviewer(spec-conformance、correctness、test-quality、docs-sync,以及条件触发的 robustness/UX/performance/structure/code-quality/skill-plugin-quality),汇总成 punch list。同包内打包了 `reviewer-creator` skill,用于在 `docs/rules/review/` 下生成项目级自定义 reviewer。承担 `CLAUDE.md` 第 10 步的正式评审职责。 |
129
129
 
130
130
  ### Hooks
131
131
 
package/dist/catalog.json CHANGED
@@ -1,14 +1,10 @@
1
1
  {
2
- "generatedAt": "2026-05-09T13:57:14.259Z",
2
+ "generatedAt": "2026-05-10T08:17:52.704Z",
3
3
  "workflowSkills": [
4
4
  {
5
5
  "name": "brainstorming",
6
6
  "description": "You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation."
7
7
  },
8
- {
9
- "name": "deep-review",
10
- "description": "Run a formal, multi-dimensional code review of a pull request. Reads the PR diff, classifies change types, dispatches parallel reviewers by dimension (spec-conformance, correctness incl. test quality, docs-sync, plus conditional robustness/UX/performance/structure and code-quality for non-trivial changes), and synthesizes findings into an actionable punch list. Use when the user asks to review a PR, run /deep-review, mark a PR as ready for review, or requests a formal/thorough code review."
11
- },
12
8
  {
13
9
  "name": "parallel-implementation",
14
10
  "description": "Plan how to slice a non-trivial coding task across parallel subagents. Returns a dispatch plan (file assignments, dependencies, output-format contracts) — the main Agent then executes it with the Agent tool + `isolation: \"worktree\"`. Invoke only when work justifies multi-agent overhead: (a) greenfield 0→1 across multiple independent modules, (b) change touches ≥3 modules, or (c) ≥5 files each with >50 lines of diff. Small changes write inline."
@@ -81,6 +77,10 @@
81
77
  "name": "auriga-pr-guards",
82
78
  "description": "(Claude/Codex) PR-create snapshot inject + PR-ready structural block guardrails (Claude Code + Codex)"
83
79
  },
80
+ {
81
+ "name": "deep-review",
82
+ "description": "(Claude/Codex) Multi-dimensional PR review orchestrator. Dispatches parallel reviewers (spec-conformance, correctness, test-quality, docs-sync, robustness, security, ux, performance, structure, code-quality, skill-plugin-quality) and synthesizes findings into an actionable punch list. Supports project-level custom reviewers via docs/rules/review/ and ships a reviewer-creator skill for scaffolding them."
83
+ },
84
84
  {
85
85
  "name": "session-instructions-loader",
86
86
  "description": "(Codex) Injects extra instruction files on session start"
@@ -1,3 +1,4 @@
1
+ import { type MarketplaceRef } from "./marketplace.js";
1
2
  export interface CodexMarketplacePlugin {
2
3
  name: string;
3
4
  source?: {
@@ -13,6 +14,7 @@ export interface CodexInstallPlugin {
13
14
  name: string;
14
15
  description?: string;
15
16
  defaultOn?: boolean;
17
+ marketplace?: MarketplaceRef;
16
18
  }
17
19
  export interface CodexInstallConfig {
18
20
  plugins: CodexInstallPlugin[];
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
+ import { MARKETPLACE_NAME_RE, validateMarketplaceField, } from "./marketplace.js";
2
3
  const PLUGIN_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
3
- const MARKETPLACE_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
4
4
  export function validateCodexMarketplace(raw) {
5
5
  if (!raw || typeof raw !== "object") {
6
6
  throw new Error("Codex marketplace.json: root must be an object");
@@ -44,6 +44,9 @@ export function validateCodexInstallConfig(raw) {
44
44
  if (p.defaultOn !== undefined && typeof p.defaultOn !== "boolean") {
45
45
  throw new Error(`Codex install.json: plugins[${i}].defaultOn must be a boolean`);
46
46
  }
47
+ if (p.marketplace !== undefined) {
48
+ validateMarketplaceField(`Codex install.json: plugins[${i}]`, p.marketplace);
49
+ }
47
50
  }
48
51
  }
49
52
  export function codexLocalPluginPath(plugin) {
package/dist/guide.js CHANGED
@@ -71,11 +71,10 @@ Targeted — single category, picking from the catalog surfaced in Step 2:
71
71
  ${cmd("npx -y auriga-cli install skills --skill brainstorming test-driven-development")}
72
72
  ${cmd("npx -y auriga-cli install plugins --plugin skill-creator codex --scope user")}
73
73
  ${cmd("npx -y auriga-cli install plugins --agent codex --plugin session-instructions-loader")}
74
- ${cmd("npx -y auriga-cli install hooks --hook pr-ready-guard")}
75
74
 
76
- Opt-in hooks: some hooks (e.g. \`notify\`) are NOT in the default set
77
- because they have side effects (OS notifications, platform-gated deps).
78
- Name them explicitly to install:
75
+ Opt-in hooks (\`defaultOn: false\`) are NOT in the default \`install --all\`
76
+ set because they have side effects (OS notifications, platform-gated
77
+ deps). Inspect \`install hooks --help\` for the catalog and install by name:
79
78
  ${cmd("npx -y auriga-cli install hooks --hook notify")}
80
79
 
81
80
  Opt-in recommended skills (cross-model delegation helpers —
@@ -0,0 +1,7 @@
1
+ export declare const MARKETPLACE_NAME_RE: RegExp;
2
+ export declare const MARKETPLACE_SOURCE_RE: RegExp;
3
+ export interface MarketplaceRef {
4
+ name: string;
5
+ source: string;
6
+ }
7
+ export declare function validateMarketplaceField(label: string, raw: unknown): asserts raw is MarketplaceRef;
@@ -0,0 +1,34 @@
1
+ // Shared shape + validators for cross-Agent marketplace references.
2
+ // Used by:
3
+ // - PluginDef.marketplace in src/utils.ts (Claude side, .claude/plugins.json)
4
+ // - CodexInstallPlugin.marketplace in src/codex-plugin-config.ts
5
+ // (Codex side, .agents/plugins/install.json)
6
+ //
7
+ // Both sides interpolate `<source>` into shell commands like
8
+ // `codex plugin marketplace add https://github.com/<source>.git` and
9
+ // `<plugin>@<name>` into TOML keys, so these regexes are the only thing
10
+ // standing between a compromised metadata source and arbitrary command
11
+ // execution. Tighten with care — a regression here is a shell-injection
12
+ // vector. Both metadata files are fetched from raw GitHub at runtime.
13
+ export const MARKETPLACE_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
14
+ // GitHub `owner/repo` shape: exactly one `/`, both segments kebab-case-ish
15
+ // without leading punctuation. Tighter than the prior PLUGIN_SOURCE_RE
16
+ // which permitted multi-slash / `..` / trailing `.git` patterns and would
17
+ // compose into confusing git-layer errors like `https://github.com/a/../b.git`.
18
+ export const MARKETPLACE_SOURCE_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
19
+ // Centralizes the marketplace field shape check so the Claude-side
20
+ // validatePluginsConfig and the Codex-side validateCodexInstallConfig
21
+ // stay in lockstep. `label` is interpolated into the thrown Error so
22
+ // the caller's file context (e.g. `plugins.json: plugins[3]`) survives.
23
+ export function validateMarketplaceField(label, raw) {
24
+ if (!raw || typeof raw !== "object") {
25
+ throw new Error(`${label}.marketplace must be an object`);
26
+ }
27
+ const mp = raw;
28
+ if (typeof mp.name !== "string" || !MARKETPLACE_NAME_RE.test(mp.name)) {
29
+ throw new Error(`${label}.marketplace.name ${JSON.stringify(mp.name)} does not match ${MARKETPLACE_NAME_RE}`);
30
+ }
31
+ if (typeof mp.source !== "string" || !MARKETPLACE_SOURCE_RE.test(mp.source)) {
32
+ throw new Error(`${label}.marketplace.source ${JSON.stringify(mp.source)} does not match ${MARKETPLACE_SOURCE_RE}`);
33
+ }
34
+ }
package/dist/plugins.js CHANGED
@@ -4,16 +4,16 @@ import path from "node:path";
4
4
  import { checkbox, select } from "@inquirer/prompts";
5
5
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
6
6
  import { codexManifestPath, validateCodexInstallConfig, validateCodexMarketplace, } from "./codex-plugin-config.js";
7
+ import { validateMarketplaceField } from "./marketplace.js";
7
8
  import { atomicWriteFile, exec, fetchExtraContent, log, withEsc } from "./utils.js";
8
- // Plugin names, marketplace names/sources, and plugin-package names all
9
- // end up in `claude plugins ...` shell commands via string interpolation.
10
- // .claude/plugins.json is fetched from raw GitHub at runtime, so every
11
- // value must pass a conservative whitelist before composing the command.
12
- // Without this a compromised plugins.json would execute arbitrary
13
- // commands via shell metachar injection.
9
+ // Plugin names and plugin-package names end up in `claude plugins ...`
10
+ // shell commands via string interpolation. .claude/plugins.json is
11
+ // fetched from raw GitHub at runtime, so every value must pass a
12
+ // conservative whitelist before composing the command. Without this a
13
+ // compromised plugins.json would execute arbitrary commands via shell
14
+ // metachar injection. Marketplace shape (name + source) lives in
15
+ // `./marketplace.js` so Claude and Codex sides share one validator.
14
16
  const PLUGIN_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
15
- const PLUGIN_SOURCE_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,255}$/;
16
- const MARKETPLACE_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
17
17
  const PLUGIN_PACKAGE_RE = /^[A-Za-z0-9][A-Za-z0-9._@/-]{0,255}$/;
18
18
  export function validatePluginsConfig(raw) {
19
19
  if (!raw || typeof raw !== "object") {
@@ -35,16 +35,7 @@ export function validatePluginsConfig(raw) {
35
35
  throw new Error(`plugins.json: plugins[${i}].package ${JSON.stringify(plugin.package)} does not match ${PLUGIN_PACKAGE_RE}`);
36
36
  }
37
37
  if (plugin.marketplace !== undefined) {
38
- if (!plugin.marketplace || typeof plugin.marketplace !== "object") {
39
- throw new Error(`plugins.json: plugins[${i}].marketplace must be an object`);
40
- }
41
- const mp = plugin.marketplace;
42
- if (typeof mp.name !== "string" || !MARKETPLACE_NAME_RE.test(mp.name)) {
43
- throw new Error(`plugins.json: plugins[${i}].marketplace.name ${JSON.stringify(mp.name)} does not match ${MARKETPLACE_NAME_RE}`);
44
- }
45
- if (typeof mp.source !== "string" || !PLUGIN_SOURCE_RE.test(mp.source)) {
46
- throw new Error(`plugins.json: plugins[${i}].marketplace.source ${JSON.stringify(mp.source)} does not match ${PLUGIN_SOURCE_RE}`);
47
- }
38
+ validateMarketplaceField(`plugins.json: plugins[${i}]`, plugin.marketplace);
48
39
  }
49
40
  });
50
41
  }
@@ -136,6 +127,13 @@ function codexMarketplaceAddCommand(packageRoot) {
136
127
  }
137
128
  return "codex plugin marketplace add https://github.com/Ben2pc/auriga-cli.git";
138
129
  }
130
+ function codexExternalMarketplaceAddCommand(source) {
131
+ // `source` is validated by validateCodexInstallConfig against
132
+ // MARKETPLACE_SOURCE_RE (alphanumerics + `._/-`) — no shell metachars
133
+ // can reach this string. URL form deliberately mirrors
134
+ // codexMarketplaceAddCommand's hardcoded production branch.
135
+ return `codex plugin marketplace add https://github.com/${source}.git`;
136
+ }
139
137
  function codexMarketplaceUpgradeCommand(marketplaceName) {
140
138
  return `codex plugin marketplace upgrade ${shellQuote(marketplaceName)}`;
141
139
  }
@@ -273,15 +271,66 @@ function enableCodexPluginConfig(configPath, pluginKeys, needsPluginHooks) {
273
271
  const content = minimalContent ?? buildCodexPluginConfigToml(originalContent, configPath, pluginKeys, needsPluginHooks);
274
272
  atomicWriteFile(configPath, content.endsWith("\n") ? content : `${content}\n`);
275
273
  }
276
- async function installCodexPlugins(packageRoot, opts) {
277
- const marketplace = loadCodexMarketplace(packageRoot);
278
- if (!marketplace) {
279
- const msg = "No .agents/plugins/marketplace.json found";
280
- if (!opts.interactive)
281
- throw new Error(msg);
282
- log.warn(msg);
274
+ async function addCodexMarketplaceWithRetry(marketplaceName, addCommand, opts, marketplaceExecOpts, failures) {
275
+ try {
276
+ exec(addCommand, marketplaceExecOpts);
277
+ log.ok(`Codex marketplace ${marketplaceName} added`);
283
278
  return;
284
279
  }
280
+ catch (e) {
281
+ if (opts.interactive || isCodexMarketplaceAlreadyAdded(e, marketplaceName)) {
282
+ try {
283
+ exec(codexMarketplaceUpgradeCommand(marketplaceName), marketplaceExecOpts);
284
+ log.ok(`Codex marketplace ${marketplaceName} upgraded`);
285
+ return;
286
+ }
287
+ catch (upgradeErr) {
288
+ // Surface the underlying upgrade error so a 6-month-out reader
289
+ // can tell apart ENOENT / network / auth / git failures.
290
+ log.error(`Failed to upgrade Codex marketplace: ${marketplaceName}\n${commandErrorText(upgradeErr)}`);
291
+ failures.push(`codex marketplace ${marketplaceName}`);
292
+ return;
293
+ }
294
+ }
295
+ // Same: surface the add error rather than masking ENOENT / network / auth.
296
+ log.error(`Failed to add Codex marketplace: ${marketplaceName}\n${commandErrorText(e)}`);
297
+ failures.push(`codex marketplace ${marketplaceName}`);
298
+ }
299
+ }
300
+ // Builds the `<name>@<marketplace>` config keys + decides whether
301
+ // features.plugin_hooks needs to flip on. Local plugins resolve through
302
+ // this repo's marketplace.json and require a manifest fetch + hooks
303
+ // inspection; external plugins emit a key directly from install.json
304
+ // (Codex CLI fetches the upstream manifest itself). External plugins do
305
+ // NOT flip plugin_hooks today — we don't have access to the upstream
306
+ // manifest at install time. Acceptable while no external plugin ships
307
+ // hooks; once one does, prefer fetching the manifest or adding an
308
+ // explicit `requiresPluginHooks: true` field on the install.json entry.
309
+ async function composeCodexPluginKeys(packageRoot, localMarketplace, localSelected, externalSelected) {
310
+ const pluginKeys = [];
311
+ let needsPluginHooks = false;
312
+ if (localMarketplace) {
313
+ const localMpByName = new Map(localMarketplace.plugins.map((p) => [p.name, p]));
314
+ const selectedMarketplacePlugins = localSelected.map((p) => {
315
+ const plugin = localMpByName.get(p.name);
316
+ if (!plugin) {
317
+ throw new Error(`Codex install.json: plugin ${p.name} is not present in marketplace.json`);
318
+ }
319
+ return plugin;
320
+ });
321
+ await ensureCodexPluginManifests(packageRoot, selectedMarketplacePlugins);
322
+ for (const plugin of selectedMarketplacePlugins) {
323
+ pluginKeys.push(`${plugin.name}@${localMarketplace.name}`);
324
+ if (pluginHasHooks(packageRoot, plugin))
325
+ needsPluginHooks = true;
326
+ }
327
+ }
328
+ for (const p of externalSelected) {
329
+ pluginKeys.push(`${p.name}@${p.marketplace.name}`);
330
+ }
331
+ return { pluginKeys, needsPluginHooks };
332
+ }
333
+ async function installCodexPlugins(packageRoot, opts) {
285
334
  const installConfig = loadCodexInstallConfig(packageRoot);
286
335
  if (!installConfig) {
287
336
  const msg = "No .agents/plugins/install.json found";
@@ -290,7 +339,6 @@ async function installCodexPlugins(packageRoot, opts) {
290
339
  log.warn(msg);
291
340
  return;
292
341
  }
293
- const marketplaceByName = new Map(marketplace.plugins.map((p) => [p.name, p]));
294
342
  const selected = opts.interactive
295
343
  ? await withEsc(checkbox({
296
344
  message: "Select Codex plugins to install:",
@@ -305,41 +353,53 @@ async function installCodexPlugins(packageRoot, opts) {
305
353
  log.skip("No Codex plugins selected");
306
354
  return;
307
355
  }
308
- const failures = [];
309
- const marketplaceExecOpts = opts.interactive ? { inherit: true } : undefined;
310
- try {
311
- exec(codexMarketplaceAddCommand(packageRoot), marketplaceExecOpts);
312
- log.ok(`Codex marketplace ${marketplace.name} added`);
313
- }
314
- catch (e) {
315
- if (opts.interactive || isCodexMarketplaceAlreadyAdded(e, marketplace.name)) {
316
- try {
317
- exec(codexMarketplaceUpgradeCommand(marketplace.name), marketplaceExecOpts);
318
- log.ok(`Codex marketplace ${marketplace.name} upgraded`);
319
- }
320
- catch {
321
- log.error(`Failed to upgrade Codex marketplace: ${marketplace.name}`);
322
- failures.push(`codex marketplace ${marketplace.name}`);
356
+ // Local plugins are described by this repo's .agents/plugins/marketplace.json
357
+ // and need a manifest fetch + hooks-detection. External plugins point to a
358
+ // different GitHub-hosted Codex marketplace and are resolved by Codex CLI
359
+ // itself when the marketplace is added — we only need to register the
360
+ // marketplace and emit the right `<name>@<marketplace>` plugin key.
361
+ let localSelected = selected.filter((p) => p.marketplace === undefined);
362
+ const externalSelected = selected.filter((p) => p.marketplace !== undefined);
363
+ let localMarketplace = null;
364
+ if (localSelected.length > 0) {
365
+ localMarketplace = loadCodexMarketplace(packageRoot);
366
+ if (!localMarketplace) {
367
+ const msg = "No .agents/plugins/marketplace.json found";
368
+ // External-only fallback: when the user also selected external
369
+ // plugins (which don't depend on local marketplace.json), drop the
370
+ // local set and proceed instead of bailing out — partial install is
371
+ // strictly better than zero install. Throw / log-and-skip the
372
+ // local-only case as before.
373
+ if (externalSelected.length === 0) {
374
+ if (!opts.interactive)
375
+ throw new Error(msg);
376
+ log.warn(msg);
377
+ return;
323
378
  }
379
+ log.warn(`${msg} — skipping ${localSelected.length} local plugin(s) but continuing with externals`);
380
+ localSelected = [];
324
381
  }
325
- else {
326
- log.error(`Failed to add Codex marketplace: ${marketplace.name}`);
327
- failures.push(`codex marketplace ${marketplace.name}`);
328
- }
382
+ }
383
+ const failures = [];
384
+ const marketplaceExecOpts = opts.interactive
385
+ ? { inherit: true }
386
+ : undefined;
387
+ if (localMarketplace) {
388
+ await addCodexMarketplaceWithRetry(localMarketplace.name, codexMarketplaceAddCommand(packageRoot), opts, marketplaceExecOpts, failures);
389
+ }
390
+ // Dedupe external marketplaces by name — multiple plugins from the same
391
+ // upstream share a single `marketplace add` call.
392
+ const uniqueExternalMarketplaces = new Map();
393
+ for (const p of externalSelected) {
394
+ uniqueExternalMarketplaces.set(p.marketplace.name, p.marketplace);
395
+ }
396
+ for (const mp of uniqueExternalMarketplaces.values()) {
397
+ await addCodexMarketplaceWithRetry(mp.name, codexExternalMarketplaceAddCommand(mp.source), opts, marketplaceExecOpts, failures);
329
398
  }
330
399
  if (failures.length === 0) {
331
- const selectedMarketplacePlugins = selected.map((p) => {
332
- const plugin = marketplaceByName.get(p.name);
333
- if (!plugin) {
334
- throw new Error(`Codex install.json: plugin ${p.name} is not present in marketplace.json`);
335
- }
336
- return plugin;
337
- });
338
- await ensureCodexPluginManifests(packageRoot, selectedMarketplacePlugins);
339
- const pluginKeys = selectedMarketplacePlugins.map((p) => `${p.name}@${marketplace.name}`);
340
- const needsPluginHooks = selectedMarketplacePlugins.some((p) => pluginHasHooks(packageRoot, p));
400
+ const { pluginKeys, needsPluginHooks } = await composeCodexPluginKeys(packageRoot, localMarketplace, localSelected, externalSelected);
341
401
  enableCodexPluginConfig(path.join(codexHome(), "config.toml"), pluginKeys, needsPluginHooks);
342
- for (const plugin of selected) {
402
+ for (const plugin of [...localSelected, ...externalSelected]) {
343
403
  log.ok(`${plugin.name} enabled for Codex`);
344
404
  }
345
405
  }
package/dist/skills.js CHANGED
@@ -7,7 +7,6 @@ import { exec, log, withEsc } from "./utils.js";
7
7
  // installRecommendedSkills as an opt-in utility.
8
8
  export const WORKFLOW_SKILLS = [
9
9
  "brainstorming",
10
- "deep-review",
11
10
  "parallel-implementation",
12
11
  "planning-with-files",
13
12
  "playwright-cli",
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { MarketplaceRef } from "./marketplace.js";
1
2
  export interface SkillEntry {
2
3
  source: string;
3
4
  sourceType: string;
@@ -11,10 +12,7 @@ export interface PluginDef {
11
12
  name: string;
12
13
  package: string;
13
14
  description: string;
14
- marketplace?: {
15
- name: string;
16
- source: string;
17
- };
15
+ marketplace?: MarketplaceRef;
18
16
  }
19
17
  export interface PluginsConfig {
20
18
  plugins: PluginDef[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auriga-cli",
3
- "version": "1.10.3",
3
+ "version": "1.11.1",
4
4
  "description": "Interactive CLI to install Claude Code harness modules (Workflow, Skills, Recommended Skills, Plugins, Hooks)",
5
5
  "license": "MIT",
6
6
  "repository": {