auriga-cli 1.18.3 → 1.18.4

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.
@@ -63,6 +63,12 @@ export interface PluginState {
63
63
  * "user" (Codex has no project-scope plugin concept). See WorkflowState
64
64
  * comment on why this is typed optional. */
65
65
  observedScope?: ScanScope;
66
+ /** True for plugins whose source lives in an upstream marketplace, not in
67
+ * this repo (skill-creator / claude-md-management / codex). The scanner
68
+ * short-circuits update-available reporting for these — upgrades go
69
+ * through `claude plugins update`, not us. The UI renders an EXTERNAL
70
+ * badge to make the "not our jurisdiction" signal explicit. */
71
+ external?: boolean;
66
72
  }
67
73
  export interface HookState {
68
74
  name: string;
package/dist/catalog.d.ts CHANGED
@@ -9,9 +9,29 @@ export interface CatalogEntry {
9
9
  * because `plugins/<name>/.claude-plugin/plugin.json` is NOT shipped in
10
10
  * the npm tarball (see `package.json` `files` field). */
11
11
  expectedVersion?: string;
12
+ /** Build-time-baked agent map for plugin entries. Derived from
13
+ * `.claude/plugins.json` ∪ `.agents/plugins/install.json` — those config
14
+ * files are NOT shipped in the npm tarball, so the scanner can't read
15
+ * them at runtime. Baking here lets `/api/state` correctly classify
16
+ * dual-Agent plugins as `["claude","codex"]` for installed users.
17
+ * Absent on skill / hook entries. */
18
+ agents?: ("claude" | "codex")[];
19
+ /** True for plugins whose source lives in an UPSTREAM marketplace
20
+ * (skill-creator / claude-md-management / codex), not in this repo. The
21
+ * scanner uses this to disable update-available reporting — those
22
+ * plugins update through `claude plugins update`, not through us. UI
23
+ * surfaces an EXTERNAL badge so users know where to look. */
24
+ external?: boolean;
12
25
  }
13
26
  export interface Catalog {
14
27
  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;
15
35
  workflowSkills: CatalogEntry[];
16
36
  recommendedSkills: CatalogEntry[];
17
37
  plugins: CatalogEntry[];
package/dist/catalog.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
- "generatedAt": "2026-05-12T13:58:42.583Z",
2
+ "generatedAt": "2026-05-12T15:14:34.188Z",
3
+ "workflowVersion": "1.7.0",
3
4
  "workflowSkills": [
4
5
  {
5
6
  "name": "brainstorming",
@@ -75,34 +76,61 @@
75
76
  "plugins": [
76
77
  {
77
78
  "name": "skill-creator",
78
- "description": "Create and manage custom skills"
79
+ "description": "Create and manage custom skills",
80
+ "agents": [
81
+ "claude"
82
+ ],
83
+ "external": true
79
84
  },
80
85
  {
81
86
  "name": "claude-md-management",
82
- "description": "Audit and improve CLAUDE.md files"
87
+ "description": "Audit and improve CLAUDE.md files",
88
+ "agents": [
89
+ "claude"
90
+ ],
91
+ "external": true
83
92
  },
84
93
  {
85
94
  "name": "codex",
86
- "description": "Cross-model collaboration with Codex"
95
+ "description": "Cross-model collaboration with Codex",
96
+ "agents": [
97
+ "claude"
98
+ ],
99
+ "external": true
87
100
  },
88
101
  {
89
102
  "name": "auriga-go",
90
103
  "description": "(Claude/Codex) Workflow autopilot for the auriga workflow. Reminder-based navigation across CLAUDE.md's phases. Bundles a /goalify skill that plans an autonomous goal from a spec or work-in-progress and dispatches it via Claude Code's built-in /goal command.",
104
+ "agents": [
105
+ "claude",
106
+ "codex"
107
+ ],
91
108
  "expectedVersion": "1.1.0"
92
109
  },
93
110
  {
94
111
  "name": "auriga-git-guards",
95
112
  "description": "(Claude/Codex) Git lifecycle guardrails: commit-reminder + PR-create snapshot inject + PR-ready structural block. Bundles the git-workflow skill (Claude Code + Codex).",
113
+ "agents": [
114
+ "claude",
115
+ "codex"
116
+ ],
96
117
  "expectedVersion": "1.1.0"
97
118
  },
98
119
  {
99
120
  "name": "deep-review",
100
121
  "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.",
122
+ "agents": [
123
+ "claude",
124
+ "codex"
125
+ ],
101
126
  "expectedVersion": "0.3.1"
102
127
  },
103
128
  {
104
129
  "name": "session-instructions-loader",
105
130
  "description": "(Codex) Injects extra instruction files on session start",
131
+ "agents": [
132
+ "codex"
133
+ ],
106
134
  "expectedVersion": "1.0.0"
107
135
  }
108
136
  ],
@@ -1,37 +1,28 @@
1
- // Build the scan-time Catalog (the shape src/state.ts consumes) from
2
- // auriga-cli's installed package state. This bridges the build-time
3
- // `dist/catalog.json` (which carries names + descriptions for the menu)
4
- // and the runtime scanner's need for expected hashes + versions.
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.
5
9
  //
6
- // Inputs (all under packageRoot):
7
- // dist/catalog.json — names + descriptions for 5 categories
8
- // skills-lock.json — expected SHA256 for every vendored skill
9
- // .claude/plugins.json — Claude plugin entries (agent = "claude")
10
- // .agents/plugins/install.json — Codex plugin entries (agent = "codex")
11
- // .claude/hooks/hooks.json — registers settingsEvents per hook (event /
12
- // matcher / if) used by state.ts for drift
13
- // detection in <scope>/.claude/settings.json
14
- // CLAUDE.md — `# auriga Workflow (vX.Y.Z)` provides
15
- // workflowVersion
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`.
16
12
  //
17
- // Anything missing is treated as "no expectation" (empty hash / version)
18
- // rather than throwing; scanState will still produce a structurally valid
19
- // StateReportitems just classify as not-installed or installed
20
- // depending on whether the user-side data exists.
21
- import { createHash } from "node:crypto";
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).
22
23
  import { readFile } from "node:fs/promises";
23
24
  import path from "node:path";
24
25
  import { loadCatalog } from "./catalog.js";
25
- async function sha256SkillMd(skillsRoot, name) {
26
- try {
27
- const buf = await readFile(path.join(skillsRoot, name, "SKILL.md"));
28
- return createHash("sha256").update(buf).digest("hex");
29
- }
30
- catch {
31
- return "";
32
- }
33
- }
34
- const WORKFLOW_VERSION_RE = /^#\s*auriga Workflow\s*\(v([\d.]+)\)/m;
35
26
  async function tryReadFile(p) {
36
27
  try {
37
28
  return await readFile(p, "utf8");
@@ -42,24 +33,21 @@ async function tryReadFile(p) {
42
33
  }
43
34
  export async function buildScanCatalog(packageRoot) {
44
35
  const dist = loadCatalog(packageRoot);
45
- // Workflow version: parse from auriga-cli's own CLAUDE.md template.
46
- // If missing, leave as empty string so workflow always classifies as
47
- // not-installed (no expectation set).
48
- const claudeMd = await tryReadFile(path.join(packageRoot, "CLAUDE.md"));
49
- const m = claudeMd ? WORKFLOW_VERSION_RE.exec(claudeMd) : null;
50
- const workflowVersion = m ? m[1] : "";
51
- // Skills + recommended: sha256 of each shipped SKILL.md. This is the same
52
- // hash the scanner computes for `<scope>/.claude/skills/<name>/SKILL.md`
53
- // at scan time, so a match means "user's installed copy is identical to
54
- // the version auriga-cli ships". skills-lock.json's `computedHash` field
55
- // hashes the entire skill directory (every file, sorted), which doesn't
56
- // line up with the scanner's per-file model — we deliberately ignore it.
57
- const skillsRoot = path.join(packageRoot, ".agents", "skills");
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.
58
46
  const skills = {};
59
47
  for (const entry of dist.workflowSkills) {
60
48
  skills[entry.name] = {
61
49
  description: entry.description,
62
- expectedHash: await sha256SkillMd(skillsRoot, entry.name),
50
+ expectedHash: "",
63
51
  isWorkflow: true,
64
52
  };
65
53
  }
@@ -67,73 +55,37 @@ export async function buildScanCatalog(packageRoot) {
67
55
  for (const entry of dist.recommendedSkills) {
68
56
  recommendedSkills[entry.name] = {
69
57
  description: entry.description,
70
- expectedHash: await sha256SkillMd(skillsRoot, entry.name),
58
+ expectedHash: "",
71
59
  };
72
60
  }
73
- // Plugins: split by agent based on which config file lists them. A
74
- // plugin can appear in both registries (cross-agent plugins like
75
- // auriga-go); we represent it once per agent.
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).
76
68
  const plugins = {};
77
- const claudePluginsText = await tryReadFile(path.join(packageRoot, ".claude", "plugins.json"));
78
- const claudeNames = new Set();
79
- if (claudePluginsText) {
80
- try {
81
- const parsed = JSON.parse(claudePluginsText);
82
- for (const p of parsed.plugins ?? []) {
83
- if (p.name)
84
- claudeNames.add(p.name);
85
- }
86
- }
87
- catch {
88
- /* ignore */
89
- }
90
- }
91
- const codexInstallText = await tryReadFile(path.join(packageRoot, ".agents", "plugins", "install.json"));
92
- const codexNames = new Set();
93
- if (codexInstallText) {
94
- try {
95
- const parsed = JSON.parse(codexInstallText);
96
- for (const p of parsed.plugins ?? []) {
97
- if (p.name)
98
- codexNames.add(p.name);
99
- }
100
- }
101
- catch {
102
- /* ignore */
103
- }
104
- }
105
69
  for (const entry of dist.plugins) {
106
- // Collect every agent that registers this plugin. A plugin can ship in
107
- // both registries (cross-agent plugins like auriga-go); we emit it as
108
- // a single multi-agent record so the UI shows one row + BOTH badge and
109
- // Apply installs to each side.
110
- const agents = [];
111
- if (claudeNames.has(entry.name))
112
- agents.push("claude");
113
- if (codexNames.has(entry.name))
114
- agents.push("codex");
115
- if (agents.length === 0)
116
- agents.push("claude"); // unknown defaults to claude
117
- // expectedVersion comes from the build-time-baked field in dist/catalog.json
118
- // (populated by `src/build/generate-catalog.ts` from
119
- // `plugins/<name>/.claude-plugin/plugin.json`). It MUST be baked at build
120
- // time because `plugins/<name>/` is NOT shipped in the npm tarball
121
- // (`package.json` `files` field allowlists only `dist/`), so reading it
122
- // at runtime from packageRoot would silently fail for npm-installed users.
70
+ const agents = Array.isArray(entry.agents) && entry.agents.length > 0
71
+ ? [...entry.agents]
72
+ : ["claude"]; // safety fallback: unknown shape defaults to claude
123
73
  plugins[entry.name] = {
124
74
  description: entry.description,
125
75
  agents,
126
76
  ...(typeof entry.expectedVersion === "string" && entry.expectedVersion.length > 0
127
77
  ? { expectedVersion: entry.expectedVersion }
128
78
  : {}),
79
+ ...(entry.external === true ? { external: true } : {}),
129
80
  };
130
81
  }
131
- // Hooks: the scanner reads <scope>/.claude/settings.json and matches by
132
- // `_marker` (see state.ts scanHooks). Drift detection compares the
133
- // registered event / matcher / if values against the hook's canonical
134
- // settingsEvents[0] from .claude/hooks/hooks.json. We deliberately do NOT
135
- // hash index.mjs the user's installed index.mjs lives at <scope>/.claude/
136
- // hooks/<name>/index.mjs and isn't part of the settings.json drift signal.
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.
137
89
  const hooksJsonPath = path.join(packageRoot, ".claude", "hooks", "hooks.json");
138
90
  const hooksJsonRaw = await tryReadFile(hooksJsonPath);
139
91
  const hooksJson = hooksJsonRaw ? JSON.parse(hooksJsonRaw) : {};
package/dist/state.d.ts CHANGED
@@ -15,6 +15,12 @@ export interface Catalog {
15
15
  /** Agents this plugin can install into. Length 1 or 2. */
16
16
  agents: ("claude" | "codex")[];
17
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. */
23
+ external?: boolean;
18
24
  }>;
19
25
  hooks: Record<string, {
20
26
  description: string;
package/dist/state.js CHANGED
@@ -373,9 +373,13 @@ function degradedClaudeRow(id, def, scope) {
373
373
  expectedVersion: def.expectedVersion,
374
374
  versionSource: "upstream-live",
375
375
  observedScope: scope,
376
+ ...(def.external === true ? { external: true } : {}),
376
377
  };
377
378
  }
378
379
  function classifyClaudePlugin(id, def, installed, available, scope) {
380
+ // `external` propagates onto every return below so the UI can surface the
381
+ // EXTERNAL badge regardless of install state.
382
+ const externalFlag = def.external === true ? { external: true } : {};
379
383
  if (!installed || typeof installed.version !== "string") {
380
384
  return {
381
385
  id,
@@ -385,9 +389,31 @@ function classifyClaudePlugin(id, def, installed, available, scope) {
385
389
  expectedVersion: typeof available?.source?.ref === "string" ? available.source.ref : def.expectedVersion,
386
390
  versionSource: "upstream-live",
387
391
  observedScope: scope,
392
+ ...externalFlag,
388
393
  };
389
394
  }
390
395
  const installedVersion = installed.version;
396
+ // External plugin short-circuit: we don't own these, so we don't claim
397
+ // authority on "what version they should be at". `claude plugins update`
398
+ // is the right channel — the scanner just confirms presence. Status stays
399
+ // "installed" even if installed.version differs from any signal we have.
400
+ // The catalog deliberately omits `expectedVersion` for externals, but we
401
+ // double-down with this guard so a future regression that accidentally
402
+ // populates expectedVersion still can't flip externals to update-available.
403
+ if (def.external === true) {
404
+ return {
405
+ id,
406
+ description: def.description,
407
+ status: "installed",
408
+ agents: ["claude"],
409
+ currentVersion: installedVersion,
410
+ // Don't surface any "expected" on externals — the upstream tool owns
411
+ // the version conversation.
412
+ versionSource: "upstream-live",
413
+ observedScope: scope,
414
+ ...externalFlag,
415
+ };
416
+ }
391
417
  const ref = available?.source?.ref;
392
418
  const normalizedAvailable = parseRef(typeof ref === "string" ? ref : undefined);
393
419
  const normalizedInstalled = parseRef(installedVersion);
@@ -421,6 +447,7 @@ function classifyClaudePlugin(id, def, installed, available, scope) {
421
447
  expectedVersion: expectedRaw,
422
448
  versionSource,
423
449
  observedScope: scope,
450
+ ...externalFlag,
424
451
  };
425
452
  }
426
453
  if (normalizedInstalled !== null && normalizedInstalled === expectedNormalized) {
@@ -433,6 +460,7 @@ function classifyClaudePlugin(id, def, installed, available, scope) {
433
460
  expectedVersion: expectedRaw,
434
461
  versionSource,
435
462
  observedScope: scope,
463
+ ...externalFlag,
436
464
  };
437
465
  }
438
466
  return {
@@ -444,6 +472,7 @@ function classifyClaudePlugin(id, def, installed, available, scope) {
444
472
  expectedVersion: expectedRaw,
445
473
  versionSource,
446
474
  observedScope: scope,
475
+ ...externalFlag,
447
476
  };
448
477
  }
449
478
  // ---------------------------------------------------------------------------
@@ -529,10 +558,12 @@ function degradedCodexRow(id, def) {
529
558
  expectedVersion: def.expectedVersion,
530
559
  versionSource: "catalog",
531
560
  observedScope: "user",
561
+ ...(def.external === true ? { external: true } : {}),
532
562
  };
533
563
  }
534
564
  function classifyCodexPlugin(id, def, enabled, fsVersion) {
535
565
  const expectedVersion = def.expectedVersion;
566
+ const externalFlag = def.external === true ? { external: true } : {};
536
567
  if (!enabled) {
537
568
  return {
538
569
  id,
@@ -542,6 +573,7 @@ function classifyCodexPlugin(id, def, enabled, fsVersion) {
542
573
  expectedVersion,
543
574
  versionSource: "catalog",
544
575
  observedScope: "user",
576
+ ...externalFlag,
545
577
  };
546
578
  }
547
579
  if (!fsVersion) {
@@ -553,6 +585,22 @@ function classifyCodexPlugin(id, def, enabled, fsVersion) {
553
585
  expectedVersion,
554
586
  versionSource: "catalog",
555
587
  observedScope: "user",
588
+ ...externalFlag,
589
+ };
590
+ }
591
+ // External plugin short-circuit, same rationale as classifyClaudePlugin:
592
+ // we defer authority to `codex plugin marketplace update` and never flag
593
+ // update-available for upstream-owned plugins.
594
+ if (def.external === true) {
595
+ return {
596
+ id,
597
+ description: def.description,
598
+ status: "installed",
599
+ agents: ["codex"],
600
+ currentVersion: fsVersion,
601
+ versionSource: "catalog",
602
+ observedScope: "user",
603
+ ...externalFlag,
556
604
  };
557
605
  }
558
606
  if (!expectedVersion || fsVersion === expectedVersion) {
@@ -565,6 +613,7 @@ function classifyCodexPlugin(id, def, enabled, fsVersion) {
565
613
  expectedVersion,
566
614
  versionSource: "catalog",
567
615
  observedScope: "user",
616
+ ...externalFlag,
568
617
  };
569
618
  }
570
619
  return {
@@ -576,6 +625,7 @@ function classifyCodexPlugin(id, def, enabled, fsVersion) {
576
625
  expectedVersion,
577
626
  versionSource: "catalog",
578
627
  observedScope: "user",
628
+ ...externalFlag,
579
629
  };
580
630
  }
581
631
  /** Return the set of plugin ids whose `[plugins."<id>"]` table has
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auriga-cli",
3
- "version": "1.18.3",
3
+ "version": "1.18.4",
4
4
  "description": "Interactive CLI to install Claude Code harness modules (Workflow, Skills, Recommended Skills, Plugins, Hooks)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,8 +25,8 @@
25
25
  "dev": "tsc --watch",
26
26
  "start": "node dist/cli.js",
27
27
  "pretest": "npm run build",
28
- "test": "tsc -p tsconfig.test.json && DEV=1 node --test --experimental-test-module-mocks dist-test/tests/hooks.test.js dist-test/tests/hooks-uninstall.test.js dist-test/tests/skills.test.js dist-test/tests/skills-uninstall.test.js dist-test/tests/catalog.test.js dist-test/tests/cli-parse.test.js dist-test/tests/install-nontty.test.js dist-test/tests/plugins.test.js dist-test/tests/plugins-uninstall.test.js dist-test/tests/content-fetch.test.js dist-test/tests/utils.test.js dist-test/tests/guide.test.js dist-test/tests/validators.test.js dist-test/tests/entrypoint.test.js dist-test/tests/state.test.js dist-test/tests/server.test.js dist-test/tests/server-auth.test.js dist-test/tests/server-apply.test.js dist-test/tests/apply-handlers.test.js dist-test/tests/ui-fetch.test.js dist-test/tests/workflow-uninstall.test.js",
29
- "test:watch": "tsc -p tsconfig.test.json --watch & node --test --watch --experimental-test-module-mocks dist-test/tests/hooks.test.js dist-test/tests/hooks-uninstall.test.js dist-test/tests/skills.test.js dist-test/tests/skills-uninstall.test.js dist-test/tests/catalog.test.js dist-test/tests/cli-parse.test.js dist-test/tests/install-nontty.test.js dist-test/tests/plugins.test.js dist-test/tests/plugins-uninstall.test.js dist-test/tests/content-fetch.test.js dist-test/tests/utils.test.js dist-test/tests/guide.test.js dist-test/tests/validators.test.js dist-test/tests/entrypoint.test.js dist-test/tests/state.test.js dist-test/tests/server.test.js dist-test/tests/server-auth.test.js dist-test/tests/server-apply.test.js dist-test/tests/apply-handlers.test.js dist-test/tests/ui-fetch.test.js dist-test/tests/workflow-uninstall.test.js",
28
+ "test": "tsc -p tsconfig.test.json && DEV=1 node --test --experimental-test-module-mocks dist-test/tests/hooks.test.js dist-test/tests/hooks-uninstall.test.js dist-test/tests/skills.test.js dist-test/tests/skills-uninstall.test.js dist-test/tests/catalog.test.js dist-test/tests/cli-parse.test.js dist-test/tests/install-nontty.test.js dist-test/tests/plugins.test.js dist-test/tests/plugins-uninstall.test.js dist-test/tests/content-fetch.test.js dist-test/tests/utils.test.js dist-test/tests/guide.test.js dist-test/tests/validators.test.js dist-test/tests/entrypoint.test.js dist-test/tests/state.test.js dist-test/tests/server.test.js dist-test/tests/server-auth.test.js dist-test/tests/server-apply.test.js dist-test/tests/apply-handlers.test.js dist-test/tests/ui-fetch.test.js dist-test/tests/workflow-uninstall.test.js dist-test/tests/tarball-shape.test.js",
29
+ "test:watch": "tsc -p tsconfig.test.json --watch & node --test --watch --experimental-test-module-mocks dist-test/tests/hooks.test.js dist-test/tests/hooks-uninstall.test.js dist-test/tests/skills.test.js dist-test/tests/skills-uninstall.test.js dist-test/tests/catalog.test.js dist-test/tests/cli-parse.test.js dist-test/tests/install-nontty.test.js dist-test/tests/plugins.test.js dist-test/tests/plugins-uninstall.test.js dist-test/tests/content-fetch.test.js dist-test/tests/utils.test.js dist-test/tests/guide.test.js dist-test/tests/validators.test.js dist-test/tests/entrypoint.test.js dist-test/tests/state.test.js dist-test/tests/server.test.js dist-test/tests/server-auth.test.js dist-test/tests/server-apply.test.js dist-test/tests/apply-handlers.test.js dist-test/tests/ui-fetch.test.js dist-test/tests/workflow-uninstall.test.js dist-test/tests/tarball-shape.test.js",
30
30
  "pretest:e2e": "npm run build",
31
31
  "test:e2e": "tsc -p tsconfig.test.json && node --test dist-test/tests/e2e-install.test.js",
32
32
  "pretest:web-ui-e2e": "npm run build && npm --prefix ui ci && npm --prefix ui run build",