@systemdox/mcp-server 0.1.50 → 0.1.52
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/dist/cli.js +144 -27
- package/dist/http.js +126 -19
- package/dist/index.js +126 -19
- package/dist/install-skill.js +4 -1
- package/dist/lib/context-builder.js +98 -11
- package/dist/lib/scoring.d.ts +5 -0
- package/dist/lib/skills.d.ts +7 -0
- package/dist/lib/skills.js +5 -1
- package/dist/lib/spec-label.d.ts +5 -0
- package/dist/server.js +126 -19
- package/dist/setup-mcp.js +2 -0
- package/dist/tools/get-docs.js +14 -1
- package/dist/tools/skills.js +5 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -592,9 +592,9 @@ function localDate(now) {
|
|
|
592
592
|
return `${now.getFullYear()}-${p(now.getMonth() + 1)}-${p(now.getDate())}`;
|
|
593
593
|
}
|
|
594
594
|
function conflictCopyName(path, date, taken) {
|
|
595
|
-
const
|
|
595
|
+
const stem2 = path.replace(/\.md$/, "");
|
|
596
596
|
for (let n = 1; ; n++) {
|
|
597
|
-
const name = `${
|
|
597
|
+
const name = `${stem2}.local-${date}${n === 1 ? "" : `-${n}`}.md`;
|
|
598
598
|
if (!taken.has(name.toLowerCase())) return name;
|
|
599
599
|
}
|
|
600
600
|
}
|
|
@@ -1828,6 +1828,7 @@ __export(setup_mcp_exports, {
|
|
|
1828
1828
|
writeServerEntry: () => writeServerEntry
|
|
1829
1829
|
});
|
|
1830
1830
|
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
1831
|
+
import { mkdir as mkdir4 } from "fs/promises";
|
|
1831
1832
|
import { join as join4 } from "path";
|
|
1832
1833
|
async function validateApiKey(apiKey, apiUrl) {
|
|
1833
1834
|
try {
|
|
@@ -1955,6 +1956,7 @@ async function setupMcpServer(opts) {
|
|
|
1955
1956
|
}
|
|
1956
1957
|
try {
|
|
1957
1958
|
if ((await readCredentials())[workspace]?.apiKey !== apiKey) await writeCredentials(workspace, apiKey);
|
|
1959
|
+
await mkdir4(join4(homeDir(), ".claude", "skills"), { recursive: true });
|
|
1958
1960
|
const several = Object.keys(await readCredentials()).length > 1;
|
|
1959
1961
|
for (const target of selected) {
|
|
1960
1962
|
const removed = await writeServerEntry(target.path, apiUrl, several ? workspace : void 0);
|
|
@@ -2000,7 +2002,7 @@ var init_setup_mcp = __esm({
|
|
|
2000
2002
|
|
|
2001
2003
|
// src/lib/skills.ts
|
|
2002
2004
|
import { createHash as createHash3 } from "crypto";
|
|
2003
|
-
import { lstat, mkdir as
|
|
2005
|
+
import { lstat, mkdir as mkdir5, readdir as readdir3, readFile as readFile4, rm as rm3, writeFile as writeFile4 } from "fs/promises";
|
|
2004
2006
|
import { existsSync as existsSync4 } from "fs";
|
|
2005
2007
|
import { homedir as homedir2 } from "os";
|
|
2006
2008
|
import { dirname as dirname4, join as join5, relative, resolve as resolve3, sep } from "path";
|
|
@@ -2125,7 +2127,7 @@ async function installSkill(version, dest, { overwrite = false } = {}) {
|
|
|
2125
2127
|
const written = [];
|
|
2126
2128
|
for (const f of version.files) {
|
|
2127
2129
|
const full = within(dest, f.path);
|
|
2128
|
-
await
|
|
2130
|
+
await mkdir5(dirname4(full), { recursive: true });
|
|
2129
2131
|
await writeFile4(full, f.content, "utf8");
|
|
2130
2132
|
written.push(f.path);
|
|
2131
2133
|
}
|
|
@@ -2160,6 +2162,9 @@ function fenceFor(text3) {
|
|
|
2160
2162
|
const longest = Math.max(0, ...(text3.match(/`+/g) ?? []).map((r) => r.length));
|
|
2161
2163
|
return "`".repeat(Math.max(3, longest + 1));
|
|
2162
2164
|
}
|
|
2165
|
+
function afterInstall(skillMd) {
|
|
2166
|
+
return `No restart is needed: a running Claude Code session picks it up. If one does not list it yet, read ${skillMd} and follow it, or run /reload-skills.`;
|
|
2167
|
+
}
|
|
2163
2168
|
function describeInstall(name, r) {
|
|
2164
2169
|
const lines = [
|
|
2165
2170
|
`Installed ${name} version ${r.version} into ${r.dest} (${r.written.length} file(s)).`
|
|
@@ -2169,7 +2174,7 @@ function describeInstall(name, r) {
|
|
|
2169
2174
|
lines.push(`Kept, edited here and no longer in the skill: ${r.keptEdited.join(", ")}`);
|
|
2170
2175
|
}
|
|
2171
2176
|
if (r.keptUntracked.length) lines.push(`Kept, added here: ${r.keptUntracked.join(", ")}`);
|
|
2172
|
-
lines.push("
|
|
2177
|
+
lines.push(afterInstall(join5(r.dest, "SKILL.md")));
|
|
2173
2178
|
return lines.join("\n");
|
|
2174
2179
|
}
|
|
2175
2180
|
var MANIFEST_FILE, ENTRY_FILE, PLUGIN_MANIFEST_FOLDER, MAX_FILES, MAX_FILE_BYTES, MAX_TOTAL_BYTES, NAME_RE, SEGMENT_RE, SKIPPED, SkillInstallRefused;
|
|
@@ -2296,7 +2301,7 @@ __export(plugin_path_exports, {
|
|
|
2296
2301
|
});
|
|
2297
2302
|
import { createHash as createHash4, randomBytes } from "crypto";
|
|
2298
2303
|
import { existsSync as existsSync5 } from "fs";
|
|
2299
|
-
import { mkdir as
|
|
2304
|
+
import { mkdir as mkdir6, rename as rename2, writeFile as writeFile5 } from "fs/promises";
|
|
2300
2305
|
import { dirname as dirname5, join as join6, resolve as resolve5, sep as sep2 } from "path";
|
|
2301
2306
|
import { inflateRawSync } from "zlib";
|
|
2302
2307
|
function parsePluginPathArgs(argv) {
|
|
@@ -2394,13 +2399,13 @@ async function unpackZip(zip, dest) {
|
|
|
2394
2399
|
return full;
|
|
2395
2400
|
});
|
|
2396
2401
|
const data = entries2.map((e) => e.directory ? void 0 : entryData(zip, e));
|
|
2397
|
-
await
|
|
2402
|
+
await mkdir6(root, { recursive: true });
|
|
2398
2403
|
for (const [i, entry] of entries2.entries()) {
|
|
2399
2404
|
if (entry.directory) {
|
|
2400
|
-
await
|
|
2405
|
+
await mkdir6(targets[i], { recursive: true });
|
|
2401
2406
|
continue;
|
|
2402
2407
|
}
|
|
2403
|
-
await
|
|
2408
|
+
await mkdir6(dirname5(targets[i]), { recursive: true });
|
|
2404
2409
|
await writeFile5(targets[i], data[i], { mode: entry.mode ?? 420 });
|
|
2405
2410
|
}
|
|
2406
2411
|
return entries2.filter((e) => !e.directory).length;
|
|
@@ -2852,6 +2857,21 @@ var init_get_guardrails = __esm({
|
|
|
2852
2857
|
}
|
|
2853
2858
|
});
|
|
2854
2859
|
|
|
2860
|
+
// src/lib/spec-label.ts
|
|
2861
|
+
function isDecisionDraft(name) {
|
|
2862
|
+
return name.startsWith(DECISION_PREFIX);
|
|
2863
|
+
}
|
|
2864
|
+
function specLabel(spec) {
|
|
2865
|
+
return isDecisionDraft(spec.name) ? "decision record" : `${spec.requirementCount} requirements`;
|
|
2866
|
+
}
|
|
2867
|
+
var DECISION_PREFIX;
|
|
2868
|
+
var init_spec_label = __esm({
|
|
2869
|
+
"src/lib/spec-label.ts"() {
|
|
2870
|
+
"use strict";
|
|
2871
|
+
DECISION_PREFIX = "ADR: ";
|
|
2872
|
+
}
|
|
2873
|
+
});
|
|
2874
|
+
|
|
2855
2875
|
// src/tools/get-specs.ts
|
|
2856
2876
|
import { z as z2 } from "zod";
|
|
2857
2877
|
function registerGetSpecs(server, apiClient2) {
|
|
@@ -2918,7 +2938,7 @@ ${spec.taskContent}`);
|
|
|
2918
2938
|
};
|
|
2919
2939
|
}
|
|
2920
2940
|
const text3 = filtered.map(
|
|
2921
|
-
(s) => `- **${s.name}** (${s.draftId}) \u2014 ${s.status}, ${s
|
|
2941
|
+
(s) => `- **${s.name}** (${s.draftId}) \u2014 ${s.status}, ${specLabel(s)}` + (s.repoFullName ? `, repo: ${s.repoFullName}` : "")
|
|
2922
2942
|
).join("\n");
|
|
2923
2943
|
return {
|
|
2924
2944
|
content: [
|
|
@@ -2936,11 +2956,16 @@ ${text3}`
|
|
|
2936
2956
|
var init_get_specs = __esm({
|
|
2937
2957
|
"src/tools/get-specs.ts"() {
|
|
2938
2958
|
"use strict";
|
|
2959
|
+
init_spec_label();
|
|
2939
2960
|
}
|
|
2940
2961
|
});
|
|
2941
2962
|
|
|
2942
2963
|
// src/tools/get-docs.ts
|
|
2943
2964
|
import { z as z3 } from "zod";
|
|
2965
|
+
function literalPrefix(pattern) {
|
|
2966
|
+
const wildcard = pattern.indexOf("*");
|
|
2967
|
+
return wildcard < 0 ? pattern : pattern.slice(0, wildcard);
|
|
2968
|
+
}
|
|
2944
2969
|
function matchGlob(path, pattern) {
|
|
2945
2970
|
const regex = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
2946
2971
|
return new RegExp(`^${regex}$`).test(path);
|
|
@@ -3001,11 +3026,17 @@ ${doc.content}`;
|
|
|
3001
3026
|
const params = {};
|
|
3002
3027
|
if (repo) params.repo = repo;
|
|
3003
3028
|
if (search) params.q = search;
|
|
3029
|
+
if (path) {
|
|
3030
|
+
params.limit = PATH_FILTER_LIMIT;
|
|
3031
|
+
const prefix = literalPrefix(path);
|
|
3032
|
+
if (prefix) params.pathPrefix = prefix;
|
|
3033
|
+
}
|
|
3004
3034
|
const response = await apiClient2.get(
|
|
3005
3035
|
"/v1/docs",
|
|
3006
3036
|
params
|
|
3007
3037
|
);
|
|
3008
3038
|
let items = response.items;
|
|
3039
|
+
const unread = path ? response.total - response.items.length : 0;
|
|
3009
3040
|
if (path) {
|
|
3010
3041
|
items = items.filter((item2) => matchGlob(item2.path, path));
|
|
3011
3042
|
}
|
|
@@ -3035,16 +3066,20 @@ ${doc.content}`;
|
|
|
3035
3066
|
|
|
3036
3067
|
${text3}
|
|
3037
3068
|
|
|
3038
|
-
|
|
3069
|
+
` + (unread > 0 ? `${unread} more were not read. Start the path with a folder to narrow it.
|
|
3070
|
+
|
|
3071
|
+
` : "") + "Use doc_id for full content, or doc_id + section to fetch one section."
|
|
3039
3072
|
}
|
|
3040
3073
|
]
|
|
3041
3074
|
};
|
|
3042
3075
|
}
|
|
3043
3076
|
);
|
|
3044
3077
|
}
|
|
3078
|
+
var PATH_FILTER_LIMIT;
|
|
3045
3079
|
var init_get_docs = __esm({
|
|
3046
3080
|
"src/tools/get-docs.ts"() {
|
|
3047
3081
|
"use strict";
|
|
3082
|
+
PATH_FILTER_LIMIT = "1000";
|
|
3048
3083
|
}
|
|
3049
3084
|
});
|
|
3050
3085
|
|
|
@@ -3147,7 +3182,9 @@ ${deps}`);
|
|
|
3147
3182
|
}
|
|
3148
3183
|
if (ctx.adrs.length > 0) {
|
|
3149
3184
|
const adrs = ctx.adrs.map((a) => `- ${a.title}${a.repo ? ` (${a.repo})` : ""}`).join("\n");
|
|
3150
|
-
|
|
3185
|
+
const total = ctx.adrTotal ?? ctx.adrs.length;
|
|
3186
|
+
const count = total > ctx.adrs.length ? `${ctx.adrs.length} newest of ${total}` : `${ctx.adrs.length}`;
|
|
3187
|
+
sections.push(`## Architecture Decision Records (${count})
|
|
3151
3188
|
|
|
3152
3189
|
${adrs}`);
|
|
3153
3190
|
}
|
|
@@ -3576,6 +3613,32 @@ function scoreSeverity(item2) {
|
|
|
3576
3613
|
if (!item2.severity) return 0.5;
|
|
3577
3614
|
return SEVERITY_SCORES[item2.severity] ?? 0.5;
|
|
3578
3615
|
}
|
|
3616
|
+
function stem(word) {
|
|
3617
|
+
return word.length > 3 ? word.replace(/s$/, "") : word;
|
|
3618
|
+
}
|
|
3619
|
+
function stems(text3) {
|
|
3620
|
+
return new Set(
|
|
3621
|
+
text3.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean).map(stem)
|
|
3622
|
+
);
|
|
3623
|
+
}
|
|
3624
|
+
function titleShare(title, keywords) {
|
|
3625
|
+
const wanted = stems(keywords.join(" "));
|
|
3626
|
+
if (wanted.size === 0) return 0;
|
|
3627
|
+
const held = stems(title);
|
|
3628
|
+
let matches = 0;
|
|
3629
|
+
for (const word of wanted) {
|
|
3630
|
+
if (held.has(word)) matches++;
|
|
3631
|
+
}
|
|
3632
|
+
return matches / wanted.size;
|
|
3633
|
+
}
|
|
3634
|
+
function scoreReference(item2, keywords) {
|
|
3635
|
+
const share = titleShare(item2.title, keywords);
|
|
3636
|
+
const score = Math.max(
|
|
3637
|
+
item2.relevance ?? 0,
|
|
3638
|
+
share >= MIN_TITLE_SHARE ? share : 0
|
|
3639
|
+
);
|
|
3640
|
+
return RECORD_PATH.test(item2.path ?? "") ? score * RECORD_FACTOR : score;
|
|
3641
|
+
}
|
|
3579
3642
|
function extractKeywords(text3) {
|
|
3580
3643
|
const stopWords = /* @__PURE__ */ new Set([
|
|
3581
3644
|
"the",
|
|
@@ -3672,7 +3735,8 @@ function scoreItems(items, taskDescription, filePaths) {
|
|
|
3672
3735
|
const keywordScore = scoreKeywordMatch(item2, keywords);
|
|
3673
3736
|
const recencyScore = scoreRecency(item2);
|
|
3674
3737
|
const severityScore = scoreSeverity(item2);
|
|
3675
|
-
const
|
|
3738
|
+
const isReference2 = item2.type === "spec" || item2.type === "doc";
|
|
3739
|
+
const score = isReference2 && keywords.length > 0 ? scoreReference(item2, keywords) : pathScore * WEIGHT_PATH + keywordScore * WEIGHT_KEYWORD + recencyScore * WEIGHT_RECENCY + severityScore * WEIGHT_SEVERITY;
|
|
3676
3740
|
return {
|
|
3677
3741
|
type: item2.type,
|
|
3678
3742
|
id: item2.id,
|
|
@@ -3681,7 +3745,7 @@ function scoreItems(items, taskDescription, filePaths) {
|
|
|
3681
3745
|
score,
|
|
3682
3746
|
tokenEstimate: estimateTokens(item2.content)
|
|
3683
3747
|
};
|
|
3684
|
-
}).sort((a, b) => b.score - a.score);
|
|
3748
|
+
}).filter((item2) => item2.score > 0).sort((a, b) => b.score - a.score);
|
|
3685
3749
|
}
|
|
3686
3750
|
function selectWithinBudget(items, maxTokens) {
|
|
3687
3751
|
const selected = [];
|
|
@@ -3693,7 +3757,7 @@ function selectWithinBudget(items, maxTokens) {
|
|
|
3693
3757
|
}
|
|
3694
3758
|
return { selected, tokensUsed, totalItems: items.length };
|
|
3695
3759
|
}
|
|
3696
|
-
var WEIGHT_PATH, WEIGHT_KEYWORD, WEIGHT_RECENCY, WEIGHT_SEVERITY, SEVERITY_SCORES, CHARS_PER_TOKEN;
|
|
3760
|
+
var WEIGHT_PATH, WEIGHT_KEYWORD, WEIGHT_RECENCY, WEIGHT_SEVERITY, SEVERITY_SCORES, MIN_TITLE_SHARE, RECORD_PATH, RECORD_FACTOR, CHARS_PER_TOKEN;
|
|
3697
3761
|
var init_scoring = __esm({
|
|
3698
3762
|
"src/lib/scoring.ts"() {
|
|
3699
3763
|
"use strict";
|
|
@@ -3708,6 +3772,9 @@ var init_scoring = __esm({
|
|
|
3708
3772
|
medium: 0.5,
|
|
3709
3773
|
low: 0.25
|
|
3710
3774
|
};
|
|
3775
|
+
MIN_TITLE_SHARE = 1 / 3;
|
|
3776
|
+
RECORD_PATH = /(^|\/)changelog\//;
|
|
3777
|
+
RECORD_FACTOR = 0.5;
|
|
3711
3778
|
CHARS_PER_TOKEN = 4;
|
|
3712
3779
|
}
|
|
3713
3780
|
});
|
|
@@ -3731,7 +3798,7 @@ ${g.fix}
|
|
|
3731
3798
|
}
|
|
3732
3799
|
function formatSpecHeadline(s) {
|
|
3733
3800
|
return `### ${s.name}
|
|
3734
|
-
\`draft_id: ${s.draftId}\` \xB7 ${s.status} \xB7 ${s
|
|
3801
|
+
\`draft_id: ${s.draftId}\` \xB7 ${s.status} \xB7 ${specLabel(s)}`;
|
|
3735
3802
|
}
|
|
3736
3803
|
function formatDocHeadline(d) {
|
|
3737
3804
|
return `### ${d.title}
|
|
@@ -3742,6 +3809,21 @@ function formatStack(entries2) {
|
|
|
3742
3809
|
(e) => `- ${e.name}${e.version ? ` v${e.version}` : ""}${e.notes ? ` \u2014 ${e.notes}` : ""}`
|
|
3743
3810
|
).join("\n");
|
|
3744
3811
|
}
|
|
3812
|
+
function selectStack(entries2, words, maxTokens) {
|
|
3813
|
+
const named = (e) => {
|
|
3814
|
+
const entry = `${e.name} ${e.notes ?? ""}`.toLowerCase();
|
|
3815
|
+
return words.some((w) => entry.includes(w));
|
|
3816
|
+
};
|
|
3817
|
+
const ordered = [...entries2.filter(named), ...entries2.filter((e) => !named(e))];
|
|
3818
|
+
const shown = [];
|
|
3819
|
+
for (const entry of ordered) {
|
|
3820
|
+
if (shown.length > 0 && estimateTokens(formatStack([...shown, entry])) > maxTokens) {
|
|
3821
|
+
break;
|
|
3822
|
+
}
|
|
3823
|
+
shown.push(entry);
|
|
3824
|
+
}
|
|
3825
|
+
return { text: formatStack(shown), hidden: entries2.length - shown.length };
|
|
3826
|
+
}
|
|
3745
3827
|
function formatRules(rules) {
|
|
3746
3828
|
const byCategory = /* @__PURE__ */ new Map();
|
|
3747
3829
|
for (const r of rules) {
|
|
@@ -3763,6 +3845,21 @@ function formatRules(rules) {
|
|
|
3763
3845
|
|
|
3764
3846
|
${text3.trim()}`;
|
|
3765
3847
|
}
|
|
3848
|
+
async function findDocs(api, taskDescription) {
|
|
3849
|
+
const query = taskDescription.trim().slice(0, MAX_QUERY_CHARS);
|
|
3850
|
+
if (query) {
|
|
3851
|
+
try {
|
|
3852
|
+
const found = await api.get("/v1/docs", {
|
|
3853
|
+
q: query,
|
|
3854
|
+
limit: String(SEARCH_RESULTS)
|
|
3855
|
+
});
|
|
3856
|
+
if (found.items.length > 0) return { ...found, searched: true };
|
|
3857
|
+
} catch {
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
const listed = await api.get("/v1/docs");
|
|
3861
|
+
return { ...listed, searched: false };
|
|
3862
|
+
}
|
|
3766
3863
|
async function fetchSpecBody(api, draftId) {
|
|
3767
3864
|
const spec = await api.get(
|
|
3768
3865
|
`/v1/specs/${encodeURIComponent(draftId)}`
|
|
@@ -3841,7 +3938,9 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
3841
3938
|
const [rawGuardrails, rawSpecs, docs, stack, rules, docRepos] = await Promise.all([
|
|
3842
3939
|
api.get("/v1/guardrails").catch(orEmpty([])),
|
|
3843
3940
|
api.get("/v1/specs").catch(orEmpty([])),
|
|
3844
|
-
api
|
|
3941
|
+
findDocs(api, taskDescription).catch(
|
|
3942
|
+
orEmpty({ items: [], searched: false })
|
|
3943
|
+
),
|
|
3845
3944
|
api.get("/v1/ai/settings/stack").catch(orEmpty([])),
|
|
3846
3945
|
api.get("/v1/standards/rules").catch(orEmpty([])),
|
|
3847
3946
|
// Capability states — FAIL-OPEN: an unreadable config (older backend, a
|
|
@@ -3854,6 +3953,8 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
3854
3953
|
const specs = disabled("requirements") && disabled("design") && disabled("specs") ? [] : rawSpecs;
|
|
3855
3954
|
const refs = /* @__PURE__ */ new Map();
|
|
3856
3955
|
const key = (type, id) => `${type}:${id}`;
|
|
3956
|
+
const docTitles = new Set(docs.items.map((d) => d.title));
|
|
3957
|
+
const isCopyOfDoc = (s) => isDecisionDraft(s.name) && docTitles.has(s.name.slice("ADR: ".length));
|
|
3857
3958
|
const scorable = [
|
|
3858
3959
|
...guardrails.map((g) => ({
|
|
3859
3960
|
type: "guardrail",
|
|
@@ -3864,12 +3965,12 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
3864
3965
|
severity: g.severity,
|
|
3865
3966
|
updatedAt: g.updatedAt
|
|
3866
3967
|
})),
|
|
3867
|
-
...specs.filter((s) => s.status !== "archived").map((s) => {
|
|
3968
|
+
...specs.filter((s) => s.status !== "archived" && !isCopyOfDoc(s)).map((s) => {
|
|
3868
3969
|
refs.set(key("spec", s.draftId), {
|
|
3869
3970
|
type: "spec",
|
|
3870
3971
|
id: s.draftId,
|
|
3871
3972
|
title: s.name,
|
|
3872
|
-
listLine: `- **${s.name}** \u2014 \`draft_id: ${s.draftId}\` (${s.status}, ${s
|
|
3973
|
+
listLine: `- **${s.name}** \u2014 \`draft_id: ${s.draftId}\` (${s.status}, ${specLabel(s)})`,
|
|
3873
3974
|
fetchHint: `get_specs(draft_id="${s.draftId}")`
|
|
3874
3975
|
});
|
|
3875
3976
|
return {
|
|
@@ -3880,7 +3981,7 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
3880
3981
|
updatedAt: s.updatedAt
|
|
3881
3982
|
};
|
|
3882
3983
|
}),
|
|
3883
|
-
...docs.items.map((d) => {
|
|
3984
|
+
...docs.items.map((d, position) => {
|
|
3884
3985
|
const section = selectSection(d.outline, taskDescription);
|
|
3885
3986
|
const pointer = section ? formatSectionPointer(d.repo_full_name, d.path, section) : void 0;
|
|
3886
3987
|
refs.set(key("doc", d.id), {
|
|
@@ -3899,7 +4000,9 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
3899
4000
|
title: d.title,
|
|
3900
4001
|
content: formatDocHeadline(d),
|
|
3901
4002
|
category: d.doc_type === "adr" ? "architecture" : void 0,
|
|
3902
|
-
updatedAt: d.synced_at
|
|
4003
|
+
updatedAt: d.synced_at,
|
|
4004
|
+
path: d.path,
|
|
4005
|
+
relevance: docs.searched ? 1 - position / (2 * docs.items.length) : void 0
|
|
3903
4006
|
};
|
|
3904
4007
|
})
|
|
3905
4008
|
];
|
|
@@ -3907,9 +4010,16 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
3907
4010
|
const sections = [];
|
|
3908
4011
|
let overhead = 0;
|
|
3909
4012
|
if (stack.length > 0) {
|
|
4013
|
+
const { text: text3, hidden } = selectStack(
|
|
4014
|
+
stack,
|
|
4015
|
+
[...extractKeywords(taskDescription), ...detectLanguages(filePaths)],
|
|
4016
|
+
Math.floor(budget * STACK_SHARE)
|
|
4017
|
+
);
|
|
3910
4018
|
const section = `## Tech Stack
|
|
3911
4019
|
|
|
3912
|
-
${
|
|
4020
|
+
${text3}` + (hidden > 0 ? `
|
|
4021
|
+
|
|
4022
|
+
_${hidden} more with \`get_tech_stack\`._` : "");
|
|
3913
4023
|
sections.push(section);
|
|
3914
4024
|
overhead += estimateTokens(section);
|
|
3915
4025
|
}
|
|
@@ -4028,9 +4138,10 @@ _Highest-ranked items \u2014 docs are scoped to their most relevant section. Eac
|
|
|
4028
4138
|
const tailLines = [];
|
|
4029
4139
|
for (const meta of tailCandidates) {
|
|
4030
4140
|
const cost = estimateTokens(meta.listLine);
|
|
4031
|
-
|
|
4141
|
+
const named = blocks.length + tailLines.length;
|
|
4142
|
+
if (cost > remaining && named >= MIN_REFERENCES) break;
|
|
4032
4143
|
tailLines.push(meta.listLine);
|
|
4033
|
-
remaining
|
|
4144
|
+
remaining = Math.max(0, remaining - cost);
|
|
4034
4145
|
}
|
|
4035
4146
|
if (tailLines.length > 0) {
|
|
4036
4147
|
sections.push(
|
|
@@ -4064,11 +4175,13 @@ _Ranked lower \u2014 fetch with \`get_specs\` / \`get_docs\` if you need them._
|
|
|
4064
4175
|
_Context: ${itemsWithBody} full text, ${itemsListed} listed, ${stats.itemsOmitted} omitted \xB7 ~${tokensUsed} of ${budget} tokens` + (bodyFailures > 0 ? ` \xB7 ${bodyFailures} body fetch(es) failed` : "") + (failures.length > 0 ? ` \xB7 ${failures.length} of 5 reads failed` : "") + `_`;
|
|
4065
4176
|
return { text: body + footer, stats };
|
|
4066
4177
|
}
|
|
4067
|
-
var DEFAULT_BUDGET, RESERVE_TOKENS, GUARDRAIL_SHARE, MIN_BODY_TOKENS, MAX_HYDRATED, MAX_TAIL, TAIL_SHARE, TRUNCATION_NOTICE_TOKENS, SECTION_STOP_WORDS;
|
|
4178
|
+
var DEFAULT_BUDGET, RESERVE_TOKENS, GUARDRAIL_SHARE, MIN_BODY_TOKENS, MAX_HYDRATED, MAX_TAIL, TAIL_SHARE, TRUNCATION_NOTICE_TOKENS, STACK_SHARE, MIN_REFERENCES, SEARCH_RESULTS, MAX_QUERY_CHARS, SECTION_STOP_WORDS;
|
|
4068
4179
|
var init_context_builder = __esm({
|
|
4069
4180
|
"src/lib/context-builder.ts"() {
|
|
4070
4181
|
"use strict";
|
|
4182
|
+
init_language();
|
|
4071
4183
|
init_scoring();
|
|
4184
|
+
init_spec_label();
|
|
4072
4185
|
init_scoring();
|
|
4073
4186
|
DEFAULT_BUDGET = 4e3;
|
|
4074
4187
|
RESERVE_TOKENS = 200;
|
|
@@ -4078,6 +4191,10 @@ var init_context_builder = __esm({
|
|
|
4078
4191
|
MAX_TAIL = 10;
|
|
4079
4192
|
TAIL_SHARE = 0.15;
|
|
4080
4193
|
TRUNCATION_NOTICE_TOKENS = 30;
|
|
4194
|
+
STACK_SHARE = 0.1;
|
|
4195
|
+
MIN_REFERENCES = 3;
|
|
4196
|
+
SEARCH_RESULTS = 30;
|
|
4197
|
+
MAX_QUERY_CHARS = 300;
|
|
4081
4198
|
SECTION_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
4082
4199
|
"the",
|
|
4083
4200
|
"a",
|
|
@@ -4953,7 +5070,7 @@ ${fence}
|
|
|
4953
5070
|
${f.content}${f.content.endsWith("\n") ? "" : "\n"}${fence}`;
|
|
4954
5071
|
}).join("\n\n");
|
|
4955
5072
|
return text(
|
|
4956
|
-
`Skill **${version.name}** version ${version.version}: write these ${version.files.length} file(s) into ${dest}/ exactly as shown (the content between the fences), creating folders as needed. If a file there differs from ${MANIFEST_FILE}'s hash for it, it was edited: ask before replacing it. Then write ${MANIFEST_FILE} with this content
|
|
5073
|
+
`Skill **${version.name}** version ${version.version}: write these ${version.files.length} file(s) into ${dest}/ exactly as shown (the content between the fences), creating folders as needed. If a file there differs from ${MANIFEST_FILE}'s hash for it, it was edited: ask before replacing it. Then write ${MANIFEST_FILE} with this content. ${afterInstall(`${dest}/SKILL.md`)}
|
|
4957
5074
|
|
|
4958
5075
|
### ${MANIFEST_FILE}
|
|
4959
5076
|
|
|
@@ -5229,7 +5346,7 @@ var init_server = __esm({
|
|
|
5229
5346
|
init_create_prompt_template();
|
|
5230
5347
|
init_skills2();
|
|
5231
5348
|
init_team_setup();
|
|
5232
|
-
SERVER_VERSION = true ? "0.1.
|
|
5349
|
+
SERVER_VERSION = true ? "0.1.52" : "0.0.0-dev";
|
|
5233
5350
|
}
|
|
5234
5351
|
});
|
|
5235
5352
|
|