@jefuriiij/synthra 0.5.0 → 0.6.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 +14 -0
- package/dist/cli/index.js +99 -29
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/index.js +1 -1
- package/dist/dashboard/index.js.map +1 -1
- package/dist/server/index.js +98 -28
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -2935,6 +2935,31 @@ async function pack(files, opts) {
|
|
|
2935
2935
|
};
|
|
2936
2936
|
}
|
|
2937
2937
|
|
|
2938
|
+
// src/shared/config.ts
|
|
2939
|
+
function num(name, fallback) {
|
|
2940
|
+
const v = process.env[name];
|
|
2941
|
+
if (!v) return fallback;
|
|
2942
|
+
const n = Number(v);
|
|
2943
|
+
return Number.isFinite(n) ? n : fallback;
|
|
2944
|
+
}
|
|
2945
|
+
function str(name, fallback) {
|
|
2946
|
+
return process.env[name] ?? fallback;
|
|
2947
|
+
}
|
|
2948
|
+
function loadConfig() {
|
|
2949
|
+
return {
|
|
2950
|
+
hardMaxReadChars: num("SYN_HARD_MAX_READ_CHARS", 4e3),
|
|
2951
|
+
gateHintMaxChars: num("SYN_GATE_HINT_CHARS", 1200),
|
|
2952
|
+
readDepsMaxChars: num("SYN_READ_DEPS_CHARS", 900),
|
|
2953
|
+
turnReadBudgetChars: num("SYN_TURN_READ_BUDGET_CHARS", 18e3),
|
|
2954
|
+
fallbackMaxCallsPerTurn: num("SYN_FALLBACK_MAX_CALLS_PER_TURN", 1),
|
|
2955
|
+
retrieveCacheTtlSec: num("SYN_RETRIEVE_CACHE_TTL_SEC", 900),
|
|
2956
|
+
mcpPort: process.env.SYN_MCP_PORT ? num("SYN_MCP_PORT", 0) : null,
|
|
2957
|
+
dashboardPort: num("SYN_DASHBOARD_PORT", 8901),
|
|
2958
|
+
logLevel: str("SYN_LOG_LEVEL", "info"),
|
|
2959
|
+
claudeBin: str("SYN_CLAUDE_BIN", "claude")
|
|
2960
|
+
};
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2938
2963
|
// src/server/mcp.ts
|
|
2939
2964
|
var PROTOCOL_VERSION = "2024-11-05";
|
|
2940
2965
|
var SERVER_INFO = { name: "synthra", version: "0.0.1" };
|
|
@@ -2974,7 +2999,7 @@ var TOOLS = [
|
|
|
2974
2999
|
},
|
|
2975
3000
|
{
|
|
2976
3001
|
name: "graph_read",
|
|
2977
|
-
description: "Return the source code for a specific file or symbol. Target is either a project-relative file path (e.g. 'src/auth.ts') or 'file::symbol' (e.g. 'src/auth.ts::AuthService').",
|
|
3002
|
+
description: "Return the source code for a specific file or symbol. Target is either a project-relative file path (e.g. 'src/auth.ts') or 'file::symbol' (e.g. 'src/auth.ts::AuthService'). A symbol read also returns its dependency surface \u2014 the signatures of the symbols it calls (edit against these instead of guessing or re-reading their files) and the names of the symbols that call it.",
|
|
2978
3003
|
inputSchema: {
|
|
2979
3004
|
type: "object",
|
|
2980
3005
|
properties: {
|
|
@@ -3251,6 +3276,72 @@ function resolveFileTarget(graph, filePath) {
|
|
|
3251
3276
|
if (matches.length > 1) return { ambiguous: matches.map((n) => n.path) };
|
|
3252
3277
|
return { none: true };
|
|
3253
3278
|
}
|
|
3279
|
+
var DEPS_SIG_MAX = 140;
|
|
3280
|
+
var DEPS_MAX_CALLEES = 10;
|
|
3281
|
+
var DEPS_MAX_CALLERS = 12;
|
|
3282
|
+
function buildDepsFooter(symbol, graph, maxChars = loadConfig().readDepsMaxChars) {
|
|
3283
|
+
const symById = /* @__PURE__ */ new Map();
|
|
3284
|
+
for (const n of graph.nodes) if (n.kind === "symbol") symById.set(n.id, n);
|
|
3285
|
+
const calleeIds = [];
|
|
3286
|
+
const callerIds = [];
|
|
3287
|
+
const seenCallee = /* @__PURE__ */ new Set();
|
|
3288
|
+
const seenCaller = /* @__PURE__ */ new Set();
|
|
3289
|
+
for (const e of graph.edges) {
|
|
3290
|
+
if (e.kind !== "calls") continue;
|
|
3291
|
+
if (e.from === symbol.id && e.to !== symbol.id && !seenCallee.has(e.to)) {
|
|
3292
|
+
seenCallee.add(e.to);
|
|
3293
|
+
calleeIds.push(e.to);
|
|
3294
|
+
} else if (e.to === symbol.id && e.from !== symbol.id && !seenCaller.has(e.from)) {
|
|
3295
|
+
seenCaller.add(e.from);
|
|
3296
|
+
callerIds.push(e.from);
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
const resolve2 = (ids) => ids.map((id) => symById.get(id)).filter((n) => !!n);
|
|
3300
|
+
const callees = resolve2(calleeIds).sort(
|
|
3301
|
+
(a, b) => a.file === b.file ? a.start_line - b.start_line : a.file < b.file ? -1 : 1
|
|
3302
|
+
);
|
|
3303
|
+
const callers = resolve2(callerIds);
|
|
3304
|
+
if (callees.length === 0 && callers.length === 0) return "";
|
|
3305
|
+
const lines = [];
|
|
3306
|
+
let used = 0;
|
|
3307
|
+
if (callees.length > 0) {
|
|
3308
|
+
const head = "Depends on (signatures \u2014 don't guess these):";
|
|
3309
|
+
lines.push(head);
|
|
3310
|
+
used += head.length + 1;
|
|
3311
|
+
let shown = 0;
|
|
3312
|
+
for (const c of callees.slice(0, DEPS_MAX_CALLEES)) {
|
|
3313
|
+
const sig = c.signature.trim().slice(0, DEPS_SIG_MAX);
|
|
3314
|
+
const entry = `\u2022 ${sig} \u2192 mcp__synthra__graph_read("${c.file}::${c.name}")`;
|
|
3315
|
+
if (used + entry.length + 1 > maxChars) break;
|
|
3316
|
+
lines.push(entry);
|
|
3317
|
+
used += entry.length + 1;
|
|
3318
|
+
shown += 1;
|
|
3319
|
+
}
|
|
3320
|
+
const omitted = callees.length - shown;
|
|
3321
|
+
if (omitted > 0) lines.push(`\u2026+${omitted} more`);
|
|
3322
|
+
}
|
|
3323
|
+
if (callers.length > 0) {
|
|
3324
|
+
const sep3 = lines.length > 0 ? 1 : 0;
|
|
3325
|
+
const head = `Used by (${callers.length}): `;
|
|
3326
|
+
const shown = [];
|
|
3327
|
+
let cUsed = used + sep3 + head.length;
|
|
3328
|
+
for (const c of callers.slice(0, DEPS_MAX_CALLERS)) {
|
|
3329
|
+
const part = `${c.name} \u2192 ${c.file}`;
|
|
3330
|
+
const join7 = shown.length > 0 ? 3 : 0;
|
|
3331
|
+
if (cUsed + join7 + part.length > maxChars) break;
|
|
3332
|
+
shown.push(part);
|
|
3333
|
+
cUsed += join7 + part.length;
|
|
3334
|
+
}
|
|
3335
|
+
if (lines.length > 0) lines.push("");
|
|
3336
|
+
if (shown.length > 0) {
|
|
3337
|
+
const omitted = callers.length - shown.length;
|
|
3338
|
+
lines.push(head + shown.join(" \xB7 ") + (omitted > 0 ? ` \u2026+${omitted} more` : ""));
|
|
3339
|
+
} else {
|
|
3340
|
+
lines.push(`Used by (${callers.length} callers)`);
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
return lines.join("\n");
|
|
3344
|
+
}
|
|
3254
3345
|
async function graphRead(args, ctx) {
|
|
3255
3346
|
const target = typeof args?.target === "string" ? args.target : "";
|
|
3256
3347
|
if (!target) return errorContent("graph_read: 'target' (string) is required");
|
|
@@ -3289,10 +3380,15 @@ ${fileNode.content}`);
|
|
|
3289
3380
|
|
|
3290
3381
|
---
|
|
3291
3382
|
\u270E To edit this symbol: Read("${fileNode.path}", offset=${offset}, limit=${limit}) then Edit \u2014 that satisfies Claude Code's read-gate at ~${limit} lines; do NOT re-read the whole file.`;
|
|
3383
|
+
const deps = buildDepsFooter(symbol, ctx.graph);
|
|
3384
|
+
const depsBlock = deps ? `
|
|
3385
|
+
|
|
3386
|
+
---
|
|
3387
|
+
${deps}` : "";
|
|
3292
3388
|
return textContent(
|
|
3293
3389
|
`# ${fileNode.path}::${symbol.name} (L${symbol.start_line}-${symbol.end_line})
|
|
3294
3390
|
|
|
3295
|
-
${body}${editHint}`
|
|
3391
|
+
${body}${depsBlock}${editHint}`
|
|
3296
3392
|
);
|
|
3297
3393
|
}
|
|
3298
3394
|
var editedFiles = /* @__PURE__ */ new Set();
|
|
@@ -3558,32 +3654,6 @@ async function handleContextUpdate(req, ctx) {
|
|
|
3558
3654
|
// src/server/routes/gate.ts
|
|
3559
3655
|
import { appendFile as appendFile4, mkdir as mkdir10 } from "fs/promises";
|
|
3560
3656
|
import { dirname as dirname11 } from "path";
|
|
3561
|
-
|
|
3562
|
-
// src/shared/config.ts
|
|
3563
|
-
function num(name, fallback) {
|
|
3564
|
-
const v = process.env[name];
|
|
3565
|
-
if (!v) return fallback;
|
|
3566
|
-
const n = Number(v);
|
|
3567
|
-
return Number.isFinite(n) ? n : fallback;
|
|
3568
|
-
}
|
|
3569
|
-
function str(name, fallback) {
|
|
3570
|
-
return process.env[name] ?? fallback;
|
|
3571
|
-
}
|
|
3572
|
-
function loadConfig() {
|
|
3573
|
-
return {
|
|
3574
|
-
hardMaxReadChars: num("SYN_HARD_MAX_READ_CHARS", 4e3),
|
|
3575
|
-
gateHintMaxChars: num("SYN_GATE_HINT_CHARS", 1200),
|
|
3576
|
-
turnReadBudgetChars: num("SYN_TURN_READ_BUDGET_CHARS", 18e3),
|
|
3577
|
-
fallbackMaxCallsPerTurn: num("SYN_FALLBACK_MAX_CALLS_PER_TURN", 1),
|
|
3578
|
-
retrieveCacheTtlSec: num("SYN_RETRIEVE_CACHE_TTL_SEC", 900),
|
|
3579
|
-
mcpPort: process.env.SYN_MCP_PORT ? num("SYN_MCP_PORT", 0) : null,
|
|
3580
|
-
dashboardPort: num("SYN_DASHBOARD_PORT", 8901),
|
|
3581
|
-
logLevel: str("SYN_LOG_LEVEL", "info"),
|
|
3582
|
-
claudeBin: str("SYN_CLAUDE_BIN", "claude")
|
|
3583
|
-
};
|
|
3584
|
-
}
|
|
3585
|
-
|
|
3586
|
-
// src/server/routes/gate.ts
|
|
3587
3657
|
var BLOCKABLE_TOOLS = /* @__PURE__ */ new Set(["Grep", "Glob"]);
|
|
3588
3658
|
var RECENT_ACTIVITY_WINDOW_MS = 5 * 60 * 1e3;
|
|
3589
3659
|
function extractQuery(toolName, input) {
|