@wrongstack/cli 0.305.0 → 0.306.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.
@@ -13,15 +13,12 @@ import {
13
13
  runOAuthLoginKind,
14
14
  runOAuthLoginMenu,
15
15
  validateFamily
16
- } from "./chunk-FJVCVZIV.js";
17
- import "./chunk-SSSJQVUY.js";
18
- import {
19
- restoreFlags
20
- } from "./chunk-CSAPOCBP.js";
16
+ } from "./chunk-WC3DK6BI.js";
21
17
  import {
22
18
  parseAuthFlags
23
- } from "./chunk-C5NXM2SI.js";
19
+ } from "./chunk-5JW3XCRA.js";
24
20
  import "./chunk-3IC4IEZC.js";
21
+ import "./chunk-SSSJQVUY.js";
25
22
  import {
26
23
  loadConfigProviders,
27
24
  maskedKey,
@@ -30,6 +27,9 @@ import {
30
27
  nowIso,
31
28
  writeKeysBack
32
29
  } from "./chunk-SZ42FYPT.js";
30
+ import {
31
+ restoreFlags
32
+ } from "./chunk-CSAPOCBP.js";
33
33
  import "./chunk-B4JLSXZB.js";
34
34
  import {
35
35
  activeProfileConfigPath
@@ -657,4 +657,4 @@ async function runAuthRemove(deps, providerId) {
657
657
  export {
658
658
  authCmd
659
659
  };
660
- //# sourceMappingURL=auth-K7CYVVKH.js.map
660
+ //# sourceMappingURL=auth-GVPYLJPS.js.map
@@ -1,9 +1,15 @@
1
1
  /**
2
2
  * Check argv for --help / --version and dispatch directly.
3
3
  *
4
- * Returns 0 when a flag fired, or null when neither was present.
4
+ * Returns 0 when a flag fired, or null when neither flag was present.
5
5
  * The renderer is a stub that writes to stdout — help text is plain
6
6
  * `write` calls, no TTY needed.
7
+ *
8
+ * When `--help` accompanies a known subcommand positional (e.g.
9
+ * `wstack hq --help`), this defers to the subcommand dispatcher
10
+ * (returns null) so the subcommand can print its own focused help.
11
+ * `--version` always fires globally — the CLI version is not
12
+ * subcommand-specific.
7
13
  */
8
14
  export declare function handleHelpVersionShortCircuit(argv: string[]): Promise<number | null>;
9
15
  //# sourceMappingURL=short-circuit-flags.d.ts.map
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Interactive system-prompt selection menu — Lite / Standard / Pro.
3
+ *
4
+ * Shown at startup when the user did not pin a variant via
5
+ * `--system-lite`, `--system-pro`, or `--system-prompt <variant>`.
6
+ * Each option displays the estimated token count of the identity block
7
+ * that variant injects (resolved through the same bundled → global →
8
+ * project instruction dirs the SystemPromptBuilder uses, so a project
9
+ * or profile override is reflected in the count).
10
+ *
11
+ * Flow mirrors `runLaunchPrompts`:
12
+ * - no saved variant (first run) → show the numbered menu directly
13
+ * - saved variant exists → one-line "Continue with these?"
14
+ * gate; `n` opens the full menu, `q` aborts the launch
15
+ * - the caller persists the selection via `persistSystemPromptVariant`
16
+ * so the next boot can offer the gate instead of re-asking.
17
+ *
18
+ * `q` at any prompt throws `LaunchAbortedError` (the same cancellation
19
+ * contract as the launch prompts), and the caller in boot.ts exits 0.
20
+ */
21
+ import { type SystemInstructionVariant } from '@wrongstack/core/agent';
22
+ import type { ReadlineInputReader } from '../input-reader.js';
23
+ import type { TerminalRenderer } from '../renderer.js';
24
+ /** Menu order + display labels. `default` is shown as "Standard". */
25
+ export declare const SYSTEM_PROMPT_OPTIONS: ReadonlyArray<{
26
+ variant: SystemInstructionVariant;
27
+ label: string;
28
+ hint: string;
29
+ }>;
30
+ /** All selectable variants, in menu order. */
31
+ export declare const SYSTEM_PROMPT_VARIANTS: readonly SystemInstructionVariant[];
32
+ /** Instruction dirs used to resolve the identity text per variant. */
33
+ export interface SystemPromptMenuPaths {
34
+ /** Profile override directory, e.g. `~/.wrongstack/profiles/<name>/instructions`. */
35
+ globalDir?: string | undefined;
36
+ /** Project override directory, e.g. `<project>/.wrongstack/instructions`. */
37
+ projectDir?: string | undefined;
38
+ }
39
+ /** True when any `--system-*` flag pins a variant (menu must not show). */
40
+ export declare function isSystemPromptPinned(flags: Record<string, string | boolean>): boolean;
41
+ /**
42
+ * Pure predicate — when to skip the system-prompt menu entirely. The boot
43
+ * caller additionally gates on interactive-TTY conditions; this covers the
44
+ * flag-level reasons so scripts can opt out with `--no-menu`/`--skip` and
45
+ * flag-pinned launches never prompt.
46
+ */
47
+ export declare function shouldSkipSystemPromptMenu(flags: Record<string, string | boolean>): boolean;
48
+ /**
49
+ * Estimate the tokens of the system identity block each variant injects.
50
+ *
51
+ * Resolution goes through the same `loadInstructionBundle` the
52
+ * SystemPromptBuilder runs (bundled → global → project, later layers override
53
+ * `system.identity`), and composition goes through the builder's own
54
+ * `buildIdentityLayer`. Calling the real composer matters: when the identity
55
+ * came from the *project* layer, WS-016 makes the builder emit the bundled
56
+ * identity PLUS a `<project-supplied-instructions>` delimiter block PLUS the
57
+ * project text rather than replacing the identity. Counting
58
+ * `bundle.system.identity` alone therefore under-reports a project override by
59
+ * the whole bundled prompt — which is exactly the case a repo that ships its
60
+ * own `system-pro.md` hits.
61
+ *
62
+ * The figure is an **upper bound**, not `/context` parity: `buildIdentityLayer`
63
+ * is called without an `InstructionTemplateContext`, which by its own contract
64
+ * keeps the full text, so `ws:if` blocks for tools the live request never
65
+ * registers are still counted. Erring high is the right direction for a menu
66
+ * whose purpose is comparing variant cost, and every figure is rendered with a
67
+ * leading `~`.
68
+ */
69
+ export declare function countSystemPromptTokens(paths: SystemPromptMenuPaths): Promise<Record<SystemInstructionVariant, number>>;
70
+ /**
71
+ * Read the variant the user explicitly saved to the profile config file.
72
+ *
73
+ * The config loader materializes `systemPrompt: { variant: 'default' }` for
74
+ * every config (see `@wrongstack/core` config-loader defaults), so the
75
+ * in-memory Config cannot distinguish "user chose Standard" from "never
76
+ * chose". Only the raw file can: {@link persistSystemPromptVariant} writes
77
+ * the key explicitly, so its presence on disk is the signal that a selection
78
+ * was made. Absence (first run, or a config edited before this feature
79
+ * existed) returns undefined and the caller shows the full menu instead of
80
+ * the summary gate.
81
+ */
82
+ export declare function readSavedSystemPromptVariant(configPath: string): Promise<SystemInstructionVariant | undefined>;
83
+ /**
84
+ * Run the interactive system-prompt selection. Returns the chosen variant.
85
+ * Throws {@link LaunchAbortedError} when the user presses `q` (the caller
86
+ * exits 0, matching the launch-prompts contract).
87
+ *
88
+ * @throws LaunchAbortedError when the user cancels.
89
+ */
90
+ export declare function runSystemPromptMenu(deps: {
91
+ renderer: TerminalRenderer;
92
+ reader: ReadlineInputReader;
93
+ paths: SystemPromptMenuPaths;
94
+ /** Saved variant from config — enables the summary gate. */
95
+ lastVariant?: SystemInstructionVariant | undefined;
96
+ }): Promise<SystemInstructionVariant>;
97
+ /**
98
+ * Persist the chosen variant to the profile config file so the next boot
99
+ * can offer a one-line "Continue with these?" gate. Mirrors
100
+ * `persistLaunchChoices`: reads the existing JSON, mutates only the
101
+ * `systemPrompt` block, writes back atomically with mode 0600. Other fields
102
+ * (including encrypted secrets) pass through round-trip unchanged.
103
+ *
104
+ * @throws when the config file exists but is corrupt (same policy as the
105
+ * launch-choices writer — never overwrite unreadable user config silently).
106
+ */
107
+ export declare function persistSystemPromptVariant(configPath: string, variant: SystemInstructionVariant): Promise<void>;
108
+ /** Outcome of {@link maybeRunSystemPromptMenu}. */
109
+ export interface SystemPromptMenuOutcome {
110
+ /**
111
+ * The variant the user selected, or `undefined` when the menu did not run
112
+ * (non-TTY / flag-skipped) or was aborted. `undefined` means "caller must
113
+ * not patch config" — it is not a synonym for `'default'`.
114
+ */
115
+ variant?: SystemInstructionVariant | undefined;
116
+ /** True when the user pressed `q`. The caller should exit cleanly. */
117
+ aborted: boolean;
118
+ /** True when the selection differs from what was saved on disk. */
119
+ changed: boolean;
120
+ /** Set when persistence failed; the caller surfaces it as a warning. */
121
+ persistError?: unknown;
122
+ }
123
+ /**
124
+ * The real enforcement point for the startup system-prompt menu.
125
+ *
126
+ * `isInteractiveTTY` is an explicit **parameter**, not read from
127
+ * `process.stdin` — that is the entire reason this function exists. While the
128
+ * gate lived as a bare `if (isInteractiveTTY)` wrapping an inline block in
129
+ * `boot.ts`, no test could reach it: the only importable symbol was
130
+ * `shouldSkipSystemPromptMenu`, which covers the *flag-level* reasons and is
131
+ * nested one level deeper. Coverage therefore stopped at the predicate and
132
+ * never touched the condition that actually suppresses the prompt.
133
+ *
134
+ * Behavior is intentionally identical to the inline block it replaces:
135
+ * - non-TTY or flag-skipped → return early, touch nothing, read nothing
136
+ * - `q` at the prompt → `aborted: true` (caller closes + exits 0)
137
+ * - selection === saved → `changed: false`, no config write
138
+ * - persistence throws → captured in `persistError`, never rethrown
139
+ *
140
+ * Persistence failure is deliberately non-fatal: a read-only config must not
141
+ * prevent the session from starting.
142
+ */
143
+ export declare function maybeRunSystemPromptMenu(opts: {
144
+ isInteractiveTTY: boolean;
145
+ flags: Record<string, string | boolean>;
146
+ renderer: TerminalRenderer;
147
+ reader: ReadlineInputReader;
148
+ profileConfigPath: string;
149
+ paths: SystemPromptMenuPaths;
150
+ }): Promise<SystemPromptMenuOutcome>;
151
+ //# sourceMappingURL=system-prompt-menu.d.ts.map
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Machine-evidence contract for Chimera cascade fix agents (P0-3).
3
+ *
4
+ * Cascade agents (security-scanner / bug-hunter) must apply fixes and then
5
+ * run the project's real verification commands (typecheck / lint / tests),
6
+ * returning a structured JSON evidence block:
7
+ *
8
+ * ```json
9
+ * {
10
+ * "verification_evidence": {
11
+ * "typecheck": { "command": "pnpm typecheck", "exitCode": 0 },
12
+ * "lint": { "command": "pnpm lint", "exitCode": 0 },
13
+ * "tests": { "command": "pnpm test --filter affected", "exitCode": 0 }
14
+ * }
15
+ * }
16
+ * ```
17
+ *
18
+ * The cascade handler extracts this block and, before the re-review step,
19
+ * re-runs the claimed commands against the working tree with a safe runner
20
+ * (plain spawn, no shell, allowlisted executables) and compares the observed
21
+ * exit codes. A fix only counts as verified when every claimed check ran and
22
+ * its exit code matches reality.
23
+ *
24
+ * @module chimera-cascade-evidence
25
+ */
26
+ import type { CascadeEvidenceCheckResult, CascadeEvidenceStatus } from '@wrongstack/core/plugin';
27
+ export type { CascadeEvidenceCheckResult, CascadeEvidenceStatus, } from '@wrongstack/core/plugin';
28
+ /** One claimed verification check: the exact command and its observed exit code. */
29
+ export interface CascadeEvidenceCheck {
30
+ command: string;
31
+ exitCode: number;
32
+ /** Optional truncated output the agent chose to include (informational). */
33
+ output?: string | undefined;
34
+ }
35
+ /** The three standard verification checks a cascade agent can claim. */
36
+ export interface CascadeEvidence {
37
+ typecheck?: CascadeEvidenceCheck | undefined;
38
+ lint?: CascadeEvidenceCheck | undefined;
39
+ tests?: CascadeEvidenceCheck | undefined;
40
+ }
41
+ /** Result of verifying a cascade agent's claimed evidence. */
42
+ export interface CascadeEvidenceVerification {
43
+ status: CascadeEvidenceStatus;
44
+ checks: CascadeEvidenceCheckResult[];
45
+ }
46
+ /**
47
+ * Extract a `verification_evidence` block from a cascade agent's response.
48
+ *
49
+ * Scans every fenced ```json block in the text and returns the first that
50
+ * parses as an object with a `verification_evidence` member whose value is an
51
+ * object of `{command, exitCode}` checks. Any malformed JSON fence is ignored
52
+ * (the agent may have used a fence for something else); when the evidence is
53
+ * genuinely absent, returns null so the caller can treat it as `missing`.
54
+ *
55
+ * The returned evidence is validated but NOT sanitized here — command safety
56
+ * is enforced by the verification runner (allowlist + no shell).
57
+ */
58
+ export declare function extractCascadeEvidence(text: string | null | undefined): CascadeEvidence | null;
59
+ /**
60
+ * Maximum wall-clock per verification command. Verification must never turn
61
+ * the cascade re-review into an unbounded wait, so a hung command is killed
62
+ * and reported as a mismatch (like a non-zero exit).
63
+ */
64
+ export declare const CASCADE_EVIDENCE_COMMAND_TIMEOUT_MS = 120000;
65
+ /** Exit code used when the re-run is refused (unsafe command). */
66
+ export declare const CASCADE_EVIDENCE_UNSAFE_EXIT = 126;
67
+ /** Exit code used when the re-run times out or cannot execute. */
68
+ export declare const CASCADE_EVIDENCE_RUN_ERROR_EXIT = 127;
69
+ export interface RunCommandResult {
70
+ exitCode: number;
71
+ }
72
+ export type RunCommandFn = (command: string, cwd: string, timeoutMs: number) => Promise<RunCommandResult>;
73
+ /**
74
+ * Validate a claimed command against the safety contract: first token must be
75
+ * an allowlisted executable and every token must be a bare word/path (no shell
76
+ * metacharacters). Shell-free spawning is the second layer — even a validated
77
+ * command is run with `shell: false`.
78
+ */
79
+ export declare function isSafeCascadeCommand(command: string): boolean;
80
+ /**
81
+ * Run a single verification command against the working tree without a shell.
82
+ * Never throws — every failure mode (unsafe, timeout, spawn error) resolves to
83
+ * an exit code so the caller's comparison stays uniform.
84
+ */
85
+ export declare const runCascadeVerificationCommand: RunCommandFn;
86
+ /**
87
+ * Verify a cascade agent's claimed evidence by re-running each command against
88
+ * the working tree and comparing exit codes.
89
+ *
90
+ * Verdict rules:
91
+ * - `missing` — no evidence block was extracted (agent returned no checks).
92
+ * - `verified` — every claimed check re-ran, actual exit code matched the
93
+ * claim, and the claimed exit code was 0 (a passing check).
94
+ * - `failed` — any check was refused (unsafe), could not run, timed out,
95
+ * claimed a non-zero exit code, or actual ≠ claimed.
96
+ *
97
+ * An unsafe command is refused rather than executed — a cascade agent cannot
98
+ * make the orchestrator run arbitrary shell through a fabricated evidence
99
+ * block.
100
+ */
101
+ export declare function verifyCascadeEvidence(evidence: CascadeEvidence | null | undefined, cwd: string, runCommand?: RunCommandFn, timeoutMs?: number): Promise<CascadeEvidenceVerification>;
102
+ //# sourceMappingURL=chimera-cascade-evidence.d.ts.map
@@ -2,5 +2,12 @@ import type { CascadeAgentKind, ChimeraCascadeNeededPayload, ChimeraReviewNeeded
2
2
  export declare function truncateAtCodePointBoundary(text: string, maxCodeUnits: number): string;
3
3
  export declare function isChimeraAllClearReview(text: string): boolean;
4
4
  export declare function buildChimeraReviewTaskDescription(p: ChimeraReviewNeededPayload): string;
5
+ /**
6
+ * The machine-evidence contract every cascade fix agent must satisfy before
7
+ * its work counts as verified. The agent runs the real verification commands
8
+ * and returns a JSON block with the exact commands and their exit codes; the
9
+ * re-review step re-runs those commands and compares.
10
+ */
11
+ export declare const CASCADE_EVIDENCE_INSTRUCTIONS: string;
5
12
  export declare function buildChimeraCascadeTaskDescription(agentKind: CascadeAgentKind, p: ChimeraCascadeNeededPayload): string;
6
13
  //# sourceMappingURL=chimera-review-task.d.ts.map
@@ -62,6 +62,10 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
62
62
  "insecure-open",
63
63
  "strict-port",
64
64
  "client",
65
+ // `wstack doctor` booleans. Without these, `--daemons --clear-stale` parses
66
+ // as `daemons="--clear-stale"` and the second flag disappears.
67
+ "daemons",
68
+ "clear-stale",
65
69
  // `wstack update` booleans. Keeping these here prevents parseArgs from
66
70
  // consuming a following positional token as an accidental flag value.
67
71
  "check-only",
@@ -127,7 +131,7 @@ function parseArgs(argv) {
127
131
  }
128
132
  } else if (a.startsWith("-") && a.length === 2) {
129
133
  const short = a.slice(1);
130
- const expand = { v: "verbose", y: "yes" };
134
+ const expand = { v: "verbose", y: "yes", h: "help" };
131
135
  flags[expand[short] ?? short] = true;
132
136
  } else {
133
137
  positional.push(a);
@@ -151,7 +155,7 @@ function normalizeSurfaceAliases(flags, positional) {
151
155
  positional.splice(0, 1);
152
156
  return;
153
157
  }
154
- if (first === "hq" && (positional.length === 1 || positional[1] === "serve")) {
158
+ if (first === "hq" && (positional.length === 1 || positional[1] === "serve") && flags["help"] !== true && flags["version"] !== true) {
155
159
  flags["hq"] = true;
156
160
  positional.splice(0, positional[1] === "serve" ? 2 : 1);
157
161
  }
@@ -1052,4 +1056,4 @@ export {
1052
1056
  runLiveProviderPicker,
1053
1057
  runPicker
1054
1058
  };
1055
- //# sourceMappingURL=chunk-C5NXM2SI.js.map
1059
+ //# sourceMappingURL=chunk-5JW3XCRA.js.map
@@ -1,12 +1,12 @@
1
+ import {
2
+ LOCAL_LLM_PRESETS,
3
+ runLiveProviderPicker
4
+ } from "./chunk-5JW3XCRA.js";
1
5
  import {
2
6
  openBrowser,
3
7
  runCodexOAuthLogin,
4
8
  startLoopbackServer
5
9
  } from "./chunk-SSSJQVUY.js";
6
- import {
7
- LOCAL_LLM_PRESETS,
8
- runLiveProviderPicker
9
- } from "./chunk-C5NXM2SI.js";
10
10
  import {
11
11
  activeLabel,
12
12
  loadConfigProviders,
@@ -1374,4 +1374,4 @@ export {
1374
1374
  addCustomProvider,
1375
1375
  addKeyForProvider
1376
1376
  };
1377
- //# sourceMappingURL=chunk-FJVCVZIV.js.map
1377
+ //# sourceMappingURL=chunk-WC3DK6BI.js.map