@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 CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../../plugin-api/dist/types-CSNepTNr.d.ts
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 profile as defined by the user (decoded JSON blobs). */
31
- interface ProfileDefinition {
32
- /** Human-friendly profile name */
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 profile's config sources apply (isolation) */
42
+ /** If true, only this preset's config sources apply (isolation) */
43
43
  configIsolation: boolean;
44
- /** If true, new subshells from this profile auto-restart on exit */
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 profile. */
48
- interface ProfileValidationIssue {
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 ProfileValidationResult {
54
+ interface PresetValidationResult {
55
55
  valid: boolean;
56
- issues: ProfileValidationIssue[];
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 profile being used */
65
- profile: ProfileDefinition;
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 profile env;
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 profile editor for manual-setup harnesses. */
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 profile definition before saving. */
263
- validateProfile(profile: ProfileDefinition): ProfileValidationResult;
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 PROFILE editor and stored on the profile. */
296
- profileSettings?(): SettingsField[];
297
- /** Known extra env var suggestions for the profile editor. */
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 profile editor. */
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
- /** Optional emoji/glyph */
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> [profile flags] [extra flags]
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
- * profile config never needs to write into the user's real ~/.claude).
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-UFScnYqn.js
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 1` };
36
- if (block.apiVersion > 1) return { error: `this plugin needs plugin-api ${block.apiVersion}, but this host implements 1; upgrade the agent` };
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 && typeof block.icon !== "string") return { error: "`subshell.icon` must be a string" };
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 profile checks every built-in harness performs unchanged: a non-empty
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 profile - The decoded profile definition to check
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 validateGenericProfile(profile) {
106
+ function validateGenericPreset(preset) {
91
107
  const issues = [];
92
- if (!profile.name || profile.name.trim().length === 0) issues.push({
108
+ if (!preset.name || preset.name.trim().length === 0) issues.push({
93
109
  field: "name",
94
- message: "Profile name is required."
110
+ message: "Preset name is required."
95
111
  });
96
- for (const [key, value] of Object.entries(profile.env)) if (typeof value !== "string") issues.push({
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 profile.flags) if (!flag.startsWith("-")) issues.push({
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: "1.0.1",
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": 1,
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> [profile flags] [extra flags]
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
- * profile config never needs to write into the user's real ~/.claude).
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, profile, subshellName, extraFlags, mcp, harnessSession, reporter } = input;
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 = { ...profile.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 profile.flags) args.push(flag);
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
- validateProfile(profile) {
490
- return validateGenericProfile(profile);
505
+ validatePreset(preset) {
506
+ return validateGenericPreset(preset);
491
507
  },
492
- profileSettings() {
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": "1.0.1",
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": 1,
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",