@manishbht/helpcode 0.2.3 → 0.3.5

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 (55) hide show
  1. package/dist/bin/helpcode.js +0 -0
  2. package/dist/src/commands/ask.js +15 -1
  3. package/dist/src/commands/ask.js.map +1 -1
  4. package/dist/src/commands/cockpit.d.ts +13 -0
  5. package/dist/src/commands/cockpit.js +33 -0
  6. package/dist/src/commands/cockpit.js.map +1 -0
  7. package/dist/src/commands/plan.d.ts +14 -0
  8. package/dist/src/commands/plan.js +113 -0
  9. package/dist/src/commands/plan.js.map +1 -0
  10. package/dist/src/commands/run.js +51 -4
  11. package/dist/src/commands/run.js.map +1 -1
  12. package/dist/src/core/cockpit.d.ts +27 -0
  13. package/dist/src/core/cockpit.js +109 -0
  14. package/dist/src/core/cockpit.js.map +1 -0
  15. package/dist/src/core/cockpitHtml.d.ts +52 -0
  16. package/dist/src/core/cockpitHtml.js +395 -0
  17. package/dist/src/core/cockpitHtml.js.map +1 -0
  18. package/dist/src/core/consent.d.ts +20 -0
  19. package/dist/src/core/consent.js +34 -0
  20. package/dist/src/core/consent.js.map +1 -0
  21. package/dist/src/core/decompose.d.ts +45 -0
  22. package/dist/src/core/decompose.js +86 -0
  23. package/dist/src/core/decompose.js.map +1 -0
  24. package/dist/src/core/gemini.d.ts +41 -0
  25. package/dist/src/core/gemini.js +91 -0
  26. package/dist/src/core/gemini.js.map +1 -0
  27. package/dist/src/core/keys.d.ts +29 -0
  28. package/dist/src/core/keys.js +92 -0
  29. package/dist/src/core/keys.js.map +1 -0
  30. package/dist/src/core/llmSelector.d.ts +6 -0
  31. package/dist/src/core/llmSelector.js +22 -7
  32. package/dist/src/core/llmSelector.js.map +1 -1
  33. package/dist/src/core/providers.d.ts +60 -0
  34. package/dist/src/core/providers.js +117 -0
  35. package/dist/src/core/providers.js.map +1 -0
  36. package/dist/src/core/remoteRouter.d.ts +27 -0
  37. package/dist/src/core/remoteRouter.js +71 -0
  38. package/dist/src/core/remoteRouter.js.map +1 -0
  39. package/dist/src/core/selector.d.ts +9 -4
  40. package/dist/src/core/selector.js +85 -29
  41. package/dist/src/core/selector.js.map +1 -1
  42. package/dist/src/core/souschef.d.ts +63 -0
  43. package/dist/src/core/souschef.js +55 -0
  44. package/dist/src/core/souschef.js.map +1 -0
  45. package/dist/src/core/state.d.ts +13 -1
  46. package/dist/src/core/state.js +47 -5
  47. package/dist/src/core/state.js.map +1 -1
  48. package/dist/src/core/triage.d.ts +14 -1
  49. package/dist/src/core/triage.js +23 -5
  50. package/dist/src/core/triage.js.map +1 -1
  51. package/dist/src/index.d.ts +8 -7
  52. package/dist/src/index.js +21 -8
  53. package/dist/src/index.js.map +1 -1
  54. package/dist/src/types.d.ts +70 -1
  55. package/package.json +1 -1
