@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/.claude-plugin/marketplace.json +2 -2
- package/README.md +71 -61
- package/dist/cli/main.js +46 -24
- package/dist/cli/setup.d.ts +39 -29
- package/dist/cli/setup.js +191 -180
- package/dist/handlers.d.ts +2 -7
- package/dist/handlers.js +157 -459
- 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,101 +96,131 @@ 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
|
-
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
|
|
119
|
-
|
|
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 (
|
|
113
|
+
* return its path (the marketplace root).
|
|
129
114
|
*
|
|
130
|
-
* The plugin manifest (`.claude-plugin/`)
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
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(
|
|
137
|
-
fs.mkdirSync(
|
|
138
|
-
// Static
|
|
139
|
-
|
|
140
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
//
|
|
160
|
-
|
|
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
|
-
|
|
163
|
-
|
|
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:
|
|
169
|
-
SessionEnd:
|
|
170
|
-
UserPromptSubmit:
|
|
171
|
-
PreToolUse:
|
|
172
|
-
PostToolUse:
|
|
173
|
-
PostToolUseFailure:
|
|
174
|
-
SubagentStart:
|
|
175
|
-
SubagentStop:
|
|
176
|
-
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,
|
|
177
203
|
},
|
|
178
204
|
}, null, 2) + "\n");
|
|
179
|
-
return
|
|
205
|
+
return persistentDir();
|
|
180
206
|
}
|
|
181
207
|
/**
|
|
182
|
-
* Whether
|
|
208
|
+
* Whether an Ory plugin tree was assembled into `dir`.
|
|
183
209
|
*
|
|
184
|
-
* The data dir doubles as the default
|
|
185
|
-
* `ory-agent-
|
|
186
|
-
* its mere existence does NOT indicate
|
|
187
|
-
*
|
|
188
|
-
*
|
|
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
|
|
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(
|
|
197
|
-
fs.rmSync(
|
|
198
|
-
console.log(` Removed ${
|
|
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
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
*
|
|
313
|
-
*
|
|
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
|
|
318
|
-
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
|
+
}
|
|
319
305
|
console.log("Assembling Ory plugin (skills, commands, hooks, MCP) at:");
|
|
320
|
-
console.log(` ${
|
|
321
|
-
const pluginRoot = assemblePluginDir(
|
|
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
|
|
363
|
-
*
|
|
364
|
-
*
|
|
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 =
|
|
377
|
-
const marketplaceName =
|
|
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
|
-
|
|
408
|
-
|
|
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
|
-
--
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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)(
|
|
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
|
});
|
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>;
|