@krtclcdy/tersio 2.25.5 → 2.25.7

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 (231) hide show
  1. package/README.md +42 -38
  2. package/dashboard/dist/index.html +15 -15
  3. package/dist/bench/code-out/pfull-0/debounce.js +13 -0
  4. package/dist/bench/code-out/pfull-0/deepClone.js +18 -0
  5. package/dist/bench/code-out/pfull-0/groupBy.js +15 -0
  6. package/dist/bench/code-out/pfull-1/debounce.js +13 -0
  7. package/dist/bench/code-out/pfull-1/deepClone.js +17 -0
  8. package/dist/bench/code-out/pfull-1/groupBy.js +14 -0
  9. package/dist/bench/code-out/pfull-2/debounce.js +13 -0
  10. package/dist/bench/code-out/pfull-2/deepClone.js +18 -0
  11. package/dist/bench/code-out/pfull-2/groupBy.js +15 -0
  12. package/dist/bench/code-out/pfull-3/debounce.js +12 -0
  13. package/dist/bench/code-out/pfull-3/deepClone.js +17 -0
  14. package/dist/bench/code-out/pfull-3/groupBy.js +7 -0
  15. package/dist/bench/code-out/pfull-4/debounce.js +10 -0
  16. package/dist/bench/code-out/pfull-4/deepClone.js +17 -0
  17. package/dist/bench/code-out/pfull-4/groupBy.js +14 -0
  18. package/dist/bench/code-out/plite-0/debounce.js +13 -0
  19. package/dist/bench/code-out/plite-0/deepClone.js +18 -0
  20. package/dist/bench/code-out/plite-0/groupBy.js +15 -0
  21. package/dist/bench/code-out/plite-1/debounce.js +13 -0
  22. package/dist/bench/code-out/plite-1/deepClone.js +17 -0
  23. package/dist/bench/code-out/plite-1/groupBy.js +11 -0
  24. package/dist/bench/code-out/plite-2/debounce.js +12 -0
  25. package/dist/bench/code-out/plite-2/deepClone.js +17 -0
  26. package/dist/bench/code-out/plite-2/groupBy.js +14 -0
  27. package/dist/bench/code-out/plite-3/debounce.js +12 -0
  28. package/dist/bench/code-out/plite-3/deepClone.js +17 -0
  29. package/dist/bench/code-out/plite-3/groupBy.js +14 -0
  30. package/dist/bench/code-out/plite-4/debounce.js +12 -0
  31. package/dist/bench/code-out/plite-4/deepClone.js +17 -0
  32. package/dist/bench/code-out/plite-4/groupBy.js +14 -0
  33. package/dist/bench/code-out/pultra-0/debounce.js +12 -0
  34. package/dist/bench/code-out/pultra-0/deepClone.js +20 -0
  35. package/dist/bench/code-out/pultra-0/groupBy.js +14 -0
  36. package/dist/bench/code-out/pultra-1/debounce.js +10 -0
  37. package/dist/bench/code-out/pultra-1/deepClone.js +17 -0
  38. package/dist/bench/code-out/pultra-1/groupBy.js +7 -0
  39. package/dist/bench/code-out/pultra-2/debounce.js +13 -0
  40. package/dist/bench/code-out/pultra-2/deepClone.js +18 -0
  41. package/dist/bench/code-out/pultra-2/groupBy.js +15 -0
  42. package/dist/bench/code-out/pultra-3/debounce.js +13 -0
  43. package/dist/bench/code-out/pultra-3/deepClone.js +18 -0
  44. package/dist/bench/code-out/pultra-3/groupBy.js +15 -0
  45. package/dist/bench/code-out/pultra-4/debounce.js +13 -0
  46. package/dist/bench/code-out/pultra-4/deepClone.js +18 -0
  47. package/dist/bench/code-out/pultra-4/groupBy.js +15 -0
  48. package/dist/bench/code-out/run-0/debounce.js +12 -0
  49. package/dist/bench/code-out/run-0/deepClone.js +17 -0
  50. package/dist/bench/code-out/run-0/groupBy.js +14 -0
  51. package/dist/bench/code-out/run-1/debounce.js +13 -0
  52. package/dist/bench/code-out/run-1/deepClone.js +15 -0
  53. package/dist/bench/code-out/run-1/groupBy.js +13 -0
  54. package/dist/bench/code-out/run-2/debounce.js +12 -0
  55. package/dist/bench/code-out/run-2/deepClone.js +18 -0
  56. package/dist/bench/code-out/run-2/groupBy.js +13 -0
  57. package/dist/bench/code-out/run-3/debounce.js +12 -0
  58. package/dist/bench/code-out/run-3/deepClone.js +18 -0
  59. package/dist/bench/code-out/run-3/groupBy.js +14 -0
  60. package/dist/bench/code-out/run-4/debounce.js +12 -0
  61. package/dist/bench/code-out/run-4/deepClone.js +16 -0
  62. package/dist/bench/code-out/run-4/groupBy.js +13 -0
  63. package/dist/cli/backups.js +7 -14
  64. package/dist/cli/banner.js +3 -3
  65. package/dist/cli/common.js +29 -13
  66. package/dist/cli/currency.js +1 -1
  67. package/dist/cli/dashboard.js +66 -35
  68. package/dist/cli/doctor-fix.js +204 -19
  69. package/dist/cli/doctor.js +263 -40
  70. package/dist/cli/hosts.js +25 -6
  71. package/dist/cli/install.js +150 -49
  72. package/dist/cli/interactive.js +3 -3
  73. package/dist/cli/manifest.js +109 -9
  74. package/dist/cli/profile.js +13 -8
  75. package/dist/cli/rtk-wiring.js +1 -1
  76. package/dist/cli/settings.js +20 -5
  77. package/dist/cli/uninstall.js +4 -4
  78. package/dist/cli/update.js +6 -7
  79. package/dist/cli/usage.js +6 -9
  80. package/dist/extensions/caveman/index.js +10 -4
  81. package/dist/extensions/caveman/rule-lite.md +77 -0
  82. package/dist/extensions/caveman/rule-megacave-lite.md +59 -0
  83. package/dist/extensions/caveman/rule-megacave-ultra.md +59 -0
  84. package/dist/extensions/caveman/rule-megacave.md +5 -3
  85. package/dist/extensions/caveman/rule-ultra.md +4 -4
  86. package/dist/extensions/caveman/rule.md +55 -45
  87. package/dist/extensions/combo/index.js +85 -25
  88. package/dist/extensions/commands/index.js +1 -1
  89. package/dist/extensions/pstack/index.js +121 -0
  90. package/dist/extensions/pstack/rule.md +141 -0
  91. package/dist/extensions/rtk/filter.js +0 -1
  92. package/dist/extensions/rtk/session.js +20 -12
  93. package/dist/extensions/server.js +90 -6
  94. package/dist/extensions/shared/agent-stats.js +214 -278
  95. package/dist/extensions/shared/brand-marks.js +33 -34
  96. package/dist/extensions/shared/carbon.js +1 -1
  97. package/dist/extensions/shared/plugin-settings.js +11 -6
  98. package/dist/extensions/shared/pricing.js +8 -16
  99. package/dist/extensions/shared/rtk-eligible.js +84 -0
  100. package/dist/extensions/shared/rtk-gain.js +5 -10
  101. package/dist/extensions/shared/session-state.js +15 -10
  102. package/dist/extensions/shared/sqlite.js +162 -0
  103. package/dist/extensions/shared/status.js +8 -9
  104. package/dist/extensions/shared/types.js +1 -1
  105. package/dist/extensions/shared/usage-ledger.js +155 -61
  106. package/dist/extensions/shared/usage-store.js +111 -52
  107. package/dist/extensions/shared/utils.js +5 -5
  108. package/dist/extensions/timing/index.js +31 -0
  109. package/dist/extensions/updater/index.js +3 -6
  110. package/dist/tersio.js +8 -3
  111. package/extensions/caveman/index.ts +10 -4
  112. package/extensions/caveman/rule-lite.md +77 -0
  113. package/extensions/caveman/rule-megacave-lite.md +59 -0
  114. package/extensions/caveman/rule-megacave-ultra.md +59 -0
  115. package/extensions/caveman/rule-megacave.md +5 -3
  116. package/extensions/caveman/rule-ultra.md +4 -4
  117. package/extensions/caveman/rule.md +55 -45
  118. package/extensions/combo/index.ts +86 -23
  119. package/extensions/commands/index.ts +1 -1
  120. package/extensions/pstack/index.ts +130 -0
  121. package/extensions/pstack/playbooks/authoring-a-skill.md +12 -0
  122. package/extensions/pstack/playbooks/autonomous-run.md +13 -0
  123. package/extensions/pstack/playbooks/autopilot-full.md +13 -0
  124. package/extensions/pstack/playbooks/autopilot-stack.md +16 -0
  125. package/extensions/pstack/playbooks/babysit.md +23 -0
  126. package/extensions/pstack/playbooks/bug-fix.md +15 -0
  127. package/extensions/pstack/playbooks/eval.md +25 -0
  128. package/extensions/pstack/playbooks/feature.md +21 -0
  129. package/extensions/pstack/playbooks/hillclimb.md +21 -0
  130. package/extensions/pstack/playbooks/investigation.md +14 -0
  131. package/extensions/pstack/playbooks/multi-phase-plan.md +155 -0
  132. package/extensions/pstack/playbooks/opening-a-pr.md +34 -0
  133. package/extensions/pstack/playbooks/orchestrate.md +111 -0
  134. package/extensions/pstack/playbooks/pause-safely.md +10 -0
  135. package/extensions/pstack/playbooks/perf-issue.md +25 -0
  136. package/extensions/pstack/playbooks/prototype.md +14 -0
  137. package/extensions/pstack/playbooks/refactoring.md +16 -0
  138. package/extensions/pstack/playbooks/runtime-forensics.md +11 -0
  139. package/extensions/pstack/playbooks/session-pickup.md +11 -0
  140. package/extensions/pstack/playbooks/shipping.md +17 -0
  141. package/extensions/pstack/playbooks/trace-forensics.md +14 -0
  142. package/extensions/pstack/playbooks/visual-parity.md +11 -0
  143. package/extensions/pstack/playbooks/worktree-cleanup.md +12 -0
  144. package/extensions/pstack/principles/attack-the-premise.md +22 -0
  145. package/extensions/pstack/principles/boundary-discipline.md +33 -0
  146. package/extensions/pstack/principles/build-the-lever.md +22 -0
  147. package/extensions/pstack/principles/encode-lessons-in-structure.md +10 -0
  148. package/extensions/pstack/principles/exhaust-the-design-space.md +8 -0
  149. package/extensions/pstack/principles/experience-first.md +18 -0
  150. package/extensions/pstack/principles/explain-the-number.md +16 -0
  151. package/extensions/pstack/principles/fix-root-causes.md +23 -0
  152. package/extensions/pstack/principles/foundational-thinking.md +20 -0
  153. package/extensions/pstack/principles/guard-the-context-window.md +11 -0
  154. package/extensions/pstack/principles/laziness-protocol.md +9 -0
  155. package/extensions/pstack/principles/make-operations-idempotent.md +23 -0
  156. package/extensions/pstack/principles/migrate-callers-then-delete-legacy-apis.md +21 -0
  157. package/extensions/pstack/principles/minimize-reader-load.md +17 -0
  158. package/extensions/pstack/principles/model-the-domain.md +23 -0
  159. package/extensions/pstack/principles/never-block-on-the-human.md +14 -0
  160. package/extensions/pstack/principles/outcome-oriented-execution.md +20 -0
  161. package/extensions/pstack/principles/prove-it-works.md +21 -0
  162. package/extensions/pstack/principles/redesign-from-first-principles.md +15 -0
  163. package/extensions/pstack/principles/separate-before-serializing-shared-state.md +15 -0
  164. package/extensions/pstack/principles/sequence-verifiable-units.md +16 -0
  165. package/extensions/pstack/principles/subtract-before-you-add.md +17 -0
  166. package/extensions/pstack/principles/test-behavior-not-implementation.md +20 -0
  167. package/extensions/pstack/principles/type-system-discipline.md +17 -0
  168. package/extensions/pstack/references/bugbot-triage.md +142 -0
  169. package/extensions/pstack/rule.md +141 -0
  170. package/extensions/pstack/skills/architect/SKILL.md +78 -0
  171. package/extensions/pstack/skills/architect/references/design-red-flags.md +57 -0
  172. package/extensions/pstack/skills/architect/references/rationale-template.md +35 -0
  173. package/extensions/pstack/skills/architect/references/runner-prompt.md +20 -0
  174. package/extensions/pstack/skills/arena/SKILL.md +47 -0
  175. package/extensions/pstack/skills/benchmark-checklist/SKILL.md +38 -0
  176. package/extensions/pstack/skills/blast-radius/SKILL.md +49 -0
  177. package/extensions/pstack/skills/bro/SKILL.md +6 -0
  178. package/extensions/pstack/skills/correct/SKILL.md +31 -0
  179. package/extensions/pstack/skills/figure-it-out/SKILL.md +51 -0
  180. package/extensions/pstack/skills/how/SKILL.md +35 -0
  181. package/extensions/pstack/skills/how/references/explainer-prompt.md +55 -0
  182. package/extensions/pstack/skills/how/references/explorer-prompt.md +35 -0
  183. package/extensions/pstack/skills/interrogate/SKILL.md +73 -0
  184. package/extensions/pstack/skills/interrogate/references/code-quality-review.md +47 -0
  185. package/extensions/pstack/skills/interrogate/references/lead-judgment.md +58 -0
  186. package/extensions/pstack/skills/interrogate/references/reviewer-prompt.md +62 -0
  187. package/extensions/pstack/skills/interrogate/references/rubric.md +77 -0
  188. package/extensions/pstack/skills/no-comments/SKILL.md +23 -0
  189. package/extensions/pstack/skills/reflect/SKILL.md +66 -0
  190. package/extensions/pstack/skills/reflect/references/divergent-reviewer.md +38 -0
  191. package/extensions/pstack/skills/reflect/references/judgment-reviewer.md +37 -0
  192. package/extensions/pstack/skills/reflect/references/synthesizer.md +56 -0
  193. package/extensions/pstack/skills/reflect/references/tooling-reviewer.md +48 -0
  194. package/extensions/pstack/skills/show-me-your-work/SKILL.md +81 -0
  195. package/extensions/pstack/skills/swarm/SKILL.md +45 -0
  196. package/extensions/pstack/skills/tdd/SKILL.md +41 -0
  197. package/extensions/pstack/skills/technical-writing/SKILL.md +105 -0
  198. package/extensions/pstack/skills/unslop/SKILL.md +61 -0
  199. package/extensions/pstack/skills/why/SKILL.md +97 -0
  200. package/extensions/pstack/skills/why/references/epistemics.md +71 -0
  201. package/extensions/pstack/skills/why/references/investigator-prompt.md +84 -0
  202. package/extensions/pstack/skills/why/references/source-playbook.md +17 -0
  203. package/extensions/pstack/skills/why/references/sources/code-archaeology.md +51 -0
  204. package/extensions/pstack/skills/why/references/sources/databricks.md +70 -0
  205. package/extensions/pstack/skills/why/references/sources/datadog.md +97 -0
  206. package/extensions/pstack/skills/why/references/sources/incident-postmortem.md +5 -0
  207. package/extensions/pstack/skills/why/references/sources/linear.md +48 -0
  208. package/extensions/pstack/skills/why/references/sources/notion.md +55 -0
  209. package/extensions/pstack/skills/why/references/sources/sentry.md +66 -0
  210. package/extensions/pstack/skills/why/references/sources/slack.md +17 -0
  211. package/extensions/pstack/skills/why/references/synthesizer-prompt.md +129 -0
  212. package/extensions/rtk/filter.ts +0 -1
  213. package/extensions/rtk/session.ts +19 -11
  214. package/extensions/server.ts +72 -7
  215. package/extensions/shared/agent-stats.ts +226 -278
  216. package/extensions/shared/brand-marks.ts +34 -34
  217. package/extensions/shared/carbon.ts +1 -1
  218. package/extensions/shared/plugin-settings.ts +12 -6
  219. package/extensions/shared/pricing.ts +10 -26
  220. package/extensions/shared/rtk-eligible.ts +83 -0
  221. package/extensions/shared/rtk-gain.ts +5 -10
  222. package/extensions/shared/session-state.ts +18 -13
  223. package/extensions/shared/sqlite.ts +162 -0
  224. package/extensions/shared/status.ts +9 -9
  225. package/extensions/shared/types.ts +2 -1
  226. package/extensions/shared/usage-ledger.ts +157 -66
  227. package/extensions/shared/usage-store.ts +120 -52
  228. package/extensions/shared/utils.ts +5 -5
  229. package/extensions/timing/index.ts +44 -0
  230. package/extensions/updater/index.ts +3 -8
  231. package/package.json +21 -9
