auriga-cli 1.10.2 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/dist/catalog.json +5 -5
- package/dist/codex-plugin-config.d.ts +2 -0
- package/dist/codex-plugin-config.js +4 -1
- package/dist/guide.js +3 -4
- package/dist/marketplace.d.ts +7 -0
- package/dist/marketplace.js +34 -0
- package/dist/plugins.js +116 -56
- package/dist/skills.js +0 -1
- package/dist/utils.d.ts +2 -4
- package/package.json +1 -1
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,
|
|
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、
|
|
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-
|
|
2
|
+
"generatedAt": "2026-05-10T01:41:17.863Z",
|
|
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
|
|
77
|
-
because they have side effects (OS notifications, platform-gated
|
|
78
|
-
|
|
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
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
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
|
-
|
|
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
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
|
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
|
|
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
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