@@ -0,0 +1,395 @@
1
+ /**
2
+ * Cockpit HTML view — `helpcode cockpit --html`.
3
+ *
4
+ * Reproduces the original orchestration-cockpit mockup's mission-control layout
5
+ * (1fr 2fr 1fr grid: flanking LLM panels, central orchestrator + metrics +
6
+ * pipeline, bottom row of activity/routing panels), filled with REAL data from
7
+ * the sous-chef event log where we have it.
8
+ *
9
+ * Honesty principle: workers that actually ran (local qwen, remote Gemini) are
10
+ * shown live. Panels for providers/features we haven't built (DeepSeek, Kimi,
11
+ * Claude-as-orchestrated, the autonomous pipeline, complexity-based routing)
12
+ * are rendered faithfully to the mockup but marked "demo" — a vision view with
13
+ * real data layered in as we build, not theatre passed off as live.
14
+ *
15
+ * View-model shaping is separated from HTML string-building for unit testing.
16
+ */
17
+ import { summariseWorkers, sessionMetrics } from './cockpit.js';
18
+ const RECENT_CAP = 12;
19
+ function tierLabel(kind) {
20
+ switch (kind) {
21
+ case 'local': return 'local';
22
+ case 'remote': return 'free-tier';
23
+ case 'principal': return 'paid';
24
+ case 'deterministic': return 'code';
25
+ }
26
+ }
27
+ function comingCatalogue() {
28
+ return [
29
+ { name: 'Complexity scorer', note: 'route by task difficulty (mockup: 0.87 vs 0.75 threshold)' },
30
+ { name: 'Cross-model review', note: 'one model reviews another before it reaches you' },
31
+ { name: 'More providers', note: 'DeepSeek, Groq, Kimi — each just another SousChef' },
32
+ { name: 'Live quota bars', note: 'real per-provider free-tier usage tracking' },
33
+ ];
34
+ }
35
+ export function buildCockpitViewModel(log) {
36
+ const workers = summariseWorkers(log).map(w => ({
37
+ model: w.model ?? tierLabel(w.kind),
38
+ kind: w.kind,
39
+ tierLabel: tierLabel(w.kind),
40
+ events: w.eventsThisSession,
41
+ lastSummary: w.lastSummary,
42
+ quotaUsed: w.quotaUsed,
43
+ }));
44
+ const recent = [...log]
45
+ .sort((a, b) => a.at.localeCompare(b.at))
46
+ .slice(-RECENT_CAP)
47
+ .map(e => ({
48
+ time: e.at.slice(11, 16),
49
+ who: e.model ?? tierLabel(e.worker),
50
+ summary: e.summary,
51
+ outcome: e.outcome,
52
+ }));
53
+ // CLI shell lines: render the real event stream as helpcode terminal output.
54
+ const cli = buildCliLines(log);
55
+ return {
56
+ workers,
57
+ metrics: sessionMetrics(log),
58
+ recent,
59
+ cli,
60
+ coming: comingCatalogue(),
61
+ generatedAt: new Date().toISOString(),
62
+ };
63
+ }
64
+ /** Map the verb for a task, for terminal-style output. */
65
+ function taskVerb(task) {
66
+ switch (task) {
67
+ case 'file_selection': return 'select';
68
+ case 'output_triage': return 'triage';
69
+ case 'decomposition': return 'plan';
70
+ default: return 'prep';
71
+ }
72
+ }
73
+ /** Turn the real event log into helpcode-shell-style terminal lines. */
74
+ export function buildCliLines(log) {
75
+ if (log.length === 0) {
76
+ return [
77
+ { kind: 'prefix', text: '$ helpcode ask "..."' },
78
+ { kind: 'dim', text: ' no sous-chef activity yet this session' },
79
+ { kind: 'dim', text: ' run helpcode ask / plan to put the kitchen to work' },
80
+ ];
81
+ }
82
+ const recent = [...log].sort((a, b) => a.at.localeCompare(b.at)).slice(-8);
83
+ const lines = [];
84
+ for (const e of recent) {
85
+ const who = e.model ?? tierLabel(e.worker);
86
+ const verb = taskVerb(e.task);
87
+ if (e.outcome === 'ok') {
88
+ lines.push({ kind: 'ok', text: ` \u2714 ${verb} \u2192 ${who} (${tierLabel(e.worker)}) \u00b7 ${e.summary}` });
89
+ }
90
+ else if (e.outcome === 'escalated') {
91
+ lines.push({ kind: 'warn', text: ` \u2191 ${verb} escalated \u2192 principal \u00b7 ${e.summary}` });
92
+ }
93
+ else {
94
+ lines.push({ kind: 'dim', text: ` \u00b7 ${verb} fell back \u2192 ${who} \u00b7 ${e.summary}` });
95
+ }
96
+ }
97
+ return lines;
98
+ }
99
+ // --- HTML rendering -------------------------------------------------------
100
+ function esc(s) {
101
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
102
+ }
103
+ function badgeFor(kind) {
104
+ if (kind === 'remote')
105
+ return { cls: 'badge-free', label: 'free' };
106
+ if (kind === 'principal')
107
+ return { cls: 'badge-paid', label: 'paid' };
108
+ if (kind === 'deterministic')
109
+ return { cls: 'badge-demo', label: 'code' };
110
+ return { cls: 'badge-active', label: 'local' };
111
+ }
112
+ /** A live worker panel (real data), color-coded by worker kind. */
113
+ function livePanel(p) {
114
+ const b = badgeFor(p.kind);
115
+ // Panel border/glow encodes worker kind: local=cyan(working), remote=green, code=amber.
116
+ const stateClass = p.kind === 'local' ? 'working'
117
+ : p.kind === 'remote' ? 'remote-active'
118
+ : p.kind === 'deterministic' ? 'fallback-active'
119
+ : 'active';
120
+ // Task chip colour by the kind of work last done (inferred from summary).
121
+ const chipClass = /triag/i.test(p.lastSummary ?? '') ? 'reviewing'
122
+ : /step|breakdown|plan/i.test(p.lastSummary ?? '') ? 'plan'
123
+ : 'writing';
124
+ // Token/usage bar colour by level (matches the original's green/amber/red).
125
+ let fillClass = 'green';
126
+ let pct;
127
+ if (p.quotaUsed !== null) {
128
+ pct = Math.round(p.quotaUsed * 100);
129
+ fillClass = pct > 80 ? 'red' : pct > 50 ? 'amber' : 'green';
130
+ }
131
+ else {
132
+ pct = Math.min(100, p.events * 20);
133
+ }
134
+ const barLabel = p.quotaUsed !== null
135
+ ? `<span>quota</span><span>${pct}%</span>`
136
+ : `<span>tasks</span><span>${p.events}</span>`;
137
+ return `
138
+ <div class="llm-panel ${stateClass}">
139
+ <div class="panel-header">
140
+ <span class="panel-name">${esc(p.model)}</span>
141
+ <span class="panel-tier badge ${b.cls}">${b.label}</span>
142
+ </div>
143
+ <div class="token-bar-wrap"><div class="token-label">${barLabel}</div><div class="token-bar"><div class="token-fill ${fillClass}" style="width:${pct}%"></div></div></div>
144
+ <div class="task-chip ${chipClass}">${p.events} task(s) · ${esc(p.tierLabel)}</div>
145
+ <div class="panel-log">
146
+ <div class="log-line"><span class="log-msg ok">${p.lastSummary ? esc(p.lastSummary) : 'idle'}</span></div>
147
+ </div>
148
+ </div>`;
149
+ }
150
+ /** A demo panel for an unbuilt provider — faithful to the mockup, marked demo. */
151
+ function demoPanel(name, tier, note) {
152
+ const cls = tier === 'paid' ? 'badge-paid' : 'badge-free';
153
+ // The paid tier is the principal (Claude) — give it the brick glow, not greyed.
154
+ const panelClass = tier === 'paid' ? 'principal' : 'demo';
155
+ return `
156
+ <div class="llm-panel ${panelClass}">
157
+ <span class="demo-tag">demo</span>
158
+ <div class="panel-header">
159
+ <span class="panel-name">${esc(name)}</span>
160
+ <span class="panel-tier badge ${cls}">${tier}</span>
161
+ </div>
162
+ <div class="token-bar-wrap"><div class="token-label"><span>${tier === 'paid' ? 'principal' : 'quota'}</span><span>—</span></div><div class="token-bar"><div class="token-fill ${tier === 'paid' ? 'red' : 'green'}" style="width:0%"></div></div></div>
163
+ <div class="task-chip idle">${tier === 'paid' ? 'copy-paste · you stay in the loop' : 'not yet built'}</div>
164
+ <div class="panel-log"><div class="log-line"><span class="log-msg">${esc(note)}</span></div></div>
165
+ </div>`;
166
+ }
167
+ function feedRows(items) {
168
+ if (items.length === 0) {
169
+ return '<div class="demo-overlay">No activity yet — run helpcode ask / plan</div>';
170
+ }
171
+ return items.map(i => {
172
+ const cls = i.outcome === 'ok' ? 'ok' : i.outcome === 'escalated' ? 'warn' : '';
173
+ return `<div class="feed-row"><span class="log-ts">${esc(i.time)}</span><span class="log-msg ${cls}">${esc(i.who)}</span><span class="log-msg">${esc(i.summary)}</span></div>`;
174
+ }).join('');
175
+ }
176
+ export function renderCockpitHtml(vm) {
177
+ const m = vm.metrics;
178
+ // Real workers fill the left column (up to 2); a third real worker (or demo) goes right.
179
+ const live = vm.workers.map(livePanel);
180
+ const leftPanels = live.slice(0, 2).join('') || demoPanel('helpcode-local', 'free', 'run a task to populate');
181
+ const rightPanels = [
182
+ live[2] ?? demoPanel('Claude', 'paid', 'the principal · your copy-paste brain'),
183
+ demoPanel('DeepSeek v3', 'free', 'second free provider — coming'),
184
+ ].join('');
185
+ const realCount = vm.workers.length;
186
+ const css = `*{box-sizing:border-box;margin:0;padding:0}
187
+ :root{
188
+ --bg:#0a0c10;--bg2:#111318;--bg3:#181b22;--bg4:#1e2129;
189
+ --border:#ffffff14;--border2:#ffffff22;--border3:#ffffff33;
190
+ --text:#e8eaf0;--text2:#9aa0b0;--text3:#5a6070;
191
+ --green:#22c55e;--green2:#16a34a;--green-dim:#052e16;
192
+ --amber:#f59e0b;--amber-dim:#1c1200;
193
+ --blue:#3b82f6;--blue-dim:#0a1628;
194
+ --purple:#a78bfa;--purple-dim:#1a1040;
195
+ --cyan:#06b6d4;--cyan-dim:#042830;
196
+ --red:#ef4444;--red-dim:#1c0a0a;
197
+ --brick:#cc785c;--brick2:#da7756;--brick-dim:#2a1610;
198
+ --font-mono:'JetBrains Mono','Fira Mono','Courier New',monospace;
199
+ }
200
+ body{background:var(--bg);color:var(--text);font-family:'system-ui',sans-serif;font-size:13px;min-height:800px;padding:12px;display:flex;flex-direction:column;gap:8px}
201
+ .sr-only{position:absolute;width:1px;height:1px;overflow:hidden}
202
+ .topbar{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:var(--bg2);border:0.5px solid var(--border);border-radius:8px}
203
+ .topbar-left{display:flex;align-items:center;gap:12px}
204
+ .logo{font-size:15px;font-weight:500;letter-spacing:-0.3px}
205
+ .logo span{color:var(--brick2)}
206
+ .badge{font-size:10px;padding:2px 7px;border-radius:4px;font-weight:500;letter-spacing:0.3px}
207
+ .badge-free{background:var(--green-dim);color:var(--green);border:0.5px solid var(--green2)}
208
+ .badge-paid{background:var(--brick-dim);color:var(--brick2);border:0.5px solid var(--brick)}
209
+ .badge-active{background:var(--blue-dim);color:var(--blue);border:0.5px solid #185fa5}
210
+ .badge-demo{background:var(--bg3);color:var(--text3);border:0.5px solid var(--border2)}
211
+ .topbar-right{display:flex;align-items:center;gap:8px}
212
+ .dot{width:7px;height:7px;border-radius:50%;background:var(--green)}
213
+ .dot.amber{background:var(--amber)}
214
+ .dot.pulse{animation:pulse 2s ease-in-out infinite}
215
+ @keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
216
+ .status-text{font-size:11px;color:var(--text2)}
217
+ .cockpit{display:grid;grid-template-columns:1fr 2fr 1fr;grid-template-rows:auto auto;gap:8px;flex:1}
218
+ .llm-panel{background:var(--bg2);border:0.5px solid var(--border);border-radius:8px;padding:10px;display:flex;flex-direction:column;gap:6px;min-height:160px;position:relative}
219
+ .llm-panel.active{border-color:var(--border2)}
220
+ .llm-panel.working{border-color:var(--cyan);box-shadow:0 0 0 1px var(--cyan-dim)}
221
+ .llm-panel.remote-active{border-color:var(--green2);box-shadow:0 0 0 1px var(--green-dim)}
222
+ .llm-panel.fallback-active{border-color:var(--amber);box-shadow:0 0 0 1px var(--amber-dim)}
223
+ .llm-panel.principal{border-color:var(--brick);box-shadow:0 0 0 1px var(--brick-dim);opacity:.7}
224
+ .llm-panel.demo{opacity:.5}
225
+ .demo-tag{position:absolute;top:8px;right:8px;font-size:8px;padding:1px 5px;border-radius:3px;background:var(--bg4);color:var(--text3);border:0.5px solid var(--border2);text-transform:uppercase;letter-spacing:.5px}
226
+ .panel-header{display:flex;align-items:center;justify-content:space-between}
227
+ .panel-name{font-size:11px;font-weight:500;color:var(--text2);letter-spacing:0.4px;text-transform:uppercase}
228
+ .panel-tier{font-size:9px;padding:1px 5px;border-radius:3px}
229
+ .token-bar-wrap{display:flex;flex-direction:column;gap:2px}
230
+ .token-label{display:flex;justify-content:space-between;font-size:9px;color:var(--text3)}
231
+ .token-bar{height:3px;background:var(--bg4);border-radius:2px;overflow:hidden}
232
+ .token-fill{height:100%;border-radius:2px;transition:width 1.2s ease}
233
+ .token-fill.green{background:var(--green)}
234
+ .token-fill.amber{background:var(--amber)}
235
+ .token-fill.blue{background:var(--blue)}
236
+ .token-fill.red{background:var(--red)}
237
+ .panel-log{flex:1;font-family:var(--font-mono);font-size:9px;color:var(--text3);line-height:1.6;overflow:hidden;display:flex;flex-direction:column;padding-top:2px}
238
+ .log-line{display:flex;gap:4px}
239
+ .log-ts{color:var(--text3);flex-shrink:0}
240
+ .log-msg{color:var(--text2)}
241
+ .log-msg.ok{color:var(--green)}
242
+ .log-msg.warn{color:var(--amber)}
243
+ .log-msg.info{color:var(--cyan)}
244
+ .task-chip{display:inline-flex;align-items:center;gap:4px;font-size:9px;padding:2px 6px;border-radius:3px;margin-top:2px;border:0.5px solid}
245
+ .task-chip.writing{background:var(--blue-dim);color:var(--blue);border-color:var(--blue)}
246
+ .task-chip.reviewing{background:var(--purple-dim);color:var(--purple);border-color:var(--purple)}
247
+ .task-chip.plan{background:var(--cyan-dim);color:var(--cyan);border-color:var(--cyan)}
248
+ .task-chip.idle{background:var(--bg3);color:var(--text3);border-color:var(--border)}
249
+ .center-col{display:flex;flex-direction:column;gap:8px}
250
+ .orchestrator{background:var(--bg2);border:0.5px solid var(--border2);border-radius:8px;padding:10px;display:flex;flex-direction:column;gap:8px;position:relative}
251
+ .orch-header{display:flex;align-items:center;justify-content:space-between}
252
+ .orch-title{font-size:12px;font-weight:500;color:var(--cyan)}
253
+ .orch-subtitle{font-size:10px;color:var(--text3)}
254
+ .pipeline{display:flex;flex-direction:column;gap:4px}
255
+ .pipe-row{display:flex;align-items:center;gap:6px;padding:5px 8px;background:var(--bg3);border-radius:5px;border:0.5px solid var(--border)}
256
+ .pipe-num{font-size:9px;color:var(--text3);width:14px;flex-shrink:0;font-family:var(--font-mono)}
257
+ .pipe-name{font-size:10px;color:var(--text);flex:1;font-weight:500}
258
+ .pipe-assign{font-size:9px;color:var(--text3)}
259
+ .pipe-status{font-size:9px;padding:1px 5px;border-radius:3px;flex-shrink:0;font-weight:500}
260
+ .pipe-status.done{background:var(--green-dim);color:var(--green)}
261
+ .pipe-status.queued{background:var(--bg4);color:var(--text3)}
262
+ .pipe-arrow{font-size:8px;color:var(--text3);flex-shrink:0}
263
+ .metrics-row{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
264
+ .metric{background:var(--bg3);border-radius:6px;padding:7px 10px;display:flex;flex-direction:column;gap:2px}
265
+ .metric-label{font-size:9px;color:var(--text3);text-transform:uppercase;letter-spacing:0.4px}
266
+ .metric-val{font-size:17px;font-weight:500;color:var(--text);line-height:1}
267
+ .metric-sub{font-size:9px;color:var(--text3)}
268
+ .metric-val.green{color:var(--green)}
269
+ .metric-val.amber{color:var(--amber)}
270
+ .metric-val.blue{color:var(--blue)}
271
+ .metric-val.purple{color:var(--purple)}
272
+ .cli-panel{background:var(--bg2);border:0.5px solid var(--border);border-radius:8px;padding:10px;flex:1;display:flex;flex-direction:column}
273
+ .cli-header{font-size:10px;color:var(--text3);margin-bottom:6px;display:flex;align-items:center;gap:6px}
274
+ .cli-dot{width:6px;height:6px;border-radius:50%}
275
+ .cli-output{flex:1;font-family:var(--font-mono);font-size:10px;line-height:1.7;color:var(--text2);overflow:hidden}
276
+ .cli-line-prefix{color:var(--cyan)}
277
+ .cli-line-ok{color:var(--green)}
278
+ .cli-line-warn{color:var(--amber)}
279
+ .cli-line-dim{color:var(--text3)}
280
+ .cli-input-row{display:flex;align-items:center;gap:6px;margin-top:8px;border-top:0.5px solid var(--border);padding-top:8px}
281
+ .cli-prompt{font-family:var(--font-mono);font-size:10px;color:var(--cyan);flex-shrink:0}
282
+ .cli-input{flex:1;background:transparent;border:none;outline:none;font-family:var(--font-mono);font-size:10px;color:var(--text3)}
283
+ @keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
284
+ .cursor{animation:blink 1s step-end infinite;color:var(--cyan)}
285
+ .bottom-row{display:grid;grid-template-columns:1fr 2fr 1fr;gap:8px}
286
+ .side-panel{background:var(--bg2);border:0.5px solid var(--border);border-radius:8px;padding:10px;display:flex;flex-direction:column;gap:6px;position:relative}
287
+ .side-panel.live{border-left:2px solid var(--green)}
288
+ .side-panel.routing{border-left:2px solid var(--cyan)}
289
+ .side-panel.roadmap{border-left:2px solid var(--text3);opacity:.7}
290
+ .side-title{font-size:10px;font-weight:500;color:var(--text2);display:flex;align-items:center;gap:5px}
291
+ .feed-row{display:flex;gap:6px;font-size:9px;padding:3px 0;border-bottom:0.5px solid var(--border);font-family:var(--font-mono)}
292
+ .feed-row:last-child{border-bottom:none}
293
+ .router-rule{display:flex;align-items:center;gap:6px;font-size:9px;padding:4px 6px;background:var(--bg3);border-radius:4px}
294
+ .router-cond{color:var(--text2);flex:1}
295
+ .router-then{color:var(--cyan)}
296
+ .router-arrow{color:var(--text3)}
297
+ .demo-overlay{font-size:8px;color:var(--text3);text-align:center;padding-top:4px;font-style:italic}
298
+ @keyframes spin{to{transform:rotate(360deg)}}
299
+ .spinner{display:inline-block;animation:spin 1s linear infinite;font-size:10px}
300
+ footer{color:var(--text3);font-size:10px;margin-top:4px;padding:8px 4px;text-align:center}`;
301
+ return `<!DOCTYPE html>
302
+ <html lang="en"><head><meta charset="utf-8">
303
+ <meta name="viewport" content="width=device-width, initial-scale=1">
304
+ <title>helpcode cockpit</title>
305
+ <style>${css}</style></head>
306
+ <body>
307
+ <h2 class="sr-only">helpcode cockpit — sous-chef kitchen, real activity plus roadmap</h2>
308
+
309
+ <div class="topbar">
310
+ <div class="topbar-left">
311
+ <span class="logo">help<span>code</span></span>
312
+ <span class="badge badge-active">cockpit</span>
313
+ <span class="badge badge-free">${realCount} worker(s) active</span>
314
+ </div>
315
+ <div class="topbar-right">
316
+ <div class="dot pulse"></div>
317
+ <span class="status-text">${m.totalEvents} prep task(s) this session · ~${m.estTokensSaved} principal tokens saved</span>
318
+ </div>
319
+ </div>
320
+
321
+ <div class="cockpit">
322
+ <div style="display:flex;flex-direction:column;gap:8px">${leftPanels}</div>
323
+
324
+ <div class="center-col">
325
+ <div class="metrics-row">
326
+ <div class="metric"><span class="metric-label">prep tasks</span><span class="metric-val blue">${m.totalEvents}</span><span class="metric-sub">this session</span></div>
327
+ <div class="metric"><span class="metric-label">escalated</span><span class="metric-val amber">${m.escalations}</span><span class="metric-sub">to principal</span></div>
328
+ <div class="metric"><span class="metric-label">fell back</span><span class="metric-val purple">${m.fallbacks}</span><span class="metric-sub">to cheaper</span></div>
329
+ <div class="metric"><span class="metric-label">tokens saved</span><span class="metric-val green">~${m.estTokensSaved}</span><span class="metric-sub">principal</span></div>
330
+ </div>
331
+
332
+ <div class="orchestrator">
333
+ <span class="demo-tag">demo</span>
334
+ <div class="orch-header">
335
+ <div>
336
+ <div class="orch-title">orchestrator · autonomous pipeline</div>
337
+ <div class="orch-subtitle">vision: decompose → assign → escalate (not yet built)</div>
338
+ </div>
339
+ <span class="badge badge-demo">roadmap</span>
340
+ </div>
341
+ <div class="pipeline">
342
+ <div class="pipe-row"><span class="pipe-num">01</span><span class="pipe-name">decompose task → subtasks</span><span class="pipe-assign">local</span><span class="pipe-arrow">→</span><span class="pipe-status done">live</span></div>
343
+ <div class="pipe-row"><span class="pipe-num">02</span><span class="pipe-name">assign by complexity score</span><span class="pipe-assign">router</span><span class="pipe-arrow">→</span><span class="pipe-status queued">coming</span></div>
344
+ <div class="pipe-row"><span class="pipe-num">03</span><span class="pipe-name">cross-model review</span><span class="pipe-assign">peer</span><span class="pipe-arrow">→</span><span class="pipe-status queued">coming</span></div>
345
+ <div class="pipe-row"><span class="pipe-num">04</span><span class="pipe-name">escalate hard subtasks → principal</span><span class="pipe-assign">you (paste)</span><span class="pipe-arrow">→</span><span class="pipe-status queued">coming</span></div>
346
+ </div>
347
+ </div>
348
+
349
+ <div class="cli-panel">
350
+ <div class="cli-header">
351
+ <div class="cli-dot" style="background:#ef4444"></div>
352
+ <div class="cli-dot" style="background:#f59e0b"></div>
353
+ <div class="cli-dot" style="background:#22c55e"></div>
354
+ <span style="margin-left:6px;font-size:10px;color:var(--text3)">helpcode · sous-chef shell · live</span>
355
+ </div>
356
+ <div class="cli-output">
357
+ \${vm.cli.map(l => \`<div class="cli-line-\${l.kind}">\${esc(l.text)}</div>\`).join('')}
358
+ </div>
359
+ <div class="cli-input-row">
360
+ <span class="cli-prompt">\u276f</span>
361
+ <span class="cli-input">helpcode cockpit<span class="cursor">_</span></span>
362
+ </div>
363
+ </div>
364
+ </div>
365
+
366
+ <div style="display:flex;flex-direction:column;gap:8px">${rightPanels}</div>
367
+ </div>
368
+
369
+ <div class="bottom-row">
370
+ <div class="side-panel live">
371
+ <div class="side-title">recent activity · live</div>
372
+ ${feedRows(vm.recent)}
373
+ </div>
374
+
375
+ <div class="side-panel routing">
376
+ <span class="demo-tag">part live</span>
377
+ <div class="side-title">routing rules · current policy</div>
378
+ <div class="router-rule"><span class="router-cond">no code, no key</span><span class="router-arrow">→</span><span class="router-then">local only (live)</span></div>
379
+ <div class="router-rule"><span class="router-cond">key, no opt-in</span><span class="router-arrow">→</span><span class="router-then">remote: decomposition only (live)</span></div>
380
+ <div class="router-rule"><span class="router-cond">allowRemoteCode on</span><span class="router-arrow">→</span><span class="router-then">remote: code tasks too (live)</span></div>
381
+ <div class="router-rule"><span class="router-cond">complexity &gt; threshold</span><span class="router-arrow">→</span><span class="router-then">escalate to principal (coming)</span></div>
382
+ <div class="demo-overlay">first three rules are live; complexity routing is roadmap</div>
383
+ </div>
384
+
385
+ <div class="side-panel roadmap">
386
+ <span class="demo-tag">demo</span>
387
+ <div class="side-title">roadmap</div>
388
+ ${vm.coming.map(c => `<div class="router-rule"><span class="router-cond">${esc(c.name)}</span></div>`).join('')}
389
+ </div>
390
+ </div>
391
+
392
+ <footer>helpcode · principal + sous-chefs · live panels reflect this project's real activity; "demo" panels show the roadmap. generated ${esc(vm.generatedAt.slice(0, 16).replace('T', ' '))}</footer>
393
+ </body></html>`;
394
+ }
395
+ //# sourceMappingURL=cockpitHtml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cockpitHtml.js","sourceRoot":"","sources":["../../../src/core/cockpitHtml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAkB,MAAM,cAAc,CAAC;AAkChF,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB,SAAS,SAAS,CAAC,IAAgB;IACjC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC;QAC7B,KAAK,QAAQ,CAAC,CAAC,OAAO,WAAW,CAAC;QAClC,KAAK,WAAW,CAAC,CAAC,OAAO,MAAM,CAAC;QAChC,KAAK,eAAe,CAAC,CAAC,OAAO,MAAM,CAAC;IACtC,CAAC;AACH,CAAC;AAED,SAAS,eAAe;IACtB,OAAO;QACL,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,2DAA2D,EAAE;QAChG,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,iDAAiD,EAAE;QACvF,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,mDAAmD,EAAE;QACrF,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,4CAA4C,EAAE;KAChF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAoB;IACxD,MAAM,OAAO,GAAc,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACzD,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QACnC,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5B,MAAM,EAAE,CAAC,CAAC,iBAAiB;QAC3B,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,SAAS,EAAE,CAAC,CAAC,SAAS;KACvB,CAAC,CAAC,CAAC;IAEJ,MAAM,MAAM,GAAiB,CAAC,GAAG,GAAG,CAAC;SAClC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACxC,KAAK,CAAC,CAAC,UAAU,CAAC;SAClB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACT,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QACxB,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QACnC,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,OAAO,EAAE,CAAC,CAAC,OAAO;KACnB,CAAC,CAAC,CAAC;IAEN,6EAA6E;IAC7E,MAAM,GAAG,GAAgB,aAAa,CAAC,GAAG,CAAC,CAAC;IAE5C,OAAO;QACL,OAAO;QACP,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC;QAC5B,MAAM;QACN,GAAG;QACH,MAAM,EAAE,eAAe,EAAE;QACzB,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACtC,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,SAAS,QAAQ,CAAC,IAA2B;IAC3C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,gBAAgB,CAAC,CAAC,OAAO,QAAQ,CAAC;QACvC,KAAK,eAAe,CAAC,CAAC,OAAO,QAAQ,CAAC;QACtC,KAAK,eAAe,CAAC,CAAC,OAAO,MAAM,CAAC;QACpC,OAAO,CAAC,CAAC,OAAO,MAAM,CAAC;IACzB,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,aAAa,CAAC,GAAoB;IAChD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;YACL,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,sBAAsB,EAAE;YAChD,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,0CAA0C,EAAE;YACjE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,sDAAsD,EAAE;SAC9E,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,IAAI,WAAW,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAClH,CAAC;aAAM,IAAI,CAAC,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,IAAI,sCAAsC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACxG,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,IAAI,qBAAqB,GAAG,WAAW,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACpG,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,6EAA6E;AAE7E,SAAS,GAAG,CAAC,CAAS;IACpB,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACtG,CAAC;AAED,SAAS,QAAQ,CAAC,IAAgB;IAChC,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACnE,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACtE,IAAI,IAAI,KAAK,eAAe;QAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC1E,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC;AAED,mEAAmE;AACnE,SAAS,SAAS,CAAC,CAAU;IAC3B,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3B,wFAAwF;IACxF,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS;QAC/C,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,eAAe;YACvC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,iBAAiB;gBAChD,CAAC,CAAC,QAAQ,CAAC;IACb,0EAA0E;IAC1E,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW;QAChE,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM;YAC3D,CAAC,CAAC,SAAS,CAAC;IACd,4EAA4E;IAC5E,IAAI,SAAS,GAAG,OAAO,CAAC;IACxB,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QACzB,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC;QACpC,SAAS,GAAG,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAC9D,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,QAAQ,GAAG,CAAC,CAAC,SAAS,KAAK,IAAI;QACnC,CAAC,CAAC,2BAA2B,GAAG,UAAU;QAC1C,CAAC,CAAC,2BAA2B,CAAC,CAAC,MAAM,SAAS,CAAC;IACjD,OAAO;4BACmB,UAAU;;mCAEH,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;wCACP,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK;;6DAEI,QAAQ,uDAAuD,SAAS,kBAAkB,GAAG;8BAC5H,SAAS,KAAK,CAAC,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;;yDAEzB,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM;;WAEzF,CAAC;AACZ,CAAC;AAED,kFAAkF;AAClF,SAAS,SAAS,CAAC,IAAY,EAAE,IAAqB,EAAE,IAAY;IAClE,MAAM,GAAG,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;IAC1D,gFAAgF;IAChF,MAAM,UAAU,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1D,OAAO;4BACmB,UAAU;;;mCAGH,GAAG,CAAC,IAAI,CAAC;wCACJ,GAAG,KAAK,IAAI;;mEAEe,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,4EAA4E,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO;oCACnL,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,mCAAmC,CAAC,CAAC,CAAC,eAAe;2EAChC,GAAG,CAAC,IAAI,CAAC;WACzE,CAAC;AACZ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAmB;IACnC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,2EAA2E,CAAC;IACrF,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QACnB,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,OAAO,8CAA8C,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,+BAA+B,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,gCAAgC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;IACjL,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAoB;IACpD,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;IACrB,yFAAyF;IACzF,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,SAAS,CAAC,gBAAgB,EAAE,MAAM,EAAE,wBAAwB,CAAC,CAAC;IAC9G,MAAM,WAAW,GAAG;QAClB,IAAI,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,uCAAuC,CAAC;QAC/E,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,+BAA+B,CAAC;KAClE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEX,MAAM,SAAS,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IACpC,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2FAkH6E,CAAC;IAE1F,OAAO;;;;SAIA,GAAG;;;;;;;;qCAQyB,SAAS;;;;gCAId,CAAC,CAAC,WAAW,iCAAiC,CAAC,CAAC,cAAc;;;;;4DAKlC,UAAU;;;;sGAIgC,CAAC,CAAC,WAAW;sGACb,CAAC,CAAC,WAAW;uGACZ,CAAC,CAAC,SAAS;0GACR,CAAC,CAAC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4DAqC9D,WAAW;;;;;;MAMjE,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;MAgBnB,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,sDAAsD,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;;;;0IAIuB,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;eAC7K,CAAC;AAChB,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * One-time consent notice for sending source code to a remote free tier (v0.3.2).
3
+ *
4
+ * The privacy gate (core/souschef.ts) decides whether a task MAY go remote. This
5
+ * module decides whether to SHOW THE USER A NOTICE the first time code actually
6
+ * leaves the machine to a free tier — informed consent, per the design doc.
7
+ *
8
+ * The notice fires when ALL of:
9
+ * - allowRemoteCode is on (the user opted in)
10
+ * - the task is code-bearing (not decomposition, which never sends code)
11
+ * - we haven't already shown it (tracked in state)
12
+ */
13
+ import { SousChefTask } from '../types.js';
14
+ export interface RemoteCodeNoticeInput {
15
+ allowRemoteCode: boolean;
16
+ task: SousChefTask;
17
+ alreadyShown: boolean;
18
+ }
19
+ export declare function shouldShowRemoteCodeNotice(input: RemoteCodeNoticeInput): boolean;
20
+ export declare function remoteCodeNoticeText(model: string): string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * One-time consent notice for sending source code to a remote free tier (v0.3.2).
3
+ *
4
+ * The privacy gate (core/souschef.ts) decides whether a task MAY go remote. This
5
+ * module decides whether to SHOW THE USER A NOTICE the first time code actually
6
+ * leaves the machine to a free tier — informed consent, per the design doc.
7
+ *
8
+ * The notice fires when ALL of:
9
+ * - allowRemoteCode is on (the user opted in)
10
+ * - the task is code-bearing (not decomposition, which never sends code)
11
+ * - we haven't already shown it (tracked in state)
12
+ */
13
+ /** Code-bearing tasks — these send source code, unlike decomposition. */
14
+ function isCodeBearing(task) {
15
+ return task !== 'decomposition';
16
+ }
17
+ export function shouldShowRemoteCodeNotice(input) {
18
+ if (!input.allowRemoteCode)
19
+ return false;
20
+ if (!isCodeBearing(input.task))
21
+ return false;
22
+ if (input.alreadyShown)
23
+ return false;
24
+ return true;
25
+ }
26
+ export function remoteCodeNoticeText(model) {
27
+ return [
28
+ 'Heads up: allowRemoteCode is on, so helpcode is about to send source code',
29
+ `to a remote free tier (${model}). Free-tier providers may use your inputs to`,
30
+ 'train their models. Only proceed if you accept your code leaving this machine.',
31
+ 'Disable any time by setting "allowRemoteCode": false in .helpcode/project.json.',
32
+ ].join('\n');
33
+ }
34
+ //# sourceMappingURL=consent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../../src/core/consent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,yEAAyE;AACzE,SAAS,aAAa,CAAC,IAAkB;IACvC,OAAO,IAAI,KAAK,eAAe,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAA4B;IACrE,IAAI,CAAC,KAAK,CAAC,eAAe;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI,KAAK,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO;QACL,2EAA2E;QAC3E,0BAA0B,KAAK,+CAA+C;QAC9E,gFAAgF;QAChF,iFAAiF;KAClF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Task decomposition (v0.3.1) — the third local sous-chef task.
3
+ *
4
+ * A big, sprawling task ("add auth: login, logout, reset, sessions") produces a
5
+ * sprawling brief and a sprawling Claude reply. Decomposition asks the local
6
+ * model to break it into ordered, focused sub-steps, so the principal can be
7
+ * given ONE step at a time.
8
+ *
9
+ * CRITICAL constraint (from the design doc): decomposition PROPOSES, the human
10
+ * APPROVES. It never autonomously runs the steps. `helpcode plan` prints the
11
+ * breakdown and stops. This keeps helpcode in-the-loop, not an autonomous agent.
12
+ *
13
+ * Unlike selection (keyword fallback) and triage (truncation fallback), there
14
+ * is no sensible deterministic fallback for decomposition — you can't split a
15
+ * task into meaningful steps without understanding it. So if the local model
16
+ * is unavailable, decomposition honestly reports ok=false and the user just
17
+ * proceeds with their task as a single unit. Doing nothing is the safe fallback.
18
+ */
19
+ import { OllamaSettings } from '../types.js';
20
+ export interface DecompositionResult {
21
+ ok: boolean;
22
+ /** Ordered sub-steps (empty if ok is false). */
23
+ steps: string[];
24
+ /** If ok is false, why. Empty otherwise. */
25
+ reason: string;
26
+ }
27
+ type GenerateImpl = (host: string, model: string, prompt: string, opts?: {
28
+ timeoutMs?: number;
29
+ }) => Promise<string>;
30
+ export interface DecomposeDeps {
31
+ generateImpl?: GenerateImpl;
32
+ }
33
+ export declare function buildDecompositionPrompt(task: string): string;
34
+ /**
35
+ * Parse a numbered/bulleted list of steps from the model's response. Tolerant
36
+ * of prose around the list, various markers (1. / 1) / -), and markdown bold.
37
+ */
38
+ export declare function parseDecomposition(response: string): string[];
39
+ /**
40
+ * Decompose a task using the local model. Proposes only — never runs anything.
41
+ * ok=false when: ollama disabled, model failed, or the result was a single step
42
+ * (i.e. decomposition added no value). The caller then just proceeds normally.
43
+ */
44
+ export declare function decomposeTask(task: string, ollama: OllamaSettings, deps?: DecomposeDeps): Promise<DecompositionResult>;
45
+ export {};
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Task decomposition (v0.3.1) — the third local sous-chef task.
3
+ *
4
+ * A big, sprawling task ("add auth: login, logout, reset, sessions") produces a
5
+ * sprawling brief and a sprawling Claude reply. Decomposition asks the local
6
+ * model to break it into ordered, focused sub-steps, so the principal can be
7
+ * given ONE step at a time.
8
+ *
9
+ * CRITICAL constraint (from the design doc): decomposition PROPOSES, the human
10
+ * APPROVES. It never autonomously runs the steps. `helpcode plan` prints the
11
+ * breakdown and stops. This keeps helpcode in-the-loop, not an autonomous agent.
12
+ *
13
+ * Unlike selection (keyword fallback) and triage (truncation fallback), there
14
+ * is no sensible deterministic fallback for decomposition — you can't split a
15
+ * task into meaningful steps without understanding it. So if the local model
16
+ * is unavailable, decomposition honestly reports ok=false and the user just
17
+ * proceeds with their task as a single unit. Doing nothing is the safe fallback.
18
+ */
19
+ import { generate, OllamaError } from './ollama.js';
20
+ export function buildDecompositionPrompt(task) {
21
+ return [
22
+ 'Break the following software task into a short ordered list of focused,',
23
+ 'self-contained sub-steps. Each step should be something that could be',
24
+ 'tackled in a single round with a coding assistant. Aim for 2-6 steps.',
25
+ 'If the task is already small enough, return a single step.',
26
+ '',
27
+ 'Respond ONLY with a numbered list, one step per line, no preamble:',
28
+ ' 1. first step',
29
+ ' 2. second step',
30
+ '',
31
+ `TASK: ${task}`,
32
+ ].join('\n');
33
+ }
34
+ /**
35
+ * Parse a numbered/bulleted list of steps from the model's response. Tolerant
36
+ * of prose around the list, various markers (1. / 1) / -), and markdown bold.
37
+ */
38
+ export function parseDecomposition(response) {
39
+ const steps = [];
40
+ for (const rawLine of response.split(/\r?\n/)) {
41
+ const line = rawLine.trim();
42
+ if (!line)
43
+ continue;
44
+ // Match a leading list marker: "1." / "1)" / "-" / "*"
45
+ const m = line.match(/^(?:\d+[.)]|[-*])\s+(.*)$/);
46
+ if (!m)
47
+ continue;
48
+ let text = m[1].trim();
49
+ // Strip surrounding markdown emphasis (**bold**, *italic*, `code`)
50
+ text = text.replace(/\*\*(.*?)\*\*/g, '$1')
51
+ .replace(/\*(.*?)\*/g, '$1')
52
+ .replace(/`(.*?)`/g, '$1')
53
+ .trim();
54
+ if (text)
55
+ steps.push(text);
56
+ }
57
+ return steps;
58
+ }
59
+ /**
60
+ * Decompose a task using the local model. Proposes only — never runs anything.
61
+ * ok=false when: ollama disabled, model failed, or the result was a single step
62
+ * (i.e. decomposition added no value). The caller then just proceeds normally.
63
+ */
64
+ export async function decomposeTask(task, ollama, deps = {}) {
65
+ if (!ollama.enabled) {
66
+ return { ok: false, steps: [], reason: 'local model not enabled' };
67
+ }
68
+ const generateImpl = deps.generateImpl ?? generate;
69
+ let response;
70
+ try {
71
+ response = await generateImpl(ollama.host, ollama.model, buildDecompositionPrompt(task), { timeoutMs: ollama.timeoutMs });
72
+ }
73
+ catch (e) {
74
+ const why = e instanceof OllamaError ? e.message : e.message;
75
+ return { ok: false, steps: [], reason: `local model failed: ${why}` };
76
+ }
77
+ const steps = parseDecomposition(response);
78
+ if (steps.length === 0) {
79
+ return { ok: false, steps: [], reason: 'no steps parsed from model output' };
80
+ }
81
+ if (steps.length === 1) {
82
+ return { ok: false, steps, reason: 'task came back as a single step — not worth decomposing' };
83
+ }
84
+ return { ok: true, steps, reason: '' };
85
+ }
86
+ //# sourceMappingURL=decompose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decompose.js","sourceRoot":"","sources":["../../../src/core/decompose.ts"],"names":[],"mappings":"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"}