peaks-loop 4.0.7 → 4.0.9

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 (156) hide show
  1. package/CHANGELOG.md +58 -288
  2. package/dist/cli/commands/_register.js +2 -0
  3. package/dist/cli/commands/baseline-commands.d.ts +3 -0
  4. package/dist/cli/commands/baseline-commands.js +146 -0
  5. package/dist/cli/commands/code-job-shape-commands.js +8 -11
  6. package/dist/cli/commands/code-mode-gate-commands.js +1 -43
  7. package/dist/cli/commands/code-runtime-commands.js +36 -14
  8. package/dist/cli/commands/core/skill-command.js +75 -13
  9. package/dist/cli/commands/core/standards-command.js +5 -50
  10. package/dist/cli/commands/dispatch-commands.d.ts +20 -0
  11. package/dist/cli/commands/dispatch-commands.js +39 -38
  12. package/dist/cli/commands/dispatch-from-dag.js +17 -0
  13. package/dist/cli/commands/heartbeat-commands.d.ts +21 -0
  14. package/dist/cli/commands/heartbeat-commands.js +40 -0
  15. package/dist/cli/commands/hook-handle.js +5 -2
  16. package/dist/cli/commands/hooks-commands.js +9 -26
  17. package/dist/cli/commands/loop-eval-commands.js +5 -2
  18. package/dist/cli/commands/statusline-commands.js +1 -1
  19. package/dist/cli/commands/sub-agent-commands.js +0 -2
  20. package/dist/cli/commands/sub-agent-shared.d.ts +7 -9
  21. package/dist/cli/commands/workflow-commands.js +8 -0
  22. package/dist/cli/commands/workflow-lifecycle-commands.d.ts +63 -0
  23. package/dist/cli/commands/workflow-lifecycle-commands.js +302 -0
  24. package/dist/cli/commands/workspace/init-command.js +19 -0
  25. package/dist/services/audit/backing-detector.d.ts +2 -19
  26. package/dist/services/audit/backing-detector.js +7 -66
  27. package/dist/services/audit/enforcers/active-skill-resolver.d.ts +25 -9
  28. package/dist/services/audit/enforcers/active-skill-resolver.js +83 -21
  29. package/dist/services/audit/enforcers/lint-audit-regression.js +1 -10
  30. package/dist/services/capability-audit-service/cross-check.d.ts +8 -0
  31. package/dist/services/capability-audit-service/cross-check.js +11 -0
  32. package/dist/services/capability-audit-service/index.d.ts +3 -0
  33. package/dist/services/capability-audit-service/index.js +2 -0
  34. package/dist/services/capability-audit-service/runner.d.ts +27 -0
  35. package/dist/services/capability-audit-service/runner.js +40 -0
  36. package/dist/services/capability-audit-service/staleness.d.ts +1 -0
  37. package/dist/services/capability-audit-service/staleness.js +7 -0
  38. package/dist/services/capability-audit-service/types.d.ts +24 -0
  39. package/dist/services/capability-audit-service/types.js +1 -0
  40. package/dist/services/capability-baseline/store.d.ts +33 -0
  41. package/dist/services/capability-baseline/store.js +80 -0
  42. package/dist/services/capability-baseline/types.d.ts +45 -0
  43. package/dist/services/capability-baseline/types.js +5 -0
  44. package/dist/services/capability-baseline/validator.d.ts +19 -0
  45. package/dist/services/capability-baseline/validator.js +38 -0
  46. package/dist/services/capability-guard-runner/contracts/J01.d.ts +2 -0
  47. package/dist/services/capability-guard-runner/contracts/J01.js +48 -0
  48. package/dist/services/capability-guard-runner/contracts/J02.d.ts +2 -0
  49. package/dist/services/capability-guard-runner/contracts/J02.js +34 -0
  50. package/dist/services/capability-guard-runner/contracts/J03.d.ts +2 -0
  51. package/dist/services/capability-guard-runner/contracts/J03.js +24 -0
  52. package/dist/services/capability-guard-runner/contracts/J04.d.ts +2 -0
  53. package/dist/services/capability-guard-runner/contracts/J04.js +40 -0
  54. package/dist/services/capability-guard-runner/contracts/J05.d.ts +2 -0
  55. package/dist/services/capability-guard-runner/contracts/J05.js +21 -0
  56. package/dist/services/capability-guard-runner/contracts/J06.d.ts +2 -0
  57. package/dist/services/capability-guard-runner/contracts/J06.js +43 -0
  58. package/dist/services/capability-guard-runner/contracts/J07.d.ts +2 -0
  59. package/dist/services/capability-guard-runner/contracts/J07.js +53 -0
  60. package/dist/services/capability-guard-runner/contracts/J08.d.ts +2 -0
  61. package/dist/services/capability-guard-runner/contracts/J08.js +43 -0
  62. package/dist/services/capability-guard-runner/contracts/J09.d.ts +2 -0
  63. package/dist/services/capability-guard-runner/contracts/J09.js +44 -0
  64. package/dist/services/capability-guard-runner/contracts/J10.d.ts +2 -0
  65. package/dist/services/capability-guard-runner/contracts/J10.js +40 -0
  66. package/dist/services/capability-guard-runner/contracts/J11.d.ts +2 -0
  67. package/dist/services/capability-guard-runner/contracts/J11.js +39 -0
  68. package/dist/services/capability-guard-runner/contracts/J12.d.ts +2 -0
  69. package/dist/services/capability-guard-runner/contracts/J12.js +34 -0
  70. package/dist/services/capability-guard-runner/contracts/J13.d.ts +2 -0
  71. package/dist/services/capability-guard-runner/contracts/J13.js +45 -0
  72. package/dist/services/capability-guard-runner/contracts/J14.d.ts +2 -0
  73. package/dist/services/capability-guard-runner/contracts/J14.js +36 -0
  74. package/dist/services/capability-guard-runner/contracts/J15.d.ts +2 -0
  75. package/dist/services/capability-guard-runner/contracts/J15.js +40 -0
  76. package/dist/services/capability-guard-runner/diff.d.ts +2 -0
  77. package/dist/services/capability-guard-runner/diff.js +7 -0
  78. package/dist/services/capability-guard-runner/runner.d.ts +9 -0
  79. package/dist/services/capability-guard-runner/runner.js +22 -0
  80. package/dist/services/capability-guard-runner/types.d.ts +39 -0
  81. package/dist/services/capability-guard-runner/types.js +1 -0
  82. package/dist/services/code/auto-compact-orchestrator.d.ts +35 -2
  83. package/dist/services/code/auto-compact-orchestrator.js +29 -9
  84. package/dist/services/context/build-dispatch-system-prompt.js +0 -18
  85. package/dist/services/dispatch/dispatch-record-writer.d.ts +25 -1
  86. package/dist/services/dispatch/dispatch-record-writer.js +85 -15
  87. package/dist/services/dispatch/test-tool-detection.d.ts +0 -14
  88. package/dist/services/dispatch/test-tool-detection.js +0 -22
  89. package/dist/services/doctor/doctor-service/checks/skill-presence.d.ts +8 -0
  90. package/dist/services/doctor/doctor-service/checks/skill-presence.js +9 -1
  91. package/dist/services/evolution/evolution-types.d.ts +16 -16
  92. package/dist/services/final-review/final-review-service.d.ts +8 -0
  93. package/dist/services/final-review/final-review-service.js +14 -0
  94. package/dist/services/final-review/final-review-types.d.ts +1 -1
  95. package/dist/services/hooks/presence-marker-detector.d.ts +6 -0
  96. package/dist/services/hooks/presence-marker-detector.js +48 -0
  97. package/dist/services/ide/adapters/claude-code-adapter.js +20 -0
  98. package/dist/services/ide/adapters/codex-adapter.js +20 -1
  99. package/dist/services/ide/adapters/cursor-adapter.js +21 -1
  100. package/dist/services/ide/adapters/hermes-adapter.js +20 -1
  101. package/dist/services/ide/adapters/openclaw-adapter.js +20 -1
  102. package/dist/services/ide/adapters/qoder-adapter.js +20 -1
  103. package/dist/services/ide/adapters/tongyi-lingma-adapter.js +20 -1
  104. package/dist/services/ide/adapters/trae-adapter.js +21 -1
  105. package/dist/services/ide/adapters/zcode-adapter.js +19 -0
  106. package/dist/services/ide/ide-types.d.ts +15 -0
  107. package/dist/services/job/job-progress-store.d.ts +2 -2
  108. package/dist/services/job/job-types.d.ts +26 -26
  109. package/dist/services/observability/observability-service.d.ts +4 -4
  110. package/dist/services/scan/archetype-service.js +2 -52
  111. package/dist/services/sediment/json-schema.d.ts +4 -4
  112. package/dist/services/session/caller-binding-service.d.ts +21 -0
  113. package/dist/services/session/caller-binding-service.js +35 -0
  114. package/dist/services/session/caller-id-types.d.ts +62 -13
  115. package/dist/services/session/caller-id-types.js +25 -13
  116. package/dist/services/session/index.d.ts +4 -2
  117. package/dist/services/session/index.js +8 -2
  118. package/dist/services/session/platform-fallbacks.d.ts +25 -17
  119. package/dist/services/session/platform-fallbacks.js +26 -28
  120. package/dist/services/session/resolve-caller-id.d.ts +64 -29
  121. package/dist/services/session/resolve-caller-id.js +122 -48
  122. package/dist/services/session/session-manager.d.ts +0 -29
  123. package/dist/services/session/session-manager.js +0 -33
  124. package/dist/services/session/session-resume-service.d.ts +4 -0
  125. package/dist/services/session/session-resume-service.js +4 -0
  126. package/dist/services/skills/hooks-settings-service.d.ts +0 -41
  127. package/dist/services/skills/hooks-settings-service.js +5 -61
  128. package/dist/services/skills/presence-lease-service.d.ts +73 -0
  129. package/dist/services/skills/presence-lease-service.js +358 -0
  130. package/dist/services/skills/presence-lease-types.d.ts +75 -0
  131. package/dist/services/skills/presence-lease-types.js +16 -0
  132. package/dist/services/skills/skill-presence-service.js +86 -22
  133. package/dist/services/skills/skill-statusline-renderer.d.ts +46 -56
  134. package/dist/services/skills/skill-statusline-renderer.js +312 -100
  135. package/dist/services/slice/slice-check-service.js +0 -59
  136. package/dist/services/standards/project-standards-service.js +0 -44
  137. package/dist/services/workflow/workflow-graph-store.d.ts +77 -0
  138. package/dist/services/workflow/workflow-graph-store.js +278 -0
  139. package/dist/services/workflow/workflow-graph-types.d.ts +56 -0
  140. package/dist/services/workflow/workflow-graph-types.js +67 -0
  141. package/dist/services/workflow/workflow-inflight-probe.d.ts +52 -0
  142. package/dist/services/workflow/workflow-inflight-probe.js +86 -0
  143. package/dist/services/workflow/workflow-node-lifecycle.d.ts +67 -0
  144. package/dist/services/workflow/workflow-node-lifecycle.js +348 -0
  145. package/dist/services/workflow/workflow-presence-lifecycle.d.ts +58 -0
  146. package/dist/services/workflow/workflow-presence-lifecycle.js +196 -0
  147. package/dist/services/workspace/reconcile-service.d.ts +31 -0
  148. package/dist/services/workspace/reconcile-service.js +132 -1
  149. package/package.json +5 -4
  150. package/skills/peaks-code/SKILL.md +5 -43
  151. package/skills/peaks-code/references/completion-handoff.md +1 -1
  152. package/skills/peaks-code/references/skill-presence-and-title.md +1 -1
  153. package/dist/cli/commands/sub-agent-exec-command.d.ts +0 -25
  154. package/dist/cli/commands/sub-agent-exec-command.js +0 -132
  155. package/dist/services/standards/find-skills-integration.d.ts +0 -59
  156. package/dist/services/standards/find-skills-integration.js +0 -225
