@pentoshi/clai 2.0.37 → 2.0.42

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/README.md CHANGED
@@ -182,40 +182,49 @@ export OLLAMA_HOST=http://localhost:11434
182
182
 
183
183
  ## REPL Commands
184
184
 
185
- | Command | Action |
186
- |-------------------------|-------------------------------------------------- |
187
- | `/ask` | Switch to ask mode |
188
- | `/agent` | Switch to agent mode |
189
- | `/model` | Open interactive model picker (type/↑/↓, Tab fills, Enter selects) |
190
- | `/model <name\|#>` | Switch model by name or number (e.g. `/model 2`) |
191
- | `/provider [name]` | Switch provider or open interactive picker |
192
- | `/use <provider>` | Alias for `/provider <name>` |
193
- | `/set <provider> [key]` | Store API key (masked input if key omitted) |
194
- | `/unset <provider>` | Remove stored key |
195
- | `/keys` | List configured providers, masked |
196
- | `/info [provider]` | Show details & usage plan for a provider |
197
- | `/variants [on|off|low|medium|high]` | Toggle model thinking/reasoning variants |
198
- | `/think` | Show hidden thinking from last response |
199
- | `/output [last|id|list]`| Toggle full saved tool output |
200
- | `/clear` | Clear conversation context |
201
- | `/new` | Save current session & start fresh |
202
- | `/history` | Browse & resume past sessions (interactive picker) |
203
- | `/save <name>` | Save current session |
204
- | `/reset` | Clear all saved history |
205
- | `/cwd <path>` | Change working directory |
206
- | `/allow <tool>` | Whitelist a tool for the session |
207
- | `/permissions [default\|allow-all]` | Toggle confirmation prompting (allow-all bypasses confirm gates except passwords) |
208
- | `/plan` | View the current session plan (also `Ctrl+P`) |
209
- | `/implement` | Approve the current plan and have clai execute it |
210
- | `/discard` | Discard the current plan so later messages ignore it |
211
- | `/scope add <targets>` | Add authorized pentest targets |
212
- | `/fallback [on|off]` | Try other configured providers after a failure |
213
- | `/update` | Check for updates |
214
- | `/exit` | Quit |
215
- | `/help` | List commands |
216
- | `Ctrl+C` | Abort current response (second Ctrl+C exits) |
217
- | `Ctrl+O` | Toggle full tool output (same keys on all OSes) |
218
- | `Ctrl+P` | View the current session plan |
185
+ | Command | Action |
186
+ |---------|--------|
187
+ | `/ask` | Switch to read-only ask mode (suggests commands/steps but does not execute them) |
188
+ | `/agent` | Switch to active agent mode (proposes plans, executes commands, installs tools) |
189
+ | `/model` | Open the interactive model picker to select a model (press Enter to open) |
190
+ | `/model <name\|#>` | Switch active model by model name or index number (e.g. `/model 2`) |
191
+ | `/provider [name]` | Switch LLM provider or open interactive provider picker |
192
+ | `/use <provider>` | Alias for `/provider <name>` |
193
+ | `/set <provider> [key]` | Store API key for a provider (prompts with masked input if key is omitted) |
194
+ | `/unset <provider>` | Remove stored API key for a provider |
195
+ | `/keys` | List configured providers with their API keys masked |
196
+ | `/info [provider]` | Show model parameters, token/thinking limits, and usage details for a provider |
197
+ | `/search [provider]` | Switch search provider (Brave, Tavily, etc.) or open interactive picker |
198
+ | `/search-provider [name]`| Alias for `/search` |
199
+ | `/mouse [on\|off]` | Enable (`on`) or disable (`off`) REPL mouse tracking for scrolling vs native copy-paste selection |
200
+ | `/variants [on\|off\|none\|minimal\|low\|medium\|high\|xhigh]` | Configure reasoning effort. Use `on`/`off` to toggle, or set reasoning effort level (opens picker if no arg) |
201
+ | `/reasoning [...]` | Alias for `/variants` |
202
+ | `/think` / `/thinking` | Print the full, hidden reasoning/thinking trace from the last LLM response |
203
+ | `/output [last\|id\|list]`| Toggle full tool output pager (`Ctrl+O`) or list saved output files |
204
+ | `/clear` | Clear the current conversation messages and active state in memory |
205
+ | `/new` / `/clean` | Save current session to history and start a fresh session with cleared context |
206
+ | `/history` | Open interactive session browser to search and restore past conversation sessions |
207
+ | `/save <name>` | Manually save the current session under a custom name |
208
+ | `/reset` | Clear all saved history sessions permanently |
209
+ | `/cwd <path>` | Change the current working directory for shell and tool executions |
210
+ | `/allow <tool>` | Whitelist a tool for the current session to run without prompting for confirmation |
211
+ | `/disallow <tool>` | Revoke session authorization for a tool |
212
+ | `/freeonly [on\|off]` | Toggle (`on` to restrict, `off` to permit) paid providers when automatic fallback is active |
213
+ | `/fallback [on\|off]` | Toggle (`on` to try other providers after a primary failure, `off` to fail immediately) |
214
+ | `/compact` | Compress session history to fit context window constraints |
215
+ | `/context` | Display estimated current context size in tokens |
216
+ | `/plan` | View the current session plan checklist (also `Ctrl+P`) |
217
+ | `/implement` | Approve the proposed plan and begin automated execution |
218
+ | `/discard` | Discard the current plan and return to normal conversational mode |
219
+ | `/scope [show\|clear\|new\|add <targets>]` | View or adjust authorized penetration testing target scopes |
220
+ | `/privacy [status\|clear-history\|clear-logs\|clear-artifacts\|clear-all\|on\|off]` | Toggle private mode (`on` disables all disk writing, `off` enables) or purge logs/history |
221
+ | `/permissions [default\|allow-all]` | Set confirmation prompt behavior (`allow-all` bypasses confirm prompts for mutating tasks except keys) |
222
+ | `/update` | Check for updates and upgrade clai |
223
+ | `/exit` / `/quit` | Quit and close the REPL |
224
+ | `/help` | Print this command reference guide |
225
+ | `Ctrl+C` | Abort current response (second Ctrl+C exits) |
226
+ | `Ctrl+O` | Toggle full tool output (same keys on all OSes) |
227
+ | `Ctrl+P` | View the current session plan |
219
228
 
