@ory/claude-code 0.14.0 → 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,55 +96,37 @@ 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
- /**
112
- * Hook command for a `--from-source` install fetched via npx. The plugin lives
113
- * in an ephemeral `_npx` cache dir (cleaned between runs), so the hook must
114
- * re-resolve `@ory/claude-code` at runtime rather than point at a soon-to-vanish
115
- * path. `--from-source` is dev-only and always paired with the local Verdaccio
116
- * registry, so the hook's npx is pinned to it (with `--min-release-age=0` so a
117
- * just-published dev version isn't filtered out). Without the explicit
118
- * `--registry`, a normally-launched `claude` (i.e. not via the dev launcher,
119
- * which sets `npm_config_registry`) would resolve `@ory/claude-code` from
120
- * npmjs.org and run the last *published* release instead of your local build.
121
- */
122
- const HOOK_CMD_FROM_SOURCE_REMOTE = `npx -y --registry ${argus_1.REGISTRY_URL} --min-release-age=0 -p @ory/claude-code ory-claude-hook`;
123
- /**
124
- * Detect if we're running from a temporary npx/pnpm-dlx cache directory
125
- * rather than a proper local or global npm installation. Only consulted on
126
- * 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).
127
107
  */
128
- function isRemoteContext() {
129
- const normalized = PACKAGE_ROOT.replace(/\\/g, "/");
130
- return normalized.includes("/_npx/") || normalized.includes("/dlx-");
131
- }
132
- /** Hook command for a local/global install — runs the built hook directly. */
133
- function localHookCommand() {
134
- return `node "${path.join(PACKAGE_ROOT, "dist", "hook.js")}"`;
108
+ function persistentDir() {
109
+ return (0, argus_1.getHarnessDataDir)(HARNESS);
135
110
  }
136
111
  /**
137
112
  * Assemble the Claude Code plugin directory in the persistent location and
138
- * return its path (used as the marketplace root for `--from-source`).
113
+ * return its path (the marketplace root).
139
114
  *
140
- * The plugin manifest (`.claude-plugin/`) and MCP config (`.mcp.json`) are
141
- * copied from the package; the skills, commands, and hooks are generated:
142
- * skills/commands are materialized from the shared `@ory/argus` templates (the
143
- * single source of truth across every harness), and `hooks.json` is written
144
- * 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.
145
120
  */
146
- function assemblePluginDir(hookCommand) {
147
- fs.mkdirSync(PERSISTENT_DIR, { recursive: true });
148
- // Static manifests copied straight from the package.
149
- for (const asset of [".claude-plugin", ".mcp.json"]) {
150
- 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");
151
128
  if (fs.existsSync(src)) {
152
- fs.cpSync(src, path.join(PERSISTENT_DIR, asset), {
129
+ fs.cpSync(src, path.join(persistentDir(), ".claude-plugin"), {
153
130
  recursive: true,
154
131
  force: true,
155
132
  });
@@ -162,7 +139,7 @@ function assemblePluginDir(hookCommand) {
162
139
  // stale command set until the user manually removed the plugin. Tracking the
163
140
  // package version means every release advertises a new plugin version and
164
141
  // Claude Code refreshes.
165
- const manifestPath = path.join(PERSISTENT_DIR, ".claude-plugin", "plugin.json");
142
+ const manifestPath = path.join(persistentDir(), ".claude-plugin", "plugin.json");
166
143
  try {
167
144
  const pkgVersion = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8")).version;
168
145
  if (pkgVersion && fs.existsSync(manifestPath)) {
@@ -175,56 +152,75 @@ function assemblePluginDir(hookCommand) {
175
152
  /* best-effort — a missing/unreadable manifest just keeps its default version */
176
153
  }
177
154
  // Skills — materialized fresh from the shared core templates.
178
- const skillsDir = path.join(PERSISTENT_DIR, "skills");
155
+ const skillsDir = path.join(persistentDir(), "skills");
179
156
  fs.rmSync(skillsDir, { recursive: true, force: true });
180
157
  (0, argus_1.writeSkillTree)(skillsDir, (0, argus_1.renderOrySkills)("claude-code", RENDER_OPTS));
181
158
  // Commands — plain markdown command files (Claude Code's `/ory:<name>`).
182
- const commandsDir = path.join(PERSISTENT_DIR, "commands");
159
+ const commandsDir = path.join(persistentDir(), "commands");
183
160
  fs.rmSync(commandsDir, { recursive: true, force: true });
184
161
  fs.mkdirSync(commandsDir, { recursive: true });
185
162
  for (const cmd of (0, argus_1.renderOryCommands)("claude-code", RENDER_OPTS)) {
186
163
  fs.writeFileSync(path.join(commandsDir, `${cmd.slug}.md`), (0, argus_1.commandToPlainMarkdown)(cmd));
187
164
  }
188
- // Hooksgenerated with a command appropriate for the install context.
189
- 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");
190
180
  fs.mkdirSync(hooksDir, { recursive: true });
191
- const entry = [
192
- { 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 }] },
193
188
  ];
189
+ const sessionStart = entry((0, argus_1.sessionStartHookTimeout)("claude-code", "seconds"));
190
+ const tool = entry((0, argus_1.toolHookTimeout)("claude-code", "seconds"));
194
191
  fs.writeFileSync(path.join(hooksDir, "hooks.json"), JSON.stringify({
195
192
  description: "Ory Claude Code Hooks",
196
193
  hooks: {
197
- SessionStart: entry,
198
- SessionEnd: entry,
199
- UserPromptSubmit: entry,
200
- PreToolUse: entry,
201
- PostToolUse: entry,
202
- PostToolUseFailure: entry,
203
- SubagentStart: entry,
204
- SubagentStop: entry,
205
- 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,
206
203
  },
207
204
  }, null, 2) + "\n");
208
- return PERSISTENT_DIR;
205
+ return persistentDir();
209
206
  }
210
207
  /**
211
- * Whether a `--from-source` plugin was assembled into `dir`.
208
+ * Whether an Ory plugin tree was assembled into `dir`.
212
209
  *
213
- * The data dir doubles as the default trace/debug-log home (`fromEnv` writes
214
- * `ory-agent-trace.ndjson` / `ory-agent-debug.log` there on every session), so
215
- * its mere existence does NOT indicate a local assembly a production
216
- * marketplace install that has run once already creates it. The plugin
217
- * manifest (`.claude-plugin`) is written only by {@link assemblePluginDir}, so
218
- * 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.
219
215
  */
220
- function isAssembledFromSource(dir) {
216
+ function isPluginAssembled(dir) {
221
217
  return fs.existsSync(path.join(dir, ".claude-plugin"));
222
218
  }
223
219
  /** Remove the persistent install directory if it exists. */
224
220
  function cleanPersistentDir() {
225
- if (fs.existsSync(PERSISTENT_DIR)) {
226
- fs.rmSync(PERSISTENT_DIR, { recursive: true, force: true });
227
- console.log(` Removed ${PERSISTENT_DIR}`);
221
+ if (fs.existsSync(persistentDir())) {
222
+ fs.rmSync(persistentDir(), { recursive: true, force: true });
223
+ console.log(` Removed ${persistentDir()}`);
228
224
  }
229
225
  }
230
226
  /** Map --global flag to the claude CLI's scope values. */
@@ -258,96 +254,57 @@ function checkClaudeCli() {
258
254
  /**
259
255
  * Install the Ory plugin via the `claude` CLI.
260
256
  *
261
- * Two paths exist:
262
- *
263
- * 1. Default (production) point the `claude` CLI at the public
264
- * `ory/claude-plugins` GitHub marketplace and install from there. The
265
- * release workflow keeps that repo's plugin subtree pinned to the latest
266
- * `@ory/claude-code` version, so the hook commands, skills, commands,
267
- * and MCP config all come from the published artifact. The install CLI
268
- * writes nothing to the persistent data dir on this path.
269
- *
270
- * 2. `--from-source` (dev only) — assemble the plugin directory from this
271
- * checkout's `.claude-plugin/`, register it as a local-file marketplace,
272
- * and install from there. The dev launcher uses this so changes in the
273
- * monorepo are exercised end-to-end without going through the release
274
- * 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.
275
262
  */
276
263
  function install(args) {
277
264
  const isGlobal = args.includes("--global");
278
- const fromSource = args.includes("--from-source");
279
265
  const scope = claudeScope(isGlobal);
280
266
  if (!checkClaudeCli()) {
281
267
  console.error("Error: 'claude' CLI not found in PATH.");
282
268
  console.error("Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code");
283
269
  process.exit(1);
284
270
  }
285
- if (fromSource) {
286
- installFromSource(scope);
287
- }
288
- else {
289
- installFromProductionMarketplace(scope);
290
- }
291
- (0, argus_1.printNextSteps)("Claude Code", fromSource
292
- ? "npx -y -p @ory/claude-code ory-claude uninstall --from-source"
293
- : isRemoteContext()
294
- ? "npx @ory/claude-code uninstall"
295
- : "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
+ });
296
276
  }
297
- /**
298
- * Default production install path: register the `ory/claude-plugins` GitHub
299
- * marketplace and install `ory-agent-plugin` from it. Nothing is assembled
300
- * locally — the marketplace and the plugin tree both live in the GitHub repo,
301
- * and the `claude` CLI fetches them on demand.
302
- */
303
- function installFromProductionMarketplace(scope) {
304
- console.log(`Registering Ory plugin marketplace from ${PROD_MARKETPLACE_REPO}...`);
305
- const addResult = runClaude([
306
- "plugin",
307
- "marketplace",
308
- "add",
309
- PROD_MARKETPLACE_REPO,
310
- "--scope",
311
- scope,
312
- ]);
313
- if (!addResult.ok) {
314
- if (addResult.output.toLowerCase().includes("already")) {
315
- console.log(" Marketplace already registered, updating...");
316
- runClaude(["plugin", "marketplace", "update", PROD_MARKETPLACE_NAME]);
317
- }
318
- else {
319
- console.error(`Failed to add marketplace: ${addResult.output}`);
320
- process.exit(1);
321
- }
322
- }
323
- else {
324
- console.log(" Marketplace added.");
325
- }
326
- console.log(`Installing ${PROD_PLUGIN_NAME}@${PROD_MARKETPLACE_NAME} plugin...`);
327
- const installResult = runClaude([
328
- "plugin",
329
- "install",
330
- `${PROD_PLUGIN_NAME}@${PROD_MARKETPLACE_NAME}`,
331
- "--scope",
332
- scope,
333
- ]);
334
- if (!installResult.ok) {
335
- console.error(`Failed to install plugin: ${installResult.output}`);
336
- process.exit(1);
337
- }
338
- 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));
339
285
  }
340
286
  /**
341
- * Dev-only install path: assemble the plugin from this checkout and register
342
- * it as a local-file marketplace. Used by the dev launcher (which publishes
343
- * the monorepo packages to a local Verdaccio registry and then calls this CLI
344
- * 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.
345
289
  */
346
- function installFromSource(scope) {
347
- 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
+ }
348
305
  console.log("Assembling Ory plugin (skills, commands, hooks, MCP) at:");
349
- console.log(` ${PERSISTENT_DIR}`);
350
- const pluginRoot = assemblePluginDir(remote ? HOOK_CMD_FROM_SOURCE_REMOTE : localHookCommand());
306
+ console.log(` ${persistentDir()}`);
307
+ const pluginRoot = assemblePluginDir(runtime);
351
308
  const marketplaceName = readLocalMarketplaceName();
352
309
  const pluginName = readLocalPluginName();
353
310
  console.log(`Source: ${pluginName}@${marketplaceName} (from ${pluginRoot})`);
@@ -394,27 +351,32 @@ function installFromSource(scope) {
394
351
  console.error(`Failed to install plugin: ${installResult.output}`);
395
352
  process.exit(1);
396
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
+ });
397
364
  console.log(" Plugin installed.");
398
365
  }
399
366
  /**
400
- * Uninstall the Ory plugin via the `claude` CLI. Mirrors `install`:
401
- *
402
- * - Default: uninstall `ory-agent-plugin`, remove the production marketplace.
403
- * - `--from-source`: uninstall the locally-assembled plugin, remove its
404
- * 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).
405
370
  */
406
371
  function uninstall(args) {
407
372
  const isGlobal = args.includes("--global");
408
- const fromSource = args.includes("--from-source");
409
373
  const scope = claudeScope(isGlobal);
410
374
  if (!checkClaudeCli()) {
411
375
  console.error("Error: 'claude' CLI not found in PATH.");
412
376
  process.exit(1);
413
377
  }
414
- const pluginName = fromSource ? readLocalPluginName() : PROD_PLUGIN_NAME;
415
- const marketplaceName = fromSource
416
- ? readLocalMarketplaceName()
417
- : PROD_MARKETPLACE_NAME;
378
+ const pluginName = readLocalPluginName();
379
+ const marketplaceName = readLocalMarketplaceName();
418
380
  console.log(`Uninstalling ${pluginName} plugin...`);
419
381
  const uninstallResult = runClaude([
420
382
  "plugin",
@@ -442,8 +404,14 @@ function uninstall(args) {
442
404
  else {
443
405
  console.log(" Marketplace removed.");
444
406
  }
445
- if (fromSource) {
446
- 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}`);
447
415
  }
448
416
  }
449
417
  // --- Standalone binary entry point (ory-claude-setup) ---
@@ -457,13 +425,15 @@ Usage:
457
425
 
458
426
  Options:
459
427
  --global Install to user scope (default: project scope)
460
- --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)
461
431
  --uninstall Remove the Ory plugin and marketplace
462
432
  -h, --help Show this help
463
433
 
464
- By default, points the 'claude' CLI at the public ory/claude-plugins
465
- marketplace and installs ory-agent-plugin from it. Pass --from-source to
466
- 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.
467
437
  `);
468
438
  process.exit(0);
469
439
  }
@@ -472,7 +442,10 @@ install from the local monorepo checkout instead (used by the dev launcher).
472
442
  uninstall(args);
473
443
  // Direct `-setup --uninstall` path: also clear stored Ory credentials.
474
444
  // (The `ory-claude uninstall` command handles this itself.)
475
- (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) => {
476
449
  console.error(err.message ?? err);
477
450
  process.exit(1);
478
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>;