@molecule/api-ai-tools 1.1.0 → 1.2.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
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-09-22T18:02:23.461Z
6
+ Generated: 2026-09-22T21:57:22.227Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -581,6 +581,37 @@ function outputIsWithheldUntilExit(command: string): boolean
581
581
 
582
582
  **Returns:** True when a kill would return no useful output.
583
583
 
584
+ #### `parseCheckCommand(path)`
585
+
586
+ The shell command that parses one file and exits non-zero with the error
587
+ when it does not parse: `node --check` for JavaScript, `JSON.parse` for
588
+ JSON, esbuild's transform (present wherever Vite is) for TypeScript.
589
+
590
+ ```typescript
591
+ function parseCheckCommand(path: string): string | null
592
+ ```
593
+
594
+ - `path` — The file's absolute path.
595
+
596
+ **Returns:** The command, or null when the file type has no cheap parser.
597
+
598
+ #### `parseCheckProblem(result)`
599
+
600
+ What a failed parse check says to the executor, or null when the check
601
+ passed or could not run (no parser available is not a syntax error).
602
+
603
+ ```typescript
604
+ function parseCheckProblem(result: {
605
+ stdout: string
606
+ stderr: string
607
+ exitCode: number
608
+ }): string | null
609
+ ```
610
+
611
+ - `result` — The check command's result.
612
+
613
+ **Returns:** The message for the tool result, or null.
614
+
584
615
  #### `pathArgError(path, tool)`
585
616
 
586
617
  Validate a file tool's `path` argument is a non-empty string. A weak model
@@ -881,6 +912,14 @@ Max content size for write_file (10MB).
881
912
  const MAX_WRITE_SIZE: number