@@ -0,0 +1,162 @@
1
+ // One place that runs SQL against a database file. Bun and Node 22.5+ carry SQLite in process, so the
2
+ // `sqlite3` binary is a fallback: hosts without it kept silently reading nothing. Reads return the sqlite3
3
+ // CLI shape (one row per line, cells tab-joined, NULL an empty cell) because every caller parses that.
4
+ // The write path leaves the journal mode alone: the schema sets WAL so a reader never blocks a writer,
5
+ // and the pragma needs exclusive access, so with a reader holding the file it fails after the caller's
6
+ // rows have already committed (see docs/ERRORS.md).
7
+ import { execFileSync } from 'node:child_process';
8
+ import fs from 'node:fs';
9
+ import { createRequire } from 'node:module';
10
+ import os from 'node:os';
11
+ import path from 'node:path';
12
+
13
+ /** A read this large used to vanish behind a 1MB cap, which cleared on one busy session_message table. */
14
+ const SQLITE_READ_BUFFER = 50 * 1024 * 1024;
15
+
16
+ const TIMEOUT = 30_000;
17
+ const requireOptional = createRequire(import.meta.url);
18
+
19
+ interface Engine {
20
+ kind: 'bun' | 'node';
21
+ read(db: string, sql: string): unknown[][];
22
+ exec(db: string, sql: string): void;
23
+ }
24
+
25
+ interface BunDatabase {
26
+ query(sql: string): { values(): unknown[][] };
27
+ exec(sql: string): void;
28
+ close(): void;
29
+ }
30
+
31
+ interface NodeStatement {
32
+ all(): unknown[][];
33
+ setReturnArrays?(on: boolean): unknown;
34
+ }
35
+
36
+ interface NodeDatabase {
37
+ prepare(sql: string): NodeStatement;
38
+ exec(sql: string): void;
39
+ close(): void;
40
+ }
41
+
42
+ let cached: Engine | null | undefined;
43
+ let cachedFor: string | undefined;
44
+
45
+ /** Bun's SQLite, or Node's when this build carries one. Null when neither runtime has an engine. */
46
+ function engine(): Engine | null {
47
+ const asked = process.env.TERSIO_SQLITE ?? '';
48
+ if (cached !== undefined && cachedFor === asked) return cached;
49
+ cachedFor = asked;
50
+ cached = null;
51
+ if (asked === 'cli') return cached;
52
+ try {
53
+ const { Database } = requireOptional('bun:sqlite') as { Database: new (db: string, opts?: { readonly?: boolean }) => BunDatabase };
54
+ cached = {
55
+ kind: 'bun',
56
+ read: (db, sql) => { const d = new Database(db, { readonly: true }); try { return d.query(sql).values(); } finally { d.close(); } },
57
+ exec: (db, sql) => { const d = new Database(db); try { d.exec(sql); } finally { d.close(); } },
58
+ };
59
+ return cached;
60
+ } catch { /* not Bun, or no SQLite in this build */ }
61
+ try {
62
+ const { DatabaseSync } = requireOptional('node:sqlite') as { DatabaseSync: new (db: string, opts?: { readOnly?: boolean }) => NodeDatabase };
63
+ // Rows arrive keyed by column name unless the statement returns arrays: a duplicate name collapses its key and shifts every later cell, so a Node that cannot return arrays is no engine.
64
+ const probe = new DatabaseSync(':memory:');
65
+ try {
66
+ if (typeof probe.prepare('SELECT 1').setReturnArrays !== 'function') throw new Error('no positional rows');
67
+ } finally {
68
+ probe.close();
69
+ }
70
+ cached = {
71
+ kind: 'node',
72
+ read: (db, sql) => {
73
+ const d = new DatabaseSync(db, { readOnly: true });
74
+ try {
75
+ const st = d.prepare(sql);
76
+ st.setReturnArrays?.(true);
77
+ return st.all();
78
+ } finally { d.close(); }
79
+ },
80
+ exec: (db, sql) => { const d = new DatabaseSync(db); try { d.exec(sql); } finally { d.close(); } },
81
+ };
82
+ return cached;
83
+ } catch { /* Node older than 22.5, or one built without SQLite or positional rows */ }
84
+ return cached;
85
+ }
86
+
87
+ /** The `sqlite3` binary, resolved once from PATH so a missing one costs one scan, not one spawn per call. */
88
+ let binary: string | null | undefined;
89
+ function findBinary(): string | null {
90
+ if (binary !== undefined) return binary;
91
+ binary = null;
92
+ for (const dir of (process.env.PATH ?? '').split(path.delimiter)) {
93
+ if (!dir) continue;
94
+ for (const name of process.platform === 'win32' ? ['sqlite3.exe', 'sqlite3'] : ['sqlite3']) {
95
+ const at = path.join(dir, name);
96
+ try {
97
+ fs.accessSync(at, fs.constants.X_OK);
98
+ binary = at;
99
+ return binary;
100
+ } catch { /* not here */ }
101
+ }
102
+ }
103
+ return binary;
104
+ }
105
+
106
+ /** `bun`, `node`, `cli`, or null when this host has no way to read SQLite at all. */
107
+ export function sqliteEngine(): 'bun' | 'node' | 'cli' | null {
108
+ return engine()?.kind ?? (findBinary() === null ? null : 'cli');
109
+ }
110
+
111
+ /** Whether any path can run SQL: what the tests guard their skips on. */
112
+ export function sqliteAvailable(): boolean {
113
+ return sqliteEngine() !== null;
114
+ }
115
+
116
+ /** Rows as the CLI prints them, so a caller cannot tell which engine answered. */
117
+ function toTsv(rows: unknown[][]): string {
118
+ if (rows.length === 0) return '';
119
+ return `${rows.map((row) => row.map((cell) => (cell === null || cell === undefined ? '' : String(cell))).join('\t')).join('\n')}\n`;
120
+ }
121
+
122
+ function cliRead(db: string, sql: string): string {
123
+ const opts = { encoding: 'utf8' as const, timeout: TIMEOUT, stdio: ['pipe', 'pipe', 'ignore'] as ['pipe', 'pipe', 'ignore'], maxBuffer: SQLITE_READ_BUFFER, input: sql };
124
+ try {
125
+ return execFileSync('sqlite3', ['-separator', '\t', '-readonly', db], opts);
126
+ } catch { /* the side files may be gone; try the next rung */ }
127
+ if (!fs.existsSync(`${db}-wal`)) {
128
+ try {
129
+ return execFileSync('sqlite3', ['-separator', '\t', `file:${db}?immutable=1`], opts);
130
+ } catch { /* fall through to the copy */ }
131
+ }
132
+ // A live database cannot be read without its write-ahead log, so copy the set and read the copy.
133
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'tersio-sqlite-'));
134
+ const target = path.join(dir, path.basename(db));
135
+ try {
136
+ fs.copyFileSync(db, target);
137
+ for (const suffix of ['-wal', '-shm']) {
138
+ if (fs.existsSync(`${db}${suffix}`)) fs.copyFileSync(`${db}${suffix}`, `${target}${suffix}`);
139
+ }
140
+ return execFileSync('sqlite3', ['-separator', '\t', '-readonly', target], opts);
141
+ } finally {
142
+ fs.rmSync(dir, { recursive: true, force: true });
143
+ }
144
+ }
145
+
146
+ /** Run a read and return the tab-separated rows. Throws when the file is missing, as the callers expect. */
147
+ export function sqliteRead(db: string, sql: string): string {
148
+ if (!fs.existsSync(db)) throw new Error(`missing database: ${db}`);
149
+ const e = engine();
150
+ if (e !== null) return toTsv(e.read(db, sql));
151
+ return cliRead(db, sql);
152
+ }
153
+
154
+ /** Run a script, creating the file if needed. Stops at the first error, so a failed INSERT leaves no DELETEs. */
155
+ export function sqliteExec(db: string, sql: string): void {
156
+ const e = engine();
157
+ if (e !== null) {
158
+ e.exec(db, sql);
159
+ return;
160
+ }
161
+ execFileSync('sqlite3', ['-bail', db, sql], { stdio: ['ignore', 'ignore', 'ignore'], timeout: TIMEOUT });
162
+ }
@@ -1,4 +1,4 @@
1
- // One status string every surface renders: host footer bar, CLI menu title, dashboard banner. Nothing else formats mode state.
1
+ // One status string every surface renders: host footer, CLI title, dashboard banner. Nothing else formats mode state.
2
2
  import { getSharedComboState } from './session-state.ts';
3
3
  import type { ExtensionCtx, UiApi } from './types.ts';
4
4
 
@@ -7,19 +7,20 @@ export interface StatusState {
7
7
  caveman: string;
8
8
  rtk: string;
9
9
  ponytail: string;
10
+ pstack: string;
10
11
  }
11
12
 
12
- /** OFF collapses the three modes into one word; a preset or a mix names itself. */
13
+ /** OFF collapses every mode into one word; a preset or a mix names itself. */
13
14
  export function formatStatus(state: StatusState): string {
14
- const { level, caveman, rtk, ponytail } = state;
15
- if (level === 'off' && caveman === 'off' && rtk === 'off' && ponytail === 'off') return '🧩 combo OFF';
15
+ const { level, caveman, rtk, ponytail, pstack } = state;
16
+ if (level === 'off' && caveman === 'off' && rtk === 'off' && ponytail === 'off' && pstack === 'off') return '🧩 combo OFF';
16
17
  const label = level === 'custom' ? 'CUSTOM' : level.toUpperCase();
17
- return `🧩 combo ${label}: 🪨caveman=${caveman.toUpperCase()} ⚡rtk=${rtk.toUpperCase()} 🦥ponytail=${ponytail.toUpperCase()}`;
18
+ return `🧩 combo ${label}: 🪨caveman=${caveman.toUpperCase()} ⚡rtk=${rtk.toUpperCase()} 🦥ponytail=${ponytail.toUpperCase()} 🥔pstack=${pstack.toUpperCase()}`;
18
19
  }
19
20
 
20
- // The string upstream's skill tells the model to relay for `/caveman status`.
21
+ // The string upstream's skill tells the model to relay for `/caveman status`; `off` is a mode, so it reads as one.
21
22
  export function formatCavemanStatus(mode: string): string {
22
- return `Caveman mode: ${mode && mode !== 'off' ? mode : 'unknown'}`;
23
+ return `Caveman mode: ${mode || 'unknown'}`;
23
24
  }
24
25
 
25
26
  // Stale ctx access throws, so never let it abort a mode restore.
@@ -37,7 +38,7 @@ let remembered: ExtensionCtx | undefined;
37
38
  let lastText: string | undefined;
38
39
  let lastUi: UiApi | undefined;
39
40
 
40
- // Paint the host footer bar; same text on the same UI is skipped, a new ctx repaints it.
41
+ // Paint the host footer bar; same text on the same UI is skipped. One key only: the combo line already names the caveman mode, and a second key renders as a second footer line.
41
42
  export function announceStatus(ctx: ExtensionCtx | undefined): void {
42
43
  const state = getSharedComboState();
43
44
  const text = formatStatus(state);
@@ -48,5 +49,4 @@ export function announceStatus(ctx: ExtensionCtx | undefined): void {
48
49
  lastText = text;
49
50
  lastUi = ui;
50
51
  ui.setStatus?.('tersio', text);
51
- ui.setStatus?.('caveman', formatCavemanStatus(state.caveman));
52
52
  }
@@ -1,10 +1,11 @@
1
- // Shared OMP extension host types. Structural — the real `pi` object comes from the OMP runtime; these describe the surface this package touches.
1
+ // Shared OMP extension host types. Structural — the real `pi` object comes from the OMP runtime.
2
2
 
3
3
  export interface ComboState {
4
4
  level: string;
5
5
  caveman: string;
6
6
  rtk: string;
7
7
  ponytail: string;
8
+ pstack: string;
8
9
  }
9
10
 
10
11
  export type ComboLevel = 'off' | 'medium' | 'balanced' | 'max' | 'custom';