harness-everything 0.30.4 → 0.30.6

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.
Files changed (71) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +38 -0
  4. package/README.md +17 -7
  5. package/bin/cli.js +5 -0
  6. package/ci/fable-mode-test.js +112 -51
  7. package/ci/mechanism-2t-workspace-identity.test.js +3 -1
  8. package/ci/mechanism-2w-platform-compatibility.test.js +39 -0
  9. package/ci/mechanism-2z-claude-plugin-manifest.test.js +12 -0
  10. package/ci/router-workflow-plan.test.js +92 -7
  11. package/docs/platform-capabilities.md +14 -1
  12. package/docs/troubleshooting.md +31 -1
  13. package/docs/workflows/fable-mode.md +10 -9
  14. package/evals/fable-mode/tasks/negative-tier2-no-model.yaml +3 -3
  15. package/evals/fable-mode/tasks/positive-haiku-mode.yaml +1 -1
  16. package/evals/fable-mode/tasks/positive-opus-mode.yaml +1 -1
  17. package/evals/fable-mode/tasks/positive-sonnet-mode.yaml +1 -1
  18. package/evals/fable-mode/tasks/positive-trigger.yaml +1 -1
  19. package/fable-mode/SKILL.md +18 -12
  20. package/fable-mode/agents/fable-orchestrator.md +7 -6
  21. package/fable-mode/agents/fable-verifier.md +1 -1
  22. package/fable-mode/agents/fable-worker-haiku.md +5 -4
  23. package/fable-mode/agents/fable-worker-sonnet.md +5 -4
  24. package/fable-mode/behavior-profile-matrix.json +36 -0
  25. package/fable-mode/execution-guardrails/SKILL.md +3 -6
  26. package/fable-mode/execution-guardrails/references/guardrail-rules.md +8 -9
  27. package/fable-mode/fable-haiku/SKILL.md +7 -5
  28. package/fable-mode/fable-haiku/references/routing-notes.md +2 -1
  29. package/fable-mode/fable-opus/SKILL.md +8 -6
  30. package/fable-mode/fable-sonnet/SKILL.md +8 -6
  31. package/fable-mode/model-matrix.json +7 -30
  32. package/fable-mode/references/model-matrix.md +35 -30
  33. package/fable-mode/references/profile-lookup.md +19 -0
  34. package/fable-mode/runtime-model-floor-matrix.json +47 -0
  35. package/fable-mode/scripts/model-selector.js +106 -60
  36. package/fable-mode/scripts/workflow-plan-consumer.js +1 -1
  37. package/harness-everything/SKILL.md +11 -14
  38. package/harness-everything/references/router-workflow-plan.md +2 -2
  39. package/harness-everything/schemas/router-task-shape.schema.json +3 -2
  40. package/harness-everything/schemas/router-workflow-plan.schema.json +6 -5
  41. package/harness-everything/scripts/router-contract-core.js +19 -12
  42. package/harness-everything/scripts/tier-router.js +100 -12
  43. package/opencode-plugin/plugin.json +1 -1
  44. package/package.json +1 -1
  45. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  46. package/plugins/harness-everything/plugin.json +1 -1
  47. package/plugins/harness-everything/skills/fable-mode/SKILL.md +18 -12
  48. package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +7 -6
  49. package/plugins/harness-everything/skills/fable-mode/agents/fable-verifier.md +1 -1
  50. package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-haiku.md +5 -4
  51. package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-sonnet.md +5 -4
  52. package/plugins/harness-everything/skills/fable-mode/behavior-profile-matrix.json +36 -0
  53. package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +3 -6
  54. package/plugins/harness-everything/skills/fable-mode/execution-guardrails/references/guardrail-rules.md +8 -9
  55. package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +7 -5
  56. package/plugins/harness-everything/skills/fable-mode/fable-haiku/references/routing-notes.md +2 -1
  57. package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +8 -6
  58. package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +8 -6
  59. package/plugins/harness-everything/skills/fable-mode/model-matrix.json +7 -30
  60. package/plugins/harness-everything/skills/fable-mode/references/model-matrix.md +35 -30
  61. package/plugins/harness-everything/skills/fable-mode/references/profile-lookup.md +19 -0
  62. package/plugins/harness-everything/skills/fable-mode/runtime-model-floor-matrix.json +47 -0
  63. package/plugins/harness-everything/skills/fable-mode/scripts/model-selector.js +106 -60
  64. package/plugins/harness-everything/skills/fable-mode/scripts/workflow-plan-consumer.js +1 -1
  65. package/plugins/harness-everything/skills/harness-everything/SKILL.md +11 -14
  66. package/plugins/harness-everything/skills/harness-everything/references/router-workflow-plan.md +2 -2
  67. package/plugins/harness-everything/skills/harness-everything/schemas/router-task-shape.schema.json +3 -2
  68. package/plugins/harness-everything/skills/harness-everything/schemas/router-workflow-plan.schema.json +6 -5
  69. package/plugins/harness-everything/skills/harness-everything/scripts/router-contract-core.js +19 -12
  70. package/plugins/harness-everything/skills/harness-everything/scripts/tier-router.js +100 -12
  71. package/sbom.cdx.json +7 -7
@@ -10,7 +10,7 @@
10
10
  "name": "harness-everything",
11
11
  "source": "./",
12
12
  "description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
13
- "version": "0.30.4",
13
+ "version": "0.30.6",
14
14
  "author": {
15
15
  "name": "Miya Daniel"
16
16
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "harness-everything",
3
3
  "description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
4
- "version": "0.30.4",
4
+ "version": "0.30.6",
5
5
  "author": {
6
6
  "name": "Miya Daniel"
7
7
  },
package/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.30.6](https://github.com/dyphn1/Harness-everything/compare/v0.30.5...v0.30.6) (2026-10-06)
6
+
7
+ ### Bug Fixes
8
+
9
+ * **fable-mode:** align Claude nested skill contract ([aadfe34](https://github.com/dyphn1/Harness-everything/commit/aadfe349e6f9a6ce9623f9b408ad8c6d9958208e))
10
+ * **fable-mode:** compress guardrail reference contract ([302efcd](https://github.com/dyphn1/Harness-everything/commit/302efcd0433c75be10619dbad7aa2b925b57400f))
11
+ * **fable-mode:** preserve unknown audit states and raw aliases ([2716be9](https://github.com/dyphn1/Harness-everything/commit/2716be9feb3e6b986c0f5da884ed4a686b95791b))
12
+ * **fable-mode:** remove residual model-gated routing ([32fee42](https://github.com/dyphn1/Harness-everything/commit/32fee423e9af7416172bf146cb876f72d61e331e))
13
+ * **fable-mode:** stay within skill token budget ([d934d36](https://github.com/dyphn1/Harness-everything/commit/d934d36cef17d5e0b30dfffd888cdab5d7c9a747))
14
+ * **fable:** accept curly-quoted lookups and restore execution deep dive ([08517e4](https://github.com/dyphn1/Harness-everything/commit/08517e4469bab46042358df136446e2495144a7f)), closes [#290](https://github.com/dyphn1/Harness-everything/issues/290)
15
+ * **fable:** dispatch profile lookups before loading stage references ([58ce883](https://github.com/dyphn1/Harness-everything/commit/58ce8834d3fa0ab288f9106f26758d4945ca49b6)), closes [#290](https://github.com/dyphn1/Harness-everything/issues/290)
16
+ * **router:** preserve Fable profile alias across normalization ([50e84da](https://github.com/dyphn1/Harness-everything/commit/50e84da3f14216800e4c757996663debfb7a8f7a))
17
+ * **router:** scope fable lookup negations to their own list ([032b52b](https://github.com/dyphn1/Harness-everything/commit/032b52bba809237c4b891e44fc7ee7b082262ddb)), closes [#290](https://github.com/dyphn1/Harness-everything/issues/290)
18
+ * **router:** select bounded Fable profile lookups before staged topology ([0d6fdac](https://github.com/dyphn1/Harness-everything/commit/0d6fdac675a9225f9425fcb699444992032371a3)), closes [#290](https://github.com/dyphn1/Harness-everything/issues/290)
19
+
20
+ ## [Unreleased]
21
+
22
+ ### Fixed
23
+
24
+ - Select a bounded Fable profile lookup before loading staged workflow references; preserve execution topology and required obligations (#290).
25
+ - Validate entire Fable lookup clauses so same-clause execution keeps its obligations, while canonical JSON field lists remain bounded lookups (#294).
26
+ - Scope lookup negations to their own noun list so an un-negated action after a negation keeps execution routing (#294).
27
+
28
+ ## [0.30.5](https://github.com/dyphn1/Harness-everything/compare/v0.30.4...v0.30.5) (2026-10-06)
29
+
30
+ ### Bug Fixes
31
+
32
+ * align Windows PowerShell quick-start contract ([f780f77](https://github.com/dyphn1/Harness-everything/commit/f780f777559d14f9bcf929793d954ddcaf2c6cf7))
33
+ * enforce Windows-safe shell contract ([a373a16](https://github.com/dyphn1/Harness-everything/commit/a373a16f5bdd3659e290715c971f530e66ffe2c1))
34
+ * fit Harness routing contract within budget ([6fa9065](https://github.com/dyphn1/Harness-everything/commit/6fa90657611bcbc85f669e864a3245b469e99d23))
35
+ * keep host-shell invariant within skill budget ([1381dc9](https://github.com/dyphn1/Harness-everything/commit/1381dc9877a0facccbea8f31a3047055eabaf5b2))
36
+ * preserve semantic contract phrases within Waza budget ([ab29217](https://github.com/dyphn1/Harness-everything/commit/ab292175cc691c6bd01d1e0da219a15c6779f7be))
37
+ * preserve skill metadata while restoring token budget ([189660a](https://github.com/dyphn1/Harness-everything/commit/189660ad6283ecad84d8a07232515fd80da6691c))
38
+ * restore Harness skill token budget ([80b1eee](https://github.com/dyphn1/Harness-everything/commit/80b1eeeceb681d9551bfbe4d3c881ae2c3e5747f))
39
+ * restore required skill sections within Waza budget ([acbb314](https://github.com/dyphn1/Harness-everything/commit/acbb314ec4e7769c2a2c253918ac67e1158052fe))
40
+ * show Windows-safe npx entry point ([d208b77](https://github.com/dyphn1/Harness-everything/commit/d208b7735912b7282dc158068b3e5c17a5c62e0d))
41
+ * sync packaged Windows shell contract ([201eb5a](https://github.com/dyphn1/Harness-everything/commit/201eb5a53cbea4e696db633f857b6c30c15f1855))
42
+
5
43
  ## [0.30.4](https://github.com/dyphn1/Harness-everything/compare/v0.30.3...v0.30.4) (2026-10-01)
6
44
 
7
45
  ### Bug Fixes
package/README.md CHANGED
@@ -56,12 +56,14 @@ Harness integrates directly into your workspace. There is no heavy daemon, no pa
56
56
  # /plugin install harness-everything
57
57
 
58
58
  # Option B: install Harness hooks/skills/advisory integrations into your workspace
59
- npx github:dyphn1/Harness-everything install
59
+ npx github:dyphn1/Harness-everything install # POSIX shells / Git Bash
60
+ npx.cmd github:dyphn1/Harness-everything install # Windows PowerShell
60
61
 
61
62
  # Option C: install or update the native Claude Code/Codex plugin
62
- # npx github:dyphn1/Harness-everything plugin-sync
63
- # ./scripts/plugin-sync.sh # POSIX shells / Git Bash
64
- # powershell -File scripts/plugin-sync.ps1 # Windows PowerShell
63
+ # npx github:dyphn1/Harness-everything plugin-sync # POSIX shells / Git Bash
64
+ # npx.cmd github:dyphn1/Harness-everything plugin-sync # Windows PowerShell
65
+ # ./scripts/plugin-sync.sh # POSIX shells / Git Bash
66
+ # node scripts/plugin-sync.js # Windows-safe direct entry
65
67
 
66
68
  # OpenAI/Codex local plugin packaging is repository-owned under:
67
69
  # .agents/plugins/marketplace.json
@@ -69,6 +71,8 @@ npx github:dyphn1/Harness-everything install
69
71
  # See docs/openai-plugin.md for local import/install and public Skills-only submission.
70
72
  ```
71
73
 
74
+ **Windows PowerShell:** use npm-family `.cmd` shims such as `npx.cmd` rather than weakening `ExecutionPolicy`. When reading Harness Markdown from Windows PowerShell 5.1, specify UTF-8 explicitly, for example `Get-Content -Encoding UTF8 -Raw <path>`.
75
+
72
76
  ### Expected Behavior After Installation:
73
77
  1. **Use the selected surface's real mechanism:** Claude Code hooks, the local OpenAI plugin lifecycle hooks, OpenCode's plugin API, or advisory instructions depending on what you installed.
74
78
  2. **Preflight / session context where packaged:** Hook-capable surfaces can inject environment/session context automatically; advisory-only surfaces must not be described as if they do.
@@ -100,9 +104,15 @@ The native `plugin-sync` command detects the installed host CLIs and applies the
100
104
  The installer records its state directories in `.git/info/exclude` — a local-only git ignore file — so Harness state never lands in a commit and your working tree (including `.gitignore`) is never modified. Everything owned by the general installer is removed with the built-in uninstaller:
101
105
 
102
106
  ```bash
103
- npx github:dyphn1/Harness-everything uninstall # interactive
104
- npx github:dyphn1/Harness-everything uninstall --local --skills -y # non-interactive, workspace only
105
- npx github:dyphn1/Harness-everything uninstall --global # also remove Harness-owned global state
107
+ # POSIX shells / Git Bash
108
+ npx github:dyphn1/Harness-everything uninstall
109
+ npx github:dyphn1/Harness-everything uninstall --local --skills -y
110
+ npx github:dyphn1/Harness-everything uninstall --global
111
+
112
+ # Windows PowerShell
113
+ npx.cmd github:dyphn1/Harness-everything uninstall
114
+ npx.cmd github:dyphn1/Harness-everything uninstall --local --skills -y
115
+ npx.cmd github:dyphn1/Harness-everything uninstall --global
106
116
  ```
107
117
 
108
118
  ---
package/bin/cli.js CHANGED
@@ -55,6 +55,11 @@ Harness OS - AI Agent Operating System CLI
55
55
 
56
56
  Usage:
57
57
  npx github:dyphn1/Harness-everything <command> [options]
58
+ npx.cmd github:dyphn1/Harness-everything <command> [options] # Windows PowerShell
59
+
60
+ Windows PowerShell note:
61
+ Use npx.cmd so restrictive ExecutionPolicy cannot redirect this command to npx.ps1.
62
+ Harness does not require changing ExecutionPolicy.
58
63
 
59
64
  Commands:
60
65
  install Install Harness OS hooks & skills into the local repository
@@ -5,6 +5,7 @@ const fs = require('fs');
5
5
  const os = require('os');
6
6
  const { spawnSync } = require('child_process');
7
7
  const path = require('path');
8
+ const { evaluateRuntimeFloor } = require('../fable-mode/scripts/model-selector.js');
8
9
 
9
10
  const root = path.resolve(__dirname, '..');
10
11
  const selector = path.join(root, 'fable-mode', 'scripts', 'model-selector.js');
@@ -13,19 +14,17 @@ const auditDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-fable-mode-'));
13
14
  const auditFile = path.join(auditDir, 'audit.jsonl');
14
15
 
15
16
  function runNode(file, args) {
16
- return spawnSync(process.execPath, [file, ...args], {
17
- cwd: root,
18
- encoding: 'utf8'
19
- });
17
+ return spawnSync(process.execPath, [file, ...args], { cwd: root, encoding: 'utf8' });
20
18
  }
21
19
 
22
20
  function selectorArgs(overrides = {}) {
23
21
  const input = {
24
22
  requested: 'opus',
25
- available: 'opus,sonnet,haiku',
26
23
  'available-agents': 'fable-orchestrator,fable-worker-sonnet,fable-worker-haiku',
27
24
  fallback: 'stop',
28
- 'host-model': 'opus',
25
+ host: 'codex',
26
+ 'runtime-model': 'gpt-6.1-sol',
27
+ 'runtime-effort': 'xhigh',
29
28
  'audit-file': auditFile,
30
29
  'stage-brief': 'Choose the architecture',
31
30
  'pass-condition': 'ADR exists and names the decision',
@@ -33,104 +32,166 @@ function selectorArgs(overrides = {}) {
33
32
  'verifier-result': 'pending',
34
33
  ...overrides
35
34
  };
36
- return Object.entries(input).flatMap(([key, value]) => [`--${key}`, value]);
35
+ return Object.entries(input).flatMap(([key, value]) => ['--' + key, String(value)]);
37
36
  }
38
37
 
39
38
  function readRecord(result) {
40
- assert.ok(result.stdout.trim(), `selector produced no JSON: ${result.stderr}`);
39
+ assert.ok(result.stdout.trim(), 'selector produced no JSON: ' + result.stderr);
41
40
  return JSON.parse(result.stdout);
42
41
  }
43
42
 
44
- console.log('=== Fable model mode contract tests ===');
43
+ console.log('=== Fable behavior profile + runtime floor contract tests ===');
44
+
45
+ const publicDescription = 'Stage large tasks through plans, named fable agents, failable checks, and skeptical review; fable on opus|sonnet|haiku selects a behavior profile, not a runtime model.';
46
+ const fableSkillText = fs.readFileSync(path.join(root, 'fable-mode', 'SKILL.md'), 'utf8');
47
+ assert.ok(fableSkillText.includes('description: "' + publicDescription + '"'));
48
+
49
+ for (const task of ['positive-opus-mode.yaml', 'positive-sonnet-mode.yaml', 'positive-haiku-mode.yaml', 'positive-trigger.yaml']) {
50
+ const source = fs.readFileSync(path.join(root, 'evals', 'fable-mode', 'tasks', task), 'utf8');
51
+ assert.ok(source.includes('description: "' + publicDescription + '"'), task + ' must keep the exact public fable-mode description');
52
+ }
53
+
54
+ const behavior = JSON.parse(fs.readFileSync(path.join(root, 'fable-mode', 'behavior-profile-matrix.json'), 'utf8'));
55
+ assert.strictEqual(behavior.profiles.orchestrator.legacyAlias, 'opus');
56
+ assert.ok(behavior.profiles.reasoning.aliases.includes('sonnect'));
57
+
58
+ const floors = JSON.parse(fs.readFileSync(path.join(root, 'fable-mode', 'runtime-model-floor-matrix.json'), 'utf8'));
59
+ assert.strictEqual(floors.hosts.claude.orchestrator.minVersion, '5.5');
60
+ assert.strictEqual(floors.hosts.claude.orchestrator.minEffort, 'medium');
61
+ assert.strictEqual(floors.hosts.claude.reasoning.minVersion, '5.5');
62
+ assert.strictEqual(floors.hosts.claude.mechanical.minVersion, '5.5');
63
+ assert.strictEqual(floors.hosts.codex.orchestrator.minGeneration, 6);
64
+ assert.strictEqual(floors.hosts.codex.orchestrator.minEffort, 'xhigh');
65
+ assert.strictEqual(floors.hosts.codex.reasoning.minEffort, 'medium');
66
+ assert.strictEqual(floors.hosts.codex.mechanical.family, 'luna');
67
+ assert.strictEqual(floors.hosts.codex.mechanical.minEffort, 'xhigh');
68
+
69
+ for (const profile of ['fable-opus', 'fable-sonnet', 'fable-haiku']) {
70
+ const source = fs.readFileSync(path.join(root, 'fable-mode', profile, 'SKILL.md'), 'utf8');
71
+ assert.match(source, /Reference profile, not a standalone plugin skill/);
72
+ }
73
+ for (const agent of ['fable-orchestrator.md', 'fable-worker-sonnet.md', 'fable-worker-haiku.md', 'fable-verifier.md']) {
74
+ const source = fs.readFileSync(path.join(root, 'fable-mode', 'agents', agent), 'utf8');
75
+ assert.match(source, /^model: inherit$/m, agent + ' must not pin a branded runtime model');
76
+ }
77
+
78
+ const guardrailSource = fs.readFileSync(path.join(root, 'fable-mode', 'execution-guardrails', 'SKILL.md'), 'utf8');
79
+ assert.match(guardrailSource, /reference-only, not an always-on carrier/);
80
+ assert.doesNotMatch(guardrailSource, /These rules are always-on/);
45
81
 
46
82
  const selected = runNode(selector, selectorArgs());
47
83
  assert.strictEqual(selected.status, 0, selected.stderr);
48
84
  const selectedRecord = readRecord(selected);
49
85
  assert.strictEqual(selectedRecord.status, 'selected');
50
- assert.strictEqual(selectedRecord.requestedModel, 'opus');
51
- assert.strictEqual(selectedRecord.effectiveModel, 'opus');
52
- assert.strictEqual(selectedRecord.fallbackReason, 'none');
86
+ assert.strictEqual(selectedRecord.requestedProfile, 'orchestrator');
87
+ assert.strictEqual(selectedRecord.effectiveProfile, 'orchestrator');
88
+ assert.strictEqual(selectedRecord.profileAlias, 'opus');
53
89
  assert.strictEqual(selectedRecord.agent, 'fable-orchestrator');
90
+ assert.strictEqual(selectedRecord.runtimeFloorStatus, 'meets-recommended');
54
91
 
55
- const sonnect = runNode(selector, selectorArgs({ requested: 'sonnect', available: 'haiku,sonnet' }));
92
+ const sonnect = runNode(selector, selectorArgs({
93
+ requested: 'sonnect',
94
+ host: 'claude',
95
+ 'runtime-model': 'claude-sonnet-5.5',
96
+ 'runtime-effort': 'medium'
97
+ }));
56
98
  assert.strictEqual(sonnect.status, 0, sonnect.stderr);
57
99
  const sonnectRecord = readRecord(sonnect);
58
- assert.strictEqual(sonnectRecord.requestedModel, 'sonnet');
59
- assert.strictEqual(sonnectRecord.requestedInput, 'sonnect');
100
+ assert.strictEqual(sonnectRecord.requestedProfile, 'reasoning');
101
+ assert.strictEqual(sonnectRecord.profileAlias, 'sonnect');
102
+ assert.strictEqual(sonnectRecord.runtimeFloorStatus, 'meets-recommended');
60
103
 
61
- const inline = runNode(selector, selectorArgs({
104
+ const belowFloor = runNode(selector, selectorArgs({
62
105
  requested: 'opus',
63
- available: 'sonnet',
64
- fallback: 'inline',
65
- 'host-model': 'sonnet'
106
+ host: 'codex',
107
+ 'runtime-model': 'gpt-6-sol',
108
+ 'runtime-effort': 'medium'
109
+ }));
110
+ assert.strictEqual(belowFloor.status, 0, belowFloor.stderr);
111
+ const belowFloorRecord = readRecord(belowFloor);
112
+ assert.strictEqual(belowFloorRecord.status, 'selected');
113
+ assert.strictEqual(belowFloorRecord.effectiveProfile, 'orchestrator');
114
+ assert.strictEqual(belowFloorRecord.runtimeFloorStatus, 'below-recommended');
115
+ assert.strictEqual(belowFloorRecord.escalationRequired, false);
116
+
117
+ const unknownFamily = evaluateRuntimeFloor('reasoning', {
118
+ host: 'codex',
119
+ runtimeModel: 'vendor-private-model',
120
+ runtimeEffort: 'xhigh'
121
+ });
122
+ assert.strictEqual(unknownFamily.status, 'unknown');
123
+ assert.match(unknownFamily.reason, /not comparable/);
124
+
125
+ const unknownEffort = evaluateRuntimeFloor('reasoning', {
126
+ host: 'codex',
127
+ runtimeModel: 'gpt-6.1-sol',
128
+ runtimeEffort: 'custom-effort'
129
+ });
130
+ assert.strictEqual(unknownEffort.status, 'unknown');
131
+ assert.match(unknownEffort.reason, /not comparable/);
132
+
133
+ const inline = runNode(selector, selectorArgs({
134
+ requested: 'sonnet',
135
+ 'available-agents': 'fable-worker-haiku',
136
+ fallback: 'inline'
66
137
  }));
67
138
  assert.strictEqual(inline.status, 0, inline.stderr);
68
139
  const inlineRecord = readRecord(inline);
69
140
  assert.strictEqual(inlineRecord.status, 'fallback');
70
- assert.strictEqual(inlineRecord.effectiveModel, 'sonnet');
71
- assert.match(inlineRecord.fallbackReason, /opus is not in availableModels/);
72
- assert.strictEqual(inlineRecord.escalationRequired, false);
141
+ assert.strictEqual(inlineRecord.effectiveProfile, 'reasoning');
142
+ assert.match(inlineRecord.fallbackReason, /execute the same reasoning behavior profile inline/);
73
143
 
74
- const blocked = runNode(selector, selectorArgs({ requested: 'opus', available: 'sonnet' }));
144
+ const blocked = runNode(selector, selectorArgs({
145
+ requested: 'sonnet',
146
+ 'available-agents': 'fable-worker-haiku',
147
+ fallback: 'stop'
148
+ }));
75
149
  assert.strictEqual(blocked.status, 2);
76
150
  const blockedRecord = readRecord(blocked);
77
151
  assert.strictEqual(blockedRecord.status, 'blocked');
78
- assert.strictEqual(blockedRecord.effectiveModel, null);
152
+ assert.strictEqual(blockedRecord.effectiveProfile, null);
79
153
  assert.strictEqual(blockedRecord.escalationRequired, true);
80
154
  assert.match(blockedRecord.fallbackReason, /inline fallback was not authorized/);
81
155
 
82
- const missingAgent = runNode(selector, selectorArgs({
83
- requested: 'sonnet',
84
- available: 'sonnet',
85
- 'available-agents': 'fable-worker-haiku',
86
- fallback: 'inline',
87
- 'host-model': 'opus'
88
- }));
89
- assert.strictEqual(missingAgent.status, 0, missingAgent.stderr);
90
- const missingAgentRecord = readRecord(missingAgent);
91
- assert.strictEqual(missingAgentRecord.status, 'fallback');
92
- assert.strictEqual(missingAgentRecord.effectiveModel, 'opus');
93
- assert.match(missingAgentRecord.fallbackReason, /fable-worker-sonnet is not in availableAgents/);
94
-
95
156
  const missingField = runNode(selector, selectorArgs({ 'pass-condition': '' }));
96
157
  assert.strictEqual(missingField.status, 2);
97
158
  assert.match(missingField.stderr, /passCondition is required/);
98
159
 
99
160
  const explicitRoute = runNode(router, ['fable on sonnect for architecture synthesis']);
100
161
  assert.strictEqual(explicitRoute.status, 0);
101
- assert.match(explicitRoute.stdout, /REQUESTED FABLE MODEL MODE: sonnet/);
102
- assert.match(explicitRoute.stdout, /fable-mode\/fable-sonnet\/SKILL\.md/);
162
+ assert.match(explicitRoute.stdout, /REQUESTED FABLE PROFILE: sonnect/);
163
+ assert.match(explicitRoute.stdout, /=> ROUTE: fable-mode\/SKILL\.md/);
164
+ assert.doesNotMatch(explicitRoute.stdout, /REQUESTED FABLE MODEL MODE/);
165
+ assert.doesNotMatch(explicitRoute.stdout, /=> ROUTE: fable-mode\/fable-sonnet\/SKILL\.md/);
103
166
 
104
167
  const ordinaryTier2 = runNode(router, ['Fix this checkout bug and add a regression test.']);
105
168
  assert.strictEqual(ordinaryTier2.status, 0);
106
169
  assert.match(ordinaryTier2.stdout, /RECOMMENDED TIER: Tier 2/);
107
- assert.doesNotMatch(ordinaryTier2.stdout, /REQUESTED FABLE MODEL MODE/);
108
- assert.doesNotMatch(ordinaryTier2.stdout, /fable-opus\/SKILL\.md/);
170
+ assert.doesNotMatch(ordinaryTier2.stdout, /REQUESTED FABLE PROFILE/);
109
171
 
110
172
  const warningBatchContract = '**Warning batching.** Collect minor concerns and list them together in the next natural report or handoff; their count alone MUST NOT stop, pause, or return the worker/stage. An independently material, confirmed concern may stop the current stage and be surfaced immediately.';
111
173
  for (const rel of [
112
174
  'fable-mode/agents/fable-worker-haiku.md',
113
175
  'fable-mode/agents/fable-worker-sonnet.md',
114
176
  'fable-mode/agents/fable-orchestrator.md',
115
- 'fable-mode/execution-guardrails/SKILL.md',
177
+ 'fable-mode/execution-guardrails/SKILL.md'
116
178
  ]) {
117
179
  const prompt = fs.readFileSync(path.join(root, rel), 'utf8').replace(/\s+/g, ' ');
118
- assert.ok(prompt.includes(warningBatchContract), `${rel} must keep minor-concern batching reporting-only`);
119
- assert.doesNotMatch(
120
- prompt,
121
- /minor concerns.{0,200}stop and surface all at once before continuing/i,
122
- `${rel} must not introduce a warning-count stop condition`
123
- );
180
+ assert.ok(prompt.includes(warningBatchContract), rel + ' must keep minor-concern batching reporting-only');
124
181
  }
125
182
 
126
183
  const auditLines = fs.readFileSync(auditFile, 'utf8').trim().split(/\r?\n/).filter(Boolean);
127
184
  assert.strictEqual(auditLines.length, 5);
128
185
  for (const line of auditLines) {
129
186
  const record = JSON.parse(line);
130
- for (const field of ['requestedModel', 'effectiveModel', 'fallbackReason', 'stageBrief', 'passCondition', 'verificationCommand', 'verifierResult']) {
131
- assert.ok(Object.prototype.hasOwnProperty.call(record, field), `audit missing ${field}`);
187
+ for (const field of [
188
+ 'requestedProfile', 'effectiveProfile', 'profileAlias', 'runtimeModel', 'runtimeEffort',
189
+ 'recommendedRuntimeFloor', 'runtimeFloorStatus', 'runtimeFloorReason', 'fallbackReason',
190
+ 'stageBrief', 'passCondition', 'verificationCommand', 'verifierResult'
191
+ ]) {
192
+ assert.ok(Object.prototype.hasOwnProperty.call(record, field), 'audit missing ' + field);
132
193
  }
133
194
  }
134
195
  fs.rmSync(auditDir, { recursive: true, force: true });
135
196
 
136
- console.log('PASS: selected, sonnect alias, inline fallback, blocked escalation, audit fields, and routing separation');
197
+ console.log('PASS: profiles are behavior contracts; runtime floors are advisory; agent fallback remains explicit');
@@ -131,7 +131,9 @@ check('hooks can resolve state from a bound session after cwd changes', result.s
131
131
  const installed = path.join(fixture, 'installed', 'fable-mode', 'scripts');
132
132
  fs.mkdirSync(installed, { recursive: true });
133
133
  fs.copyFileSync(path.join(root, 'fable-mode', 'scripts', 'model-selector.js'), path.join(installed, 'model-selector.js'));
134
- fs.copyFileSync(path.join(root, 'fable-mode', 'model-matrix.json'), path.join(installed, '..', 'model-matrix.json'));
134
+ for (const matrix of ['behavior-profile-matrix.json', 'runtime-model-floor-matrix.json']) {
135
+ fs.copyFileSync(path.join(root, 'fable-mode', matrix), path.join(installed, '..', matrix));
136
+ }
135
137
  const fable = path.join(installed, 'model-selector.js');
136
138
  const fableArgs = ['--requested', 'haiku', '--available', 'haiku', '--available-agents', 'fable-worker-haiku', '--stage-brief', 'identity', '--pass-condition', 'audit', '--verification-command', 'node test', '--verifier-result', 'pass', '--session-id', 'issue-42-fable-session'];
137
139
  result = spawnSync(process.execPath, [fable, ...fableArgs], { cwd: outer, env: baseEnv, encoding: 'utf8' });
@@ -152,7 +152,46 @@ try {
152
152
  fs.rmSync(tempOutput.replace(/\.zip$/i, '') + '.manifest.json', { force: true });
153
153
  }
154
154
 
155
+ // Windows PowerShell 5.1 treats BOM-less UTF-8 as a legacy code page when
156
+ // Get-Content has no explicit encoding, and resolves bare npm-family commands
157
+ // to .ps1 shims before .cmd. The routing skill is the agent-facing boundary
158
+ // that must prevent both failures before Harness itself starts.
159
+ const canonicalHarnessSkill = fs.readFileSync(path.join(ROOT, 'harness-everything', 'SKILL.md'), 'utf8');
160
+ const packagedHarnessSkill = fs.readFileSync(path.join(ROOT, 'plugins', 'harness-everything', 'skills', 'harness-everything', 'SKILL.md'), 'utf8');
161
+ assert.match(canonicalHarnessSkill, /Windows PowerShell/);
162
+ assert.match(canonicalHarnessSkill, /npx\.cmd/);
163
+ assert.match(canonicalHarnessSkill, /Get-Content -Encoding UTF8 -Raw/);
164
+ assert.strictEqual(packagedHarnessSkill, canonicalHarnessSkill, 'packaged Harness skill must preserve the Windows shell contract byte-for-text');
165
+ assert.match(canonicalHarnessSkill, /—/u, 'UTF-8 em dash must remain intact');
166
+ assert.match(canonicalHarnessSkill, /🚦 Harness Status/u, 'UTF-8 Harness Status marker must remain intact');
167
+
168
+ const readme = fs.readFileSync(path.join(ROOT, 'README.md'), 'utf8');
169
+ assert.match(readme, /npx\.cmd github:dyphn1\/Harness-everything install/);
170
+ assert.match(readme, /npx\.cmd github:dyphn1\/Harness-everything uninstall/);
171
+ assert.match(readme, /npx\.cmd github:dyphn1\/Harness-everything uninstall --local --skills -y/);
172
+ assert.match(readme, /npx\.cmd github:dyphn1\/Harness-everything uninstall --global/);
173
+ assert.match(readme, /Get-Content -Encoding UTF8 -Raw/);
174
+ assert.doesNotMatch(readme, /powershell -File scripts\/plugin-sync\.ps1/);
175
+
176
+ const troubleshootingDocs = fs.readFileSync(path.join(ROOT, 'docs', 'troubleshooting.md'), 'utf8');
177
+ for (const command of [
178
+ 'install',
179
+ 'install --global',
180
+ 'uninstall',
181
+ 'install --cursor',
182
+ 'install --codex',
183
+ 'plugin-sync --json'
184
+ ]) {
185
+ assert.ok(
186
+ troubleshootingDocs.includes(`npx.cmd github:dyphn1/Harness-everything ${command}`),
187
+ `troubleshooting must include Windows PowerShell form for: ${command}`
188
+ );
189
+ }
190
+
155
191
  const capabilityDocs = fs.readFileSync(path.join(ROOT, 'docs', 'platform-capabilities.md'), 'utf8');
192
+ assert.match(capabilityDocs, /npx\.cmd github:dyphn1\/Harness-everything plugin-sync/);
193
+ assert.match(capabilityDocs, /node scripts\/plugin-sync\.js/);
194
+ assert.doesNotMatch(capabilityDocs, /powershell -File scripts\/plugin-sync\.ps1/);
156
195
  assert.match(capabilityDocs, /platform-compatibility\.json/);
157
196
  assert.match(capabilityDocs, /Continue\.dev[\s\S]*`Unknown` for standalone `SKILL\.md` discovery/);
158
197
  assert.match(capabilityDocs, /live-host evidence|Partial` live-host loading/i);
@@ -81,6 +81,18 @@ if (manifestHooks !== undefined) {
81
81
  }
82
82
 
83
83
  assert.ok(Array.isArray(manifest.skills) && manifest.skills.length > 0, 'Claude plugin must declare packaged skills');
84
+ assert.ok(manifest.skills.includes('./fable-mode'), 'Claude plugin must expose fable-mode as the public Fable entrypoint');
85
+ for (const nestedRef of [
86
+ './fable-mode/fable-opus',
87
+ './fable-mode/fable-sonnet',
88
+ './fable-mode/fable-haiku',
89
+ './fable-mode/execution-guardrails',
90
+ ]) {
91
+ assert.ok(
92
+ !manifest.skills.includes(nestedRef),
93
+ `${nestedRef} is a nested reference contract, not an independently routable Claude skill`
94
+ );
95
+ }
84
96
  for (const skillRef of manifest.skills) {
85
97
  const skillDir = resolvePluginPath(skillRef, `skill ${skillRef}`);
86
98
  assert.ok(fs.statSync(skillDir).isDirectory(), `skill path must be a directory: ${skillRef}`);
@@ -202,10 +202,95 @@ if (validContract(fableTypo, 'fable keyword trivial edit')) {
202
202
  }
203
203
 
204
204
  const explicitFable = runTier('fable on opus audit the entire repository architecture');
205
- if (validContract(explicitFable, 'explicit Fable model')) {
205
+ if (validContract(explicitFable, 'explicit Fable profile')) {
206
206
  check(explicitFable.contract.workflowPlan.strategy === 'fable-staged', 'explicit Fable request selects Fable topology');
207
- check(explicitFable.contract.workflowPlan.modelSelection.requested === 'opus', 'explicit Fable model stays delegated to model selector');
208
- check(explicitFable.result.stdout.includes('REQUESTED FABLE MODEL MODE: opus'), 'legacy explicit Fable route remains visible');
207
+ check(explicitFable.contract.workflowPlan.profileSelection.requested === 'orchestrator', 'legacy opus alias normalizes to orchestrator profile');
208
+ check(explicitFable.contract.workflowPlan.profileSelection.alias === 'opus', 'legacy alias remains auditable');
209
+ check(explicitFable.result.stdout.includes('REQUESTED FABLE PROFILE: opus'), 'explicit Fable profile remains visible');
210
+ }
211
+
212
+ const explicitSonnect = runTier('fable on sonnect for architecture synthesis');
213
+ if (validContract(explicitSonnect, 'explicit Fable sonnect alias')) {
214
+ check(explicitSonnect.contract.workflowPlan.profileSelection.requested === 'reasoning', 'sonnect normalizes to reasoning profile');
215
+ check(explicitSonnect.contract.workflowPlan.profileSelection.alias === 'sonnect', 'raw sonnect alias remains auditable end-to-end');
216
+ check(explicitSonnect.result.stdout.includes('REQUESTED FABLE PROFILE: sonnect'), 'router output preserves raw sonnect alias');
217
+ }
218
+
219
+ // #290: mentioning a profile in a bounded lookup must not launch orchestration.
220
+ for (const prompt of [
221
+ "Explain what 'fable on sonnect' means; return only a small JSON selection record.",
222
+ "Resolve fable on haiku into a profile JSON record; no orchestration or delegation.",
223
+ "解釋 fable on opus 的意思,只回傳 JSON。",
224
+ "Explain what 'run fable on sonnect' means; return only JSON.",
225
+ 'Explain fable on sonnet; return JSON with requestedProfile, effectiveProfile.',
226
+ "Explain what 'fable on sonnect' means; return only a small JSON selection record with requestedProfile, effectiveProfile, profileAlias, assignedRole, runtimeModel, runtimeEffort. No orchestration or delegation.",
227
+ 'Explain fable on haiku; output JSON with `requestedProfile`, `runtimeModel` and `runtimeEffort`.',
228
+ '解釋 fable on opus 的意思,只回傳 JSON,包含 requestedProfile、effectiveProfile、runtimeEffort。',
229
+ "Explain what 'use fable on opus' means; no execution or model switching.",
230
+ 'Explain fable on sonnet; return JSON; no stages, delegation or execution.',
231
+ '請說明 fable on sonnet 的意思,並只回傳含 requestedProfile 的 JSON。',
232
+ 'Explain fable on sonnet, please return JSON.',
233
+ 'Explain fable on sonnet and please return only JSON.',
234
+ '請說明 fable on sonnet,請只回傳 JSON。',
235
+ 'Explain what “run fable on sonnect” means; return only JSON.',
236
+ 'Explain what ‘use fable on opus’ means.',
237
+ 'Explain fable on sonnet; no model switching.',
238
+ "Explain fable on sonnet; don't switch model or delegate.",
239
+ 'Explain fable on sonnet; no stages and no delegation.',
240
+ '解釋 fable on sonnet,不要委派或切換模型。',
241
+ ]) {
242
+ const lookup = runTier(prompt);
243
+ const repeat = runTier(prompt);
244
+ if (validContract(lookup, 'bounded Fable lookup')) {
245
+ check(lookup.contract.classification.tier === 'tier1', 'lookup remains a bounded Tier 1 operation');
246
+ check(lookup.contract.workflowPlan.strategy === 'direct-single', 'lookup selects direct-single before loading references');
247
+ check(lookup.contract.workflowPlan.parallelism.allowed === false, 'lookup does not delegate');
248
+ check(lookup.result.stdout.includes('references/profile-lookup.md'), 'lookup selects only compact reference');
249
+ check(!lookup.result.stdout.includes('RECOMMENDED KNOWLEDGE GUIDES'), 'lookup omits unrelated keyword guides');
250
+ check(!lookup.result.stdout.includes('with fable-mode/scripts/model-selector.js'), 'lookup does not send agent to selector implementation');
251
+ check(JSON.stringify(lookup.contract) === JSON.stringify(repeat.contract), 'lookup contract is deterministic');
252
+ }
253
+ }
254
+ for (const prompt of [
255
+ 'Use fable on sonnect to implement a multi-stage architecture migration.',
256
+ 'Explain fable on opus and then implement a repository-wide migration.',
257
+ 'Explain how to run fable-staged across all modules.',
258
+ 'Explain fable on sonnet then run it to analyze security.',
259
+ 'Explain fable on sonnet and analyze the authentication design.',
260
+ 'Explain fable on sonnet; then send the findings to Slack.',
261
+ 'Explain fable on sonnet and update README.',
262
+ 'Explain fable on haiku, add a regression test.',
263
+ 'Explain fable on opus and commit the documentation change.',
264
+ '說明 fable on sonnet 並更新 README。',
265
+ '解釋 fable on haiku,新增回歸測試。',
266
+ '解析 fable on opus 並刪除過期文件。',
267
+ 'Explain fable on sonnet before updating README.',
268
+ 'Explain fable on sonnet; return JSON after updating README.',
269
+ "Explain fable on sonnet before updating README.md to change 'Instalation' to 'Installation'.",
270
+ '解釋 fable on sonnet 後更新 README。',
271
+ 'Explain fable on sonnet; no delegation before deleting stale files.',
272
+ 'Explain fable on sonnet; return JSON with requestedProfile, update README.',
273
+ 'Explain fable on sonnet; output JSON with requestedProfile and then publish the result.',
274
+ 'Explain fable on sonnet; return JSON after restarting the server.',
275
+ 'Explain fable on sonnet; no execution except updating README.',
276
+ 'Explain fable on sonnet; provide a profile result while running the tests.',
277
+ 'Explain fable on sonnet; return JSON with unknownField.',
278
+ 'Explain fable on sonnet; some unknown continuation.',
279
+ // A negation scopes only its own noun list; affirmative actions after it execute.
280
+ 'Explain fable on sonnet; no delegation, do execution.',
281
+ 'Explain fable on sonnet; no delegation, switch model.',
282
+ 'Explain fable on sonnet; no delegation and stage execution.',
283
+ 'Explain fable on sonnet; without orchestration do execution.',
284
+ 'Explain fable on sonnet; no delegation or switch model.',
285
+ '解釋 fable on sonnet,不要委派,執行。',
286
+ '解釋 fable on sonnet,不要委派,切換模型。',
287
+ ]) {
288
+ const execution = runTier(prompt);
289
+ if (validContract(execution, 'Fable execution boundary')) {
290
+ check(execution.contract.workflowPlan.strategy === 'fable-staged', 'execution/macro scope preserves staged topology');
291
+ check(!execution.contract.workflowPlan.reasonCodes.includes('fable-profile-lookup'), 'mixed execution never records the bounded lookup reason');
292
+ check(execution.contract.workflowPlan.requiredInvariants.includes('isolated-worktree-before-mutation'), 'execution retains isolation obligation');
293
+ }
209
294
  }
210
295
 
211
296
  const explicitOverride = runTier('Use iterative-single to audit the entire repository architecture.');
@@ -250,10 +335,10 @@ if (validContract(noWorkspaceState, 'workspace state block')) {
250
335
  const unavailableModel = runTier('fable on opus audit the entire repository architecture', {
251
336
  context: { hostCapabilities: { modelAvailability: 'unavailable', subagents: 'available' } },
252
337
  });
253
- if (validContract(unavailableModel, 'model capability block')) {
254
- check(unavailableModel.contract.workflowPlan.modelSelection.requested === 'opus', 'router never remaps requested branded model');
255
- check(unavailableModel.contract.workflowPlan.fallback.disposition === 'blocked', 'known unavailable model is visibly blocked');
256
- check(unavailableModel.contract.workflowPlan.fallback.reasonCodes.includes('requested-model-capability-unavailable'), 'model capability block reason is recorded');
338
+ if (validContract(unavailableModel, 'runtime model capability advisory')) {
339
+ check(unavailableModel.contract.workflowPlan.profileSelection.requested === 'orchestrator', 'opus remains a behavior-profile alias');
340
+ check(unavailableModel.contract.workflowPlan.fallback.disposition !== 'blocked', 'runtime model availability does not block Fable profile selection');
341
+ check(!unavailableModel.contract.workflowPlan.fallback.reasonCodes.includes('requested-model-capability-unavailable'), 'router no longer treats a branded model as a Fable requirement');
257
342
  }
258
343
 
259
344
  const hookUnavailable = runTier('Drop the prod database table.', {
@@ -38,6 +38,14 @@ This is a **repository/agent contract**, not an automatic upgrade to any platfor
38
38
 
39
39
  The full ten-dimension entries and official source URLs are in [platform-compatibility.json](platform-compatibility.json). “Supported” in the standalone-skills column refers to the host’s documented skill surface plus the repository’s verified path contract; it does not mean that a live Harness session was run on every platform.
40
40
 
41
+ ### Claude Code Fable entrypoint boundary
42
+
43
+ The Claude plugin registers `./fable-mode` as the public Fable skill. The nested `fable-opus`, `fable-sonnet`, `fable-haiku`, and `execution-guardrails` directories are reference contracts inside that skill tree; they are not independently registered or host-routable Claude skills. The legacy prompts `fable on opus`, `fable on sonnet`/`sonnect`, and `fable on haiku` select Fable behavior profiles (orchestrator/reasoning/mechanical), not branded runtime models. `model-selector.js` records the profile and separately evaluates the host-specific advisory runtime floor; a below-floor or unknown runtime does not block Fable by itself.
44
+
45
+ The router distinguishes bounded profile explanations from Fable execution. Supported lookup prompts select `direct-single` and `references/profile-lookup.md`; the public skill dispatches before loading stage references. Entire explanation, format and negative-constraint clauses are checked against a bounded lookup vocabulary; comma/and-separated canonical output fields remain lookup data. Unknown continuations, same-clause actions (including `before`/`after`), mixed execution or macro scope retain execution routing and required invariants. This is a deterministic routing and packaged instruction contract, not a host-enforced reference-read limit or a measured token-cost guarantee.
46
+
47
+ The nested execution-guardrails file is also not an always-on carrier. Fable agents that need those rules carry them inline; any non-Fable surface must load, copy, or inject the rules through a carrier the host actually executes before claiming that they apply there.
48
+
41
49
  ## Installer target contract
42
50
 
43
51
  The general installer’s target paths are tested independently from host discovery. This prevents a successful file copy from being reported as proof that an agent will load the file.
@@ -69,10 +77,15 @@ The package is tested at the mechanism layer and remains subject to the host’s
69
77
  The repository also ships a host-facing synchronization command for the native marketplace/plugin surfaces:
70
78
 
71
79
  ```bash
80
+ # POSIX shells / Git Bash
72
81
  npx github:dyphn1/Harness-everything plugin-sync
73
82
  # or, from a checkout:
74
83
  ./scripts/plugin-sync.sh
75
- powershell -File scripts/plugin-sync.ps1
84
+
85
+ # Windows PowerShell
86
+ npx.cmd github:dyphn1/Harness-everything plugin-sync
87
+ # or, from a checkout:
88
+ node scripts/plugin-sync.js
76
89
  ```
77
90
 
78
91
  The command detects Claude Code and Codex independently, ensures the Harness marketplace is configured, reads the installed-plugin state, then refreshes that marketplace before choosing the operation: