pi-shorthand 0.13.0 → 0.14.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-shorthand
2
2
 
3
- ![A code call in Pi](https://raw.githubusercontent.com/sebinsua/shortsight/main/docs/shorthand.png)
3
+ ![A shorthand call in Pi](https://raw.githubusercontent.com/sebinsua/shortsight/main/docs/shorthand.png)
4
4
 
5
5
  A [Pi](https://github.com/earendil-works/pi) tool for editing repositories with Bun programs, with first-class support for TypeScript and JavaScript.
6
6
 
@@ -38,9 +38,9 @@ pi-shorthand gives Pi a programming environment instead of a patch format. This
38
38
 
39
39
  Programs edit a private workspace, with host files outside the repository kept read-only. Agents are good at improving things a step at a time, so changes are applied a whole file at a time and the agent is told which worked and which didn't. Concurrent edits can cause a run to be rejected; conflict detection is best-effort, not an atomic commit.
40
40
 
41
- The `code` tool uses Pi's working directory by default. Pass `cwd` to target another checkout; relative paths are resolved from Pi's working directory. For example, when Pi starts in a bare worktree container, `cwd: "child"` targets its `child` worktree. The chosen directory must be inside a Git worktree.
41
+ The `shorthand` tool uses Pi's working directory by default. Pass `cwd` to target another checkout; relative paths are resolved from Pi's working directory. For example, when Pi starts in a bare worktree container, `cwd: "child"` targets its `child` worktree. The chosen directory must be inside a Git worktree.
42
42
 
43
- Scripts run by Pi's `codemode` tool can call `code` too. They get back what the run changed as data, not text.
43
+ Scripts run by Pi's `codemode` tool can call `shorthand` too. They get back what the run changed as data, not text.
44
44
 
45
45
  ## Without Pi
46
46
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-shorthand",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Pi tool for editing repositories with Bun programs, with first-class support for TypeScript and JavaScript.",
5
5
  "keywords": [
6
6
  "ast-grep",
@@ -26,7 +26,7 @@
26
26
  ],
27
27
  "type": "module",
28
28
  "dependencies": {
29
- "shorthand-code": "0.13.0"
29
+ "shorthand-code": "0.14.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "@earendil-works/pi-ai": "*",
package/src/display.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * How a code call looks in Pi. (The model gets a separate plain-text version: see textForModel.)
2
+ * How a shorthand call looks in Pi. (The model gets a separate plain-text version: see textForModel.)
3
3
  *
4
4
  * A verdict line first, whose colour carries the outcome. Lines indented under it belong to it: what
5
5
  * went wrong, or what to watch out for. A blank line starts a new section. The sections come in the
@@ -38,7 +38,7 @@ export function callLine(
38
38
  args.timeout && `program timeout ${args.timeout}s`,
39
39
  ];
40
40
  const suffix = settings.filter(Boolean).join(", ");
41
- return `${theme.fg("toolTitle", theme.bold("code"))} ${args.title ?? ""}${suffix ? theme.fg("muted", ` (${suffix})`) : ""}`;
41
+ return `${theme.fg("toolTitle", theme.bold("shorthand"))} ${args.title ?? ""}${suffix ? theme.fg("muted", ` (${suffix})`) : ""}`;
42
42
  }
43
43
 
44
44
  /** Text to show when a completed tool result has no structured RunResult details. */
@@ -51,7 +51,7 @@ export function unstructuredResultText(content: readonly unknown[]): string {
51
51
  )
52
52
  .join("\n")
53
53
  .trim();
54
- return text || "Code failed without result details";
54
+ return text || "Shorthand failed without result details";
55
55
  }
56
56
 
57
57
  /** Lines that belong to the verdict line above them. */
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The `code` tool: the model writes one Bun program that makes a multi-step change to the repository.
2
+ * The `shorthand` tool: the model writes one Bun program that makes a multi-step change to the repository.
3
3
  * Its writes go to a copy-on-write overlay; if it succeeds, they're applied and the diff is returned.
4
4
  */
5
5
 
@@ -22,17 +22,18 @@ import {
22
22
  } from "shorthand-code";
23
23
  import { callLine, resultLines, unstructuredResultText } from "./display.ts";
24
24
 
25
- const DESCRIPTION = `Edit repository files with a TypeScript program run by Bun. Best for changes across many files, repeated edits and semantic TypeScript renames or moves; a small change to one file is quicker as a direct edit. Top-level await and ordinary Bun/Node APIs work. Use repository-relative paths. Set cwd to a checkout path when Pi's working directory is outside the repository, such as a child worktree in a bare worktree container. The program runs in an isolated workspace; changes apply on successful exit by default and the tool reports the diff. Tests, type-checks and builds belong outside the program: in a shell call, or, when a codemode script calls code, later in that script.
25
+ const DESCRIPTION = `Edit repository files with a TypeScript program run by Bun. Best for changes across many files, repeated edits and semantic TypeScript renames or moves; a small change to one file is quicker as a direct edit. Top-level await and ordinary Bun/Node APIs work. Use repository-relative paths. Only edits files inside a git repository; use edit or write for anything else. Set cwd to a checkout path when Pi's working directory is outside the repository, such as a child worktree in a bare worktree container. The program runs in an isolated workspace; changes apply on successful exit by default and the tool reports the diff. Tests, type-checks and builds belong outside the program: in a shell call, or, when a codemode script calls shorthand, later in that script.
26
26
 
27
27
  Common operations:
28
28
  - edit({ path, oldText, newText }) replaces exactly one literal occurrence; missing or ambiguous text is an error. Use text edits for known source, structural matching when it saves enumerating occurrences or preserves varying syntax.
29
29
  - await Bun.file(path).text(); await Bun.write(path, text)
30
30
  - sg.rewrite(pattern, replacement, files?) discovers and rewrites matching code; omit files for the working directory. $X captures one node; $$$X captures a sequence.
31
- - sg.one(pattern, files?) selects exactly one match; sg.find returns an array. sg.rewrite also accepts a selected match or array without a file scope.
31
+ - sg.one(pattern, files?) selects exactly one match; sg.find returns an array. sg.rewrite also accepts a selected match or array without a file scope, or a batch of [pattern, replacement] rules applied together to the original code.
32
32
  - A rewrite callback receives a match and returns text, a native node.replace(text) edit, or null to skip. Return native edits to apply them. Pass selected arrays together for independent edits; select again after changing their file.
33
- - await refactor.rename({ file, symbol, to }) renames one resolved TypeScript symbol across the project without changing unrelated names.
34
- - await refactor.references({ file, symbol }) returns resolved identifier matches that pass straight to sg.rewrite(matches, callback).
33
+ - await refactor.rename({ file, symbol, to }) renames one resolved TypeScript symbol across the project without changing unrelated names; a parameter is named through its function ("load.id"), or give a place with { at: match | { file, line, column }, to }.
34
+ - await refactor.references({ file, symbol } | { at }) returns resolved identifier matches that pass straight to sg.rewrite(matches, callback).
35
35
  - await refactor.renameFile({ from, to }) moves a TypeScript file and updates module paths that resolve to it.
36
+ - rename, move and renameFile check the result with TypeScript, refuse what would break it, and return { files }; pass dryRun: true to get { files, diff } and change nothing.
36
37
  - await refactor.move({ file, symbol, to }) moves a top-level declaration to another file and updates the imports that follow it.
37
38
 
38
39
  See the shorthand skill for renames, moves and call-site migrations. Read its advanced-refactors.md guide only to extract code, move syntax or use rule objects. The default timeout is five seconds; request more for longer programs. Time spent inside the helpers above does not count toward it, up to 60 extra seconds.`;
@@ -45,7 +46,7 @@ export const SKILLS_DIRECTORY = path.join(
45
46
  "skills",
46
47
  );
47
48
 
48
- export function codeDescription(graphAvailable: boolean): string {
49
+ export function shorthandDescription(graphAvailable: boolean): string {
49
50
  return graphAvailable
50
51
  ? DESCRIPTION.replace("\n\nSee the shorthand skill", `\n${GRAPH_DESCRIPTION}\nSee the shorthand skill`)
51
52
  : DESCRIPTION;
@@ -54,10 +55,10 @@ export function codeDescription(graphAvailable: boolean): string {
54
55
  const paths = (description: string) => Type.Array(Type.String(), { description });
55
56
 
56
57
  /**
57
- * What a script gets back from `code`, for example one run by Pi's codemode tool. The model still sees the text.
58
+ * What a script gets back from `shorthand`, for example one run by Pi's codemode tool. The model still sees the text.
58
59
  * A failed run resolves to this too, so a script checks `exitCode`.
59
60
  */
60
- export const CODE_OUTPUT_SCHEMA = Type.Object({
61
+ export const SHORTHAND_OUTPUT_SCHEMA = Type.Object({
61
62
  exitCode: Type.Optional(
62
63
  Type.Union([Type.Number(), Type.Null()], { description: "0 on success; null when the program was killed" }),
63
64
  ),
@@ -80,7 +81,7 @@ export const CODE_OUTPUT_SCHEMA = Type.Object({
80
81
  infrastructureError: Type.Optional(Type.String({ description: "Set, alone, when the program couldn't be run" })),
81
82
  });
82
83
 
83
- export function codeOutput(result: RunResult) {
84
+ export function shorthandOutput(result: RunResult) {
84
85
  const { exitCode, timedOut, output, errorLine, warnings, applied, conflicts, rolledBack } = result;
85
86
  const changes = result.changes.map((change) => ({ path: change.path, kind: change.kind, patch: change.patch }));
86
87
  return {
@@ -112,13 +113,13 @@ export default async function (pi: ExtensionAPI, findGraph: () => Promise<unknow
112
113
  // rather than throwing, since a thrown error loses them.)
113
114
  pi.on("tool_result", async (event) => {
114
115
  const run = event.details as RunResult | undefined;
115
- if (event.toolName === "code" && run && runFailed(run)) return { isError: true };
116
+ if (event.toolName === "shorthand" && run && runFailed(run)) return { isError: true };
116
117
  });
117
118
 
118
119
  pi.registerTool({
119
- name: "code",
120
- label: "Code",
121
- description: codeDescription(graphAvailable),
120
+ name: "shorthand",
121
+ label: "Shorthand",
122
+ description: shorthandDescription(graphAvailable),
122
123
  promptSnippet:
123
124
  "Make multi-file, repetitive or rename/move changes with one Bun program; a small change to one file is quicker as a direct edit. Keep verification outside the program",
124
125
 
@@ -143,7 +144,7 @@ export default async function (pi: ExtensionAPI, findGraph: () => Promise<unknow
143
144
  }),
144
145
  ),
145
146
  }),
146
- outputSchema: CODE_OUTPUT_SCHEMA,
147
+ outputSchema: SHORTHAND_OUTPUT_SCHEMA,
147
148
 
148
149
  async execute(toolCallId, params, signal, onUpdate, ctx) {
149
150
  // While it runs, show how long it's been going and the latest reported step.
@@ -189,7 +190,7 @@ export default async function (pi: ExtensionAPI, findGraph: () => Promise<unknow
189
190
  },
190
191
  ],
191
192
  details: result,
192
- structuredContent: codeOutput(result),
193
+ structuredContent: shorthandOutput(result),
193
194
  };
194
195
  },
195
196
 
@@ -198,12 +199,12 @@ export default async function (pi: ExtensionAPI, findGraph: () => Promise<unknow
198
199
  },
199
200
 
200
201
  renderResult(result, options, theme) {
201
- return renderCodeResult(result, options, theme);
202
+ return renderShorthandResult(result, options, theme);
202
203
  },
203
204
  });
204
205
  }
205
206
 
206
- export function renderCodeResult(
207
+ export function renderShorthandResult(
207
208
  result: { content: readonly unknown[]; details?: unknown },
208
209
  { expanded, isPartial }: { expanded: boolean; isPartial: boolean },
209
210
  theme: Theme,