@ory/claude-code 0.13.9 → 1.0.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/cli/setup.js CHANGED
@@ -3,11 +3,20 @@
3
3
  /**
4
4
  * Setup CLI for the Ory Claude Code plugin.
5
5
  *
6
- * Uses the `claude` CLI to manage plugin installation via the marketplace:
6
+ * Assembles the plugin locally and hands it to the `claude` CLI as a
7
+ * local-file marketplace:
7
8
  * npx ory-claude-setup # Install plugin (project scope)
8
9
  * npx ory-claude-setup --global # Install plugin (user scope)
9
- * npx ory-claude-setup --from-source # Install from the local source tree (dev only)
10
+ * npx ory-claude-setup --runtime-dir <d> # Wire a workspace build (dev only)
10
11
  * npx ory-claude-setup --uninstall # Remove plugin
12
+ *
13
+ * The plugin's runtime is resolved **once, here** — materialized into the
14
+ * shared runtime store (or linked to a workspace build) and wired through a
15
+ * stable shim, so no package resolution happens on the tool-call path. That is
16
+ * why the install always assembles: the generated `hooks.json` / `.mcp.json`
17
+ * carry this machine's shim paths, which a marketplace fetched from GitHub
18
+ * cannot. The public `ory/claude-plugins` mirror stays a valid install route
19
+ * on its own terms (it ships a bootstrap keyed off `${CLAUDE_PLUGIN_ROOT}`).
11
20
  */
12
21
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
22
  if (k2 === undefined) k2 = k;
@@ -43,42 +52,28 @@ var __importStar = (this && this.__importStar) || (function () {
43
52
  };
44
53
  })();
45
54
  Object.defineProperty(exports, "__esModule", { value: true });
46
- exports.isAssembledFromSource = isAssembledFromSource;
55
+ exports.assemblePluginDir = assemblePluginDir;
56
+ exports.isPluginAssembled = isPluginAssembled;
47
57
  exports.install = install;
58
+ exports.printInstallPlan = printInstallPlan;
48
59
  exports.uninstall = uninstall;
49
60
  const node_child_process_1 = require("node:child_process");
50
61
  const fs = __importStar(require("node:fs"));
51
62
  const path = __importStar(require("node:path"));
52
63
  const argus_1 = require("@ory/argus");
53
64
  const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
65
+ const PACKAGE_NAME = "@ory/claude-code";
66
+ const HARNESS = "claude-code";
67
+ const INSTALL_COMMAND = "npx -y -p @ory/claude-code ory-claude install";
54
68
  /**
55
- * Production marketplace published by the release workflow as the read-only
56
- * mirror of this monorepo's Claude Code plugin. The `name` field below is the
57
- * short identifier declared by that repo's top-level
58
- * `.claude-plugin/marketplace.json` — that's what the `claude` CLI registers
59
- * the marketplace under, and what `claude plugin install <plugin>@<name>`
60
- * dereferences. The plugin name matches the local `.claude-plugin/plugin.json`
61
- * because the sync script preserves it.
62
- *
63
- * Keep these in sync with whatever the production marketplace declares — if
64
- * the repo's marketplace.json `name` changes, update `PROD_MARKETPLACE_NAME`
65
- * here too.
66
- */
67
- const PROD_MARKETPLACE_REPO = "ory/claude-plugins";
68
- const PROD_MARKETPLACE_NAME = "ory";
69
- const PROD_PLUGIN_NAME = "ory-agent-plugin";
70
- /**
71
- * For `--from-source` installs, resolve the marketplace and plugin names from
72
- * the local `.claude-plugin/` JSON files instead of hardcoding them.
69
+ * Resolve the marketplace and plugin names from the `.claude-plugin/` JSON
70
+ * files shipped in the package, rather than hardcoding them here.
73
71
  *
74
72
  * Source of truth is `packages/claude-code/.claude-plugin/marketplace.json`
75
- * (marketplace name) and `.claude-plugin/plugin.json` (plugin name). The local
76
- * marketplace uses intentionally dev-flavored names e.g.
77
- * `ory-plugins-local-development` so a developer can see at a glance the
78
- * install came from this monorepo and not the production
79
- * `ory/claude-plugins` repo. The default install path (no `--from-source`)
80
- * bypasses these entirely and points the `claude` CLI at the production
81
- * GitHub marketplace.
73
+ * (marketplace name) and `.claude-plugin/plugin.json` (plugin name). The
74
+ * marketplace name is deliberately distinct from the one the public
75
+ * `ory/claude-plugins` mirror declares, so a user who has registered both
76
+ * sees two clearly-labelled sources instead of a silent collision.
82
77
  */
83
78
  function readLocalMarketplaceName() {
84
79
  const mfPath = path.join(PACKAGE_ROOT, ".claude-plugin", "marketplace.json");
@@ -101,101 +96,131 @@ const RENDER_OPTS = {
101
96
  packageName: "@ory/claude-code",
102
97
  };
103
98
  /**
104
- * Persistent install location used as the marketplace root for `--from-source`
105
- * installs. Lives under the shared OS-agnostic data dir so all plugin state
106
- * config, DCR creds, harness assets — sits in one place per platform. The
107
- * production install path never touches this directory: the `claude` CLI
108
- * fetches everything directly from the GitHub marketplace.
109
- */
110
- const PERSISTENT_DIR = (0, argus_1.getHarnessDataDir)("claude-code");
111
- /** Hook command that resolves the binary via the npm registry. */
112
- const HOOK_CMD_REMOTE = "npx -y -p @ory/claude-code ory-claude-hook";
113
- /**
114
- * Detect if we're running from a temporary npx/pnpm-dlx cache directory
115
- * rather than a proper local or global npm installation. Only consulted on
116
- * the `--from-source` path to pick the right hook command.
99
+ * Persistent install location: the marketplace root the `claude` CLI is
100
+ * pointed at. Lives under the shared OS-agnostic data dir so all plugin state
101
+ * config, DCR creds, harness assets — sits in one place per platform.
102
+ *
103
+ * Resolved on each call rather than captured at module load: `getHarnessDataDir`
104
+ * reads `XDG_CONFIG_HOME`, so a module-level constant would freeze whatever the
105
+ * env said at import time — which silently defeats test isolation (and any
106
+ * caller that sets the var itself).
117
107
  */
118
- function isRemoteContext() {
119
- const normalized = PACKAGE_ROOT.replace(/\\/g, "/");
120
- return normalized.includes("/_npx/") || normalized.includes("/dlx-");
121
- }
122
- /** Hook command for a local/global install — runs the built hook directly. */
123
- function localHookCommand() {
124
- return `node "${path.join(PACKAGE_ROOT, "dist", "hook.js")}"`;
108
+ function persistentDir() {
109
+ return (0, argus_1.getHarnessDataDir)(HARNESS);
125
110
  }
126
111
  /**
127
112
  * Assemble the Claude Code plugin directory in the persistent location and
128
- * return its path (used as the marketplace root for `--from-source`).
113
+ * return its path (the marketplace root).
129
114
  *
130
- * The plugin manifest (`.claude-plugin/`) and MCP config (`.mcp.json`) are
131
- * copied from the package; the skills, commands, and hooks are generated:
132
- * skills/commands are materialized from the shared `@ory/argus` templates (the
133
- * single source of truth across every harness), and `hooks.json` is written
134
- * with a hook command appropriate for the install context.
115
+ * The plugin manifest (`.claude-plugin/`) is copied from the package; the
116
+ * skills, commands, hooks, and MCP config are generated: skills/commands come
117
+ * from the shared `@ory/argus` templates (the single source of truth across
118
+ * every harness), and `hooks.json` / `.mcp.json` are written with the shim
119
+ * paths this machine's runtime resolved to.
135
120
  */
136
- function assemblePluginDir(hookCommand) {
137
- fs.mkdirSync(PERSISTENT_DIR, { recursive: true });
138
- // Static manifests copied straight from the package.
139
- for (const asset of [".claude-plugin", ".mcp.json"]) {
140
- const src = path.join(PACKAGE_ROOT, asset);
121
+ function assemblePluginDir(runtime) {
122
+ fs.mkdirSync(persistentDir(), { recursive: true });
123
+ // Static manifest copied straight from the package. `.mcp.json` is NOT
124
+ // copied it is generated below from the resolved runtime, so the server
125
+ // starts from the materialized copy instead of being re-resolved by npx.
126
+ {
127
+ const src = path.join(PACKAGE_ROOT, ".claude-plugin");
141
128
  if (fs.existsSync(src)) {
142
- fs.cpSync(src, path.join(PERSISTENT_DIR, asset), {
129
+ fs.cpSync(src, path.join(persistentDir(), ".claude-plugin"), {
143
130
  recursive: true,
144
131
  force: true,
145
132
  });
146
133
  }
147
134
  }
135
+ // Stamp the assembled plugin manifest with this package's version. The
136
+ // committed manifest carries a frozen placeholder version, and the `claude`
137
+ // CLI keys its cached component inventory (commands, skills) on the plugin
138
+ // version — so without this, a release that adds a command would keep the
139
+ // stale command set until the user manually removed the plugin. Tracking the
140
+ // package version means every release advertises a new plugin version and
141
+ // Claude Code refreshes.
142
+ const manifestPath = path.join(persistentDir(), ".claude-plugin", "plugin.json");
143
+ try {
144
+ const pkgVersion = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8")).version;
145
+ if (pkgVersion && fs.existsSync(manifestPath)) {
146
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
147
+ manifest.version = pkgVersion;
148
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
149
+ }
150
+ }
151
+ catch {
152
+ /* best-effort — a missing/unreadable manifest just keeps its default version */
153
+ }
148
154
  // Skills — materialized fresh from the shared core templates.
149
- const skillsDir = path.join(PERSISTENT_DIR, "skills");
155
+ const skillsDir = path.join(persistentDir(), "skills");
150
156
  fs.rmSync(skillsDir, { recursive: true, force: true });
151
157
  (0, argus_1.writeSkillTree)(skillsDir, (0, argus_1.renderOrySkills)("claude-code", RENDER_OPTS));
152
158
  // Commands — plain markdown command files (Claude Code's `/ory:<name>`).
153
- const commandsDir = path.join(PERSISTENT_DIR, "commands");
159
+ const commandsDir = path.join(persistentDir(), "commands");
154
160
  fs.rmSync(commandsDir, { recursive: true, force: true });
155
161
  fs.mkdirSync(commandsDir, { recursive: true });
156
162
  for (const cmd of (0, argus_1.renderOryCommands)("claude-code", RENDER_OPTS)) {
157
163
  fs.writeFileSync(path.join(commandsDir, `${cmd.slug}.md`), (0, argus_1.commandToPlainMarkdown)(cmd));
158
164
  }
159
- // Hooksgenerated with a command appropriate for the install context.
160
- const hooksDir = path.join(PERSISTENT_DIR, "hooks");
165
+ // MCP server points at the shim, so the server starts from the resolved
166
+ // runtime. Omitted entirely when this runtime ships no MCP server, rather
167
+ // than writing a registration that would fail to start.
168
+ const mcpPath = path.join(persistentDir(), ".mcp.json");
169
+ if (runtime.mcpServer) {
170
+ fs.writeFileSync(mcpPath, JSON.stringify({
171
+ description: "Ory Claude Code MCP Server",
172
+ mcpServers: { "ory-claude-mcp": runtime.mcpServer },
173
+ }, null, 2) + "\n");
174
+ }
175
+ else {
176
+ fs.rmSync(mcpPath, { force: true });
177
+ }
178
+ // Hooks — every event routed through the stable runtime shim.
179
+ const hooksDir = path.join(persistentDir(), "hooks");
161
180
  fs.mkdirSync(hooksDir, { recursive: true });
162
- const entry = [
163
- { matcher: "", hooks: [{ type: "command", command: hookCommand }] },
181
+ // Claude Code's hook timeout is in SECONDS and defaults to 60 — half the
182
+ // window the interactive login needs, so leaving it undeclared let the hook
183
+ // be killed mid-sign-in (#220). Both values come from core so they cannot
184
+ // drift from the login timeout they have to outlast.
185
+ const command = (0, argus_1.requireHookCommand)(runtime);
186
+ const entry = (timeout) => [
187
+ { matcher: "", hooks: [{ type: "command", command, timeout }] },
164
188
  ];
189
+ const sessionStart = entry((0, argus_1.sessionStartHookTimeout)("claude-code", "seconds"));
190
+ const tool = entry((0, argus_1.toolHookTimeout)("claude-code", "seconds"));
165
191
  fs.writeFileSync(path.join(hooksDir, "hooks.json"), JSON.stringify({
166
192
  description: "Ory Claude Code Hooks",
167
193
  hooks: {
168
- SessionStart: entry,
169
- SessionEnd: entry,
170
- UserPromptSubmit: entry,
171
- PreToolUse: entry,
172
- PostToolUse: entry,
173
- PostToolUseFailure: entry,
174
- SubagentStart: entry,
175
- SubagentStop: entry,
176
- PermissionRequest: entry,
194
+ SessionStart: sessionStart,
195
+ SessionEnd: tool,
196
+ UserPromptSubmit: tool,
197
+ PreToolUse: tool,
198
+ PostToolUse: tool,
199
+ PostToolUseFailure: tool,
200
+ SubagentStart: tool,
201
+ SubagentStop: tool,
202
+ PermissionRequest: tool,
177
203
  },
178
204
  }, null, 2) + "\n");
179
- return PERSISTENT_DIR;
205
+ return persistentDir();
180
206
  }
181
207
  /**
182
- * Whether a `--from-source` plugin was assembled into `dir`.
208
+ * Whether an Ory plugin tree was assembled into `dir`.
183
209
  *
184
- * The data dir doubles as the default trace/debug-log home (`fromEnv` writes
185
- * `ory-agent-trace.ndjson` / `ory-agent-debug.log` there on every session), so
186
- * its mere existence does NOT indicate a local assembly a production
187
- * marketplace install that has run once already creates it. The plugin
188
- * manifest (`.claude-plugin`) is written only by {@link assemblePluginDir}, so
189
- * it's the reliable signal that this dir holds a from-source plugin.
210
+ * The data dir doubles as the default activity/debug-log home (`fromEnv` writes
211
+ * `ory-agent-debug.log` there on every session), so
212
+ * its mere existence does NOT indicate an install. The plugin manifest
213
+ * (`.claude-plugin`) is written only by {@link assemblePluginDir}, so it's the
214
+ * reliable signal that this dir holds an assembled plugin.
190
215
  */
191
- function isAssembledFromSource(dir) {
216
+ function isPluginAssembled(dir) {
192
217
  return fs.existsSync(path.join(dir, ".claude-plugin"));
193
218
  }
194
219
  /** Remove the persistent install directory if it exists. */
195
220
  function cleanPersistentDir() {
196
- if (fs.existsSync(PERSISTENT_DIR)) {
197
- fs.rmSync(PERSISTENT_DIR, { recursive: true, force: true });
198
- console.log(` Removed ${PERSISTENT_DIR}`);
221
+ if (fs.existsSync(persistentDir())) {
222
+ fs.rmSync(persistentDir(), { recursive: true, force: true });
223
+ console.log(` Removed ${persistentDir()}`);
199
224
  }
200
225
  }
201
226
  /** Map --global flag to the claude CLI's scope values. */
@@ -229,99 +254,69 @@ function checkClaudeCli() {
229
254
  /**
230
255
  * Install the Ory plugin via the `claude` CLI.
231
256
  *
232
- * Two paths exist:
233
- *
234
- * 1. Default (production) point the `claude` CLI at the public
235
- * `ory/claude-plugins` GitHub marketplace and install from there. The
236
- * release workflow keeps that repo's plugin subtree pinned to the latest
237
- * `@ory/claude-code` version, so the hook commands, skills, commands,
238
- * and MCP config all come from the published artifact. The install CLI
239
- * writes nothing to the persistent data dir on this path.
240
- *
241
- * 2. `--from-source` (dev only) — assemble the plugin directory from this
242
- * checkout's `.claude-plugin/`, register it as a local-file marketplace,
243
- * and install from there. The dev launcher uses this so changes in the
244
- * monorepo are exercised end-to-end without going through the release
245
- * mirror. End users should never need this flag.
257
+ * The runtime is resolved here, once: materialized into the shared runtime
258
+ * store from npm (or linked to a workspace build with `--link`), then wired
259
+ * through a stable shim that the generated `hooks.json` / `.mcp.json` point
260
+ * at. Because those files carry machine-specific shim paths, the plugin tree
261
+ * is always assembled locally and registered as a local-file marketplace.
246
262
  */
247
263
  function install(args) {
248
264
  const isGlobal = args.includes("--global");
249
- const fromSource = args.includes("--from-source");
250
265
  const scope = claudeScope(isGlobal);
251
266
  if (!checkClaudeCli()) {
252
267
  console.error("Error: 'claude' CLI not found in PATH.");
253
268
  console.error("Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code");
254
269
  process.exit(1);
255
270
  }
256
- if (fromSource) {
257
- installFromSource(scope);
258
- }
259
- else {
260
- installFromProductionMarketplace(scope);
261
- }
262
- (0, argus_1.printNextSteps)("Claude Code", fromSource
263
- ? "npx -y -p @ory/claude-code ory-claude uninstall --from-source"
264
- : isRemoteContext()
265
- ? "npx @ory/claude-code uninstall"
266
- : "npx -y -p @ory/claude-code ory-claude uninstall", { binName: "ory-claude", harness: "claude-code" });
271
+ installAssembled(scope, args);
272
+ (0, argus_1.printNextSteps)("Claude Code", "npx -y -p @ory/claude-code ory-claude uninstall", {
273
+ binName: "ory-claude",
274
+ harness: HARNESS,
275
+ });
267
276
  }
268
- /**
269
- * Default production install path: register the `ory/claude-plugins` GitHub
270
- * marketplace and install `ory-agent-plugin` from it. Nothing is assembled
271
- * locally — the marketplace and the plugin tree both live in the GitHub repo,
272
- * and the `claude` CLI fetches them on demand.
273
- */
274
- function installFromProductionMarketplace(scope) {
275
- console.log(`Registering Ory plugin marketplace from ${PROD_MARKETPLACE_REPO}...`);
276
- const addResult = runClaude([
277
- "plugin",
278
- "marketplace",
279
- "add",
280
- PROD_MARKETPLACE_REPO,
281
- "--scope",
282
- scope,
283
- ]);
284
- if (!addResult.ok) {
285
- if (addResult.output.toLowerCase().includes("already")) {
286
- console.log(" Marketplace already registered, updating...");
287
- runClaude(["plugin", "marketplace", "update", PROD_MARKETPLACE_NAME]);
288
- }
289
- else {
290
- console.error(`Failed to add marketplace: ${addResult.output}`);
291
- process.exit(1);
292
- }
293
- }
294
- else {
295
- console.log(" Marketplace added.");
296
- }
297
- console.log(`Installing ${PROD_PLUGIN_NAME}@${PROD_MARKETPLACE_NAME} plugin...`);
298
- const installResult = runClaude([
299
- "plugin",
300
- "install",
301
- `${PROD_PLUGIN_NAME}@${PROD_MARKETPLACE_NAME}`,
302
- "--scope",
303
- scope,
304
- ]);
305
- if (!installResult.ok) {
306
- console.error(`Failed to install plugin: ${installResult.output}`);
307
- process.exit(1);
308
- }
309
- console.log(" Plugin installed.");
277
+ function printInstallPlan(args) {
278
+ console.log(JSON.stringify({
279
+ scope: claudeScope(args.includes("--global")),
280
+ marketplaceRoot: persistentDir(),
281
+ hooks: 'node "<runtime shim>"',
282
+ mcpServer: { command: "node", args: ["<MCP runtime shim>"] },
283
+ assets: ["skills/", "commands/"],
284
+ }, null, 2));
310
285
  }
311
286
  /**
312
- * Dev-only install path: assemble the plugin from this checkout and register
313
- * it as a local-file marketplace. Used by the dev launcher (which publishes
314
- * the monorepo packages to a local Verdaccio registry and then calls this CLI
315
- * with `--from-source`) and by anyone hand-iterating on plugin code locally.
287
+ * Resolve the runtime, assemble the plugin tree around it, and register that
288
+ * tree with the `claude` CLI as a local-file marketplace.
316
289
  */
317
- function installFromSource(scope) {
318
- const remote = isRemoteContext();
290
+ function installAssembled(scope, args) {
291
+ const runtime = (0, argus_1.wireRuntime)({
292
+ harness: HARNESS,
293
+ packageName: PACKAGE_NAME,
294
+ packageRoot: PACKAGE_ROOT,
295
+ installCommand: INSTALL_COMMAND,
296
+ args,
297
+ });
298
+ console.log(runtime.target.kind === "linked"
299
+ ? `Runtime: linked to ${runtime.target.packageDir} (dev)`
300
+ : `Runtime: ${runtime.target.packageName}@${runtime.target.version} in ${runtime.target.storeDir}`);
301
+ console.log(` Hook: ${runtime.hookShim}`);
302
+ for (const pruned of runtime.prunedStores) {
303
+ console.log(` Removed stale runtime: ${pruned}`);
304
+ }
319
305
  console.log("Assembling Ory plugin (skills, commands, hooks, MCP) at:");
320
- console.log(` ${PERSISTENT_DIR}`);
321
- const pluginRoot = assemblePluginDir(remote ? HOOK_CMD_REMOTE : localHookCommand());
306
+ console.log(` ${persistentDir()}`);
307
+ const pluginRoot = assemblePluginDir(runtime);
322
308
  const marketplaceName = readLocalMarketplaceName();
323
309
  const pluginName = readLocalPluginName();
324
310
  console.log(`Source: ${pluginName}@${marketplaceName} (from ${pluginRoot})`);
311
+ // Force a clean slate before re-adding. The `claude` CLI caches an
312
+ // already-installed plugin by version and won't re-read its command/skill
313
+ // files on a same-version re-install — so a freshly-assembled command (e.g.
314
+ // `dashboard`) would be missing after a republish. Removing the plugin and
315
+ // its marketplace first guarantees the current command set is picked up on
316
+ // every dev iteration. Best-effort and quiet: a not-yet-installed plugin
317
+ // just no-ops here.
318
+ runClaude(["plugin", "uninstall", pluginName, "--scope", scope]);
319
+ runClaude(["plugin", "marketplace", "remove", marketplaceName]);
325
320
  console.log("Adding local Ory plugin marketplace...");
326
321
  const addResult = runClaude([
327
322
  "plugin",
@@ -356,27 +351,32 @@ function installFromSource(scope) {
356
351
  console.error(`Failed to install plugin: ${installResult.output}`);
357
352
  process.exit(1);
358
353
  }
354
+ // Claude Code stores this marketplace path and re-reads it, so a data-dir
355
+ // wipe that skips uninstall would leave it pointing at nothing. Record how to
356
+ // undo the registration so the destructive paths can replay it (#206).
357
+ (0, argus_1.recordExternalRegistration)({
358
+ harness: "claude-code",
359
+ tool: "claude",
360
+ description: `Claude Code marketplace "${marketplaceName}"`,
361
+ root: persistentDir(),
362
+ remove: { command: "claude", args: ["plugin", "marketplace", "remove", marketplaceName] },
363
+ });
359
364
  console.log(" Plugin installed.");
360
365
  }
361
366
  /**
362
- * Uninstall the Ory plugin via the `claude` CLI. Mirrors `install`:
363
- *
364
- * - Default: uninstall `ory-agent-plugin`, remove the production marketplace.
365
- * - `--from-source`: uninstall the locally-assembled plugin, remove its
366
- * marketplace entry, and clean up the assembled directory.
367
+ * Uninstall the Ory plugin via the `claude` CLI: remove the plugin and its
368
+ * marketplace entry, delete the assembled plugin tree, and drop the runtime
369
+ * wiring (shim + any store no other harness still references).
367
370
  */
368
371
  function uninstall(args) {
369
372
  const isGlobal = args.includes("--global");
370
- const fromSource = args.includes("--from-source");
371
373
  const scope = claudeScope(isGlobal);
372
374
  if (!checkClaudeCli()) {
373
375
  console.error("Error: 'claude' CLI not found in PATH.");
374
376
  process.exit(1);
375
377
  }
376
- const pluginName = fromSource ? readLocalPluginName() : PROD_PLUGIN_NAME;
377
- const marketplaceName = fromSource
378
- ? readLocalMarketplaceName()
379
- : PROD_MARKETPLACE_NAME;
378
+ const pluginName = readLocalPluginName();
379
+ const marketplaceName = readLocalMarketplaceName();
380
380
  console.log(`Uninstalling ${pluginName} plugin...`);
381
381
  const uninstallResult = runClaude([
382
382
  "plugin",
@@ -404,8 +404,14 @@ function uninstall(args) {
404
404
  else {
405
405
  console.log(" Marketplace removed.");
406
406
  }
407
- if (fromSource) {
408
- cleanPersistentDir();
407
+ // Deregistered above, so nothing is left to undo.
408
+ (0, argus_1.clearExternalRegistration)("claude-code");
409
+ cleanPersistentDir();
410
+ // Drop the shim and GC the runtime store, unless another harness still
411
+ // points at it.
412
+ (0, argus_1.removeRuntimeWiring)(HARNESS);
413
+ for (const pruned of (0, argus_1.pruneRuntimeStores)()) {
414
+ console.log(` Removed runtime: ${pruned}`);
409
415
  }
410
416
  }
411
417
  // --- Standalone binary entry point (ory-claude-setup) ---
@@ -419,13 +425,15 @@ Usage:
419
425
 
420
426
  Options:
421
427
  --global Install to user scope (default: project scope)
422
- --from-source Install from this checkout's local marketplace (dev only)
428
+ --runtime-dir <dir>
429
+ Wire the runtime to a built package directory instead of
430
+ installing it from npm (dev only)
423
431
  --uninstall Remove the Ory plugin and marketplace
424
432
  -h, --help Show this help
425
433
 
426
- By default, points the 'claude' CLI at the public ory/claude-plugins
427
- marketplace and installs ory-agent-plugin from it. Pass --from-source to
428
- install from the local monorepo checkout instead (used by the dev launcher).
434
+ Resolves the plugin runtime once (installing it into the shared runtime store),
435
+ assembles the plugin tree around the resulting shim, and registers that tree
436
+ with the 'claude' CLI as a local-file marketplace.
429
437
  `);
430
438
  process.exit(0);
431
439
  }
@@ -434,7 +442,10 @@ install from the local monorepo checkout instead (used by the dev launcher).
434
442
  uninstall(args);
435
443
  // Direct `-setup --uninstall` path: also clear stored Ory credentials.
436
444
  // (The `ory-claude uninstall` command handles this itself.)
437
- (0, argus_1.clearCredentialsForUninstall)().then(() => process.exit(0), (err) => {
445
+ (0, argus_1.clearCredentialsForUninstall)({
446
+ harness: "claude-code",
447
+ purge: process.argv.includes("--purge"),
448
+ }).then(() => process.exit(0), (err) => {
438
449
  console.error(err.message ?? err);
439
450
  process.exit(1);
440
451
  });
@@ -1,14 +1,9 @@
1
- import { OryAgentClient, ensureUserAuthenticated, ensureAgentIdentity, ensureSubAgentIdentity } from "@ory/argus";
1
+ import { ensureAgentIdentity, ensureSubAgentIdentity, ensureUserAuthenticated } from "@ory/argus";
2
+ import type { OryAgentClient } from "@ory/argus";
2
3
  import type { ClaudeCodeHookInput, ClaudeCodeHookOutput } from "./types.js";
3
4
  export interface HandleHookEventDeps {
4
- /** Test injection point for the user login flow. */
5
5
  userLogin?: typeof ensureUserAuthenticated;
6
- /** Test injection point for the agent identity gate. */
7
6
  agentGate?: typeof ensureAgentIdentity;
8
- /** Test injection point for the sub-agent identity resolver. */
9
7
  subAgentGate?: typeof ensureSubAgentIdentity;
10
8
  }
11
- /**
12
- * Route a Claude Code hook event to the appropriate Ory integration.
13
- */
14
9
  export declare function handleHookEvent(input: ClaudeCodeHookInput, client: OryAgentClient, deps?: HandleHookEventDeps): Promise<ClaudeCodeHookOutput>;