@aexol/spectral 0.9.127 → 0.9.129

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 (28) hide show
  1. package/dist/agent/index.js +2 -2
  2. package/dist/sdk/agent-core/agent-loop.js +1 -1
  3. package/dist/sdk/coding-agent/core/extensions/runner.d.ts.map +1 -1
  4. package/dist/sdk/coding-agent/core/extensions/runner.js +15 -1
  5. package/dist/sdk/coding-agent/core/tools/apply-patch.d.ts +7 -2
  6. package/dist/sdk/coding-agent/core/tools/apply-patch.d.ts.map +1 -1
  7. package/dist/sdk/coding-agent/core/tools/apply-patch.js +219 -41
  8. package/dist/sdk/coding-agent/core/tools/atomic-write.d.ts +30 -0
  9. package/dist/sdk/coding-agent/core/tools/atomic-write.d.ts.map +1 -0
  10. package/dist/sdk/coding-agent/core/tools/atomic-write.js +42 -0
  11. package/dist/sdk/coding-agent/core/tools/edit-diff.d.ts +10 -4
  12. package/dist/sdk/coding-agent/core/tools/edit-diff.d.ts.map +1 -1
  13. package/dist/sdk/coding-agent/core/tools/edit-diff.js +44 -24
  14. package/dist/sdk/coding-agent/core/tools/edit.d.ts +6 -0
  15. package/dist/sdk/coding-agent/core/tools/edit.d.ts.map +1 -1
  16. package/dist/sdk/coding-agent/core/tools/edit.js +31 -5
  17. package/dist/sdk/coding-agent/core/tools/file-mutation-queue.d.ts +9 -3
  18. package/dist/sdk/coding-agent/core/tools/file-mutation-queue.d.ts.map +1 -1
  19. package/dist/sdk/coding-agent/core/tools/file-mutation-queue.js +42 -3
  20. package/dist/sdk/coding-agent/core/tools/read.d.ts.map +1 -1
  21. package/dist/sdk/coding-agent/core/tools/read.js +79 -47
  22. package/dist/sdk/coding-agent/core/tools/truncate.d.ts +4 -2
  23. package/dist/sdk/coding-agent/core/tools/truncate.d.ts.map +1 -1
  24. package/dist/sdk/coding-agent/core/tools/truncate.js +43 -23
  25. package/dist/sdk/coding-agent/core/tools/write.d.ts +5 -8
  26. package/dist/sdk/coding-agent/core/tools/write.d.ts.map +1 -1
  27. package/dist/sdk/coding-agent/core/tools/write.js +29 -11
  28. package/package.json +1 -1
@@ -6,18 +6,15 @@ declare const writeSchema: Type.TObject<{
6
6
  content: Type.TString;
7
7
  }>;
8
8
  export type WriteToolInput = Static<typeof writeSchema>;
9
- /**
10
- * Pluggable operations for the write tool.
11
- * Override these to delegate file writing to remote systems (for example SSH).
12
- */
13
9
  export interface WriteOperations {
14
- /** Write content to a file */
15
- writeFile: (absolutePath: string, content: string) => Promise<void>;
16
- /** Create directory recursively */
10
+ /**
11
+ * Optional atomic write override (temp file + rename). When omitted,
12
+ * the default {@link atomicWriteFile} helper is used.
13
+ */
14
+ atomicWrite?: (absolutePath: string, content: string) => Promise<void>;
17
15
  mkdir: (dir: string) => Promise<void>;
18
16
  }
19
17
  export interface WriteToolOptions {
20
- /** Custom operations for file writing. Default: local filesystem */
21
18
  operations?: WriteOperations;
22
19
  }
23
20
  export declare function createWriteToolDefinition(cwd: string, options?: WriteToolOptions): ToolDefinition<typeof writeSchema, undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAM7D,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,8BAA8B;IAC9B,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,mCAAmC;IACnC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAOD,MAAM,WAAW,gBAAgB;IAChC,oEAAoE;IACpE,UAAU,CAAC,EAAE,eAAe,CAAC;CAC7B;AA2DD,wBAAgB,yBAAyB,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,GACxB,cAAc,CAAC,OAAO,WAAW,EAAE,SAAS,CAAC,CA2D/C;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC,OAAO,WAAW,CAAC,CAEtG"}
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAe7D,QAAA,MAAM,WAAW;;;EAKf,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AACxD,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAID,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AA6DD,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,GACzB,cAAc,CAAC,OAAO,WAAW,EAAE,SAAS,CAAC,CA+E/C;AACD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,GACzB,SAAS,CAAC,OAAO,WAAW,CAAC,CAE/B"}
@@ -1,17 +1,19 @@
1
- import { mkdir as fsMkdir, writeFile as fsWriteFile } from "fs/promises";
1
+ import { mkdir as fsMkdir } from "fs/promises";
2
2
  import { dirname } from "path";