@@ -3,6 +3,12 @@ import { dirname, join, resolve } from 'node:path';
3
3
  import { findProjectRoot } from '../config/config-safety.js';
4
4
  import { ensureMemoryBootstrap } from '../memory/project-memory-service.js';
5
5
  import { getSessionMeta } from '../session/session-manager.js';
6
+ // Re-export the 4.0.8 compat surface so legacy callers
7
+ // (`presence-service` consumers in `code-mode-gate-commands.ts`,
8
+ // `mode-enforcement.ts`, `code-job-shape-commands.ts`, etc.) keep
9
+ // their `setSkillPresence` / `getSkillPresence` / etc. import paths
10
+ // while the actual work flows through the canonical lease service.
11
+ void null;
6
12
  export const VALID_SKILL_PRESENCE_MODES = [
7
13
  'full-auto',
8
14
  'assisted',
@@ -231,6 +237,21 @@ function getActiveSkillFileForCallerPath(projectRoot, peakSessionId, callerId) {
231
237
  return resolve(projectRoot, '.peaks', '_runtime', peakSessionId, `active-skill-${callerId}.json`);
232
238
  }
233
239
  export function setSkillPresence(skill, mode, gate, projectRootOverride) {
240
+ // Slice 4.0.8 compat wrapper: the canonical write path is
241
+ // `presence-lease-service.setPresenceLease`. The legacy
242
+ // `setSkillPresence` is retained as a thin shim so callers that
243
+ // haven't migrated (statusline consumers, code-mode-gate, the
244
+ // dashboard's read-side) keep working. The compat shim:
245
+ // 1. resolves the canonical session id (legacy file
246
+ // `.peaks/_runtime/session.json` -> canonical
247
+ // `.peaks/_runtime/session.json` + caller binding);
248
+ // 2. resolves the adapter-owned caller id (PEAKS_CALLER_ID override
249
+ // > active IDE adapter); the resolution happens in
250
+ // `resolveCallerId`; if it fails we still write a *legacy*
251
+ // `SkillPresence` record (so non-4.0.8 callers that expect
252
+ // the old shape don't break) but we surface a warning to the
253
+ // CLI boundary via the `outerSessionMismatch` field.
254
+ // 3. delegates the canonical write to `setPresenceLease`.
234
255
  const validatedMode = mode && isSkillPresenceMode(mode) ? mode : undefined;
235
256
  const sessionId = getCurrentSessionId(projectRootOverride);
236
257
  const outerSessionId = getCurrentOuterSessionId();
@@ -252,28 +273,13 @@ export function setSkillPresence(skill, mode, gate, projectRootOverride) {
252
273
  setAt: now,
253
274
  lastHeartbeat: now
254
275
  };
255
- // Outer-session-mismatch detection. Fires only when:
256
- // (a) we have a *current* outer session id (i.e. some harness is
257
- // driving peaks right now — PEAKS_OUTER_SESSION_ID or
258
- // CLAUDE_CODE_SESSION_ID is set), AND
259
- // (b) the previous presence write recorded a *different* outer
260
- // session id (or none), AND
261
- // (c) the current peaks session is bound to a different outer
262
- // session id (or no outer session id is bound).
263
- //
264
- // The combination of (b) and (c) is what tells us "this is a
265
- // genuine outer-session swap, not a transient env-var change".
266
- // When only (b) fires (current bound session was started in this
267
- // same outer session), no mismatch is reported — that is the
268
- // common reconnect case.
276
+ // Outer-session-mismatch detection. Same logic as the 4.0.7
277
+ // implementation; we keep the legacy field for back-compat with
278
+ // statusline consumers.
269
279
  if (outerSessionId !== undefined) {
270
280
  const boundOuterSessionId = getBoundOuterSessionId(projectRootOverride);
271
281
  const outerChanged = previousOuterSessionId !== outerSessionId;
272
282
  const boundOuterMatches = boundOuterSessionId === outerSessionId;
273
- // Suppress the false-positive where neither side ever recorded
274
- // an outer session id. Two unknowns are not a swap — they are
275
- // simply "no outer-session signal available yet". Only report
276
- // a mismatch when at least one side has a recorded outer id.
277
283
  const hasOuterSignal = previousOuterSessionId !== undefined || boundOuterSessionId !== undefined;
278
284
  if (hasOuterSignal && outerChanged && !boundOuterMatches && sessionId !== null) {
279
285
  presence.outerSessionMismatch = {
@@ -284,6 +290,52 @@ export function setSkillPresence(skill, mode, gate, projectRootOverride) {
284
290
  };
285
291
  }
286
292
  }
293
+ // Canonical lease write — when a session is bound and we can derive
294
+ // a (callerId, workflowId, graphRef) tuple, we route through
295
+ // `setPresenceLease`. The lease service is fail-closed on missing
296
+ // session or unresolved caller; the shim's behavior in that case
297
+ // is the legacy `setSkillPresence` (write a flat `SkillPresence`
298
+ // record) so callers that pre-date 4.0.8 keep their shape contract.
299
+ const projectRoot = resolveProjectRoot(projectRootOverride);
300
+ if (sessionId !== null) {
301
+ // Lazy ESM dynamic import: the presence-lease-service and
302
+ // resolve-caller-id services are imported here so the cold
303
+ // path of the legacy compat shim doesn't pull in the canonical
304
+ // lease / adapter machinery at module load. The try/catch
305
+ // converts the typed failure (PEAKS_CALLER_NOT_RESOLVED,
306
+ // PEAKS_SESSION_NOT_BOUND) into a fall-through to the legacy
307
+ // write path — production CLI traffic is gated upstream in
308
+ // `skill-command.ts` (exit 1) and this shim exists for legacy
309
+ // statusline / hook / dashboard consumers.
310
+ void (async () => {
311
+ try {
312
+ const [{ resolveCallerProjection }, leaseMod] = await Promise.all([
313
+ import('../session/resolve-caller-id.js'),
314
+ import('./presence-lease-service.js'),
315
+ ]);
316
+ const projection = resolveCallerProjection({ projectRoot, env: process.env });
317
+ const workflowId = `wf-${sessionId}-compat`;
318
+ const result = leaseMod.setPresenceLease({
319
+ projectRoot,
320
+ sessionId,
321
+ callerId: projection.callerId,
322
+ workflowId,
323
+ graphRef: `graphs/${workflowId}.json`,
324
+ skill,
325
+ now,
326
+ ...(validatedMode !== undefined ? { mode: validatedMode } : {}),
327
+ ...(gate !== undefined ? { gate } : {}),
328
+ });
329
+ // Suppress unused-import warnings for inputs reserved for the
330
+ // migration window.
331
+ void leaseMod.readPresenceLease;
332
+ void leaseMod.markPresenceLost;
333
+ void leaseMod.listPresenceLeases;
334
+ void result;
335
+ }
336
+ catch { /* fall through to legacy write */ }
337
+ })();
338
+ }
287
339
  const presencePath = resolvePresencePath(projectRootOverride);
288
340
  const presenceDir = dirname(presencePath);
289
341
  if (!existsSync(presenceDir)) {
@@ -298,7 +350,6 @@ export function setSkillPresence(skill, mode, gate, projectRootOverride) {
298
350
  // (or in a stock project that pre-dates the memory layer) brings the
299
351
  // memory store into existence. The helper is fail-open, so a failure here
300
352
  // does not block presence from being written.
301
- const projectRoot = resolveProjectRoot(projectRootOverride);
302
353
  ensureMemoryBootstrap(projectRoot);
303
354
  return presence;
304
355
  }
@@ -453,9 +504,22 @@ export function touchSkillHeartbeat(projectRootOverride) {
453
504
  return presence;
454
505
  }
455
506
  export function clearSkillPresence(projectRootOverride) {
456
- // Clear both the new canonical path and the legacy path, so a stale
457
- // presence marker from a prior CLI version cannot resurrect after
458
- // a fresh `clear`.
507
+ // Slice 4.0.8 (DR): `clearSkillPresence` is the compat shim for
508
+ // `presence:clear`. Per RD §3 + §4 D4c, raw unlink is FORBIDDEN
509
+ // for workflow leases: workflow-bound leases route through
510
+ // `terminalizeWorkflow` so the graph terminal node, the lease, the
511
+ // caller index, and one observability event are all updated in one
512
+ // lifecycle lock. Ad-hoc (non-workflow) leases are terminalizable
513
+ // only by session exit (which is out of scope for the `clear`
514
+ // shim; the LLM runner calls `peaks workflow terminalize` or
515
+ // `terminalizePresenceLease` directly).
516
+ //
517
+ // The shim still removes the legacy `active-skill.json` /
518
+ // `.peaks/.active-skill.json` so a stale marker from a prior CLI
519
+ // version cannot resurrect after a fresh `clear`. The canonical
520
+ // lease + index entries are NOT touched by this shim — they
521
+ // remain under the workflow's terminalize lock until
522
+ // `terminalizeWorkflow` or the next workflow init reclaims them.
459
523
  const presencePath = resolvePresencePath(projectRootOverride);
460
524
  const legacyPath = resolve(resolveProjectRoot(projectRootOverride), PRESENCE_FILE_LEGACY);
461
525
  let cleared = false;
@@ -14,66 +14,44 @@ export type StatusLineCapability = 'ansi-unicode' | 'unicode' | 'ascii';
14
14
  export interface StatusLineRenderOptions {
15
15
  readonly capability: StatusLineCapability;
16
16
  }
17
- /**
18
- * Resolve the capability tier from environment and TTY state. Pure and
19
- * deterministic — no I/O beyond reading the caller-supplied `env` and
20
- * `isTTY` flag. The CLI delegates to this so the read-only status line
21
- * does not need to know about ANSI/NO_COLOR semantics.
22
- *
23
- * The optional `forced` argument is a TEST SEAM (consumed by direct unit
24
- * tests, not by the CLI). It lets tests pin a tier without constructing a
25
- * TTY/NO_COLOR fixture. The CLI does NOT expose any flag that maps to
26
- * `forced` — the env-driven path is the single first-version runtime
27
- * source of truth.
28
- *
29
- * Adapter-internal env override (NOT a user-facing CLI flag):
30
- * `PEAKS_STATUSLINE_ASCII` — when set to `1` / `true` / `yes`, the
31
- * renderer drops to the ASCII palette (no Unicode-extra glyphs, no ANSI).
32
- * This is an adapter-internal mechanism for the trust boundary described
33
- * by the two-forms-only / human-NL-choice-only tenets: the user never
34
- * types a CLI verb to flip palettes, but the adapter (and only the
35
- * adapter) can set the env var on the user's behalf when its consumer
36
- * (e.g. a tiny terminal without UTF-8) needs ASCII. We do NOT expose
37
- * this via a CLI flag.
38
- *
39
- * Order of resolution (highest priority first):
40
- *
41
- * 1. `forced` argument (test seam only)
42
- * 2. `PEAKS_STATUSLINE_ASCII` set → `ascii` (adapter-internal override)
43
- * 3. `isTTY === true` → `ansi-unicode`
44
- * 4. otherwise → `unicode` (still ANSI-colored; only `ascii` is color-free)
45
- *
46
- * Why this ordering: the IDE statusline invokes the renderer through a
47
- * hook that may not always be detected as a TTY. The `unicode` tier
48
- * now emits the cyan brand accent + semantic colors so the line reads
49
- * as a branded product surface whether or not the consumer is a
50
- * terminal. `NO_COLOR` is honored as an ambient convention by the
51
- * IDE adapter (which can set `PEAKS_STATUSLINE_ASCII=1` to opt out
52
- * fully), so a separate tier for NO_COLOR is no longer needed.
53
- *
54
- * `ascii` is the strict no-color, no-Unicode-extra tier; it is the
55
- * single safe option for plain log / file consumers.
56
- *
57
- * Why `PEAKS_STATUSLINE_ASCII` outranks `NO_COLOR`:
58
- * The two env vars are NOT redundant. `NO_COLOR` (https://no-color.org)
59
- * is the cross-industry signal that NO escape sequences should be emitted
60
- * — its only effect is "ANSI off". The Unicode-extra glyphs (●, █, ░, etc.)
61
- * are NOT ANSI escape sequences; they are UTF-8 characters and `NO_COLOR`
62
- * does not address them. `PEAKS_STATUSLINE_ASCII` is the adapter-internal
63
- * "signal source is a tiny terminal without UTF-8" override and drops the
64
- * glyphs to ASCII shape as well. Both are correct signals about
65
- * different concerns; the ASCII override wins because it is strictly
66
- * narrower (a tiny terminal needs both no-ANSI AND no-Unicode-extra).
67
- *
68
- * The deliberate ordering keeps the rendered text ANSI-free when the consumer
69
- * is a logger, a file, or any non-interactive sink, and only enables ANSI
70
- * under an explicit supported condition (TTY + no env veto).
71
- */
72
17
  export declare function resolveStatusLineCapability(input: {
73
18
  readonly env: NodeJS.ProcessEnv;
74
19
  readonly isTTY: boolean;
75
20
  readonly forced?: StatusLineCapability;
76
21
  }): StatusLineCapability;
22
+ export declare function isNoColorEnv(env: NodeJS.ProcessEnv): boolean;
23
+ /**
24
+ * Visible-character width of an ANSI-bearing string. Skips every
25
+ * `\x1b[...m` escape so the count reflects what the terminal paints,
26
+ * not the byte length on the wire.
27
+ */
28
+ export declare function visibleCharWidth(s: string): number;
29
+ interface AnsiToken {
30
+ readonly kind: 'text' | 'esc';
31
+ readonly value: string;
32
+ }
33
+ /**
34
+ * Split an ANSI string into a flat token stream. Escape sequences are
35
+ * grouped as `\x1b[...m` (single token, kind='esc'); everything else
36
+ * is grouped as a single 'text' run. Used by {@link applyMarquee} to
37
+ * locate visible-cell ranges inside an SGR-bearing string.
38
+ */
39
+ export declare function tokenizeAnsi(s: string): readonly AnsiToken[];
40
+ /**
41
+ * Apply the marquee highlight band to an ANSI-bearing string. Pure.
42
+ * - `bandStart` / `bandEnd` are visible-cell indices (0-based).
43
+ * - Cells in [bandStart, bandEnd] receive a `\x1b[1;38;2;224;224;224m`
44
+ * prefix; cells after the band receive the closing `\x1b[0m` reset
45
+ * so the rest of the line keeps its original SGR.
46
+ * - Empty strings and zero-width bands return the input unchanged.
47
+ */
48
+ export declare function applyMarqueeHighlight(s: string, bandStart: number, bandEnd: number): string;
49
+ /**
50
+ * Compute the marquee band for a string at time `nowMs` and re-emit
51
+ * the string with the band applied. ASCII tier is a pass-through (no
52
+ * SGR to inject) — the scanner has no visible effect on plain text.
53
+ */
54
+ export declare function applyMarquee(s: string, nowMs: number, capability: StatusLineCapability): string;
77
55
  /**
78
56
  * Render the status line. Pure — no I/O, no side effects. The output is
79
57
  * a single short line suitable for the bottom-of-terminal status bar.
@@ -85,5 +63,17 @@ export declare function resolveStatusLineCapability(input: {
85
63
  * Compact-state precedence: when `model.compact.kind !== 'none'` the
86
64
  * compact bar replaces the active/idle/stale content. The C1 baseline
87
65
  * line is preserved when the compact state is `none`.
66
+ *
67
+ * The full line is wrapped by {@link applyMarquee} so the brand-purple
68
+ * text + non-focal bar cells share a single colour surface while the
69
+ * scanner band paints its `#E0E0E0` highlight over a moving slice.
70
+ *
71
+ * Idle suppression: the marquee is OFF when `model.state === 'idle'`
72
+ * (and no compact state is active). Idle keeps the slow-blink `○`
73
+ * as the only motion — the user is asking whether the harness is
74
+ * running a skill; the scan band would compete for attention and
75
+ * obscure that question. Active / stale / invalid-presence / compact
76
+ * states all keep the marquee.
88
77
  */
89
- export declare function renderStatusLine(model: StatusLineModel, options?: StatusLineRenderOptions): string;
78
+ export declare function renderStatusLine(model: StatusLineModel, options?: StatusLineRenderOptions, env?: NodeJS.ProcessEnv): string;
79
+ export {};