@cydm/pie 1.0.37 → 1.0.39

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
@@ -309,6 +309,8 @@ Pie exposes one default model-visible web tool: `web_research`. Use it for publi
309
309
 
310
310
  Pie's default tools operate from the current workspace. File tools are workspace-scoped, `~/.pie` is allowlisted for Pie configuration/session support, and shell commands return structured timeout/truncation/error details. The canonical shell tool is still named `bash` for compatibility; on Windows `shell:auto` uses PowerShell, and callers can explicitly request `bash`, `powershell`, or `cmd`. High-risk shell commands require confirmation outside YOLO mode. `/settings/yolo` relaxes filesystem sandbox directory restrictions and should only be used in trusted workspaces. `pie permissions` prints the current policy-derived safety summary.
311
311
 
312
+ In interactive Plan mode (`/tools/plan`), workspace editing tools are unavailable. The `bash` guard accepts simple read commands and read-only pipelines such as `rg 'TODO' . | head -20`; it blocks redirects, command substitution, and write-capable subcommands. Use `plan_scratch` to write, read, or list temporary text drafts in a directory dedicated to the current session. Shell redirection to `/tmp` remains blocked in Plan mode. Leave Plan mode before changing workspace files.
313
+
312
314
  ## Code Intelligence
313
315
 
314
316
  `code_intel` provides read-only JS/TS diagnostics, definition, references, symbols, and hover. CLI uses an isolated TypeScript worker by default and falls back to bounded lightweight text analysis on worker startup/OOM/crash/timeout failures; aborts, permission errors, and path escapes do not fallback. Unity uses a Node sidecar contract for TypeScript intelligence so the PuerTS bundle does not import Node-only parser dependencies.
@@ -1,9 +1,9 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  createAskUserCapability
4
- } from "../../../chunks/chunk-MGZHZSHB.js";
5
- import "../../../chunks/chunk-4B42ZU4B.js";
6
- import "../../../chunks/chunk-64XJIKVK.js";
4
+ } from "../../../chunks/chunk-SNSSIR3E.js";
5
+ import "../../../chunks/chunk-7IFMKPHH.js";
6
+ import "../../../chunks/chunk-HPTWCKJE.js";
7
7
  import "../../../chunks/chunk-TG2EQLX2.js";
8
8
 
9
9
  // builtin/extensions/ask-user/index.ts
@@ -7,14 +7,23 @@ import {
7
7
  isPlanModeSafeCommand,
8
8
  markCompletedPlanSteps,
9
9
  restoreExecutionState
10
- } from "../../../chunks/chunk-MGZHZSHB.js";
11
- import "../../../chunks/chunk-4B42ZU4B.js";
12
- import "../../../chunks/chunk-64XJIKVK.js";
10
+ } from "../../../chunks/chunk-SNSSIR3E.js";
11
+ import "../../../chunks/chunk-7IFMKPHH.js";
12
+ import {
13
+ Type
14
+ } from "../../../chunks/chunk-HPTWCKJE.js";
13
15
  import "../../../chunks/chunk-TG2EQLX2.js";
14
16
 
15
17
  // builtin/extensions/plan-mode/index.ts
18
+ import { randomUUID } from "node:crypto";
19
+ import { promises as fs, constants as fsConstants } from "node:fs";
20
+ import os from "node:os";
21
+ import path from "node:path";
16
22
  var SESSION_METADATA_KEY = "planModeEnabled";
17
23
  var EXECUTION_METADATA_KEY = "executionState";
24
+ var SCRATCH_METADATA_KEY = "planScratchId";
25
+ var SCRATCH_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
26
+ var MAX_SCRATCH_CHARS = 64e3;
18
27
  var planModeEnabled = false;
19
28
  var executionMode = false;