3
3
  import { Type } from "typebox";
4
4
  import { getLanguageFromPath, highlightCode } from "../theme.js";
5
+ import { atomicWriteFile, defaultAtomicWriteOperations, } from "./atomic-write.js";
5
6
  import { withFileMutationQueue } from "./file-mutation-queue.js";
6
7
  import { resolveToCwd } from "./path-utils.js";
7
- import { normalizeDisplayText, replaceTabs, shortenPath, str } from "./render-utils.js";
8
+ import { normalizeDisplayText, replaceTabs, shortenPath, str, } from "./render-utils.js";
8
9
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
9
10
  const writeSchema = Type.Object({
10
- path: Type.String({ description: "Path to the file to write (relative or absolute)" }),
11
+ path: Type.String({
12
+ description: "Path to the file to write (relative or absolute)",
13
+ }),
11
14
  content: Type.String({ description: "Content to write to the file" }),
12
15
  });
13
16
  const defaultWriteOperations = {
14
- writeFile: (path, content) => fsWriteFile(path, content, "utf-8"),
15
17
  mkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => { }),
16
18
  };
17
19
  function trimTrailingEmptyLines(lines) {
@@ -62,6 +64,7 @@ function formatWriteResult(result, _theme) {
62
64
  }
63
65
  export function createWriteToolDefinition(cwd, options) {
64
66
  const ops = options?.operations ?? defaultWriteOperations;
67
+ const atomicOps = defaultAtomicWriteOperations;
65
68
  return {
66
69
  name: "write",
67
70
  label: "write",
@@ -73,30 +76,45 @@ export function createWriteToolDefinition(cwd, options) {
73
76
  const absolutePath = resolveToCwd(path, cwd);
74
77
  const dir = dirname(absolutePath);
75
78
  return withFileMutationQueue(absolutePath, () => new Promise((resolve, reject) => {
79
+ // Abort BEFORE the write begins -> reject immediately.
76
80
  if (signal?.aborted) {
77
81
  reject(new Error("Operation aborted"));
78
82
  return;
79
83
  }
80
84
  let aborted = false;
85
+ let writeStarted = false;
81
86
  const onAbort = () => {
82
87
  aborted = true;
83
- reject(new Error("Operation aborted"));
88
+ // W2: only reject when aborted BEFORE the write begins. An abort
89
+ // that fires during/after the write must not swallow the
90
+ // successful resolve once the write completes.
91
+ if (!writeStarted) {
92
+ reject(new Error("Operation aborted"));
93
+ }
84
94
  };
85
95
  signal?.addEventListener("abort", onAbort, { once: true });
86
96
  (async () => {
87
97
  try {
88
- // Create parent directories if needed.
89
98
  await ops.mkdir(dir);
99
+ // Reject if aborted before the write begins.
90
100
  if (aborted)
91
101
  return;
92
- // Write the file contents.
93
- await ops.writeFile(absolutePath, content);
94
- if (aborted)
95
- return;
102
+ writeStarted = true;
103
+ // W1 + W5: atomic write (temp + rename). On failure the
104
+ // temp is cleaned up and the target is left untouched.
105
+ const writeFn = ops.atomicWrite ??
106
+ ((p, c) => atomicWriteFile(p, c, "utf-8", atomicOps));
107
+ await writeFn(absolutePath, content);
108
+ // W2: once the write has completed successfully, resolve
109
+ // with the success content even if the abort signal
110
+ // fired mid-write. Do NOT swallow the resolve.
96
111
  signal?.removeEventListener("abort", onAbort);
97
112
  resolve({
98
113
  content: [
99
- { type: "text", text: `Successfully wrote ${content.length} bytes to ${path}` },
114
+ {
115
+ type: "text",
116
+ text: `Successfully wrote ${Buffer.byteLength(content, "utf-8")} bytes to ${path}`,
117
+ },
100
118
  ],
101
119
  details: undefined,
102
120
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexol/spectral",
3
- "version": "0.9.127",
3
+ "version": "0.9.129",
4
4
  "description": "AI coding agent for Aexol with relay-based browser access.",
5
5
  "type": "module",
6
6
  "private": false,