zelari-code 1.45.0 → 1.46.1

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.
@@ -10,9 +10,13 @@
10
10
  * wrapped) edit path — instead of fragile whole-file string matching.
11
11
  *
12
12
  * `typescript` is loaded lazily via dynamic import and kept OUT of the CLI
13
- * bundle (it's ~7MB, marked external in bundle-cli.mjs). Everything here is
14
- * best-effort: if `typescript` can't be loaded, the file can't be read, or it
15
- * isn't a TS/JS file, the functions return empty/null rather than throwing.
13
+ * bundle (it's ~7MB, marked external in bundle-cli.mjs).
14
+ *
15
+ * Loud degradation (v0.10.0 "loud tool errors"): `parseFileSymbolsDiag` is
16
+ * the primary entry point and returns a DISCRIMINATED result, so an empty
17
+ * outcome can never be confused with an unavailable backend. The legacy
18
+ * `parseFileSymbols`/`astOutline`/`findSymbol` helpers remain as quiet
19
+ * compatibility wrappers (empty/null on any non-ok status).
16
20
  */
17
21
  import { readFile } from 'node:fs/promises';
18
22
  import path from 'node:path';
@@ -31,30 +35,76 @@ function loadTs() {
31
35
  }
32
36
  return tsPromise;
33
37
  }
38
+ function errMessage(err) {
39
+ return err instanceof Error ? err.message : String(err);
40
+ }
34
41
  /**
35
42
  * Parse a TS/JS file into its top-level + nested declarations (with exact
36
- * source text). Returns [] for unsupported files, read errors, or when the
37
- * TypeScript compiler API is unavailable.
43
+ * source text), reporting WHY on every failure path.
44
+ *
45
+ * Relative paths resolve against `cwd ?? process.cwd()` (same rule as
46
+ * grep_content, see core builtin search.ts). Read errors distinguish ENOENT
47
+ * (`file-not-found`, reports the resolved absolute path) from anything else
48
+ * (`read-error`, propagates the message); a failing `createSourceFile`
49
+ * surfaces as `parse-error`.
38
50
  */
39
- export async function parseFileSymbols(file) {
40
- if (!isAstSupported(file))
41
- return [];
51
+ export async function parseFileSymbolsDiag(file, cwd) {
52
+ const resolvedPath = path.isAbsolute(file)
53
+ ? file
54
+ : path.join(cwd ?? process.cwd(), file);
55
+ const extension = path.extname(resolvedPath).toLowerCase();
56
+ if (!TS_EXTENSIONS.has(extension)) {
57
+ return {
58
+ status: 'unsupported-extension',
59
+ extension: extension || '(none)',
60
+ resolvedPath,
61
+ recoverable: false,
62
+ recommendedFallback: 'read_file',
63
+ };
64
+ }
42
65
  const ts = await loadTs();
43
- if (!ts)
44
- return [];
66
+ if (!ts) {
67
+ return {
68
+ status: 'typescript-unavailable',
69
+ resolvedPath,
70
+ recoverable: true,
71
+ recommendedFallback: 'read_file',
72
+ };
73
+ }
45
74
  let text;
46
75
  try {
47
- text = await readFile(file, 'utf8');
76
+ text = await readFile(resolvedPath, 'utf8');
48
77
  }
49
- catch {
50
- return [];
78
+ catch (err) {
79
+ const code = err?.code;
80
+ if (code === 'ENOENT') {
81
+ return {
82
+ status: 'file-not-found',
83
+ resolvedPath,
84
+ recoverable: false,
85
+ recommendedFallback: 'grep_content',
86
+ };
87
+ }
88
+ return {
89
+ status: 'read-error',
90
+ resolvedPath,
91
+ message: errMessage(err),
92
+ recoverable: true,
93
+ recommendedFallback: 'read_file',
94
+ };
51
95
  }
52
96
  let source;
53
97
  try {
54
- source = ts.createSourceFile(path.basename(file), text, ts.ScriptTarget.Latest, true);
98
+ source = ts.createSourceFile(path.basename(resolvedPath), text, ts.ScriptTarget.Latest, true);
55
99
  }
56
- catch {
57
- return [];
100
+ catch (err) {
101
+ return {
102
+ status: 'parse-error',
103
+ resolvedPath,
104
+ message: errMessage(err),
105
+ recoverable: false,
106
+ recommendedFallback: 'read_file',
107
+ };
58
108
  }
59
109
  const out = [];
60
110
  const lineOf = (pos) => source.getLineAndCharacterOfPosition(pos).line + 1;
@@ -107,20 +157,33 @@ export async function parseFileSymbols(file) {
107
157
  ts.forEachChild(node, visit);
108
158
  };
109
159
  visit(source);
110
- return out;
160
+ return { status: 'ok', symbols: out };
161
+ }
162
+ /**
163
+ * Compatibility wrapper over {@link parseFileSymbolsDiag}: same quiet
164
+ * contract as before (empty array on any non-ok status). New callers should
165
+ * prefer the diag version.
166
+ */
167
+ export async function parseFileSymbols(file) {
168
+ const r = await parseFileSymbolsDiag(file);
169
+ return r.status === 'ok' ? r.symbols : [];
111
170
  }
112
- /** Outline: all declarations in a file (no source text). */
171
+ /** Outline: all declarations in a file (no source text). Quiet on failure. */
113
172
  export async function astOutline(file) {
114
- const symbols = await parseFileSymbols(file);
115
- return symbols.map(({ text: _text, ...rest }) => rest);
173
+ const r = await parseFileSymbolsDiag(file);
174
+ if (r.status !== 'ok')
175
+ return [];
176
+ return r.symbols.map(({ text: _text, ...rest }) => rest);
116
177
  }
117
178
  /**
118
179
  * Find a named declaration and return its exact source text + range. When
119
180
  * several declarations share a name (overloads, a method and a function),
120
- * returns the first by source order.
181
+ * returns the first by source order. Quiet (null) on any non-ok status.
121
182
  */
122
183
  export async function findSymbol(file, name) {
123
- const symbols = await parseFileSymbols(file);
124
- return symbols.find((s) => s.name === name) ?? null;
184
+ const r = await parseFileSymbolsDiag(file);
185
+ if (r.status !== 'ok')
186
+ return null;
187
+ return r.symbols.find((s) => s.name === name) ?? null;
125
188
  }
126
189
  //# sourceMappingURL=engine.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/cli/ast/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/cli/ast/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * S2.1/S2.3 — unit tests for the loud, discriminated parse result of
3
+ * ast/engine (plan: .zelari/docs/piano-loud-tool-errors-ast-diag-readonly-shell.md §2).
4
+ */
5
+ import { mkdtemp, writeFile } from 'node:fs/promises';
6
+ import { tmpdir } from 'node:os';
7
+ import path from 'node:path';
8
+ import { vi, describe, it, expect } from 'vitest';
9
+ import { parseFileSymbolsDiag, parseFileSymbols, astOutline, findSymbol, } from './engine.js';
10
+ const SAMPLE = [
11
+ 'export interface Shape { kind: string }',
12
+ 'export class Circle implements Shape {',
13
+ ' kind = "circle";',
14
+ ' area(): number { return 1; }',
15
+ '}',
16
+ 'export const area = (r: number): number => r * r;',
17
+ 'const hidden = 1;',
18
+ '',
19
+ ].join('\n');
20
+ describe('parseFileSymbolsDiag', () => {
21
+ it('resolves relative paths against the injected cwd and parses symbols', async () => {
22
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
23
+ await writeFile(path.join(dir, 'sample.ts'), SAMPLE, 'utf8');
24
+ const r = await parseFileSymbolsDiag('sample.ts', dir);
25
+ expect(r.status).toBe('ok');
26
+ if (r.status !== 'ok')
27
+ return;
28
+ const names = r.symbols.map((s) => s.name);
29
+ expect(names).toContain('Shape');
30
+ expect(names).toContain('Circle');
31
+ expect(names).toContain('area'); // arrow function → kind 'function'
32
+ expect(names).toContain('hidden');
33
+ const circle = r.symbols.find((s) => s.name === 'Circle');
34
+ expect(circle.exported).toBe(true);
35
+ expect(circle.text).toContain('area(): number');
36
+ });
37
+ it('reports file-not-found with the ABSOLUTE resolved path', async () => {
38
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
39
+ const r = await parseFileSymbolsDiag('missing.ts', dir);
40
+ expect(r.status).toBe('file-not-found');
41
+ if (r.status !== 'file-not-found')
42
+ return;
43
+ expect(r.resolvedPath).toBe(path.join(dir, 'missing.ts'));
44
+ expect(path.isAbsolute(r.resolvedPath)).toBe(true);
45
+ expect(r.recoverable).toBe(false);
46
+ expect(r.recommendedFallback).toBe('grep_content');
47
+ });
48
+ it('reports unsupported-extension with the extension it saw', async () => {
49
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
50
+ await writeFile(path.join(dir, 'script.py'), 'def f(): pass\n', 'utf8');
51
+ const r = await parseFileSymbolsDiag('script.py', dir);
52
+ expect(r.status).toBe('unsupported-extension');
53
+ if (r.status !== 'unsupported-extension')
54
+ return;
55
+ expect(r.extension).toBe('.py');
56
+ expect(r.resolvedPath).toBe(path.join(dir, 'script.py'));
57
+ });
58
+ it('reports typescript-unavailable when the compiler API cannot load', async () => {
59
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
60
+ await writeFile(path.join(dir, 'sample.ts'), SAMPLE, 'utf8');
61
+ // Fresh module graph where the dynamic `import('typescript')` rejects.
62
+ vi.resetModules();
63
+ vi.doMock('typescript', () => {
64
+ throw new Error('simulated: typescript not installed');
65
+ });
66
+ try {
67
+ const fresh = await import('./engine.js');
68
+ const r = await fresh.parseFileSymbolsDiag('sample.ts', dir);
69
+ expect(r.status).toBe('typescript-unavailable');
70
+ if (r.status !== 'typescript-unavailable')
71
+ return;
72
+ expect(r.recoverable).toBe(true);
73
+ expect(r.recommendedFallback).toBe('read_file');
74
+ }
75
+ finally {
76
+ vi.doUnmock('typescript');
77
+ }
78
+ });
79
+ });
80
+ describe('quiet compatibility wrappers', () => {
81
+ it('parseFileSymbols: [] on non-ok, symbols on ok', async () => {
82
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
83
+ const file = path.join(dir, 'sample.ts');
84
+ await writeFile(file, SAMPLE, 'utf8');
85
+ // No cwd → resolves against process.cwd(); absolute path keeps it exact.
86
+ expect(await parseFileSymbols(path.join(dir, 'missing.ts'))).toEqual([]);
87
+ expect((await parseFileSymbols(file)).length).toBeGreaterThan(0);
88
+ });
89
+ it('astOutline drops text; findSymbol round-trips exact text', async () => {
90
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
91
+ const file = path.join(dir, 'sample.ts');
92
+ await writeFile(file, SAMPLE, 'utf8');
93
+ const outline = await astOutline(file);
94
+ expect(outline.every((s) => !('text' in s))).toBe(true);
95
+ const sym = await findSymbol(file, 'Circle');
96
+ expect(sym?.text.startsWith('export class Circle')).toBe(true);
97
+ expect(await findSymbol(file, 'DoesNotExist')).toBeNull();
98
+ expect(await findSymbol(path.join(dir, 'missing.ts'), 'X')).toBeNull();
99
+ });
100
+ });
101
+ //# sourceMappingURL=engine.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.test.js","sourceRoot":"","sources":["../../../src/cli/ast/engine.test.ts"],"names":[],"mappings":"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"}
@@ -7,34 +7,85 @@
7
7
  * named declaration's EXACT source span + text, so the agent can target it and
8
8
  * edit reliably through the normal edit tools instead of fuzzy string matching.
9
9
  *
10
- * Read-only: neither tool writes. Both are best-effort (empty result when
11
- * TypeScript is unavailable or the file isn't TS/JS).
10
+ * Read-only: neither tool writes.
11
+ *
12
+ * Loud degradation (v0.10.0 "loud tool errors"): every empty outcome carries a
13
+ * discriminated `status` (file-not-found / typescript-unavailable /
14
+ * unsupported-extension / read-error / parse-error) plus machine-readable
15
+ * `resolvedPath` / `recoverable` / `recommendedFallback` fields. `found: false`
16
+ * is only ever reported when the file was read AND parsed successfully.
12
17
  */
13
18
  import { z } from 'zod';
14
19
  import { typedOk } from '@zelari/core/harness/tools/toolTypes';
15
- import { astOutline, findSymbol } from './engine.js';
20
+ import { parseFileSymbolsDiag, } from './engine.js';
16
21
  /** Cap the returned declaration text so a huge class can't flood the context. */
17
22
  const MAX_TEXT_CHARS = 4000;
18
- export function createAstTools() {
23
+ /**
24
+ * Build the loud, machine-readable payload shared by every non-ok status.
25
+ * `recommendedFallback` is a hint for the model — never auto-executed.
26
+ */
27
+ function degradedPayload(r) {
28
+ return {
29
+ status: r.status,
30
+ resolvedPath: r.resolvedPath,
31
+ recoverable: r.recoverable,
32
+ recommendedFallback: r.recommendedFallback,
33
+ };
34
+ }
35
+ /** Human-facing note for each non-ok status — one cause, no "maybe" lists. */
36
+ function degradedNote(r) {
37
+ switch (r.status) {
38
+ case 'file-not-found':
39
+ return `file not found — looked at ${r.resolvedPath} (relative paths resolve against the workspace root)`;
40
+ case 'typescript-unavailable':
41
+ return 'TypeScript compiler API unavailable — the "typescript" package could not be loaded. ' +
42
+ 'AST tools need it; disable them with ZELARI_AST=0 or fall back to read_file/grep_content.';
43
+ case 'unsupported-extension':
44
+ return `unsupported file extension "${r.extension}" — ast_outline/find_symbol only parse TS/JS files (.ts/.tsx/.js/.jsx/.mjs/.cjs)`;
45
+ case 'read-error':
46
+ return `could not read ${r.resolvedPath}: ${r.message}`;
47
+ case 'parse-error':
48
+ return `TypeScript failed to parse ${r.resolvedPath}: ${r.message}`;
49
+ }
50
+ }
51
+ /**
52
+ * Create the AST tools bound to a workspace `root` (S2.0: the root is
53
+ * propagated by the registry, which already has it in scope). At call time
54
+ * relative paths resolve against `ctx.cwd` first (aligned with grep_content),
55
+ * falling back to `root`.
56
+ */
57
+ export function createAstTools(root) {
19
58
  const outline = {
20
59
  name: 'ast_outline',
21
60
  description: 'Structural outline of a TS/JS file: every declaration (function, class, ' +
22
61
  'method, interface, type, enum, variable) with its line range and whether ' +
23
62
  "it's exported. Faster and more precise than reading the whole file to find " +
24
- 'where things are. TS/JS only.',
63
+ 'where things are. Relative paths resolve against the working directory. ' +
64
+ 'TS/JS only.',
25
65
  permissions: ['read'],
26
66
  inputSchema: z.object({
27
67
  path: z.string().min(1).describe('Path to the TS/JS file to outline.'),
28
68
  }),
29
- execute: async (args) => {
69
+ execute: async (args, ctx) => {
30
70
  const { path: file } = args;
31
- const symbols = await astOutline(file);
32
- if (symbols.length === 0) {
33
- return typedOk({ symbols: [], note: 'no declarations found (or not a TS/JS file / TypeScript unavailable)' });
71
+ const r = await parseFileSymbolsDiag(file, ctx?.cwd ?? root);
72
+ if (r.status !== 'ok') {
73
+ return typedOk({ symbols: [], note: degradedNote(r), ...degradedPayload(r) });
74
+ }
75
+ if (r.symbols.length === 0) {
76
+ // Genuinely empty file (or only ambient/implicit declarations):
77
+ // the file WAS read and parsed, so this is a true empty — say so.
78
+ return typedOk({
79
+ status: 'ok',
80
+ count: 0,
81
+ symbols: [],
82
+ note: 'file parsed successfully but contains no declarations',
83
+ });
34
84
  }
35
85
  return typedOk({
36
- count: symbols.length,
37
- symbols: symbols.map((s) => `${s.exported ? 'export ' : ''}${s.kind} ${s.name} (lines ${s.line}-${s.endLine})`),
86
+ status: 'ok',
87
+ count: r.symbols.length,
88
+ symbols: r.symbols.map((s) => `${s.exported ? 'export ' : ''}${s.kind} ${s.name} (lines ${s.line}-${s.endLine})`),
38
89
  });
39
90
  },
40
91
  };
@@ -43,21 +94,32 @@ export function createAstTools() {
43
94
  description: "Locate a named declaration in a TS/JS file and return its EXACT source " +
44
95
  'text and line range. Use this to grab a function/class/method verbatim so ' +
45
96
  'you can edit_file it reliably (node-accurate) instead of guessing the ' +
46
- 'surrounding text. TS/JS only.',
97
+ 'surrounding text. Relative paths resolve against the working directory. ' +
98
+ 'TS/JS only.',
47
99
  permissions: ['read'],
48
100
  inputSchema: z.object({
49
101
  path: z.string().min(1).describe('Path to the TS/JS file.'),
50
102
  name: z.string().min(1).describe('The declaration name to find (function/class/method/etc).'),
51
103
  }),
52
- execute: async (args) => {
104
+ execute: async (args, ctx) => {
53
105
  const { path: file, name } = args;
54
- const sym = await findSymbol(file, name);
106
+ const r = await parseFileSymbolsDiag(file, ctx?.cwd ?? root);
107
+ if (r.status !== 'ok') {
108
+ return typedOk({ found: false, note: degradedNote(r), ...degradedPayload(r) });
109
+ }
110
+ const sym = r.symbols.find((s) => s.name === name);
55
111
  if (!sym) {
56
- return typedOk({ found: false, note: `no declaration named "${name}" found in ${file}` });
112
+ // File was read and parsed: a REAL not-found, safe to state.
113
+ return typedOk({
114
+ found: false,
115
+ status: 'ok',
116
+ note: `no declaration named "${name}" found in ${r.symbols.length > 0 ? `${file} (declarations present: ${[...new Set(r.symbols.map((s) => s.name))].slice(0, 12).join(', ')})` : file}`,
117
+ });
57
118
  }
58
119
  const truncated = sym.text.length > MAX_TEXT_CHARS;
59
120
  return typedOk({
60
121
  found: true,
122
+ status: 'ok',
61
123
  kind: sym.kind,
62
124
  exported: sym.exported,
63
125
  line: sym.line,
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/cli/ast/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/cli/ast/tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * S2.0/S2.2/S2.3 — tool-level tests: root propagation, ctx.cwd resolution,
3
+ * loud degraded notes with machine fields, and "found: false only when the
4
+ * file was actually read and parsed".
5
+ */
6
+ import { mkdtemp, writeFile } from 'node:fs/promises';
7
+ import { tmpdir } from 'node:os';
8
+ import path from 'node:path';
9
+ import { describe, it, expect } from 'vitest';
10
+ import { createAstTools } from './tools.js';
11
+ const SAMPLE = [
12
+ 'export interface Shape { kind: string }',
13
+ 'export class Circle implements Shape {',
14
+ ' kind = "circle";',
15
+ ' area(): number { return 1; }',
16
+ '}',
17
+ 'export const area = (r: number): number => r * r;',
18
+ '',
19
+ ].join('\n');
20
+ function fakeCtx(cwd) {
21
+ return {
22
+ signal: new AbortController().signal,
23
+ cwd,
24
+ audit: () => { },
25
+ sessionId: 'ast-tools-test',
26
+ };
27
+ }
28
+ async function run(tool, args, ctx) {
29
+ const r = await tool.execute(args, ctx);
30
+ if (!r.ok)
31
+ throw new Error(r.error);
32
+ return r.value;
33
+ }
34
+ function getTools(root) {
35
+ const tools = createAstTools(root);
36
+ const outline = tools.find((t) => t.name === 'ast_outline');
37
+ const findSym = tools.find((t) => t.name === 'find_symbol');
38
+ return { outline, findSym };
39
+ }
40
+ describe('ast_outline (loud degradation)', () => {
41
+ it('resolves a RELATIVE path against ctx.cwd', async () => {
42
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
43
+ await writeFile(path.join(dir, 'sample.ts'), SAMPLE, 'utf8');
44
+ const { outline } = getTools('/nonexistent-root');
45
+ const v = await run(outline, { path: 'sample.ts' }, fakeCtx(dir));
46
+ expect(v.status).toBe('ok');
47
+ expect(v.count).toBeGreaterThan(0);
48
+ const symbols = v.symbols;
49
+ expect(symbols.some((s) => s.includes('export class Circle'))).toBe(true);
50
+ });
51
+ it('falls back to the factory root when ctx has no cwd (S2.0)', async () => {
52
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
53
+ await writeFile(path.join(dir, 'sample.ts'), SAMPLE, 'utf8');
54
+ const { outline } = getTools(dir);
55
+ const ctxNoCwd = {
56
+ signal: new AbortController().signal,
57
+ audit: () => { },
58
+ sessionId: 'ast-tools-test',
59
+ };
60
+ const v = await run(outline, { path: 'sample.ts' }, ctxNoCwd);
61
+ expect(v.status).toBe('ok');
62
+ expect(v.count).toBeGreaterThan(0);
63
+ });
64
+ it('file-not-found names the absolute path it looked at', async () => {
65
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
66
+ const { outline } = getTools('/nonexistent-root');
67
+ const v = await run(outline, { path: 'missing.ts' }, fakeCtx(dir));
68
+ expect(v.status).toBe('file-not-found');
69
+ expect(v.symbols).toEqual([]);
70
+ expect(v.note).toContain(path.join(dir, 'missing.ts'));
71
+ expect(v.recoverable).toBe(false);
72
+ expect(v.recommendedFallback).toBe('grep_content');
73
+ });
74
+ it('unsupported-extension reports the extension in the note', async () => {
75
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
76
+ await writeFile(path.join(dir, 'script.py'), 'def f(): pass\n', 'utf8');
77
+ const { outline } = getTools('/nonexistent-root');
78
+ const v = await run(outline, { path: 'script.py' }, fakeCtx(dir));
79
+ expect(v.status).toBe('unsupported-extension');
80
+ expect(v.note).toContain('.py');
81
+ });
82
+ it('an empty-but-parsed file is a TRUE empty, not a degraded note', async () => {
83
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
84
+ await writeFile(path.join(dir, 'empty.ts'), '\n', 'utf8');
85
+ const { outline } = getTools('/nonexistent-root');
86
+ const v = await run(outline, { path: 'empty.ts' }, fakeCtx(dir));
87
+ expect(v.status).toBe('ok');
88
+ expect(v.symbols).toEqual([]);
89
+ expect(v.note).toContain('no declarations');
90
+ // The old conflated three-causes note must be gone.
91
+ expect(String(v.note)).not.toContain('TypeScript unavailable');
92
+ });
93
+ });
94
+ describe('find_symbol (found:false only after a successful parse)', () => {
95
+ it('degraded file surfaces status, not a fake "no declaration named"', async () => {
96
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
97
+ const { findSym } = getTools('/nonexistent-root');
98
+ const v = await run(findSym, { path: 'missing.ts', name: 'Circle' }, fakeCtx(dir));
99
+ expect(v.found).toBe(false);
100
+ expect(v.status).toBe('file-not-found');
101
+ expect(v.note).toContain(path.join(dir, 'missing.ts'));
102
+ });
103
+ it('symbol truly absent from a PARSED file → found:false, status ok, hint of present names', async () => {
104
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
105
+ await writeFile(path.join(dir, 'sample.ts'), SAMPLE, 'utf8');
106
+ const { findSym } = getTools('/nonexistent-root');
107
+ const v = await run(findSym, { path: 'sample.ts', name: 'Sphere' }, fakeCtx(dir));
108
+ expect(v.found).toBe(false);
109
+ expect(v.status).toBe('ok');
110
+ expect(v.note).toContain('Sphere');
111
+ expect(v.note).toContain('Circle');
112
+ });
113
+ it('returns the exact declaration text for editing', async () => {
114
+ const dir = await mkdtemp(path.join(tmpdir(), 'zelari-ast-'));
115
+ await writeFile(path.join(dir, 'sample.ts'), SAMPLE, 'utf8');
116
+ const { findSym } = getTools('/nonexistent-root');
117
+ const v = await run(findSym, { path: 'sample.ts', name: 'Circle' }, fakeCtx(dir));
118
+ expect(v.found).toBe(true);
119
+ expect(v.kind).toBe('class');
120
+ expect(v.exported).toBe(true);
121
+ expect(String(v.text).startsWith('export class Circle')).toBe(true);
122
+ });
123
+ });
124
+ //# sourceMappingURL=tools.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.test.js","sourceRoot":"","sources":["../../../src/cli/ast/tools.test.ts"],"names":[],"mappings":"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"}
@@ -351,6 +351,8 @@ export function useChatTurn(params) {
351
351
  "You are in PLAN mode. Explore and design only.",
352
352
  "- Do NOT implement production code or run destructive shell commands.",
353
353
  "- write_file / edit_file / bash / apply_diff are unavailable.",
354
+ "- inspect_command IS available: allowlisted read-only inspector (no shell). Use it for git_status/git_log/git_diff/git_show/git_branch_current/git_ls_files/typecheck/node_version/npm_ls/npm_outdated/npm_view - it turns claims into execution-verified observations.",
355
+ "- OBSERVATION INTEGRITY: negative evidence is valid only from a completed observation. Never conclude that code/symbols/files do not exist from degraded results, zero files examined, or unavailable backends (grep_content SEARCH_EMPTY_SCOPE, ast/LSP degraded status, inspect_command unsupported shapes).",
354
356
  "- Produce a clear plan, ask clarifying questions (---QUESTION---), use workspace plan tools when relevant.",
355
357
  "- When the plan is ready, tell the user to run /build to implement.",
356
358
  ].join("\n")