@pentoshi/clai 0.12.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/bin/clai.mjs +25 -0
  2. package/dist/agent/loop-guard.js +10 -2
  3. package/dist/agent/loop-guard.js.map +1 -1
  4. package/dist/agent/runner.d.ts +38 -1
  5. package/dist/agent/runner.js +516 -36
  6. package/dist/agent/runner.js.map +1 -1
  7. package/dist/commands/update.js +1 -1
  8. package/dist/commands/update.js.map +1 -1
  9. package/dist/llm/anthropic.js +31 -12
  10. package/dist/llm/anthropic.js.map +1 -1
  11. package/dist/llm/capabilities.d.ts +13 -0
  12. package/dist/llm/capabilities.js +107 -24
  13. package/dist/llm/capabilities.js.map +1 -1
  14. package/dist/llm/gemini.js +17 -4
  15. package/dist/llm/gemini.js.map +1 -1
  16. package/dist/llm/http.d.ts +12 -1
  17. package/dist/llm/http.js +50 -25
  18. package/dist/llm/http.js.map +1 -1
  19. package/dist/llm/ollama.js +16 -8
  20. package/dist/llm/ollama.js.map +1 -1
  21. package/dist/modes/agent.d.ts +2 -1
  22. package/dist/modes/agent.js.map +1 -1
  23. package/dist/modes/ask.d.ts +2 -1
  24. package/dist/modes/ask.js +5 -1
  25. package/dist/modes/ask.js.map +1 -1
  26. package/dist/os/cwd.d.ts +30 -0
  27. package/dist/os/cwd.js +76 -0
  28. package/dist/os/cwd.js.map +1 -0
  29. package/dist/os/detect.js +2 -1
  30. package/dist/os/detect.js.map +1 -1
  31. package/dist/prompts/index.d.ts +1 -1
  32. package/dist/prompts/index.js +95 -22
  33. package/dist/prompts/index.js.map +1 -1
  34. package/dist/repl.d.ts +10 -0
  35. package/dist/repl.js +258 -28
  36. package/dist/repl.js.map +1 -1
  37. package/dist/safety/classifier.js +147 -26
  38. package/dist/safety/classifier.js.map +1 -1
  39. package/dist/safety/patterns.d.ts +26 -0
  40. package/dist/safety/patterns.js +167 -0
  41. package/dist/safety/patterns.js.map +1 -1
  42. package/dist/store/config.js +2 -1
  43. package/dist/store/config.js.map +1 -1
  44. package/dist/store/history.js +19 -5
  45. package/dist/store/history.js.map +1 -1
  46. package/dist/store/plan.d.ts +43 -0
  47. package/dist/store/plan.js +201 -0
  48. package/dist/store/plan.js.map +1 -0
  49. package/dist/store/project.js +3 -2
  50. package/dist/store/project.js.map +1 -1
  51. package/dist/tools/capabilities.js +6 -1
  52. package/dist/tools/capabilities.js.map +1 -1
  53. package/dist/tools/fs.d.ts +15 -0
  54. package/dist/tools/fs.js +69 -3
  55. package/dist/tools/fs.js.map +1 -1
  56. package/dist/tools/image.d.ts +13 -0
  57. package/dist/tools/image.js +81 -0
  58. package/dist/tools/image.js.map +1 -0
  59. package/dist/tools/jobs.js +2 -1
  60. package/dist/tools/jobs.js.map +1 -1
  61. package/dist/tools/pdf.d.ts +18 -0
  62. package/dist/tools/pdf.js +200 -0
  63. package/dist/tools/pdf.js.map +1 -0
  64. package/dist/tools/registry.js +87 -7
  65. package/dist/tools/registry.js.map +1 -1
  66. package/dist/tools/shell.js +3 -2
  67. package/dist/tools/shell.js.map +1 -1
  68. package/dist/types.d.ts +16 -0
  69. package/dist/ui/keys.d.ts +1 -0
  70. package/dist/ui/keys.js +4 -0
  71. package/dist/ui/keys.js.map +1 -1
  72. package/dist/ui/mentions.d.ts +32 -1
  73. package/dist/ui/mentions.js +304 -27
  74. package/dist/ui/mentions.js.map +1 -1
  75. package/dist/ui/plan-pane.d.ts +19 -0
  76. package/dist/ui/plan-pane.js +101 -0
  77. package/dist/ui/plan-pane.js.map +1 -0
  78. package/package.json +6 -5
package/bin/clai.mjs CHANGED
@@ -1,2 +1,27 @@
1
1
  #!/usr/bin/env node
2
+ // Guard against a deleted / inaccessible working directory BEFORE importing
3
+ // anything from dist. If clai was launched (or elevated via `sudo`) from a
4
+ // folder that no longer exists, process.cwd() throws ENOENT (uv_cwd) and the
5
+ // whole CLI used to crash at module-load. Relocate to a directory that
6
+ // definitely exists so startup — and every later spawn — works.
7
+ try {
8
+ process.cwd();
9
+ } catch {
10
+ const candidates = [
11
+ process.env.HOME,
12
+ process.env.USERPROFILE,
13
+ process.env.TMPDIR,
14
+ "/tmp",
15
+ "/",
16
+ ].filter(Boolean);
17
+ for (const dir of candidates) {
18
+ try {
19
+ process.chdir(dir);
20
+ break;
21
+ } catch {
22
+ // try the next candidate
23
+ }
24
+ }
25
+ }
26
+
2
27
  await import('../dist/index.js');
