@jigyasudham/veto 1.2.6 → 1.2.9
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 +473 -393
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +18 -9
- package/dist/adapters/index.js.map +1 -1
- package/dist/cli.js +191 -85
- package/dist/cli.js.map +1 -1
- package/dist/council/product-manager.js +1 -1
- package/dist/council/security.js +1 -1
- package/dist/council/ux-designer.js +2 -2
- package/dist/discover.d.ts +24 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +143 -0
- package/dist/discover.js.map +1 -0
- package/dist/server.js +133 -3
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
|
@@ -127,8 +127,8 @@ export function analyze(task) {
|
|
|
127
127
|
recommendation: 'Default to minimal UI: one status bar item. Expand to sidebar only when user explicitly enables it. Follow VS Code\'s own design patterns and icon conventions.',
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
|
-
pattern: /
|
|
131
|
-
concern: '
|
|
130
|
+
pattern: /45.?tool|tool.?count|tool.?list|discover/i,
|
|
131
|
+
concern: '45 tools requires users to read documentation before they can use the product. No tool is valuable if users can\'t discover it exists.',
|
|
132
132
|
recommendation: 'Make veto_discover the entry point. Ship it as the first thing users learn about. Consider surfacing 3 "most useful for your current task" recommendations automatically.',
|
|
133
133
|
},
|
|
134
134
|
{
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type DiscoverDepth = 'quick' | 'standard' | 'full';
|
|
2
|
+
export interface DiscoverResult {
|
|
3
|
+
project_dir: string;
|
|
4
|
+
scanned_at: string;
|
|
5
|
+
depth: DiscoverDepth;
|
|
6
|
+
git: {
|
|
7
|
+
branch: string | null;
|
|
8
|
+
remote: string | null;
|
|
9
|
+
commit: string | null;
|
|
10
|
+
dirty_files: string[];
|
|
11
|
+
recent_commits: string[];
|
|
12
|
+
};
|
|
13
|
+
ecosystems: Record<string, string>;
|
|
14
|
+
tech_stack: string[];
|
|
15
|
+
dependencies: string[];
|
|
16
|
+
key_files: string[];
|
|
17
|
+
structure: string[];
|
|
18
|
+
file_counts: Record<string, number>;
|
|
19
|
+
total_files: number;
|
|
20
|
+
duration_ms: number;
|
|
21
|
+
}
|
|
22
|
+
export declare const KEY_CONFIG_FILES: string[];
|
|
23
|
+
export declare function discoverProject(projectDir: string, depth?: DiscoverDepth): DiscoverResult;
|
|
24
|
+
//# sourceMappingURL=discover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE;QACH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,cAAc,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;IACF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD,eAAO,MAAM,gBAAgB,UAM5B,CAAC;AA8BF,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,aAA0B,GAAG,cAAc,CA+ErG"}
|
package/dist/discover.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Shared workspace discovery logic — used by veto_discover (MCP tool) and veto init (CLI)
|
|
2
|
+
import { readFileSync, statSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { join, extname, resolve } from 'node:path';
|
|
4
|
+
import { execSync } from 'node:child_process';
|
|
5
|
+
import { readProjectContext } from './context/reader.js';
|
|
6
|
+
const SKIP_DIRS = new Set([
|
|
7
|
+
'node_modules', '.git', '__pycache__', '.next', 'dist', 'build', 'target',
|
|
8
|
+
'.cache', 'coverage', '.venv', 'venv', '.idea', '.vs', 'out', '.turbo',
|
|
9
|
+
]);
|
|
10
|
+
export const KEY_CONFIG_FILES = [
|
|
11
|
+
'tsconfig.json', 'vite.config.ts', 'vite.config.js', 'next.config.ts', 'next.config.js',
|
|
12
|
+
'tailwind.config.ts', 'tailwind.config.js', 'drizzle.config.ts', 'prisma/schema.prisma',
|
|
13
|
+
'.env.example', 'Dockerfile', 'docker-compose.yml', '.github/workflows',
|
|
14
|
+
'eslint.config.js', '.eslintrc.json', 'vitest.config.ts', 'jest.config.ts',
|
|
15
|
+
'pyproject.toml', 'requirements.txt', 'Cargo.toml', 'go.mod', 'CLAUDE.md', 'README.md',
|
|
16
|
+
];
|
|
17
|
+
function safeExec(cmd, cwd) {
|
|
18
|
+
try {
|
|
19
|
+
return execSync(cmd, { cwd, encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function safeRead(p) {
|
|
26
|
+
try {
|
|
27
|
+
return readFileSync(p, 'utf8');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function walkDir(dir, prefix, depth, structure, fileCounts) {
|
|
34
|
+
if (depth > 3 || structure.length > 200)
|
|
35
|
+
return;
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = readdirSync(dir);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
for (const entry of entries.filter(e => !SKIP_DIRS.has(e)).sort()) {
|
|
44
|
+
const full = join(dir, entry);
|
|
45
|
+
let st;
|
|
46
|
+
try {
|
|
47
|
+
st = statSync(full);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (st.isDirectory()) {
|
|
53
|
+
structure.push(`${prefix}${entry}/`);
|
|
54
|
+
walkDir(full, prefix + ' ', depth + 1, structure, fileCounts);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
structure.push(`${prefix}${entry}`);
|
|
58
|
+
const ext = extname(entry) || '(none)';
|
|
59
|
+
fileCounts[ext] = (fileCounts[ext] ?? 0) + 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function discoverProject(projectDir, depth = 'standard') {
|
|
64
|
+
const dir = resolve(projectDir);
|
|
65
|
+
const start = Date.now();
|
|
66
|
+
// Git state
|
|
67
|
+
const git_branch = safeExec('git rev-parse --abbrev-ref HEAD', dir);
|
|
68
|
+
const git_remote = safeExec('git remote get-url origin', dir);
|
|
69
|
+
const git_commit = safeExec('git rev-parse --short HEAD', dir);
|
|
70
|
+
const git_status_raw = safeExec('git status --short', dir);
|
|
71
|
+
const git_log_raw = safeExec('git log --oneline -10 --no-color', dir);
|
|
72
|
+
const dirty_files = git_status_raw.split('\n').filter(Boolean).map(l => l.trim());
|
|
73
|
+
const recent_commits = git_log_raw.split('\n').filter(Boolean);
|
|
74
|
+
// Package metadata & tech stack
|
|
75
|
+
const ecosystems = {};
|
|
76
|
+
const tech_stack = [];
|
|
77
|
+
let dependencies = [];
|
|
78
|
+
const pkgRaw = safeRead(join(dir, 'package.json'));
|
|
79
|
+
if (pkgRaw) {
|
|
80
|
+
try {
|
|
81
|
+
const pkg = JSON.parse(pkgRaw);
|
|
82
|
+
ecosystems['node'] = `${String(pkg.name ?? 'unknown')} v${String(pkg.version ?? '?')}`;
|
|
83
|
+
const allDeps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
84
|
+
dependencies = Object.keys(allDeps).slice(0, 30);
|
|
85
|
+
tech_stack.push(...readProjectContext(dir).tech_stack);
|
|
86
|
+
}
|
|
87
|
+
catch { /* malformed */ }
|
|
88
|
+
}
|
|
89
|
+
const pyprojectRaw = safeRead(join(dir, 'pyproject.toml'));
|
|
90
|
+
const requirementsRaw = safeRead(join(dir, 'requirements.txt'));
|
|
91
|
+
if (pyprojectRaw || requirementsRaw) {
|
|
92
|
+
ecosystems['python'] = pyprojectRaw ? 'pyproject.toml' : 'requirements.txt';
|
|
93
|
+
if (!tech_stack.includes('Python'))
|
|
94
|
+
tech_stack.push('Python');
|
|
95
|
+
}
|
|
96
|
+
const cargoRaw = safeRead(join(dir, 'Cargo.toml'));
|
|
97
|
+
if (cargoRaw) {
|
|
98
|
+
const nm = cargoRaw.match(/^name\s*=\s*"(.+)"/m);
|
|
99
|
+
const vm = cargoRaw.match(/^version\s*=\s*"(.+)"/m);
|
|
100
|
+
ecosystems['rust'] = `${nm?.[1] ?? 'crate'} v${vm?.[1] ?? '?'}`;
|
|
101
|
+
if (!tech_stack.includes('Rust'))
|
|
102
|
+
tech_stack.push('Rust');
|
|
103
|
+
}
|
|
104
|
+
const goModRaw = safeRead(join(dir, 'go.mod'));
|
|
105
|
+
if (goModRaw) {
|
|
106
|
+
const mm = goModRaw.match(/^module\s+(.+)/m);
|
|
107
|
+
ecosystems['go'] = mm?.[1]?.trim() ?? 'go module';
|
|
108
|
+
if (!tech_stack.includes('Go'))
|
|
109
|
+
tech_stack.push('Go');
|
|
110
|
+
}
|
|
111
|
+
// File tree
|
|
112
|
+
const file_counts = {};
|
|
113
|
+
const structure = [];
|
|
114
|
+
if (depth !== 'quick') {
|
|
115
|
+
walkDir(dir, '', 0, structure, file_counts);
|
|
116
|
+
}
|
|
117
|
+
// Key config files
|
|
118
|
+
const key_files = KEY_CONFIG_FILES.filter(f => {
|
|
119
|
+
try {
|
|
120
|
+
statSync(join(dir, f));
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const total_files = Object.values(file_counts).reduce((s, c) => s + c, 0);
|
|
128
|
+
return {
|
|
129
|
+
project_dir: dir,
|
|
130
|
+
scanned_at: new Date().toISOString(),
|
|
131
|
+
depth,
|
|
132
|
+
git: { branch: git_branch || null, remote: git_remote || null, commit: git_commit || null, dirty_files, recent_commits },
|
|
133
|
+
ecosystems,
|
|
134
|
+
tech_stack: [...new Set(tech_stack)],
|
|
135
|
+
dependencies,
|
|
136
|
+
key_files,
|
|
137
|
+
structure: depth !== 'quick' ? structure : [],
|
|
138
|
+
file_counts,
|
|
139
|
+
total_files,
|
|
140
|
+
duration_ms: Date.now() - start,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Veto MCP Server —
|
|
2
|
+
// Veto MCP Server — 45 tools, 16 phases, self-learning router
|
|
3
3
|
// Suppress node:sqlite experimental warning — it would corrupt the MCP stdio protocol
|
|
4
4
|
process.removeAllListeners('warning');
|
|
5
5
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
@@ -16,6 +16,7 @@ import { startWatch, pollWatch, stopWatch } from './watcher/index.js';
|
|
|
16
16
|
import { runPipeline } from './workflow/pipeline.js';
|
|
17
17
|
import { loadPlugins, listPlugins } from './plugins/loader.js';
|
|
18
18
|
import { fetchPrDiff } from './github/pr-fetcher.js';
|
|
19
|
+
import { discoverProject } from './discover.js';
|
|
19
20
|
import { readFileSync, statSync } from 'node:fs';
|
|
20
21
|
import { extname, basename, join, dirname } from 'node:path';
|
|
21
22
|
import { createHash } from 'node:crypto';
|
|
@@ -734,6 +735,34 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
734
735
|
required: ['pr_url'],
|
|
735
736
|
},
|
|
736
737
|
},
|
|
738
|
+
// ── Phase 16: Workspace Discovery & Summarization ─────────────────────────
|
|
739
|
+
{
|
|
740
|
+
name: 'veto_discover',
|
|
741
|
+
description: 'Scans a project directory and builds a rich context map: git state, tech stack, file structure, dependencies, and key config files. Stores the result in Veto memory so agents always have accurate project context. Call this once per project or after major structural changes.',
|
|
742
|
+
inputSchema: {
|
|
743
|
+
type: 'object',
|
|
744
|
+
properties: {
|
|
745
|
+
project_dir: { type: 'string', description: 'Absolute path to the project directory to scan.' },
|
|
746
|
+
depth: { type: 'string', enum: ['quick', 'standard', 'full'], description: 'Scan depth. quick: git + package metadata only. standard: + file tree up to 3 levels (default). full: + contents of key config files.' },
|
|
747
|
+
store: { type: 'boolean', description: 'Whether to store the discovery in Veto memory as a project map. Default: true.' },
|
|
748
|
+
},
|
|
749
|
+
required: ['project_dir'],
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
name: 'veto_summarize',
|
|
754
|
+
description: 'Generates a concise expert briefing of a project, directory, or file. Use at the start of a session to orient yourself on unfamiliar code. Returns bullet-point summary, key components, tech stack, and entry points. Faster and higher-level than veto_explain.',
|
|
755
|
+
inputSchema: {
|
|
756
|
+
type: 'object',
|
|
757
|
+
properties: {
|
|
758
|
+
project_dir: { type: 'string', description: 'Absolute path to a project directory to summarize.' },
|
|
759
|
+
file_path: { type: 'string', description: 'Absolute path to a single file to summarize. If both project_dir and file_path are given, file_path takes precedence.' },
|
|
760
|
+
focus: { type: 'string', description: 'Optional focus area: e.g. "security", "APIs", "data flow", "architecture". Narrows the summary.' },
|
|
761
|
+
format: { type: 'string', enum: ['brief', 'detailed'], description: 'brief: 4–6 bullet points (default). detailed: paragraph-level prose.' },
|
|
762
|
+
},
|
|
763
|
+
required: [],
|
|
764
|
+
},
|
|
765
|
+
},
|
|
737
766
|
],
|
|
738
767
|
}));
|
|
739
768
|
// ─── Shared Scan Utility ──────────────────────────────────────────────────────
|
|
@@ -767,7 +796,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
767
796
|
status: 'running',
|
|
768
797
|
version: VERSION,
|
|
769
798
|
server: 'veto',
|
|
770
|
-
phase:
|
|
799
|
+
phase: 17,
|
|
771
800
|
capabilities: [
|
|
772
801
|
'session_save', 'session_restore', 'sessions_list',
|
|
773
802
|
'router', 'rate_monitor',
|
|
@@ -783,7 +812,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
783
812
|
'plugins',
|
|
784
813
|
'docs_fetch', 'context_status', 'task_parse',
|
|
785
814
|
'usage_status', 'audit_log', 'health',
|
|
786
|
-
'auto_save',
|
|
815
|
+
'auto_save', 'discover', 'summarize',
|
|
787
816
|
],
|
|
788
817
|
db_path: getDbPath(),
|
|
789
818
|
uptime_ms: process.uptime() * 1000,
|
|
@@ -1748,6 +1777,107 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1748
1777
|
}],
|
|
1749
1778
|
};
|
|
1750
1779
|
}
|
|
1780
|
+
// ── Phase 16: Workspace Discovery & Summarization ─────────────────────────
|
|
1781
|
+
case 'veto_discover': {
|
|
1782
|
+
const discoverDir = String(args?.project_dir ?? '').trim();
|
|
1783
|
+
const discoverDepth = (['quick', 'standard', 'full'].includes(String(args?.depth ?? '')))
|
|
1784
|
+
? String(args.depth)
|
|
1785
|
+
: 'standard';
|
|
1786
|
+
const discoverStore = args?.store !== false;
|
|
1787
|
+
if (!discoverDir) {
|
|
1788
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'project_dir is required.' }) }], isError: true };
|
|
1789
|
+
}
|
|
1790
|
+
try {
|
|
1791
|
+
statSync(discoverDir);
|
|
1792
|
+
}
|
|
1793
|
+
catch {
|
|
1794
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Directory not found: ${discoverDir}` }) }], isError: true };
|
|
1795
|
+
}
|
|
1796
|
+
const result = discoverProject(discoverDir, discoverDepth);
|
|
1797
|
+
if (discoverStore) {
|
|
1798
|
+
updateProjectMap({
|
|
1799
|
+
project_dir: result.project_dir,
|
|
1800
|
+
structure: { ecosystems: result.ecosystems, key_files: result.key_files, file_count_by_ext: result.file_counts, total_files: result.total_files, scanned_at: result.scanned_at },
|
|
1801
|
+
key_modules: result.key_files,
|
|
1802
|
+
tech_stack: result.tech_stack,
|
|
1803
|
+
});
|
|
1804
|
+
storeKnowledge({
|
|
1805
|
+
type: 'solution',
|
|
1806
|
+
title: `Project discovery: ${result.project_dir}`,
|
|
1807
|
+
content: `Stack: ${result.tech_stack.join(', ') || 'unknown'}. Branch: ${result.git.branch || 'none'}. Commit: ${result.git.commit || 'none'}. Files: ${result.total_files}. Ecosystems: ${Object.keys(result.ecosystems).join(', ') || 'none'}. Key files: ${result.key_files.join(', ')}.`,
|
|
1808
|
+
tags: ['discover', ...result.tech_stack.map(t => t.toLowerCase().replace(/[^a-z0-9]/g, ''))],
|
|
1809
|
+
project_dir: result.project_dir,
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, stored: discoverStore, ...result }, null, 2) }] };
|
|
1813
|
+
}
|
|
1814
|
+
case 'veto_summarize': {
|
|
1815
|
+
const sumFilePath = args?.file_path ? String(args.file_path).trim() : undefined;
|
|
1816
|
+
const sumProjectDir = args?.project_dir ? String(args.project_dir).trim() : undefined;
|
|
1817
|
+
const sumFocus = args?.focus ? String(args.focus) : undefined;
|
|
1818
|
+
const sumFormat = args?.format === 'detailed' ? 'detailed' : 'brief';
|
|
1819
|
+
if (!sumFilePath && !sumProjectDir) {
|
|
1820
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'Provide project_dir or file_path.' }) }], isError: true };
|
|
1821
|
+
}
|
|
1822
|
+
const sumStart = Date.now();
|
|
1823
|
+
// ── File summary ───────────────────────────────────────────────────────
|
|
1824
|
+
if (sumFilePath) {
|
|
1825
|
+
let fileContent;
|
|
1826
|
+
try {
|
|
1827
|
+
fileContent = readFileSync(sumFilePath, 'utf8');
|
|
1828
|
+
}
|
|
1829
|
+
catch {
|
|
1830
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Cannot read file: ${sumFilePath}` }) }], isError: true };
|
|
1831
|
+
}
|
|
1832
|
+
const ext = extname(sumFilePath).toLowerCase();
|
|
1833
|
+
const name_ = basename(sumFilePath).toLowerCase();
|
|
1834
|
+
let agent = 'documentation';
|
|
1835
|
+
if (['.tsx', '.jsx', '.vue', '.svelte'].includes(ext))
|
|
1836
|
+
agent = 'frontend';
|
|
1837
|
+
else if (['.sql', '.prisma'].includes(ext) || name_.includes('schema'))
|
|
1838
|
+
agent = 'database';
|
|
1839
|
+
else if (/\.(test|spec)\.(ts|js)$/.test(name_))
|
|
1840
|
+
agent = 'tester';
|
|
1841
|
+
else if (['.yaml', '.yml', '.dockerfile'].includes(ext) || name_ === 'dockerfile')
|
|
1842
|
+
agent = 'devops';
|
|
1843
|
+
else if (name_.includes('auth') || name_.includes('jwt'))
|
|
1844
|
+
agent = 'auth';
|
|
1845
|
+
const focusNote = sumFocus ? ` Focus on: ${sumFocus}.` : '';
|
|
1846
|
+
const depthNote = sumFormat === 'detailed' ? 'Write paragraph-level prose.' : 'Return 4–6 bullet points only.';
|
|
1847
|
+
const task = `Summarize this file concisely for a developer who has never seen it.${focusNote} ${depthNote} File: ${basename(sumFilePath)}`;
|
|
1848
|
+
const r = await executeOne({ id: 'sum-file', agent, task, code: fileContent.slice(0, 8000) });
|
|
1849
|
+
return { content: [{ type: 'text', text: JSON.stringify({
|
|
1850
|
+
success: true, subject: 'file', path: sumFilePath, format: sumFormat,
|
|
1851
|
+
summary: r.plan ?? r.analysis ?? r.output,
|
|
1852
|
+
agent_used: agent, duration_ms: Date.now() - sumStart,
|
|
1853
|
+
}, null, 2) }] };
|
|
1854
|
+
}
|
|
1855
|
+
// ── Project / directory summary ────────────────────────────────────────
|
|
1856
|
+
const discResult = discoverProject(sumProjectDir, 'standard');
|
|
1857
|
+
const ctx = [
|
|
1858
|
+
`Project: ${sumProjectDir}`,
|
|
1859
|
+
`Stack: ${discResult.tech_stack.join(', ') || 'unknown'}`,
|
|
1860
|
+
`Ecosystems: ${JSON.stringify(discResult.ecosystems)}`,
|
|
1861
|
+
`Key files: ${discResult.key_files.join(', ')}`,
|
|
1862
|
+
`Total files: ${discResult.total_files}`,
|
|
1863
|
+
`Git branch: ${discResult.git.branch ?? 'none'}, commit: ${discResult.git.commit ?? 'none'}`,
|
|
1864
|
+
discResult.structure.length > 0 ? `\nFile tree (top 60 lines):\n${discResult.structure.slice(0, 60).join('\n')}` : '',
|
|
1865
|
+
].filter(Boolean).join('\n');
|
|
1866
|
+
const focusNote = sumFocus ? ` Focus especially on: ${sumFocus}.` : '';
|
|
1867
|
+
const depthNote = sumFormat === 'detailed' ? 'Write paragraph-level prose with sections.' : 'Return 5–7 bullet points that capture the essence.';
|
|
1868
|
+
const task = `You are a senior engineer briefing a colleague on this codebase.${focusNote} ${depthNote} Be concise and precise — no filler.`;
|
|
1869
|
+
const r = await executeOne({ id: 'sum-proj', agent: 'project-mapper', task, context: ctx });
|
|
1870
|
+
return { content: [{ type: 'text', text: JSON.stringify({
|
|
1871
|
+
success: true, subject: 'project', path: sumProjectDir, format: sumFormat,
|
|
1872
|
+
tech_stack: discResult.tech_stack,
|
|
1873
|
+
ecosystems: discResult.ecosystems,
|
|
1874
|
+
key_files: discResult.key_files,
|
|
1875
|
+
total_files: discResult.total_files,
|
|
1876
|
+
git: discResult.git,
|
|
1877
|
+
summary: r.plan ?? r.analysis ?? r.output,
|
|
1878
|
+
agent_used: 'project-mapper', duration_ms: Date.now() - sumStart,
|
|
1879
|
+
}, null, 2) }] };
|
|
1880
|
+
}
|
|
1751
1881
|
default:
|
|
1752
1882
|
throw new Error(`Unknown tool: ${name}`);
|
|
1753
1883
|
}
|