@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.
- package/dist/bin/helpcode.js +0 -0
- package/dist/src/commands/ask.js +15 -1
- package/dist/src/commands/ask.js.map +1 -1
- package/dist/src/commands/cockpit.d.ts +13 -0
- package/dist/src/commands/cockpit.js +33 -0
- package/dist/src/commands/cockpit.js.map +1 -0
- package/dist/src/commands/plan.d.ts +14 -0
- package/dist/src/commands/plan.js +113 -0
- package/dist/src/commands/plan.js.map +1 -0
- package/dist/src/commands/run.js +51 -4
- package/dist/src/commands/run.js.map +1 -1
- package/dist/src/core/cockpit.d.ts +27 -0
- package/dist/src/core/cockpit.js +109 -0
- package/dist/src/core/cockpit.js.map +1 -0
- package/dist/src/core/cockpitHtml.d.ts +52 -0
- package/dist/src/core/cockpitHtml.js +395 -0
- package/dist/src/core/cockpitHtml.js.map +1 -0
- package/dist/src/core/consent.d.ts +20 -0
- package/dist/src/core/consent.js +34 -0
- package/dist/src/core/consent.js.map +1 -0
- package/dist/src/core/decompose.d.ts +45 -0
- package/dist/src/core/decompose.js +86 -0
- package/dist/src/core/decompose.js.map +1 -0
- package/dist/src/core/gemini.d.ts +41 -0
- package/dist/src/core/gemini.js +91 -0
- package/dist/src/core/gemini.js.map +1 -0
- package/dist/src/core/keys.d.ts +29 -0
- package/dist/src/core/keys.js +92 -0
- package/dist/src/core/keys.js.map +1 -0
- package/dist/src/core/llmSelector.d.ts +6 -0
- package/dist/src/core/llmSelector.js +22 -7
- package/dist/src/core/llmSelector.js.map +1 -1
- package/dist/src/core/providers.d.ts +60 -0
- package/dist/src/core/providers.js +117 -0
- package/dist/src/core/providers.js.map +1 -0
- package/dist/src/core/remoteRouter.d.ts +27 -0
- package/dist/src/core/remoteRouter.js +71 -0
- package/dist/src/core/remoteRouter.js.map +1 -0
- package/dist/src/core/selector.d.ts +9 -4
- package/dist/src/core/selector.js +85 -29
- package/dist/src/core/selector.js.map +1 -1
- package/dist/src/core/souschef.d.ts +63 -0
- package/dist/src/core/souschef.js +55 -0
- package/dist/src/core/souschef.js.map +1 -0
- package/dist/src/core/state.d.ts +13 -1
- package/dist/src/core/state.js +47 -5
- package/dist/src/core/state.js.map +1 -1
- package/dist/src/core/triage.d.ts +14 -1
- package/dist/src/core/triage.js +23 -5
- package/dist/src/core/triage.js.map +1 -1
- package/dist/src/index.d.ts +8 -7
- package/dist/src/index.js +21 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/types.d.ts +70 -1
- package/package.json +1 -1
|
@@ -23,14 +23,19 @@ export interface SelectedFile {
|
|
|
23
23
|
export interface SelectionResult {
|
|
24
24
|
files: SelectedFile[];
|
|
25
25
|
/** Which strategy actually produced the result. */
|
|
26
|
-
strategy: 'llm' | 'heuristic';
|
|
26
|
+
strategy: 'llm' | 'remote' | 'heuristic';
|
|
27
27
|
/** If the LLM was attempted but fell back, why. Empty otherwise. */
|
|
28
28
|
fallbackReason: string;
|
|
29
|
+
/** For the remote strategy: the model used (for the cockpit). */
|
|
30
|
+
remoteModel?: string;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
|
-
* The entry point used by `ask`. Chooses
|
|
32
|
-
*
|
|
33
|
-
* to the
|
|
33
|
+
* The entry point used by `ask`. Chooses, cheapest-first:
|
|
34
|
+
* local LLM (Ollama) → remote free-tier (if opted in) → keyword heuristic.
|
|
35
|
+
* Never throws — any model failure degrades to the next option with a reason.
|
|
36
|
+
*
|
|
37
|
+
* The remote branch only runs when allowRemoteCode is on AND a key is present:
|
|
38
|
+
* file selection sends file signatures (code), so it's privacy-gated.
|
|
34
39
|
*/
|
|
35
40
|
export declare function selectFilesWithStrategy(taskDescription: string, config: ProjectConfig, opts?: {
|
|
36
41
|
forceHeuristic?: boolean;
|
|
@@ -14,8 +14,30 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import * as fs from 'fs';
|
|
16
16
|
import * as path from 'path';
|
|
17
|
-
import { isOllamaReachable
|
|
18
|
-
import { llmSelectFiles } from './llmSelector.js';
|
|
17
|
+
import { isOllamaReachable } from './ollama.js';
|
|
18
|
+
import { llmSelectFiles, selectFilesWithGenerate } from './llmSelector.js';
|
|
19
|
+
import { firstAvailableProvider } from './remoteRouter.js';
|
|
20
|
+
import { shouldShowRemoteCodeNotice, remoteCodeNoticeText } from './consent.js';
|
|
21
|
+
import { loadState, saveState } from './state.js';
|
|
22
|
+
/**
|
|
23
|
+
* Show the one-time "code is leaving the machine to a free tier" notice if it
|
|
24
|
+
* hasn't been shown before, and persist that it has. Best-effort; never throws.
|
|
25
|
+
*/
|
|
26
|
+
function maybeShowRemoteCodeNotice(task, model) {
|
|
27
|
+
try {
|
|
28
|
+
const state = loadState();
|
|
29
|
+
const alreadyShown = state.flags?.remoteCodeNoticeShown === true;
|
|
30
|
+
if (shouldShowRemoteCodeNotice({ allowRemoteCode: true, task, alreadyShown })) {
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.error('\n' + remoteCodeNoticeText(model) + '\n');
|
|
33
|
+
state.flags = { ...(state.flags ?? {}), remoteCodeNoticeShown: true };
|
|
34
|
+
saveState(state);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// never block selection on the notice
|
|
39
|
+
}
|
|
40
|
+
}
|
|
19
41
|
const IGNORE_DIRS = new Set([
|
|
20
42
|
'.git', '.venv', 'venv', 'env', 'node_modules', '__pycache__',
|
|
21
43
|
'.pytest_cache', '.mypy_cache', 'dist', 'build', '.next',
|
|
@@ -29,42 +51,76 @@ const MAX_RESULTS = 6;
|
|
|
29
51
|
const RECENCY_WINDOW_DAYS = 14;
|
|
30
52
|
const LLM_CANDIDATE_CAP = 60; // cap files sent to the model (design §3.1)
|
|
31
53
|
/**
|
|
32
|
-
* The entry point used by `ask`. Chooses
|
|
33
|
-
*
|
|
34
|
-
* to the
|
|
54
|
+
* The entry point used by `ask`. Chooses, cheapest-first:
|
|
55
|
+
* local LLM (Ollama) → remote free-tier (if opted in) → keyword heuristic.
|
|
56
|
+
* Never throws — any model failure degrades to the next option with a reason.
|
|
57
|
+
*
|
|
58
|
+
* The remote branch only runs when allowRemoteCode is on AND a key is present:
|
|
59
|
+
* file selection sends file signatures (code), so it's privacy-gated.
|
|
35
60
|
*/
|
|
36
61
|
export async function selectFilesWithStrategy(taskDescription, config, opts = {}) {
|
|
37
62
|
const ollama = config.ollama;
|
|
38
63
|
const wantLlm = !opts.forceHeuristic && ollama?.enabled === true;
|
|
39
64
|
if (wantLlm && ollama) {
|
|
40
65
|
const reachable = await isOllamaReachable(ollama.host, { timeoutMs: 1000 });
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
if (reachable) {
|
|
67
|
+
try {
|
|
68
|
+
const allFiles = walkAllSourceFiles(config).slice(0, LLM_CANDIDATE_CAP);
|
|
69
|
+
const selections = await llmSelectFiles(taskDescription, allFiles, config.root, {
|
|
70
|
+
host: ollama.host,
|
|
71
|
+
model: ollama.model,
|
|
72
|
+
count: MAX_RESULTS,
|
|
73
|
+
timeoutMs: ollama.timeoutMs,
|
|
74
|
+
});
|
|
75
|
+
if (selections.length > 0) {
|
|
76
|
+
const files = selections.map(s => ({
|
|
77
|
+
filepath: path.join(config.root, s.path),
|
|
78
|
+
reason: s.reason,
|
|
79
|
+
}));
|
|
80
|
+
return { files, strategy: 'llm', fallbackReason: '' };
|
|
81
|
+
}
|
|
82
|
+
// fall through to remote/heuristic
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// fall through to remote/heuristic
|
|
54
86
|
}
|
|
55
|
-
// Map relative paths back to absolute for the caller
|
|
56
|
-
const files = selections.map(s => ({
|
|
57
|
-
filepath: path.join(config.root, s.path),
|
|
58
|
-
reason: s.reason,
|
|
59
|
-
}));
|
|
60
|
-
return { files, strategy: 'llm', fallbackReason: '' };
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
const why = e instanceof OllamaError ? e.message : e.message;
|
|
64
|
-
return heuristicResult(taskDescription, config, why);
|
|
65
87
|
}
|
|
66
88
|
}
|
|
67
|
-
|
|
89
|
+
// Remote branch: only if NOT forcing heuristic, the project opted into
|
|
90
|
+
// sending code remotely, and a key is configured. Selection sends code
|
|
91
|
+
// (file signatures), so the privacy gate requires allowRemoteCode.
|
|
92
|
+
if (!opts.forceHeuristic && config.remote?.allowRemoteCode === true) {
|
|
93
|
+
const remote = await tryRemoteSelection(taskDescription, config);
|
|
94
|
+
if (remote)
|
|
95
|
+
return remote;
|
|
96
|
+
}
|
|
97
|
+
return heuristicResult(taskDescription, config, wantLlm ? 'local model unavailable; used keyword heuristic' : '');
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Attempt remote file selection via the first available free-tier provider.
|
|
101
|
+
* Returns null on any failure (caller falls back to heuristic). Shows the
|
|
102
|
+
* one-time code-consent notice on first use.
|
|
103
|
+
*/
|
|
104
|
+
async function tryRemoteSelection(taskDescription, config) {
|
|
105
|
+
const provider = firstAvailableProvider(process.env, config.remote?.provider);
|
|
106
|
+
if (!provider)
|
|
107
|
+
return null;
|
|
108
|
+
// One-time consent notice (code is about to leave the machine to a free tier).
|
|
109
|
+
maybeShowRemoteCodeNotice('file_selection', `${provider.label} · ${provider.model}`);
|
|
110
|
+
try {
|
|
111
|
+
const allFiles = walkAllSourceFiles(config).slice(0, LLM_CANDIDATE_CAP);
|
|
112
|
+
const selections = await selectFilesWithGenerate(taskDescription, allFiles, config.root, MAX_RESULTS, (prompt) => provider.generate(prompt));
|
|
113
|
+
if (selections.length === 0)
|
|
114
|
+
return null;
|
|
115
|
+
const files = selections.map(s => ({
|
|
116
|
+
filepath: path.join(config.root, s.path),
|
|
117
|
+
reason: s.reason,
|
|
118
|
+
}));
|
|
119
|
+
return { files, strategy: 'remote', fallbackReason: '', remoteModel: provider.model };
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
68
124
|
}
|
|
69
125
|
function heuristicResult(taskDescription, config, fallbackReason) {
|
|
70
126
|
const files = selectFiles(taskDescription, config).map(filepath => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selector.js","sourceRoot":"","sources":["../../../src/core/selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"selector.js","sourceRoot":"","sources":["../../../src/core/selector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sous-chef layer (v0.3.2) — makes local and remote workers interchangeable
|
|
3
|
+
* behind one interface, with a privacy gate and cheapest-capable-first routing.
|
|
4
|
+
*
|
|
5
|
+
* Design doc: docs/v0.3.2-remote-souschef-design.md
|
|
6
|
+
*
|
|
7
|
+
* The privacy gate is the load-bearing rule: a REMOTE free-tier worker may only
|
|
8
|
+
* receive data the user has accepted leaving the machine. By default that means
|
|
9
|
+
* decomposition only (input is the task description, not code). A per-project
|
|
10
|
+
* `allowRemoteCode` opt-in lets consenting users route code-bearing tasks too.
|
|
11
|
+
*
|
|
12
|
+
* Routing is cheapest-capable-first: local before remote. A remote worker is
|
|
13
|
+
* only chosen when (a) the privacy gate permits the task, (b) no local worker
|
|
14
|
+
* can handle it, and (c) the worker's own canHandle() says yes (e.g. has quota).
|
|
15
|
+
*/
|
|
16
|
+
import { SousChefTask } from '../types.js';
|
|
17
|
+
export interface QuotaStatus {
|
|
18
|
+
/** Free requests used in the current day, if tracked. */
|
|
19
|
+
usedToday: number;
|
|
20
|
+
/** Daily limit, if known. */
|
|
21
|
+
limitPerDay: number | null;
|
|
22
|
+
/** True if the provider is currently throttled (e.g. after a 429). */
|
|
23
|
+
throttled: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface WorkerContext {
|
|
26
|
+
/** Project opt-in: may code-bearing tasks go to remote free tiers? */
|
|
27
|
+
allowRemoteCode: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface SousChef {
|
|
30
|
+
/** Stable id, e.g. "local:qwen2.5-coder:7b" or "gemini:2.5-flash-lite". */
|
|
31
|
+
id: string;
|
|
32
|
+
kind: 'local' | 'remote';
|
|
33
|
+
/**
|
|
34
|
+
* Whether this worker can take this task right now, ignoring privacy (that's
|
|
35
|
+
* enforced separately in canWorkerHandle). Used for quota/availability vetoes.
|
|
36
|
+
*/
|
|
37
|
+
canHandle: (task: SousChefTask, ctx: WorkerContext) => boolean;
|
|
38
|
+
/** Do the work. Throws on failure; callers fall back to a cheaper worker. */
|
|
39
|
+
run: (prompt: string, opts?: {
|
|
40
|
+
timeoutMs?: number;
|
|
41
|
+
}) => Promise<string>;
|
|
42
|
+
/** Remote workers report quota for the cockpit + routing; local returns null. */
|
|
43
|
+
quota: () => QuotaStatus | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The privacy gate. Which tasks may a REMOTE worker receive?
|
|
47
|
+
* - decomposition: always (input is the user's task description, no code)
|
|
48
|
+
* - everything else: only if the project opted into allowRemoteCode
|
|
49
|
+
*
|
|
50
|
+
* Local workers are never gated by this — code never leaves the machine.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isRemoteAllowedForTask(task: SousChefTask, allowRemoteCode: boolean): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Can this specific worker handle this task, accounting for privacy AND the
|
|
55
|
+
* worker's own availability veto?
|
|
56
|
+
*/
|
|
57
|
+
export declare function canWorkerHandle(worker: SousChef, task: SousChefTask, ctx: WorkerContext): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Pick the cheapest capable worker. Local workers are cheaper than remote, so
|
|
60
|
+
* they're preferred; within a kind, the first in the list wins (stable order).
|
|
61
|
+
* Returns null if nothing can handle the task.
|
|
62
|
+
*/
|
|
63
|
+
export declare function pickWorker(workers: SousChef[], task: SousChefTask, ctx: WorkerContext): SousChef | null;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sous-chef layer (v0.3.2) — makes local and remote workers interchangeable
|
|
3
|
+
* behind one interface, with a privacy gate and cheapest-capable-first routing.
|
|
4
|
+
*
|
|
5
|
+
* Design doc: docs/v0.3.2-remote-souschef-design.md
|
|
6
|
+
*
|
|
7
|
+
* The privacy gate is the load-bearing rule: a REMOTE free-tier worker may only
|
|
8
|
+
* receive data the user has accepted leaving the machine. By default that means
|
|
9
|
+
* decomposition only (input is the task description, not code). A per-project
|
|
10
|
+
* `allowRemoteCode` opt-in lets consenting users route code-bearing tasks too.
|
|
11
|
+
*
|
|
12
|
+
* Routing is cheapest-capable-first: local before remote. A remote worker is
|
|
13
|
+
* only chosen when (a) the privacy gate permits the task, (b) no local worker
|
|
14
|
+
* can handle it, and (c) the worker's own canHandle() says yes (e.g. has quota).
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The privacy gate. Which tasks may a REMOTE worker receive?
|
|
18
|
+
* - decomposition: always (input is the user's task description, no code)
|
|
19
|
+
* - everything else: only if the project opted into allowRemoteCode
|
|
20
|
+
*
|
|
21
|
+
* Local workers are never gated by this — code never leaves the machine.
|
|
22
|
+
*/
|
|
23
|
+
export function isRemoteAllowedForTask(task, allowRemoteCode) {
|
|
24
|
+
if (task === 'decomposition')
|
|
25
|
+
return true;
|
|
26
|
+
return allowRemoteCode;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Can this specific worker handle this task, accounting for privacy AND the
|
|
30
|
+
* worker's own availability veto?
|
|
31
|
+
*/
|
|
32
|
+
export function canWorkerHandle(worker, task, ctx) {
|
|
33
|
+
if (worker.kind === 'remote' && !isRemoteAllowedForTask(task, ctx.allowRemoteCode)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return worker.canHandle(task, ctx);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Pick the cheapest capable worker. Local workers are cheaper than remote, so
|
|
40
|
+
* they're preferred; within a kind, the first in the list wins (stable order).
|
|
41
|
+
* Returns null if nothing can handle the task.
|
|
42
|
+
*/
|
|
43
|
+
export function pickWorker(workers, task, ctx) {
|
|
44
|
+
const capable = workers.filter(w => canWorkerHandle(w, task, ctx));
|
|
45
|
+
if (capable.length === 0)
|
|
46
|
+
return null;
|
|
47
|
+
// local (cheaper) before remote; preserve input order within a kind.
|
|
48
|
+
const localFirst = [...capable].sort((a, b) => {
|
|
49
|
+
if (a.kind === b.kind)
|
|
50
|
+
return 0;
|
|
51
|
+
return a.kind === 'local' ? -1 : 1;
|
|
52
|
+
});
|
|
53
|
+
return localFirst[0];
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=souschef.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"souschef.js","sourceRoot":"","sources":["../../../src/core/souschef.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiCH;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAkB,EAClB,eAAwB;IAExB,IAAI,IAAI,KAAK,eAAe;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAgB,EAChB,IAAkB,EAClB,GAAkB;IAElB,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,sBAAsB,CAAC,IAAI,EAAE,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;QACnF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,OAAmB,EACnB,IAAkB,EAClB,GAAkB;IAElB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACnE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,qEAAqE;IACrE,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC5C,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;YAAE,OAAO,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IACH,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC"}
|
package/dist/src/core/state.d.ts
CHANGED
|
@@ -4,8 +4,20 @@
|
|
|
4
4
|
* State is intentionally readable JSON. Users can `cat` it to see exactly
|
|
5
5
|
* what helpcode is thinking. No black-box memory.
|
|
6
6
|
*/
|
|
7
|
-
import { AgentState, CurrentTask } from '../types.js';
|
|
7
|
+
import { AgentState, CurrentTask, SousChefEvent } from '../types.js';
|
|
8
8
|
export declare function loadState(cwd?: string): AgentState;
|
|
9
9
|
export declare function saveState(state: AgentState, cwd?: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Append a sous-chef event to an in-memory state object (capped). Use this
|
|
12
|
+
* when the caller already holds a loaded state and will saveState() itself —
|
|
13
|
+
* avoids the read-modify-write clobber of two independent load/save cycles.
|
|
14
|
+
*/
|
|
15
|
+
export declare function appendSousChefEvent(state: AgentState, event: SousChefEvent): void;
|
|
16
|
+
/**
|
|
17
|
+
* Append a sous-chef event and persist immediately. Use this only when the
|
|
18
|
+
* caller does NOT already hold a state it will save (standalone recording).
|
|
19
|
+
* If you have a loaded state, prefer appendSousChefEvent + your own saveState.
|
|
20
|
+
*/
|
|
21
|
+
export declare function recordSousChefEvent(event: SousChefEvent, cwd?: string): void;
|
|
10
22
|
export declare function createTask(description: string): CurrentTask;
|
|
11
23
|
export declare function resetState(cwd?: string): void;
|
package/dist/src/core/state.js
CHANGED
|
@@ -9,11 +9,14 @@ import * as path from 'path';
|
|
|
9
9
|
import { HelpcodeError, ErrorCode } from '../lib/errors.js';
|
|
10
10
|
const STATE_DIR = '.helpcode';
|
|
11
11
|
const STATE_FILE = path.join(STATE_DIR, 'state.json');
|
|
12
|
+
/** Keep the sous-chef log bounded so state.json stays small. */
|
|
13
|
+
const MAX_SOUSCHEF_EVENTS = 50;
|
|
12
14
|
function emptyState() {
|
|
13
15
|
return {
|
|
14
|
-
version:
|
|
16
|
+
version: 2,
|
|
15
17
|
currentTask: null,
|
|
16
18
|
history: [],
|
|
19
|
+
sousChefLog: [],
|
|
17
20
|
};
|
|
18
21
|
}
|
|
19
22
|
export function loadState(cwd = process.cwd()) {
|
|
@@ -23,10 +26,7 @@ export function loadState(cwd = process.cwd()) {
|
|
|
23
26
|
}
|
|
24
27
|
try {
|
|
25
28
|
const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
26
|
-
|
|
27
|
-
throw new HelpcodeError(ErrorCode.STATE_ERROR, `Unknown state version: ${parsed.version}`, 'This state was written by a different helpcode version. Run `helpcode reset` to start fresh.');
|
|
28
|
-
}
|
|
29
|
-
return parsed;
|
|
29
|
+
return migrate(parsed);
|
|
30
30
|
}
|
|
31
31
|
catch (e) {
|
|
32
32
|
if (e instanceof HelpcodeError)
|
|
@@ -34,6 +34,27 @@ export function loadState(cwd = process.cwd()) {
|
|
|
34
34
|
throw new HelpcodeError(ErrorCode.STATE_ERROR, `Could not parse state file: ${e.message}`, 'Run `helpcode reset` to start fresh (this won\'t touch your code).');
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Migrate older state forward. v1 had no sousChefLog; we add an empty one and
|
|
39
|
+
* bump the version rather than erroring, so existing installs keep working.
|
|
40
|
+
*/
|
|
41
|
+
function migrate(parsed) {
|
|
42
|
+
if (parsed.version === 2) {
|
|
43
|
+
// Defensive: ensure the log array exists even if hand-edited away.
|
|
44
|
+
if (!Array.isArray(parsed.sousChefLog))
|
|
45
|
+
parsed.sousChefLog = [];
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
if (parsed.version === 1) {
|
|
49
|
+
return {
|
|
50
|
+
version: 2,
|
|
51
|
+
currentTask: parsed.currentTask ?? null,
|
|
52
|
+
history: Array.isArray(parsed.history) ? parsed.history : [],
|
|
53
|
+
sousChefLog: [],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
throw new HelpcodeError(ErrorCode.STATE_ERROR, `Unknown state version: ${parsed.version}`, 'This state was written by a newer helpcode. Upgrade, or run `helpcode reset`.');
|
|
57
|
+
}
|
|
37
58
|
export function saveState(state, cwd = process.cwd()) {
|
|
38
59
|
const dir = path.join(cwd, STATE_DIR);
|
|
39
60
|
if (!fs.existsSync(dir))
|
|
@@ -43,6 +64,27 @@ export function saveState(state, cwd = process.cwd()) {
|
|
|
43
64
|
fs.writeFileSync(tmp, JSON.stringify(state, null, 2), 'utf-8');
|
|
44
65
|
fs.renameSync(tmp, file);
|
|
45
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Append a sous-chef event to an in-memory state object (capped). Use this
|
|
69
|
+
* when the caller already holds a loaded state and will saveState() itself —
|
|
70
|
+
* avoids the read-modify-write clobber of two independent load/save cycles.
|
|
71
|
+
*/
|
|
72
|
+
export function appendSousChefEvent(state, event) {
|
|
73
|
+
state.sousChefLog.push(event);
|
|
74
|
+
if (state.sousChefLog.length > MAX_SOUSCHEF_EVENTS) {
|
|
75
|
+
state.sousChefLog = state.sousChefLog.slice(-MAX_SOUSCHEF_EVENTS);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Append a sous-chef event and persist immediately. Use this only when the
|
|
80
|
+
* caller does NOT already hold a state it will save (standalone recording).
|
|
81
|
+
* If you have a loaded state, prefer appendSousChefEvent + your own saveState.
|
|
82
|
+
*/
|
|
83
|
+
export function recordSousChefEvent(event, cwd = process.cwd()) {
|
|
84
|
+
const state = loadState(cwd);
|
|
85
|
+
appendSousChefEvent(state, event);
|
|
86
|
+
saveState(state, cwd);
|
|
87
|
+
}
|
|
46
88
|
export function createTask(description) {
|
|
47
89
|
const now = new Date().toISOString();
|
|
48
90
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/core/state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE5D,MAAM,SAAS,GAAG,WAAW,CAAC;AAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAEtD,SAAS,UAAU;IACjB,OAAO;QACL,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/core/state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE5D,MAAM,SAAS,GAAG,WAAW,CAAC;AAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAEtD,gEAAgE;AAChE,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,SAAS,UAAU;IACjB,OAAO;QACL,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;KAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,UAAU,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QAC1D,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,aAAa;YAAE,MAAM,CAAC,CAAC;QACxC,MAAM,IAAI,aAAa,CACrB,SAAS,CAAC,WAAW,EACrB,+BAAgC,CAAW,CAAC,OAAO,EAAE,EACrD,oEAAoE,CACrE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,OAAO,CAAC,MAAW;IAC1B,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QACzB,mEAAmE;QACnE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC;YAAE,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC;QAChE,OAAO,MAAoB,CAAC;IAC9B,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,IAAI;YACvC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YAC5D,WAAW,EAAE,EAAE;SAChB,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,aAAa,CACrB,SAAS,CAAC,WAAW,EACrB,0BAA0B,MAAM,CAAC,OAAO,EAAE,EAC1C,+EAA+E,CAChF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAiB,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IACtE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;IACjE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/D,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAiB,EAAE,KAAoB;IACzE,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QACnD,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,mBAAmB,CAAC,CAAC;IACpE,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAoB,EACpB,MAAc,OAAO,CAAC,GAAG,EAAE;IAE3B,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAClC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,WAAmB;IAC5C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO;QACL,EAAE,EAAE,QAAQ,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;QACvC,WAAW;QACX,SAAS,EAAE,GAAG;QACd,UAAU,EAAE,CAAC;QACb,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,IAAI;QAChB,eAAe,EAAE,IAAI;QACrB,cAAc,EAAE,IAAI;QACpB,gBAAgB,EAAE,EAAE;KACrB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IACpD,SAAS,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -16,8 +16,10 @@ import { OllamaSettings } from '../types.js';
|
|
|
16
16
|
export interface TriageResult {
|
|
17
17
|
/** The compacted output to put in the brief. */
|
|
18
18
|
text: string;
|
|
19
|
-
/** True if
|
|
19
|
+
/** True if a model (local or remote) produced the summary; false if fallback. */
|
|
20
20
|
triaged: boolean;
|
|
21
|
+
/** True if the remote model produced it (for the cockpit). */
|
|
22
|
+
remote?: boolean;
|
|
21
23
|
}
|
|
22
24
|
type GenerateImpl = (host: string, model: string, prompt: string, opts?: {
|
|
23
25
|
timeoutMs?: number;
|
|
@@ -25,6 +27,12 @@ type GenerateImpl = (host: string, model: string, prompt: string, opts?: {
|
|
|
25
27
|
export interface TriageDeps {
|
|
26
28
|
/** Injectable model call for testing. Defaults to the real Ollama client. */
|
|
27
29
|
generateImpl?: GenerateImpl;
|
|
30
|
+
/**
|
|
31
|
+
* Optional remote-model fallback (prompt → text). Supplied by the caller ONLY
|
|
32
|
+
* when allowRemoteCode is on and a key is present (triage output is
|
|
33
|
+
* code-bearing). Tried after local fails, before deterministic truncation.
|
|
34
|
+
*/
|
|
35
|
+
remoteGenerate?: (prompt: string) => Promise<string>;
|
|
28
36
|
}
|
|
29
37
|
/** Is this output worth sending to the model? Long, and Ollama enabled. */
|
|
30
38
|
export declare function shouldTriage(output: string, ollama: Pick<OllamaSettings, 'enabled'>): boolean;
|
|
@@ -32,6 +40,11 @@ export declare function buildTriagePrompt(output: string): string;
|
|
|
32
40
|
/**
|
|
33
41
|
* Triage command output. Returns a compacted version plus whether the model
|
|
34
42
|
* was used. Never throws — any failure falls back to deterministic truncation.
|
|
43
|
+
*
|
|
44
|
+
* Order: local model → remote model (if provided; privacy-gated by the caller)
|
|
45
|
+
* → deterministic truncation. `deps.remoteGenerate` is supplied by the caller
|
|
46
|
+
* only when allowRemoteCode is on and a key is present — triage output can
|
|
47
|
+
* contain code, so it's code-bearing.
|
|
35
48
|
*/
|
|
36
49
|
export declare function triageOutput(output: string, ollama: OllamaSettings, deps?: TriageDeps): Promise<TriageResult>;
|
|
37
50
|
export {};
|
package/dist/src/core/triage.js
CHANGED
|
@@ -41,6 +41,11 @@ export function buildTriagePrompt(output) {
|
|
|
41
41
|
/**
|
|
42
42
|
* Triage command output. Returns a compacted version plus whether the model
|
|
43
43
|
* was used. Never throws — any failure falls back to deterministic truncation.
|
|
44
|
+
*
|
|
45
|
+
* Order: local model → remote model (if provided; privacy-gated by the caller)
|
|
46
|
+
* → deterministic truncation. `deps.remoteGenerate` is supplied by the caller
|
|
47
|
+
* only when allowRemoteCode is on and a key is present — triage output can
|
|
48
|
+
* contain code, so it's code-bearing.
|
|
44
49
|
*/
|
|
45
50
|
export async function triageOutput(output, ollama, deps = {}) {
|
|
46
51
|
// Short output, or triage disabled: return as-is or truncate, no model.
|
|
@@ -51,19 +56,32 @@ export async function triageOutput(output, ollama, deps = {}) {
|
|
|
51
56
|
: output;
|
|
52
57
|
return { text, triaged: false };
|
|
53
58
|
}
|
|
59
|
+
const prompt = buildTriagePrompt(output);
|
|
60
|
+
// 1. Local model (cheapest).
|
|
54
61
|
const generateImpl = deps.generateImpl ?? generate;
|
|
55
62
|
try {
|
|
56
|
-
const summary = await generateImpl(ollama.host, ollama.model,
|
|
63
|
+
const summary = await generateImpl(ollama.host, ollama.model, prompt, { timeoutMs: ollama.timeoutMs });
|
|
57
64
|
if (summary && summary.trim().length > 0) {
|
|
58
65
|
return { text: summary.trim(), triaged: true };
|
|
59
66
|
}
|
|
60
|
-
// Empty model result: fall back.
|
|
61
|
-
return { text: truncateLines(output, FALLBACK_MAX_LINES, 'lines'), triaged: false };
|
|
62
67
|
}
|
|
63
68
|
catch (e) {
|
|
64
|
-
// Transport/timeout/etc.: fall back to deterministic truncation.
|
|
65
69
|
void (e instanceof OllamaError);
|
|
66
|
-
|
|
70
|
+
// fall through to remote / deterministic
|
|
71
|
+
}
|
|
72
|
+
// 2. Remote model, if the caller provided one (privacy-gated upstream).
|
|
73
|
+
if (deps.remoteGenerate) {
|
|
74
|
+
try {
|
|
75
|
+
const summary = await deps.remoteGenerate(prompt);
|
|
76
|
+
if (summary && summary.trim().length > 0) {
|
|
77
|
+
return { text: summary.trim(), triaged: true, remote: true };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// fall through to deterministic
|
|
82
|
+
}
|
|
67
83
|
}
|
|
84
|
+
// 3. Deterministic truncation — always works.
|
|
85
|
+
return { text: truncateLines(output, FALLBACK_MAX_LINES, 'lines'), triaged: false };
|
|
68
86
|
}
|
|
69
87
|
//# sourceMappingURL=triage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triage.js","sourceRoot":"","sources":["../../../src/core/triage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,iEAAiE;AACjE,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAClC,6CAA6C;AAC7C,MAAM,kBAAkB,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"triage.js","sourceRoot":"","sources":["../../../src/core/triage.ts"],"names":[],"mappings":"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"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* helpcode — CLI router.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* init
|
|
6
|
-
* ask
|
|
7
|
-
* apply
|
|
8
|
-
* run
|
|
9
|
-
* status
|
|
10
|
-
*
|
|
4
|
+
* Commands, kept deliberately small:
|
|
5
|
+
* init detect project and set up .helpcode/
|
|
6
|
+
* ask compose a prompt for Claude.ai
|
|
7
|
+
* apply apply Claude's reply
|
|
8
|
+
* run run a shell command, get compact output
|
|
9
|
+
* status show current state
|
|
10
|
+
* cockpit show the sous-chef kitchen this session (v0.3)
|
|
11
|
+
* reset clear state
|
|
11
12
|
*/
|
|
12
13
|
export declare function run(argv: string[]): Promise<number>;
|
package/dist/src/index.js
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* helpcode — CLI router.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* init
|
|
6
|
-
* ask
|
|
7
|
-
* apply
|
|
8
|
-
* run
|
|
9
|
-
* status
|
|
10
|
-
*
|
|
4
|
+
* Commands, kept deliberately small:
|
|
5
|
+
* init detect project and set up .helpcode/
|
|
6
|
+
* ask compose a prompt for Claude.ai
|
|
7
|
+
* apply apply Claude's reply
|
|
8
|
+
* run run a shell command, get compact output
|
|
9
|
+
* status show current state
|
|
10
|
+
* cockpit show the sous-chef kitchen this session (v0.3)
|
|
11
|
+
* reset clear state
|
|
11
12
|
*/
|
|
12
13
|
import { handleInit } from './commands/init.js';
|
|
13
14
|
import { handleAsk } from './commands/ask.js';
|
|
14
15
|
import { handleApply } from './commands/apply.js';
|
|
15
16
|
import { handleRun } from './commands/run.js';
|
|
16
17
|
import { handleStatus } from './commands/status.js';
|
|
18
|
+
import { handlePlan } from './commands/plan.js';
|
|
19
|
+
import { handleCockpit } from './commands/cockpit.js';
|
|
17
20
|
import { handleReset } from './commands/reset.js';
|
|
18
21
|
import { HelpcodeError } from './lib/errors.js';
|
|
19
22
|
import { c, log } from './lib/ui.js';
|
|
20
|
-
const VERSION = '0.
|
|
23
|
+
const VERSION = '0.3.5';
|
|
21
24
|
const HELP = `helpcode v${VERSION}
|
|
22
25
|
|
|
23
26
|
A local agent that makes Claude.ai conversations efficient enough to ship
|
|
@@ -32,9 +35,11 @@ COMMANDS:
|
|
|
32
35
|
--files <paths...> include specific files (skips auto-selection)
|
|
33
36
|
--no-llm force keyword selection even if Ollama is on
|
|
34
37
|
--explain-selection show why each file was chosen
|
|
38
|
+
plan "<task>" Propose an ordered breakdown of a big task (local model)
|
|
35
39
|
apply [--yes] [--dry-run] Apply Claude's pasted reply (from stdin)
|
|
36
40
|
run "<command>" Run a shell command, get compact output
|
|
37
41
|
status Show current task and state
|
|
42
|
+
cockpit [--html] [--out F] Show the kitchen; --html writes a visual cockpit
|
|
38
43
|
reset [--yes] Clear state (project config is untouched)
|
|
39
44
|
|
|
40
45
|
GLOBAL FLAGS:
|
|
@@ -66,6 +71,10 @@ export async function run(argv) {
|
|
|
66
71
|
explainSelection: rest.includes('--explain-selection'),
|
|
67
72
|
});
|
|
68
73
|
}
|
|
74
|
+
case 'plan': {
|
|
75
|
+
const task = rest.filter(a => !a.startsWith('--')).join(' ');
|
|
76
|
+
return await handlePlan(task);
|
|
77
|
+
}
|
|
69
78
|
case 'apply':
|
|
70
79
|
return await handleApply({
|
|
71
80
|
yes: rest.includes('--yes'),
|
|
@@ -78,6 +87,10 @@ export async function run(argv) {
|
|
|
78
87
|
}
|
|
79
88
|
case 'status':
|
|
80
89
|
return await handleStatus();
|
|
90
|
+
case 'cockpit': {
|
|
91
|
+
const out = extractFlagValues(rest, '--out')[0];
|
|
92
|
+
return await handleCockpit({ html: rest.includes('--html'), out });
|
|
93
|
+
}
|
|
81
94
|
case 'reset':
|
|
82
95
|
return await handleReset({ yes: rest.includes('--yes') });
|
|
83
96
|
default:
|