220
229
  ### Plan → Implement workflow
221
230
 
@@ -59,4 +59,9 @@ export type AgentEvent = {
59
59
  } | {
60
60
  type: "turn-error";
61
61
  message: string;
62
+ } | {
63
+ type: "compacted";
64
+ summary: string;
65
+ beforeTokens: number;
66
+ afterTokens: number;
62
67
  };
@@ -17,9 +17,15 @@ export function planContextMessage(plan, approved) {
17
17
  lines.push(` ${i + 1}. [${t.id}] (${t.state}) ${t.title}`);
18
18
  });
19
19
  if (approved) {
20
+ const inProgress = plan.tasks.find((t) => t.state === "in_progress");
20
21
  const firstPending = plan.tasks.find((t) => t.state === "pending");
21
22
  lines.push("The user APPROVED this plan. Execute it task by task NOW.");
22
- if (firstPending) {
23
+ if (inProgress) {
24
+ lines.push(`RESUME TASK ${inProgress.id} (${inProgress.title}) — it was started but interrupted. ` +
25
+ "Retry what was in progress; do NOT restart completed work from scratch. " +
26
+ "Do NOT re-do tasks already marked done, and do NOT skip ahead to later tasks.");
27
+ }
28
+ else if (firstPending) {
23
29
  lines.push(`START WITH TASK ${firstPending.id} (${firstPending.title}). ` +
24
30
  "Do NOT re-do tasks already marked done, and do NOT skip ahead to later tasks.");
25
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plan-tool.js","sourceRoot":"","sources":["../../src/agent/plan-tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plan-tool.js","sourceRoot":"","sources":["../../src/agent/plan-tool.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,16 @@
1
1
  import chalk from "chalk";
2
2
  import { homedir } from "node:os";
3
- import { join, resolve } from "node:path";
3
+ import { join, relative, resolve } from "node:path";
4
4
  import { streamWithProvider, completeWithProvider } from "../llm/router.js";
5
5
  import { randomUUID } from "node:crypto";
6
6
  import { jobManager } from "../tools/jobs.js";
7
- import { renderAgentSystemPrompt, } from "../prompts/index.js";
7
+ import { renderAgentSystemPrompt, scratchDirFor, } from "../prompts/index.js";
8
8
  import { getConfig } from "../store/config.js";
9
9
  import { classifyToolCall, isPentestToolCall, scopeHint, scopeTargetForToolCall, } from "../safety/classifier.js";
10
10
  import { availableToolNames, normalizeToolCall, runToolCall, BATCH_SAFE_TOOLS, } from "../tools/registry.js";
11
11
  import { looksInteractiveStdin } from "../tools/shell.js";
12
12
  import { formatViewportHint, registerViewport } from "../ui/output-pane.js";
13
- import { compactMessagesWithSummary, estimateMessagesTokens, } from "./context-manager.js";
13
+ import { compactMessagesWithSummary, estimateMessagesTokens, COMPACTION_MEMORY_PREFIX, } from "./context-manager.js";
14
14
  import { auditLog } from "../store/logs.js";
15
15
  import { loadProjectContext } from "../store/project.js";
16
16
  import { loadScope, isScopeActive, targetInScope } from "../store/scope.js";
@@ -23,7 +23,7 @@ import { analyzeTask } from "./task-analyzer.js";
23
23
  import { LoopGuard } from "./loop-guard.js";
24
24
  import { loadPlan } from "../store/plan.js";
25
25
  import { pathInsideSandbox } from "../tools/fs.js";
26
- import { stripSentinelTokens, parseToolCall, recognizeBareToolJson, looksLikeTruncatedToolCall, countToolFences, parseAllToolCalls, groupToolCallsForExecution, buildTurnHistory, collapseRepeatedText, textBeforeToolCall, formatToolArgs, looksLikePentestTask, looksLikeBuildTask, looksLikeInformationalQuery, looksLikeActionNarration, looksLikePlanNarration, requiresFreshWebSearch, freshnessGuardMessage, buildWorkflowDirective, shouldDimToolChatter, looksLikePromptLeak, } from "./tool-call-parser.js";
26
+ import { stripSentinelTokens, parseToolCall, recognizeBareToolJson, looksLikeTruncatedToolCall, countToolFences, parseAllToolCalls, groupToolCallsForExecution, buildTurnHistory, collapseRepeatedText, textBeforeToolCall, formatToolArgs, looksLikePentestTask, looksLikeBuildTask, looksLikeInformationalQuery, looksLikeActionNarration, looksLikePlanNarration, requiresFreshWebSearch, freshnessGuardMessage, buildWorkflowDirective, pentestWorkflowDirective, shouldDimToolChatter, looksLikePromptLeak, } from "./tool-call-parser.js";
27
27
  import { createSessionPolicy, isPreApprovalAllowedTool, isPlanApprovedByStatus, planHasOpenWork, isAbortError, shouldEnableImageOcr, } from "./session-policy.js";
28
28
  import { saveToolOutput, summarizeOutput, formatToolContext, } from "./tool-output-formatting.js";
29
29
  import { planContextMessage, handlePlanTool, } from "./plan-tool.js";
@@ -38,6 +38,78 @@ export { createSessionPolicy, isPreApprovalAllowedTool, isPlanApprovedByStatus,
38
38
  export function styleToolChatter(call, text) {
39
39
  return shouldDimToolChatter(call) ? chalk.dim(text) : text;
40
40
  }
41
+ /**
42
+ * Tool names that may write into the project tree. Writes restricted to the
43
+ * per-project scratch directory (under tmpdir()/clai/<name>) are exempted
44
+ * from the active-plan and plan-approved gates so the model can use scratch
45
+ * space to build / inspect / stage work without first creating a plan.
46
+ */
47
+ const SCRATCH_WRITABLE_TOOLS = new Set([
48
+ "fs.write",
49
+ "fs.writeMany",
50
+ "fs.edit",
51
+ "fs.replaceLines",
52
+ "fs.append",
53
+ "fs.delete",
54
+ ]);
55
+ /**
56
+ * Expand `~` the same way `src/tools/fs.ts` does so callers can compare an
57
+ * already-expanded scratch path against paths supplied by the model.
58
+ */
59
+ function expandHomeLocal(path) {
60
+ if (path === "~")
61
+ return homedir();
62
+ if (path.startsWith("~/") || path.startsWith("~\\")) {
63
+ return resolve(homedir(), path.slice(2));
64
+ }
65
+ return path;
66
+ }
67
+ /**
68
+ * Extract the target path(s) a write-shaped tool call would touch. Returns
69
+ * an empty array when the call has no resolvable path (so the caller can
70
+ * treat it as NOT scratch-only and fall through to the normal gates).
71
+ */
72
+ function scratchWriteTargetPaths(call) {
73
+ if (call.name === "fs.writeMany") {
74
+ const files = call.args.files;
75
+ if (!Array.isArray(files))
76
+ return [];
77
+ const paths = [];
78
+ for (const entry of files) {
79
+ if (entry && typeof entry === "object" && "path" in entry) {
80
+ const p = entry.path;
81
+ if (typeof p === "string" && p.length > 0)
82
+ paths.push(p);
83
+ }
84
+ }
85
+ return paths;
86
+ }
87
+ const pathArg = call.args.path;
88
+ if (typeof pathArg !== "string" || pathArg.length === 0)
89
+ return [];
90
+ return [pathArg];
91
+ }
92
+ /**
93
+ * True iff every target path this call would write is inside the resolved
94
+ * scratch directory. A path is considered inside when its `path.relative`
95
+ * against the scratch root is empty (the scratch root itself) or does not
96
+ * start with `..` (no parent traversal). Calls without a recognizable
97
+ * target path return false so they fall through to the normal gates.
98
+ */
99
+ function isScratchOnlyWrite(call, scratchDir) {
100
+ if (!SCRATCH_WRITABLE_TOOLS.has(call.name))
101
+ return false;
102
+ const paths = scratchWriteTargetPaths(call);
103
+ if (paths.length === 0)
104
+ return false;
105
+ const resolvedScratch = resolve(scratchDir);
106
+ return paths.every((raw) => {
107
+ const expanded = expandHomeLocal(raw);
108
+ const resolved = resolve(expanded);
109
+ const rel = relative(resolvedScratch, resolved);
110
+ return rel === "" || (!rel.startsWith("..") && rel !== "..");
111
+ });
112
+ }
41
113
  export async function runAgentLoop(prompt, options = {}) {
42
114
  const writesDirectly = !options.onEvent;
43
115
  const emit = (event) => options.onEvent?.(event);
@@ -114,6 +186,22 @@ export async function runAgentLoop(prompt, options = {}) {
114
186
  }
115
187
  emit(event);
116
188
  };
189
+ /** Strip a known prefix from a string, returning the remainder unchanged. */
190
+ const insertedText = (value, prefix) => value.startsWith(prefix) ? value.slice(prefix.length) : value;
191
+ /**
192
+ * Surface the compacted-context summary to both the TUI (via an event)
193
+ * and, when running with a direct stdout writer, as a rendered box.
194
+ * Token-count stats are always emitted for logs.
195
+ */
196
+ const writeCompacted = (summary, beforeTokens, afterTokens) => {
197
+ emit({ type: "compacted", summary, beforeTokens, afterTokens });
198
+ if (writesDirectly) {
199
+ const header = chalk.dim(" \u2726 Compacted Context");
200
+ const footer = chalk.dim(` ~${beforeTokens.toLocaleString()} \u2192 ~${afterTokens.toLocaleString()} tokens`);
201
+ const body = summary ? renderMarkdown(summary) : "(empty summary)";
202
+ process.stdout.write(`${header}\n\n${body}\n${footer}\n`);
203
+ }
204
+ };
117
205
  // Points at the live message array so finishTurn can hand the full
118
206
  // conversation back to the caller. Assigned once `messages` is built below;
119
207
  // all later mutations are in-place so this reference stays current.
@@ -199,6 +287,15 @@ export async function runAgentLoop(prompt, options = {}) {
199
287
  if (buildLikeTurn && !activePlan && !informationalQuery) {
200
288
  systemSections.push(buildWorkflowDirective());
201
289
  }
290
+ // Pentest / security engagements need a different shape than a coding
291
+ // build: recon first, then a plan built from real findings, then
292
+ // incremental task additions as new attack surface appears. The
293
+ // directive is only injected before a plan exists; once a plan is in
294
+ // place (or being refined), the ACTIVE PLAN block already carries the
295
+ // current task state and recon-vs-active-tool guidance.
296
+ if (pentestLikeTurn && !activePlan && !informationalQuery) {
297
+ systemSections.push(pentestWorkflowDirective());
298
+ }
202
299
  const fullSystemPrompt = systemSections.join("\n\n");
203
300
  const userMessage = { role: "user", content: prompt };
204
301
  if (options.images && options.images.length > 0) {
@@ -321,6 +418,9 @@ export async function runAgentLoop(prompt, options = {}) {
321
418
  },
322
419
  };
323
420
  async function executeSingleTool(rawCall, toolEventId, parentSignal) {
421
+ // Resolved once per call so the scratch-only exemption can compare the
422
+ // model-supplied paths against the canonical per-project scratch root.
423
+ const scratchDir = scratchDirFor(safeCwd());
324
424
  let call = normalizeToolCall(rawCall);
325
425
  if (call.name === "image.ocr" && !imageOcrEnabled) {
326
426
  writeNotice("info", "skipped OCR because the original image is attached to the vision model", chalk.dim(" ℹ skipped OCR — inspecting the attached image directly\n"));
@@ -385,21 +485,10 @@ export async function runAgentLoop(prompt, options = {}) {
385
485
  scope: isScopeActive(scope) ? (scope.name ?? "(unnamed)") : "(none)",
386
486
  });
387
487
  const isMutatingAction = (decision.level === "confirm" || decision.level === "block") &&
388
- !isPreApprovalAllowedTool(call.name);
488
+ !isPreApprovalAllowedTool(call.name) &&
489
+ !isScratchOnlyWrite(call, scratchDir);
389
490
  if (isMutatingAction) {
390
- if (!activePlan) {
391
- const reason = `No active plan — ${call.name} is blocked. You must first create a plan using plan.create before executing mutating actions.`;
392
- writeNotice("warn", reason, chalk.yellow(` ⚠ ${reason}\n`));
393
- const result = { ok: false, output: reason, exitCode: 1 };
394
- return {
395
- ok: false,
396
- call,
397
- result,
398
- contextOutput: reason,
399
- blockOrCancel: true,
400
- };
401
- }
402
- if (!session.planApproved.value) {
491
+ if (activePlan && !session.planApproved.value) {
403
492
  const reason = `plan awaiting approval — ${call.name} is blocked until you /implement (or /discard)`;
404
493
  writeNotice("warn", reason, chalk.yellow(` ⚠ ${reason}\n`));
405
494
  const result = { ok: false, output: reason, exitCode: 1 };
@@ -721,7 +810,7 @@ export async function runAgentLoop(prompt, options = {}) {
721
810
  // what is done, and what remains. The estimate is chars/4; the budget is
722
811
  // deliberately conservative so we compact a little early rather than hit a
723
812
  // provider context-window error mid-task.
724
- const AUTO_COMPACT_TOKEN_BUDGET = 60_000;
813
+ const AUTO_COMPACT_TOKEN_BUDGET = 120_000;
725
814
  const AUTO_COMPACT_KEEP_RECENT = 12;
726
815
  let lastCompactionMsgCount = 0;
727
816
  const summarizeForCompaction = async (summaryPrompt) => {
@@ -773,6 +862,16 @@ export async function runAgentLoop(prompt, options = {}) {
773
862
  estimatedTokens: afterTokens,
774
863
  reason,
775
864
  });
865
+ // Extract the inserted compaction memory so we can surface the
866
+ // summary itself (not just token-count stats). The summary lives in
867
+ // the first system message whose content begins with
868
+ // COMPACTION_MEMORY_PREFIX.
869
+ const insertedSummary = messages.find((m) => m.role === "system" &&
870
+ m.content.startsWith(COMPACTION_MEMORY_PREFIX))?.content ?? "";
871
+ const summaryText = insertedSummary.startsWith(`${COMPACTION_MEMORY_PREFIX}\n\n`)
872
+ ? insertedText(insertedSummary, `${COMPACTION_MEMORY_PREFIX}\n\n`)
873
+ : insertedText(insertedSummary, COMPACTION_MEMORY_PREFIX);
874
+ writeCompacted(summaryText, beforeTokens, afterTokens);
776
875
  writeNotice("info", `context auto-compacted to fit the window (~${beforeTokens.toLocaleString()} → ~${afterTokens.toLocaleString()} tokens)`, chalk.dim(` ℹ context auto-compacted (~${beforeTokens.toLocaleString()} → ~${afterTokens.toLocaleString()} tokens)\n`));
777
876
  }
778
877
  catch (error) {
@@ -1136,9 +1235,10 @@ export async function runAgentLoop(prompt, options = {}) {
1136
1235
  messages.push({
1137
1236
  role: "user",
1138
1237
  content: "Your previous tool call was cut off before it finished — the JSON was incomplete, so NOTHING ran. " +
1139
- "Retry now with a COMPLETE, valid ```tool block. " +
1140
- "If it was fs.writeMany, split the FILE LIST into smaller batches. Do not shorten, truncate, or simplify file contents. " +
1141
- "For one large file, retry that file alone as one complete fs.write call. Do NOT claim any file was written until a tool call actually succeeds.",
1238
+ "Keep your reasoning SHORT and emit the ```tool block as early as possible. " +
1239
+ "If it was fs.writeMany, split the FILE LIST into smaller batches (3-5 files per call). " +
1240
+ "For one large file (e.g. a report), write it in sections: use fs.write for the first section, then shell.exec with 'cat >> file' or separate fs.write calls for remaining sections. " +
1241
+ "Do not shorten, truncate, or simplify file contents. Do NOT claim any file was written until a tool call actually succeeds.",
1142
1242
  });
1143
1243
  continue;
1144
1244
  }
@@ -1428,6 +1528,10 @@ export async function runAgentLoop(prompt, options = {}) {
1428
1528
  content: `Tool ${res.call.name} result (exit=${res.result.exitCode ?? 0}, ok=${res.result.ok}):\n${res.contextOutput}`,
1429
1529
  });
1430
1530
  productiveSteps += 1;
1531
+ // Reset retry counters — they track consecutive failures, not cumulative.
1532
+ truncatedToolRetries = 0;
1533
+ malformedFenceRetries = 0;
1534
+ bareToolJsonRetries = 0;
1431
1535
  if (res.ok && res.call.name === "shell.start")
1432
1536
  sawServerStart = true;
1433
1537
  if (res.ok && res.call.name === "shell.tail")