@dungle-scrubs/harness-cli-normalizer 0.6.1 → 0.6.3

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 (59) hide show
  1. package/README.md +15 -0
  2. package/dist/cli/args.d.ts.map +1 -1
  3. package/dist/cli/args.js +5 -0
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/cli/config.d.ts.map +1 -1
  6. package/dist/cli/config.js +2 -0
  7. package/dist/cli/config.js.map +1 -1
  8. package/dist/cli/help.d.ts +2 -2
  9. package/dist/cli/help.d.ts.map +1 -1
  10. package/dist/cli/help.js +4 -0
  11. package/dist/cli/help.js.map +1 -1
  12. package/dist/cli/plan-turn.d.ts.map +1 -1
  13. package/dist/cli/plan-turn.js +3 -1
  14. package/dist/cli/plan-turn.js.map +1 -1
  15. package/dist/cli/session.d.ts.map +1 -1
  16. package/dist/cli/session.js +11 -0
  17. package/dist/cli/session.js.map +1 -1
  18. package/dist/interpretation/argv.d.ts +1 -0
  19. package/dist/interpretation/argv.d.ts.map +1 -1
  20. package/dist/interpretation/argv.js +2 -0
  21. package/dist/interpretation/argv.js.map +1 -1
  22. package/dist/interpretation/isolation.d.ts +10 -0
  23. package/dist/interpretation/isolation.d.ts.map +1 -0
  24. package/dist/interpretation/isolation.js +30 -0
  25. package/dist/interpretation/isolation.js.map +1 -0
  26. package/dist/interpretation/resolve-options.d.ts.map +1 -1
  27. package/dist/interpretation/resolve-options.js +15 -1
  28. package/dist/interpretation/resolve-options.js.map +1 -1
  29. package/dist/interpretation/support.d.ts.map +1 -1
  30. package/dist/interpretation/support.js +2 -0
  31. package/dist/interpretation/support.js.map +1 -1
  32. package/dist/interpretation/turn-options.d.ts.map +1 -1
  33. package/dist/interpretation/turn-options.js +2 -0
  34. package/dist/interpretation/turn-options.js.map +1 -1
  35. package/dist/knowledge/claude-code.d.ts +3 -1
  36. package/dist/knowledge/claude-code.d.ts.map +1 -1
  37. package/dist/knowledge/claude-code.js +23 -3
  38. package/dist/knowledge/claude-code.js.map +1 -1
  39. package/dist/knowledge/descriptor.d.ts +1 -1
  40. package/dist/knowledge/descriptor.d.ts.map +1 -1
  41. package/dist/knowledge/descriptor.js +1 -0
  42. package/dist/knowledge/descriptor.js.map +1 -1
  43. package/dist/knowledge/muse.d.ts.map +1 -1
  44. package/dist/knowledge/muse.js +6 -1
  45. package/dist/knowledge/muse.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/cli/args.ts +4 -0
  48. package/src/cli/config.ts +2 -0
  49. package/src/cli/help.ts +4 -0
  50. package/src/cli/plan-turn.ts +5 -1
  51. package/src/cli/session.ts +17 -0
  52. package/src/interpretation/argv.ts +3 -0
  53. package/src/interpretation/isolation.ts +39 -0
  54. package/src/interpretation/resolve-options.ts +13 -1
  55. package/src/interpretation/support.ts +2 -0
  56. package/src/interpretation/turn-options.ts +2 -0
  57. package/src/knowledge/claude-code.ts +23 -3
  58. package/src/knowledge/descriptor.ts +1 -0
  59. package/src/knowledge/muse.ts +6 -1
@@ -10,6 +10,7 @@ import type {
10
10
  StreamingGranularity,
11
11
  } from "../knowledge/descriptor.js";
12
12
  import { defaultDescriptors } from "../knowledge/overrides.js";
13
+ import { assertIsolationCombination } from "./isolation.js";
13
14
  import { ArgvRefusalError } from "./refusal.js";
14
15
  import { assertUsableSessionId, SESSION_ID_MAX, SessionIdRefusalError } from "./session-id.js";
15
16
  import { renderSkillsSelection, type SkillsSelection } from "./skills-selection.js";
@@ -61,6 +62,7 @@ export interface DiscoveryOptions {
61
62
  }
62
63
 
