@subshell-ai/plugin-claude-code 1.0.1 → 2.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/index.d.ts +27 -23
- package/dist/index.js +39 -23
- package/icon.svg +3 -0
- package/package.json +4 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../plugin-api/dist/types-
|
|
1
|
+
//#region ../../plugin-api/dist/types-B_wiApwK.d.ts
|
|
2
2
|
//#region src/types.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* The contract a Subshell plugin implements.
|
|
@@ -27,9 +27,9 @@ type PluginType = "agent-harness" | "terminal";
|
|
|
27
27
|
* branches on {@link PluginType}.
|
|
28
28
|
*/
|
|
29
29
|
type PluginCapability = "mcp" | "resume" | "attention" | "settings";
|
|
30
|
-
/** A harness
|
|
31
|
-
interface
|
|
32
|
-
/** Human-friendly
|
|
30
|
+
/** A harness preset as defined by the user (decoded JSON blobs). */
|
|
31
|
+
interface PresetDefinition {
|
|
32
|
+
/** Human-friendly preset name */
|
|
33
33
|
name: string;
|
|
34
34
|
/** Optional longer description */
|
|
35
35
|
description?: string | null;
|
|
@@ -39,21 +39,21 @@ interface ProfileDefinition {
|
|
|
39
39
|
flags: string[];
|
|
40
40
|
/** Settings blob passed to the harness (e.g. claude --settings JSON) */
|
|
41
41
|
settings: Record<string, unknown> | null;
|
|
42
|
-
/** If true, only this
|
|
42
|
+
/** If true, only this preset's config sources apply (isolation) */
|
|
43
43
|
configIsolation: boolean;
|
|
44
|
-
/** If true, new subshells from this
|
|
44
|
+
/** If true, new subshells from this preset auto-restart on exit */
|
|
45
45
|
restartOnExit?: boolean;
|
|
46
46
|
}
|
|
47
|
-
/** Snapshot of a single field-level validation error on a
|
|
48
|
-
interface
|
|
47
|
+
/** Snapshot of a single field-level validation error on a preset. */
|
|
48
|
+
interface PresetValidationIssue {
|
|
49
49
|
/** Field name (e.g. "name", "env", "settings") */
|
|
50
50
|
field: string;
|
|
51
51
|
/** Human-readable problem description */
|
|
52
52
|
message: string;
|
|
53
53
|
}
|
|
54
|
-
interface
|
|
54
|
+
interface PresetValidationResult {
|
|
55
55
|
valid: boolean;
|
|
56
|
-
issues:
|
|
56
|
+
issues: PresetValidationIssue[];
|
|
57
57
|
}
|
|
58
58
|
/** Everything a plugin needs to build a launch command. */
|
|
59
59
|
interface BuildCommandInput {
|
|
@@ -61,8 +61,8 @@ interface BuildCommandInput {
|
|
|
61
61
|
binary: string;
|
|
62
62
|
/** Working directory the harness runs in */
|
|
63
63
|
cwd: string;
|
|
64
|
-
/** The validated
|
|
65
|
-
|
|
64
|
+
/** The validated preset being used */
|
|
65
|
+
preset: PresetDefinition;
|
|
66
66
|
/** Subshell display name ("" = let the harness pick a default) */
|
|
67
67
|
subshellName: string;
|
|
68
68
|
/** Any additional CLI flags from route/request context */
|
|
@@ -169,12 +169,12 @@ interface McpRegistration {
|
|
|
169
169
|
args?: string[];
|
|
170
170
|
/**
|
|
171
171
|
* Extra pane env the harness needs to discover the file (e.g. OPENCODE_CONFIG).
|
|
172
|
-
* Consumed by the HOST, which bakes it into the pane env ahead of
|
|
172
|
+
* Consumed by the HOST, which bakes it into the pane env ahead of preset env;
|
|
173
173
|
* plugins never read it back.
|
|
174
174
|
*/
|
|
175
175
|
env?: Record<string, string>;
|
|
176
176
|
}
|
|
177
|
-
/** One copy-paste line shown in the
|
|
177
|
+
/** One copy-paste line shown in the preset editor for manual-setup harnesses. */
|
|
178
178
|
interface McpSetupStep {
|
|
179
179
|
/** What the user should do, and where the text goes */
|
|
180
180
|
label: string;
|
|
@@ -259,8 +259,8 @@ interface PluginHost {
|
|
|
259
259
|
interface SubshellPlugin {
|
|
260
260
|
/** Builds the argv (no shell) used to launch a subshell. */
|
|
261
261
|
buildCommand(input: BuildCommandInput): string[];
|
|
262
|
-
/** Validates a
|
|
263
|
-
|
|
262
|
+
/** Validates a preset definition before saving. */
|
|
263
|
+
validatePreset(preset: PresetDefinition): PresetValidationResult;
|
|
264
264
|
/** Which optional members below are meaningful on this plugin. */
|
|
265
265
|
capabilities(): PluginCapability[];
|
|
266
266
|
/** Maps a harness exit code to a human label (null = unknown). */
|
|
@@ -292,14 +292,14 @@ interface SubshellPlugin {
|
|
|
292
292
|
mcpRegistration?(launch: McpLaunchSpec, configPath: string): McpRegistration;
|
|
293
293
|
/** How users obtain the subshell MCP tools here. Declare the `mcp` capability with it. */
|
|
294
294
|
mcpSetup?(launch: McpLaunchSpec): McpSetupInfo;
|
|
295
|
-
/** Settings rendered in the
|
|
296
|
-
|
|
297
|
-
/** Known extra env var suggestions for the
|
|
295
|
+
/** Settings rendered in the PRESET editor and stored on the preset. */
|
|
296
|
+
presetSettings?(): SettingsField[];
|
|
297
|
+
/** Known extra env var suggestions for the preset editor. */
|
|
298
298
|
suggestedEnv?(): {
|
|
299
299
|
key: string;
|
|
300
300
|
description: string;
|
|
301
301
|
}[];
|
|
302
|
-
/** Known CLI flag suggestions for the
|
|
302
|
+
/** Known CLI flag suggestions for the preset editor. */
|
|
303
303
|
suggestedFlags?(): {
|
|
304
304
|
flag: string;
|
|
305
305
|
description: string;
|
|
@@ -337,7 +337,11 @@ interface SubshellManifest {
|
|
|
337
337
|
name: string;
|
|
338
338
|
/** One-line description shown in the UI */
|
|
339
339
|
description: string;
|
|
340
|
-
/**
|
|
340
|
+
/**
|
|
341
|
+
* Relative path to this plugin's icon file, e.g. `icon.svg`. Served by
|
|
342
|
+
* the control plane at `/api/plugins/<id>/icon`; absent means the UI
|
|
343
|
+
* renders a monogram instead.
|
|
344
|
+
*/
|
|
341
345
|
icon?: string;
|
|
342
346
|
/** Module to import, relative to the package directory */
|
|
343
347
|
entry: string;
|
|
@@ -365,9 +369,9 @@ declare const manifest: SubshellManifest;
|
|
|
365
369
|
* Built-in harness: Claude Code.
|
|
366
370
|
*
|
|
367
371
|
* Launch shape:
|
|
368
|
-
* claude --settings <json> --name <subshell> [
|
|
372
|
+
* claude --settings <json> --name <subshell> [preset flags] [extra flags]
|
|
369
373
|
* Settings are passed as a JSON string via `--settings` (per-invocation, so
|
|
370
|
-
*
|
|
374
|
+
* preset config never needs to write into the user's real ~/.claude).
|
|
371
375
|
*
|
|
372
376
|
* Identity, detection and install guidance live in this package's
|
|
373
377
|
* package.json `subshell` block, NOT here: the host reads them without
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join, resolve } from "node:path";
|
|
2
|
-
//#region ../../plugin-api/dist/manifest-
|
|
2
|
+
//#region ../../plugin-api/dist/manifest-Czd9ea1g.js
|
|
3
3
|
/** Every {@link PluginType}, for anything that has to iterate or validate them. */
|
|
4
4
|
const PLUGIN_TYPES = ["agent-harness", "terminal"];
|
|
5
5
|
/**
|
|
@@ -10,6 +10,17 @@ const PLUGIN_TYPES = ["agent-harness", "terminal"];
|
|
|
10
10
|
*/
|
|
11
11
|
const MCP_SERVER_NAME = "subshell";
|
|
12
12
|
/**
|
|
13
|
+
* Image formats a plugin icon may be. The server maps these to a Content-Type
|
|
14
|
+
* from a fixed table rather than sniffing the file, so this list and that
|
|
15
|
+
* table are one decision: adding a format here without adding it there leaves
|
|
16
|
+
* an icon that parses and then 500s when something asks for it.
|
|
17
|
+
*/
|
|
18
|
+
const ICON_EXTENSIONS = [
|
|
19
|
+
".svg",
|
|
20
|
+
".png",
|
|
21
|
+
".webp"
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
13
24
|
* Ids become directory names under `<dataDir>/plugins/`, so they are path
|
|
14
25
|
* segments and nothing else: no separators, no traversal, no surprises from a
|
|
15
26
|
* case-insensitive filesystem.
|
|
@@ -32,13 +43,18 @@ function parseManifest(pkgJson) {
|
|
|
32
43
|
const block = pkgJson.subshell;
|
|
33
44
|
if (!isRecord(block)) return { error: "package.json has no `subshell` block, so it is not a Subshell plugin" };
|
|
34
45
|
if (typeof block.apiVersion !== "number" || !Number.isInteger(block.apiVersion)) return { error: "`subshell.apiVersion` must be an integer" };
|
|
35
|
-
if (block.apiVersion < 1) return { error: `this plugin declares plugin-api ${block.apiVersion}, which is not a version; the lowest is 1 and this host implements
|
|
36
|
-
if (block.apiVersion >
|
|
46
|
+
if (block.apiVersion < 1) return { error: `this plugin declares plugin-api ${block.apiVersion}, which is not a version; the lowest is 1 and this host implements 2` };
|
|
47
|
+
if (block.apiVersion > 2) return { error: `this plugin needs plugin-api ${block.apiVersion}, but this host implements 2; upgrade the agent` };
|
|
37
48
|
if (typeof block.id !== "string" || !ID_RE.test(block.id)) return { error: "`subshell.id` must be lowercase letters, digits and hyphens, starting alphanumeric (it becomes a directory name)" };
|
|
38
49
|
if (typeof block.type !== "string" || !PLUGIN_TYPES.includes(block.type)) return { error: `\`subshell.type\` must be one of: ${PLUGIN_TYPES.join(", ")}` };
|
|
39
50
|
if (typeof block.name !== "string" || block.name.trim() === "") return { error: "`subshell.name` must be a non-empty string" };
|
|
40
51
|
if (typeof block.description !== "string") return { error: "`subshell.description` must be a string" };
|
|
41
|
-
if (block.icon !== void 0
|
|
52
|
+
if (block.icon !== void 0) {
|
|
53
|
+
if (typeof block.icon !== "string" || block.icon.trim() === "") return { error: "`subshell.icon` must be a non-empty relative path to an image in the package" };
|
|
54
|
+
if (block.icon.startsWith("/") || block.icon.split("/").includes("..")) return { error: "`subshell.icon` must stay inside the package (no leading `/` and no `..` segment)" };
|
|
55
|
+
const icon = block.icon;
|
|
56
|
+
if (!ICON_EXTENSIONS.some((ext) => icon.endsWith(ext))) return { error: `\`subshell.icon\` must end in one of: ${ICON_EXTENSIONS.join(", ")}` };
|
|
57
|
+
}
|
|
42
58
|
if (typeof block.entry !== "string" || block.entry.trim() === "") return { error: "`subshell.entry` must be a non-empty relative path" };
|
|
43
59
|
if (block.entry.startsWith("/") || block.entry.split("/").includes("..")) return { error: "`subshell.entry` must stay inside the package (no leading `/` and no `..` segment)" };
|
|
44
60
|
let detect;
|
|
@@ -81,23 +97,23 @@ function parseManifest(pkgJson) {
|
|
|
81
97
|
//#endregion
|
|
82
98
|
//#region ../../plugin-api/dist/index.js
|
|
83
99
|
/**
|
|
84
|
-
* The
|
|
100
|
+
* The preset checks every built-in harness performs unchanged: a non-empty
|
|
85
101
|
* name, string env values, and flags that look like flags. Harnesses with
|
|
86
102
|
* extra rules run their own checks on top of these.
|
|
87
|
-
* @param
|
|
103
|
+
* @param preset - The decoded preset definition to check
|
|
88
104
|
* @returns Issues per field; `valid` is true when no issues were found
|
|
89
105
|
*/
|
|
90
|
-
function
|
|
106
|
+
function validateGenericPreset(preset) {
|
|
91
107
|
const issues = [];
|
|
92
|
-
if (!
|
|
108
|
+
if (!preset.name || preset.name.trim().length === 0) issues.push({
|
|
93
109
|
field: "name",
|
|
94
|
-
message: "
|
|
110
|
+
message: "Preset name is required."
|
|
95
111
|
});
|
|
96
|
-
for (const [key, value] of Object.entries(
|
|
112
|
+
for (const [key, value] of Object.entries(preset.env)) if (typeof value !== "string") issues.push({
|
|
97
113
|
field: "env",
|
|
98
114
|
message: `Env var "${key}" must be a string.`
|
|
99
115
|
});
|
|
100
|
-
for (const flag of
|
|
116
|
+
for (const flag of preset.flags) if (!flag.startsWith("-")) issues.push({
|
|
101
117
|
field: "flags",
|
|
102
118
|
message: `Flag "${flag}" must start with "-".`
|
|
103
119
|
});
|
|
@@ -119,7 +135,7 @@ function validateGenericProfile(profile) {
|
|
|
119
135
|
const parsed = parseManifest({
|
|
120
136
|
name: "@subshell-ai/plugin-claude-code",
|
|
121
137
|
description: "Claude Code harness plugin for Subshell",
|
|
122
|
-
version: "
|
|
138
|
+
version: "2.0.0",
|
|
123
139
|
license: "Apache-2.0",
|
|
124
140
|
type: "module",
|
|
125
141
|
main: "dist/index.js",
|
|
@@ -153,15 +169,15 @@ const parsed = parseManifest({
|
|
|
153
169
|
"tsdown": "0.22.14",
|
|
154
170
|
"typescript": "7.0.2"
|
|
155
171
|
},
|
|
156
|
-
files: ["dist"],
|
|
172
|
+
files: ["dist", "icon.svg"],
|
|
157
173
|
publishConfig: { "access": "public" },
|
|
158
174
|
subshell: {
|
|
159
|
-
"apiVersion":
|
|
175
|
+
"apiVersion": 2,
|
|
160
176
|
"id": "claude-code",
|
|
161
177
|
"type": "agent-harness",
|
|
162
178
|
"name": "Claude Code",
|
|
163
179
|
"description": "Anthropic's agentic coding assistant (interactive CLI)",
|
|
164
|
-
"icon": "
|
|
180
|
+
"icon": "icon.svg",
|
|
165
181
|
"entry": "dist/index.js",
|
|
166
182
|
"detect": {
|
|
167
183
|
"binaryName": "claude",
|
|
@@ -410,9 +426,9 @@ function projectSlug(cwd) {
|
|
|
410
426
|
* Built-in harness: Claude Code.
|
|
411
427
|
*
|
|
412
428
|
* Launch shape:
|
|
413
|
-
* claude --settings <json> --name <subshell> [
|
|
429
|
+
* claude --settings <json> --name <subshell> [preset flags] [extra flags]
|
|
414
430
|
* Settings are passed as a JSON string via `--settings` (per-invocation, so
|
|
415
|
-
*
|
|
431
|
+
* preset config never needs to write into the user's real ~/.claude).
|
|
416
432
|
*
|
|
417
433
|
* Identity, detection and install guidance live in this package's
|
|
418
434
|
* package.json `subshell` block, NOT here: the host reads them without
|
|
@@ -456,16 +472,16 @@ const createPlugin = (host) => ({
|
|
|
456
472
|
}
|
|
457
473
|
},
|
|
458
474
|
buildCommand(input) {
|
|
459
|
-
const { binary,
|
|
475
|
+
const { binary, preset, subshellName, extraFlags, mcp, harnessSession, reporter } = input;
|
|
460
476
|
const args = [binary];
|
|
461
477
|
if (mcp?.args) args.push(...mcp.args);
|
|
462
478
|
if (harnessSession) args.push(harnessSession.mode === "resume" ? "--resume" : "--session-id", harnessSession.id);
|
|
463
|
-
const settings = { ...
|
|
479
|
+
const settings = { ...preset.settings ?? {} };
|
|
464
480
|
if (reporter) settings.hooks = attentionHooks(host, reporter);
|
|
465
481
|
else host.log.warn("no reporter resolved for this launch: attention and conversation-identity hooks are omitted");
|
|
466
482
|
if (Object.keys(settings).length > 0) args.push("--settings", JSON.stringify(settings));
|
|
467
483
|
if (subshellName) args.push("--name", subshellName);
|
|
468
|
-
for (const flag of
|
|
484
|
+
for (const flag of preset.flags) args.push(flag);
|
|
469
485
|
if (extraFlags) args.push(...extraFlags);
|
|
470
486
|
return args;
|
|
471
487
|
},
|
|
@@ -486,10 +502,10 @@ const createPlugin = (host) => ({
|
|
|
486
502
|
summary: "Subshell registers itself with every Claude Code subshell automatically (via --mcp-config)."
|
|
487
503
|
};
|
|
488
504
|
},
|
|
489
|
-
|
|
490
|
-
return
|
|
505
|
+
validatePreset(preset) {
|
|
506
|
+
return validateGenericPreset(preset);
|
|
491
507
|
},
|
|
492
|
-
|
|
508
|
+
presetSettings() {
|
|
493
509
|
return CLAUDE_SETTINGS_FIELDS;
|
|
494
510
|
},
|
|
495
511
|
suggestedEnv() {
|
package/icon.svg
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg viewBox="0 0 248 248" fill="none" xmlns="http://www.w3.org/2000/svg"><title>Claude Code</title>
|
|
2
|
+
<path d="M52.4285 162.873L98.7844 136.879L99.5485 134.602L98.7844 133.334H96.4921L88.7237 132.862L62.2346 132.153L39.3113 131.207L17.0249 130.026L11.4214 128.844L6.2 121.873L6.7094 118.447L11.4214 115.257L18.171 115.847L33.0711 116.911L55.485 118.447L71.6586 119.392L95.728 121.873H99.5485L100.058 120.337L98.7844 119.392L97.7656 118.447L74.5877 102.732L49.4995 86.1905L36.3823 76.62L29.3779 71.7757L25.8121 67.2858L24.2839 57.3608L30.6515 50.2716L39.3113 50.8623L41.4763 51.4531L50.2636 58.1879L68.9842 72.7209L93.4357 90.6804L97.0015 93.6343L98.4374 92.6652L98.6571 91.9801L97.0015 89.2625L83.757 65.2772L69.621 40.8192L63.2534 30.6579L61.5978 24.632C60.9565 22.1032 60.579 20.0111 60.579 17.4246L67.8381 7.49965L71.9133 6.19995L81.7193 7.49965L85.7946 11.0443L91.9074 24.9865L101.714 46.8451L116.996 76.62L121.453 85.4816L123.873 93.6343L124.764 96.1155H126.292V94.6976L127.566 77.9197L129.858 57.3608L132.15 30.8942L132.915 23.4505L136.608 14.4708L143.994 9.62643L149.725 12.344L154.437 19.0788L153.8 23.4505L150.998 41.6463L145.522 70.1215L141.957 89.2625H143.994L146.414 86.7813L156.093 74.0206L172.266 53.698L179.398 45.6635L187.803 36.802L193.152 32.5484H203.34L210.726 43.6549L207.415 55.1159L196.972 68.3492L188.312 79.5739L175.896 96.2095L168.191 109.585L168.882 110.689L170.738 110.53L198.755 104.504L213.91 101.787L231.994 98.7149L240.144 102.496L241.036 106.395L237.852 114.311L218.495 119.037L195.826 123.645L162.07 131.592L161.696 131.893L162.137 132.547L177.36 133.925L183.855 134.279H199.774L229.447 136.524L237.215 141.605L241.8 147.867L241.036 152.711L229.065 158.737L213.019 154.956L175.45 145.977L162.587 142.787H160.805V143.85L171.502 154.366L191.242 172.089L215.82 195.011L217.094 200.682L213.91 205.172L210.599 204.699L188.949 188.394L180.544 181.069L161.696 165.118H160.422V166.772L164.752 173.152L187.803 207.771L188.949 218.405L187.294 221.832L181.308 223.959L174.813 222.777L161.187 203.754L147.305 182.486L136.098 163.345L134.745 164.2L128.075 235.42L125.019 239.082L117.887 241.8L111.902 237.31L108.718 229.984L111.902 215.452L115.722 196.547L118.779 181.541L121.58 162.873L123.291 156.636L123.14 156.219L121.773 156.449L107.699 175.752L86.304 204.699L69.3663 222.777L65.291 224.431L58.2867 220.768L58.9235 214.27L62.8713 208.48L86.304 178.705L100.44 160.155L109.551 149.507L109.462 147.967L108.959 147.924L46.6977 188.512L35.6182 189.93L30.7788 185.44L31.4156 178.115L33.7079 175.752L52.4285 162.873Z" fill="#D97757"/>
|
|
3
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@subshell-ai/plugin-claude-code",
|
|
3
3
|
"description": "Claude Code harness plugin for Subshell",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -39,19 +39,17 @@
|
|
|
39
39
|
"tsdown": "0.22.14",
|
|
40
40
|
"typescript": "7.0.2"
|
|
41
41
|
},
|
|
42
|
-
"files": [
|
|
43
|
-
"dist"
|
|
44
|
-
],
|
|
42
|
+
"files": ["dist", "icon.svg"],
|
|
45
43
|
"publishConfig": {
|
|
46
44
|
"access": "public"
|
|
47
45
|
},
|
|
48
46
|
"subshell": {
|
|
49
|
-
"apiVersion":
|
|
47
|
+
"apiVersion": 2,
|
|
50
48
|
"id": "claude-code",
|
|
51
49
|
"type": "agent-harness",
|
|
52
50
|
"name": "Claude Code",
|
|
53
51
|
"description": "Anthropic's agentic coding assistant (interactive CLI)",
|
|
54
|
-
"icon": "
|
|
52
|
+
"icon": "icon.svg",
|
|
55
53
|
"entry": "dist/index.js",
|
|
56
54
|
"detect": {
|
|
57
55
|
"binaryName": "claude",
|