token-pilot 0.17.0 → 0.18.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/CHANGELOG.md +17 -0
- package/dist/core/validation.d.ts +5 -0
- package/dist/core/validation.js +16 -2
- package/dist/handlers/csv-sections.d.ts +35 -0
- package/dist/handlers/csv-sections.js +129 -0
- package/dist/handlers/json-sections.d.ts +17 -0
- package/dist/handlers/json-sections.js +66 -0
- package/dist/handlers/markdown-sections.d.ts +15 -0
- package/dist/handlers/markdown-sections.js +49 -0
- package/dist/handlers/non-code.js +48 -11
- package/dist/handlers/read-for-edit.d.ts +1 -0
- package/dist/handlers/read-for-edit.js +107 -1
- package/dist/handlers/read-section.d.ts +12 -0
- package/dist/handlers/read-section.js +96 -0
- package/dist/handlers/yaml-sections.d.ts +17 -0
- package/dist/handlers/yaml-sections.js +46 -0
- package/dist/server/tool-definitions.d.ts +96 -0
- package/dist/server/tool-definitions.js +19 -0
- package/dist/server.js +16 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to Token Pilot will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.18.0] - 2026-04-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **`read_section` tool** — read a specific section from Markdown, YAML, JSON, or CSV files. Markdown: by heading name. YAML/JSON: by top-level key. CSV: by row range (`rows:1-50`). Much cheaper than reading the whole file.
|
|
12
|
+
- **`read_for_edit` section parameter** — prepare edit context for non-code file sections. Works with all 4 formats.
|
|
13
|
+
- **Markdown outline with line ranges** — `smart_read` on `.md` files now shows `[L5-20]` ranges and hints for `read_section`.
|
|
14
|
+
- **YAML/JSON section ranges** — `smart_read` on `.yaml`/`.json` shows top-level key ranges.
|
|
15
|
+
- **CSV smart_read** — shows columns, row count, sample rows, and hints for row-range reading.
|
|
16
|
+
- **4 section parsers** — `markdown-sections.ts`, `yaml-sections.ts`, `json-sections.ts`, `csv-sections.ts`.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **20 tools** (was 19) — added `read_section`.
|
|
20
|
+
- **492 tests** (was 441).
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- `npm audit` — resolved brace-expansion, path-to-regexp, picomatch vulnerabilities.
|
|
24
|
+
|
|
8
25
|
## [0.17.0] - 2026-04-02
|
|
9
26
|
|
|
10
27
|
### Added
|
|
@@ -80,6 +80,7 @@ export declare function validateReadForEditArgs(args: unknown): {
|
|
|
80
80
|
include_callers?: boolean;
|
|
81
81
|
include_tests?: boolean;
|
|
82
82
|
include_changes?: boolean;
|
|
83
|
+
section?: string;
|
|
83
84
|
};
|
|
84
85
|
/**
|
|
85
86
|
* Validate related_files arguments.
|
|
@@ -155,6 +156,10 @@ export interface TestSummaryArgs {
|
|
|
155
156
|
timeout?: number;
|
|
156
157
|
}
|
|
157
158
|
export declare function validateTestSummaryArgs(args: unknown): TestSummaryArgs;
|
|
159
|
+
export declare function validateReadSectionArgs(args: unknown): {
|
|
160
|
+
path: string;
|
|
161
|
+
heading: string;
|
|
162
|
+
};
|
|
158
163
|
/** Detect roots that would cause ast-index to scan the entire filesystem */
|
|
159
164
|
export declare function isDangerousRoot(root: string): boolean;
|
|
160
165
|
//# sourceMappingURL=validation.d.ts.map
|
package/dist/core/validation.js
CHANGED
|
@@ -228,8 +228,8 @@ export function validateReadForEditArgs(args) {
|
|
|
228
228
|
if (typeof a.path !== 'string' || a.path.length === 0) {
|
|
229
229
|
throw new Error('Required parameter "path" must be a non-empty string.');
|
|
230
230
|
}
|
|
231
|
-
if (!a.symbol && !a.line && (!Array.isArray(a.symbols) || a.symbols.length === 0)) {
|
|
232
|
-
throw new Error('Either "symbol", "symbols", or "
|
|
231
|
+
if (!a.symbol && !a.line && (!Array.isArray(a.symbols) || a.symbols.length === 0) && !a.section) {
|
|
232
|
+
throw new Error('Either "symbol", "symbols", "line", or "section" must be provided.');
|
|
233
233
|
}
|
|
234
234
|
// Validate symbols array (batch mode)
|
|
235
235
|
let symbols;
|
|
@@ -256,6 +256,7 @@ export function validateReadForEditArgs(args) {
|
|
|
256
256
|
include_callers: optionalBool(a.include_callers, 'include_callers'),
|
|
257
257
|
include_tests: optionalBool(a.include_tests, 'include_tests'),
|
|
258
258
|
include_changes: optionalBool(a.include_changes, 'include_changes'),
|
|
259
|
+
section: optionalString(a.section, 'section'),
|
|
259
260
|
};
|
|
260
261
|
}
|
|
261
262
|
/**
|
|
@@ -453,6 +454,19 @@ export function validateTestSummaryArgs(args) {
|
|
|
453
454
|
}
|
|
454
455
|
return { command: a.command, runner, timeout };
|
|
455
456
|
}
|
|
457
|
+
export function validateReadSectionArgs(args) {
|
|
458
|
+
if (!args || typeof args !== 'object') {
|
|
459
|
+
throw new Error('Arguments must be an object.');
|
|
460
|
+
}
|
|
461
|
+
const a = args;
|
|
462
|
+
if (typeof a.path !== 'string' || a.path.length === 0) {
|
|
463
|
+
throw new Error('Required parameter "path" must be a non-empty string.');
|
|
464
|
+
}
|
|
465
|
+
if (typeof a.heading !== 'string' || a.heading.length === 0) {
|
|
466
|
+
throw new Error('Required parameter "heading" must be a non-empty string.');
|
|
467
|
+
}
|
|
468
|
+
return { path: a.path, heading: a.heading };
|
|
469
|
+
}
|
|
456
470
|
/** Detect roots that would cause ast-index to scan the entire filesystem */
|
|
457
471
|
export function isDangerousRoot(root) {
|
|
458
472
|
const normalized = root.replace(/\/+$/, '') || '/';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSV parser — column-aware reading with row/column subsetting.
|
|
3
|
+
*/
|
|
4
|
+
export interface CsvOutline {
|
|
5
|
+
columns: string[];
|
|
6
|
+
rowCount: number;
|
|
7
|
+
sampleRows: string[][];
|
|
8
|
+
}
|
|
9
|
+
export interface CsvSection {
|
|
10
|
+
heading: string;
|
|
11
|
+
startLine: number;
|
|
12
|
+
endLine: number;
|
|
13
|
+
lineCount: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse CSV into an outline: columns, row count, sample.
|
|
17
|
+
* Simple parser — handles quoted fields with commas.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseCsvOutline(content: string): CsvOutline;
|
|
20
|
+
/**
|
|
21
|
+
* Parse a row range specification into a CsvSection.
|
|
22
|
+
* Supported formats:
|
|
23
|
+
* "rows:1-50" — row range (1-indexed, refers to data rows, not header)
|
|
24
|
+
* "rows:1-50" with column filter isn't supported at section level
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseCsvSectionSpec(heading: string, totalDataRows: number): CsvSection | null;
|
|
27
|
+
/**
|
|
28
|
+
* Extract CSV rows for a section. Returns header + requested rows.
|
|
29
|
+
*/
|
|
30
|
+
export declare function extractCsvSectionContent(lines: string[], section: CsvSection): string;
|
|
31
|
+
/**
|
|
32
|
+
* Format CSV outline for smart_read output.
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatCsvOutline(filePath: string, outline: CsvOutline, lineCount: number): string;
|
|
35
|
+
//# sourceMappingURL=csv-sections.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSV parser — column-aware reading with row/column subsetting.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Parse CSV into an outline: columns, row count, sample.
|
|
6
|
+
* Simple parser — handles quoted fields with commas.
|
|
7
|
+
*/
|
|
8
|
+
export function parseCsvOutline(content) {
|
|
9
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
10
|
+
if (lines.length === 0)
|
|
11
|
+
return { columns: [], rowCount: 0, sampleRows: [] };
|
|
12
|
+
const columns = parseCsvRow(lines[0]);
|
|
13
|
+
const dataLines = lines.slice(1);
|
|
14
|
+
const sampleRows = dataLines.slice(0, 5).map(parseCsvRow);
|
|
15
|
+
return {
|
|
16
|
+
columns,
|
|
17
|
+
rowCount: dataLines.length,
|
|
18
|
+
sampleRows,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Parse a row range specification into a CsvSection.
|
|
23
|
+
* Supported formats:
|
|
24
|
+
* "rows:1-50" — row range (1-indexed, refers to data rows, not header)
|
|
25
|
+
* "rows:1-50" with column filter isn't supported at section level
|
|
26
|
+
*/
|
|
27
|
+
export function parseCsvSectionSpec(heading, totalDataRows) {
|
|
28
|
+
// rows:N-M format
|
|
29
|
+
const rowMatch = heading.match(/^rows?:\s*(\d+)\s*-\s*(\d+)$/i);
|
|
30
|
+
if (rowMatch) {
|
|
31
|
+
const start = Math.max(1, parseInt(rowMatch[1], 10));
|
|
32
|
+
const end = Math.min(totalDataRows, parseInt(rowMatch[2], 10));
|
|
33
|
+
if (start > end || start > totalDataRows)
|
|
34
|
+
return null;
|
|
35
|
+
// +1 for header line offset
|
|
36
|
+
return {
|
|
37
|
+
heading: `rows ${start}-${end}`,
|
|
38
|
+
startLine: start + 1, // +1 because line 1 is header
|
|
39
|
+
endLine: end + 1,
|
|
40
|
+
lineCount: end - start + 1,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Single row number
|
|
44
|
+
const singleMatch = heading.match(/^rows?:\s*(\d+)$/i);
|
|
45
|
+
if (singleMatch) {
|
|
46
|
+
const row = parseInt(singleMatch[1], 10);
|
|
47
|
+
if (row < 1 || row > totalDataRows)
|
|
48
|
+
return null;
|
|
49
|
+
return {
|
|
50
|
+
heading: `row ${row}`,
|
|
51
|
+
startLine: row + 1,
|
|
52
|
+
endLine: row + 1,
|
|
53
|
+
lineCount: 1,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Extract CSV rows for a section. Returns header + requested rows.
|
|
60
|
+
*/
|
|
61
|
+
export function extractCsvSectionContent(lines, section) {
|
|
62
|
+
const header = lines[0]; // always include header
|
|
63
|
+
const dataRows = lines.slice(section.startLine - 1, section.endLine);
|
|
64
|
+
return [header, ...dataRows].join('\n');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse a single CSV row handling quoted fields.
|
|
68
|
+
*/
|
|
69
|
+
function parseCsvRow(line) {
|
|
70
|
+
const fields = [];
|
|
71
|
+
let current = '';
|
|
72
|
+
let inQuote = false;
|
|
73
|
+
for (let i = 0; i < line.length; i++) {
|
|
74
|
+
const ch = line[i];
|
|
75
|
+
if (inQuote) {
|
|
76
|
+
if (ch === '"') {
|
|
77
|
+
if (i + 1 < line.length && line[i + 1] === '"') {
|
|
78
|
+
current += '"';
|
|
79
|
+
i++; // skip escaped quote
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
inQuote = false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
current += ch;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
if (ch === '"') {
|
|
91
|
+
inQuote = true;
|
|
92
|
+
}
|
|
93
|
+
else if (ch === ',') {
|
|
94
|
+
fields.push(current.trim());
|
|
95
|
+
current = '';
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
current += ch;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
fields.push(current.trim());
|
|
103
|
+
return fields;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Format CSV outline for smart_read output.
|
|
107
|
+
*/
|
|
108
|
+
export function formatCsvOutline(filePath, outline, lineCount) {
|
|
109
|
+
const lines = [
|
|
110
|
+
`FILE: ${filePath} (${lineCount} lines, CSV)`,
|
|
111
|
+
'',
|
|
112
|
+
`COLUMNS (${outline.columns.length}): ${outline.columns.join(', ')}`,
|
|
113
|
+
`ROWS: ${outline.rowCount}`,
|
|
114
|
+
'',
|
|
115
|
+
];
|
|
116
|
+
if (outline.sampleRows.length > 0) {
|
|
117
|
+
lines.push(`SAMPLE (first ${outline.sampleRows.length} rows):`);
|
|
118
|
+
for (const row of outline.sampleRows) {
|
|
119
|
+
// Format as: col1=val1, col2=val2, ...
|
|
120
|
+
const pairs = outline.columns.map((col, i) => `${col}=${row[i] ?? ''}`);
|
|
121
|
+
lines.push(` ${pairs.join(', ')}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
lines.push('');
|
|
125
|
+
lines.push(`HINT: Use read_section("${filePath}", heading="rows:1-50") to load specific rows.`);
|
|
126
|
+
lines.push(` Use read_section("${filePath}", heading="rows:${outline.rowCount}") for last row.`);
|
|
127
|
+
return lines.join('\n');
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=csv-sections.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON section parser — parses top-level keys with line ranges.
|
|
3
|
+
*/
|
|
4
|
+
export interface JsonSection {
|
|
5
|
+
heading: string;
|
|
6
|
+
startLine: number;
|
|
7
|
+
endLine: number;
|
|
8
|
+
lineCount: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse JSON into sections based on top-level keys.
|
|
12
|
+
* Works with formatted JSON (pretty-printed). For minified JSON, returns empty.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseJsonSections(content: string): JsonSection[];
|
|
15
|
+
export declare function findJsonSection(sections: JsonSection[], heading: string): JsonSection | undefined;
|
|
16
|
+
export declare function extractJsonSectionContent(lines: string[], section: JsonSection): string;
|
|
17
|
+
//# sourceMappingURL=json-sections.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON section parser — parses top-level keys with line ranges.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Parse JSON into sections based on top-level keys.
|
|
6
|
+
* Works with formatted JSON (pretty-printed). For minified JSON, returns empty.
|
|
7
|
+
*/
|
|
8
|
+
export function parseJsonSections(content) {
|
|
9
|
+
if (!content.trim())
|
|
10
|
+
return [];
|
|
11
|
+
const lines = content.split('\n');
|
|
12
|
+
if (lines.length < 3)
|
|
13
|
+
return []; // minified or trivial
|
|
14
|
+
// Find top-level keys: lines matching /^\s{0,2}"key":/ (0-2 spaces indent = top level)
|
|
15
|
+
const topKeys = [];
|
|
16
|
+
// Track brace depth to identify top-level
|
|
17
|
+
let depth = 0;
|
|
18
|
+
let inString = false;
|
|
19
|
+
let lineIdx = 0;
|
|
20
|
+
for (lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
21
|
+
const line = lines[lineIdx];
|
|
22
|
+
// Simple top-level key detection: at depth 1 (inside root object)
|
|
23
|
+
// Match: "key": or "key" : at the beginning of a line (with indent)
|
|
24
|
+
if (depth === 1) {
|
|
25
|
+
const keyMatch = line.match(/^\s*"([^"]+)"\s*:/);
|
|
26
|
+
if (keyMatch) {
|
|
27
|
+
topKeys.push({ key: keyMatch[1], line: lineIdx + 1 });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Track depth (simplified — doesn't handle strings perfectly but good enough for formatted JSON)
|
|
31
|
+
for (let ci = 0; ci < line.length; ci++) {
|
|
32
|
+
const ch = line[ci];
|
|
33
|
+
if (ch === '"' && (ci === 0 || line[ci - 1] !== '\\')) {
|
|
34
|
+
inString = !inString;
|
|
35
|
+
}
|
|
36
|
+
if (!inString) {
|
|
37
|
+
if (ch === '{' || ch === '[')
|
|
38
|
+
depth++;
|
|
39
|
+
if (ch === '}' || ch === ']')
|
|
40
|
+
depth--;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (topKeys.length === 0)
|
|
45
|
+
return [];
|
|
46
|
+
const sections = [];
|
|
47
|
+
for (let i = 0; i < topKeys.length; i++) {
|
|
48
|
+
const start = topKeys[i].line;
|
|
49
|
+
const end = i + 1 < topKeys.length ? topKeys[i + 1].line - 1 : lines.length - 1; // -1 to exclude closing }
|
|
50
|
+
sections.push({
|
|
51
|
+
heading: topKeys[i].key,
|
|
52
|
+
startLine: start,
|
|
53
|
+
endLine: end,
|
|
54
|
+
lineCount: end - start + 1,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return sections;
|
|
58
|
+
}
|
|
59
|
+
export function findJsonSection(sections, heading) {
|
|
60
|
+
const normalized = heading.trim().toLowerCase();
|
|
61
|
+
return sections.find(s => s.heading.toLowerCase() === normalized);
|
|
62
|
+
}
|
|
63
|
+
export function extractJsonSectionContent(lines, section) {
|
|
64
|
+
return lines.slice(section.startLine - 1, section.endLine).join('\n');
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=json-sections.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown section parser — shared helper for section-aware tools.
|
|
3
|
+
* Parses heading structure with line ranges for targeted reading.
|
|
4
|
+
*/
|
|
5
|
+
export interface MarkdownSection {
|
|
6
|
+
heading: string;
|
|
7
|
+
level: number;
|
|
8
|
+
startLine: number;
|
|
9
|
+
endLine: number;
|
|
10
|
+
lineCount: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function parseMarkdownSections(content: string): MarkdownSection[];
|
|
13
|
+
export declare function findSection(sections: MarkdownSection[], heading: string): MarkdownSection | undefined;
|
|
14
|
+
export declare function extractSectionContent(lines: string[], section: MarkdownSection): string;
|
|
15
|
+
//# sourceMappingURL=markdown-sections.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown section parser — shared helper for section-aware tools.
|
|
3
|
+
* Parses heading structure with line ranges for targeted reading.
|
|
4
|
+
*/
|
|
5
|
+
export function parseMarkdownSections(content) {
|
|
6
|
+
if (!content.trim())
|
|
7
|
+
return [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
const headings = [];
|
|
10
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11
|
+
const match = lines[i].match(/^(#{1,6})\s+(.+)/);
|
|
12
|
+
if (match) {
|
|
13
|
+
headings.push({
|
|
14
|
+
heading: match[2].trim(),
|
|
15
|
+
level: match[1].length,
|
|
16
|
+
line: i + 1,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (headings.length === 0)
|
|
21
|
+
return [];
|
|
22
|
+
const sections = [];
|
|
23
|
+
for (let i = 0; i < headings.length; i++) {
|
|
24
|
+
const current = headings[i];
|
|
25
|
+
let endLine = lines.length;
|
|
26
|
+
for (let j = i + 1; j < headings.length; j++) {
|
|
27
|
+
if (headings[j].level <= current.level) {
|
|
28
|
+
endLine = headings[j].line - 1;
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
sections.push({
|
|
33
|
+
heading: current.heading,
|
|
34
|
+
level: current.level,
|
|
35
|
+
startLine: current.line,
|
|
36
|
+
endLine,
|
|
37
|
+
lineCount: endLine - current.line + 1,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return sections;
|
|
41
|
+
}
|
|
42
|
+
export function findSection(sections, heading) {
|
|
43
|
+
const normalized = heading.replace(/^#+\s*/, '').trim().toLowerCase();
|
|
44
|
+
return sections.find(s => s.heading.toLowerCase() === normalized);
|
|
45
|
+
}
|
|
46
|
+
export function extractSectionContent(lines, section) {
|
|
47
|
+
return lines.slice(section.startLine - 1, section.endLine).join('\n');
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=markdown-sections.js.map
|
|
@@ -2,12 +2,16 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { extname } from 'node:path';
|
|
3
3
|
import { estimateTokens } from '../core/token-estimator.js';
|
|
4
4
|
import { resolveSafePath } from '../core/validation.js';
|
|
5
|
+
import { parseMarkdownSections } from './markdown-sections.js';
|
|
6
|
+
import { parseYamlSections } from './yaml-sections.js';
|
|
7
|
+
import { parseJsonSections } from './json-sections.js';
|
|
8
|
+
import { parseCsvOutline, formatCsvOutline } from './csv-sections.js';
|
|
5
9
|
/**
|
|
6
10
|
* Detect if a file is a non-code structured file (JSON, YAML, Markdown, etc.)
|
|
7
11
|
*/
|
|
8
12
|
export function isNonCodeStructured(filePath) {
|
|
9
13
|
const ext = extname(filePath).toLowerCase();
|
|
10
|
-
return ['.json', '.yaml', '.yml', '.md', '.markdown', '.toml'].includes(ext);
|
|
14
|
+
return ['.json', '.yaml', '.yml', '.md', '.markdown', '.toml', '.csv'].includes(ext);
|
|
11
15
|
}
|
|
12
16
|
/**
|
|
13
17
|
* Generate a structural summary for non-code files.
|
|
@@ -37,6 +41,9 @@ export async function handleNonCodeRead(filePath, projectRoot, contextRegistry,
|
|
|
37
41
|
case '.toml':
|
|
38
42
|
summary = summarizeToml(filePath, content, lines.length);
|
|
39
43
|
break;
|
|
44
|
+
case '.csv':
|
|
45
|
+
summary = summarizeCsv(filePath, content, lines.length);
|
|
46
|
+
break;
|
|
40
47
|
default:
|
|
41
48
|
return null;
|
|
42
49
|
}
|
|
@@ -87,14 +94,34 @@ function summarizeJson(filePath, content, lineCount) {
|
|
|
87
94
|
catch {
|
|
88
95
|
lines.push('(Invalid JSON — parse error)');
|
|
89
96
|
}
|
|
97
|
+
// Add section line ranges for navigation
|
|
98
|
+
const jsonSections = parseJsonSections(content);
|
|
99
|
+
if (jsonSections.length > 0) {
|
|
100
|
+
lines.push('');
|
|
101
|
+
lines.push('SECTION RANGES:');
|
|
102
|
+
for (const sec of jsonSections) {
|
|
103
|
+
lines.push(` "${sec.heading}" [L${sec.startLine}-${sec.endLine}] (${sec.lineCount} lines)`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
lines.push('');
|
|
107
|
+
lines.push(`HINT: Use read_section("${filePath}", heading="<key>") to load a specific section.`);
|
|
90
108
|
return lines.join('\n');
|
|
91
109
|
}
|
|
92
110
|
function summarizeYaml(filePath, content, lineCount) {
|
|
93
111
|
const lines = [
|
|
94
112
|
`FILE: ${filePath} (${lineCount} lines, YAML)`,
|
|
95
113
|
'',
|
|
96
|
-
'STRUCTURE:',
|
|
97
114
|
];
|
|
115
|
+
// Add top-level section overview with line ranges
|
|
116
|
+
const yamlSections = parseYamlSections(content);
|
|
117
|
+
if (yamlSections.length > 0) {
|
|
118
|
+
lines.push('SECTIONS:');
|
|
119
|
+
for (const sec of yamlSections) {
|
|
120
|
+
lines.push(` ${sec.heading}: [L${sec.startLine}-${sec.endLine}] (${sec.lineCount} lines)`);
|
|
121
|
+
}
|
|
122
|
+
lines.push('');
|
|
123
|
+
}
|
|
124
|
+
lines.push('STRUCTURE:');
|
|
98
125
|
const rawLines = content.split('\n');
|
|
99
126
|
const roots = [];
|
|
100
127
|
// Stack tracks parent nodes at each indent level
|
|
@@ -159,6 +186,8 @@ function summarizeYaml(filePath, content, lineCount) {
|
|
|
159
186
|
formatYamlNode(root, lines, 1, 3); // max 3 levels deep
|
|
160
187
|
}
|
|
161
188
|
}
|
|
189
|
+
lines.push('');
|
|
190
|
+
lines.push(`HINT: Use read_section("${filePath}", heading="<key>") to load a specific section.`);
|
|
162
191
|
return lines.join('\n');
|
|
163
192
|
}
|
|
164
193
|
function formatYamlNode(node, lines, depth, maxDepth) {
|
|
@@ -194,16 +223,17 @@ function summarizeMarkdown(filePath, content, lineCount) {
|
|
|
194
223
|
const lines = [
|
|
195
224
|
`FILE: ${filePath} (${lineCount} lines, Markdown)`,
|
|
196
225
|
'',
|
|
197
|
-
'TABLE OF CONTENTS:',
|
|
198
226
|
];
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
227
|
+
const sections = parseMarkdownSections(content);
|
|
228
|
+
if (sections.length === 0) {
|
|
229
|
+
lines.push('(No headings found)');
|
|
230
|
+
return lines.join('\n');
|
|
231
|
+
}
|
|
232
|
+
lines.push('SECTIONS:');
|
|
233
|
+
for (const sec of sections) {
|
|
234
|
+
const indent = ' '.repeat(sec.level);
|
|
235
|
+
const hashes = '#'.repeat(sec.level);
|
|
236
|
+
lines.push(`${indent}${hashes} ${sec.heading} [L${sec.startLine}-${sec.endLine}] (${sec.lineCount} lines)`);
|
|
207
237
|
}
|
|
208
238
|
// Count code blocks
|
|
209
239
|
const codeBlocks = (content.match(/```/g) || []).length / 2;
|
|
@@ -211,6 +241,9 @@ function summarizeMarkdown(filePath, content, lineCount) {
|
|
|
211
241
|
lines.push('');
|
|
212
242
|
lines.push(`Code blocks: ${Math.floor(codeBlocks)}`);
|
|
213
243
|
}
|
|
244
|
+
lines.push('');
|
|
245
|
+
lines.push(`HINT: Use read_section("${filePath}", heading="<name>") to load a specific section.`);
|
|
246
|
+
lines.push(` Use read_for_edit("${filePath}", section="<name>") for edit context.`);
|
|
214
247
|
return lines.join('\n');
|
|
215
248
|
}
|
|
216
249
|
function summarizeToml(filePath, content, lineCount) {
|
|
@@ -228,4 +261,8 @@ function summarizeToml(filePath, content, lineCount) {
|
|
|
228
261
|
}
|
|
229
262
|
return lines.join('\n');
|
|
230
263
|
}
|
|
264
|
+
function summarizeCsv(filePath, content, lineCount) {
|
|
265
|
+
const outline = parseCsvOutline(content);
|
|
266
|
+
return formatCsvOutline(filePath, outline, lineCount);
|
|
267
|
+
}
|
|
231
268
|
//# sourceMappingURL=non-code.js.map
|
|
@@ -11,6 +11,7 @@ export interface ReadForEditArgs {
|
|
|
11
11
|
include_callers?: boolean;
|
|
12
12
|
include_tests?: boolean;
|
|
13
13
|
include_changes?: boolean;
|
|
14
|
+
section?: string;
|
|
14
15
|
}
|
|
15
16
|
export declare function handleReadForEdit(args: ReadForEditArgs, projectRoot: string, symbolResolver: SymbolResolver, fileCache: FileCache, contextRegistry: ContextRegistry, astIndex: AstIndexClient, options?: {
|
|
16
17
|
actionableHints?: boolean;
|
|
@@ -2,7 +2,11 @@ import { readFile, stat, access } from 'node:fs/promises';
|
|
|
2
2
|
import { execFile } from 'node:child_process';
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
|
-
import { relative, join } from 'node:path';
|
|
5
|
+
import { relative, join, extname } from 'node:path';
|
|
6
|
+
import { parseMarkdownSections, findSection, extractSectionContent } from './markdown-sections.js';
|
|
7
|
+
import { parseYamlSections, findYamlSection, extractYamlSectionContent } from './yaml-sections.js';
|
|
8
|
+
import { parseJsonSections, findJsonSection, extractJsonSectionContent } from './json-sections.js';
|
|
9
|
+
import { parseCsvOutline, parseCsvSectionSpec, extractCsvSectionContent } from './csv-sections.js';
|
|
6
10
|
import { estimateTokens } from '../core/token-estimator.js';
|
|
7
11
|
import { resolveSafePath } from '../core/validation.js';
|
|
8
12
|
import { assessConfidence, formatConfidence } from '../core/confidence.js';
|
|
@@ -11,6 +15,108 @@ const DEFAULT_CONTEXT = 5;
|
|
|
11
15
|
export async function handleReadForEdit(args, projectRoot, symbolResolver, fileCache, contextRegistry, astIndex, options) {
|
|
12
16
|
const absPath = resolveSafePath(projectRoot, args.path);
|
|
13
17
|
const ctx = args.context ?? DEFAULT_CONTEXT;
|
|
18
|
+
// Section mode: markdown/YAML section extraction for edit
|
|
19
|
+
if (args.section) {
|
|
20
|
+
const ext = extname(absPath).toLowerCase();
|
|
21
|
+
const supportedExts = new Set(['.md', '.markdown', '.yaml', '.yml', '.json', '.csv']);
|
|
22
|
+
if (!supportedExts.has(ext)) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{
|
|
25
|
+
type: 'text',
|
|
26
|
+
text: `"section" parameter only works with Markdown, YAML, or JSON files. Got: ${ext}. Use "symbol" for code files.`,
|
|
27
|
+
}],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const fileContent = await readFile(absPath, 'utf-8');
|
|
31
|
+
const fileLines = fileContent.split('\n');
|
|
32
|
+
// Cache file in fileCache for read_diff baseline
|
|
33
|
+
if (!fileCache.get(absPath)) {
|
|
34
|
+
const fileStat = await stat(absPath);
|
|
35
|
+
const hash = createHash('sha256').update(fileContent).digest('hex');
|
|
36
|
+
const language = ext === '.csv' ? 'csv' : ext === '.json' ? 'json' : (ext === '.md' || ext === '.markdown') ? 'markdown' : 'yaml';
|
|
37
|
+
fileCache.set(absPath, {
|
|
38
|
+
structure: { path: absPath, language, meta: { lines: fileLines.length, bytes: fileContent.length, lastModified: fileStat.mtimeMs, contentHash: hash }, imports: [], exports: [], symbols: [] },
|
|
39
|
+
content: fileContent, lines: fileLines, mtime: fileStat.mtimeMs, hash, lastAccess: Date.now(),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
let sectionResult = null;
|
|
43
|
+
if (ext === '.md' || ext === '.markdown') {
|
|
44
|
+
const sections = parseMarkdownSections(fileContent);
|
|
45
|
+
const section = findSection(sections, args.section);
|
|
46
|
+
if (!section) {
|
|
47
|
+
const available = sections.map(s => s.heading).join(', ');
|
|
48
|
+
return {
|
|
49
|
+
content: [{
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: `Section "${args.section}" not found in ${args.path}.\nAvailable: ${available}`,
|
|
52
|
+
}],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const hashes = '#'.repeat(section.level);
|
|
56
|
+
sectionResult = { ...section, rawContent: extractSectionContent(fileLines, section), label: `${hashes} ${section.heading}` };
|
|
57
|
+
}
|
|
58
|
+
else if (ext === '.yaml' || ext === '.yml') {
|
|
59
|
+
const sections = parseYamlSections(fileContent);
|
|
60
|
+
const section = findYamlSection(sections, args.section);
|
|
61
|
+
if (!section) {
|
|
62
|
+
const available = sections.map(s => s.heading).join(', ');
|
|
63
|
+
return {
|
|
64
|
+
content: [{
|
|
65
|
+
type: 'text',
|
|
66
|
+
text: `Section "${args.section}" not found in ${args.path}.\nAvailable: ${available}`,
|
|
67
|
+
}],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
sectionResult = { ...section, rawContent: extractYamlSectionContent(fileLines, section), label: section.heading };
|
|
71
|
+
}
|
|
72
|
+
else if (ext === '.json') {
|
|
73
|
+
const sections = parseJsonSections(fileContent);
|
|
74
|
+
const section = findJsonSection(sections, args.section);
|
|
75
|
+
if (!section) {
|
|
76
|
+
const available = sections.map(s => s.heading).join(', ');
|
|
77
|
+
return {
|
|
78
|
+
content: [{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: `Section "${args.section}" not found in ${args.path}.\nAvailable: ${available}`,
|
|
81
|
+
}],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
sectionResult = { ...section, rawContent: extractJsonSectionContent(fileLines, section), label: section.heading };
|
|
85
|
+
}
|
|
86
|
+
else if (ext === '.csv') {
|
|
87
|
+
const outline = parseCsvOutline(fileContent);
|
|
88
|
+
const section = parseCsvSectionSpec(args.section, outline.rowCount);
|
|
89
|
+
if (!section) {
|
|
90
|
+
return {
|
|
91
|
+
content: [{
|
|
92
|
+
type: 'text',
|
|
93
|
+
text: `Invalid section "${args.section}" for CSV. Use: rows:1-50 or row:5\nTotal rows: ${outline.rowCount}`,
|
|
94
|
+
}],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
sectionResult = { ...section, rawContent: extractCsvSectionContent(fileLines, section), label: section.heading };
|
|
98
|
+
}
|
|
99
|
+
if (!sectionResult) {
|
|
100
|
+
return { content: [{ type: 'text', text: `Unsupported file type: ${ext}` }] };
|
|
101
|
+
}
|
|
102
|
+
const outputLines = [
|
|
103
|
+
`FILE: ${args.path}`,
|
|
104
|
+
`EDIT SECTION: ${sectionResult.label} [L${sectionResult.startLine}-${sectionResult.endLine}] (${sectionResult.lineCount} lines)`,
|
|
105
|
+
'',
|
|
106
|
+
sectionResult.rawContent,
|
|
107
|
+
'',
|
|
108
|
+
`AFTER EDIT: Use read_diff("${args.path}") to verify changes (90% cheaper than re-reading).`,
|
|
109
|
+
];
|
|
110
|
+
const output = outputLines.join('\n');
|
|
111
|
+
const tokens = estimateTokens(output);
|
|
112
|
+
contextRegistry.trackLoad(absPath, {
|
|
113
|
+
type: 'range',
|
|
114
|
+
startLine: sectionResult.startLine,
|
|
115
|
+
endLine: sectionResult.endLine,
|
|
116
|
+
tokens,
|
|
117
|
+
});
|
|
118
|
+
return { content: [{ type: 'text', text: output }] };
|
|
119
|
+
}
|
|
14
120
|
// Get file content — also cache for read_diff baseline
|
|
15
121
|
const cached = fileCache.get(absPath);
|
|
16
122
|
let lines;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ContextRegistry } from '../core/context-registry.js';
|
|
2
|
+
export interface ReadSectionArgs {
|
|
3
|
+
path: string;
|
|
4
|
+
heading: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function handleReadSection(args: ReadSectionArgs, projectRoot: string, contextRegistry: ContextRegistry): Promise<{
|
|
7
|
+
content: Array<{
|
|
8
|
+
type: 'text';
|
|
9
|
+
text: string;
|
|
10
|
+
}>;
|
|
11
|
+
}>;
|
|
12
|
+
//# sourceMappingURL=read-section.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { extname } from 'node:path';
|
|
3
|
+
import { estimateTokens } from '../core/token-estimator.js';
|
|
4
|
+
import { resolveSafePath } from '../core/validation.js';
|
|
5
|
+
import { parseMarkdownSections, findSection, extractSectionContent } from './markdown-sections.js';
|
|
6
|
+
import { parseYamlSections, findYamlSection, extractYamlSectionContent } from './yaml-sections.js';
|
|
7
|
+
import { parseJsonSections, findJsonSection, extractJsonSectionContent } from './json-sections.js';
|
|
8
|
+
import { parseCsvOutline, parseCsvSectionSpec, extractCsvSectionContent } from './csv-sections.js';
|
|
9
|
+
export async function handleReadSection(args, projectRoot, contextRegistry) {
|
|
10
|
+
const absPath = resolveSafePath(projectRoot, args.path);
|
|
11
|
+
const ext = extname(absPath).toLowerCase();
|
|
12
|
+
const content = await readFile(absPath, 'utf-8');
|
|
13
|
+
const lines = content.split('\n');
|
|
14
|
+
// Dispatch to format-specific parser
|
|
15
|
+
let sectionData = null;
|
|
16
|
+
if (ext === '.md' || ext === '.markdown') {
|
|
17
|
+
const sections = parseMarkdownSections(content);
|
|
18
|
+
const section = findSection(sections, args.heading);
|
|
19
|
+
if (!section) {
|
|
20
|
+
return {
|
|
21
|
+
content: [{
|
|
22
|
+
type: 'text',
|
|
23
|
+
text: `Section "${args.heading}" not found in ${args.path}.\nAvailable sections: ${sections.map(s => s.heading).join(', ')}`,
|
|
24
|
+
}],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const hashes = '#'.repeat(section.level);
|
|
28
|
+
sectionData = { ...section, content: extractSectionContent(lines, section), label: `${hashes} ${section.heading}` };
|
|
29
|
+
}
|
|
30
|
+
else if (ext === '.yaml' || ext === '.yml') {
|
|
31
|
+
const sections = parseYamlSections(content);
|
|
32
|
+
const section = findYamlSection(sections, args.heading);
|
|
33
|
+
if (!section) {
|
|
34
|
+
return {
|
|
35
|
+
content: [{
|
|
36
|
+
type: 'text',
|
|
37
|
+
text: `Section "${args.heading}" not found in ${args.path}.\nAvailable sections: ${sections.map(s => s.heading).join(', ')}`,
|
|
38
|
+
}],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
sectionData = { ...section, content: extractYamlSectionContent(lines, section), label: section.heading };
|
|
42
|
+
}
|
|
43
|
+
else if (ext === '.json') {
|
|
44
|
+
const sections = parseJsonSections(content);
|
|
45
|
+
const section = findJsonSection(sections, args.heading);
|
|
46
|
+
if (!section) {
|
|
47
|
+
return {
|
|
48
|
+
content: [{
|
|
49
|
+
type: 'text',
|
|
50
|
+
text: `Section "${args.heading}" not found in ${args.path}.\nAvailable sections: ${sections.map(s => s.heading).join(', ')}`,
|
|
51
|
+
}],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
sectionData = { ...section, content: extractJsonSectionContent(lines, section), label: section.heading };
|
|
55
|
+
}
|
|
56
|
+
else if (ext === '.csv') {
|
|
57
|
+
const outline = parseCsvOutline(content);
|
|
58
|
+
const section = parseCsvSectionSpec(args.heading, outline.rowCount);
|
|
59
|
+
if (!section) {
|
|
60
|
+
return {
|
|
61
|
+
content: [{
|
|
62
|
+
type: 'text',
|
|
63
|
+
text: `Invalid section spec "${args.heading}" for CSV. Use format: rows:1-50 or row:5\nTotal rows: ${outline.rowCount}`,
|
|
64
|
+
}],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
sectionData = { ...section, content: extractCsvSectionContent(lines, section), label: section.heading };
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return {
|
|
71
|
+
content: [{
|
|
72
|
+
type: 'text',
|
|
73
|
+
text: `read_section supports: .md, .yaml, .yml, .json, .csv. Got: ${ext}`,
|
|
74
|
+
}],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const outputLines = [
|
|
78
|
+
`FILE: ${args.path}`,
|
|
79
|
+
`SECTION: ${sectionData.label} [L${sectionData.startLine}-${sectionData.endLine}] (${sectionData.lineCount} lines)`,
|
|
80
|
+
'',
|
|
81
|
+
sectionData.content,
|
|
82
|
+
'',
|
|
83
|
+
`HINT: Use read_for_edit("${args.path}", section="${sectionData.heading}") for edit context.`,
|
|
84
|
+
'CONTEXT TRACKED.',
|
|
85
|
+
];
|
|
86
|
+
const output = outputLines.join('\n');
|
|
87
|
+
const tokens = estimateTokens(output);
|
|
88
|
+
contextRegistry.trackLoad(absPath, {
|
|
89
|
+
type: 'range',
|
|
90
|
+
startLine: sectionData.startLine,
|
|
91
|
+
endLine: sectionData.endLine,
|
|
92
|
+
tokens,
|
|
93
|
+
});
|
|
94
|
+
return { content: [{ type: 'text', text: output }] };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=read-section.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML section parser — parses top-level keys with line ranges.
|
|
3
|
+
*/
|
|
4
|
+
export interface YamlSection {
|
|
5
|
+
heading: string;
|
|
6
|
+
startLine: number;
|
|
7
|
+
endLine: number;
|
|
8
|
+
lineCount: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse YAML into sections based on top-level keys.
|
|
12
|
+
* A top-level key is a key at indent level 0.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseYamlSections(content: string): YamlSection[];
|
|
15
|
+
export declare function findYamlSection(sections: YamlSection[], heading: string): YamlSection | undefined;
|
|
16
|
+
export declare function extractYamlSectionContent(lines: string[], section: YamlSection): string;
|
|
17
|
+
//# sourceMappingURL=yaml-sections.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML section parser — parses top-level keys with line ranges.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Parse YAML into sections based on top-level keys.
|
|
6
|
+
* A top-level key is a key at indent level 0.
|
|
7
|
+
*/
|
|
8
|
+
export function parseYamlSections(content) {
|
|
9
|
+
if (!content.trim())
|
|
10
|
+
return [];
|
|
11
|
+
const lines = content.split('\n');
|
|
12
|
+
const topKeys = [];
|
|
13
|
+
for (let i = 0; i < lines.length; i++) {
|
|
14
|
+
const line = lines[i];
|
|
15
|
+
// Skip comments and empty lines
|
|
16
|
+
if (!line.trim() || line.trim().startsWith('#'))
|
|
17
|
+
continue;
|
|
18
|
+
// Top-level key: starts at column 0, has format "key:" or "key: value"
|
|
19
|
+
const match = line.match(/^([a-zA-Z_][a-zA-Z0-9_.-]*):/);
|
|
20
|
+
if (match) {
|
|
21
|
+
topKeys.push({ key: match[1], line: i + 1 });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (topKeys.length === 0)
|
|
25
|
+
return [];
|
|
26
|
+
const sections = [];
|
|
27
|
+
for (let i = 0; i < topKeys.length; i++) {
|
|
28
|
+
const start = topKeys[i].line;
|
|
29
|
+
const end = i + 1 < topKeys.length ? topKeys[i + 1].line - 1 : lines.length;
|
|
30
|
+
sections.push({
|
|
31
|
+
heading: topKeys[i].key,
|
|
32
|
+
startLine: start,
|
|
33
|
+
endLine: end,
|
|
34
|
+
lineCount: end - start + 1,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return sections;
|
|
38
|
+
}
|
|
39
|
+
export function findYamlSection(sections, heading) {
|
|
40
|
+
const normalized = heading.trim().toLowerCase().replace(/:$/, '');
|
|
41
|
+
return sections.find(s => s.heading.toLowerCase() === normalized);
|
|
42
|
+
}
|
|
43
|
+
export function extractYamlSectionContent(lines, section) {
|
|
44
|
+
return lines.slice(section.startLine - 1, section.endLine).join('\n');
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=yaml-sections.js.map
|
|
@@ -38,12 +38,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
38
38
|
symbols?: undefined;
|
|
39
39
|
start_line?: undefined;
|
|
40
40
|
end_line?: undefined;
|
|
41
|
+
heading?: undefined;
|
|
41
42
|
context_lines?: undefined;
|
|
42
43
|
line?: undefined;
|
|
43
44
|
context?: undefined;
|
|
44
45
|
include_callers?: undefined;
|
|
45
46
|
include_tests?: undefined;
|
|
46
47
|
include_changes?: undefined;
|
|
48
|
+
section?: undefined;
|
|
47
49
|
paths?: undefined;
|
|
48
50
|
kind?: undefined;
|
|
49
51
|
limit?: undefined;
|
|
@@ -104,12 +106,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
104
106
|
symbols?: undefined;
|
|
105
107
|
start_line?: undefined;
|
|
106
108
|
end_line?: undefined;
|
|
109
|
+
heading?: undefined;
|
|
107
110
|
context_lines?: undefined;
|
|
108
111
|
line?: undefined;
|
|
109
112
|
context?: undefined;
|
|
110
113
|
include_callers?: undefined;
|
|
111
114
|
include_tests?: undefined;
|
|
112
115
|
include_changes?: undefined;
|
|
116
|
+
section?: undefined;
|
|
113
117
|
paths?: undefined;
|
|
114
118
|
kind?: undefined;
|
|
115
119
|
limit?: undefined;
|
|
@@ -170,12 +174,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
170
174
|
include_edit_context?: undefined;
|
|
171
175
|
start_line?: undefined;
|
|
172
176
|
end_line?: undefined;
|
|
177
|
+
heading?: undefined;
|
|
173
178
|
context_lines?: undefined;
|
|
174
179
|
line?: undefined;
|
|
175
180
|
context?: undefined;
|
|
176
181
|
include_callers?: undefined;
|
|
177
182
|
include_tests?: undefined;
|
|
178
183
|
include_changes?: undefined;
|
|
184
|
+
section?: undefined;
|
|
179
185
|
paths?: undefined;
|
|
180
186
|
kind?: undefined;
|
|
181
187
|
limit?: undefined;
|
|
@@ -226,12 +232,69 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
226
232
|
show?: undefined;
|
|
227
233
|
include_edit_context?: undefined;
|
|
228
234
|
symbols?: undefined;
|
|
235
|
+
heading?: undefined;
|
|
229
236
|
context_lines?: undefined;
|
|
230
237
|
line?: undefined;
|
|
231
238
|
context?: undefined;
|
|
232
239
|
include_callers?: undefined;
|
|
233
240
|
include_tests?: undefined;
|
|
234
241
|
include_changes?: undefined;
|
|
242
|
+
section?: undefined;
|
|
243
|
+
paths?: undefined;
|
|
244
|
+
kind?: undefined;
|
|
245
|
+
limit?: undefined;
|
|
246
|
+
lang?: undefined;
|
|
247
|
+
mode?: undefined;
|
|
248
|
+
include?: undefined;
|
|
249
|
+
recursive?: undefined;
|
|
250
|
+
max_depth?: undefined;
|
|
251
|
+
verbose?: undefined;
|
|
252
|
+
module?: undefined;
|
|
253
|
+
export_only?: undefined;
|
|
254
|
+
check?: undefined;
|
|
255
|
+
pattern?: undefined;
|
|
256
|
+
name?: undefined;
|
|
257
|
+
ref?: undefined;
|
|
258
|
+
count?: undefined;
|
|
259
|
+
command?: undefined;
|
|
260
|
+
runner?: undefined;
|
|
261
|
+
timeout?: undefined;
|
|
262
|
+
};
|
|
263
|
+
required: string[];
|
|
264
|
+
};
|
|
265
|
+
} | {
|
|
266
|
+
name: string;
|
|
267
|
+
description: string;
|
|
268
|
+
inputSchema: {
|
|
269
|
+
type: "object";
|
|
270
|
+
properties: {
|
|
271
|
+
path: {
|
|
272
|
+
type: string;
|
|
273
|
+
description: string;
|
|
274
|
+
};
|
|
275
|
+
heading: {
|
|
276
|
+
type: string;
|
|
277
|
+
description: string;
|
|
278
|
+
};
|
|
279
|
+
show_imports?: undefined;
|
|
280
|
+
show_docs?: undefined;
|
|
281
|
+
depth?: undefined;
|
|
282
|
+
scope?: undefined;
|
|
283
|
+
symbol?: undefined;
|
|
284
|
+
context_before?: undefined;
|
|
285
|
+
context_after?: undefined;
|
|
286
|
+
show?: undefined;
|
|
287
|
+
include_edit_context?: undefined;
|
|
288
|
+
symbols?: undefined;
|
|
289
|
+
start_line?: undefined;
|
|
290
|
+
end_line?: undefined;
|
|
291
|
+
context_lines?: undefined;
|
|
292
|
+
line?: undefined;
|
|
293
|
+
context?: undefined;
|
|
294
|
+
include_callers?: undefined;
|
|
295
|
+
include_tests?: undefined;
|
|
296
|
+
include_changes?: undefined;
|
|
297
|
+
section?: undefined;
|
|
235
298
|
paths?: undefined;
|
|
236
299
|
kind?: undefined;
|
|
237
300
|
limit?: undefined;
|
|
@@ -280,11 +343,13 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
280
343
|
symbols?: undefined;
|
|
281
344
|
start_line?: undefined;
|
|
282
345
|
end_line?: undefined;
|
|
346
|
+
heading?: undefined;
|
|
283
347
|
line?: undefined;
|
|
284
348
|
context?: undefined;
|
|
285
349
|
include_callers?: undefined;
|
|
286
350
|
include_tests?: undefined;
|
|
287
351
|
include_changes?: undefined;
|
|
352
|
+
section?: undefined;
|
|
288
353
|
paths?: undefined;
|
|
289
354
|
kind?: undefined;
|
|
290
355
|
limit?: undefined;
|
|
@@ -348,6 +413,10 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
348
413
|
type: string;
|
|
349
414
|
description: string;
|
|
350
415
|
};
|
|
416
|
+
section: {
|
|
417
|
+
type: string;
|
|
418
|
+
description: string;
|
|
419
|
+
};
|
|
351
420
|
show_imports?: undefined;
|
|
352
421
|
show_docs?: undefined;
|
|
353
422
|
depth?: undefined;
|
|
@@ -358,6 +427,7 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
358
427
|
include_edit_context?: undefined;
|
|
359
428
|
start_line?: undefined;
|
|
360
429
|
end_line?: undefined;
|
|
430
|
+
heading?: undefined;
|
|
361
431
|
context_lines?: undefined;
|
|
362
432
|
paths?: undefined;
|
|
363
433
|
kind?: undefined;
|
|
@@ -407,12 +477,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
407
477
|
symbols?: undefined;
|
|
408
478
|
start_line?: undefined;
|
|
409
479
|
end_line?: undefined;
|
|
480
|
+
heading?: undefined;
|
|
410
481
|
context_lines?: undefined;
|
|
411
482
|
line?: undefined;
|
|
412
483
|
context?: undefined;
|
|
413
484
|
include_callers?: undefined;
|
|
414
485
|
include_tests?: undefined;
|
|
415
486
|
include_changes?: undefined;
|
|
487
|
+
section?: undefined;
|
|
416
488
|
kind?: undefined;
|
|
417
489
|
limit?: undefined;
|
|
418
490
|
lang?: undefined;
|
|
@@ -482,11 +554,13 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
482
554
|
symbols?: undefined;
|
|
483
555
|
start_line?: undefined;
|
|
484
556
|
end_line?: undefined;
|
|
557
|
+
heading?: undefined;
|
|
485
558
|
line?: undefined;
|
|
486
559
|
context?: undefined;
|
|
487
560
|
include_callers?: undefined;
|
|
488
561
|
include_tests?: undefined;
|
|
489
562
|
include_changes?: undefined;
|
|
563
|
+
section?: undefined;
|
|
490
564
|
paths?: undefined;
|
|
491
565
|
include?: undefined;
|
|
492
566
|
recursive?: undefined;
|
|
@@ -532,12 +606,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
532
606
|
symbols?: undefined;
|
|
533
607
|
start_line?: undefined;
|
|
534
608
|
end_line?: undefined;
|
|
609
|
+
heading?: undefined;
|
|
535
610
|
context_lines?: undefined;
|
|
536
611
|
line?: undefined;
|
|
537
612
|
context?: undefined;
|
|
538
613
|
include_callers?: undefined;
|
|
539
614
|
include_tests?: undefined;
|
|
540
615
|
include_changes?: undefined;
|
|
616
|
+
section?: undefined;
|
|
541
617
|
paths?: undefined;
|
|
542
618
|
kind?: undefined;
|
|
543
619
|
limit?: undefined;
|
|
@@ -581,12 +657,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
581
657
|
symbols?: undefined;
|
|
582
658
|
start_line?: undefined;
|
|
583
659
|
end_line?: undefined;
|
|
660
|
+
heading?: undefined;
|
|
584
661
|
context_lines?: undefined;
|
|
585
662
|
line?: undefined;
|
|
586
663
|
context?: undefined;
|
|
587
664
|
include_callers?: undefined;
|
|
588
665
|
include_tests?: undefined;
|
|
589
666
|
include_changes?: undefined;
|
|
667
|
+
section?: undefined;
|
|
590
668
|
paths?: undefined;
|
|
591
669
|
kind?: undefined;
|
|
592
670
|
limit?: undefined;
|
|
@@ -639,12 +717,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
639
717
|
symbols?: undefined;
|
|
640
718
|
start_line?: undefined;
|
|
641
719
|
end_line?: undefined;
|
|
720
|
+
heading?: undefined;
|
|
642
721
|
context_lines?: undefined;
|
|
643
722
|
line?: undefined;
|
|
644
723
|
context?: undefined;
|
|
645
724
|
include_callers?: undefined;
|
|
646
725
|
include_tests?: undefined;
|
|
647
726
|
include_changes?: undefined;
|
|
727
|
+
section?: undefined;
|
|
648
728
|
paths?: undefined;
|
|
649
729
|
kind?: undefined;
|
|
650
730
|
limit?: undefined;
|
|
@@ -688,12 +768,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
688
768
|
symbols?: undefined;
|
|
689
769
|
start_line?: undefined;
|
|
690
770
|
end_line?: undefined;
|
|
771
|
+
heading?: undefined;
|
|
691
772
|
context_lines?: undefined;
|
|
692
773
|
line?: undefined;
|
|
693
774
|
context?: undefined;
|
|
694
775
|
include_callers?: undefined;
|
|
695
776
|
include_tests?: undefined;
|
|
696
777
|
include_changes?: undefined;
|
|
778
|
+
section?: undefined;
|
|
697
779
|
paths?: undefined;
|
|
698
780
|
kind?: undefined;
|
|
699
781
|
limit?: undefined;
|
|
@@ -746,12 +828,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
746
828
|
symbols?: undefined;
|
|
747
829
|
start_line?: undefined;
|
|
748
830
|
end_line?: undefined;
|
|
831
|
+
heading?: undefined;
|
|
749
832
|
context_lines?: undefined;
|
|
750
833
|
line?: undefined;
|
|
751
834
|
context?: undefined;
|
|
752
835
|
include_callers?: undefined;
|
|
753
836
|
include_tests?: undefined;
|
|
754
837
|
include_changes?: undefined;
|
|
838
|
+
section?: undefined;
|
|
755
839
|
paths?: undefined;
|
|
756
840
|
kind?: undefined;
|
|
757
841
|
lang?: undefined;
|
|
@@ -811,12 +895,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
811
895
|
symbols?: undefined;
|
|
812
896
|
start_line?: undefined;
|
|
813
897
|
end_line?: undefined;
|
|
898
|
+
heading?: undefined;
|
|
814
899
|
context_lines?: undefined;
|
|
815
900
|
line?: undefined;
|
|
816
901
|
context?: undefined;
|
|
817
902
|
include_callers?: undefined;
|
|
818
903
|
include_tests?: undefined;
|
|
819
904
|
include_changes?: undefined;
|
|
905
|
+
section?: undefined;
|
|
820
906
|
paths?: undefined;
|
|
821
907
|
kind?: undefined;
|
|
822
908
|
mode?: undefined;
|
|
@@ -862,12 +948,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
862
948
|
symbols?: undefined;
|
|
863
949
|
start_line?: undefined;
|
|
864
950
|
end_line?: undefined;
|
|
951
|
+
heading?: undefined;
|
|
865
952
|
context_lines?: undefined;
|
|
866
953
|
line?: undefined;
|
|
867
954
|
context?: undefined;
|
|
868
955
|
include_callers?: undefined;
|
|
869
956
|
include_tests?: undefined;
|
|
870
957
|
include_changes?: undefined;
|
|
958
|
+
section?: undefined;
|
|
871
959
|
paths?: undefined;
|
|
872
960
|
kind?: undefined;
|
|
873
961
|
limit?: undefined;
|
|
@@ -918,12 +1006,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
918
1006
|
symbols?: undefined;
|
|
919
1007
|
start_line?: undefined;
|
|
920
1008
|
end_line?: undefined;
|
|
1009
|
+
heading?: undefined;
|
|
921
1010
|
context_lines?: undefined;
|
|
922
1011
|
line?: undefined;
|
|
923
1012
|
context?: undefined;
|
|
924
1013
|
include_callers?: undefined;
|
|
925
1014
|
include_tests?: undefined;
|
|
926
1015
|
include_changes?: undefined;
|
|
1016
|
+
section?: undefined;
|
|
927
1017
|
paths?: undefined;
|
|
928
1018
|
kind?: undefined;
|
|
929
1019
|
limit?: undefined;
|
|
@@ -975,12 +1065,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
975
1065
|
symbols?: undefined;
|
|
976
1066
|
start_line?: undefined;
|
|
977
1067
|
end_line?: undefined;
|
|
1068
|
+
heading?: undefined;
|
|
978
1069
|
context_lines?: undefined;
|
|
979
1070
|
line?: undefined;
|
|
980
1071
|
context?: undefined;
|
|
981
1072
|
include_callers?: undefined;
|
|
982
1073
|
include_tests?: undefined;
|
|
983
1074
|
include_changes?: undefined;
|
|
1075
|
+
section?: undefined;
|
|
984
1076
|
paths?: undefined;
|
|
985
1077
|
kind?: undefined;
|
|
986
1078
|
limit?: undefined;
|
|
@@ -1032,12 +1124,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1032
1124
|
symbols?: undefined;
|
|
1033
1125
|
start_line?: undefined;
|
|
1034
1126
|
end_line?: undefined;
|
|
1127
|
+
heading?: undefined;
|
|
1035
1128
|
context_lines?: undefined;
|
|
1036
1129
|
line?: undefined;
|
|
1037
1130
|
context?: undefined;
|
|
1038
1131
|
include_callers?: undefined;
|
|
1039
1132
|
include_tests?: undefined;
|
|
1040
1133
|
include_changes?: undefined;
|
|
1134
|
+
section?: undefined;
|
|
1041
1135
|
paths?: undefined;
|
|
1042
1136
|
kind?: undefined;
|
|
1043
1137
|
limit?: undefined;
|
|
@@ -1090,12 +1184,14 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1090
1184
|
symbols?: undefined;
|
|
1091
1185
|
start_line?: undefined;
|
|
1092
1186
|
end_line?: undefined;
|
|
1187
|
+
heading?: undefined;
|
|
1093
1188
|
context_lines?: undefined;
|
|
1094
1189
|
line?: undefined;
|
|
1095
1190
|
context?: undefined;
|
|
1096
1191
|
include_callers?: undefined;
|
|
1097
1192
|
include_tests?: undefined;
|
|
1098
1193
|
include_changes?: undefined;
|
|
1194
|
+
section?: undefined;
|
|
1099
1195
|
paths?: undefined;
|
|
1100
1196
|
kind?: undefined;
|
|
1101
1197
|
limit?: undefined;
|
|
@@ -27,12 +27,15 @@ export const MCP_INSTRUCTIONS = [
|
|
|
27
27
|
'15. Code quality → code_audit (TODOs, deprecated, structural patterns)',
|
|
28
28
|
'16. Dead code → find_unused (unreferenced symbols across project)',
|
|
29
29
|
'17. Module architecture → module_info (deps, dependents, public API)',
|
|
30
|
+
'18. Read markdown/yaml/json/csv section → read_section (loads one heading/key/row-range, NOT the whole file)',
|
|
31
|
+
' - For editing sections: read_for_edit(path, section="Section Name")',
|
|
30
32
|
'',
|
|
31
33
|
'USE DEFAULT TOOLS ONLY FOR: regex text search → Grep | exact raw content → Read | non-code configs → Read',
|
|
32
34
|
'',
|
|
33
35
|
'WORKFLOWS:',
|
|
34
36
|
'• Explore: project_overview → explore_area → smart_read → read_symbol',
|
|
35
37
|
'• Edit: smart_read → read_symbol(include_edit_context=true) → Edit → read_diff',
|
|
38
|
+
'• Docs: smart_read (outline) → read_section → read_for_edit(section=) → Edit → read_diff',
|
|
36
39
|
'• Refactor: find_usages → read_symbols → read_for_edit → Edit → test_summary',
|
|
37
40
|
'• Audit: code_audit + find_unused + Grep (for regex patterns)',
|
|
38
41
|
].join('\n');
|
|
@@ -108,6 +111,18 @@ export const TOOL_DEFINITIONS = [
|
|
|
108
111
|
required: ['path', 'start_line', 'end_line'],
|
|
109
112
|
},
|
|
110
113
|
},
|
|
114
|
+
{
|
|
115
|
+
name: 'read_section',
|
|
116
|
+
description: 'Read a specific section from Markdown, YAML, JSON, or CSV files. Markdown: by heading name. YAML/JSON: by top-level key. CSV: by row range (rows:1-50). Much cheaper than reading the whole file.',
|
|
117
|
+
inputSchema: {
|
|
118
|
+
type: 'object',
|
|
119
|
+
properties: {
|
|
120
|
+
path: { type: 'string', description: 'Path to .md, .yaml, .yml, .json, or .csv file' },
|
|
121
|
+
heading: { type: 'string', description: 'Section heading (Markdown), top-level key (YAML/JSON), or row range "rows:1-50" (CSV). Case-insensitive.' },
|
|
122
|
+
},
|
|
123
|
+
required: ['path', 'heading'],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
111
126
|
{
|
|
112
127
|
name: 'read_diff',
|
|
113
128
|
description: 'Use INSTEAD OF re-reading whole file after edits. Shows only changed hunks. REQUIRES: call smart_read or read_for_edit BEFORE editing to create baseline snapshot.',
|
|
@@ -138,6 +153,10 @@ export const TOOL_DEFINITIONS = [
|
|
|
138
153
|
include_callers: { type: 'boolean', description: 'Show top callers of this symbol (saves a separate find_usages call)' },
|
|
139
154
|
include_tests: { type: 'boolean', description: 'Show related test file and test names' },
|
|
140
155
|
include_changes: { type: 'boolean', description: 'Show recent git changes in the target region' },
|
|
156
|
+
section: {
|
|
157
|
+
type: 'string',
|
|
158
|
+
description: 'Section to edit: heading (Markdown), top-level key (YAML/JSON), or "rows:1-50" (CSV). Returns raw section content for Edit old_string.',
|
|
159
|
+
},
|
|
141
160
|
},
|
|
142
161
|
required: ['path'],
|
|
143
162
|
},
|
package/dist/server.js
CHANGED
|
@@ -36,12 +36,13 @@ import { handleSmartDiff } from './handlers/smart-diff.js';
|
|
|
36
36
|
import { handleExploreArea } from './handlers/explore-area.js';
|
|
37
37
|
import { handleSmartLog } from './handlers/smart-log.js';
|
|
38
38
|
import { handleTestSummary } from './handlers/test-summary.js';
|
|
39
|
+
import { handleReadSection } from './handlers/read-section.js';
|
|
39
40
|
import { detectContextMode } from './integration/context-mode-detector.js';
|
|
40
41
|
import { estimateTokens } from './core/token-estimator.js';
|
|
41
42
|
import { checkPolicy, isFullReadTool } from './core/policy-engine.js';
|
|
42
43
|
import { MCP_INSTRUCTIONS, TOOL_DEFINITIONS } from './server/tool-definitions.js';
|
|
43
44
|
import { createTokenEstimates } from './server/token-estimates.js';
|
|
44
|
-
import { validateSmartReadArgs, validateReadSymbolArgs, validateReadSymbolsArgs, validateReadRangeArgs, validateReadDiffArgs, validateFindUsagesArgs, validateSmartReadManyArgs, validateReadForEditArgs, validateRelatedFilesArgs, validateOutlineArgs, validateFindUnusedArgs, validateCodeAuditArgs, validateProjectOverviewArgs, validateModuleInfoArgs, validateSmartDiffArgs, validateExploreAreaArgs, validateSmartLogArgs, validateTestSummaryArgs, } from './core/validation.js';
|
|
45
|
+
import { validateSmartReadArgs, validateReadSymbolArgs, validateReadSymbolsArgs, validateReadRangeArgs, validateReadDiffArgs, validateFindUsagesArgs, validateSmartReadManyArgs, validateReadForEditArgs, validateRelatedFilesArgs, validateOutlineArgs, validateFindUnusedArgs, validateCodeAuditArgs, validateProjectOverviewArgs, validateModuleInfoArgs, validateSmartDiffArgs, validateExploreAreaArgs, validateSmartLogArgs, validateTestSummaryArgs, validateReadSectionArgs, } from './core/validation.js';
|
|
45
46
|
export async function createServer(projectRoot, options) {
|
|
46
47
|
const config = await loadConfig(projectRoot);
|
|
47
48
|
const astIndex = new AstIndexClient(projectRoot, config.astIndex.timeout, {
|
|
@@ -310,6 +311,20 @@ export async function createServer(projectRoot, options) {
|
|
|
310
311
|
recordWithTrace({ tool: 'read_range', path: rangeArgs.path, tokensReturned: rangeTokens, tokensWouldBe: fullTokensRange || rangeTokens, timestamp: Date.now(), savingsCategory: detectSavingsCategory(rangeText), absPath: resolve(projectRoot, rangeArgs.path), args: rangeArgs });
|
|
311
312
|
return rangeResult;
|
|
312
313
|
}
|
|
314
|
+
case 'read_section': {
|
|
315
|
+
const secArgs = validateReadSectionArgs(args);
|
|
316
|
+
const secResult = await handleReadSection(secArgs, projectRoot, contextRegistry);
|
|
317
|
+
const secText = secResult.content[0]?.text ?? '';
|
|
318
|
+
const secTokens = estimateTokens(secText);
|
|
319
|
+
const fullTokensSec = await fullFileTokens(secArgs.path);
|
|
320
|
+
recordWithTrace({
|
|
321
|
+
tool: 'read_section', path: secArgs.path,
|
|
322
|
+
tokensReturned: secTokens, tokensWouldBe: fullTokensSec || secTokens,
|
|
323
|
+
timestamp: Date.now(), savingsCategory: 'compression',
|
|
324
|
+
absPath: resolve(projectRoot, secArgs.path), args: secArgs,
|
|
325
|
+
});
|
|
326
|
+
return secResult;
|
|
327
|
+
}
|
|
313
328
|
case 'read_diff': {
|
|
314
329
|
const diffArgs = validateReadDiffArgs(args);
|
|
315
330
|
const diffResult = await handleReadDiff(diffArgs, projectRoot, fileCache, contextRegistry);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "token-pilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Save up to 80% tokens when AI reads code — MCP server for token-efficient code navigation, AST-aware structural reading instead of dumping full files into context window",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|