@coreyuan/vector-mind 1.0.48 → 1.0.49
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 +139 -394
- package/dist/builtin-conventions.js +2 -2
- package/dist/builtin-conventions.js.map +1 -1
- package/dist/builtin-instructions.d.ts +1 -1
- package/dist/builtin-instructions.js +1 -1
- package/dist/builtin-instructions.js.map +1 -1
- package/dist/index.js +430 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
|
|
|
16
16
|
import { BUILTIN_CONVENTIONS } from "./builtin-conventions.js";
|
|
17
17
|
import { BUILTIN_ARCHITECTURE_AND_CODE_ORGANIZATION_INSTRUCTIONS, BUILTIN_DESTRUCTIVE_OPERATION_GUARD_INSTRUCTIONS, BUILTIN_FRONTEND_OUTPUT_PURITY_INSTRUCTIONS, BUILTIN_GIT_COMMIT_SUMMARY_INSTRUCTIONS, BUILTIN_LOW_OVERHEAD_WORKFLOW_INSTRUCTIONS, BUILTIN_PAYLOAD_GUARD_INSTRUCTIONS, BUILTIN_PLAN_LITE_INSTRUCTIONS, BUILTIN_REQUIREMENT_BOUNDARY_AND_MODULARITY_INSTRUCTIONS, BUILTIN_THREAD_HANDOFF_SWITCH_INSTRUCTIONS, BUILTIN_WRITE_POLICY_INSTRUCTIONS, } from "./builtin-instructions.js";
|
|
18
18
|
const SERVER_NAME = "vector-mind";
|
|
19
|
-
const SERVER_VERSION = "1.0.
|
|
19
|
+
const SERVER_VERSION = "1.0.49";
|
|
20
20
|
const rootFromEnv = process.env.VECTORMIND_ROOT?.trim() ?? "";
|
|
21
21
|
const prettyJsonOutput = ["1", "true", "on", "yes"].includes((process.env.VECTORMIND_PRETTY_JSON ?? "").trim().toLowerCase());
|
|
22
22
|
const debugLogEnabled = ["1", "true", "on", "yes"].includes((process.env.VECTORMIND_DEBUG_LOG ?? "").trim().toLowerCase());
|
|
@@ -83,6 +83,16 @@ const DEVELOPMENT_BLOCK_FILE_LINES = (() => {
|
|
|
83
83
|
return Math.max(1200, DEVELOPMENT_WARN_FILE_LINES);
|
|
84
84
|
return Math.min(100_000, n);
|
|
85
85
|
})();
|
|
86
|
+
const DEVELOPMENT_HUGE_FILE_LINES = (() => {
|
|
87
|
+
const raw = process.env.VECTORMIND_HUGE_FILE_LINES?.trim();
|
|
88
|
+
if (!raw)
|
|
89
|
+
return 3000;
|
|
90
|
+
const n = Number.parseInt(raw, 10);
|
|
91
|
+
if (!Number.isFinite(n) || n < DEVELOPMENT_BLOCK_FILE_LINES) {
|
|
92
|
+
return Math.max(3000, DEVELOPMENT_BLOCK_FILE_LINES);
|
|
93
|
+
}
|
|
94
|
+
return Math.min(200_000, n);
|
|
95
|
+
})();
|
|
86
96
|
const DEVELOPMENT_WARN_FILE_BYTES = (() => {
|
|
87
97
|
const raw = process.env.VECTORMIND_WARN_FILE_BYTES?.trim();
|
|
88
98
|
if (!raw)
|
|
@@ -1503,6 +1513,54 @@ function buildCrossProjectPathWarnings(paths) {
|
|
|
1503
1513
|
},
|
|
1504
1514
|
];
|
|
1505
1515
|
}
|
|
1516
|
+
function buildLargeImplementationFileWarning(args) {
|
|
1517
|
+
const linesValue = args.lineCountTruncated ? `${args.lineCount}+` : args.lineCount;
|
|
1518
|
+
if (args.lineCount >= DEVELOPMENT_HUGE_FILE_LINES) {
|
|
1519
|
+
return {
|
|
1520
|
+
code: "huge_file_modularization_required",
|
|
1521
|
+
severity: "blocker",
|
|
1522
|
+
message: "This implementation file is huge. Before any normal feature work, perform mechanical modularization: move whole functions/types/impl blocks into real, clearly named modules/directories, avoid *.generated.* or *.parts files, preserve behavior, then run format/build/tests.",
|
|
1523
|
+
files: [args.filePath],
|
|
1524
|
+
details: {
|
|
1525
|
+
lines: linesValue,
|
|
1526
|
+
bytes: args.bytes,
|
|
1527
|
+
warn_lines: DEVELOPMENT_WARN_FILE_LINES,
|
|
1528
|
+
block_lines: DEVELOPMENT_BLOCK_FILE_LINES,
|
|
1529
|
+
huge_lines: DEVELOPMENT_HUGE_FILE_LINES,
|
|
1530
|
+
required_action: "mechanical_modularization",
|
|
1531
|
+
allowed_change_modes: ["mechanical_modularization", "emergency_hotfix"],
|
|
1532
|
+
forbidden_file_patterns: ["*.generated.*", "*.parts", "*.rs.parts", "*_part*"],
|
|
1533
|
+
mechanical_rules: [
|
|
1534
|
+
"move whole declarations only",
|
|
1535
|
+
"use real module names and clear directory boundaries",
|
|
1536
|
+
"preserve behavior and public semantics",
|
|
1537
|
+
"only add necessary mod/use/pub(crate)/re-export glue",
|
|
1538
|
+
"run formatter, build, and tests after each phase",
|
|
1539
|
+
],
|
|
1540
|
+
reading: !!args.reading,
|
|
1541
|
+
},
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
return {
|
|
1545
|
+
code: args.code,
|
|
1546
|
+
severity: args.code === "large_file" || (args.code === "large_file_read" && args.lineCount < DEVELOPMENT_BLOCK_FILE_LINES)
|
|
1547
|
+
? "warning"
|
|
1548
|
+
: "blocker",
|
|
1549
|
+
message: args.code === "large_file"
|
|
1550
|
+
? "This implementation file is getting large. Prefer extracting focused modules instead of continuing to pile unrelated responsibilities into it."
|
|
1551
|
+
: args.reading
|
|
1552
|
+
? "You are reading a very large implementation file. Do not keep patching new feature code into it; identify a narrow function and split new behavior into focused modules unless this task is explicitly a planned extraction."
|
|
1553
|
+
: "This implementation file is already very large. Do not add new feature code here by default; split into a focused module/service/component and keep this file as a thin entry.",
|
|
1554
|
+
files: [args.filePath],
|
|
1555
|
+
details: {
|
|
1556
|
+
lines: linesValue,
|
|
1557
|
+
bytes: args.bytes,
|
|
1558
|
+
warn_lines: DEVELOPMENT_WARN_FILE_LINES,
|
|
1559
|
+
block_lines: DEVELOPMENT_BLOCK_FILE_LINES,
|
|
1560
|
+
huge_lines: DEVELOPMENT_HUGE_FILE_LINES,
|
|
1561
|
+
},
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1506
1564
|
function buildFileReadDevelopmentWarnings(filePath, absPath, stat) {
|
|
1507
1565
|
const warnings = [];
|
|
1508
1566
|
if (!isPathInsideProjectRoot(absPath)) {
|
|
@@ -1527,21 +1585,14 @@ function buildFileReadDevelopmentWarnings(filePath, absPath, stat) {
|
|
|
1527
1585
|
const warnBytes = st.size >= DEVELOPMENT_WARN_FILE_BYTES;
|
|
1528
1586
|
if (!tooManyLines && !warnLines && !warnBytes)
|
|
1529
1587
|
return warnings;
|
|
1530
|
-
warnings.push({
|
|
1588
|
+
warnings.push(buildLargeImplementationFileWarning({
|
|
1531
1589
|
code: "large_file_read",
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
lines: lineInfo?.truncated ? `${lineCount}+` : lineCount,
|
|
1539
|
-
bytes: st.size,
|
|
1540
|
-
warn_lines: DEVELOPMENT_WARN_FILE_LINES,
|
|
1541
|
-
block_lines: DEVELOPMENT_BLOCK_FILE_LINES,
|
|
1542
|
-
warn_bytes: DEVELOPMENT_WARN_FILE_BYTES,
|
|
1543
|
-
},
|
|
1544
|
-
});
|
|
1590
|
+
filePath,
|
|
1591
|
+
lineCount,
|
|
1592
|
+
lineCountTruncated: lineInfo?.truncated,
|
|
1593
|
+
bytes: st.size,
|
|
1594
|
+
reading: true,
|
|
1595
|
+
}));
|
|
1545
1596
|
return warnings;
|
|
1546
1597
|
}
|
|
1547
1598
|
function buildMatchedFileDevelopmentWarnings(filePaths) {
|
|
@@ -1802,24 +1853,37 @@ function buildDevelopmentWarnings(files, opts = {}) {
|
|
|
1802
1853
|
const warnBytes = stat.size >= DEVELOPMENT_WARN_FILE_BYTES;
|
|
1803
1854
|
if (!tooManyLines && !warnLines && !warnBytes)
|
|
1804
1855
|
continue;
|
|
1805
|
-
warnings.push({
|
|
1856
|
+
warnings.push(buildLargeImplementationFileWarning({
|
|
1806
1857
|
code: tooManyLines ? "very_large_file" : "large_file",
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
details: {
|
|
1813
|
-
lines: lineInfo?.truncated ? `${lineCount}+` : lineCount,
|
|
1814
|
-
bytes: stat.size,
|
|
1815
|
-
warn_lines: DEVELOPMENT_WARN_FILE_LINES,
|
|
1816
|
-
block_lines: DEVELOPMENT_BLOCK_FILE_LINES,
|
|
1817
|
-
warn_bytes: DEVELOPMENT_WARN_FILE_BYTES,
|
|
1818
|
-
},
|
|
1819
|
-
});
|
|
1858
|
+
filePath: relPath,
|
|
1859
|
+
lineCount,
|
|
1860
|
+
lineCountTruncated: lineInfo?.truncated,
|
|
1861
|
+
bytes: stat.size,
|
|
1862
|
+
}));
|
|
1820
1863
|
}
|
|
1821
1864
|
return warnings;
|
|
1822
1865
|
}
|
|
1866
|
+
function isLargeFileWarningCode(code) {
|
|
1867
|
+
return (code === "large_file" ||
|
|
1868
|
+
code === "very_large_file" ||
|
|
1869
|
+
code === "large_file_read" ||
|
|
1870
|
+
code === "huge_file_modularization_required");
|
|
1871
|
+
}
|
|
1872
|
+
function isDevelopmentWarningBlockingForChangeMode(warning, changeMode) {
|
|
1873
|
+
if (changeMode === "mechanical_modularization") {
|
|
1874
|
+
if (isLargeFileWarningCode(warning.code))
|
|
1875
|
+
return false;
|
|
1876
|
+
if (warning.code === "scope_contract_missing")
|
|
1877
|
+
return false;
|
|
1878
|
+
}
|
|
1879
|
+
if (changeMode === "emergency_hotfix") {
|
|
1880
|
+
if (isLargeFileWarningCode(warning.code))
|
|
1881
|
+
return false;
|
|
1882
|
+
if (warning.code === "scope_contract_missing")
|
|
1883
|
+
return false;
|
|
1884
|
+
}
|
|
1885
|
+
return warning.severity === "blocker" || warning.severity === "warning";
|
|
1886
|
+
}
|
|
1823
1887
|
function compactDevelopmentWarningsText(warnings) {
|
|
1824
1888
|
if (!warnings.length)
|
|
1825
1889
|
return [];
|
|
@@ -1987,6 +2051,142 @@ function extractCLikeSymbols(content) {
|
|
|
1987
2051
|
}
|
|
1988
2052
|
return symbols;
|
|
1989
2053
|
}
|
|
2054
|
+
function declarationRegexForExtension(ext) {
|
|
2055
|
+
switch (ext) {
|
|
2056
|
+
case ".rs":
|
|
2057
|
+
return /^\s*(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?(?:fn|struct|enum|trait|impl|mod|type|const|static)\s+([A-Za-z_][\w]*)?/;
|
|
2058
|
+
case ".go":
|
|
2059
|
+
return /^\s*(?:func|type|const|var)\s+(?:\([^)]*\)\s*)?([A-Za-z_][\w]*)?/;
|
|
2060
|
+
case ".py":
|
|
2061
|
+
return /^(?:class|async\s+def|def)\s+([A-Za-z_][\w]*)/;
|
|
2062
|
+
case ".ts":
|
|
2063
|
+
case ".tsx":
|
|
2064
|
+
case ".js":
|
|
2065
|
+
case ".jsx":
|
|
2066
|
+
case ".mjs":
|
|
2067
|
+
case ".cjs":
|
|
2068
|
+
return /^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?(?:function|class|interface|type|enum|const|let|var)\s+([A-Za-z_$][\w$]*)?/;
|
|
2069
|
+
default:
|
|
2070
|
+
return /^\s*(?:pub\s+)?(?:async\s+)?(?:fn|function|class|struct|enum|interface|type|const|static)\s+([A-Za-z_][\w]*)?/;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
function moduleNameFromDeclaration(name, signature) {
|
|
2074
|
+
const text = `${name} ${signature}`.toLowerCase();
|
|
2075
|
+
const rules = [
|
|
2076
|
+
[/(config|setting|env)/, "config"],
|
|
2077
|
+
[/(state|store|persist)/, "state"],
|
|
2078
|
+
[/(api|client|request|response|heartbeat|activate|sync)/, "api"],
|
|
2079
|
+
[/(service|daemon|install|start|stop)/, "service"],
|
|
2080
|
+
[/(log|logger|redact)/, "logging"],
|
|
2081
|
+
[/(gui|window|dialog|form|view|button|list)/, "ui"],
|
|
2082
|
+
[/(share|disk|smb|unc|folder|directory)/, "share"],
|
|
2083
|
+
[/(repair|cleanup|probe|health)/, "maintenance"],
|
|
2084
|
+
[/(path|sanitize|normalize|host|ip|util|helper)/, "util"],
|
|
2085
|
+
[/(test|mock|fixture)/, "tests"],
|
|
2086
|
+
];
|
|
2087
|
+
for (const [pattern, moduleName] of rules) {
|
|
2088
|
+
if (pattern.test(text))
|
|
2089
|
+
return moduleName;
|
|
2090
|
+
}
|
|
2091
|
+
return "core";
|
|
2092
|
+
}
|
|
2093
|
+
function topLevelDeclarationsForPlan(content, ext, maxDecls = 160) {
|
|
2094
|
+
const decls = [];
|
|
2095
|
+
const lines = content.split(/\r?\n/);
|
|
2096
|
+
const regex = declarationRegexForExtension(ext);
|
|
2097
|
+
for (let i = 0; i < lines.length && decls.length < maxDecls; i++) {
|
|
2098
|
+
const raw = lines[i];
|
|
2099
|
+
const trimmed = raw.trim();
|
|
2100
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("#"))
|
|
2101
|
+
continue;
|
|
2102
|
+
const m = raw.match(regex);
|
|
2103
|
+
if (!m)
|
|
2104
|
+
continue;
|
|
2105
|
+
const fallback = trimmed.split(/\s+/).slice(0, 3).join("_").replace(/[^\w$]+/g, "_");
|
|
2106
|
+
const name = (m[1] || fallback || `declaration_${i + 1}`).replace(/^[^A-Za-z_]+/, "") || `declaration_${i + 1}`;
|
|
2107
|
+
const kind = trimmed.split(/\s+/).find((part) => ["fn", "function", "class", "struct", "enum", "trait", "impl", "mod", "type", "const", "static", "interface"].includes(part.replace(/[({].*$/, ""))) ?? "declaration";
|
|
2108
|
+
decls.push({
|
|
2109
|
+
line: i + 1,
|
|
2110
|
+
kind,
|
|
2111
|
+
name,
|
|
2112
|
+
signature: oneLine(trimmed, 180),
|
|
2113
|
+
suggested_module: moduleNameFromDeclaration(name, trimmed),
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
return decls;
|
|
2117
|
+
}
|
|
2118
|
+
function targetPathForModule(originalFilePath, targetDir, moduleName) {
|
|
2119
|
+
const ext = path.extname(originalFilePath) || ".txt";
|
|
2120
|
+
const normalizedTargetDir = targetDir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
2121
|
+
if (!normalizedTargetDir || normalizedTargetDir === ".")
|
|
2122
|
+
return `${moduleName}${ext}`;
|
|
2123
|
+
return `${normalizedTargetDir}/${moduleName}${ext}`;
|
|
2124
|
+
}
|
|
2125
|
+
function buildLargeFileSplitPlan(args) {
|
|
2126
|
+
const content = fs.readFileSync(args.absPath, "utf8");
|
|
2127
|
+
const st = fs.statSync(args.absPath);
|
|
2128
|
+
const lines = content.split(/\r?\n/).length;
|
|
2129
|
+
const ext = path.extname(args.absPath).toLowerCase();
|
|
2130
|
+
const baseName = path.basename(args.filePath, path.extname(args.filePath));
|
|
2131
|
+
const parentDir = path.dirname(args.filePath).replace(/\\/g, "/");
|
|
2132
|
+
const defaultTargetDir = parentDir === "." ? baseName : `${parentDir}/${baseName}`;
|
|
2133
|
+
const targetDir = normalizeToDbPath(args.targetDir ?? defaultTargetDir);
|
|
2134
|
+
const declarations = topLevelDeclarationsForPlan(content, ext);
|
|
2135
|
+
const grouped = new Map();
|
|
2136
|
+
for (const decl of declarations) {
|
|
2137
|
+
const key = grouped.size >= args.maxModules && !grouped.has(decl.suggested_module) ? "core" : decl.suggested_module;
|
|
2138
|
+
const list = grouped.get(key) ?? [];
|
|
2139
|
+
list.push(decl);
|
|
2140
|
+
grouped.set(key, list);
|
|
2141
|
+
}
|
|
2142
|
+
if (!grouped.size)
|
|
2143
|
+
grouped.set("core", []);
|
|
2144
|
+
const modules = Array.from(grouped.entries())
|
|
2145
|
+
.slice(0, args.maxModules)
|
|
2146
|
+
.map(([moduleName, decls]) => ({
|
|
2147
|
+
module: moduleName,
|
|
2148
|
+
target_path: targetPathForModule(args.filePath, targetDir, moduleName),
|
|
2149
|
+
declarations: decls.slice(0, 24).map((d) => `${d.kind} ${d.name} @L${d.line}`),
|
|
2150
|
+
reason: `Move whole ${moduleName}-related declarations together without changing behavior.`,
|
|
2151
|
+
}));
|
|
2152
|
+
return {
|
|
2153
|
+
ok: true,
|
|
2154
|
+
file_path: args.filePath,
|
|
2155
|
+
line_count: lines,
|
|
2156
|
+
bytes: st.size,
|
|
2157
|
+
huge_threshold_lines: DEVELOPMENT_HUGE_FILE_LINES,
|
|
2158
|
+
required_action: "mechanical_modularization",
|
|
2159
|
+
intent: args.intent,
|
|
2160
|
+
target_dir: targetDir,
|
|
2161
|
+
forbidden_patterns: ["*.generated.*", "*.parts", "*.rs.parts", "*_part*", "*Part*"],
|
|
2162
|
+
mechanical_rules: [
|
|
2163
|
+
"Move only complete declarations/impl blocks/functions/classes/types; do not split a declaration body.",
|
|
2164
|
+
"Use real module names and clear directory boundaries; do not create generated/parts/partN files.",
|
|
2165
|
+
"Preserve behavior, names, API semantics, data formats, side effects, and test expectations.",
|
|
2166
|
+
"Only add necessary module declarations, imports, pub(crate), and re-exports to make moved code compile.",
|
|
2167
|
+
"Run formatter, build/check, and relevant tests after each small phase.",
|
|
2168
|
+
],
|
|
2169
|
+
modules,
|
|
2170
|
+
steps: [
|
|
2171
|
+
"Create a dedicated mechanical modularization requirement before touching the huge file.",
|
|
2172
|
+
"Add the target module directory and move one cohesive declaration group at a time.",
|
|
2173
|
+
"Keep the original file as a thin entry/mod orchestration file where possible.",
|
|
2174
|
+
"After each group, run formatter and the smallest available compile/test command.",
|
|
2175
|
+
"Record the split with record_large_file_split(status='partial' or 'resolved').",
|
|
2176
|
+
"Resume the original feature only after the target huge file is no longer the default place for new code.",
|
|
2177
|
+
],
|
|
2178
|
+
validation: [
|
|
2179
|
+
"No *.generated.*, *.parts, *.rs.parts, or numbered part files were created.",
|
|
2180
|
+
"The original file line count decreased or contains only thin orchestration glue.",
|
|
2181
|
+
"Formatter passes.",
|
|
2182
|
+
"Build/check passes.",
|
|
2183
|
+
"Relevant tests pass.",
|
|
2184
|
+
],
|
|
2185
|
+
notes: declarations.length
|
|
2186
|
+
? [`Detected ${declarations.length} top-level declarations for mechanical grouping.`]
|
|
2187
|
+
: ["No top-level declarations were detected by lightweight scanning; split by obvious cohesive sections and verify after each move."],
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
1990
2190
|
function chunkTextByLines(content, opts) {
|
|
1991
2191
|
const lines = content.split(/\r?\n/);
|
|
1992
2192
|
if (lines.length === 0)
|
|
@@ -2204,11 +2404,28 @@ const PreflightChangeScopeArgsSchema = ProjectRootArgSchema.merge(OutputFormatSc
|
|
|
2204
2404
|
intent: z.string().optional().default(""),
|
|
2205
2405
|
files: z.array(z.string().min(1)).optional(),
|
|
2206
2406
|
planned_files: z.array(z.string().min(1)).optional(),
|
|
2407
|
+
change_mode: z
|
|
2408
|
+
.enum(["feature", "bugfix", "refactor", "mechanical_modularization", "emergency_hotfix"])
|
|
2409
|
+
.optional()
|
|
2410
|
+
.default("feature"),
|
|
2207
2411
|
scope_allow: z.array(z.string().min(1)).optional(),
|
|
2208
2412
|
scope_deny: z.array(z.string().min(1)).optional(),
|
|
2209
2413
|
allowed_paths: z.array(z.string().min(1)).optional(),
|
|
2210
2414
|
denied_paths: z.array(z.string().min(1)).optional(),
|
|
2211
2415
|
}));
|
|
2416
|
+
const PlanLargeFileSplitArgsSchema = ProjectRootArgSchema.merge(OutputFormatSchema).merge(z.object({
|
|
2417
|
+
file: z.string().min(1),
|
|
2418
|
+
intent: z.string().optional().default("mechanical modularization"),
|
|
2419
|
+
target_dir: z.string().optional(),
|
|
2420
|
+
max_modules: z.number().int().min(2).max(30).optional().default(12),
|
|
2421
|
+
}));
|
|
2422
|
+
const RecordLargeFileSplitArgsSchema = ProjectRootArgSchema.merge(z.object({
|
|
2423
|
+
file: z.string().min(1),
|
|
2424
|
+
status: z.enum(["planned", "in_progress", "partial", "resolved"]),
|
|
2425
|
+
summary: z.string().min(1),
|
|
2426
|
+
modules: z.array(z.string().min(1)).optional(),
|
|
2427
|
+
remaining_lines: z.number().int().min(0).optional(),
|
|
2428
|
+
}));
|
|
2212
2429
|
const QueryCodebaseArgsSchema = ProjectRootArgSchema.merge(OutputFormatSchema).merge(z.object({
|
|
2213
2430
|
query: z.string().min(1),
|
|
2214
2431
|
}));
|
|
@@ -2617,9 +2834,13 @@ function compactMaintenanceText(data) {
|
|
|
2617
2834
|
function compactPreflightChangeScopeText(data) {
|
|
2618
2835
|
const req = data.active_requirement ? `#${data.active_requirement.id} ${data.active_requirement.title}` : "none";
|
|
2619
2836
|
const lines = [
|
|
2620
|
-
`preflight_change_scope ok=${data.ok} safe_to_edit=${data.safe_to_edit} requirement=${req} files=${data.files.length} intent="${oneLine(data.intent, 120)}"`,
|
|
2837
|
+
`preflight_change_scope ok=${data.ok} safe_to_edit=${data.safe_to_edit} mode=${data.change_mode} requirement=${req} files=${data.files.length} intent="${oneLine(data.intent, 120)}"`,
|
|
2621
2838
|
`action: ${oneLine(data.recommended_action, 180)}`,
|
|
2622
2839
|
];
|
|
2840
|
+
if (data.required_action)
|
|
2841
|
+
lines.push(`required_action=${data.required_action}`);
|
|
2842
|
+
if (data.allowed_change_modes?.length)
|
|
2843
|
+
lines.push(`allowed_change_modes=${data.allowed_change_modes.join(",")}`);
|
|
2623
2844
|
if (data.scope_contract) {
|
|
2624
2845
|
lines.push(`scope allow_terms=${data.scope_contract.allow_terms.length} deny_terms=${data.scope_contract.deny_terms.length} allowed_paths=${data.scope_contract.allowed_paths.length} denied_paths=${data.scope_contract.denied_paths.length}`);
|
|
2625
2846
|
}
|
|
@@ -2628,6 +2849,26 @@ function compactPreflightChangeScopeText(data) {
|
|
|
2628
2849
|
lines.push("- no development warnings");
|
|
2629
2850
|
return lines.join("\n");
|
|
2630
2851
|
}
|
|
2852
|
+
function compactLargeFileSplitPlanText(data) {
|
|
2853
|
+
const lines = [
|
|
2854
|
+
`large_file_split ok=${data.ok} file=${data.file_path} lines=${data.line_count} threshold=${data.huge_threshold_lines} action=${data.required_action}`,
|
|
2855
|
+
`target_dir=${data.target_dir}`,
|
|
2856
|
+
`forbidden=${data.forbidden_patterns.join(",")}`,
|
|
2857
|
+
];
|
|
2858
|
+
lines.push("modules:");
|
|
2859
|
+
for (const m of data.modules.slice(0, 20)) {
|
|
2860
|
+
const decls = m.declarations.length ? ` decls=${m.declarations.slice(0, 8).join("; ")}` : " decls=(manual sections)";
|
|
2861
|
+
lines.push(`- ${m.module} -> ${m.target_path}${decls}`);
|
|
2862
|
+
}
|
|
2863
|
+
lines.push("steps:");
|
|
2864
|
+
for (const step of data.steps.slice(0, 8))
|
|
2865
|
+
lines.push(`- ${step}`);
|
|
2866
|
+
lines.push("validation:");
|
|
2867
|
+
for (const v of data.validation.slice(0, 8))
|
|
2868
|
+
lines.push(`- ${v}`);
|
|
2869
|
+
lines.push("hint: use format=json for full declarations/rules");
|
|
2870
|
+
return lines.join("\n");
|
|
2871
|
+
}
|
|
2631
2872
|
function compactBootstrapText(data) {
|
|
2632
2873
|
const lines = [];
|
|
2633
2874
|
lines.push(`ok ctx ${data.root_source} watcher=${data.watcher_enabled ? (data.watcher_ready ? "ready" : "starting") : "off"} root=${data.project_root}`);
|
|
@@ -4800,6 +5041,7 @@ function buildServerInstructions() {
|
|
|
4800
5041
|
"- BEFORE editing once target files/modules are known: call preflight_change_scope(intent, files/planned_files, optional scope_allow/scope_deny/allowed_paths/denied_paths). If ok=false/safe_to_edit=false or it returns development_warnings, stop before editing and narrow the plan unless the user explicitly expands the requirement.",
|
|
4801
5042
|
"- Treat the active requirement as the only change boundary. Do not add extra business behavior, new flows, new fields, new interfaces, or touch completed/related features unless the user explicitly asked or the change is strictly necessary.",
|
|
4802
5043
|
"- Do not keep piling new feature code into a large single file. Prefer small modules/services/components; if an implementation file is already large, split it before adding more responsibilities.",
|
|
5044
|
+
"- If preflight_change_scope/read_file_lines/grep/query_codebase/get_pending_changes/sync_change_intent returns huge_file_modularization_required, do not continue normal feature work. Call plan_large_file_split, perform mechanical modularization with real module names/directories, never create generated/parts/partN files, then call record_large_file_split before resuming normal feature work. Only use preflight_change_scope(change_mode='mechanical_modularization') for the split itself; use change_mode='emergency_hotfix' only for the smallest urgent fix and still record why the split was deferred.",
|
|
4803
5045
|
"- AFTER editing + saving: call get_pending_changes() to see unsynced files, then call sync_change_intent(intent, files). (You can omit files to auto-link all pending changes.)",
|
|
4804
5046
|
"- If preflight_change_scope, read_file_lines, grep, query_codebase, get_pending_changes, or sync_change_intent returns development_warnings, address those warnings before continuing or explain why the current requirement truly needs that scope.",
|
|
4805
5047
|
"- After major milestones/decisions: call upsert_project_summary(summary) and/or add_note(...) to persist durable context locally.",
|
|
@@ -5470,9 +5712,19 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
5470
5712
|
},
|
|
5471
5713
|
{
|
|
5472
5714
|
name: "preflight_change_scope",
|
|
5473
|
-
description: "MUST call BEFORE editing once you know the intended files/modules. Checks planned files against the active requirement and optional generic scope_allow/scope_deny/allowed_paths/denied_paths. If ok=false/safe_to_edit=false, stop before editing and narrow the plan or scope contract.",
|
|
5715
|
+
description: "MUST call BEFORE editing once you know the intended files/modules. Checks planned files against the active requirement and optional generic scope_allow/scope_deny/allowed_paths/denied_paths. If ok=false/safe_to_edit=false, stop before editing and narrow the plan or scope contract. For huge files, use change_mode='mechanical_modularization' only when the task is to split the file.",
|
|
5474
5716
|
inputSchema: toJsonSchemaCompat(PreflightChangeScopeArgsSchema),
|
|
5475
5717
|
},
|
|
5718
|
+
{
|
|
5719
|
+
name: "plan_large_file_split",
|
|
5720
|
+
description: "Plan a mechanical modularization split for a huge implementation file. Produces real module names/directories and explicitly forbids generated/parts/partN files. Use this before normal feature work when preflight_change_scope returns huge_file_modularization_required.",
|
|
5721
|
+
inputSchema: toJsonSchemaCompat(PlanLargeFileSplitArgsSchema),
|
|
5722
|
+
},
|
|
5723
|
+
{
|
|
5724
|
+
name: "record_large_file_split",
|
|
5725
|
+
description: "Record the planned/in-progress/partial/resolved status of a huge-file mechanical modularization split so future sessions know the file is being decomposed and where modules moved.",
|
|
5726
|
+
inputSchema: toJsonSchemaCompat(RecordLargeFileSplitArgsSchema),
|
|
5727
|
+
},
|
|
5476
5728
|
{
|
|
5477
5729
|
name: "get_brain_dump",
|
|
5478
5730
|
description: "Restore recent requirements/changes/notes/summary/pending changes. Prefer bootstrap_context() at session start when you also want recall from the local memory store.",
|
|
@@ -5812,6 +6064,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
5812
6064
|
}
|
|
5813
6065
|
if (toolName === "preflight_change_scope") {
|
|
5814
6066
|
const args = PreflightChangeScopeArgsSchema.parse(rawArgs);
|
|
6067
|
+
const changeMode = args.change_mode;
|
|
5815
6068
|
flushPendingChangeBuffer();
|
|
5816
6069
|
const files = (args.files ?? args.planned_files ?? []).filter((f) => typeof f === "string" && f.length > 0);
|
|
5817
6070
|
const active = getActiveRequirementStmt.get();
|
|
@@ -5838,22 +6091,41 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
5838
6091
|
logActivity("preflight_change_scope", {
|
|
5839
6092
|
req_id: active?.id ?? null,
|
|
5840
6093
|
intent_preview: makePreviewText(args.intent, 200),
|
|
6094
|
+
change_mode: changeMode,
|
|
5841
6095
|
files: files.slice(0, 25),
|
|
5842
6096
|
files_total: files.length,
|
|
5843
6097
|
development_warnings: development_warnings.length,
|
|
5844
6098
|
});
|
|
5845
6099
|
const hasTargetFiles = fileInputs.length > 0;
|
|
5846
|
-
const
|
|
6100
|
+
const hugeWarnings = development_warnings.filter((w) => w.code === "huge_file_modularization_required");
|
|
6101
|
+
const hasHugeFile = hugeWarnings.length > 0;
|
|
6102
|
+
const blockingWarnings = development_warnings.filter((w) => isDevelopmentWarningBlockingForChangeMode(w, changeMode));
|
|
6103
|
+
const hasBlockingWarnings = blockingWarnings.length > 0;
|
|
5847
6104
|
const safeToEdit = hasTargetFiles && !hasBlockingWarnings;
|
|
5848
6105
|
const recommendedAction = !hasTargetFiles
|
|
5849
6106
|
? "Identify the intended target files/modules and rerun preflight_change_scope before editing."
|
|
5850
|
-
:
|
|
5851
|
-
? "
|
|
5852
|
-
:
|
|
6107
|
+
: hasHugeFile && changeMode === "mechanical_modularization" && safeToEdit
|
|
6108
|
+
? "Proceed only with mechanical modularization: call plan_large_file_split, move whole declarations into real named modules/directories, avoid generated/parts files, validate, then record_large_file_split."
|
|
6109
|
+
: hasHugeFile && changeMode === "emergency_hotfix" && safeToEdit
|
|
6110
|
+
? "Proceed only with the smallest urgent fix, do not add new responsibilities, record why mechanical modularization was deferred, and plan/record the split next."
|
|
6111
|
+
: hasHugeFile
|
|
6112
|
+
? "Stop normal feature work. Call plan_large_file_split and perform mechanical modularization first, or rerun preflight_change_scope with change_mode='mechanical_modularization' for the split itself."
|
|
6113
|
+
: hasBlockingWarnings
|
|
6114
|
+
? "Stop before editing. Narrow the planned files or explicitly expand the current requirement/scope contract."
|
|
6115
|
+
: "Planned files are within the current generic scope checks.";
|
|
6116
|
+
const requiredAction = hasHugeFile && changeMode !== "mechanical_modularization"
|
|
6117
|
+
? "mechanical_modularization"
|
|
6118
|
+
: undefined;
|
|
6119
|
+
const allowedChangeModes = hasHugeFile
|
|
6120
|
+
? ["mechanical_modularization", "emergency_hotfix"]
|
|
6121
|
+
: undefined;
|
|
5853
6122
|
const outputValue = {
|
|
5854
6123
|
ok: safeToEdit,
|
|
5855
6124
|
safe_to_edit: safeToEdit,
|
|
6125
|
+
change_mode: changeMode,
|
|
5856
6126
|
recommended_action: recommendedAction,
|
|
6127
|
+
required_action: requiredAction,
|
|
6128
|
+
allowed_change_modes: allowedChangeModes,
|
|
5857
6129
|
active_requirement: active ? { id: active.id, title: active.title } : null,
|
|
5858
6130
|
intent: args.intent,
|
|
5859
6131
|
files: files.map(normalizeToDbPath),
|
|
@@ -5869,6 +6141,129 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
5869
6141
|
],
|
|
5870
6142
|
};
|
|
5871
6143
|
}
|
|
6144
|
+
if (toolName === "plan_large_file_split") {
|
|
6145
|
+
const args = PlanLargeFileSplitArgsSchema.parse(rawArgs);
|
|
6146
|
+
flushPendingChangeBuffer();
|
|
6147
|
+
const resolved = resolveReadPathUnderProjectRoot(args.file);
|
|
6148
|
+
let stat;
|
|
6149
|
+
try {
|
|
6150
|
+
stat = fs.statSync(resolved.absPath);
|
|
6151
|
+
}
|
|
6152
|
+
catch (err) {
|
|
6153
|
+
return {
|
|
6154
|
+
isError: true,
|
|
6155
|
+
content: [{ type: "text", text: toolJson({ ok: false, error: `File not found: ${String(err)}` }) }],
|
|
6156
|
+
};
|
|
6157
|
+
}
|
|
6158
|
+
if (!stat.isFile()) {
|
|
6159
|
+
return { isError: true, content: [{ type: "text", text: toolJson({ ok: false, error: "Not a file" }) }] };
|
|
6160
|
+
}
|
|
6161
|
+
if (!isLikelySourceImplementationFile(resolved.dbFilePath)) {
|
|
6162
|
+
return {
|
|
6163
|
+
isError: true,
|
|
6164
|
+
content: [
|
|
6165
|
+
{
|
|
6166
|
+
type: "text",
|
|
6167
|
+
text: toolJson({
|
|
6168
|
+
ok: false,
|
|
6169
|
+
error: "Not a recognized source implementation file",
|
|
6170
|
+
file_path: resolved.dbFilePath,
|
|
6171
|
+
}),
|
|
6172
|
+
},
|
|
6173
|
+
],
|
|
6174
|
+
};
|
|
6175
|
+
}
|
|
6176
|
+
const lineInfo = countFileLinesBounded(resolved.absPath, 8_000_000);
|
|
6177
|
+
const lineCount = lineInfo?.lines ?? 0;
|
|
6178
|
+
if (lineCount < DEVELOPMENT_HUGE_FILE_LINES) {
|
|
6179
|
+
return {
|
|
6180
|
+
content: [
|
|
6181
|
+
{
|
|
6182
|
+
type: "text",
|
|
6183
|
+
text: toolJson({
|
|
6184
|
+
ok: false,
|
|
6185
|
+
file_path: resolved.dbFilePath,
|
|
6186
|
+
line_count: lineInfo?.truncated ? `${lineCount}+` : lineCount,
|
|
6187
|
+
huge_threshold_lines: DEVELOPMENT_HUGE_FILE_LINES,
|
|
6188
|
+
recommended_action: "This file is not above the huge-file threshold. Use normal focused modularity rules unless the user explicitly asked for refactoring.",
|
|
6189
|
+
}),
|
|
6190
|
+
},
|
|
6191
|
+
],
|
|
6192
|
+
};
|
|
6193
|
+
}
|
|
6194
|
+
let targetDir = args.target_dir;
|
|
6195
|
+
if (targetDir) {
|
|
6196
|
+
targetDir = resolveProjectPathUnderRoot(targetDir, { allowRoot: true }).dbFilePath;
|
|
6197
|
+
}
|
|
6198
|
+
const plan = buildLargeFileSplitPlan({
|
|
6199
|
+
filePath: resolved.dbFilePath,
|
|
6200
|
+
absPath: resolved.absPath,
|
|
6201
|
+
intent: args.intent,
|
|
6202
|
+
targetDir,
|
|
6203
|
+
maxModules: args.max_modules,
|
|
6204
|
+
});
|
|
6205
|
+
logActivity("plan_large_file_split", {
|
|
6206
|
+
file_path: plan.file_path,
|
|
6207
|
+
line_count: plan.line_count,
|
|
6208
|
+
target_dir: plan.target_dir,
|
|
6209
|
+
modules: plan.modules.map((m) => m.module),
|
|
6210
|
+
});
|
|
6211
|
+
return {
|
|
6212
|
+
content: [
|
|
6213
|
+
{
|
|
6214
|
+
type: "text",
|
|
6215
|
+
text: toolCompactOrJson("plan_large_file_split", plan, compactLargeFileSplitPlanText(plan), args.format),
|
|
6216
|
+
},
|
|
6217
|
+
],
|
|
6218
|
+
};
|
|
6219
|
+
}
|
|
6220
|
+
if (toolName === "record_large_file_split") {
|
|
6221
|
+
const args = RecordLargeFileSplitArgsSchema.parse(rawArgs);
|
|
6222
|
+
flushPendingChangeBuffer();
|
|
6223
|
+
const normalizedFile = normalizeToDbPath(args.file);
|
|
6224
|
+
const active = getActiveRequirementStmt.get();
|
|
6225
|
+
const modules = (args.modules ?? []).map(normalizeToDbPath);
|
|
6226
|
+
const content = [
|
|
6227
|
+
`Huge-file mechanical modularization ${args.status}: ${normalizedFile}`,
|
|
6228
|
+
"",
|
|
6229
|
+
args.summary,
|
|
6230
|
+
modules.length ? `\nModules:\n${modules.map((m) => `- ${m}`).join("\n")}` : "",
|
|
6231
|
+
args.remaining_lines != null ? `\nRemaining lines: ${args.remaining_lines}` : "",
|
|
6232
|
+
].filter(Boolean).join("\n");
|
|
6233
|
+
const meta = {
|
|
6234
|
+
tags: ["large-file-split", "mechanical-modularization"],
|
|
6235
|
+
file: normalizedFile,
|
|
6236
|
+
status: args.status,
|
|
6237
|
+
modules,
|
|
6238
|
+
remaining_lines: args.remaining_lines ?? null,
|
|
6239
|
+
active_requirement_id: active?.id ?? null,
|
|
6240
|
+
};
|
|
6241
|
+
const info = insertMemoryItemStmt.run("note", `large-file-split:${normalizedFile}:${args.status}`, content, normalizedFile, null, null, active?.id ?? null, safeJson(meta), sha256Hex(content));
|
|
6242
|
+
const id = Number(info.lastInsertRowid);
|
|
6243
|
+
enqueueEmbedding(id);
|
|
6244
|
+
logActivity("record_large_file_split", {
|
|
6245
|
+
memory_item_id: id,
|
|
6246
|
+
file_path: normalizedFile,
|
|
6247
|
+
status: args.status,
|
|
6248
|
+
modules: modules.slice(0, 20),
|
|
6249
|
+
remaining_lines: args.remaining_lines ?? null,
|
|
6250
|
+
});
|
|
6251
|
+
return {
|
|
6252
|
+
content: [
|
|
6253
|
+
{
|
|
6254
|
+
type: "text",
|
|
6255
|
+
text: toolJson({
|
|
6256
|
+
ok: true,
|
|
6257
|
+
note: { id },
|
|
6258
|
+
file_path: normalizedFile,
|
|
6259
|
+
status: args.status,
|
|
6260
|
+
modules,
|
|
6261
|
+
remaining_lines: args.remaining_lines ?? null,
|
|
6262
|
+
}),
|
|
6263
|
+
},
|
|
6264
|
+
],
|
|
6265
|
+
};
|
|
6266
|
+
}
|
|
5872
6267
|
if (toolName === "sync_change_intent") {
|
|
5873
6268
|
const args = SyncChangeIntentArgsSchema.parse(rawArgs);
|
|
5874
6269
|
flushPendingChangeBuffer();
|