20
29
  function toPlanTodoItems(state) {
@@ -26,6 +35,62 @@ function toPlanTodoItems(state) {
26
35
  }
27
36
  function planModeExtension(ctx) {
28
37
  const capability = createPlanCapability();
38
+ function scratchDirectory() {
39
+ const metadata = ctx.getSessionMetadata();
40
+ let id = metadata?.[SCRATCH_METADATA_KEY];
41
+ if (typeof id !== "string" || !/^[0-9a-f-]{36}$/.test(id)) {
42
+ id = randomUUID();
43
+ ctx.setSessionMetadata(SCRATCH_METADATA_KEY, id);
44
+ }
45
+ return path.join(os.tmpdir(), `pie-plan-${id}`);
46
+ }
47
+ ctx.registerTool({
48
+ name: "plan_scratch",
49
+ label: "plan_scratch",
50
+ description: "Read, write, or list session-local temporary text drafts while planning. Use a simple filename; files stay outside the workspace. Shell output redirection is blocked in Plan mode.",
51
+ parameters: Type.Object({
52
+ action: Type.Union([Type.Literal("write"), Type.Literal("read"), Type.Literal("list")]),
53
+ name: Type.Optional(Type.String({ description: "Simple filename within this session's scratch directory" })),
54
+ content: Type.Optional(Type.String({ description: "Text to write (maximum 64,000 characters)" }))
55
+ }),
56
+ risk: { riskClass: "session_mutation", permissionScope: "session", mutatesSession: true, availableInPlanMode: true },
57
+ async execute(args) {
58
+ const reply = (text, isError = false) => ({ content: [{ type: "text", text }], isError });
59
+ try {
60
+ const directory = scratchDirectory();
61
+ await fs.mkdir(directory, { recursive: true, mode: 448 });
62
+ if (!(await fs.lstat(directory)).isDirectory()) return reply("Temporary draft directory is invalid.", true);
63
+ if (args.action === "list") {
64
+ const names = (await fs.readdir(directory)).filter((name) => SCRATCH_NAME.test(name)).sort();
65
+ return reply(names.length ? names.join("\n") : "No temporary drafts in this session.");
66
+ }
67
+ if (!args.name || !SCRATCH_NAME.test(args.name) || args.name === "." || args.name === "..") {
68
+ return reply("Invalid draft filename. Use letters, digits, dots, underscores, or hyphens without directories.", true);
69
+ }
70
+ const filename = path.join(directory, args.name);
71
+ if (args.action === "read") {
72
+ const handle2 = await fs.open(filename, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW || 0));
73
+ try {
74
+ return reply(await handle2.readFile("utf8"));
75
+ } finally {
76
+ await handle2.close();
77
+ }
78
+ }
79
+ if (typeof args.content !== "string" || args.content.length > MAX_SCRATCH_CHARS) {
80
+ return reply(`Draft content must be text of at most ${MAX_SCRATCH_CHARS} characters.`, true);
81
+ }
82
+ const handle = await fs.open(filename, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_TRUNC | (fsConstants.O_NOFOLLOW || 0), 384);
83
+ try {
84
+ await handle.writeFile(args.content, "utf8");
85
+ } finally {
86
+ await handle.close();
87
+ }
88
+ return reply(`Saved ${args.name} (${args.content.length} characters).`);
89
+ } catch (error) {
90
+ return reply(`Temporary draft failed: ${error instanceof Error ? error.message : String(error)}`, true);
91
+ }
92
+ }
93
+ });
29
94
  function readPersistedPlanMode() {
30
95
  return ctx.getSessionMetadata()?.[SESSION_METADATA_KEY] === true;
31
96
  }
@@ -45,7 +110,7 @@ function planModeExtension(ctx) {
45
110
  const completed = state.steps.filter((step) => step.status === "completed").length;
46
111
  ctx.ui.notify(`\u{1F4CB} Plan: ${completed}/${state.steps.length} completed`, "info");
47
112
  } else if (planModeEnabled) {
48
- ctx.ui.notify("\u23F8\uFE0F Plan mode active (read-only)", "info");
113
+ ctx.ui.notify("\u23F8\uFE0F Plan mode active (workspace read-only; session drafts available)", "info");
49
114
  }
50
115
  }
51
116
  function applyPlanModeState(enabled, forcePromptInjection = false) {
@@ -84,11 +149,11 @@ function planModeExtension(ctx) {
84
149
  const command = event.args?.command;
85
150
  if (command && !isPlanModeSafeCommand(command)) {
86
151
  ctx.ui.notify(
87
- `Plan mode: Command blocked (not allowlisted). Use /plan to disable plan mode first.
152
+ `Plan mode: Command blocked. Use read-only commands, or plan_scratch for temporary drafts. Use /plan to leave Plan mode.
88
153
  Command: ${command}`,
89
154
  "error"
90
155
  );
91
- return { block: true, reason: `Plan mode: command "${command}" is not in the allowlist. Disable plan mode with /plan to run this command.` };
156
+ return { block: true, reason: `Plan mode: command "${command}" is not a read-only shell command. Use plan_scratch for temporary drafts, or /plan to leave Plan mode.` };
92
157
  }
93
158
  });
94
159
  ctx.on("agent:start", async () => {
@@ -1,13 +1,13 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  createCliHostCapabilities
4
- } from "../../../chunks/chunk-QXTAEPLR.js";
4
+ } from "../../../chunks/chunk-F6VAOGFL.js";
5
5
  import {
6
6
  createSharedFileSystemTools,
7
7
  createSubagentCapability
8
- } from "../../../chunks/chunk-MGZHZSHB.js";
9
- import "../../../chunks/chunk-4B42ZU4B.js";
10
- import "../../../chunks/chunk-64XJIKVK.js";
8
+ } from "../../../chunks/chunk-SNSSIR3E.js";
9
+ import "../../../chunks/chunk-7IFMKPHH.js";
10
+ import "../../../chunks/chunk-HPTWCKJE.js";
11
11
  import "../../../chunks/chunk-TG2EQLX2.js";
12
12
 
13
13
  // builtin/extensions/subagent/index.ts
@@ -7,9 +7,9 @@ import {
7
7
  executeManageTodoList,
8
8
  executionStateToTodos,
9
9
  restoreExecutionState
10
- } from "../../../chunks/chunk-MGZHZSHB.js";
11
- import "../../../chunks/chunk-4B42ZU4B.js";
12
- import "../../../chunks/chunk-64XJIKVK.js";
10
+ } from "../../../chunks/chunk-SNSSIR3E.js";
11
+ import "../../../chunks/chunk-7IFMKPHH.js";
12
+ import "../../../chunks/chunk-HPTWCKJE.js";
13
13
  import "../../../chunks/chunk-TG2EQLX2.js";
14
14
 
15
15
  // builtin/extensions/todo/index.ts
@@ -3,7 +3,7 @@ import {
3
3
  EventStream,
4
4
  streamSimple,
5
5
  validateToolArguments
6
- } from "./chunk-64XJIKVK.js";
6
+ } from "./chunk-HPTWCKJE.js";
7
7
 
8
8
  // ../../packages/agent-core/src/agent-loop.ts
9
9
  function createLoopErrorMessage(error, config, signal) {
@@ -8,10 +8,10 @@ import {
8
8
  createSharedWebSearchTool,
9
9
  interpretShellExit,
10
10
  requestInteraction
11
- } from "./chunk-MGZHZSHB.js";
11
+ } from "./chunk-SNSSIR3E.js";
12
12
  import {
13
13
  Type
14
- } from "./chunk-64XJIKVK.js";
14
+ } from "./chunk-HPTWCKJE.js";
15
15
 
16
16
  // src/config.ts
17
17
  import { existsSync, mkdirSync, readFileSync, renameSync } from "fs";