@harness-engineering/cli 8.0.0 → 9.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.
@@ -3,7 +3,7 @@ import {
3
3
  createProgram,
4
4
  installCommandTelemetry,
5
5
  printFirstRunWelcome
6
- } from "../chunk-MSJJU6KH.js";
6
+ } from "../chunk-SHOTI4TA.js";
7
7
  import "../chunk-PYDUECJ3.js";
8
8
  import "../chunk-7IVIRKHD.js";
9
9
  import "../chunk-XYP5CIEN.js";
@@ -3338,7 +3338,7 @@ import { spawn } from "child_process";
3338
3338
  import { existsSync as existsSync6 } from "fs";
3339
3339
  import { createRequire } from "module";
3340
3340
  import { dirname as dirname4, join as join10, resolve as resolve15 } from "path";
3341
- import { setTimeout } from "timers";
3341
+ import { setTimeout as setTimeout2 } from "timers";
3342
3342
  import { fileURLToPath } from "url";
3343
3343
  var __filename = fileURLToPath(import.meta.url);
3344
3344
  var __dirname = dirname4(__filename);
@@ -3413,7 +3413,7 @@ function runDashboard(opts) {
3413
3413
  console.log(`Dashboard API starting on http://localhost:${apiPort}`);
3414
3414
  console.log(`Open ${url} (pass --no-open to suppress)`);
3415
3415
  if (opts.noOpen !== true) {
3416
- setTimeout(() => openBrowser(url), 1500);
3416
+ setTimeout2(() => openBrowser(url), 1500);
3417
3417
  }
3418
3418
  }
3419
3419
  function createDashboardCommand() {
@@ -9518,9 +9518,12 @@ function createOrchestratorCommand() {
9518
9518
  logger.error(`Failed to load workflow: ${result.error.message}`);
9519
9519
  process.exit(ExitCode.ERROR);
9520
9520
  }
9521
- const { config, promptTemplate, warnings } = result.value;
9521
+ const { config, promptTemplate, localPromptTemplate, warnings } = result.value;
9522
9522
  for (const w of warnings) logger.warn(w);
9523
- const daemon = new Orchestrator(config, promptTemplate, { discoverCandidates });
9523
+ const daemon = new Orchestrator(config, promptTemplate, {
9524
+ discoverCandidates,
9525
+ ...localPromptTemplate !== void 0 ? { localPromptTemplate } : {}
9526
+ });
9524
9527
  const shutdown = () => {
9525
9528
  daemon.stop();
9526
9529
  process.exit(ExitCode.SUCCESS);
@@ -11792,7 +11795,7 @@ function firstModel(model) {
11792
11795
  if (Array.isArray(model)) return model[0];
11793
11796
  return model;
11794
11797
  }
11795
- function resolveTriageProvider(config, modelOverride) {
11798
+ function resolveTriageProvider(config, modelOverride, localModelPreference) {
11796
11799
  const explicit = config?.intelligence?.provider;
11797
11800
  if (explicit) {
11798
11801
  if (explicit.kind === "anthropic") {
@@ -11815,7 +11818,7 @@ function resolveTriageProvider(config, modelOverride) {
11815
11818
  if (!backends) return null;
11816
11819
  const local = pickBackend(backends, ["local", "pi"]);
11817
11820
  if (local && local.endpoint) {
11818
- const model = modelOverride ?? firstModel(local.model);
11821
+ const model = modelOverride ?? localModelPreference ?? firstModel(local.model);
11819
11822
  return new OpenAICompatibleAnalysisProvider2({
11820
11823
  apiKey: local.apiKey ?? "ollama",
11821
11824
  baseUrl: local.endpoint,
@@ -11841,6 +11844,48 @@ function pickBackend(backends, types) {
11841
11844
  return null;
11842
11845
  }
11843
11846
 
11847
+ // src/commands/roadmap/triage-pool.ts
11848
+ import {
11849
+ PoolStateStore,
11850
+ poolStateToCandidates
11851
+ } from "@harness-engineering/orchestrator";
11852
+ var PROBE_TIMEOUT_MS = 4e3;
11853
+ async function probeServedModels(endpoint, apiKey, fetchImpl) {
11854
+ const url = endpoint.replace(/\/$/, "") + "/models";
11855
+ const controller = new AbortController();
11856
+ const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
11857
+ try {
11858
+ const res = await fetchImpl(url, {
11859
+ signal: controller.signal,
11860
+ ...apiKey !== void 0 ? { headers: { Authorization: `Bearer ${apiKey}` } } : {}
11861
+ });
11862
+ if (!res.ok) return void 0;
11863
+ const body = await res.json();
11864
+ const ids = (body.data ?? []).map((m) => m.id).filter((id) => typeof id === "string" && id.length > 0);
11865
+ return new Set(ids);
11866
+ } catch {
11867
+ return void 0;
11868
+ } finally {
11869
+ clearTimeout(timer);
11870
+ }
11871
+ }
11872
+ async function resolvePreferredLocalModel(profile, deps = {}) {
11873
+ try {
11874
+ const store = deps.store ?? new PoolStateStore();
11875
+ await store.load();
11876
+ const candidates = poolStateToCandidates(store.snapshot(), profile).filter(
11877
+ (name) => typeof name === "string" && name.length > 0
11878
+ );
11879
+ if (candidates.length === 0) return void 0;
11880
+ if (deps.endpoint === void 0) return void 0;
11881
+ const served = await probeServedModels(deps.endpoint, deps.apiKey, deps.fetchImpl ?? fetch);
11882
+ if (served === void 0) return void 0;
11883
+ return candidates.find((name) => served.has(name));
11884
+ } catch {
11885
+ return void 0;
11886
+ }
11887
+ }
11888
+
11844
11889
  // src/commands/roadmap/triage-approve.ts
11845
11890
  import {
11846
11891
  detectScopeTier,
@@ -12010,12 +12055,13 @@ async function runTriageReport(roadmap, deps = {}) {
12010
12055
  ...deps.only !== void 0 ? { only: deps.only } : {},
12011
12056
  ...deps.limit !== void 0 ? { limit: deps.limit } : {}
12012
12057
  });
12058
+ const useModel = deps.offline !== true && deps.provider != null;
12013
12059
  const cheapDeps = {
12014
12060
  ...deps.graphStore ? { graphStore: deps.graphStore } : {},
12015
12061
  ...deps.precedent ? { precedent: deps.precedent } : {},
12016
- ...deps.config ? { config: deps.config } : {}
12062
+ ...deps.config ? { config: deps.config } : {},
12063
+ ...useModel ? { modelDeferred: true } : {}
12017
12064
  };
12018
- const useModel = deps.offline !== true && deps.provider != null;
12019
12065
  const rows = [];
12020
12066
  for (const feature of features) {
12021
12067
  let verdict = await triageIssue(featureToIssue(feature), cheapDeps);
@@ -12214,10 +12260,29 @@ function renderBrainstormJson(rows) {
12214
12260
  2
12215
12261
  );
12216
12262
  }
12217
- function resolveBrainstormProvider(configPath, model) {
12263
+ async function resolveBrainstormProvider(configPath, model) {
12218
12264
  const configResult = resolveConfig(configPath);
12219
12265
  if (!configResult.ok) return null;
12220
- return resolveTriageProvider(configResult.value, model);
12266
+ const config = configResult.value;
12267
+ const localBackend = pickLocalBackendConn(config);
12268
+ const preferredLocalModel = await resolvePreferredLocalModel("reasoning", {
12269
+ ...localBackend?.endpoint !== void 0 ? { endpoint: localBackend.endpoint } : {},
12270
+ ...localBackend?.apiKey !== void 0 ? { apiKey: localBackend.apiKey } : {}
12271
+ });
12272
+ return resolveTriageProvider(config, model, preferredLocalModel);
12273
+ }
12274
+ function pickLocalBackendConn(config) {
12275
+ const backends = config.agent?.backends;
12276
+ if (!backends) return void 0;
12277
+ for (const def of Object.values(backends)) {
12278
+ if (def.type === "local" || def.type === "pi") {
12279
+ return {
12280
+ ...def.endpoint !== void 0 ? { endpoint: def.endpoint } : {},
12281
+ ...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {}
12282
+ };
12283
+ }
12284
+ }
12285
+ return void 0;
12221
12286
  }
12222
12287
  async function runApproveCommand(cwd, opts) {
12223
12288
  const configResult = resolveConfig(opts.config);
@@ -12234,7 +12299,7 @@ async function runApproveCommand(cwd, opts) {
12234
12299
  const parsed = parseRoadmap(fs36.readFileSync(roadmapPath, "utf-8"));
12235
12300
  if (!parsed.ok) return { marked: [], held: [], note: "roadmap-parse-error" };
12236
12301
  const graphStore = await loadGraphStore(cwd);
12237
- const provider = resolveBrainstormProvider(opts.config);
12302
+ const provider = await resolveBrainstormProvider(opts.config);
12238
12303
  const precedent = await buildPrecedentLookup(cwd);
12239
12304
  const rows = await runBrainstormReport(parsed.value, {
12240
12305
  ...graphStore ? { graphStore } : {},
@@ -12339,7 +12404,7 @@ async function runTriageCommandAction(opts, cmd) {
12339
12404
  if (!parsed) return;
12340
12405
  const graphStore = await loadGraphStore(cwd);
12341
12406
  const precedent = await buildPrecedentLookup(cwd);
12342
- const provider = opts.offline === true ? null : resolveBrainstormProvider(globalOpts.config);
12407
+ const provider = opts.offline === true ? null : await resolveBrainstormProvider(globalOpts.config);
12343
12408
  const onProgress = globalOpts.json === true ? void 0 : (message) => logger.info(message);
12344
12409
  const shared = {
12345
12410
  ...graphStore ? { graphStore } : {},
@@ -14114,6 +14179,17 @@ function detectComplexity(projectPath) {
14114
14179
  }
14115
14180
 
14116
14181
  // src/commands/skill/preamble.ts
14182
+ var AUTONOMOUS_SECTION = `## Autonomous Decision-Making: Active
14183
+
14184
+ You are running this workflow **headless \u2014 there is no human in this session to answer questions.**
14185
+
14186
+ Wherever this skill instructs you to present options and a recommendation and then STOP and wait for the human (design forks, \`emit_interaction\`/\`AskUserQuestion\` prompts, spec sign-off, the next-skill handoff), do NOT wait. Instead:
14187
+
14188
+ 1. Do the full analysis exactly as written \u2014 honor every gate (at least two approaches with tradeoffs, YAGNI, the persona council, soundness review). Autonomy narrows the decider, never the rigor.
14189
+ 2. Commit to the option **you** recommend, and record the question, the chosen answer, and the rationale in the spec's **"Decisions made"** section.
14190
+ 3. Continue to the next step. Self-approve the Phase-4 sign-off once the soundness review converges; proceed through the handoff to the next skill.
14191
+
14192
+ **Safety valve \u2014 surface, do not bury.** For any decision made at **low confidence**, and for any **strategy contradiction** the skill says to "never auto-resolve," still decide and proceed, but additionally record it under an **"## Autonomous decisions requiring review"** heading in the spec (question, your call, confidence, why) so the human's PR review sees exactly what you decided and where you were unsure. You are the decider, not a shortcut \u2014 the human's checkpoint is the PR, not a mid-run pause.`;
14117
14193
  function buildActivePhasesSection(complexity, phases) {
14118
14194
  const lines = [`## Active Phases (complexity: ${complexity})`];
14119
14195
  for (const phase of phases) {
@@ -14138,6 +14214,9 @@ ${priorState}`);
14138
14214
  }
14139
14215
  function buildPreamble(options) {
14140
14216
  const sections = [];
14217
+ if (options.autonomous) {
14218
+ sections.push(AUTONOMOUS_SECTION);
14219
+ }
14141
14220
  if (options.complexity && options.phases && options.phases.length > 0) {
14142
14221
  sections.push(buildActivePhasesSection(options.complexity, options.phases));
14143
14222
  }
@@ -14256,7 +14335,8 @@ async function runSkill(name, opts) {
14256
14335
  ...opts.phase !== void 0 && { phase: opts.phase },
14257
14336
  ...priorState !== void 0 && { priorState },
14258
14337
  ...stateWarning !== void 0 && { stateWarning },
14259
- ...opts.party !== void 0 && { party: opts.party }
14338
+ ...opts.party !== void 0 && { party: opts.party },
14339
+ ...opts.autonomous !== void 0 && { autonomous: opts.autonomous }
14260
14340
  });
14261
14341
  const skillMdPath = path66.join(skillDir, "SKILL.md");
14262
14342
  if (!fs42.existsSync(skillMdPath)) {
@@ -14277,6 +14357,9 @@ async function runSkill(name, opts) {
14277
14357
  }
14278
14358
  function createRunCommand3() {
14279
14359
  return new Command104("run").description("Run a skill (outputs SKILL.md content with context preamble)").argument("<name>", "Skill name (e.g., harness-tdd)").option("--path <path>", "Project root path for context injection").option("--complexity <level>", "Rigor level: fast, standard, thorough", "standard").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").option(
14360
+ "--autonomous",
14361
+ "Headless mode: inject the autonomous-decider preamble so the agent decides every fork at full rigor (recording each decision in the spec) instead of pausing for a human"
14362
+ ).option(
14280
14363
  "--backend <name>",
14281
14364
  "Spec B: one-shot routing override forwarded to the orchestrator as HARNESS_BACKEND_OVERRIDE"
14282
14365
  ).action(async (name, opts) => runSkill(name, opts));
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ interface PreambleOptions {
18
18
  priorState?: string;
19
19
  stateWarning?: string;
20
20
  party?: boolean;
21
+ autonomous?: boolean;
21
22
  }
22
23
  declare function buildPreamble(options: PreambleOptions): string;
23
24
 
@@ -864,6 +865,7 @@ declare const HarnessConfigSchema: z.ZodObject<{
864
865
  model?: string | undefined;
865
866
  } | undefined;
866
867
  }>>;
868
+ workflowGates: z.ZodOptional<z.ZodEnum<["local", "primary"]>>;
867
869
  }, "strict", z.ZodTypeAny, {
868
870
  default: string | readonly [string, ...string[]];
869
871
  'quick-fix'?: string | readonly [string, ...string[]] | undefined;
@@ -898,6 +900,7 @@ declare const HarnessConfigSchema: z.ZodObject<{
898
900
  model?: string | undefined;
899
901
  } | undefined;
900
902
  } | undefined;
903
+ workflowGates?: "local" | "primary" | undefined;
901
904
  }, {
902
905
  default: string | readonly [string, ...string[]];
903
906
  'quick-fix'?: string | readonly [string, ...string[]] | undefined;
@@ -932,6 +935,7 @@ declare const HarnessConfigSchema: z.ZodObject<{
932
935
  model?: string | undefined;
933
936
  } | undefined;
934
937
  } | undefined;
938
+ workflowGates?: "local" | "primary" | undefined;
935
939
  }>>;
936
940
  }, "strip", z.ZodTypeAny, {
937
941
  executor: "subprocess" | "cloud" | "noop";
@@ -1059,6 +1063,7 @@ declare const HarnessConfigSchema: z.ZodObject<{
1059
1063
  model?: string | undefined;
1060
1064
  } | undefined;
1061
1065
  } | undefined;
1066
+ workflowGates?: "local" | "primary" | undefined;
1062
1067
  } | undefined;
1063
1068
  }, {
1064
1069
  executor?: "subprocess" | "cloud" | "noop" | undefined;
@@ -1186,6 +1191,7 @@ declare const HarnessConfigSchema: z.ZodObject<{
1186
1191
  model?: string | undefined;
1187
1192
  } | undefined;
1188
1193
  } | undefined;
1194
+ workflowGates?: "local" | "primary" | undefined;
1189
1195
  } | undefined;
1190
1196
  }>>;
1191
1197
  /** Source-file ingestion controls (skip-dirs, exclude patterns, gitignore handling) */
@@ -2825,6 +2831,7 @@ declare const HarnessConfigSchema: z.ZodObject<{
2825
2831
  model?: string | undefined;
2826
2832
  } | undefined;
2827
2833
  } | undefined;
2834
+ workflowGates?: "local" | "primary" | undefined;
2828
2835
  } | undefined;
2829
2836
  } | undefined;
2830
2837
  ingest?: {
@@ -3247,6 +3254,7 @@ declare const HarnessConfigSchema: z.ZodObject<{
3247
3254
  model?: string | undefined;
3248
3255
  } | undefined;
3249
3256
  } | undefined;
3257
+ workflowGates?: "local" | "primary" | undefined;
3250
3258
  } | undefined;
3251
3259
  } | undefined;
3252
3260
  ingest?: {
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  runSnapshotCapture,
13
13
  runUninstall,
14
14
  runUninstallConstraints
15
- } from "./chunk-MSJJU6KH.js";
15
+ } from "./chunk-SHOTI4TA.js";
16
16
  import {
17
17
  generateRuntime
18
18
  } from "./chunk-PYDUECJ3.js";
@@ -0,0 +1,195 @@
1
+ ---
2
+ tracker:
3
+ kind: roadmap
4
+ filePath: docs/roadmap.md
5
+ activeStates: [planned, in-progress]
6
+ terminalStates: [done]
7
+ polling:
8
+ intervalMs: 30000
9
+ workspace:
10
+ root: .harness/workspaces
11
+ hooks:
12
+ afterCreate: null
13
+ beforeRun: null
14
+ afterRun: null
15
+ beforeRemove: null
16
+ timeoutMs: 60000
17
+ agent:
18
+ # Named backend definitions (Spec 2). See docs/guides/multi-backend-routing.md.
19
+ # `capabilities` (tier/cost/privacy/context) let AMR compare backends and select a
20
+ # capability tier per dispatch — a backend without one is invisible to tier selection.
21
+ backends:
22
+ primary:
23
+ type: claude
24
+ command: claude
25
+ capabilities:
26
+ { tier: strong, costPer1kTokens: 15, privacyClass: shared-cloud, contextWindow: 200000 }
27
+ # Local backend for autonomous execution of simple tasks.
28
+ # `model` accepts a string OR a prefer-and-fallback array — first
29
+ # match wins after a `/v1/models` probe.
30
+ local:
31
+ type: pi
32
+ # Ollama's OpenAI-compatible API lives under /v1 (the resolver probes
33
+ # `${endpoint}/models`). Names must match `ollama list` exactly.
34
+ endpoint: http://127.0.0.1:11434/v1
35
+ # Prefer-and-fallback: first name present in /v1/models wins. Coder model
36
+ # first (local routes are quick-fix/diagnostic), general model as fallback.
37
+ # Quote the names — the ':tag' colon otherwise breaks YAML flow parsing.
38
+ model: ['qwen2.5-coder:7b', 'gemma3n:e4b']
39
+ capabilities:
40
+ { tier: fast, costPer1kTokens: 0, privacyClass: on-device, contextWindow: 32768 }
41
+ # Routing — controls WHICH backend handles each use case.
42
+ routing:
43
+ default: primary
44
+ quick-fix: local
45
+ diagnostic: local
46
+ # Route the intelligence pipeline (sel/pesl) to the local backend.
47
+ intelligence:
48
+ sel: local
49
+ pesl: local
50
+ # AMR (Adaptive Model Routing) — opt-in; its PRESENCE flips dispatch from the
51
+ # identity/default chain to complexity-aware tier selection. trivial/simple →
52
+ # local (fast, free); moderate/complex → claude (no `standard` backend, so a
53
+ # `standard` requirement resolves to the cheapest backend at-or-above it =
54
+ # primary). The always-on baseline-relative security-defect feeder climbs a
55
+ # unit's tier after `escalationThreshold` consecutive quality failures
56
+ # (strong-capped, then hard-fails to a human). Budget cap + LLM acceptance-eval
57
+ # are available opt-ins (docs/guides/adaptive-model-routing.md) — left off here.
58
+ policy:
59
+ complexityTierMatrix: { trivial: fast, simple: fast, moderate: standard, complex: strong }
60
+ escalationThreshold: 2
61
+ # Escalation — controls WHETHER a tier dispatches at all (orthogonal to routing).
62
+ escalation:
63
+ alwaysHuman: [full-exploration]
64
+ autoExecute: [quick-fix, diagnostic]
65
+ primaryExecute: [guided-change]
66
+ signalGated: []
67
+ diagnosticRetryBudget: 1
68
+ maxConcurrentAgents: 1
69
+ maxTurns: 10
70
+ maxRetryBackoffMs: 5000
71
+ maxConcurrentAgentsByState: {}
72
+ globalCooldownMs: 60000
73
+ maxRequestsPerMinute: 50
74
+ maxRequestsPerSecond: 1
75
+ # Default limits based on Anthropic Tier 3. Adjust according to your account tier.
76
+ # Tier 1: 40k ITPM / 10k OTPM
77
+ # Tier 2: 200k ITPM / 40k OTPM
78
+ # Tier 3: 400k ITPM / 80k OTPM
79
+ # Tier 4: 1m ITPM / 200k OTPM
80
+ maxInputTokensPerMinute: 400000
81
+ maxOutputTokensPerMinute: 80000
82
+ turnTimeoutMs: 300000
83
+ readTimeoutMs: 30000
84
+ stallTimeoutMs: 60000
85
+ intelligence:
86
+ enabled: true
87
+ requestTimeoutMs: 180000
88
+ server:
89
+ port: 8080
90
+ localModels:
91
+ enabled: true
92
+ pool:
93
+ diskBudgetGb: 100
94
+ allowedOrgs: [Qwen, deepseek-ai, meta-llama, google]
95
+ allowedFamilies: []
96
+ refresh:
97
+ intervalMs: 86400000
98
+ proposalThreshold: 5
99
+ jitterMs: 600000
100
+ installer:
101
+ backend: ollama
102
+ ollamaEndpoint: http://127.0.0.1:11434
103
+ # Built-in maintenance tasks run on cron when `maintenance.enabled: true`.
104
+ # Notable housekeeping tasks: `main-sync` (every 15 min) fast-forwards the
105
+ # orchestrator's local default branch from origin so files read from `cwd`
106
+ # (e.g., docs/roadmap.md, harness.orchestrator.md) stay current. Sync is
107
+ # fast-forward-only — never destructive — and skips with a structured
108
+ # warning event if the working tree is dirty, the branch is wrong, or the
109
+ # local default has diverged. Disable all maintenance via `maintenance.enabled: false`.
110
+ #
111
+ # Per-task Run Now (since 2026-05-09): the dashboard Maintenance page renders
112
+ # a Run Now button on every row of the schedule table. The previous single-
113
+ # button affordance (which always triggered `project-health`) has been
114
+ # removed. Each button is disabled while a `maintenance:started` event is in
115
+ # flight for that task ID and re-enables on the matching `maintenance:completed`
116
+ # or `maintenance:error` event.
117
+ maintenance:
118
+ enabled: true
119
+ ---
120
+
121
+ # Prompt Template (Local Backend — bootstrap shim)
122
+
123
+ You are a local agent with `bash`, `read`, `write`, `grep`, and `find`. You do
124
+ NOT have `/harness:*` slash commands or harness MCP tools. You run the REAL
125
+ harness workflow skills by reading them over bash — this template carries no
126
+ methodology of its own.
127
+
128
+ ## Issue: {{ issue.title }}
129
+
130
+ **Identifier:** {{ issue.identifier }}
131
+ **Description:** {{ issue.description }}
132
+
133
+ ## How to run a harness skill (the indirection rule)
134
+
135
+ To run any harness workflow skill, execute it over bash and follow its output
136
+ **verbatim**:
137
+
138
+ ```bash
139
+ harness skill run <skill-name> --autonomous --path .
140
+ ```
141
+
142
+ `harness skill run` prints the skill's full instructions (the same content the
143
+ primary backend gets from a `/harness:*` slash command) to stdout as a plain CLI
144
+ read. `--autonomous` prepends the headless-decider preamble: you do the full
145
+ analysis at full rigor but YOU decide every fork and record it in the spec — you
146
+ never pause for a human.
147
+
148
+ **Redirect rule.** Whenever a skill's output instructs you to run `/harness:X`,
149
+ instead run `harness skill run harness-X --autonomous`. Slash commands are
150
+ unavailable on this backend; the skill-run indirection is their equivalent.
151
+
152
+ ## Full-workflow entry sequence
153
+
154
+ Run these in order, each via `harness skill run <name> --autonomous --path .`,
155
+ following each skill's output before moving on:
156
+
157
+ 1. `harness-brainstorming` — runs at full rigor (≥2 approaches, YAGNI, persona
158
+ council, soundness), but YOU decide the forks (autonomous) and record each
159
+ decision in the spec.
160
+ 2. `harness-planning`
161
+ 3. `harness-execution` (or `harness-tdd` for test-driven work)
162
+ 4. `harness-verification`
163
+ 5. `harness-code-review`
164
+
165
+ Run `harness skill list` to see the full roster of available skills.
166
+
167
+ ## Gates (enforced by the harness, not just by you)
168
+
169
+ The orchestrator INDEPENDENTLY enforces `harness validate` plus the
170
+ verify/outcome-eval gates against your branch after you exit — you cannot ship
171
+ past a red gate. Reach a green state: run `harness validate` and the project's
172
+ own typecheck/lint/test yourself and fix every failure before shipping. A run
173
+ that cannot reach green is halted and re-dispatched rather than shipped, and
174
+ escalated to a human if the retry budget is exhausted.
175
+
176
+ ## Ship (only after the gates are green)
177
+
178
+ - Create a topic branch if you are still on `main`/`master`
179
+ (e.g. `feat/{{ issue.identifier }}`).
180
+ - Stage your changes and create a descriptive commit (Conventional Commits style).
181
+ - Push the branch with `git push -u origin HEAD`.
182
+ - Open a pull request with `gh pr create`. Use a HEREDOC for the body:
183
+
184
+ ```bash
185
+ gh pr create --title "<title>" --body "$(cat <<'EOF'
186
+ ## Summary
187
+
188
+ <body content with real newlines>
189
+ EOF
190
+ )"
191
+ ```
192
+
193
+ - Report the PR URL as your final output, then stop.
194
+
195
+ Attempt Number: {{ attempt }}
@@ -82,6 +82,11 @@ Your goal is to implement the following issue using the standard Harness Enginee
82
82
  Follow these steps exactly, using the corresponding slash commands to ensure
83
83
  high-quality, architecturally sound delivery:
84
84
 
85
+ <!-- This slash-command workflow targets Claude-shaped backends. A backend-specific
86
+ variant, `harness.orchestrator.local.md`, is auto-selected for `local`/`pi`
87
+ backends (which lack `/harness:*` slash commands) and expresses the same
88
+ workflow via bash `harness <gate>` CLI calls. See ADR 0071. -->
89
+
85
90
  1. **Brainstorming:** Use `/harness:brainstorming` to explore the problem space
86
91
  and draft a technical proposal in `docs/changes/`. The spec MUST include an
87
92
  Integration Points section defining how the feature connects to the system.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harness-engineering/cli",
3
- "version": "8.0.0",
3
+ "version": "9.0.0",
4
4
  "description": "CLI for Harness Engineering toolkit",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,16 +39,16 @@
39
39
  "web-tree-sitter": "^0.24.7",
40
40
  "yaml": "^2.8.3",
41
41
  "zod": "^3.25.76",
42
- "@harness-engineering/core": "0.37.0",
43
- "@harness-engineering/dashboard": "0.14.4",
44
- "@harness-engineering/graph": "0.11.8",
42
+ "@harness-engineering/core": "0.37.1",
43
+ "@harness-engineering/dashboard": "0.14.5",
44
+ "@harness-engineering/graph": "0.11.9",
45
45
  "@harness-engineering/linter-gen": "0.1.7",
46
- "@harness-engineering/orchestrator": "0.15.1",
47
- "@harness-engineering/signals": "0.2.6",
48
- "@harness-engineering/types": "0.22.0"
46
+ "@harness-engineering/orchestrator": "0.16.0",
47
+ "@harness-engineering/signals": "0.2.7",
48
+ "@harness-engineering/types": "0.23.0"
49
49
  },
50
50
  "peerDependencies": {
51
- "@harness-engineering/intelligence": "0.8.0"
51
+ "@harness-engineering/intelligence": "0.9.0"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@harness-engineering/intelligence": {