auriga-cli 1.18.5 → 1.20.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/dist/guide.js CHANGED
@@ -20,7 +20,7 @@ export function renderGuide(opts) {
20
20
  return `${h(`# auriga-cli bootstrap SOP (v${opts.version})`)}
21
21
 
22
22
  This guide walks an Agent through installing the auriga harness
23
- (CLAUDE.md + skills + plugins + hooks) into the current repository.
23
+ (CLAUDE.md + skills + plugins, plus any legacy hooks) into the current repository.
24
24
 
25
25
  Run each step in order. If any step fails with exit 1, stop and report.
26
26
  If exit 2, see stderr for per-category status and follow the "Retry"
@@ -62,8 +62,8 @@ Per-type detail (flags + only that category's catalog slice):
62
62
 
63
63
  ${h("## Step 3 — Install")}
64
64
 
65
- Preset — the full default-on set (workflow + skills + plugins + hooks;
66
- recommended skills are NOT included):
65
+ Preset — the full default-on set (workflow + skills + default plugins;
66
+ the legacy hooks category is currently empty; recommended skills are NOT included):
67
67
  ${cmd("npx -y auriga-cli install --all")}
68
68
 
69
69
  Targeted — single category, picking from the catalog surfaced in Step 2:
@@ -72,10 +72,10 @@ Targeted — single category, picking from the catalog surfaced in Step 2:
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
74
 
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:
78
- ${cmd("npx -y auriga-cli install hooks --hook notify")}
75
+ Opt-in plugins (\`defaultOn: false\`) are NOT in the default \`install --all\`
76
+ set because they have side effects or platform-specific behavior. For example,
77
+ the macOS notification plugin is explicit opt-in:
78
+ ${cmd("npx -y auriga-cli install plugins --plugin auriga-notify")}
79
79
 
80
80
  Opt-in recommended skills (cross-model delegation helpers —
81
81
  claude-code-agent, codex-agent):
@@ -94,7 +94,7 @@ Exit codes:
94
94
  ${h("## Step 4 — Reload session (REQUIRED when installed non-interactively)")}
95
95
 
96
96
  ${warn("⚠")} CLAUDE.md, .agents/skills/, .claude/plugins.json, Codex plugin
97
- config, and hook registrations are loaded at session startup. If you ran
97
+ config, and hook/plugin registrations are loaded at session startup. If you ran
98
98
  \`npx -y auriga-cli install\` inside an existing Claude Code or Codex session
99
99
  (e.g., \`claude -p\` / \`claude -p --worktree\` / \`codex exec\`), the current session
100
100
  will NOT see the new harness.
@@ -112,7 +112,8 @@ Expected artifacts:
112
112
  - .agents/skills/<name>/ (one per installed skill)
113
113
  - .claude/plugins.json
114
114
  - ~/.codex/config.toml (Codex plugin enablement, if Codex plugins selected)
115
- - .claude/settings.json (updated hook registrations, if hooks selected)
115
+ - .claude/settings.json (updated hook/plugin registrations, if selected)
116
+ - .claude/auriga-notify/ (project notify config, if auriga-notify selected)
116
117
 
117
118
  ${h("## Troubleshooting")}
118
119
 
package/dist/help.js CHANGED
@@ -125,7 +125,7 @@ USAGE
125
125
 
126
126
  FLAGS
127
127
  --plugin <names...> space-separated; '*' = all
128
- omit → install every plugin available for the selected agent
128
+ omit → install every plugin with defaultOn != false
129
129
  --agent <...> target runtime: claude, codex, or both
130
130
  default claude; codex enablement is user-level
131
131
  --scope <project|user> default project
package/dist/hooks.js CHANGED
@@ -687,7 +687,12 @@ export async function installHooks(packageRoot, opts) {
687
687
  const config = loadHooksConfig(packageRoot);
688
688
  const compatible = config.hooks.filter((h) => h.runtimePlatforms.includes(process.platform));
689
689
  if (compatible.length === 0) {
690
- log.warn(`No hooks available for your platform (${process.platform}). Skipping.`);
690
+ if (config.hooks.length === 0) {
691
+ log.warn("No legacy hooks are defined. The notify hook moved to the opt-in auriga-notify plugin.");
692
+ }
693
+ else {
694
+ log.warn(`No hooks available for your platform (${process.platform}). Skipping.`);
695
+ }
691
696
  return;
692
697
  }
693
698
  // Non-interactive explicit `--hook <name>` has stronger intent than
package/dist/plugins.js CHANGED
@@ -15,6 +15,14 @@ import { atomicWriteFile, exec, execAsync, fetchExtraContent, log, withEsc } fro
15
15
  // `./marketplace.js` so Claude and Codex sides share one validator.
16
16
  const PLUGIN_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
+ const MIGRATED_WORKFLOW_SKILLS = [
19
+ "incremental-impl",
20
+ "test-designer",
21
+ "session-compound",
22
+ ];
23
+ const NOTIFY_PLUGIN_NAME = "auriga-notify";
24
+ const WORKFLOW_SKILLS_PLUGIN_NAME = "auriga-workflow-skills";
25
+ const LEGACY_NOTIFY_MARKER = "auriga:notify";
18
26
  export function validatePluginsConfig(raw) {
19
27
  if (!raw || typeof raw !== "object") {
20
28
  throw new Error("plugins.json: root must be an object");
@@ -37,6 +45,9 @@ export function validatePluginsConfig(raw) {
37
45
  if (plugin.marketplace !== undefined) {
38
46
  validateMarketplaceField(`plugins.json: plugins[${i}]`, plugin.marketplace);
39
47
  }
48
+ if (plugin.defaultOn !== undefined && typeof plugin.defaultOn !== "boolean") {
49
+ throw new Error(`plugins.json: plugins[${i}].defaultOn must be a boolean`);
50
+ }
40
51
  });
41
52
  }
42
53
  function getInstalledPlugins() {
@@ -60,12 +71,14 @@ function getInstalledPlugins() {
60
71
  }
61
72
  }
62
73
  /**
63
- * Non-interactive selection resolver for plugins. Mirrors the skills
64
- * resolveSelected: `undefined` / `["*"]` = full set; explicit names =
65
- * filter. CLI parser validates names up-front.
74
+ * Non-interactive selection resolver for plugins.
75
+ * `undefined` = default-on set; `["*"]` = full set; explicit names =
76
+ * exact filter. CLI parser validates names up-front.
66
77
  */
67
78
  function resolvePluginSelection(all, selected) {
68
- if (!selected || (selected.length === 1 && selected[0] === "*"))
79
+ if (!selected)
80
+ return all.filter((p) => p.defaultOn !== false);
81
+ if (selected.length === 1 && selected[0] === "*")
69
82
  return all;
70
83
  const byName = new Map(all.map((p) => [p.name, p]));
71
84
  const missing = selected.filter((name) => !byName.has(name));
@@ -121,6 +134,164 @@ function codexHome() {
121
134
  function shellQuote(value) {
122
135
  return `'${value.replace(/'/g, "'\\''")}'`;
123
136
  }
137
+ function installTargetCwd(opts) {
138
+ return path.resolve(opts.cwd ?? process.cwd());
139
+ }
140
+ function emitMigrationLog(opts, line) {
141
+ opts.onLog?.(line, "stdout");
142
+ }
143
+ function runtimeSkillRoot(runtime) {
144
+ return runtime === "claude" ? ".claude" : ".agents";
145
+ }
146
+ function legacySkillDir(opts, runtime, name) {
147
+ const cwd = installTargetCwd(opts);
148
+ const scope = opts.scope ?? "project";
149
+ const baseDir = scope === "user" ? os.homedir() : cwd;
150
+ return path.join(baseDir, runtimeSkillRoot(runtime), "skills", name);
151
+ }
152
+ function isWorkflowPluginDevSymlink(skillPath, cwd, name) {
153
+ const stat = fs.lstatSync(skillPath, { throwIfNoEntry: false });
154
+ if (!stat?.isSymbolicLink())
155
+ return false;
156
+ const target = fs.readlinkSync(skillPath);
157
+ const resolved = path.resolve(path.dirname(skillPath), target);
158
+ const expected = path.resolve(cwd, "plugins", "auriga-workflow-skills", "skills", name);
159
+ return resolved === expected;
160
+ }
161
+ function removeMigratedSkillFromLock(cwd, name, opts) {
162
+ const lockPath = path.join(cwd, "skills-lock.json");
163
+ if (!fs.existsSync(lockPath))
164
+ return;
165
+ const raw = JSON.parse(fs.readFileSync(lockPath, "utf-8"));
166
+ if (!raw.skills || typeof raw.skills !== "object" || !(name in raw.skills))
167
+ return;
168
+ const nextSkills = { ...raw.skills };
169
+ delete nextSkills[name];
170
+ atomicWriteFile(lockPath, JSON.stringify({ ...raw, skills: nextSkills }, null, 2) + "\n");
171
+ emitMigrationLog(opts, `removed ${name} from skills-lock.json`);
172
+ }
173
+ function cleanupMigratedWorkflowSkillInstalls(opts, runtimes) {
174
+ const cwd = installTargetCwd(opts);
175
+ const scope = opts.scope ?? "project";
176
+ for (const name of MIGRATED_WORKFLOW_SKILLS) {
177
+ for (const runtime of runtimes) {
178
+ const dir = legacySkillDir(opts, runtime, name);
179
+ const stat = fs.lstatSync(dir, { throwIfNoEntry: false });
180
+ if (!stat) {
181
+ emitMigrationLog(opts, `${runtimeSkillRoot(runtime)}/skills/${name} not present`);
182
+ continue;
183
+ }
184
+ if (scope === "project" && isWorkflowPluginDevSymlink(dir, cwd, name)) {
185
+ emitMigrationLog(opts, `preserved ${runtimeSkillRoot(runtime)}/skills/${name} development symlink`);
186
+ continue;
187
+ }
188
+ fs.rmSync(dir, { recursive: true, force: true });
189
+ emitMigrationLog(opts, `removed ${runtimeSkillRoot(runtime)}/skills/${name}`);
190
+ }
191
+ if (scope === "project")
192
+ removeMigratedSkillFromLock(cwd, name, opts);
193
+ }
194
+ }
195
+ function copyIfPresentWithoutOverwrite(src, dest) {
196
+ if (!fs.existsSync(src) || fs.existsSync(dest))
197
+ return false;
198
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
199
+ fs.copyFileSync(src, dest);
200
+ return true;
201
+ }
202
+ function removeMarkerFromSettings(settings, marker) {
203
+ const next = JSON.parse(JSON.stringify(settings ?? {}));
204
+ if (!next.hooks || typeof next.hooks !== "object" || Array.isArray(next.hooks)) {
205
+ return { settings: next, removed: 0 };
206
+ }
207
+ let removed = 0;
208
+ for (const event of Object.keys(next.hooks)) {
209
+ const groups = next.hooks[event];
210
+ if (!Array.isArray(groups))
211
+ continue;
212
+ const nextGroups = [];
213
+ for (const group of groups) {
214
+ if (!Array.isArray(group.hooks)) {
215
+ nextGroups.push(group);
216
+ continue;
217
+ }
218
+ const hooks = group.hooks.filter((action) => {
219
+ if (action?._marker === marker) {
220
+ removed += 1;
221
+ return false;
222
+ }
223
+ return true;
224
+ });
225
+ if (hooks.length > 0)
226
+ nextGroups.push({ ...group, hooks });
227
+ }
228
+ if (nextGroups.length > 0)
229
+ next.hooks[event] = nextGroups;
230
+ else
231
+ delete next.hooks[event];
232
+ }
233
+ return { settings: next, removed };
234
+ }
235
+ function cleanLegacyNotifySettings(settingsPaths, opts) {
236
+ let allReadable = true;
237
+ for (const settingsPath of settingsPaths) {
238
+ if (!fs.existsSync(settingsPath))
239
+ continue;
240
+ let parsed;
241
+ try {
242
+ parsed = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
243
+ }
244
+ catch {
245
+ emitMigrationLog(opts, `skipped unreadable legacy notify settings: ${settingsPath}`);
246
+ allReadable = false;
247
+ continue;
248
+ }
249
+ const result = removeMarkerFromSettings(parsed, LEGACY_NOTIFY_MARKER);
250
+ if (result.removed === 0)
251
+ continue;
252
+ atomicWriteFile(settingsPath, JSON.stringify(result.settings, null, 2) + "\n");
253
+ emitMigrationLog(opts, `removed ${result.removed} legacy notify settings entries from ${settingsPath}`);
254
+ }
255
+ return allReadable;
256
+ }
257
+ function migrateLegacyNotifyConfig(opts) {
258
+ const scope = opts.scope ?? "project";
259
+ const cwd = installTargetCwd(opts);
260
+ const home = os.homedir();
261
+ const legacyBase = scope === "user" ? home : cwd;
262
+ const legacyDir = path.join(legacyBase, ".claude", "hooks", "notify");
263
+ const destDir = scope === "user"
264
+ ? path.join(home, ".config", "auriga-cli", "notify")
265
+ : path.join(cwd, ".claude", "auriga-notify");
266
+ const copiedConfig = copyIfPresentWithoutOverwrite(path.join(legacyDir, "config.json"), path.join(destDir, "config.json"));
267
+ const copiedIcon = copyIfPresentWithoutOverwrite(path.join(legacyDir, "icon.png"), path.join(destDir, "icon.png"));
268
+ if (copiedConfig)
269
+ emitMigrationLog(opts, `migrated legacy notify config to ${path.join(destDir, "config.json")}`);
270
+ if (copiedIcon)
271
+ emitMigrationLog(opts, `migrated legacy notify icon to ${path.join(destDir, "icon.png")}`);
272
+ const settingsPaths = scope === "user"
273
+ ? [path.join(home, ".claude", "settings.json")]
274
+ : [
275
+ path.join(cwd, ".claude", "settings.json"),
276
+ path.join(cwd, ".claude", "settings.local.json"),
277
+ ];
278
+ const settingsCleaned = cleanLegacyNotifySettings(settingsPaths, opts);
279
+ if (settingsCleaned && fs.existsSync(legacyDir)) {
280
+ fs.rmSync(legacyDir, { recursive: true, force: true });
281
+ emitMigrationLog(opts, `removed legacy notify hook directory ${legacyDir}`);
282
+ }
283
+ else if (!settingsCleaned && fs.existsSync(legacyDir)) {
284
+ emitMigrationLog(opts, `kept legacy notify hook directory because settings cleanup was incomplete: ${legacyDir}`);
285
+ }
286
+ }
287
+ function runPostInstallMigration(pluginName, opts, runtimes) {
288
+ if (pluginName === WORKFLOW_SKILLS_PLUGIN_NAME) {
289
+ cleanupMigratedWorkflowSkillInstalls(opts, runtimes);
290
+ }
291
+ if (pluginName === NOTIFY_PLUGIN_NAME) {
292
+ migrateLegacyNotifyConfig(opts);
293
+ }
294
+ }
124
295
  function codexMarketplaceAddCommand(packageRoot) {
125
296
  if (process.env.DEV === "1") {
126
297
  return `codex plugin marketplace add ${shellQuote(packageRoot)}`;
@@ -401,6 +572,7 @@ async function installCodexPlugins(packageRoot, opts) {
401
572
  enableCodexPluginConfig(path.join(codexHome(), "config.toml"), pluginKeys, needsPluginHooks);
402
573
  for (const plugin of [...localSelected, ...externalSelected]) {
403
574
  log.ok(`${plugin.name} enabled for Codex`);
575
+ runPostInstallMigration(plugin.name, opts, ["codex"]);
404
576
  }
405
577
  }
406
578
  if (failures.length > 0 && !opts.interactive) {
@@ -490,10 +662,11 @@ export async function installPlugins(packageRoot, opts) {
490
662
  choices: config.plugins.map((p) => {
491
663
  const scopes = installed.get(p.package);
492
664
  const suffix = scopes ? ` (installed: ${scopes.join(", ")})` : "";
665
+ const installedEverywhere = scopes?.includes("user") && scopes?.includes("project");
493
666
  return {
494
667
  name: `${p.name} — ${p.description}${suffix}`,
495
668
  value: p,
496
- checked: !scopes || !(scopes.includes("user") && scopes.includes("project")),
669
+ checked: p.defaultOn !== false && !installedEverywhere,
497
670
  };
498
671
  }),
499
672
  }))
@@ -572,14 +745,16 @@ export async function installPlugins(packageRoot, opts) {
572
745
  console.log(`\nInstalling ${plugin.name}...`);
573
746
  try {
574
747
  const cmd = `claude plugins install ${plugin.package} --scope ${scope}`;
748
+ const cmdOpts = { cwd: installTargetCwd(opts) };
575
749
  if (opts.onLog) {
576
750
  opts.onLog(`▸ ${cmd}`, "stdout");
577
- await execAsync(cmd, { onLine: opts.onLog });
751
+ await execAsync(cmd, { ...cmdOpts, onLine: opts.onLog });
578
752
  }
579
753
  else {
580
- exec(cmd, { inherit: true });
754
+ exec(cmd, { ...cmdOpts, inherit: true });
581
755
  }
582
756
  log.ok(`${plugin.name} installed`);
757
+ runPostInstallMigration(plugin.name, { ...opts, scope }, ["claude"]);
583
758
  }
584
759
  catch {
585
760
  log.error(`Failed to install: ${plugin.name}`);
@@ -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`. This module is intentionally a *thin
3
- // adapter* it must NOT read any file outside `dist/catalog.json`, because
4
- // the npm tarball's `files` field allowlists only `dist/`. Reading from
5
- // `packageRoot/CLAUDE.md`, `packageRoot/.claude/plugins.json`, or
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
- // Workflow version baked from CLAUDE.md header at build time. See
37
- // module comment for the "no runtime reads outside dist/" rule.
38
- const workflowVersion = dist.workflowVersion ?? "";
39
- // Skills: drift detection deliberately deferred to `npx skills update
40
- // --project`, which already compares against the skill's own upstream
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
- const ev = settingsEventsByName.get(entry.name);
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", "update", "uninstall"]);
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/skills.js CHANGED
@@ -8,12 +8,9 @@ import { atomicWriteFile, exec, execAsync, log, withEsc } from "./utils.js";
8
8
  // installRecommendedSkills as an opt-in utility.
9
9
  export const WORKFLOW_SKILLS = [
10
10
  "brainstorming",
11
- "incremental-impl",
12
11
  "planning-with-files",
13
12
  "playwright-cli",
14
- "session-compound",
15
13
  "systematic-debugging",
16
- "test-designer",
17
14
  "test-driven-development",
18
15
  "verification-before-completion",
19
16
  ];
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
- expectedVersion?: string;
18
- /** When true, this plugin is published in an UPSTREAM marketplace
19
- * (skill-creator / claude-md-management / codex), not in this repo.
20
- * Classifier MUST NOT report `update-available` for external plugins
21
- * those upgrade through `claude plugins update`, not us. The UI surfaces
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>>;
@@ -73,13 +53,14 @@ export interface ScanOptions {
73
53
  export declare function scanState(projectRoot: string, catalog: Catalog, opts?: ScanOptions): Promise<StateReport>;
74
54
  export declare function mergePluginsById(records: PluginState[]): PluginState[];
75
55
  /** Default: run `claude plugins list --json` (no scope flag — the CLI
76
- * doesn't expose one) plus the `--available` variant, then filter the
77
- * installed records to the requested scope (and current projectRoot for
78
- * project-scope) client-side. Server.ts decides whether to pass this
79
- * function based on `which claude`. */
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. */
80
62
  export declare function defaultExecPluginList(scope?: ScanScope, projectRoot?: string): Promise<{
81
63
  installed: any[];
82
- available: any[];
83
64
  }>;
84
65
  export declare function defaultReadCodexConfig(): Promise<string | null>;
85
66
  /** Walk `~/.codex/plugins/cache/<marketplace>/<plugin>/<version>/`, returning