63
64
  export interface TurnOptions {
65
+ readonly isolation?: "tool-free";
64
66
  readonly prompt: Prompt;
65
67
  readonly tools?: readonly string[];
66
68
  readonly excludeTools?: readonly string[];
@@ -205,6 +207,7 @@ export interface SpawnArgvOptions extends TurnOptions {
205
207
  * passthrough tail after a bare separator. One owner, so the CLI's preview
206
208
  * and the runner's spawn agree by construction (RFC-02 change 10). */
207
209
  export const buildSpawnArgv = (h: HarnessDescriptor, opts: SpawnArgvOptions): string[] => {
210
+ assertIsolationCombination(h, opts);
208
211
  const base =
209
212
  opts.resume === undefined
210
213
  ? buildLaunchArgv(h, opts)
@@ -0,0 +1,39 @@
1
+ import type { HarnessDescriptor } from "../knowledge/descriptor.js";
2
+ import type { TurnOptions } from "./argv.js";
3
+ import { ArgvRefusalError } from "./refusal.js";
4
+
5
+ /** Isolation owns these dimensions for the whole invocation. Config defaults
6
+ * yield to it; explicit competing selections refuse before registry reads. */
7
+ export const ISOLATION_OVERRIDES = [
8
+ "tools",
9
+ "excludeTools",
10
+ "skills",
11
+ "access",
12
+ "autonomy",
13
+ "discovery",
14
+ ] as const;
15
+ export const assertIsolationCombination = (
16
+ h: HarnessDescriptor,
17
+ opts: Partial<TurnOptions> & {
18
+ readonly skillNames?: readonly string[];
19
+ readonly passthrough?: readonly string[];
20
+ },
21
+ ): void => {
22
+ if (opts.isolation === undefined) return;
23
+ const conflict =
24
+ ISOLATION_OVERRIDES.find((key) =>
25
+ key === "autonomy" ? opts[key] === true : opts[key] !== undefined,
26
+ ) ??
27
+ (opts.skillNames !== undefined ? "skills" : undefined) ??
28
+ (opts.passthrough?.length ? "native passthrough" : undefined);
29
+ if (conflict === undefined) return;
30
+ throw new ArgvRefusalError({
31
+ issue: "mutually-exclusive-options",
32
+ harness: h.name,
33
+ option: "isolation",
34
+ detail: conflict,
35
+ supported: [
36
+ "tool-free without explicit tools, skills, access, autonomy, discovery, or native passthrough",
37
+ ],
38
+ });
39
+ };
@@ -11,6 +11,7 @@ import type { HarnessDescriptor } from "../knowledge/descriptor.js";
11
11
  import { defaultDescriptors } from "../knowledge/overrides.js";
12
12
  import { DEFAULT_TURN_PROFILE, type ProfileKey } from "../knowledge/profile.js";
13
13
  import type { TurnOptions } from "./argv.js";
14
+ import { assertIsolationCombination, ISOLATION_OVERRIDES } from "./isolation.js";
14
15
  import type { QuestionMode } from "./question.js";
15
16
  import { ArgvRefusalError } from "./refusal.js";
16
17
  import type { ToolMapConfig } from "./tool-vocabulary.js";
@@ -152,10 +153,17 @@ export const resolveEffectiveOptions = (
152
153
  args: TurnOptions,
153
154
  tiers: ConfigTiers = {},
154
155
  ): ResolvedOptions => {
156
+ assertIsolationCombination(h, args);
155
157
  const provenance: ProvenanceEntry[] = [];
158
+ if (args.isolation !== undefined)
159
+ provenance.push({ key: "isolation", value: args.isolation, tier: "arg" });
156
160
  const unrenderable: string[] = [];
157
- const config = effectiveConfig(tiers);
161
+ const config = { ...effectiveConfig(tiers) };
162
+ if (args.isolation !== undefined) {
163
+ for (const key of ISOLATION_OVERRIDES) delete config[key];
164
+ }
158
165
  const sourceTier = (key: string): ProvenanceTier | undefined => {
166
+ if (config[key as keyof TurnOptions] === undefined) return undefined;
159
167
  if (tiers.project?.[key as keyof TurnOptions] !== undefined) return "project-config";
160
168
  if (tiers.user?.[key as keyof TurnOptions] !== undefined) return "user-config";
161
169
  return undefined;
@@ -288,6 +296,10 @@ export const resolveEffectiveOptions = (
288
296
 
289
297
  // Profile is the floor: apply only where nothing above it set the key.
290
298
  for (const [key, value] of Object.entries(DEFAULT_TURN_PROFILE)) {
299
+ if (args.isolation !== undefined && ISOLATION_OVERRIDES.some((owned) => owned === key)) {
300
+ provenance.push({ key, value: "disabled (tool-free isolation)", tier: "arg" });
301
+ continue;
302
+ }
291
303
  const argsSet = effectiveArgs[key as keyof TurnOptions] !== undefined;
292
304
  const tier = sourceTier(key);
293
305
  if (argsSet) {
@@ -51,6 +51,7 @@ const spellingOf = (h: HarnessDescriptor, option: RefusalOption): string | null
51
51
  }
52
52
  case "autonomy":
53
53
  return h.autonomy?.flag ?? null;
54
+ case "isolation":
54
55
  case "effort":
55
56
  case "sandbox":
56
57
  case "contextWindow":
@@ -124,6 +125,7 @@ export const recognizeNativeSpelling = (
124
125
  "tools",
125
126
  "excludeTools",
126
127
  "autonomy",
128
+ "isolation",
127
129
  "effort",
128
130
  "sandbox",
129
131
  "contextWindow",
@@ -24,6 +24,7 @@ import {
24
24
  import { defaultDescriptors } from "../knowledge/overrides.js";
25
25
  import type { DiscoveryOptions, TurnOptions } from "./argv.js";
26
26
  import { hintFor } from "./hints.js";
27
+ import { assertIsolationCombination } from "./isolation.js";
27
28
  import { ArgvRefusalError } from "./refusal.js";
28
29
  import { supportedBy } from "./support.js";
29
30
  import { renderToolSelection } from "./tool-selection.js";
@@ -41,6 +42,7 @@ export const renderTurnOptions = (
41
42
  opts: TurnOptions,
42
43
  phase: "launch" | "resume",
43
44
  ): string[] => {
45
+ assertIsolationCombination(h, opts);
44
46
  const sequences: string[][] = [];
45
47
  // The turn option the access preset displaces when set (codex: sandbox),
46
48
  // read from the descriptor so no arm below branches on a harness name.
@@ -9,7 +9,9 @@
9
9
  * `ANTHROPIC_API_KEY` / `apiKeyHelper` (OAuth never read) - so a caller
10
10
  * authenticated via OAuth would break. The descriptor therefore offers no
11
11
  * `instructionFiles` facet and a call passing it must refuse. Likewise
12
- * `tools` has no discovery flag on claude and refuses.
12
+ * `tools` has no discovery flag on claude and refuses. The explicit
13
+ * tool-free isolation composite below accepts the bare-mode auth cost and
14
+ * disables native tools too. It is separate from a granular discovery facet.
13
15
  *
14
16
  * Effort: A-002 showed `--effort bogus` warns on stderr and runs at DEFAULT
15
17
  * effort, exit 0, with nothing echoed in the stream. The library-side
@@ -115,9 +117,16 @@ export const claudeCode: HarnessDescriptor = deepFreeze({
115
117
  autonomy: { flag: "--dangerously-skip-permissions" },
116
118
  vocabulary: {
117
119
  modelFlag: "--model",
118
- models: ["claude-fable-5", "claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5-20251001"],
120
+ // https://platform.claude.com/docs/en/models/fable-5-1/overview
121
+ models: [
122
+ "claude-fable-5-1",
123
+ "claude-fable-5",
124
+ "claude-opus-5",
125
+ "claude-sonnet-5",
126
+ "claude-haiku-4-5-20251001",
127
+ ],
119
128
  aliases: {
120
- fable: "claude-fable-5",
129
+ fable: "claude-fable-5-1",
121
130
  opus: "claude-opus-5",
122
131
  sonnet: "claude-sonnet-5",
123
132
  haiku: "claude-haiku-4-5-20251001",
@@ -161,6 +170,17 @@ export const claudeCode: HarnessDescriptor = deepFreeze({
161
170
  observedOn: { harness: "claude", model: "", version: "2.1.235", date: "2026-08-19" },
162
171
  },
163
172
  turnOptions: {
173
+ // Native CLI reference: bare removes discovery; the empty built-in list and
174
+ // MCP deny remove tool access. Resume and native overrides are refused.
175
+ isolation: {
176
+ kind: "enum",
177
+ values: ["tool-free"],
178
+ resumeRender: null,
179
+ render: {
180
+ kind: "flag-list",
181
+ flags: ["--bare", "--tools", "", "--disallowedTools", "mcp__*", "--strict-mcp-config"],
182
+ },
183
+ },
164
184
  effort: { kind: "effort", render: { kind: "flag-value", flag: "--effort" } },
165
185
  // issue #48, live-verified 2.1.235: --system-prompt replaces the built-in
166
186
  // prompt; --exclude-dynamic-system-prompt-sections strips the dynamic
@@ -146,6 +146,7 @@ export type UnavailableMatcher = PhraseMatcher;
146
146
  * arm would be dead data that can only drift. Render order is the tuple
147
147
  * order, so argv is deterministic regardless of caller field order. */
148
148
  export const TURN_OPTION_KEYS = deepFreeze([
149
+ "isolation",
149
150
  "effort",
150
151
  "sandbox",
151
152
  "contextWindow",
@@ -59,7 +59,12 @@ export const museCode: HarnessDescriptor = deepFreeze({
59
59
  autonomy: { flag: "--yolo" },
60
60
  vocabulary: {
61
61
  modelFlag: "--model",
62
- models: ["muse-spark-1.2-contributor", "muse-spark-1.2", "muse-spark-1.1"],
62
+ models: [
63
+ "muse-spark-1.3-contributor",
64
+ "muse-spark-1.2-contributor",
65
+ "muse-spark-1.2",
66
+ "muse-spark-1.1",
67
+ ],
63
68
  aliases: {},
64
69
  efforts: ["none", "minimal", "low", "medium", "high", "xhigh"],
65
70
  extensible: false,