@subshell-ai/plugin-claude-code 0.0.1 → 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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../../plugin-api/dist/types-90lJTTx6.d.ts
1
+ //#region ../../plugin-api/dist/types-CzNsDcbd.d.ts
2
2
  //#region src/types.d.ts
3
3
  /**
4
4
  * The contract a Subshell plugin implements.
@@ -81,8 +81,8 @@ interface BuildCommandInput {
81
81
  * declares the `resume` capability. `mode: "start"` means the conversation
82
82
  * is NEW and must be created under exactly this id (pin it, because the host
83
83
  * stores it and later resumes by it); `mode: "resume"` names an EXISTING
84
- * conversation to continue (the host only asks after {@link HarnessResume.canResume}
85
- * confirmed it survives).
84
+ * conversation to continue (the host only asks after {@link HarnessResume.resumePath}
85
+ * pointed at a file the host found there).
86
86
  */
87
87
  harnessSession?: {
88
88
  id: string;
@@ -95,18 +95,31 @@ interface BuildCommandInput {
95
95
  *
96
96
  * The ids here are HARNESS conversation ids (a claude transcript uuid, say),
97
97
  * never subshell ids: the host pins {@link allocateHarnessSessionId} at launch,
98
- * stores it on the subshell row, and consults {@link canResume} before every
98
+ * stores it on the subshell row, and consults {@link resumePath} before every
99
99
  * restart to choose between continuing and starting fresh.
100
+ *
101
+ * Both members are PURE, and that is the load-bearing property (spec
102
+ * 2026-09-10 §5): a resume runs on the CONTROL PLANE, for a pane that lives on
103
+ * a machine the plane cannot see. Computing (rather than checking) is what
104
+ * lets one plugin implementation serve local and remote restarts identically.
100
105
  */
101
106
  interface HarnessResume {
102
107
  /** Allocates the HARNESS conversation id to pin at launch (a uuid for claude). */
103
108
  allocateHarnessSessionId(): string;
104
109
  /**
105
- * Whether the harness conversation `harnessSessionId` (last run in `cwd`)
106
- * still exists and can be resumed. False means the host launches a fresh
107
- * conversation rather than handing the harness an id it would reject.
110
+ * Where the resumable transcript would be, given the target machine's
111
+ * environment. PURE: it computes a path and never touches a filesystem, so
112
+ * the control plane can build it for a machine it cannot see. The HOST
113
+ * checks existence.
108
114
  */
109
- canResume(harnessSessionId: string, cwd: string): boolean;
115
+ resumePath(harnessSessionId: string, cwd: string, hostEnv: HostEnv): string;
116
+ }
117
+ /** The parts of a target machine's environment a plugin may compute against. */
118
+ interface HostEnv {
119
+ /** The node's home directory */
120
+ homeDir: string;
121
+ /** Values for the variables this plugin's manifest declared it needs */
122
+ env: Record<string, string>;
110
123
  }
111
124
  /** How to spawn the `subshell mcp` stdio server. */
112
125
  interface McpLaunchSpec {
@@ -299,6 +312,16 @@ interface SubshellManifest {
299
312
  detect?: DetectSpec;
300
313
  /** Install guidance shown when the binary is missing */
301
314
  install?: InstallSpec;
315
+ /**
316
+ * Environment variables this plugin computes against (spec 2026-09-10 §5).
317
+ *
318
+ * The node reports the VALUES of exactly these names at `ready`, so the
319
+ * reported set grows by declaration rather than a machine shipping its
320
+ * whole environment to the control plane. Declaring data is deliberate: a
321
+ * WRONG name is silent, yielding an absent key and the plugin's own
322
+ * fallback, i.e. a resume that quietly never offers itself (§11).
323
+ */
324
+ hostEnv?: string[];
302
325
  }
303
326
  //#endregion
304
327
  //#region src/manifest.d.ts
package/dist/index.js CHANGED
@@ -1,7 +1,5 @@
1
- import { existsSync } from "node:fs";
2
- import { homedir } from "node:os";
3
1
  import { join, resolve } from "node:path";
4
- //#region ../../plugin-api/dist/manifest-im30YVIq.js
2
+ //#region ../../plugin-api/dist/manifest-UFScnYqn.js
5
3
  /** Every {@link PluginType}, for anything that has to iterate or validate them. */
6
4
  const PLUGIN_TYPES = ["agent-harness", "terminal"];
7
5
  /**
@@ -62,6 +60,11 @@ function parseManifest(pkgJson) {
62
60
  docsUrl: i.docsUrl
63
61
  };
64
62
  }
63
+ let hostEnv;
64
+ if (block.hostEnv !== void 0) {
65
+ if (!Array.isArray(block.hostEnv) || !block.hostEnv.every((k) => typeof k === "string" && k.trim() !== "")) return { error: "`subshell.hostEnv` must be an array of environment variable names" };
66
+ hostEnv = [...block.hostEnv];
67
+ }
65
68
  return {
66
69
  apiVersion: block.apiVersion,
67
70
  id: block.id,
@@ -71,7 +74,8 @@ function parseManifest(pkgJson) {
71
74
  ...typeof block.icon === "string" ? { icon: block.icon } : {},
72
75
  entry: block.entry,
73
76
  ...detect ? { detect } : {},
74
- ...install ? { install } : {}
77
+ ...install ? { install } : {},
78
+ ...hostEnv ? { hostEnv } : {}
75
79
  };
76
80
  }
77
81
  //#endregion
@@ -115,7 +119,7 @@ function validateGenericProfile(profile) {
115
119
  const parsed = parseManifest({
116
120
  name: "@subshell-ai/plugin-claude-code",
117
121
  description: "Claude Code harness plugin for Subshell",
118
- version: "0.0.1",
122
+ version: "1.0.0",
119
123
  license: "Apache-2.0",
120
124
  type: "module",
121
125
  main: "dist/index.js",
@@ -162,6 +166,7 @@ const parsed = parseManifest({
162
166
  ".npm-global/bin/claude"
163
167
  ]
164
168
  },
169
+ "hostEnv": ["CLAUDE_CONFIG_DIR"],
165
170
  "install": {
166
171
  "command": "curl -fsSL https://claude.ai/install.sh | bash",
167
172
  "docsUrl": "https://code.claude.com/docs/en/setup"
@@ -387,11 +392,6 @@ const ATTENTION_HOOKS = {
387
392
  command: sessionReportPing()
388
393
  }] }]
389
394
  };
390
- /** Claude's state dir: the documented override, else `~/.claude`. */
391
- function claudeConfigDir() {
392
- const override = process.env.CLAUDE_CONFIG_DIR?.trim();
393
- return override ? resolve(override) : join(homedir(), ".claude");
394
- }
395
395
  /** Claude's transcript folder name for a project dir: every non-alphanumeric → `-`. */
396
396
  function projectSlug(cwd) {
397
397
  return cwd.replace(/[^a-zA-Z0-9]/g, "-");
@@ -431,7 +431,19 @@ const createPlugin = (_host) => ({
431
431
  */
432
432
  resume: {
433
433
  allocateHarnessSessionId: () => crypto.randomUUID(),
434
- canResume: (harnessSessionId, cwd) => existsSync(join(claudeConfigDir(), "projects", projectSlug(cwd), `${harnessSessionId}.jsonl`))
434
+ /**
435
+ * Claude's state dir: the documented override, else `<homeDir>/.claude`.
436
+ * Both halves come from the TARGET machine's `ready` report, never from
437
+ * this process, and no filesystem is touched (spec 2026-09-10 §5) — that
438
+ * is what lets the control plane build the path for a node it cannot
439
+ * see. The host stats the result and decides; this member cannot answer
440
+ * whether the transcript exists.
441
+ */
442
+ resumePath: (harnessSessionId, cwd, hostEnv) => {
443
+ const override = hostEnv.env.CLAUDE_CONFIG_DIR?.trim();
444
+ const configDir = override ? resolve(override) : join(hostEnv.homeDir, ".claude");
445
+ return join(configDir, "projects", projectSlug(cwd), `${harnessSessionId}.jsonl`);
446
+ }
435
447
  },
436
448
  buildCommand(input) {
437
449
  const { binary, profile, subshellName, extraFlags, mcp, harnessSession } = input;
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": "0.0.1",
4
+ "version": "1.0.0",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -54,6 +54,7 @@
54
54
  ".npm-global/bin/claude"
55
55
  ]
56
56
  },
57
+ "hostEnv": ["CLAUDE_CONFIG_DIR"],
57
58
  "install": {
58
59
  "command": "curl -fsSL https://claude.ai/install.sh | bash",
59
60
  "docsUrl": "https://code.claude.com/docs/en/setup"