@@ -56,16 +56,24 @@ export class LoopGuard {
56
56
  // Prior attempts all failed → allow the retry, no warning.
57
57
  if (this.signatureSuccess.get(sig) === false)
58
58
  return { block: false };
59
+ // Mutating file tools deserve tool-appropriate wording. Telling a model
60
+ // that just wrote a file to "use the results you already have" is
61
+ // nonsensical and has caused models to assume the whole task is done.
62
+ const isWrite = name === "fs.write" || name === "fs.writeMany" || name === "fs.edit";
59
63
  if (count === 1) {
60
64
  return {
61
65
  block: false,
62
- reason: `${name} has already been called with these arguments once and succeeded. Consider using the results you already have.`,
66
+ reason: isWrite
67
+ ? `${name} already wrote this exact path/content once. If that file is finished, move on to the NEXT file or step — do NOT rewrite it.`
68
+ : `${name} has already been called with these arguments once and succeeded. Consider using the results you already have.`,
63
69
  };
64
70
  }
65
71
  // count >= 2 and at least one success: block
66
72
  return {
67
73
  block: true,
68
- reason: `${name} was already called ${count} time(s) with the same arguments. Summarize existing results instead.`,
74
+ reason: isWrite
75
+ ? `${name} was already called ${count} time(s) with the identical path and content. That file is already written. Continue with the remaining files/steps or give your final answer.`
76
+ : `${name} was already called ${count} time(s) with the same arguments. Summarize existing results instead.`,
69
77
  };
70
78
  }
71
79
  getAttemptCount(name, args) {
@@ -1 +1 @@
1
- {"version":3,"file":"loop-guard.js","sourceRoot":"","sources":["../../src/agent/loop-guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loop-guard.js","sourceRoot":"","sources":["../../src/agent/loop-guard.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { ChatMessage, ProviderId, ToolCall, ToolResult } from "../types.js";
1
+ import type { ChatMessage, ChatImage, ProviderId, ToolCall, ToolResult } from "../types.js";
2
2
  export interface SessionPolicy {
3
3
  /** Tools the user authorized once during this REPL session. Not persisted. */
4
4
  allow: Set<string>;
@@ -6,6 +6,12 @@ export interface SessionPolicy {
6
6
  pentestAuthorized: {
7
7
  value: boolean;
8
8
  };
9
+ /** Stable id used to scope the session's plan/tasks in the plan store. */
10
+ sessionId: string;
11
+ /** When true, the agent must follow its approved plan (set by /implement). */
12
+ planApproved: {
13
+ value: boolean;
14
+ };
9
15
  }
10
16
  export declare function createSessionPolicy(): SessionPolicy;
11
17
  export interface AgentRunOptions {
@@ -15,6 +21,7 @@ export interface AgentRunOptions {
15
21
  autoConfirm?: boolean | undefined;
16
22
  maxSteps?: number | undefined;
17
23
  signal?: AbortSignal | undefined;
24
+ images?: ChatImage[] | undefined;
18
25
  onToolStart?: ((call: ToolCall) => void) | undefined;
19
26
  onToolResult?: ((call: ToolCall, result: ToolResult) => void) | undefined;
20
27
  session?: SessionPolicy | undefined;
@@ -30,6 +37,36 @@ export interface ParseToolCallOptions {
30
37
  strict?: boolean | undefined;
31
38
  }
32
39
  export declare function parseToolCall(text: string, options?: ParseToolCallOptions): ToolCall | undefined;
40
+ /**
41
+ * When a model means to call a tool but emits ONLY a bare JSON object —
42
+ * either a proper {"name","args"} that the strict matchers missed, or a bare
43
+ * args object like {"path":"file.pdf"} with the wrapper/fence dropped — this
44
+ * recognizes it. Returns:
45
+ * - { call } when the object is a complete {name, args} tool call, or
46
+ * - { argsOnly: true } when it looks like a bare args object (so the caller
47
+ * can nudge the model to re-emit a properly named, fenced tool call).
48
+ * Returns undefined for anything that is plainly a normal prose/JSON answer.
49
+ */
50
+ export declare function recognizeBareToolJson(text: string): {
51
+ call?: ToolCall;
52
+ argsOnly?: boolean;
53
+ } | undefined;
54
+ /**
55
+ * Detect an opened-but-unparseable tool call. This happens when the model's
56
+ * output is truncated by the token limit mid-JSON: we see the ```tool fence
57
+ * (or a bare {"name":"...","args" prefix) open, but parseToolCall returns
58
+ * undefined because the JSON never closed. Without this, the broken block
59
+ * leaks to the screen as a "final answer" and the requested action (e.g. a
60
+ * multi-file fs.writeMany scaffold) silently never runs.
61
+ */
62
+ export declare function looksLikeTruncatedToolCall(text: string): boolean;
63
+ /**
64
+ * Decide whether this turn should get a generous step budget because it is
65
+ * a multi-file build, a continuation of one, or a "it's not done yet" nudge.
66
+ * Looks at the current prompt first, then falls back to the most recent
67
+ * user/assistant turns so a terse follow-up inherits the build context.
68
+ */
69
+ export declare function looksLikeBuildTask(prompt: string, history?: ChatMessage[] | undefined): boolean;
33
70
  export declare function requiresFreshWebSearch(prompt: string): boolean;
34
71
  export declare function shouldDimToolChatter(call: ToolCall): boolean;
35
72
  export declare function runAgentLoop(prompt: string, options?: AgentRunOptions): Promise<string>;