auriga-cli 1.18.4 → 1.19.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 +1 -1
- package/README.zh-CN.md +2 -2
- package/dist/api-types.d.ts +21 -18
- package/dist/apply-handlers.js +11 -9
- package/dist/catalog.d.ts +3 -19
- package/dist/catalog.json +5 -10
- package/dist/cli.js +36 -5
- package/dist/scan-catalog.js +11 -87
- package/dist/server.js +1 -1
- package/dist/state.d.ts +15 -51
- package/dist/state.js +125 -408
- package/dist/workflow.js +32 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ For a browser-based view of what's installed and one-click apply, run:
|
|
|
58
58
|
npx auriga-cli web-ui
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
This boots a local server on `127.0.0.1`, opens your default browser, and serves a dashboard that scans the current project, shows each module's status (installed /
|
|
61
|
+
This boots a local server on `127.0.0.1`, opens your default browser, and serves a dashboard that scans the current project, shows each module's status (installed / not-installed / partial-install), and applies install / uninstall in a queue with live SSE progress. Re-running install is the update path — every installer is idempotent and overwrites in place. The server shuts down on its own ~15 s after the browser closes.
|
|
62
62
|
|
|
63
63
|
The UI is opt-in — `npx auriga-cli` still launches the TTY menu below.
|
|
64
64
|
|
package/README.zh-CN.md
CHANGED
|
@@ -52,13 +52,13 @@ npx -y auriga-cli --help # 完整 catalog + flag 说明
|
|
|
52
52
|
|
|
53
53
|
### Web UI(可选)
|
|
54
54
|
|
|
55
|
-
如果想在浏览器里看到“已装 /
|
|
55
|
+
如果想在浏览器里看到“已装 / 未装 / 半装”全景并一键 apply,跑:
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
58
|
npx auriga-cli web-ui
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
它会在 `127.0.0.1` 起一个本地 server、自动开浏览器,扫描当前项目并展示 5
|
|
61
|
+
它会在 `127.0.0.1` 起一个本地 server、自动开浏览器,扫描当前项目并展示 5 个分类的状态。勾选要安装/卸载的项目后点 Apply,SSE 实时回传执行进度。需要"升级"时就重新 install——每个安装器都是幂等覆盖。关浏览器后约 15 秒 server 自动退出。
|
|
62
62
|
|
|
63
63
|
Web UI 是显式入口;`npx auriga-cli` 仍然走下面的 TTY 菜单。
|
|
64
64
|
|
package/dist/api-types.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export type ItemStatus = "installed" | "
|
|
1
|
+
export type ItemStatus = "installed" | "not-installed"
|
|
2
|
+
/** Dual-Agent plugin where some target Agents have the plugin installed
|
|
3
|
+
* and some don't (e.g. Claude side installed, Codex side missing). The
|
|
4
|
+
* user-facing action is "install on the missing side"; the missing
|
|
5
|
+
* agents are enumerated in `PluginState.missingAgents`. */
|
|
6
|
+
| "partial-install";
|
|
2
7
|
/**
|
|
3
8
|
* Per-category scan scope. Each category (workflow / skills / plugins / hooks)
|
|
4
9
|
* can be independently scanned in either user scope (~/.claude/, ~/.codex/)
|
|
@@ -23,8 +28,6 @@ export interface StateReport {
|
|
|
23
28
|
}
|
|
24
29
|
export interface WorkflowState {
|
|
25
30
|
status: ItemStatus;
|
|
26
|
-
currentVersion?: string;
|
|
27
|
-
expectedVersion: string;
|
|
28
31
|
/** Which scope the scanner read to produce this row. Reflects the scope
|
|
29
32
|
* scanned, not where the file was found — e.g. when scope=user and
|
|
30
33
|
* ~/.claude/CLAUDE.md is absent, observedScope is still "user". The
|
|
@@ -38,8 +41,6 @@ export interface SkillState {
|
|
|
38
41
|
description: string;
|
|
39
42
|
status: ItemStatus;
|
|
40
43
|
isWorkflow: boolean;
|
|
41
|
-
currentHash?: string;
|
|
42
|
-
expectedHash: string;
|
|
43
44
|
/** Scope the scanner read to produce this row. See WorkflowState comment. */
|
|
44
45
|
observedScope?: ScanScope;
|
|
45
46
|
}
|
|
@@ -53,38 +54,40 @@ export interface PluginState {
|
|
|
53
54
|
* `agents.length === 2` the UI shows a BOTH badge and Apply installs to
|
|
54
55
|
* each agent in turn. Status is aggregated across all targeted agents:
|
|
55
56
|
* `installed` ⇔ all agents installed; `not-installed` ⇔ all not-installed;
|
|
56
|
-
*
|
|
57
|
-
* so a single Apply backfills the missing side. */
|
|
57
|
+
* mixed → `partial-install` (some installed, some not). */
|
|
58
58
|
agents: ApplyAgent[];
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
/** Agents that target this plugin but don't have it installed. Populated
|
|
60
|
+
* iff `status === "partial-install"`. Drives the "Missing on Codex"
|
|
61
|
+
* UI caption + tells the user exactly which side needs backfill. */
|
|
62
|
+
missingAgents?: ApplyAgent[];
|
|
62
63
|
/** Scope the scanner read to produce this row. Codex plugins are always
|
|
63
64
|
* "user" (Codex has no project-scope plugin concept). See WorkflowState
|
|
64
65
|
* comment on why this is typed optional. */
|
|
65
66
|
observedScope?: ScanScope;
|
|
66
67
|
/** True for plugins whose source lives in an upstream marketplace, not in
|
|
67
|
-
* this repo (skill-creator / claude-md-management / codex).
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* badge to make the "not our jurisdiction" signal explicit. */
|
|
68
|
+
* this repo (skill-creator / claude-md-management / codex). Pure UI hint
|
|
69
|
+
* since v1.19.0 — the EXTERNAL badge tells users upgrades go through
|
|
70
|
+
* `claude plugins update`, not via auriga-cli. */
|
|
71
71
|
external?: boolean;
|
|
72
72
|
}
|
|
73
73
|
export interface HookState {
|
|
74
74
|
name: string;
|
|
75
75
|
description: string;
|
|
76
76
|
status: ItemStatus;
|
|
77
|
-
currentHash?: string;
|
|
78
|
-
expectedHash: string;
|
|
79
77
|
/** Scope the scanner read to produce this row. See WorkflowState comment. */
|
|
80
78
|
observedScope?: ScanScope;
|
|
81
79
|
}
|
|
82
80
|
export interface StateWarning {
|
|
83
|
-
code: "claude-cli-missing" | "codex-cli-missing" | "marketplace-offline" | "claude-code-not-installed" | "settings-unreadable" | "skill-malformed"
|
|
81
|
+
code: "claude-cli-missing" | "codex-cli-missing" | "marketplace-offline" | "claude-code-not-installed" | "settings-unreadable" | "skill-malformed"
|
|
82
|
+
/** Project-scope CLAUDE.md (or the user-scope fallback when scanning
|
|
83
|
+
* user scope) is present but has no recognizable `# auriga Workflow (vX.Y.Z)`
|
|
84
|
+
* header. The row reports `not-installed`; install will back up the
|
|
85
|
+
* existing file to `CLAUDE.md.bak` and write ours. */
|
|
86
|
+
| "workflow-foreign-claudemd";
|
|
84
87
|
message: string;
|
|
85
88
|
}
|
|
86
89
|
export type ApplyCategory = "workflow" | "skill" | "recommended-skill" | "plugin" | "hook";
|
|
87
|
-
export type ApplyAction = "install" | "
|
|
90
|
+
export type ApplyAction = "install" | "uninstall";
|
|
88
91
|
/**
|
|
89
92
|
* Installer scope. Carried per-item so the Web UI can mix scopes within a
|
|
90
93
|
* single apply batch.
|
package/dist/apply-handlers.js
CHANGED
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
// hook: installHook(hookDef, "project",…)│ needs HookDef
|
|
18
18
|
// hook: uninstallHook(name, …) │
|
|
19
19
|
//
|
|
20
|
+
// v1.19.0 dropped the "update" action — every installer is idempotent and
|
|
21
|
+
// overwriting, so re-running install IS the update path. Apply receives
|
|
22
|
+
// "install" or "uninstall" only.
|
|
23
|
+
//
|
|
20
24
|
// Spec: docs/architecture/web-ui.md §6.4 (apply execution model).
|
|
21
25
|
import { installHook, loadHooksConfig, uninstallHook } from "./hooks.js";
|
|
22
26
|
import { installPlugins, uninstallPlugin, } from "./plugins.js";
|
|
@@ -24,7 +28,6 @@ import { installRecommendedSkills, installSkills, uninstallSkill, } from "./skil
|
|
|
24
28
|
import { installWorkflow, uninstallWorkflow } from "./workflow.js";
|
|
25
29
|
const ALL_ACTIONS = new Set([
|
|
26
30
|
"install",
|
|
27
|
-
"update",
|
|
28
31
|
"uninstall",
|
|
29
32
|
]);
|
|
30
33
|
function assertAction(action) {
|
|
@@ -43,7 +46,7 @@ export function buildDefaultApplyHandlers(ctx) {
|
|
|
43
46
|
// the Workflow column's EN/ZH-CN picker). Falls back to ctx lang for
|
|
44
47
|
// CLI-mode callers that don't pass it.
|
|
45
48
|
const installLang = requestedLang ?? lang;
|
|
46
|
-
if (action === "install"
|
|
49
|
+
if (action === "install") {
|
|
47
50
|
await installWorkflow(packageRoot, {
|
|
48
51
|
interactive: false,
|
|
49
52
|
cwd,
|
|
@@ -68,7 +71,7 @@ export function buildDefaultApplyHandlers(ctx) {
|
|
|
68
71
|
const skill = async (action, name, { onLog, scope }) => {
|
|
69
72
|
assertAction(action);
|
|
70
73
|
const installScope = scope ?? "project";
|
|
71
|
-
if (action === "install"
|
|
74
|
+
if (action === "install") {
|
|
72
75
|
await installSkills(packageRoot, {
|
|
73
76
|
interactive: false,
|
|
74
77
|
cwd,
|
|
@@ -88,7 +91,7 @@ export function buildDefaultApplyHandlers(ctx) {
|
|
|
88
91
|
const recommendedSkill = async (action, name, { onLog, scope }) => {
|
|
89
92
|
assertAction(action);
|
|
90
93
|
const installScope = scope ?? "project";
|
|
91
|
-
if (action === "install"
|
|
94
|
+
if (action === "install") {
|
|
92
95
|
await installRecommendedSkills(packageRoot, {
|
|
93
96
|
interactive: false,
|
|
94
97
|
cwd,
|
|
@@ -119,7 +122,7 @@ export function buildDefaultApplyHandlers(ctx) {
|
|
|
119
122
|
const failures = [];
|
|
120
123
|
for (const agent of agents) {
|
|
121
124
|
try {
|
|
122
|
-
if (action === "install"
|
|
125
|
+
if (action === "install") {
|
|
123
126
|
await installPlugins(packageRoot, {
|
|
124
127
|
interactive: false,
|
|
125
128
|
cwd,
|
|
@@ -162,10 +165,9 @@ export function buildDefaultApplyHandlers(ctx) {
|
|
|
162
165
|
});
|
|
163
166
|
return;
|
|
164
167
|
}
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
// SSE caller surfaces it as item:done success=false.
|
|
168
|
+
// Look up the HookDef in the bundled registry; unknown name → loud throw
|
|
169
|
+
// so the SSE caller surfaces it as item:done success=false. The hook
|
|
170
|
+
// installer is idempotent; re-running install is the update path.
|
|
169
171
|
const config = loadHooksConfig(packageRoot);
|
|
170
172
|
const hookDef = config.hooks.find((h) => h.name === name);
|
|
171
173
|
if (!hookDef) {
|
package/dist/catalog.d.ts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
export interface CatalogEntry {
|
|
2
2
|
name: string;
|
|
3
3
|
description: string;
|
|
4
|
-
/** Build-time-baked plugin version. Set ONLY for plugin entries whose
|
|
5
|
-
* source lives in this repo's `plugins/<name>/` directory — the scanner
|
|
6
|
-
* uses it to surface "update-available" when the user's installed copy
|
|
7
|
-
* is older. Absent for skill / hook entries and for external-marketplace
|
|
8
|
-
* plugins (whose manifest lives upstream). Must be baked at build time
|
|
9
|
-
* because `plugins/<name>/.claude-plugin/plugin.json` is NOT shipped in
|
|
10
|
-
* the npm tarball (see `package.json` `files` field). */
|
|
11
|
-
expectedVersion?: string;
|
|
12
4
|
/** Build-time-baked agent map for plugin entries. Derived from
|
|
13
5
|
* `.claude/plugins.json` ∪ `.agents/plugins/install.json` — those config
|
|
14
6
|
* files are NOT shipped in the npm tarball, so the scanner can't read
|
|
@@ -17,21 +9,13 @@ export interface CatalogEntry {
|
|
|
17
9
|
* Absent on skill / hook entries. */
|
|
18
10
|
agents?: ("claude" | "codex")[];
|
|
19
11
|
/** True for plugins whose source lives in an UPSTREAM marketplace
|
|
20
|
-
* (skill-creator / claude-md-management / codex), not in this repo.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* surfaces an EXTERNAL badge so users know where to look. */
|
|
12
|
+
* (skill-creator / claude-md-management / codex), not in this repo.
|
|
13
|
+
* Pure UI hint since v1.19.0 — the EXTERNAL badge tells users that
|
|
14
|
+
* upgrades go through `claude plugins update`, not us. */
|
|
24
15
|
external?: boolean;
|
|
25
16
|
}
|
|
26
17
|
export interface Catalog {
|
|
27
18
|
generatedAt: string;
|
|
28
|
-
/** Workflow content version baked from `CLAUDE.md`'s `# auriga Workflow (vX.Y.Z)`
|
|
29
|
-
* header at build time. MUST live here rather than be read at runtime
|
|
30
|
-
* because `CLAUDE.md` is NOT in the npm tarball — `package.json` `files`
|
|
31
|
-
* allowlists only `dist/`. Empty string when the header is unparseable;
|
|
32
|
-
* the scanner then degrades to "trust whatever the user has" rather than
|
|
33
|
-
* forcing phantom update-available against an empty expected value. */
|
|
34
|
-
workflowVersion: string;
|
|
35
19
|
workflowSkills: CatalogEntry[];
|
|
36
20
|
recommendedSkills: CatalogEntry[];
|
|
37
21
|
plugins: CatalogEntry[];
|
package/dist/catalog.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-05-
|
|
3
|
-
"workflowVersion": "1.7.0",
|
|
2
|
+
"generatedAt": "2026-05-13T02:06:00.781Z",
|
|
4
3
|
"workflowSkills": [
|
|
5
4
|
{
|
|
6
5
|
"name": "brainstorming",
|
|
@@ -104,8 +103,7 @@
|
|
|
104
103
|
"agents": [
|
|
105
104
|
"claude",
|
|
106
105
|
"codex"
|
|
107
|
-
]
|
|
108
|
-
"expectedVersion": "1.1.0"
|
|
106
|
+
]
|
|
109
107
|
},
|
|
110
108
|
{
|
|
111
109
|
"name": "auriga-git-guards",
|
|
@@ -113,8 +111,7 @@
|
|
|
113
111
|
"agents": [
|
|
114
112
|
"claude",
|
|
115
113
|
"codex"
|
|
116
|
-
]
|
|
117
|
-
"expectedVersion": "1.1.0"
|
|
114
|
+
]
|
|
118
115
|
},
|
|
119
116
|
{
|
|
120
117
|
"name": "deep-review",
|
|
@@ -122,16 +119,14 @@
|
|
|
122
119
|
"agents": [
|
|
123
120
|
"claude",
|
|
124
121
|
"codex"
|
|
125
|
-
]
|
|
126
|
-
"expectedVersion": "0.3.1"
|
|
122
|
+
]
|
|
127
123
|
},
|
|
128
124
|
{
|
|
129
125
|
"name": "session-instructions-loader",
|
|
130
126
|
"description": "(Codex) Injects extra instruction files on session start",
|
|
131
127
|
"agents": [
|
|
132
128
|
"codex"
|
|
133
|
-
]
|
|
134
|
-
"expectedVersion": "1.0.0"
|
|
129
|
+
]
|
|
135
130
|
}
|
|
136
131
|
],
|
|
137
132
|
"hooks": [
|
package/dist/cli.js
CHANGED
|
@@ -576,7 +576,29 @@ async function runUi(p, version) {
|
|
|
576
576
|
const { buildScanCatalog } = await import("./scan-catalog.js");
|
|
577
577
|
const { ensureUiBundle } = await import("./ui-fetch.js");
|
|
578
578
|
const cwd = process.cwd();
|
|
579
|
-
|
|
579
|
+
// Two roots, two responsibilities (mirrors the TTY install path's pattern):
|
|
580
|
+
// - tarballRoot: where `dist/catalog.json` + the bundled DEV ui/dist live.
|
|
581
|
+
// Always read from the installed npm package; can't be fetched because
|
|
582
|
+
// dist/ is built artifact, not git content.
|
|
583
|
+
// - contentRoot: where the runtime install recipes live (CLAUDE.md,
|
|
584
|
+
// .claude/plugins.json, .claude/hooks/hooks.json, .agents/plugins/
|
|
585
|
+
// install.json + marketplace.json, skills-lock.json). These files are
|
|
586
|
+
// NOT in the npm tarball — the `files` allowlist only ships `dist/*`
|
|
587
|
+
// + npm defaults. They are fetched from GitHub, pinned to the CLI
|
|
588
|
+
// version tag, by fetchContentRoot(). Under DEV=1 fetchContentRoot
|
|
589
|
+
// short-circuits to the repo root so this is a no-op there.
|
|
590
|
+
// Without the contentRoot fix, tarball-installed Web UI users hit ENOENT
|
|
591
|
+
// on any Codex plugin install (apply handlers read .agents/plugins/
|
|
592
|
+
// install.json from packageRoot).
|
|
593
|
+
const tarballRoot = getPackageRoot();
|
|
594
|
+
let contentRoot;
|
|
595
|
+
try {
|
|
596
|
+
contentRoot = await fetchContentRoot();
|
|
597
|
+
}
|
|
598
|
+
catch (e) {
|
|
599
|
+
log.error(`Failed to fetch content: ${e.message}`);
|
|
600
|
+
return 1;
|
|
601
|
+
}
|
|
580
602
|
// 1. Resolve UI bundle directory.
|
|
581
603
|
let uiDir;
|
|
582
604
|
if (p.uiDir) {
|
|
@@ -588,7 +610,7 @@ async function runUi(p, version) {
|
|
|
588
610
|
}
|
|
589
611
|
else if (process.env.DEV === "1") {
|
|
590
612
|
// Dev convenience: prefer the locally-built ui/dist over a network fetch.
|
|
591
|
-
const localDist = path.join(
|
|
613
|
+
const localDist = path.join(tarballRoot, "ui", "dist");
|
|
592
614
|
if (fs.existsSync(path.join(localDist, "index.html"))) {
|
|
593
615
|
uiDir = localDist;
|
|
594
616
|
}
|
|
@@ -613,7 +635,9 @@ async function runUi(p, version) {
|
|
|
613
635
|
// 2. Build scan catalog → ApplyCatalog + pluginAgentsByName.
|
|
614
636
|
let scanCatalog;
|
|
615
637
|
try {
|
|
616
|
-
|
|
638
|
+
// dist/catalog.json ships in the tarball — read from tarballRoot, not
|
|
639
|
+
// the fetched content root (which doesn't carry build artifacts).
|
|
640
|
+
scanCatalog = await buildScanCatalog(tarballRoot);
|
|
617
641
|
}
|
|
618
642
|
catch (e) {
|
|
619
643
|
log.error(`Failed to build catalog: ${e.message}`);
|
|
@@ -635,7 +659,10 @@ async function runUi(p, version) {
|
|
|
635
659
|
pluginAgentsByName.set(name, def.agents);
|
|
636
660
|
}
|
|
637
661
|
const applyHandlers = buildDefaultApplyHandlers({
|
|
638
|
-
|
|
662
|
+
// contentRoot: install handlers read CLAUDE.md, plugins.json,
|
|
663
|
+
// hooks.json, install.json, marketplace.json — all CONTENT_FILES.
|
|
664
|
+
// Routing them at tarballRoot fails ENOENT for npm-installed users.
|
|
665
|
+
packageRoot: contentRoot,
|
|
639
666
|
cwd,
|
|
640
667
|
pluginAgentsByName,
|
|
641
668
|
});
|
|
@@ -656,7 +683,11 @@ async function runUi(p, version) {
|
|
|
656
683
|
port,
|
|
657
684
|
token,
|
|
658
685
|
cwd,
|
|
659
|
-
|
|
686
|
+
// server reads dist/catalog.json (tarball-shipped) via
|
|
687
|
+
// buildScanCatalog on each /api/state call; install-time content
|
|
688
|
+
// (install.json, plugins.json, CLAUDE.md, …) was already injected
|
|
689
|
+
// into applyHandlers above with contentRoot.
|
|
690
|
+
packageRoot: tarballRoot,
|
|
660
691
|
heartbeatTimeoutMs: UI_HEARTBEAT_TIMEOUT_MS,
|
|
661
692
|
applyHandlers,
|
|
662
693
|
applyCatalog,
|
package/dist/scan-catalog.js
CHANGED
|
@@ -1,53 +1,20 @@
|
|
|
1
1
|
// Build the scan-time Catalog (the shape src/state.ts consumes) from the
|
|
2
|
-
// build-time `dist/catalog.json`.
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// `packageRoot/.agents/skills/<name>/SKILL.md` succeeds in dev (where
|
|
7
|
-
// packageRoot === repoRoot) but silently returns empty for npm-installed
|
|
8
|
-
// users, leaving the scanner unable to surface real update signals.
|
|
9
|
-
//
|
|
10
|
-
// Anything the scanner needs beyond what's already in catalog.json must
|
|
11
|
-
// first be baked at build time in `src/build/generate-catalog.ts`.
|
|
12
|
-
//
|
|
13
|
-
// Scope of the current bake (covered fields):
|
|
14
|
-
// - workflowVersion — from CLAUDE.md header
|
|
15
|
-
// - plugin agents map — from .claude/plugins.json ∪ .agents/plugins/install.json
|
|
16
|
-
// - plugin expectedVersion — from plugins/<name>/.claude-plugin/plugin.json
|
|
17
|
-
// - plugin external flag — derived (no in-tree manifest = external)
|
|
18
|
-
//
|
|
19
|
-
// Out of scope for v1.18.4 (follow-up PRs):
|
|
20
|
-
// - hook expectedEvent / expectedMatcher / expectedIf (from .claude/hooks/hooks.json)
|
|
21
|
-
// - apply-time installer config (the install path reads .claude/plugins.json
|
|
22
|
-
// directly — that needs runWebUi → fetchContentRoot rewire, not bake).
|
|
23
|
-
import { readFile } from "node:fs/promises";
|
|
24
|
-
import path from "node:path";
|
|
2
|
+
// build-time `dist/catalog.json`. Thin adapter — reads dist/catalog.json
|
|
3
|
+
// only. v1.19.0 dropped all version / hash / event comparison from the
|
|
4
|
+
// scanner, so the build-time catalog is reduced to {description, agents?,
|
|
5
|
+
// external?} per entry; runtime reads outside dist/ are no longer needed.
|
|
25
6
|
import { loadCatalog } from "./catalog.js";
|
|
26
|
-
async function tryReadFile(p) {
|
|
27
|
-
try {
|
|
28
|
-
return await readFile(p, "utf8");
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
7
|
export async function buildScanCatalog(packageRoot) {
|
|
35
8
|
const dist = loadCatalog(packageRoot);
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
// repo HEAD. Our catalog snapshot would only know "what auriga-cli
|
|
42
|
-
// shipped at this CLI release" — at best a stale proxy that mis-reports
|
|
43
|
-
// legitimate user-side updates as drift. Setting expectedHash to "" puts
|
|
44
|
-
// classifySkillByFile into wildcard mode: row reports installed if
|
|
45
|
-
// SKILL.md exists, not-installed otherwise; never update-available.
|
|
9
|
+
// v1.19.0 dropped update-available status. The scanner is now presence-
|
|
10
|
+
// only: skills / hooks / plugins / workflow all report installed iff their
|
|
11
|
+
// truth source exists, not-installed otherwise. No version / hash / event
|
|
12
|
+
// comparison happens, so the build-time catalog is reduced to the bare
|
|
13
|
+
// {description, agents?, external?} shape per entry.
|
|
46
14
|
const skills = {};
|
|
47
15
|
for (const entry of dist.workflowSkills) {
|
|
48
16
|
skills[entry.name] = {
|
|
49
17
|
description: entry.description,
|
|
50
|
-
expectedHash: "",
|
|
51
18
|
isWorkflow: true,
|
|
52
19
|
};
|
|
53
20
|
}
|
|
@@ -55,16 +22,8 @@ export async function buildScanCatalog(packageRoot) {
|
|
|
55
22
|
for (const entry of dist.recommendedSkills) {
|
|
56
23
|
recommendedSkills[entry.name] = {
|
|
57
24
|
description: entry.description,
|
|
58
|
-
expectedHash: "",
|
|
59
25
|
};
|
|
60
26
|
}
|
|
61
|
-
// Plugins: agents + expectedVersion + external all come from
|
|
62
|
-
// dist/catalog.json now (baked in src/build/generate-catalog.ts). The
|
|
63
|
-
// previous version of this module read .claude/plugins.json +
|
|
64
|
-
// .agents/plugins/install.json at runtime — those files are NOT in the
|
|
65
|
-
// npm tarball, so for installed users every plugin defaulted to a
|
|
66
|
-
// ["claude"] agent classification (root cause of dual-Agent plugin
|
|
67
|
-
// mis-classification in v1.18.x).
|
|
68
27
|
const plugins = {};
|
|
69
28
|
for (const entry of dist.plugins) {
|
|
70
29
|
const agents = Array.isArray(entry.agents) && entry.agents.length > 0
|
|
@@ -73,47 +32,12 @@ export async function buildScanCatalog(packageRoot) {
|
|
|
73
32
|
plugins[entry.name] = {
|
|
74
33
|
description: entry.description,
|
|
75
34
|
agents,
|
|
76
|
-
...(typeof entry.expectedVersion === "string" && entry.expectedVersion.length > 0
|
|
77
|
-
? { expectedVersion: entry.expectedVersion }
|
|
78
|
-
: {}),
|
|
79
35
|
...(entry.external === true ? { external: true } : {}),
|
|
80
36
|
};
|
|
81
37
|
}
|
|
82
|
-
// Hooks: TODO follow-up — bake expectedEvent / expectedMatcher / expectedIf
|
|
83
|
-
// into dist/catalog.json the same way agents are baked. Currently the
|
|
84
|
-
// runtime read of packageRoot/.claude/hooks/hooks.json works in dev
|
|
85
|
-
// (packageRoot === repoRoot) but fails silently for npm-installed users
|
|
86
|
-
// — `package.json` `files` allowlist doesn't ship `.claude/`. So hook
|
|
87
|
-
// drift detection is correct in dev and degraded (always "installed" if
|
|
88
|
-
// marker present) in production. Follow-up bake closes the dev/prod gap.
|
|
89
|
-
const hooksJsonPath = path.join(packageRoot, ".claude", "hooks", "hooks.json");
|
|
90
|
-
const hooksJsonRaw = await tryReadFile(hooksJsonPath);
|
|
91
|
-
const hooksJson = hooksJsonRaw ? JSON.parse(hooksJsonRaw) : {};
|
|
92
|
-
const settingsEventsByName = new Map();
|
|
93
|
-
for (const h of hooksJson.hooks ?? []) {
|
|
94
|
-
if (typeof h.name === "string" && h.settingsEvents?.length) {
|
|
95
|
-
settingsEventsByName.set(h.name, h.settingsEvents[0]);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
38
|
const hooks = {};
|
|
99
39
|
for (const entry of dist.hooks) {
|
|
100
|
-
|
|
101
|
-
hooks[entry.name] = {
|
|
102
|
-
description: entry.description,
|
|
103
|
-
// Empty expectedHash flips state.ts into wildcard mode — drift judged
|
|
104
|
-
// purely from the structured expected* fields below, not from a
|
|
105
|
-
// settings-entry content hash.
|
|
106
|
-
expectedHash: "",
|
|
107
|
-
...(typeof ev?.event === "string" ? { expectedEvent: ev.event } : {}),
|
|
108
|
-
...(typeof ev?.matcher === "string" ? { expectedMatcher: ev.matcher } : {}),
|
|
109
|
-
...(typeof ev?.if === "string" ? { expectedIf: ev.if } : {}),
|
|
110
|
-
};
|
|
40
|
+
hooks[entry.name] = { description: entry.description };
|
|
111
41
|
}
|
|
112
|
-
return {
|
|
113
|
-
workflowVersion,
|
|
114
|
-
skills,
|
|
115
|
-
recommendedSkills,
|
|
116
|
-
plugins,
|
|
117
|
-
hooks,
|
|
118
|
-
};
|
|
42
|
+
return { skills, recommendedSkills, plugins, hooks };
|
|
119
43
|
}
|
package/dist/server.js
CHANGED
|
@@ -171,7 +171,7 @@ const VALID_CATEGORIES = new Set([
|
|
|
171
171
|
"plugin",
|
|
172
172
|
"hook",
|
|
173
173
|
]);
|
|
174
|
-
const VALID_ACTIONS = new Set(["install", "
|
|
174
|
+
const VALID_ACTIONS = new Set(["install", "uninstall"]);
|
|
175
175
|
const VALID_SCOPES = new Set(["project", "user"]);
|
|
176
176
|
const VALID_LANGS = new Set(["en", "zh-CN"]);
|
|
177
177
|
function parseApplyRequest(raw) {
|
package/dist/state.d.ts
CHANGED
|
@@ -1,57 +1,37 @@
|
|
|
1
1
|
import type { PluginState, ScanScope, StateReport } from "./api-types.js";
|
|
2
2
|
export interface Catalog {
|
|
3
|
-
workflowVersion: string;
|
|
4
3
|
skills: Record<string, {
|
|
5
4
|
description: string;
|
|
6
|
-
expectedHash: string;
|
|
7
5
|
isWorkflow: boolean;
|
|
8
6
|
}>;
|
|
9
7
|
recommendedSkills: Record<string, {
|
|
10
8
|
description: string;
|
|
11
|
-
expectedHash: string;
|
|
12
9
|
}>;
|
|
13
10
|
plugins: Record<string, {
|
|
14
11
|
description: string;
|
|
15
12
|
/** Agents this plugin can install into. Length 1 or 2. */
|
|
16
13
|
agents: ("claude" | "codex")[];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* an EXTERNAL badge so users know to defer to the upstream tool. */
|
|
14
|
+
/** True for plugins whose source lives in an upstream marketplace
|
|
15
|
+
* (skill-creator / claude-md-management / codex). Drives the UI's
|
|
16
|
+
* EXTERNAL badge — upgrades go through `claude plugins update`, not us.
|
|
17
|
+
* Pure UI hint since v1.19.0 (used to also gate update-available
|
|
18
|
+
* reporting; that surface was removed). */
|
|
23
19
|
external?: boolean;
|
|
24
20
|
}>;
|
|
25
21
|
hooks: Record<string, {
|
|
26
22
|
description: string;
|
|
27
|
-
/** Coarse drift signal. The current production scan-catalog still
|
|
28
|
-
* populates this with sha256(index.mjs) for back-compat with the v0.x
|
|
29
|
-
* scanner that hashed the user's installed script. The new
|
|
30
|
-
* settings.json-based scanner ignores it for drift comparison unless
|
|
31
|
-
* the catalog also exposes the structured expected* fields below. */
|
|
32
|
-
expectedHash: string;
|
|
33
|
-
/** Settings.json event name (e.g. "PostToolUse", "Notification"). When
|
|
34
|
-
* set, the scanner flags drift if the on-disk settings entry registers
|
|
35
|
-
* under a different event. Optional; left undefined the scanner trusts
|
|
36
|
-
* whatever event the marker was found under. */
|
|
37
|
-
expectedEvent?: string;
|
|
38
|
-
/** Settings.json `matcher` field (e.g. "Write|Edit" for PostToolUse).
|
|
39
|
-
* Empty string means "no matcher" (e.g. Notification hooks). When set,
|
|
40
|
-
* the scanner flags drift if the on-disk value differs. */
|
|
41
|
-
expectedMatcher?: string;
|
|
42
|
-
/** Settings.json `if` field (Claude-Code-specific filter expression).
|
|
43
|
-
* Same drift semantics as expectedMatcher. */
|
|
44
|
-
expectedIf?: string;
|
|
45
23
|
}>;
|
|
46
24
|
}
|
|
47
25
|
export interface ScanOptions {
|
|
48
26
|
/** Run `claude plugins list` for the given scope. The scope argument is
|
|
49
27
|
* required so server.ts can pass --user / --project through to the CLI
|
|
50
28
|
* per opts.scopes.plugins. Implementations may accept a zero-arg legacy
|
|
51
|
-
* form for back-compat but MUST honor a scope argument when given.
|
|
29
|
+
* form for back-compat but MUST honor a scope argument when given.
|
|
30
|
+
*
|
|
31
|
+
* Returns just the installed records — v1.19.0 dropped the parallel
|
|
32
|
+
* `--available` fetch since the scanner no longer compares versions. */
|
|
52
33
|
execPluginList?: (scope: ScanScope) => Promise<{
|
|
53
34
|
installed: any[];
|
|
54
|
-
available: any[];
|
|
55
35
|
}>;
|
|
56
36
|
readCodexConfig?: () => Promise<string | null>;
|
|
57
37
|
readCodexPluginsDir?: () => Promise<Map<string, string>>;
|
|
@@ -71,32 +51,16 @@ export interface ScanOptions {
|
|
|
71
51
|
homeDir?: string;
|
|
72
52
|
}
|
|
73
53
|
export declare function scanState(projectRoot: string, catalog: Catalog, opts?: ScanOptions): Promise<StateReport>;
|
|
74
|
-
/**
|
|
75
|
-
* Dedupe plugins by `id`, merging dual-Agent records into a single
|
|
76
|
-
* multi-agent row. Aggregation rules:
|
|
77
|
-
*
|
|
78
|
-
* agents: union of all agent arrays for this id (claude before codex).
|
|
79
|
-
* status: installed ⇔ every agent's record is installed
|
|
80
|
-
* not-installed ⇔ every agent's record is not-installed
|
|
81
|
-
* otherwise → update-available (partial install or any agent
|
|
82
|
-
* with a pending update). One Apply covers all
|
|
83
|
-
* gaps because the handler iterates `agents`.
|
|
84
|
-
*
|
|
85
|
-
* Non-status fields (description, currentVersion, expectedVersion,
|
|
86
|
-
* versionSource) come from the first record we see. Today both sides report
|
|
87
|
-
* the same description (catalog-driven) and the same versions for any
|
|
88
|
-
* registry-pinned plugin, so this is safe; if a future divergence appears
|
|
89
|
-
* we'll need a deliberate merge policy.
|
|
90
|
-
*/
|
|
91
54
|
export declare function mergePluginsById(records: PluginState[]): PluginState[];
|
|
92
55
|
/** Default: run `claude plugins list --json` (no scope flag — the CLI
|
|
93
|
-
* doesn't expose one)
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
56
|
+
* doesn't expose one), then filter the installed records to the requested
|
|
57
|
+
* scope (and current projectRoot for project-scope) client-side. Server.ts
|
|
58
|
+
* decides whether to pass this function based on `which claude`.
|
|
59
|
+
*
|
|
60
|
+
* v1.19.0 dropped the parallel `--available` fetch — the scanner no longer
|
|
61
|
+
* compares versions, so there's no use for the upstream-live ref. */
|
|
97
62
|
export declare function defaultExecPluginList(scope?: ScanScope, projectRoot?: string): Promise<{
|
|
98
63
|
installed: any[];
|
|
99
|
-
available: any[];
|
|
100
64
|
}>;
|
|
101
65
|
export declare function defaultReadCodexConfig(): Promise<string | null>;
|
|
102
66
|
/** Walk `~/.codex/plugins/cache/<marketplace>/<plugin>/<version>/`, returning
|