@davstack/peek 0.1.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 ADDED
@@ -0,0 +1,11 @@
1
+ # @davstack/peek
2
+
3
+ Print concise, agent-friendly folder summaries.
4
+
5
+ ```sh
6
+ npx @davstack/peek .
7
+ npx @davstack/peek packages/context-compactor --agent
8
+ ```
9
+
10
+ The first version uses conservative text scanning and safe default ignores for
11
+ `node_modules`, `.git`, `dist`, `.next`, coverage, and generated metadata files.
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process'
3
+ import { fileURLToPath } from 'node:url'
4
+ import path from 'node:path'
5
+
6
+ const here = path.dirname(fileURLToPath(import.meta.url))
7
+ const entry = path.join(here, '..', 'dist', 'cli.js')
8
+ const child = spawn(process.execPath, [entry, ...process.argv.slice(2)], { stdio: 'inherit' })
9
+
10
+ child.on('error', (err) => {
11
+ console.error('peek: launcher error:', err)
12
+ process.exit(1)
13
+ })
14
+ child.on('exit', (code, signal) => {
15
+ if (signal) process.kill(process.pid, signal)
16
+ else process.exit(code ?? 0)
17
+ })
@@ -0,0 +1,5 @@
1
+ import { CliSpec } from '@davstack/cli-utils';
2
+
3
+ declare const cliSpec: CliSpec;
4
+
5
+ export { cliSpec };
@@ -0,0 +1,429 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/index.ts
12
+ var src_exports = {};
13
+ __export(src_exports, {
14
+ GENERATED_PEEK_FILE: () => GENERATED_PEEK_FILE,
15
+ PEEK_OUTPUT_PRESETS: () => PEEK_OUTPUT_PRESETS,
16
+ generateFolderPeek: () => generateFolderPeek,
17
+ peekFolder: () => peekFolder,
18
+ scanFolderPeek: () => scanFolderPeek
19
+ });
20
+ import { execFile } from "child_process";
21
+ import { mkdir, readdir, readFile, writeFile } from "fs/promises";
22
+ import path from "path";
23
+ import { promisify } from "util";
24
+ async function scanFolderPeek(folder, options = {}) {
25
+ const root = path.resolve(folder);
26
+ const repoRoot = await findRepoRoot(root);
27
+ const outputConfig = resolveOutputConfig(options);
28
+ const summary = await collectFolderSummary(root, repoRoot, root, outputConfig.deep, outputConfig);
29
+ return `${renderFolderSummary(summary, outputConfig)}
30
+ `;
31
+ }
32
+ async function generateFolderPeek(folder, options = {}) {
33
+ const root = path.resolve(folder);
34
+ const content = await scanFolderPeek(root, options);
35
+ const outPath = path.join(root, GENERATED_PEEK_FILE);
36
+ await mkdir(root, { recursive: true });
37
+ await writeFile(outPath, content, "utf8");
38
+ return { path: outPath, content };
39
+ }
40
+ async function peekFolder(folder, options = {}) {
41
+ const root = path.resolve(folder);
42
+ const generatedPath = path.join(root, GENERATED_PEEK_FILE);
43
+ try {
44
+ if (!options.deep && !hasOutputOverrides(options)) return await readFile(generatedPath, "utf8");
45
+ } catch (error) {
46
+ if (error.code !== "ENOENT") throw error;
47
+ }
48
+ return (await generateFolderPeek(root, options)).content;
49
+ }
50
+ async function collectFolderSummary(root, repoRoot, dir, deep, outputConfig) {
51
+ const entries = await readdir(dir, { withFileTypes: true });
52
+ const files = [];
53
+ const folders = [];
54
+ const omittedFiles = [];
55
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
56
+ const fullPath = path.join(dir, entry.name);
57
+ if (await isGitIgnored(root, fullPath)) continue;
58
+ if (entry.isDirectory()) {
59
+ if (!deep || shouldSkipDirectory(entry.name)) continue;
60
+ folders.push(await collectFolderSummary(root, repoRoot, fullPath, deep, outputConfig));
61
+ continue;
62
+ }
63
+ if (!entry.isFile() || shouldSkipFile(entry.name)) continue;
64
+ const summary = await summarizeFile(root, repoRoot, fullPath, outputConfig);
65
+ if (summary) {
66
+ files.push(summary);
67
+ } else {
68
+ omittedFiles.push(entry.name);
69
+ }
70
+ }
71
+ return {
72
+ path: formatFolderPath(root, repoRoot, dir),
73
+ files: files.sort((a, b) => a.path.localeCompare(b.path)),
74
+ folders: folders.sort((a, b) => a.path.localeCompare(b.path)),
75
+ omittedFiles: omittedFiles.sort((a, b) => a.localeCompare(b))
76
+ };
77
+ }
78
+ function formatFolderPath(root, repoRoot, dir) {
79
+ const base = isInsidePath(repoRoot, dir) ? repoRoot : root;
80
+ const relativePath = toPosix(path.relative(base, dir));
81
+ return relativePath || ".";
82
+ }
83
+ function renderFolderSummary(summary, outputConfig, depth = 0) {
84
+ const indent = outputConfig.indent ? " ".repeat(Math.max(0, depth - 1)) : "";
85
+ const childIndent = outputConfig.indent ? " ".repeat(depth) : "";
86
+ const lines = [`${indent}<folder path="${escapeAttribute(summary.path)}">`];
87
+ for (const file of summary.files) {
88
+ lines.push(`${childIndent}<file path="${escapeAttribute(file.path)}">`);
89
+ for (const item of file.items) {
90
+ const prefix = item.startsWith("[ln ") ? "" : "- ";
91
+ lines.push(`${childIndent}${prefix}${item}`);
92
+ }
93
+ lines.push(`${childIndent}</file>`);
94
+ }
95
+ for (const folder of summary.folders) {
96
+ lines.push(renderFolderSummary(folder, outputConfig, depth + 1));
97
+ }
98
+ if (summary.omittedFiles.length > 0) {
99
+ lines.push(`${childIndent}<omitted_files>`);
100
+ for (const omittedFile of summary.omittedFiles) {
101
+ lines.push(`${childIndent}- ${escapeText(omittedFile)}`);
102
+ }
103
+ lines.push(`${childIndent}</omitted_files>`);
104
+ }
105
+ lines.push(`${indent}</folder>`);
106
+ return lines.join("\n");
107
+ }
108
+ async function summarizeFile(root, repoRoot, fullPath, outputConfig) {
109
+ const extension = path.extname(fullPath);
110
+ const relativePath = formatFilePath(root, repoRoot, fullPath, outputConfig.filePaths);
111
+ if (extension === ".md" || extension === ".mdx") {
112
+ const text = await readFile(fullPath, "utf8");
113
+ return { path: relativePath, kind: "markdown", items: extractMarkdownHeadings(text) };
114
+ }
115
+ if (isTypeScriptLikeFile(extension, fullPath)) {
116
+ const text = await readFile(fullPath, "utf8");
117
+ return { path: relativePath, kind: "typescript", items: extractTypeScriptSymbols(text) };
118
+ }
119
+ if (extension === ".py") {
120
+ const text = await readFile(fullPath, "utf8");
121
+ return { path: relativePath, kind: "python", items: extractPythonSymbols(text) };
122
+ }
123
+ return null;
124
+ }
125
+ function isTypeScriptLikeFile(extension, fullPath) {
126
+ return [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs"].includes(extension) && !fullPath.endsWith(".d.ts");
127
+ }
128
+ function extractMarkdownHeadings(text) {
129
+ return text.split(/\r?\n/).map((line) => /^(#{1,6})\s+(.+?)\s*$/.exec(line)).filter((match) => match !== null).map((match) => `h${match[1].length} ${match[2].replace(/\s+#+$/, "").trim()}`);
130
+ }
131
+ function extractTypeScriptSymbols(text) {
132
+ const items = [];
133
+ const source = stripBlockComments(text);
134
+ const patterns = [
135
+ [/^(?:export\s+)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/gm, "class"],
136
+ [/^(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)/gm, "interface"],
137
+ [/^(?:export\s+)?type\s+([A-Za-z_$][\w$]*)\s*=/gm, "type"],
138
+ [/^(?:export\s+)?(?:declare\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)/gm, "const"],
139
+ [
140
+ /^(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(/gm,
141
+ "function"
142
+ ]
143
+ ];
144
+ for (const [pattern, label] of patterns) {
145
+ for (const match of source.matchAll(pattern)) {
146
+ const range = lineRangeForTypeScriptSymbol(source, match.index ?? 0);
147
+ items.push(`${formatLineRange(range)} ${label} ${match[1]}`);
148
+ }
149
+ }
150
+ items.push(...extractTypeScriptTestCalls(source));
151
+ return unique(items);
152
+ }
153
+ function extractTypeScriptTestCalls(text) {
154
+ const items = [];
155
+ const pattern = /^\s*(describe|test|it)\s*\(\s*(['"`])((?:\\.|(?!\2)[\s\S])*?)\2/gm;
156
+ for (const match of text.matchAll(pattern)) {
157
+ const callName = match[1];
158
+ const quote = match[2];
159
+ const title = match[3];
160
+ const range = lineRangeForCallExpression(text, match.index ?? 0);
161
+ items.push(`${formatLineRange(range)} ${callName}(${quote}${title}${quote})`);
162
+ }
163
+ return items;
164
+ }
165
+ function extractPythonSymbols(text) {
166
+ const items = [];
167
+ const lines = text.split(/\r?\n/);
168
+ for (const [index, line] of lines.entries()) {
169
+ const lineNumber = index + 1;
170
+ let match = /^class\s+([A-Za-z_]\w*)\s*[:(]/.exec(line);
171
+ if (match) {
172
+ items.push(`${formatLineRange(lineRangeForPythonBlock(lines, index))} class ${match[1]}`);
173
+ continue;
174
+ }
175
+ match = /^(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(/.exec(line);
176
+ if (match) {
177
+ items.push(`${formatLineRange(lineRangeForPythonBlock(lines, index))} function ${match[1]}`);
178
+ continue;
179
+ }
180
+ match = /^([A-Z][A-Z0-9_]*)\s*[:=]/.exec(line);
181
+ if (match) items.push(`${formatLineRange({ start: lineNumber, end: lineNumber })} const ${match[1]}`);
182
+ }
183
+ return unique(items);
184
+ }
185
+ function shouldSkipDirectory(name) {
186
+ return SKIP_DIRS.has(name);
187
+ }
188
+ function shouldSkipFile(name) {
189
+ return SKIP_FILES.has(name) || name.endsWith(".map");
190
+ }
191
+ function stripBlockComments(text) {
192
+ return text.replace(
193
+ /\/\*[\s\S]*?\*\//g,
194
+ (comment) => comment.replace(/[^\r\n]/g, " ")
195
+ );
196
+ }
197
+ function unique(items) {
198
+ return Array.from(new Set(items));
199
+ }
200
+ function lineNumberAt(text, index) {
201
+ let lineNumber = 1;
202
+ for (let offset = 0; offset < index; offset += 1) {
203
+ if (text.charCodeAt(offset) === 10) lineNumber += 1;
204
+ }
205
+ return lineNumber;
206
+ }
207
+ function lineRangeForTypeScriptSymbol(text, index) {
208
+ const start = lineNumberAt(text, index);
209
+ const lineEnd = text.indexOf("\n", index);
210
+ const declarationEnd = lineEnd === -1 ? text.length : lineEnd;
211
+ const openingBrace = text.indexOf("{", index);
212
+ if (openingBrace !== -1 && openingBrace < declarationEnd) {
213
+ const closingBrace = findMatchingBrace(text, openingBrace);
214
+ if (closingBrace !== -1) return { start, end: lineNumberAt(text, closingBrace) };
215
+ }
216
+ const semicolon = text.indexOf(";", index);
217
+ if (semicolon !== -1 && semicolon < declarationEnd) {
218
+ return { start, end: lineNumberAt(text, semicolon) };
219
+ }
220
+ return { start, end: start };
221
+ }
222
+ function findMatchingBrace(text, openingBrace) {
223
+ let depth = 0;
224
+ for (let index = openingBrace; index < text.length; index += 1) {
225
+ const char = text[index];
226
+ if (char === "{") depth += 1;
227
+ if (char === "}") {
228
+ depth -= 1;
229
+ if (depth === 0) return index;
230
+ }
231
+ }
232
+ return -1;
233
+ }
234
+ function lineRangeForCallExpression(text, index) {
235
+ const start = lineNumberAt(text, index);
236
+ const openingParen = text.indexOf("(", index);
237
+ if (openingParen === -1) return { start, end: start };
238
+ const closingParen = findMatchingParen(text, openingParen);
239
+ if (closingParen === -1) return { start, end: start };
240
+ return { start, end: lineNumberAt(text, closingParen) };
241
+ }
242
+ function findMatchingParen(text, openingParen) {
243
+ let depth = 0;
244
+ let quote = null;
245
+ for (let index = openingParen; index < text.length; index += 1) {
246
+ const char = text[index];
247
+ if (quote) {
248
+ if (char === "\\") {
249
+ index += 1;
250
+ continue;
251
+ }
252
+ if (char === quote) quote = null;
253
+ continue;
254
+ }
255
+ if (char === '"' || char === "'" || char === "`") {
256
+ quote = char;
257
+ continue;
258
+ }
259
+ if (char === "(") depth += 1;
260
+ if (char === ")") {
261
+ depth -= 1;
262
+ if (depth === 0) return index;
263
+ }
264
+ }
265
+ return -1;
266
+ }
267
+ function lineRangeForPythonBlock(lines, startIndex) {
268
+ const startIndent = indentationLength(lines[startIndex] ?? "");
269
+ let endIndex = startIndex;
270
+ for (let index = startIndex + 1; index < lines.length; index += 1) {
271
+ const line = lines[index] ?? "";
272
+ if (line.trim() === "") {
273
+ endIndex = index;
274
+ continue;
275
+ }
276
+ if (indentationLength(line) <= startIndent) break;
277
+ endIndex = index;
278
+ }
279
+ while (endIndex > startIndex && (lines[endIndex] ?? "").trim() === "") endIndex -= 1;
280
+ return { start: startIndex + 1, end: endIndex + 1 };
281
+ }
282
+ function indentationLength(line) {
283
+ return line.match(/^\s*/)?.[0].length ?? 0;
284
+ }
285
+ function formatLineRange(range) {
286
+ return range.start === range.end ? `[ln ${range.start}]` : `[ln ${range.start}-${range.end}]`;
287
+ }
288
+ function formatFilePath(root, repoRoot, fullPath, filePaths) {
289
+ if (filePaths === "concise") return `/${path.basename(fullPath)}`;
290
+ const base = isInsidePath(repoRoot, fullPath) ? repoRoot : root;
291
+ return toPosix(path.relative(base, fullPath));
292
+ }
293
+ function resolveOutputConfig(options) {
294
+ const presetName = options.preset ?? "human";
295
+ const preset = PEEK_OUTPUT_PRESETS[presetName];
296
+ if (!preset) throw new Error(`Unknown peek output preset: ${presetName}`);
297
+ return {
298
+ deep: options.deep ?? preset.deep,
299
+ indent: options.indent ?? preset.indent,
300
+ filePaths: normalizeFilePathMode(options.filePaths ?? options.file_paths ?? preset.filePaths)
301
+ };
302
+ }
303
+ function normalizeFilePathMode(value) {
304
+ if (value === "concise" || value === "full") return value;
305
+ throw new Error(`Unknown peek file path mode: ${String(value)}`);
306
+ }
307
+ function hasOutputOverrides(options) {
308
+ return options.preset !== void 0 || options.indent !== void 0 || options.filePaths !== void 0 || options.file_paths !== void 0;
309
+ }
310
+ function isInsidePath(parent, child) {
311
+ const relativePath = path.relative(parent, child);
312
+ return relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
313
+ }
314
+ function toPosix(value) {
315
+ return value.split(path.sep).join("/");
316
+ }
317
+ function escapeAttribute(value) {
318
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
319
+ }
320
+ function escapeText(value) {
321
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;");
322
+ }
323
+ async function isGitIgnored(root, fullPath) {
324
+ const relativePath = toPosix(path.relative(root, fullPath));
325
+ if (!relativePath || relativePath.startsWith("..")) return false;
326
+ try {
327
+ await execFileAsync("git", ["-C", root, "check-ignore", "--quiet", "--", relativePath]);
328
+ return true;
329
+ } catch (error) {
330
+ const code = error.code;
331
+ if (code === 1 || code === 128 || code === "ENOENT") return false;
332
+ return false;
333
+ }
334
+ }
335
+ async function findRepoRoot(root) {
336
+ try {
337
+ const { stdout } = await execFileAsync("git", ["-C", root, "rev-parse", "--show-toplevel"]);
338
+ return path.resolve(stdout.trim());
339
+ } catch {
340
+ return root;
341
+ }
342
+ }
343
+ var GENERATED_PEEK_FILE, execFileAsync, PEEK_OUTPUT_PRESETS, SKIP_DIRS, SKIP_FILES;
344
+ var init_src = __esm({
345
+ "src/index.ts"() {
346
+ "use strict";
347
+ GENERATED_PEEK_FILE = ".folder-peek.generated.md";
348
+ execFileAsync = promisify(execFile);
349
+ PEEK_OUTPUT_PRESETS = {
350
+ human: {
351
+ deep: true,
352
+ indent: true,
353
+ filePaths: "full"
354
+ },
355
+ agent: {
356
+ deep: true,
357
+ indent: false,
358
+ filePaths: "concise"
359
+ }
360
+ };
361
+ SKIP_DIRS = /* @__PURE__ */ new Set([
362
+ ".git",
363
+ ".next",
364
+ "coverage",
365
+ "dist",
366
+ "dist-ssr",
367
+ "node_modules"
368
+ ]);
369
+ SKIP_FILES = /* @__PURE__ */ new Set([GENERATED_PEEK_FILE]);
370
+ }
371
+ });
372
+
373
+ // src/cli-spec.ts
374
+ var outputFlags = {
375
+ human: {
376
+ type: "boolean",
377
+ default: false,
378
+ description: "Use human-readable output defaults: indented XML and full repo-relative file paths"
379
+ },
380
+ agent: {
381
+ type: "boolean",
382
+ default: false,
383
+ description: "Use token-optimized output defaults: no indentation and concise file paths"
384
+ },
385
+ indent: {
386
+ type: "boolean",
387
+ description: "Indent nested folder output"
388
+ },
389
+ file_paths: {
390
+ type: "string",
391
+ description: "File path style: concise or full"
392
+ }
393
+ };
394
+ function resolveCliScanOptions(flags) {
395
+ const human = flags.human === true;
396
+ const agent = flags.agent === true;
397
+ if (human && agent) throw new Error("Use only one output preset: --human or --agent");
398
+ const preset = agent ? "agent" : human ? "human" : void 0;
399
+ const filePaths = flags.file_paths;
400
+ if (filePaths !== void 0 && filePaths !== "concise" && filePaths !== "full") {
401
+ throw new Error('--file_paths must be "concise" or "full"');
402
+ }
403
+ return {
404
+ deep: flags.deep,
405
+ preset,
406
+ indent: flags.indent,
407
+ filePaths
408
+ };
409
+ }
410
+ var cliSpec = {
411
+ name: "peek",
412
+ description: "Print concise folder summaries for agents.",
413
+ positionals: [{ name: "path", required: true, description: "Folder to peek at" }],
414
+ flags: {
415
+ deep: {
416
+ type: "boolean",
417
+ description: "Recursively include child folders"
418
+ },
419
+ ...outputFlags
420
+ },
421
+ run: async (ctx) => {
422
+ const { peekFolder: peekFolder2 } = await Promise.resolve().then(() => (init_src(), src_exports));
423
+ console.log(await peekFolder2(ctx.positionals[0], resolveCliScanOptions(ctx.flags)));
424
+ }
425
+ };
426
+ export {
427
+ cliSpec
428
+ };
429
+ //# sourceMappingURL=cli-spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/cli-spec.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\n\nexport const GENERATED_PEEK_FILE = '.folder-peek.generated.md';\nconst execFileAsync = promisify(execFile);\n\nexport type ScanOptions = {\n deep?: boolean;\n preset?: PeekOutputPresetName;\n indent?: boolean;\n filePaths?: PeekFilePathMode;\n file_paths?: PeekFilePathMode;\n};\n\ntype FileSummary = {\n path: string;\n kind: 'markdown' | 'typescript' | 'python';\n items: string[];\n};\n\nexport type PeekFilePathMode = 'concise' | 'full';\nexport type PeekOutputPresetName = 'agent' | 'human';\n\nexport type PeekOutputConfig = {\n deep: boolean;\n indent: boolean;\n filePaths: PeekFilePathMode;\n};\n\nexport const PEEK_OUTPUT_PRESETS: Record<PeekOutputPresetName, PeekOutputConfig> = {\n human: {\n deep: true,\n indent: true,\n filePaths: 'full',\n },\n agent: {\n deep: true,\n indent: false,\n filePaths: 'concise',\n },\n};\n\ntype FolderSummary = {\n path: string;\n files: FileSummary[];\n folders: FolderSummary[];\n omittedFiles: string[];\n};\n\nconst SKIP_DIRS = new Set([\n '.git',\n '.next',\n 'coverage',\n 'dist',\n 'dist-ssr',\n 'node_modules',\n]);\n\nconst SKIP_FILES = new Set([GENERATED_PEEK_FILE]);\n\nexport async function scanFolderPeek(folder: string, options: ScanOptions = {}): Promise<string> {\n const root = path.resolve(folder);\n const repoRoot = await findRepoRoot(root);\n const outputConfig = resolveOutputConfig(options);\n const summary = await collectFolderSummary(root, repoRoot, root, outputConfig.deep, outputConfig);\n\n return `${renderFolderSummary(summary, outputConfig)}\\n`;\n}\n\nexport async function generateFolderPeek(\n folder: string,\n options: ScanOptions = {},\n): Promise<{ path: string; content: string }> {\n const root = path.resolve(folder);\n const content = await scanFolderPeek(root, options);\n const outPath = path.join(root, GENERATED_PEEK_FILE);\n await mkdir(root, { recursive: true });\n await writeFile(outPath, content, 'utf8');\n return { path: outPath, content };\n}\n\nexport async function peekFolder(folder: string, options: ScanOptions = {}): Promise<string> {\n const root = path.resolve(folder);\n const generatedPath = path.join(root, GENERATED_PEEK_FILE);\n try {\n if (!options.deep && !hasOutputOverrides(options)) return await readFile(generatedPath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n return (await generateFolderPeek(root, options)).content;\n}\n\nasync function collectFolderSummary(\n root: string,\n repoRoot: string,\n dir: string,\n deep: boolean,\n outputConfig: PeekOutputConfig,\n): Promise<FolderSummary> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: FileSummary[] = [];\n const folders: FolderSummary[] = [];\n const omittedFiles: string[] = [];\n\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const fullPath = path.join(dir, entry.name);\n if (await isGitIgnored(root, fullPath)) continue;\n\n if (entry.isDirectory()) {\n if (!deep || shouldSkipDirectory(entry.name)) continue;\n folders.push(await collectFolderSummary(root, repoRoot, fullPath, deep, outputConfig));\n continue;\n }\n if (!entry.isFile() || shouldSkipFile(entry.name)) continue;\n\n const summary = await summarizeFile(root, repoRoot, fullPath, outputConfig);\n if (summary) {\n files.push(summary);\n } else {\n omittedFiles.push(entry.name);\n }\n }\n\n return {\n path: formatFolderPath(root, repoRoot, dir),\n files: files.sort((a, b) => a.path.localeCompare(b.path)),\n folders: folders.sort((a, b) => a.path.localeCompare(b.path)),\n omittedFiles: omittedFiles.sort((a, b) => a.localeCompare(b)),\n };\n}\n\nfunction formatFolderPath(root: string, repoRoot: string, dir: string): string {\n const base = isInsidePath(repoRoot, dir) ? repoRoot : root;\n const relativePath = toPosix(path.relative(base, dir));\n return relativePath || '.';\n}\n\nfunction renderFolderSummary(\n summary: FolderSummary,\n outputConfig: PeekOutputConfig,\n depth = 0,\n): string {\n const indent = outputConfig.indent ? '\\t'.repeat(Math.max(0, depth - 1)) : '';\n const childIndent = outputConfig.indent ? '\\t'.repeat(depth) : '';\n const lines = [`${indent}<folder path=\"${escapeAttribute(summary.path)}\">`];\n\n for (const file of summary.files) {\n lines.push(`${childIndent}<file path=\"${escapeAttribute(file.path)}\">`);\n for (const item of file.items) {\n const prefix = item.startsWith('[ln ') ? '' : '- ';\n lines.push(`${childIndent}${prefix}${item}`);\n }\n lines.push(`${childIndent}</file>`);\n }\n\n for (const folder of summary.folders) {\n lines.push(renderFolderSummary(folder, outputConfig, depth + 1));\n }\n\n if (summary.omittedFiles.length > 0) {\n lines.push(`${childIndent}<omitted_files>`);\n for (const omittedFile of summary.omittedFiles) {\n lines.push(`${childIndent}- ${escapeText(omittedFile)}`);\n }\n lines.push(`${childIndent}</omitted_files>`);\n }\n\n lines.push(`${indent}</folder>`);\n return lines.join('\\n');\n}\n\nasync function summarizeFile(\n root: string,\n repoRoot: string,\n fullPath: string,\n outputConfig: PeekOutputConfig,\n): Promise<FileSummary | null> {\n const extension = path.extname(fullPath);\n const relativePath = formatFilePath(root, repoRoot, fullPath, outputConfig.filePaths);\n\n if (extension === '.md' || extension === '.mdx') {\n const text = await readFile(fullPath, 'utf8');\n return { path: relativePath, kind: 'markdown', items: extractMarkdownHeadings(text) };\n }\n if (isTypeScriptLikeFile(extension, fullPath)) {\n const text = await readFile(fullPath, 'utf8');\n return { path: relativePath, kind: 'typescript', items: extractTypeScriptSymbols(text) };\n }\n if (extension === '.py') {\n const text = await readFile(fullPath, 'utf8');\n return { path: relativePath, kind: 'python', items: extractPythonSymbols(text) };\n }\n return null;\n}\n\nfunction isTypeScriptLikeFile(extension: string, fullPath: string): boolean {\n return ['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs'].includes(extension) && !fullPath.endsWith('.d.ts');\n}\n\nfunction extractMarkdownHeadings(text: string): string[] {\n return text\n .split(/\\r?\\n/)\n .map((line) => /^(#{1,6})\\s+(.+?)\\s*$/.exec(line))\n .filter((match): match is RegExpExecArray => match !== null)\n .map((match) => `h${match[1].length} ${match[2].replace(/\\s+#+$/, '').trim()}`);\n}\n\nfunction extractTypeScriptSymbols(text: string): string[] {\n const items: string[] = [];\n const source = stripBlockComments(text);\n const patterns: Array<[RegExp, string]> = [\n [/^(?:export\\s+)?(?:abstract\\s+)?class\\s+([A-Za-z_$][\\w$]*)/gm, 'class'],\n [/^(?:export\\s+)?interface\\s+([A-Za-z_$][\\w$]*)/gm, 'interface'],\n [/^(?:export\\s+)?type\\s+([A-Za-z_$][\\w$]*)\\s*=/gm, 'type'],\n [/^(?:export\\s+)?(?:declare\\s+)?(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)/gm, 'const'],\n [\n /^(?:export\\s+)?(?:async\\s+)?function\\s+([A-Za-z_$][\\w$]*)\\s*\\(/gm,\n 'function',\n ],\n ];\n\n for (const [pattern, label] of patterns) {\n for (const match of source.matchAll(pattern)) {\n const range = lineRangeForTypeScriptSymbol(source, match.index ?? 0);\n items.push(`${formatLineRange(range)} ${label} ${match[1]}`);\n }\n }\n items.push(...extractTypeScriptTestCalls(source));\n return unique(items);\n}\n\nfunction extractTypeScriptTestCalls(text: string): string[] {\n const items: string[] = [];\n const pattern = /^\\s*(describe|test|it)\\s*\\(\\s*(['\"`])((?:\\\\.|(?!\\2)[\\s\\S])*?)\\2/gm;\n\n for (const match of text.matchAll(pattern)) {\n const callName = match[1];\n const quote = match[2];\n const title = match[3];\n const range = lineRangeForCallExpression(text, match.index ?? 0);\n items.push(`${formatLineRange(range)} ${callName}(${quote}${title}${quote})`);\n }\n\n return items;\n}\n\nfunction extractPythonSymbols(text: string): string[] {\n const items: string[] = [];\n const lines = text.split(/\\r?\\n/);\n for (const [index, line] of lines.entries()) {\n const lineNumber = index + 1;\n let match = /^class\\s+([A-Za-z_]\\w*)\\s*[:(]/.exec(line);\n if (match) {\n items.push(`${formatLineRange(lineRangeForPythonBlock(lines, index))} class ${match[1]}`);\n continue;\n }\n match = /^(?:async\\s+)?def\\s+([A-Za-z_]\\w*)\\s*\\(/.exec(line);\n if (match) {\n items.push(`${formatLineRange(lineRangeForPythonBlock(lines, index))} function ${match[1]}`);\n continue;\n }\n match = /^([A-Z][A-Z0-9_]*)\\s*[:=]/.exec(line);\n if (match) items.push(`${formatLineRange({ start: lineNumber, end: lineNumber })} const ${match[1]}`);\n }\n return unique(items);\n}\n\nfunction shouldSkipDirectory(name: string): boolean {\n return SKIP_DIRS.has(name);\n}\n\nfunction shouldSkipFile(name: string): boolean {\n return SKIP_FILES.has(name) || name.endsWith('.map');\n}\n\nfunction stripBlockComments(text: string): string {\n return text.replace(/\\/\\*[\\s\\S]*?\\*\\//g, (comment) =>\n comment.replace(/[^\\r\\n]/g, ' '),\n );\n}\n\nfunction unique(items: string[]): string[] {\n return Array.from(new Set(items));\n}\n\nfunction lineNumberAt(text: string, index: number): number {\n let lineNumber = 1;\n for (let offset = 0; offset < index; offset += 1) {\n if (text.charCodeAt(offset) === 10) lineNumber += 1;\n }\n return lineNumber;\n}\n\nfunction lineRangeForTypeScriptSymbol(text: string, index: number): { start: number; end: number } {\n const start = lineNumberAt(text, index);\n const lineEnd = text.indexOf('\\n', index);\n const declarationEnd = lineEnd === -1 ? text.length : lineEnd;\n const openingBrace = text.indexOf('{', index);\n\n if (openingBrace !== -1 && openingBrace < declarationEnd) {\n const closingBrace = findMatchingBrace(text, openingBrace);\n if (closingBrace !== -1) return { start, end: lineNumberAt(text, closingBrace) };\n }\n\n const semicolon = text.indexOf(';', index);\n if (semicolon !== -1 && semicolon < declarationEnd) {\n return { start, end: lineNumberAt(text, semicolon) };\n }\n\n return { start, end: start };\n}\n\nfunction findMatchingBrace(text: string, openingBrace: number): number {\n let depth = 0;\n for (let index = openingBrace; index < text.length; index += 1) {\n const char = text[index];\n if (char === '{') depth += 1;\n if (char === '}') {\n depth -= 1;\n if (depth === 0) return index;\n }\n }\n return -1;\n}\n\nfunction lineRangeForCallExpression(text: string, index: number): { start: number; end: number } {\n const start = lineNumberAt(text, index);\n const openingParen = text.indexOf('(', index);\n if (openingParen === -1) return { start, end: start };\n\n const closingParen = findMatchingParen(text, openingParen);\n if (closingParen === -1) return { start, end: start };\n return { start, end: lineNumberAt(text, closingParen) };\n}\n\nfunction findMatchingParen(text: string, openingParen: number): number {\n let depth = 0;\n let quote: string | null = null;\n\n for (let index = openingParen; index < text.length; index += 1) {\n const char = text[index];\n if (quote) {\n if (char === '\\\\') {\n index += 1;\n continue;\n }\n if (char === quote) quote = null;\n continue;\n }\n if (char === '\"' || char === \"'\" || char === '`') {\n quote = char;\n continue;\n }\n if (char === '(') depth += 1;\n if (char === ')') {\n depth -= 1;\n if (depth === 0) return index;\n }\n }\n return -1;\n}\n\nfunction lineRangeForPythonBlock(\n lines: string[],\n startIndex: number,\n): { start: number; end: number } {\n const startIndent = indentationLength(lines[startIndex] ?? '');\n let endIndex = startIndex;\n\n for (let index = startIndex + 1; index < lines.length; index += 1) {\n const line = lines[index] ?? '';\n if (line.trim() === '') {\n endIndex = index;\n continue;\n }\n if (indentationLength(line) <= startIndent) break;\n endIndex = index;\n }\n\n while (endIndex > startIndex && (lines[endIndex] ?? '').trim() === '') endIndex -= 1;\n return { start: startIndex + 1, end: endIndex + 1 };\n}\n\nfunction indentationLength(line: string): number {\n return line.match(/^\\s*/)?.[0].length ?? 0;\n}\n\nfunction formatLineRange(range: { start: number; end: number }): string {\n return range.start === range.end ? `[ln ${range.start}]` : `[ln ${range.start}-${range.end}]`;\n}\n\nfunction formatFilePath(\n root: string,\n repoRoot: string,\n fullPath: string,\n filePaths: PeekFilePathMode,\n): string {\n if (filePaths === 'concise') return `/${path.basename(fullPath)}`;\n const base = isInsidePath(repoRoot, fullPath) ? repoRoot : root;\n return toPosix(path.relative(base, fullPath));\n}\n\nfunction resolveOutputConfig(options: ScanOptions): PeekOutputConfig {\n const presetName = options.preset ?? 'human';\n const preset = PEEK_OUTPUT_PRESETS[presetName];\n if (!preset) throw new Error(`Unknown peek output preset: ${presetName}`);\n\n return {\n deep: options.deep ?? preset.deep,\n indent: options.indent ?? preset.indent,\n filePaths: normalizeFilePathMode(options.filePaths ?? options.file_paths ?? preset.filePaths),\n };\n}\n\nfunction normalizeFilePathMode(value: PeekFilePathMode): PeekFilePathMode {\n if (value === 'concise' || value === 'full') return value;\n throw new Error(`Unknown peek file path mode: ${String(value)}`);\n}\n\nfunction hasOutputOverrides(options: ScanOptions): boolean {\n return (\n options.preset !== undefined ||\n options.indent !== undefined ||\n options.filePaths !== undefined ||\n options.file_paths !== undefined\n );\n}\n\nfunction isInsidePath(parent: string, child: string): boolean {\n const relativePath = path.relative(parent, child);\n return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));\n}\n\nfunction toPosix(value: string): string {\n return value.split(path.sep).join('/');\n}\n\nfunction escapeAttribute(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;');\n}\n\nfunction escapeText(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/</g, '&lt;');\n}\n\nasync function isGitIgnored(root: string, fullPath: string): Promise<boolean> {\n const relativePath = toPosix(path.relative(root, fullPath));\n if (!relativePath || relativePath.startsWith('..')) return false;\n\n try {\n await execFileAsync('git', ['-C', root, 'check-ignore', '--quiet', '--', relativePath]);\n return true;\n } catch (error) {\n const code = (error as { code?: number | string }).code;\n if (code === 1 || code === 128 || code === 'ENOENT') return false;\n return false;\n }\n}\n\nasync function findRepoRoot(root: string): Promise<string> {\n try {\n const { stdout } = await execFileAsync('git', ['-C', root, 'rev-parse', '--show-toplevel']);\n return path.resolve(stdout.trim());\n } catch {\n return root;\n }\n}\n","import type { CliSpec } from '@davstack/cli-utils';\nimport type { PeekFilePathMode, PeekOutputPresetName, ScanOptions } from './index.js';\n\nconst outputFlags = {\n human: {\n type: 'boolean',\n default: false,\n description: 'Use human-readable output defaults: indented XML and full repo-relative file paths',\n },\n agent: {\n type: 'boolean',\n default: false,\n description: 'Use token-optimized output defaults: no indentation and concise file paths',\n },\n indent: {\n type: 'boolean',\n description: 'Indent nested folder output',\n },\n file_paths: {\n type: 'string',\n description: 'File path style: concise or full',\n },\n} as const;\n\nfunction resolveCliScanOptions(flags: Record<string, unknown>): ScanOptions {\n const human = flags.human === true;\n const agent = flags.agent === true;\n if (human && agent) throw new Error('Use only one output preset: --human or --agent');\n\n const preset: PeekOutputPresetName | undefined = agent ? 'agent' : human ? 'human' : undefined;\n const filePaths = flags.file_paths;\n if (filePaths !== undefined && filePaths !== 'concise' && filePaths !== 'full') {\n throw new Error('--file_paths must be \"concise\" or \"full\"');\n }\n\n return {\n deep: flags.deep as boolean,\n preset,\n indent: flags.indent as boolean | undefined,\n filePaths: filePaths as PeekFilePathMode | undefined,\n };\n}\n\nexport const cliSpec: CliSpec = {\n name: 'peek',\n description: 'Print concise folder summaries for agents.',\n positionals: [{ name: 'path', required: true, description: 'Folder to peek at' }],\n flags: {\n deep: {\n type: 'boolean',\n description: 'Recursively include child folders',\n },\n ...outputFlags,\n },\n run: async (ctx) => {\n const { peekFolder } = await import('./index.js');\n console.log(await peekFolder(ctx.positionals[0], resolveCliScanOptions(ctx.flags)));\n },\n};\n"],"mappings":"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","names":["peekFolder"]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }