@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/.claude-plugin/marketplace.json +2 -2
- package/README.md +71 -64
- package/dist/cli/main.js +46 -31
- package/dist/cli/setup.d.ts +39 -29
- package/dist/cli/setup.js +164 -191
- package/dist/handlers.d.ts +2 -7
- package/dist/handlers.js +157 -383
- package/dist/hook.js +40 -16
- package/package.json +9 -8
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
|
-
*
|
|
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 --
|
|
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.
|
|
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
|
-
*
|
|
56
|
-
*
|
|
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
|
|
76
|
-
* marketplace
|
|
77
|
-
* `ory-plugins
|
|
78
|
-
*
|
|
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
|
|
105
|
-
*
|
|
106
|
-
* config, DCR creds, harness assets — sits in one place per platform.
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
129
|
-
|
|
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 (
|
|
113
|
+
* return its path (the marketplace root).
|
|
139
114
|
*
|
|
140
|
-
* The plugin manifest (`.claude-plugin/`)
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
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(
|
|
147
|
-
fs.mkdirSync(
|
|
148
|
-
// Static
|
|
149
|
-
|
|
150
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
//
|
|
189
|
-
|
|
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
|
-
|
|
192
|
-
|
|
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:
|
|
198
|
-
SessionEnd:
|
|
199
|
-
UserPromptSubmit:
|
|
200
|
-
PreToolUse:
|
|
201
|
-
PostToolUse:
|
|
202
|
-
PostToolUseFailure:
|
|
203
|
-
SubagentStart:
|
|
204
|
-
SubagentStop:
|
|
205
|
-
PermissionRequest:
|
|
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
|
|
205
|
+
return persistentDir();
|
|
209
206
|
}
|
|
210
207
|
/**
|
|
211
|
-
* Whether
|
|
208
|
+
* Whether an Ory plugin tree was assembled into `dir`.
|
|
212
209
|
*
|
|
213
|
-
* The data dir doubles as the default
|
|
214
|
-
* `ory-agent-
|
|
215
|
-
* its mere existence does NOT indicate
|
|
216
|
-
*
|
|
217
|
-
*
|
|
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
|
|
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(
|
|
226
|
-
fs.rmSync(
|
|
227
|
-
console.log(` Removed ${
|
|
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
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
-
*
|
|
342
|
-
*
|
|
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
|
|
347
|
-
const
|
|
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(` ${
|
|
350
|
-
const pluginRoot = assemblePluginDir(
|
|
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
|
|
401
|
-
*
|
|
402
|
-
*
|
|
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 =
|
|
415
|
-
const marketplaceName =
|
|
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
|
-
|
|
446
|
-
|
|
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
|
-
--
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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)(
|
|
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
|
});
|
package/dist/handlers.d.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import {
|
|
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>;
|