@useshifu/coding-harness 0.3.22 → 0.3.23

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/README.md CHANGED
@@ -32,7 +32,7 @@ To disconnect this device, run `npx --yes @useshifu/coding-harness disconnect --
32
32
 
33
33
  ## Operate
34
34
 
35
- Version `0.3.22` retrieves context at substantive task starts and new topics, tracks current topic coverage, and surfaces Shifu-attributed support or discrepancies alongside proposals and actions. Current user instructions take precedence over historical context; adopted choices and rationale remain available to existing session sync.
35
+ Version `0.3.23` is the npm `latest` release. It retrieves context at substantive task starts and new topics, tracks current topic coverage, and surfaces Shifu-attributed support or discrepancies alongside proposals and actions. Current user instructions take precedence over historical context; adopted choices and rationale remain available to existing session sync.
36
36
 
37
37
  Version `0.3.21` adds meaningful decision and reflection checkpoints to the managed context guidance: historical reinforcement, discrepancies and goal-aware coaching, with current-context reuse and optional clarification. Existing managed blocks refresh on upgrade.
38
38
 
@@ -91,7 +91,7 @@ The AI Profile page shows detected categories, model and thinking labels, and Je
91
91
 
92
92
  The connection endpoint returns `recommendedPackageVersion`, pinned to a published release. Before `sync`, `sync-files`, `approve`, or `scheduled-sync`, the runner compares its embedded release version with that property. An older runner installs the exact recommended release through npm and resumes the original command with the updated runner. Equal or newer runners continue unchanged. Explicitly recommended major releases follow the same comparison rule. Missing or invalid recommendations, connection failures, and npm failures do not block sync. The connection, pending work, checkpoints, and schedule are preserved.
93
93
 
94
- This update mechanism first ships in `0.3.10`. Runners from earlier releases need a one-time installation of a release containing it (`npx --yes @useshifu/coding-harness@0.3.14 install --harness codex`). Raise the server recommendation only after the target release is published and its backend requirements are deployed.
94
+ This update mechanism first ships in `0.3.10`. Runners from earlier releases need a one-time installation of a release containing it (`npx --yes @useshifu/coding-harness@0.3.23 install --harness codex`). Raise the server recommendation only after the target release is published and its backend requirements are deployed.
95
95
 
96
96
  ## Force sync local work
97
97
 
@@ -122,7 +122,7 @@ Setup preserves other instruction text and MCP settings. Package installation re
122
122
  The context branch adds a remote MCP connection using the same saved Codex key:
123
123
 
124
124
  ```sh
125
- npx --yes @useshifu/coding-harness@0.3.22 mcp-setup --harness codex --mcp-url https://YOUR-BRANCH-HOST/mcp
125
+ npx --yes @useshifu/coding-harness@0.3.23 mcp-setup --harness codex --mcp-url https://YOUR-BRANCH-HOST/mcp
126
126
  ```
127
127
 
128
128
  For development, install dependencies and run from the checkout root:
@@ -153,23 +153,27 @@ Official configuration reference: https://learn.chatgpt.com/docs/config-file/con
153
153
  OpenCode uses the same `/mcp` endpoint and three tools, authenticated with its own saved OpenCode connection key:
154
154
 
155
155
  ```sh
156
- npx --yes @useshifu/coding-harness@0.3.22 mcp-setup --harness opencode --mcp-url https://YOUR-SHIFU-HOST/mcp
156
+ npx --yes @useshifu/coding-harness@0.3.23 mcp-setup --harness opencode --mcp-url https://YOUR-SHIFU-HOST/mcp
157
157
  ```
158
158
 
159
159
  For checkout usage, install dependencies with `npm ci --prefix packages/shifu-coding-harness --ignore-scripts` from the checkout root, then run the checkout CLI with the same arguments. Setup preserves existing sync settings and other MCP servers, checks authenticated initialization and tool discovery, and installs the `shifu-context` usage instructions. A missing OpenCode connection prompts for its key in the terminal. A Codex key cannot authorize an OpenCode connection.
160
160
 
161
- The installer adds a remote `shifu` entry with `oauth: false` to OpenCode's JSON or JSONC configuration, preserving existing content and comments. Its Authorization header uses OpenCode's `{file:...}` substitution to read a protected local credential file; the key stays out of configuration, command arguments and chat. Replacing the saved connection key refreshes that file; disconnect removes it. Restart OpenCode and start a new session after setup. Project-specific configuration can override global MCP settings; inspect `opencode mcp list` in the project if tools do not appear.
161
+ The installer detects OpenCode 1.x or 2.x and validates configuration through that installed client before changing files. V1 uses `mcp.shifu`; V2 uses `mcp.servers.shifu`, direct tool exposure (`codemode: false`), and separate startup/catalog (30 seconds) and execution (300 seconds) budgets. Older V2 builds that discard these options fail validation with an update instruction. Existing unrelated settings and comments are preserved. A managed V1 Shifu entry in the selected file moves to the V2 location during setup.
162
+
163
+ The installer adds a remote `shifu` entry with `oauth: false` to OpenCode's JSON or JSONC configuration, preserving existing content and comments. Its Authorization header uses OpenCode's `{file:...}` substitution to read a protected local credential file; the key stays out of configuration, command arguments and chat. Replacing the saved connection key refreshes that file; disconnect removes it. Restart OpenCode and start a new session after setup. For V2, run `opencode service stop` before reopening it so the background service loads the new connection. Project-specific configuration can override global MCP settings; inspect `opencode mcp list` in the project if tools do not appear.
162
164
 
163
165
  The API derives the harness and tenant from the authenticated connector key. Memory updates from OpenCode carry `opencode` provenance; personal eligibility, general retrieval and evidence verification use the same rules as Codex. Coding-session sync remains separate. No additional local MCP server or OpenCode plugin is required.
164
166
 
165
- Official references: [MCP servers](https://docs.opencode.ai/docs/mcp-servers/) and [configuration](https://docs.opencode.ai/docs/config/).
167
+ Official references: [V1 MCP servers](https://opencode.ai/docs/mcp-servers/), [V2 MCP servers](https://opencode.ai/v2/docs/mcp-servers/) and [V2 migration](https://opencode.ai/v2/docs/migrate-v1/).
168
+
169
+ For the optional native integration gate, set `SHIFU_TEST_OPENCODE_BIN` to a temporary official OpenCode 2.x binary and run `node --test test/opencode-native.test.js` from this package. It uses isolated configuration, a synthetic credential and local MCP/model fixtures; it checks setup, native credential-file authentication, discovery and a model-driven tool call without a user key or paid model.
166
170
 
167
171
  ## Claude Code context and memory
168
172
 
169
173
  Claude Code connects to the same `/mcp` endpoint and three tools with its own saved connection key:
170
174
 
171
175
  ```sh
172
- npx --yes @useshifu/coding-harness@0.3.22 mcp-setup --harness claude_code --mcp-url https://YOUR-SHIFU-HOST/mcp
176
+ npx --yes @useshifu/coding-harness@0.3.23 mcp-setup --harness claude_code --mcp-url https://YOUR-SHIFU-HOST/mcp
173
177
  ```
174
178
 
175
179
  For checkout usage, install dependencies as above and run the checkout CLI with these arguments. Setup checks the existing Claude Code key, authenticated initialization and tool discovery, and installs the `shifu-context` skill. It preserves the sync endpoint, schedule, checkpoints and other MCP servers. A missing connection prompts for a Claude Code key in the terminal; do not substitute a Codex or OpenCode key.
@@ -40,7 +40,7 @@ const MAX_TURNS_PER_SEGMENT = 12;
40
40
  const MAX_TITLE_LENGTH = 160;
41
41
  const MAX_WORK_DETAIL_LENGTH = 600;
42
42
  const CONFIG_VERSION = 2;
43
- const RUNNER_VERSION = "0.3.22";
43
+ const RUNNER_VERSION = "0.3.23";
44
44
  const REDACTION_VERSION = 4;
45
45
  const DEFAULT_INTERVAL_HOURS = 1;
46
46
  const LEGACY_INTERVAL_HOURS = 4;
@@ -1430,7 +1430,7 @@ async function validateConnection(apiUrl, token, harness) {
1430
1430
 
1431
1431
  async function connect(harness, readKey = readSecret) {
1432
1432
  const existing = readJSON(configPath(harness), undefined);
1433
- const refreshContextKey = harness === "opencode" && existing?.mcpUrl && openCodeMcpConfigs().some((config) => managedOpenCodeMcp(config.value?.mcp?.shifu));
1433
+ const refreshContextKey = harness === "opencode" && existing?.mcpUrl && openCodeMcpConfigs(existing.mcpConfigMajor).some((config) => managedOpenCodeMcp(config.value?.mcp?.shifu) || managedOpenCodeMcp(config.value?.mcp?.servers?.shifu));
1434
1434
  const token = (await readKey()).trim();
1435
1435
  if (!TOKEN_PATTERN.test(token)) throw new Error("The connection key is invalid.");
1436
1436
  const apiUrl = requireSafeApiUrl(option("--api-url") || existing?.apiUrl || process.env.SHIFU_API_URL || DEFAULT_API_URL);
@@ -1609,8 +1609,18 @@ function openCodeTokenPath() {
1609
1609
  return file;
1610
1610
  }
1611
1611
 
1612
- function openCodeMcpConfigs() {
1613
- return ["config.json", "opencode.json", "opencode.jsonc"].map((name) => {
1612
+ function openCodeVersion() {
1613
+ try {
1614
+ const output = execFileSync(process.env.SHIFU_OPENCODE_BIN || "opencode", ["--version"], { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8", timeout: 10_000, maxBuffer: 1024 * 1024 });
1615
+ const version = output.trim().match(/^(?:opencode\s+)?v?(\d+\.\d+\.\d+)(?:[-+][\w.-]+)?$/)?.[1];
1616
+ if (version && [1, 2].includes(Number(version.split(".")[0]))) return version;
1617
+ } catch { /* Native output can contain settings; report only the remedy. */ }
1618
+ throw new Error("Could not identify a supported OpenCode 1.x or 2.x client. Install or update OpenCode before MCP setup; nothing was changed.");
1619
+ }
1620
+
1621
+ function openCodeMcpConfigs(major = 1) {
1622
+ const names = major === 2 ? ["opencode.json", "opencode.jsonc"] : ["config.json", "opencode.json", "opencode.jsonc"];
1623
+ return names.map((name) => {
1614
1624
  const file = path.join(openCodeConfigRoot(), name);
1615
1625
  const exists = fs.existsSync(file);
1616
1626
  if (exists && !fs.lstatSync(file).isFile()) throw new Error("OpenCode configuration must be a regular file, not a link. Nothing was changed.");
@@ -1620,7 +1630,7 @@ function openCodeMcpConfigs() {
1620
1630
  if (errors.length || !value || typeof value !== "object" || Array.isArray(value) || (value.mcp !== undefined && (!value.mcp || typeof value.mcp !== "object" || Array.isArray(value.mcp)))) {
1621
1631
  throw new Error("OpenCode configuration is invalid. Fix its JSON/JSONC before MCP setup; nothing was changed.");
1622
1632
  }
1623
- if (value.$schema && value.$schema !== "https://opencode.ai/config.json") throw new Error("This OpenCode configuration schema is unsupported. Use the OpenCode v1 config.json schema before MCP setup.");
1633
+ if (value.$schema && value.$schema !== "https://opencode.ai/config.json") throw new Error("This OpenCode configuration schema is unsupported. Use the official OpenCode config.json schema before MCP setup.");
1624
1634
  return { file, original, exists, value };
1625
1635
  });
1626
1636
  }
@@ -1629,8 +1639,9 @@ function managedOpenCodeMcp(config) {
1629
1639
  return config?.type === "remote" && config.headers?.Authorization === `Bearer {file:${openCodeTokenPath()}}`;
1630
1640
  }
1631
1641
 
1632
- function editOpenCodeMcp(original, value) {
1633
- return jsonc.applyEdits(original || "{}\n", jsonc.modify(original || "{}\n", ["mcp", "shifu"], value, { formattingOptions: { insertSpaces: true, tabSize: 2, eol: "\n" } }));
1642
+ function editOpenCodeMcp(original, value, major = 1) {
1643
+ const target = major === 2 ? ["mcp", "servers", "shifu"] : ["mcp", "shifu"];
1644
+ return jsonc.applyEdits(original || "{}\n", jsonc.modify(original || "{}\n", target, value, { formattingOptions: { insertSpaces: true, tabSize: 2, eol: "\n" } }));
1634
1645
  }
1635
1646
 
1636
1647
  function writePrivateText(file, text) {
@@ -1648,12 +1659,15 @@ function unchangedConfig(config) {
1648
1659
  return fs.existsSync(config.file) === config.exists && (config.exists ? fs.readFileSync(config.file, "utf8") : "") === config.original;
1649
1660
  }
1650
1661
 
1651
- function validateOpenCodeMcpConfig(configs, prepared, mcpUrl) {
1662
+ function validateOpenCodeMcpConfig(configs, prepared, mcpUrl, major) {
1652
1663
  const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "shifu-opencode-mcp-"));
1653
1664
  const configDirectory = path.join(temporary, "config", "opencode");
1654
1665
  const probeToken = path.join(temporary, "probe.key");
1655
1666
  const environment = { ...process.env, HOME: temporary, XDG_CONFIG_HOME: path.join(temporary, "config"), XDG_DATA_HOME: path.join(temporary, "data"), XDG_CACHE_HOME: path.join(temporary, "cache"), XDG_STATE_HOME: path.join(temporary, "state"), OPENCODE_DISABLE_PROJECT_CONFIG: "1" };
1656
- for (const key of ["OPENCODE_CONFIG", "OPENCODE_CONFIG_DIR", "OPENCODE_CONFIG_CONTENT", "OPENCODE_PERMISSION"]) delete environment[key];
1667
+ for (const key of Object.keys(environment)) {
1668
+ if (major === 2 ? key.startsWith("OPENCODE_") : ["OPENCODE_CONFIG", "OPENCODE_CONFIG_DIR", "OPENCODE_CONFIG_CONTENT", "OPENCODE_PERMISSION"].includes(key)) delete environment[key];
1669
+ }
1670
+ environment.OPENCODE_DISABLE_PROJECT_CONFIG = "1";
1657
1671
  try {
1658
1672
  fs.mkdirSync(configDirectory, { recursive: true, mode: 0o700 });
1659
1673
  fs.writeFileSync(probeToken, "shifu-probe-only", { mode: 0o600 });
@@ -1667,34 +1681,57 @@ function validateOpenCodeMcpConfig(configs, prepared, mcpUrl) {
1667
1681
  });
1668
1682
  fs.writeFileSync(path.join(configDirectory, path.basename(config.file)), text, { mode: 0o600 });
1669
1683
  }
1670
- const output = execFileSync(process.env.SHIFU_OPENCODE_BIN || "opencode", ["debug", "config", "--pure"], { cwd: temporary, env: environment, stdio: ["ignore", "pipe", "pipe"], encoding: "utf8", timeout: 30_000, maxBuffer: 8 * 1024 * 1024 });
1671
- const resolved = JSON.parse(output).mcp?.shifu;
1672
- if (resolved?.type !== "remote" || resolved.url !== mcpUrl || resolved.enabled !== true || resolved.oauth !== false || resolved.timeout !== 120_000 || resolved.headers?.Authorization !== "Bearer shifu-probe-only") {
1684
+ const command = process.env.SHIFU_OPENCODE_BIN || "opencode";
1685
+ const options = { cwd: temporary, env: environment, stdio: ["ignore", "pipe", "pipe"], encoding: "utf8", timeout: 30_000, maxBuffer: 8 * 1024 * 1024 };
1686
+ // V2 debug commands otherwise attach to a shared daemon, even with an isolated HOME.
1687
+ if (major === 2) execFileSync(command, ["service", "set", "disabled", "true"], options);
1688
+ const args = major === 2 ? ["debug", "config"] : ["debug", "config", "--pure"];
1689
+ const output = execFileSync(command, args, options);
1690
+ const report = JSON.parse(output);
1691
+ const resolved = major === 2
1692
+ ? report.find((source) => source.type === "document" && source.path === path.join(configDirectory, path.basename(prepared.file)))?.info?.mcp?.servers?.shifu
1693
+ : report.mcp?.shifu;
1694
+ const accepted = major === 2
1695
+ ? resolved?.disabled !== true && resolved?.codemode === false && resolved?.timeout?.startup === 30_000 && resolved?.timeout?.catalog === 30_000 && resolved?.timeout?.execution === 300_000 && resolved?.headers?.Authorization === "***"
1696
+ : resolved?.enabled === true && resolved?.timeout === 120_000 && resolved?.headers?.Authorization === "Bearer shifu-probe-only";
1697
+ if (resolved?.type !== "remote" || resolved.url !== mcpUrl || resolved.oauth !== false || !accepted) {
1673
1698
  throw new Error("OpenCode did not accept the expected Shifu remote MCP configuration.");
1674
1699
  }
1675
1700
  } catch (error) {
1676
1701
  if (error?.code === "ENOENT") throw new Error("The opencode command is unavailable. Install OpenCode CLI before MCP setup.");
1677
1702
  // Native diagnostics may contain expanded secrets from existing settings.
1678
- throw new Error("OpenCode could not validate its v1 remote MCP configuration. Update OpenCode or fix its existing config; nothing was changed.");
1703
+ throw new Error(`OpenCode could not validate its ${major}.x remote MCP configuration. Update OpenCode or fix its existing config; nothing was changed.`);
1679
1704
  } finally {
1680
1705
  fs.rmSync(temporary, { recursive: true, force: true });
1681
1706
  }
1682
1707
  }
1683
1708
 
1684
1709
  function prepareOpenCodeMcpConfig(mcpUrl) {
1685
- const configs = openCodeMcpConfigs();
1710
+ const major = Number(openCodeVersion().split(".")[0]);
1711
+ const configs = openCodeMcpConfigs(major);
1686
1712
  const selected = configs.findLast((config) => config.exists) || configs.at(-1);
1687
- const entry = { type: "remote", url: mcpUrl, enabled: true, oauth: false, timeout: 120_000, headers: { Authorization: `Bearer {file:${openCodeTokenPath()}}` } };
1688
- const prepared = { ...selected, candidate: editOpenCodeMcp(selected.original, entry), configs };
1689
- validateOpenCodeMcpConfig(configs, prepared, mcpUrl);
1713
+ const entry = {
1714
+ type: "remote", url: mcpUrl, oauth: false,
1715
+ headers: { Authorization: `Bearer {file:${openCodeTokenPath()}}` },
1716
+ ...(major === 2
1717
+ ? { disabled: false, codemode: false, timeout: { startup: 30_000, catalog: 30_000, execution: 300_000 } }
1718
+ : { enabled: true, timeout: 120_000 }),
1719
+ };
1720
+ let candidate = selected.original;
1721
+ if (major === 2 && managedOpenCodeMcp(selected.value.mcp?.shifu)) candidate = editOpenCodeMcp(candidate, undefined);
1722
+ const prepared = { ...selected, candidate: editOpenCodeMcp(candidate, entry, major), configs, openCodeMajor: major };
1723
+ validateOpenCodeMcpConfig(configs, prepared, mcpUrl, major);
1690
1724
  return prepared;
1691
1725
  }
1692
1726
 
1693
- function removeOpenCodeMcpConfig() {
1694
- const updates = openCodeMcpConfigs().filter((config) => managedOpenCodeMcp(config.value?.mcp?.shifu));
1727
+ function removeOpenCodeMcpConfig(major) {
1728
+ const updates = openCodeMcpConfigs(major).filter((config) => managedOpenCodeMcp(config.value?.mcp?.shifu) || managedOpenCodeMcp(config.value?.mcp?.servers?.shifu));
1695
1729
  for (const config of updates) {
1696
1730
  if (!unchangedConfig(config)) throw new Error("OpenCode configuration changed during disconnect. Retry to preserve those changes.");
1697
- writePrivateText(config.file, editOpenCodeMcp(config.original, undefined));
1731
+ let candidate = config.original;
1732
+ if (managedOpenCodeMcp(config.value?.mcp?.shifu)) candidate = editOpenCodeMcp(candidate, undefined);
1733
+ if (managedOpenCodeMcp(config.value?.mcp?.servers?.shifu)) candidate = editOpenCodeMcp(candidate, undefined, 2);
1734
+ writePrivateText(config.file, candidate);
1698
1735
  }
1699
1736
  fs.rmSync(openCodeTokenPath(), { force: true });
1700
1737
  }
@@ -1802,7 +1839,7 @@ function contextInstructionFile(harness) {
1802
1839
  // Older clients fall back to XDG rules; current clients use the custom home.
1803
1840
  let version;
1804
1841
  try {
1805
- version = execFileSync(process.env.SHIFU_OPENCODE_BIN || "opencode", ["--version"], { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8", timeout: 10_000, maxBuffer: 1024 * 1024 }).match(/^(\d+\.\d+\.\d+)\b/m)?.[1];
1842
+ version = openCodeVersion();
1806
1843
  } catch { /* Diagnostics can contain settings; report only the remedy. */ }
1807
1844
  if (!version || olderPackageVersion(version, "1.18.30")) throw new Error("OpenCode's custom instruction home conflicts with existing XDG rules. Upgrade to OpenCode 1.18.30 or later, or unset OPENCODE_CONFIG_DIR before MCP setup; nothing was changed.");
1808
1845
  }
@@ -1899,17 +1936,18 @@ async function setupMcp(harness) {
1899
1936
  if (!unchangedContextInstructions(instructions)) throw new Error("The global instructions changed during setup. Run mcp-setup again to preserve those changes.");
1900
1937
  if (harness === "opencode") writePrivateText(openCodeTokenPath(), config.token);
1901
1938
  writePrivateText(prepared.file, prepared.candidate);
1902
- writeJSON(configPath(harness), { ...config, mcpUrl, ...(harness === "claude_code" ? { mcpHeadersHelper: helper } : {}) });
1939
+ writeJSON(configPath(harness), { ...config, mcpUrl, ...(harness === "opencode" ? { mcpConfigMajor: prepared.openCodeMajor } : {}), ...(harness === "claude_code" ? { mcpHeadersHelper: helper } : {}) });
1903
1940
  writeContextInstructions(instructions);
1904
1941
  console.error(`Verified Shifu MCP tools: ${tools.join(", ")}. ${HARNESS_NAMES[harness]} configuration and usage instructions are installed.`);
1905
1942
  console.error(`The existing connection key is read ${harness === "opencode" ? "from a private credential file" : "by a local credential helper"}; it was not copied into MCP configuration. The sync endpoint and schedule were preserved.`);
1906
- console.error(`Proactive context guidance is installed at ${instructions.file}. Reload ${HARNESS_NAMES[harness]} or start a new chat to load the MCP tools and instructions. This setup check does not prove the current chat has loaded them or enforce tool calls.`);
1943
+ const reload = prepared.openCodeMajor === 2 ? "Run opencode service stop, reopen OpenCode and start a new chat" : `Reload ${HARNESS_NAMES[harness]} or start a new chat`;
1944
+ console.error(`Proactive context guidance is installed at ${instructions.file}. ${reload} to load the MCP tools and instructions. This setup check does not prove the current chat has loaded them or enforce tool calls.`);
1907
1945
  }
1908
1946
 
1909
1947
  function disconnect(harness) {
1910
1948
  const config = readConfig(harness);
1911
1949
  if (fs.existsSync(lockPath(harness))) throw new Error("A Shifu sync is running. Try disconnecting again after it finishes.");
1912
- if (harness === "opencode" && (config.mcpUrl || fs.existsSync(openCodeTokenPath()))) removeOpenCodeMcpConfig();
1950
+ if (harness === "opencode" && (config.mcpUrl || fs.existsSync(openCodeTokenPath()))) removeOpenCodeMcpConfig(config.mcpConfigMajor);
1913
1951
  if (harness === "claude_code" && config.mcpUrl) removeClaudeMcpConfig(config.mcpHeadersHelper || helperCommand("claude_code"));
1914
1952
  removeSchedule(harness);
1915
1953
  for (const file of [configPath(harness), statePath(harness), pendingPath(harness)]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useshifu/coding-harness",
3
- "version": "0.3.22",
3
+ "version": "0.3.23",
4
4
  "description": "Scheduled, redacted coding-harness activity sync for Shifu",
5
5
  "bin": {
6
6
  "coding-harness": "bin/shifu-harness.js",
@@ -10,12 +10,12 @@ description: Retrieve relevant work context at substantive task starts and new t
10
10
  When asked to connect Shifu context, run:
11
11
 
12
12
  ```sh