882
913
  ```
883
914
 
915
+ #### `PARSE_CHECKABLE`
916
+
917
+ Files a parse check can vouch for cheaply right after a write.
918
+
919
+ ```typescript
920
+ const PARSE_CHECKABLE: RegExp
921
+ ```
922
+
884
923
  #### `TOOL_SCHEMAS`
885
924
 
886
925
  Canonical tool schemas shared by the agent runtime and documentation.
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAY9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA6BnE;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CA8rCxF"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAY9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA+BnE;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CA0tCxF"}
package/dist/tools.js CHANGED
@@ -15,7 +15,7 @@ import { TOOL_SCHEMAS } from './schemas.js';
15
15
  const stuckAfterMs = () => Number(process.env.MOL_AI_BG_STUCK_AFTER_MS) || 5 * 60 * 1000;
16
16
  /** Cumulative wait_for_task time per task id, this process. */
17
17
  const waitedByTask = new Map();
18
- import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, isEnvFilePath, isValidGlob, MAX_BATCH_READ_BYTES, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_OUTPUT_SIZE, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, pathArgError, redactSecrets, redactSecretsInCode, resolvePath, shellQuote, sliceLines, stripControlChars, truncateMiddle, whitespaceTolerantReplace, } from './utilities.js';
18
+ import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, isEnvFilePath, isValidGlob, MAX_BATCH_READ_BYTES, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_OUTPUT_SIZE, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, parseCheckCommand, parseCheckProblem, pathArgError, redactSecrets, redactSecretsInCode, resolvePath, shellQuote, sliceLines, stripControlChars, truncateMiddle, whitespaceTolerantReplace, } from './utilities.js';
19
19
  /**
20
20
  * Build a complete set of AI agent tools bound to an execution backend.
21
21
  *
@@ -51,6 +51,29 @@ export function buildTools(backend, config) {
51
51
  const grepExcludeArgs = excludedDirs.map((d) => `--exclude-dir=${shellQuote(d)}`).join(' ');
52
52
  const findExcludeArgs = excludedDirs.map((d) => `-not -path ${shellQuote(`*/${d}/*`)}`).join(' ');
53
53
  // ── Path helpers ───────────────────────────────────────────────
54
+ /**
55
+ * After a write or edit, parse the file and hand the error back IN THE SAME
56
+ * RESULT. An edit that leaves a file unparsable used to surface only at the
57
+ * next build or type-check, minutes and many calls later — x13 (2026-09-22)
58
+ * mangled a template literal in `scripts/prerender.mjs`, the site stopped
59
+ * building, and the fix round spent 500 calls on the fallout. `node --check`
60
+ * is ~50 ms; the executor sees the exact line while the edit is still in
61
+ * front of it.
62
+ */
63
+ async function syntaxErrorAfterWrite(path) {
64
+ const command = parseCheckCommand(path);
65
+ if (!command)
66
+ return {};
67
+ const result = await backend
68
+ .run(command, { cwd: path.slice(0, path.lastIndexOf('/')) || '/', timeout: 15_000 })
69
+ .catch(() => null);
70
+ const problem = result ? parseCheckProblem(result) : null;
71
+ return problem
72
+ ? {
73
+ syntaxError: `The file no longer parses after this change — fix it before anything else, every build and test fails until it does:\n${problem}`,
74
+ }
75
+ : {};
76
+ }
54
77
  /**
55
78
  * Resolve a workspace-relative path using optional path guards.
56
79
  *
@@ -464,7 +487,7 @@ export function buildTools(backend, config) {
464
487
  const diff = computeDiff(oldContent, finalContent);
465
488
  if (onFileChange)
466
489
  onFileChange({ type: oldContent === null ? 'created' : 'modified', path });
467
- return { path, ok: true, diff };
490
+ return { path, ok: true, diff, ...(await syntaxErrorAfterWrite(path)) };
468
491
  }
469
492
  catch (e) {
470
493
  return { error: `Failed to write ${path}: ${e.message}` };
@@ -632,7 +655,12 @@ export function buildTools(backend, config) {
632
655
  await onAfterWrite(path).catch(() => { });
633
656
  if (onFileChange)
634
657
  onFileChange({ type: 'modified', path });
635
- return { path, ok: true, replacementsApplied: replacements.length };
658
+ return {
659
+ path,
660
+ ok: true,
661
+ replacementsApplied: replacements.length,
662
+ ...(await syntaxErrorAfterWrite(path)),
663
+ };
636
664
  }
637
665
  catch (e) {
638
666
  return { error: `Failed to edit ${path}: ${e.message}` };
@@ -263,4 +263,27 @@ export declare function truncateMiddle(s: string, maxLength: number): string;
263
263
  * @returns The new content if a unique fuzzy run matched, else null.
264
264
  */
265
265
  export declare function whitespaceTolerantReplace(content: string, oldString: string, newString: string): string | null;
266
+ /** Files a parse check can vouch for cheaply right after a write. */
267
+ export declare const PARSE_CHECKABLE: RegExp;
268
+ /**
269
+ * The shell command that parses one file and exits non-zero with the error
270
+ * when it does not parse: `node --check` for JavaScript, `JSON.parse` for
271
+ * JSON, esbuild's transform (present wherever Vite is) for TypeScript.
272
+ *
273
+ * @param path - The file's absolute path.
274
+ * @returns The command, or null when the file type has no cheap parser.
275
+ */
276
+ export declare function parseCheckCommand(path: string): string | null;
277
+ /**
278
+ * What a failed parse check says to the executor, or null when the check
279
+ * passed or could not run (no parser available is not a syntax error).
280
+ *
281
+ * @param result - The check command's result.
282
+ * @returns The message for the tool result, or null.
283
+ */
284
+ export declare function parseCheckProblem(result: {
285
+ stdout: string;
286
+ stderr: string;
287
+ exitCode: number;
288
+ }): string | null;
266
289
  //# sourceMappingURL=utilities.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
package/dist/utilities.js CHANGED
@@ -521,3 +521,39 @@ export function whitespaceTolerantReplace(content, oldString, newString) {
521
521
  const start = matches[0];
522
522
  return [...fileLines.slice(0, start), newString, ...fileLines.slice(start + normOld.length)].join('\n');
523
523
  }
524
+ /** Files a parse check can vouch for cheaply right after a write. */
525
+ export const PARSE_CHECKABLE = /\.(m?js|cjs|jsx|ts|tsx|mts|cts|json)$/;
526
+ /**
527
+ * The shell command that parses one file and exits non-zero with the error
528
+ * when it does not parse: `node --check` for JavaScript, `JSON.parse` for
529
+ * JSON, esbuild's transform (present wherever Vite is) for TypeScript.
530
+ *
531
+ * @param path - The file's absolute path.
532
+ * @returns The command, or null when the file type has no cheap parser.
533
+ */
534
+ export function parseCheckCommand(path) {
535
+ if (!PARSE_CHECKABLE.test(path))
536
+ return null;
537
+ const q = shellQuote(path);
538
+ if (/\.(m?js|cjs)$/.test(path))
539
+ return `node --check ${q}`;
540
+ if (/\.json$/.test(path))
541
+ return `node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'))" ${q}`;
542
+ return (`node -e "const p=process.argv[1];const src=require('fs').readFileSync(p,'utf8');` +
543
+ `require('esbuild').transformSync(src,{loader:p.endsWith('x')?'tsx':'ts',logLevel:'silent'})" ${q}`);
544
+ }
545
+ /**
546
+ * What a failed parse check says to the executor, or null when the check
547
+ * passed or could not run (no parser available is not a syntax error).
548
+ *
549
+ * @param result - The check command's result.
550
+ * @returns The message for the tool result, or null.
551
+ */
552
+ export function parseCheckProblem(result) {
553
+ if (result.exitCode === 0)
554
+ return null;
555
+ const text = (result.stderr || result.stdout).trim();
556
+ if (/Cannot find module|not found/i.test(text) && !/SyntaxError/.test(text))
557
+ return null;
558
+ return text.slice(0, 1200);
559
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-ai-tools",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Shared AI agent tools with backend abstraction for sandbox and local execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",