13
- npx --yes @useshifu/coding-harness@0.3.22 mcp-setup --harness codex
13
+ npx --yes @useshifu/coding-harness@0.3.23 mcp-setup --harness codex
14
14
  ```
15
15
 
16
16
  For a branch deployment, append `--mcp-url` with the user's supplied HTTPS endpoint ending in `/mcp`. Keep the existing sync API endpoint. The CLI reuses this harness's saved key, checks the authenticated tools, and preserves existing settings. Codex and Claude Code use local credential helpers; OpenCode uses its native `{file:...}` substitution pointing to a private credential file. The key is never placed literally in MCP configuration. If no connection exists, the CLI asks for this harness's key in the terminal. Never request or print the key in chat or place it in command arguments. When input is unavailable, have the user run the command in an interactive terminal. Do not substitute plaintext credentials for an unsupported credential mechanism.
17
17
 
18
- Setup also installs a managed read-only context policy in the harness’s native global instruction file, preserving existing instructions. Reload the harness or start a new chat after setup. Check that the tools are actually available before claiming this chat can use them. Setup supports local macOS/Linux connections; remote execution environments cannot read this device's credential helper or file. OpenCode requires the v1 config schema (verified with 1.18.30). Claude Code requires HTTP MCP with `headersHelper` (verified with 2.1.295); setup validates it through the installed CLI. If Claude Code is outdated, help the user update it using official installation instructions before retrying. The connection uses user scope and honors `CLAUDE_CONFIG_DIR`. Restart Claude Code and start a new session, or use `/mcp` to reconnect after rotating the saved key. Unsupported versions or config schemas fail before MCP settings change.
18
+ Setup also installs a managed read-only context policy in the harness’s native global instruction file, preserving existing instructions. Reload the harness or start a new chat after setup. Check that the tools are actually available before claiming this chat can use them. Setup supports local macOS/Linux connections; remote execution environments cannot read this device's credential helper or file. OpenCode setup detects 1.x or 2.x, writes the matching MCP shape and validates it through the installed client. V2 exposes Shifu tools directly and uses separate startup, catalog and execution budgets. The saved OpenCode key remains in its private credential file. After V2 setup, tell the user to run `opencode service stop`, reopen OpenCode and start a new chat; do not stop a running user service automatically. Claude Code requires HTTP MCP with `headersHelper` (verified with 2.1.295); setup validates it through the installed CLI. If Claude Code is outdated, help the user update it using official installation instructions before retrying. The connection uses user scope and honors `CLAUDE_CONFIG_DIR`. Restart Claude Code and start a new session, or use `/mcp` to reconnect after rotating the saved key. Unsupported versions or config schemas fail before MCP settings change.
19
19
 
20
20
  ## Consult context
21
21