@dotdotgod/hermes 0.6.5 → 0.6.6
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 +8 -5
- package/__init__.py +5 -9
- package/mcp/cli.mjs +291 -200
- package/mcp/parsers/TYPESCRIPT_LICENSE +21 -0
- package/mcp/parsers/tree-sitter-tsx.wasm +0 -0
- package/mcp/parsers/tree-sitter-typescript.wasm +0 -0
- package/mcp/server.mjs +245 -86
- package/mcp/tools.json +110 -24
- package/package.json +2 -2
- package/plugin.yaml +33 -1
- package/policy.py +1 -1
- package/runtime.py +11 -11
- package/skills/document-clarify/SKILL.md +1 -1
- package/skills/impact-review/SKILL.md +1 -1
- package/skills/project-initializer/SKILL.md +1 -1
- package/skills/project-initializer/references/agent-docs.md +1 -0
- package/skills/project-load/SKILL.md +1 -1
package/mcp/cli.mjs
CHANGED
|
@@ -538,7 +538,7 @@ var init_languages = __esm({
|
|
|
538
538
|
".zsh",
|
|
539
539
|
".fish"
|
|
540
540
|
]);
|
|
541
|
-
EXTRACTOR_ID = "outline-
|
|
541
|
+
EXTRACTOR_ID = "outline-v4:vscode-0.3.1:typescript-import-types-1";
|
|
542
542
|
DECLARATIONS = {
|
|
543
543
|
function_declaration: "function",
|
|
544
544
|
generator_function_declaration: "function",
|
|
@@ -576,19 +576,24 @@ var init_languages = __esm({
|
|
|
576
576
|
|
|
577
577
|
// packages/cli/src/symbols/runtime.mjs
|
|
578
578
|
import { createRequire } from "node:module";
|
|
579
|
-
import { existsSync as
|
|
580
|
-
import { dirname as
|
|
579
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
|
|
580
|
+
import { dirname as dirname6, join as join9 } from "node:path";
|
|
581
581
|
import { fileURLToPath } from "node:url";
|
|
582
582
|
import { createHash as createHash4 } from "node:crypto";
|
|
583
|
+
function languageAsset(language) {
|
|
584
|
+
const patched = fileURLToPath(new URL(`./parsers/tree-sitter-${language}.wasm`, import.meta.url));
|
|
585
|
+
return existsSync10(patched) ? patched : join9(dirname6(parserEntry()), `tree-sitter-${language}.wasm`);
|
|
586
|
+
}
|
|
583
587
|
function parserEntry() {
|
|
584
588
|
const bundled = fileURLToPath(new URL("./parsers/tree-sitter.cjs", import.meta.url));
|
|
585
|
-
return
|
|
589
|
+
return existsSync10(bundled) ? bundled : require2.resolve("@vscode/tree-sitter-wasm");
|
|
586
590
|
}
|
|
587
591
|
function parserIdentity() {
|
|
588
592
|
if (!identity) {
|
|
589
593
|
const entry = parserEntry();
|
|
590
|
-
const hash2 = createHash4("sha256").update(
|
|
591
|
-
|
|
594
|
+
const hash2 = createHash4("sha256").update(readFileSync10(entry));
|
|
595
|
+
hash2.update(readFileSync10(join9(dirname6(entry), "tree-sitter.wasm")));
|
|
596
|
+
for (const language of Object.keys(LANGUAGE_EXTENSIONS)) hash2.update(language).update(readFileSync10(languageAsset(language)));
|
|
592
597
|
identity = hash2.digest("hex");
|
|
593
598
|
}
|
|
594
599
|
return identity;
|
|
@@ -609,17 +614,17 @@ var parser_exports = {};
|
|
|
609
614
|
__export(parser_exports, {
|
|
610
615
|
extractSymbols: () => extractSymbols
|
|
611
616
|
});
|
|
612
|
-
import { dirname as
|
|
617
|
+
import { dirname as dirname7 } from "node:path";
|
|
613
618
|
import { createHash as createHash5 } from "node:crypto";
|
|
614
619
|
async function parserFor(language) {
|
|
615
620
|
if (!runtime) runtime = (async () => {
|
|
616
621
|
const entry = parserEntry();
|
|
617
622
|
const api2 = loadParserApi();
|
|
618
623
|
await api2.Parser.init();
|
|
619
|
-
return { api: api2, directory:
|
|
624
|
+
return { api: api2, directory: dirname7(entry) };
|
|
620
625
|
})();
|
|
621
626
|
const { api, directory } = await runtime;
|
|
622
|
-
if (!languages.has(language)) languages.set(language, api.Language.load(
|
|
627
|
+
if (!languages.has(language)) languages.set(language, api.Language.load(languageAsset(language)));
|
|
623
628
|
const loaded = await languages.get(language);
|
|
624
629
|
const parser = new api.Parser();
|
|
625
630
|
parser.setLanguage(loaded);
|
|
@@ -756,8 +761,8 @@ var init_parser = __esm({
|
|
|
756
761
|
});
|
|
757
762
|
|
|
758
763
|
// packages/cli/src/init.mjs
|
|
759
|
-
import { existsSync as
|
|
760
|
-
import { basename, dirname, join as
|
|
764
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
765
|
+
import { basename, dirname as dirname2, join as join5, resolve as resolve3 } from "node:path";
|
|
761
766
|
|
|
762
767
|
// packages/cli/src/config/templates.mjs
|
|
763
768
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
@@ -896,6 +901,7 @@ function validateEmbeddingProfile(profile, label = "embedding") {
|
|
|
896
901
|
return { ...profile, model: profile.model.trim(), ...profile.baseUrl ? { baseUrl: profile.baseUrl.replace(/\/$/, "") } : {} };
|
|
897
902
|
}
|
|
898
903
|
function sanitizeEmbeddingProfile(profile) {
|
|
904
|
+
if (!profile) return null;
|
|
899
905
|
const { apiKey, ...safe } = profile;
|
|
900
906
|
return { ...safe, ...apiKey ? { apiKey: "[redacted]" } : {} };
|
|
901
907
|
}
|
|
@@ -916,7 +922,7 @@ function resolveEmbeddingProfile(root = ".", options = {}) {
|
|
|
916
922
|
if (projectConfig && Object.hasOwn(projectConfig, "embedding")) return { profile: validateEmbeddingProfile(projectConfig.embedding, "project embedding"), source: "project", path: projectPath };
|
|
917
923
|
const globalConfig = readJson(globalPath, "global config");
|
|
918
924
|
if (globalConfig && Object.hasOwn(globalConfig, "embedding")) return { profile: validateEmbeddingProfile(globalConfig.embedding, "global embedding"), source: "global", path: globalPath };
|
|
919
|
-
return { profile:
|
|
925
|
+
return { profile: null, source: "not-configured", path: null };
|
|
920
926
|
}
|
|
921
927
|
|
|
922
928
|
// packages/cli/src/memory/config.mjs
|
|
@@ -1702,6 +1708,60 @@ function resolveInitializationTemplate(explicitName = null, home = dotdotgodHome
|
|
|
1702
1708
|
` };
|
|
1703
1709
|
}
|
|
1704
1710
|
|
|
1711
|
+
// packages/cli/src/claude-plans.mjs
|
|
1712
|
+
import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync4, realpathSync, writeFileSync, mkdirSync } from "node:fs";
|
|
1713
|
+
import { dirname, join as join4, relative as relative2, resolve as resolve2, isAbsolute } from "node:path";
|
|
1714
|
+
function prepareClaudePlans(root, documentationRoot) {
|
|
1715
|
+
const path = join4(root, ".claude", "settings.json");
|
|
1716
|
+
for (const target of [join4(root, ".claude"), path]) {
|
|
1717
|
+
try {
|
|
1718
|
+
if (lstatSync(target).isSymbolicLink()) throw new Error(`Refusing symlink: ${target}`);
|
|
1719
|
+
} catch (error) {
|
|
1720
|
+
if (error.code !== "ENOENT") throw error;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
const base = resolve2(root);
|
|
1724
|
+
if (existsSync4(dirname(path))) {
|
|
1725
|
+
const rel2 = relative2(realpathSync(base), realpathSync(dirname(path)));
|
|
1726
|
+
if (rel2.startsWith("..") || isAbsolute(rel2)) throw new Error("Claude settings must stay inside the project.");
|
|
1727
|
+
}
|
|
1728
|
+
const current = existsSync4(path) ? readFileSync4(path, "utf8") : null;
|
|
1729
|
+
const settings = current === null ? {} : JSON.parse(current);
|
|
1730
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings)) throw new Error("Claude settings must be a JSON object.");
|
|
1731
|
+
const value = `./${documentationRoot}/plan`;
|
|
1732
|
+
if (Object.hasOwn(settings, "plansDirectory")) return {
|
|
1733
|
+
status: "skipped",
|
|
1734
|
+
path,
|
|
1735
|
+
reason: "Existing plansDirectory preserved.",
|
|
1736
|
+
value: settings.plansDirectory
|
|
1737
|
+
};
|
|
1738
|
+
return {
|
|
1739
|
+
status: current === null ? "created" : "updated",
|
|
1740
|
+
path,
|
|
1741
|
+
value,
|
|
1742
|
+
current,
|
|
1743
|
+
content: `${JSON.stringify({ ...settings, plansDirectory: value }, null, 2)}
|
|
1744
|
+
`
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
function applyClaudePlans(prepared, dryRun) {
|
|
1748
|
+
const { content, current, ...result } = prepared;
|
|
1749
|
+
if (!content) return result;
|
|
1750
|
+
if (dryRun) return { ...result, status: `would_${result.status === "created" ? "create" : "update"}` };
|
|
1751
|
+
for (const target of [dirname(prepared.path), prepared.path]) {
|
|
1752
|
+
try {
|
|
1753
|
+
if (lstatSync(target).isSymbolicLink()) throw new Error(`Refusing symlink: ${target}`);
|
|
1754
|
+
} catch (error) {
|
|
1755
|
+
if (error.code !== "ENOENT") throw error;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
const latest = existsSync4(prepared.path) ? readFileSync4(prepared.path, "utf8") : null;
|
|
1759
|
+
if (latest !== current) throw new Error("Claude settings changed during initialization; retry without overwriting them.");
|
|
1760
|
+
mkdirSync(dirname(prepared.path), { recursive: true });
|
|
1761
|
+
writeFileSync(prepared.path, content, { flag: current === null ? "wx" : "w" });
|
|
1762
|
+
return result;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1705
1765
|
// packages/cli/src/init.mjs
|
|
1706
1766
|
function action(status, path, extra = {}) {
|
|
1707
1767
|
return { status, path, ...extra };
|
|
@@ -1717,6 +1777,7 @@ function parseInitOptions(argv) {
|
|
|
1717
1777
|
if (arg === "--json") options.json = true;
|
|
1718
1778
|
else if (arg === "--dotdot-setting") options.dotdotSetting = true;
|
|
1719
1779
|
else if (arg === "--dry-run") options.dryRun = true;
|
|
1780
|
+
else if (arg === "--claude-plans") options.claudePlans = true;
|
|
1720
1781
|
else if (arg === "--project-name" || arg === "--template" || arg === "--documentation-root") {
|
|
1721
1782
|
const value = argv[i + 1];
|
|
1722
1783
|
if (!value || value.startsWith("-")) throw new Error(`${arg} requires a value`);
|
|
@@ -1737,7 +1798,7 @@ function parseInitOptions(argv) {
|
|
|
1737
1798
|
}
|
|
1738
1799
|
if (!options.root) throw new Error("Missing required argument: <root>");
|
|
1739
1800
|
if (!isValidDocumentationRoot(options.documentationRoot)) throw new Error("Invalid --documentation-root: expected a safe repository-relative directory");
|
|
1740
|
-
options.root =
|
|
1801
|
+
options.root = resolve3(options.root);
|
|
1741
1802
|
if (!options.projectName) options.projectName = basename(options.root);
|
|
1742
1803
|
return options;
|
|
1743
1804
|
}
|
|
@@ -1750,8 +1811,8 @@ function initError(options, code, message) {
|
|
|
1750
1811
|
process.exit(2);
|
|
1751
1812
|
}
|
|
1752
1813
|
function writeInitFile(options, relativePath, content, actions) {
|
|
1753
|
-
const target =
|
|
1754
|
-
if (
|
|
1814
|
+
const target = join5(options.root, relativePath);
|
|
1815
|
+
if (existsSync5(target)) {
|
|
1755
1816
|
actions.push(action("skipped", target));
|
|
1756
1817
|
return;
|
|
1757
1818
|
}
|
|
@@ -1759,14 +1820,14 @@ function writeInitFile(options, relativePath, content, actions) {
|
|
|
1759
1820
|
actions.push(action("would_create", target));
|
|
1760
1821
|
return;
|
|
1761
1822
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1823
|
+
mkdirSync2(dirname2(target), { recursive: true });
|
|
1824
|
+
writeFileSync2(target, content.endsWith("\n") ? content : `${content}
|
|
1764
1825
|
`);
|
|
1765
1826
|
actions.push(action("created", target));
|
|
1766
1827
|
}
|
|
1767
1828
|
function ensureInitDirectory(options, relativePath, actions) {
|
|
1768
|
-
const target =
|
|
1769
|
-
if (
|
|
1829
|
+
const target = join5(options.root, relativePath);
|
|
1830
|
+
if (existsSync5(target)) {
|
|
1770
1831
|
actions.push(action("skipped", target));
|
|
1771
1832
|
return;
|
|
1772
1833
|
}
|
|
@@ -1774,22 +1835,22 @@ function ensureInitDirectory(options, relativePath, actions) {
|
|
|
1774
1835
|
actions.push(action("would_create", target));
|
|
1775
1836
|
return;
|
|
1776
1837
|
}
|
|
1777
|
-
|
|
1838
|
+
mkdirSync2(target, { recursive: true });
|
|
1778
1839
|
actions.push(action("created", target));
|
|
1779
1840
|
}
|
|
1780
1841
|
function ensureGitignoreEntry(options, entry, actions) {
|
|
1781
|
-
const target =
|
|
1782
|
-
const existed =
|
|
1783
|
-
let current = existed ?
|
|
1842
|
+
const target = join5(options.root, ".gitignore");
|
|
1843
|
+
const existed = existsSync5(target);
|
|
1844
|
+
let current = existed ? readFileSync5(target, "utf8") : "";
|
|
1784
1845
|
if (current.split(/\r?\n/).includes(entry)) return;
|
|
1785
1846
|
if (options.dryRun) {
|
|
1786
1847
|
actions.push(action(existed ? "would_update" : "would_create", target, { add: entry }));
|
|
1787
1848
|
return;
|
|
1788
1849
|
}
|
|
1789
|
-
|
|
1850
|
+
mkdirSync2(options.root, { recursive: true });
|
|
1790
1851
|
if (current.length > 0 && !current.endsWith("\n")) current = `${current}
|
|
1791
1852
|
`;
|
|
1792
|
-
|
|
1853
|
+
writeFileSync2(target, `${current}${entry}
|
|
1793
1854
|
`);
|
|
1794
1855
|
actions.push(action(existed ? "updated" : "created", target, { add: entry }));
|
|
1795
1856
|
}
|
|
@@ -1993,15 +2054,26 @@ function runInit(argv, usage2) {
|
|
|
1993
2054
|
usage2(error instanceof Error ? error.message : String(error), "init");
|
|
1994
2055
|
return;
|
|
1995
2056
|
}
|
|
1996
|
-
if (!options.dryRun &&
|
|
2057
|
+
if (!options.dryRun && existsSync5(options.root)) {
|
|
1997
2058
|
try {
|
|
1998
2059
|
if (!statSync(options.root).isDirectory()) initError(options, "ROOT_NOT_DIRECTORY", `Project root is not a directory: ${options.root}`);
|
|
1999
2060
|
} catch {
|
|
2000
2061
|
initError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
|
|
2001
2062
|
}
|
|
2002
2063
|
}
|
|
2003
|
-
if (!options.
|
|
2004
|
-
|
|
2064
|
+
if (options.claudePlans && !options.documentationRootExplicit && existsSync5(join5(options.root, "dotdotgod.config.json"))) {
|
|
2065
|
+
options.documentationRoot = readMemoryConfig(options.root).documentation.root;
|
|
2066
|
+
}
|
|
2067
|
+
let claudePlans;
|
|
2068
|
+
if (options.claudePlans) {
|
|
2069
|
+
try {
|
|
2070
|
+
claudePlans = prepareClaudePlans(options.root, options.documentationRoot);
|
|
2071
|
+
} catch (error) {
|
|
2072
|
+
initError(options, "CLAUDE_SETTINGS_INVALID", error.message);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
if (!options.dryRun) mkdirSync2(options.root, { recursive: true });
|
|
2076
|
+
const configExists = existsSync5(join5(options.root, "dotdotgod.config.json"));
|
|
2005
2077
|
if (configExists && options.documentationRootExplicit) {
|
|
2006
2078
|
const existingRoot = readMemoryConfig(options.root).documentation.root;
|
|
2007
2079
|
if (existingRoot !== options.documentationRoot) initError(options, "DOCUMENTATION_ROOT_CONFLICT", `Existing config uses documentation root ${existingRoot}; initialization does not migrate documentation.`);
|
|
@@ -2024,6 +2096,13 @@ function runInit(argv, usage2) {
|
|
|
2024
2096
|
}
|
|
2025
2097
|
writeInitFile(options, "dotdotgod.config.json", template?.text ?? "", actions);
|
|
2026
2098
|
for (const entry of [`${options.documentationRoot}/plan`, `${options.documentationRoot}/archive`, ".dotdotgod"]) ensureGitignoreEntry(options, entry, actions);
|
|
2099
|
+
if (claudePlans) {
|
|
2100
|
+
try {
|
|
2101
|
+
actions.push(applyClaudePlans(claudePlans, options.dryRun));
|
|
2102
|
+
} catch (error) {
|
|
2103
|
+
initError(options, "CLAUDE_SETTINGS_CHANGED", error.message);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2027
2106
|
const payload = { ok: true, command: "init", root: options.root, projectName: options.projectName, documentationRoot: options.documentationRoot, dryRun: options.dryRun, dotdotSetting: options.dotdotSetting, template: template ? { name: template.name, source: template.source, selectedBy: template.selectedBy } : null, actions };
|
|
2028
2107
|
if (options.json) console.log(JSON.stringify(payload, null, 2));
|
|
2029
2108
|
else {
|
|
@@ -2033,8 +2112,8 @@ function runInit(argv, usage2) {
|
|
|
2033
2112
|
}
|
|
2034
2113
|
|
|
2035
2114
|
// packages/cli/src/cli/usage.mjs
|
|
2036
|
-
import { readFileSync as
|
|
2037
|
-
import { resolve as
|
|
2115
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
2116
|
+
import { resolve as resolve4 } from "node:path";
|
|
2038
2117
|
var HELP_TOKENS = /* @__PURE__ */ new Set(["help", "--help", "-h"]);
|
|
2039
2118
|
var VERSION_TOKENS = /* @__PURE__ */ new Set(["version", "--version", "-v"]);
|
|
2040
2119
|
function commandUsage(command = "root") {
|
|
@@ -2044,9 +2123,10 @@ function commandUsage(command = "root") {
|
|
|
2044
2123
|
dotdotgod validate <root> [--include-local-memory] [--check-index] [--max-lines n] [--max-chars n] [--no-link-check] [--json]`;
|
|
2045
2124
|
case "init":
|
|
2046
2125
|
return `Usage:
|
|
2047
|
-
dotdotgod init <root> [--project-name NAME] [--template NAME] [--documentation-root PATH] [--dotdot-setting] [--dry-run] [--json]
|
|
2126
|
+
dotdotgod init <root> [--project-name NAME] [--template NAME] [--documentation-root PATH] [--dotdot-setting] [--claude-plans] [--dry-run] [--json]
|
|
2048
2127
|
|
|
2049
|
-
Create AGENTS.md, agent entrypoints, docs indexes, a project config from an initialization template, and local memory gitignore entries
|
|
2128
|
+
Create AGENTS.md, agent entrypoints, docs indexes, a project config from an initialization template, and local memory gitignore entries.
|
|
2129
|
+
Use --claude-plans only after opting into project-local native plan storage; existing Claude settings are preserved.`;
|
|
2050
2130
|
case "index":
|
|
2051
2131
|
return `Usage:
|
|
2052
2132
|
dotdotgod index <root> [--json]`;
|
|
@@ -2066,9 +2146,9 @@ Create dotdotgod.config.json from a bundled or user template. Without --template
|
|
|
2066
2146
|
dotdotgod status <root> [--json]`;
|
|
2067
2147
|
case "query":
|
|
2068
2148
|
return `Usage:
|
|
2069
|
-
dotdotgod query <root> <query> [--limit n] [--scope docs|code|all] [--search hybrid|keyword|vector] [--
|
|
2149
|
+
dotdotgod query <root> <query> [--limit n] [--scope docs|code|all] [--search hybrid|keyword|vector] [--json]
|
|
2070
2150
|
|
|
2071
|
-
Search full documentation and code outlines/comments. Default: hybrid docs+code.
|
|
2151
|
+
Search full documentation and code outlines/comments. Default: hybrid docs+code. Selecting a remote embedding profile authorizes code metadata transmission.`;
|
|
2072
2152
|
case "embedding":
|
|
2073
2153
|
return `Usage:
|
|
2074
2154
|
dotdotgod embedding status [<root>] [--json]
|
|
@@ -2173,7 +2253,7 @@ function isVersionToken(value) {
|
|
|
2173
2253
|
function printVersion() {
|
|
2174
2254
|
let version = "unknown";
|
|
2175
2255
|
try {
|
|
2176
|
-
const data = JSON.parse(
|
|
2256
|
+
const data = JSON.parse(readFileSync6(new URL("../../package.json", import.meta.url), "utf8"));
|
|
2177
2257
|
if (typeof data.version === "string") version = data.version;
|
|
2178
2258
|
} catch {
|
|
2179
2259
|
}
|
|
@@ -2195,17 +2275,17 @@ function parseCommon(argv) {
|
|
|
2195
2275
|
if (arg === "--json") options.json = true;
|
|
2196
2276
|
else if (!arg.startsWith("-") && options.root === ".") options.root = arg;
|
|
2197
2277
|
}
|
|
2198
|
-
options.root =
|
|
2278
|
+
options.root = resolve4(options.root);
|
|
2199
2279
|
return options;
|
|
2200
2280
|
}
|
|
2201
2281
|
|
|
2202
2282
|
// packages/cli/src/validate/run.mjs
|
|
2203
|
-
import { existsSync as
|
|
2204
|
-
import { basename as basename7, dirname as
|
|
2283
|
+
import { existsSync as existsSync14, readFileSync as readFileSync14, readdirSync as readdirSync3 } from "node:fs";
|
|
2284
|
+
import { basename as basename7, dirname as dirname11, extname as extname6, join as join13, resolve as resolve9 } from "node:path";
|
|
2205
2285
|
|
|
2206
2286
|
// packages/cli/src/docs/traceability.mjs
|
|
2207
|
-
import { existsSync as
|
|
2208
|
-
import { basename as basename2, dirname as
|
|
2287
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
2288
|
+
import { basename as basename2, dirname as dirname3, extname, relative as relative3, resolve as resolve5 } from "node:path";
|
|
2209
2289
|
function isSecretIndexPath(path) {
|
|
2210
2290
|
return /(^|\/)(\.env|\.npmrc|\.pypirc|id_rsa|id_dsa|id_ed25519|credentials|secrets?)(\.|\/|$)/i.test(path);
|
|
2211
2291
|
}
|
|
@@ -2304,9 +2384,9 @@ function stripTraceabilityLinksRegion(content) {
|
|
|
2304
2384
|
return stripped.replace(/(?:^[ \t]*\r?\n)*^##[ \t]+Traceability[ \t]*\r?(?:\n(?:[ \t]*\r?\n)*|$)/gm, "");
|
|
2305
2385
|
}
|
|
2306
2386
|
function markdownLinkPath(root, file, target) {
|
|
2307
|
-
const fromDir =
|
|
2308
|
-
const absoluteTarget =
|
|
2309
|
-
let link =
|
|
2387
|
+
const fromDir = dirname3(file);
|
|
2388
|
+
const absoluteTarget = resolve5(root, target);
|
|
2389
|
+
let link = relative3(fromDir, absoluteTarget).split("\\").join("/");
|
|
2310
2390
|
if (!link.startsWith(".") && !link.startsWith("/")) link = link || basename2(target);
|
|
2311
2391
|
return link;
|
|
2312
2392
|
}
|
|
@@ -2405,7 +2485,7 @@ function validateTraceabilityPathArray(data, field2, add, root, config, { requir
|
|
|
2405
2485
|
add("TRACEABILITY_LOCAL_MEMORY_TARGET", displayField, `target points to local memory area "${area2.id}": ${value}. Traceability must point to shared durable files, not active plans or archive memory.`);
|
|
2406
2486
|
continue;
|
|
2407
2487
|
}
|
|
2408
|
-
if (!
|
|
2488
|
+
if (!existsSync6(resolve5(root, value))) add("TRACEABILITY_MISSING_TARGET", displayField, `target does not exist: ${value}.`);
|
|
2409
2489
|
}
|
|
2410
2490
|
}
|
|
2411
2491
|
function validateTraceabilityCommandArray(data, field2, add, { required = true, displayField = field2 } = {}) {
|
|
@@ -2473,9 +2553,9 @@ function validateTraceabilityBlock(data, root, file, line = null, config = defau
|
|
|
2473
2553
|
|
|
2474
2554
|
// packages/cli/src/index/files.mjs
|
|
2475
2555
|
import { createHash } from "node:crypto";
|
|
2476
|
-
import { existsSync as
|
|
2556
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync, statSync as statSync2 } from "node:fs";
|
|
2477
2557
|
import { spawnSync } from "node:child_process";
|
|
2478
|
-
import { basename as basename3, extname as extname2, join as
|
|
2558
|
+
import { basename as basename3, extname as extname2, join as join6 } from "node:path";
|
|
2479
2559
|
|
|
2480
2560
|
// packages/cli/src/index/constants.mjs
|
|
2481
2561
|
var CACHE_VERSION = 15;
|
|
@@ -2636,7 +2716,7 @@ function walkIndexCandidates(root, config = readMemoryConfig(root)) {
|
|
|
2636
2716
|
const files = [];
|
|
2637
2717
|
const walk = (dir) => {
|
|
2638
2718
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
2639
|
-
const path =
|
|
2719
|
+
const path = join6(dir, entry.name);
|
|
2640
2720
|
const pathRel = rel(root, path);
|
|
2641
2721
|
if (entry.isDirectory()) {
|
|
2642
2722
|
const area2 = resolveMemoryArea(pathRel, config);
|
|
@@ -2650,7 +2730,7 @@ function walkIndexCandidates(root, config = readMemoryConfig(root)) {
|
|
|
2650
2730
|
function addConfiguredLocalMemoryCandidates(root, candidates, config, limit = 5e3) {
|
|
2651
2731
|
let added = 0;
|
|
2652
2732
|
const addPath = (path) => {
|
|
2653
|
-
if (added >= limit || !
|
|
2733
|
+
if (added >= limit || !existsSync7(path)) return;
|
|
2654
2734
|
let stat;
|
|
2655
2735
|
try {
|
|
2656
2736
|
stat = statSync2(path);
|
|
@@ -2666,7 +2746,7 @@ function addConfiguredLocalMemoryCandidates(root, candidates, config, limit = 5e
|
|
|
2666
2746
|
if (!stat.isDirectory() || isExcludedIndexDir(pathRel)) return;
|
|
2667
2747
|
for (const entry of readdirSync(path, { withFileTypes: true })) {
|
|
2668
2748
|
if (added >= limit) return;
|
|
2669
|
-
addPath(
|
|
2749
|
+
addPath(join6(path, entry.name));
|
|
2670
2750
|
}
|
|
2671
2751
|
};
|
|
2672
2752
|
for (const area2 of config.areas ?? []) {
|
|
@@ -2674,30 +2754,30 @@ function addConfiguredLocalMemoryCandidates(root, candidates, config, limit = 5e
|
|
|
2674
2754
|
for (const pattern of area2.paths ?? []) {
|
|
2675
2755
|
if (pattern.startsWith("**/")) continue;
|
|
2676
2756
|
const base = pattern.endsWith("/**") ? pattern.slice(0, -3) : pattern;
|
|
2677
|
-
addPath(
|
|
2757
|
+
addPath(join6(root, base));
|
|
2678
2758
|
}
|
|
2679
2759
|
}
|
|
2680
2760
|
}
|
|
2681
2761
|
function collectIndexFiles(root, config = readMemoryConfig(root)) {
|
|
2682
2762
|
const candidates = new Set(gitIndexCandidates(root) ?? walkIndexCandidates(root, config));
|
|
2683
2763
|
addConfiguredLocalMemoryCandidates(root, candidates, config);
|
|
2684
|
-
return [...candidates].filter((path) => shouldIndexPath(path, config)).map((path) =>
|
|
2764
|
+
return [...candidates].filter((path) => shouldIndexPath(path, config)).map((path) => join6(root, path)).filter(existsSync7).sort();
|
|
2685
2765
|
}
|
|
2686
2766
|
function fingerprint(file) {
|
|
2687
|
-
const content =
|
|
2767
|
+
const content = readFileSync7(file);
|
|
2688
2768
|
return createHash("sha256").update(content).digest("hex");
|
|
2689
2769
|
}
|
|
2690
2770
|
function cacheFile(root) {
|
|
2691
|
-
return
|
|
2771
|
+
return join6(root, CACHE_DIR, MANIFEST_FILE);
|
|
2692
2772
|
}
|
|
2693
2773
|
|
|
2694
2774
|
// packages/cli/src/index/cache.mjs
|
|
2695
|
-
import { existsSync as
|
|
2775
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
|
|
2696
2776
|
import { basename as basename6, join as join12 } from "node:path";
|
|
2697
2777
|
|
|
2698
2778
|
// packages/cli/src/graph/store.mjs
|
|
2699
|
-
import { existsSync as
|
|
2700
|
-
import { dirname as
|
|
2779
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2780
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
2701
2781
|
var GRAPH_NODE_SHARDS = ["docs", "packages", "source"];
|
|
2702
2782
|
var GRAPH_EDGE_SHARDS = ["docs-links", "packages", "other"];
|
|
2703
2783
|
function graphNodeShard(node2) {
|
|
@@ -2728,14 +2808,14 @@ function expandEdge(row) {
|
|
|
2728
2808
|
return { source, target, relation, ...data };
|
|
2729
2809
|
}
|
|
2730
2810
|
function shardFile(root, kind, name) {
|
|
2731
|
-
return
|
|
2811
|
+
return join7(root, CACHE_DIR, "graph", kind, `${name}.json`);
|
|
2732
2812
|
}
|
|
2733
2813
|
function jsonSize(file) {
|
|
2734
|
-
return
|
|
2814
|
+
return existsSync8(file) ? statSync3(file).size : 0;
|
|
2735
2815
|
}
|
|
2736
2816
|
function writeJson(file, value) {
|
|
2737
|
-
|
|
2738
|
-
|
|
2817
|
+
mkdirSync3(dirname4(file), { recursive: true });
|
|
2818
|
+
writeFileSync3(file, `${JSON.stringify(value)}
|
|
2739
2819
|
`);
|
|
2740
2820
|
}
|
|
2741
2821
|
function compactGraph(graph) {
|
|
@@ -2798,13 +2878,13 @@ function addEdge(graph, source, target, relation, data = {}) {
|
|
|
2798
2878
|
}
|
|
2799
2879
|
|
|
2800
2880
|
// packages/cli/src/graph/extract.mjs
|
|
2801
|
-
import { readFileSync as
|
|
2802
|
-
import { basename as basename5, dirname as
|
|
2881
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
2882
|
+
import { basename as basename5, dirname as dirname5, resolve as resolve7 } from "node:path";
|
|
2803
2883
|
|
|
2804
2884
|
// packages/cli/src/graph/metadata.mjs
|
|
2805
2885
|
import { createHash as createHash2 } from "node:crypto";
|
|
2806
|
-
import { existsSync as
|
|
2807
|
-
import { basename as basename4, extname as extname3, resolve as
|
|
2886
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
2887
|
+
import { basename as basename4, extname as extname3, resolve as resolve6 } from "node:path";
|
|
2808
2888
|
function isReadmeIndexPath(path = "") {
|
|
2809
2889
|
return basename4(path) === "README.md";
|
|
2810
2890
|
}
|
|
@@ -2863,7 +2943,7 @@ function addReferenceEvidence(graph, sourceId, reference) {
|
|
|
2863
2943
|
function addTraceabilityTarget(graph, sourceId, root, relation, targetPath, data = {}, config = defaultMemoryConfig()) {
|
|
2864
2944
|
if (!isLocalRelativeTraceabilityPath(targetPath)) return;
|
|
2865
2945
|
addReferenceEvidence(graph, sourceId, { targetPath, href: targetPath, relation, data: { confidence: "CURATED_TRACEABILITY", ...data } });
|
|
2866
|
-
if (!
|
|
2946
|
+
if (!existsSync9(resolve6(root, targetPath))) return;
|
|
2867
2947
|
const targetId = `file:${targetPath}`;
|
|
2868
2948
|
addNode(graph, targetId, "file", fileNodeMetadata(targetPath, null, config));
|
|
2869
2949
|
addEdge(graph, sourceId, targetId, relation, { confidence: "CURATED_TRACEABILITY", ...data });
|
|
@@ -2915,7 +2995,7 @@ function headingId(path, title) {
|
|
|
2915
2995
|
// packages/cli/src/graph/extract.mjs
|
|
2916
2996
|
function extractMarkdownGraph(root, file, graph, config = defaultMemoryConfig()) {
|
|
2917
2997
|
const path = rel(root, file);
|
|
2918
|
-
const content =
|
|
2998
|
+
const content = readFileSync8(file, "utf8");
|
|
2919
2999
|
const fileId = `file:${path}`;
|
|
2920
3000
|
const headingRe = /^(#{1,6})\s+(.+)$/gm;
|
|
2921
3001
|
let match;
|
|
@@ -2929,7 +3009,7 @@ function extractMarkdownGraph(root, file, graph, config = defaultMemoryConfig())
|
|
|
2929
3009
|
const hashIndex = href.indexOf("#");
|
|
2930
3010
|
const pathPart = hashIndex < 0 ? href : href.slice(0, hashIndex);
|
|
2931
3011
|
const anchor = hashIndex < 0 ? "" : href.slice(hashIndex + 1);
|
|
2932
|
-
const targetPath = pathPart ? rel(root,
|
|
3012
|
+
const targetPath = pathPart ? rel(root, resolve7(dirname5(file), pathPart)) : path;
|
|
2933
3013
|
const data = { line, confidence: "EXTRACTED" };
|
|
2934
3014
|
addReferenceEvidence(graph, fileId, { targetPath, href, anchor, relation: "links_to", data });
|
|
2935
3015
|
if (pathPart && isReadmeIndexPath(path)) addReferenceEvidence(graph, fileId, { targetPath, href, anchor, relation: "routes_to", data: { line, confidence: "CURATED_INDEX", sourceRole: "readme-index" } });
|
|
@@ -2946,7 +3026,7 @@ function extractPackageGraph(root, file, graph) {
|
|
|
2946
3026
|
const fileId = `file:${path}`;
|
|
2947
3027
|
let pkg;
|
|
2948
3028
|
try {
|
|
2949
|
-
pkg = JSON.parse(
|
|
3029
|
+
pkg = JSON.parse(readFileSync8(file, "utf8"));
|
|
2950
3030
|
} catch {
|
|
2951
3031
|
return;
|
|
2952
3032
|
}
|
|
@@ -2992,18 +3072,18 @@ function buildGraph(root, files, config = defaultMemoryConfig()) {
|
|
|
2992
3072
|
}
|
|
2993
3073
|
|
|
2994
3074
|
// packages/cli/src/symbols/store.mjs
|
|
2995
|
-
import { existsSync as
|
|
3075
|
+
import { existsSync as existsSync12, lstatSync as lstatSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync12, renameSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2996
3076
|
import { randomUUID } from "node:crypto";
|
|
2997
|
-
import { dirname as
|
|
3077
|
+
import { dirname as dirname8, join as join11 } from "node:path";
|
|
2998
3078
|
|
|
2999
3079
|
// packages/cli/src/symbols/discovery.mjs
|
|
3000
3080
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3001
|
-
import { existsSync as
|
|
3002
|
-
import { extname as extname5, join as
|
|
3081
|
+
import { existsSync as existsSync11, lstatSync as lstatSync2, readFileSync as readFileSync11, realpathSync as realpathSync2 } from "node:fs";
|
|
3082
|
+
import { extname as extname5, join as join10, relative as relative5, isAbsolute as isAbsolute2 } from "node:path";
|
|
3003
3083
|
|
|
3004
3084
|
// packages/cli/src/query/chunks.mjs
|
|
3005
|
-
import { readFileSync as
|
|
3006
|
-
import { join as
|
|
3085
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync2 } from "node:fs";
|
|
3086
|
+
import { join as join8, relative as relative4 } from "node:path";
|
|
3007
3087
|
import { createHash as createHash3 } from "node:crypto";
|
|
3008
3088
|
var MAX_CHUNK_CHARS = 1600;
|
|
3009
3089
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
|
|
@@ -3011,7 +3091,7 @@ function textFingerprint(text) {
|
|
|
3011
3091
|
return createHash3("sha256").update(text).digest("hex");
|
|
3012
3092
|
}
|
|
3013
3093
|
function collectDocumentationMarkdown(root, exclude = ["docs/plan", "docs/archive"], documentationRoot = "docs") {
|
|
3014
|
-
const docsRoot =
|
|
3094
|
+
const docsRoot = join8(root, documentationRoot);
|
|
3015
3095
|
const files = [];
|
|
3016
3096
|
const walk = (directory) => {
|
|
3017
3097
|
let entries;
|
|
@@ -3023,8 +3103,8 @@ function collectDocumentationMarkdown(root, exclude = ["docs/plan", "docs/archiv
|
|
|
3023
3103
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
3024
3104
|
for (const entry of entries) {
|
|
3025
3105
|
if (entry.name.startsWith(".")) continue;
|
|
3026
|
-
const absolute =
|
|
3027
|
-
const path =
|
|
3106
|
+
const absolute = join8(directory, entry.name);
|
|
3107
|
+
const path = relative4(root, absolute).replaceAll("\\", "/");
|
|
3028
3108
|
if (isSecretLikePathPattern(path) || exclude.some((pattern) => matchPathPattern(path, pattern) || path === pattern || path.startsWith(`${pattern}/`))) continue;
|
|
3029
3109
|
if (entry.isDirectory()) {
|
|
3030
3110
|
if (!SKIPPED_DIRECTORIES.has(entry.name)) walk(absolute);
|
|
@@ -3086,7 +3166,7 @@ ${piece}`;
|
|
|
3086
3166
|
}
|
|
3087
3167
|
function collectDocumentationChunks(root, exclude, documentationRoot = "docs") {
|
|
3088
3168
|
return collectDocumentationMarkdown(root, exclude, documentationRoot).flatMap((path) => {
|
|
3089
|
-
const content =
|
|
3169
|
+
const content = readFileSync9(join8(root, path), "utf8");
|
|
3090
3170
|
return chunkMarkdown(path, content);
|
|
3091
3171
|
});
|
|
3092
3172
|
}
|
|
@@ -3095,14 +3175,14 @@ function collectDocumentationChunks(root, exclude, documentationRoot = "docs") {
|
|
|
3095
3175
|
init_languages();
|
|
3096
3176
|
init_runtime();
|
|
3097
3177
|
function inside(root, file) {
|
|
3098
|
-
if (!
|
|
3099
|
-
const path =
|
|
3100
|
-
return !path.startsWith("..") && !
|
|
3178
|
+
if (!lstatSync2(file).isFile()) return false;
|
|
3179
|
+
const path = relative5(realpathSync2(root), realpathSync2(file));
|
|
3180
|
+
return !path.startsWith("..") && !isAbsolute2(path);
|
|
3101
3181
|
}
|
|
3102
3182
|
function ignoreRules(root, directory, cache) {
|
|
3103
3183
|
if (!cache.has(directory)) {
|
|
3104
|
-
const file =
|
|
3105
|
-
cache.set(directory, (0, import_ignore.default)().add(
|
|
3184
|
+
const file = join10(root, directory, ".gitignore");
|
|
3185
|
+
cache.set(directory, (0, import_ignore.default)().add(existsSync11(file) && inside(root, file) ? readFileSync11(file, "utf8") : ""));
|
|
3106
3186
|
}
|
|
3107
3187
|
return cache.get(directory);
|
|
3108
3188
|
}
|
|
@@ -3125,18 +3205,18 @@ function ignored(root, path, cache) {
|
|
|
3125
3205
|
function sourceFiles(root) {
|
|
3126
3206
|
const cache = /* @__PURE__ */ new Map();
|
|
3127
3207
|
return collectIndexFiles(root).filter((file) => {
|
|
3128
|
-
const path =
|
|
3208
|
+
const path = relative5(root, file).replaceAll("\\", "/");
|
|
3129
3209
|
if (!SOURCE_EXTENSIONS.has(extname5(path)) || path.split("/").some((part) => part.startsWith(".")) || isSecretLikePathPattern(path)) return false;
|
|
3130
3210
|
if (!inside(root, file) || ignored(root, path, cache)) return false;
|
|
3131
|
-
return !/^#![^\n]*\n\/\/ Generated|^\/\/ Generated|@generated/m.test(
|
|
3211
|
+
return !/^#![^\n]*\n\/\/ Generated|^\/\/ Generated|@generated/m.test(readFileSync11(file, "utf8").slice(0, 256));
|
|
3132
3212
|
}).map((file) => {
|
|
3133
|
-
const source =
|
|
3134
|
-
return { path:
|
|
3213
|
+
const source = readFileSync11(file, "utf8");
|
|
3214
|
+
return { path: relative5(root, file).replaceAll("\\", "/"), hash: fingerprint(file), language: languageForPath(file), sourceLength: source.length, lineCount: source.split("\n").length };
|
|
3135
3215
|
});
|
|
3136
3216
|
}
|
|
3137
3217
|
function outlinePolicy(root) {
|
|
3138
3218
|
const config = readMemoryConfig(root);
|
|
3139
|
-
const ignores = collectIndexFiles(root).filter((file) => file.endsWith("/.gitignore") && inside(root, file)).map((file) => [
|
|
3219
|
+
const ignores = collectIndexFiles(root).filter((file) => file.endsWith("/.gitignore") && inside(root, file)).map((file) => [relative5(root, file), fingerprint(file)]);
|
|
3140
3220
|
return textFingerprint(JSON.stringify({ areas: config.areas, ignores, extractor: EXTRACTOR_ID, declarations: DECLARATIONS, extensions: LANGUAGE_EXTENSIONS, parser: parserIdentity() }));
|
|
3141
3221
|
}
|
|
3142
3222
|
|
|
@@ -3145,10 +3225,10 @@ init_languages();
|
|
|
3145
3225
|
var OUTLINE_SCHEMA = 1;
|
|
3146
3226
|
var outlinePath = (root) => join11(root, ".dotdotgod", "outlines.json");
|
|
3147
3227
|
function assertCachePath(root) {
|
|
3148
|
-
for (const path of [
|
|
3228
|
+
for (const path of [dirname8(outlinePath(root)), outlinePath(root)]) {
|
|
3149
3229
|
let stat;
|
|
3150
3230
|
try {
|
|
3151
|
-
stat =
|
|
3231
|
+
stat = lstatSync3(path);
|
|
3152
3232
|
} catch (error) {
|
|
3153
3233
|
if (error.code !== "ENOENT") throw error;
|
|
3154
3234
|
}
|
|
@@ -3158,7 +3238,7 @@ function assertCachePath(root) {
|
|
|
3158
3238
|
function readOutlines(root) {
|
|
3159
3239
|
try {
|
|
3160
3240
|
assertCachePath(root);
|
|
3161
|
-
const index = JSON.parse(
|
|
3241
|
+
const index = JSON.parse(readFileSync12(outlinePath(root), "utf8"));
|
|
3162
3242
|
if (index.schema !== OUTLINE_SCHEMA || index.extractor !== EXTRACTOR_ID || !Array.isArray(index.files)) return null;
|
|
3163
3243
|
const paths = /* @__PURE__ */ new Set();
|
|
3164
3244
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -3191,7 +3271,7 @@ function outlineStatus(root) {
|
|
|
3191
3271
|
assertCachePath(root);
|
|
3192
3272
|
const files = sourceFiles(root);
|
|
3193
3273
|
const index = readOutlines(root);
|
|
3194
|
-
if (!index) return { ok: files.length === 0 && !
|
|
3274
|
+
if (!index) return { ok: files.length === 0 && !existsSync12(outlinePath(root)), reason: existsSync12(outlinePath(root)) ? "missing-or-invalid" : "missing", paths: files.map((file) => file.path) };
|
|
3195
3275
|
const old = new Map(index.files.map((file) => [file.path, file]));
|
|
3196
3276
|
const current = new Set(files.map((file) => file.path));
|
|
3197
3277
|
const changed = files.filter((file) => old.get(file.path)?.hash !== file.hash || old.get(file.path)?.sourceLength !== file.sourceLength || old.get(file.path)?.lineCount !== file.lineCount).map((file) => file.path);
|
|
@@ -3220,7 +3300,7 @@ async function buildOutlines(root) {
|
|
|
3220
3300
|
continue;
|
|
3221
3301
|
}
|
|
3222
3302
|
const { extractSymbols: extractSymbols2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
|
|
3223
|
-
const source =
|
|
3303
|
+
const source = readFileSync12(join11(root, file.path), "utf8");
|
|
3224
3304
|
const extracted = await extractSymbols2(file.path, source, file.language);
|
|
3225
3305
|
if (fingerprint(join11(root, file.path)) !== file.hash) throw new Error(`Source changed during outline extraction: ${file.path}`);
|
|
3226
3306
|
records.push({ ...file, ...extracted });
|
|
@@ -3228,9 +3308,9 @@ async function buildOutlines(root) {
|
|
|
3228
3308
|
}
|
|
3229
3309
|
const index = { schema: OUTLINE_SCHEMA, extractor: EXTRACTOR_ID, policy: outlinePolicy(root), files: records, refresh: { parsed, reused } };
|
|
3230
3310
|
const target = outlinePath(root);
|
|
3231
|
-
|
|
3311
|
+
mkdirSync4(dirname8(target), { recursive: true });
|
|
3232
3312
|
const temporary = `${target}.tmp-${process.pid}-${randomUUID()}`;
|
|
3233
|
-
|
|
3313
|
+
writeFileSync4(temporary, JSON.stringify(index), { flag: "wx" });
|
|
3234
3314
|
renameSync(temporary, target);
|
|
3235
3315
|
return index;
|
|
3236
3316
|
}
|
|
@@ -3248,10 +3328,10 @@ ${text}`;
|
|
|
3248
3328
|
}
|
|
3249
3329
|
|
|
3250
3330
|
// packages/cli/src/symbols/graph.mjs
|
|
3251
|
-
import { dirname as
|
|
3331
|
+
import { dirname as dirname9, posix } from "node:path";
|
|
3252
3332
|
function importTarget(file, specifier, paths) {
|
|
3253
3333
|
if (!specifier.startsWith(".")) return null;
|
|
3254
|
-
const base = posix.normalize(posix.join(
|
|
3334
|
+
const base = posix.normalize(posix.join(dirname9(file).replaceAll("\\", "/"), specifier));
|
|
3255
3335
|
if (base.startsWith("../")) return null;
|
|
3256
3336
|
const stem = base.replace(/\.(js|jsx|mjs|cjs)$/, "");
|
|
3257
3337
|
const candidates = [base, ...[".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py"].map((extension) => `${stem}${extension}`), ...[".ts", ".js", ".py"].map((extension) => `${base}/index${extension}`)];
|
|
@@ -3338,14 +3418,14 @@ function writeIndex(root, index) {
|
|
|
3338
3418
|
}
|
|
3339
3419
|
function readIndex(root) {
|
|
3340
3420
|
const file = cacheFile(root);
|
|
3341
|
-
if (!
|
|
3421
|
+
if (!existsSync13(file)) return null;
|
|
3342
3422
|
try {
|
|
3343
|
-
const manifest = JSON.parse(
|
|
3423
|
+
const manifest = JSON.parse(readFileSync13(file, "utf8"));
|
|
3344
3424
|
const shards = manifest.graph?.shards;
|
|
3345
3425
|
if (!shards) return manifest;
|
|
3346
3426
|
const compact = { nodes: {}, edges: {} };
|
|
3347
|
-
for (const [name, path] of Object.entries(shards.nodes ?? {})) compact.nodes[name] =
|
|
3348
|
-
for (const [name, path] of Object.entries(shards.edges ?? {})) compact.edges[name] =
|
|
3427
|
+
for (const [name, path] of Object.entries(shards.nodes ?? {})) compact.nodes[name] = existsSync13(join12(root, path)) ? JSON.parse(readFileSync13(join12(root, path), "utf8")) : [];
|
|
3428
|
+
for (const [name, path] of Object.entries(shards.edges ?? {})) compact.edges[name] = existsSync13(join12(root, path)) ? JSON.parse(readFileSync13(join12(root, path), "utf8")) : [];
|
|
3349
3429
|
return { ...manifest, graph: expandGraph(compact) };
|
|
3350
3430
|
} catch {
|
|
3351
3431
|
return null;
|
|
@@ -3401,7 +3481,7 @@ function graphSummary(index) {
|
|
|
3401
3481
|
|
|
3402
3482
|
// packages/cli/src/validate/references.mjs
|
|
3403
3483
|
import { statSync as statSync5 } from "node:fs";
|
|
3404
|
-
import { dirname as
|
|
3484
|
+
import { dirname as dirname10, relative as relative6, resolve as resolve8 } from "node:path";
|
|
3405
3485
|
var blank = (text) => text.replace(/[^\n\r]/g, " ");
|
|
3406
3486
|
var unescape = (text) => text.replace(/\\([!"#$%&'()*+,\-./:;<=>?@[\]\\^_`{|}~])/g, "$1");
|
|
3407
3487
|
var labelKey = (text) => unescape(text).trim().replace(/\s+/g, " ").toLowerCase();
|
|
@@ -3501,7 +3581,7 @@ function extractMemoryReferences(content) {
|
|
|
3501
3581
|
return references;
|
|
3502
3582
|
}
|
|
3503
3583
|
function sharedLocalReferences(content, root, file, config) {
|
|
3504
|
-
const source = resolveMemoryArea(
|
|
3584
|
+
const source = resolveMemoryArea(relative6(root, file).split("\\").join("/"), config);
|
|
3505
3585
|
if (source?.scope !== "shared") return [];
|
|
3506
3586
|
const errors = [];
|
|
3507
3587
|
for (const item of extractMemoryReferences(content)) {
|
|
@@ -3513,14 +3593,14 @@ function sharedLocalReferences(content, root, file, config) {
|
|
|
3513
3593
|
continue;
|
|
3514
3594
|
}
|
|
3515
3595
|
if (!path) continue;
|
|
3516
|
-
const base = item.kind === "markdown-link" || /^\.\.?\//.test(path) ?
|
|
3517
|
-
const target =
|
|
3596
|
+
const base = item.kind === "markdown-link" || /^\.\.?\//.test(path) ? dirname10(file) : root;
|
|
3597
|
+
const target = relative6(root, resolve8(base, path)).split("\\").join("/");
|
|
3518
3598
|
if (target === ".." || target.startsWith("../")) continue;
|
|
3519
3599
|
if (item.kind === "inline-code") {
|
|
3520
3600
|
const configuredDirectory = (config.areas ?? []).some((area3) => (area3.paths ?? []).some((pattern) => pattern === `${target}/**`));
|
|
3521
3601
|
let directory = configuredDirectory;
|
|
3522
3602
|
try {
|
|
3523
|
-
directory ||= statSync5(
|
|
3603
|
+
directory ||= statSync5(resolve8(root, target)).isDirectory();
|
|
3524
3604
|
} catch {
|
|
3525
3605
|
}
|
|
3526
3606
|
if (directory) continue;
|
|
@@ -3545,7 +3625,7 @@ function runValidate(argv) {
|
|
|
3545
3625
|
else if (!arg.startsWith("-")) options.root = arg;
|
|
3546
3626
|
else usage(`Unknown option: ${arg}`, "validate");
|
|
3547
3627
|
}
|
|
3548
|
-
const root =
|
|
3628
|
+
const root = resolve9(options.root);
|
|
3549
3629
|
const memoryConfig = readMemoryConfig(root);
|
|
3550
3630
|
const documentationRoot = memoryConfig.documentation?.root ?? "docs";
|
|
3551
3631
|
const docs = join13(root, documentationRoot);
|
|
@@ -3566,7 +3646,7 @@ Fix: ${fix}` : message, ...prompt ? { prompt } : {} });
|
|
|
3566
3646
|
return false;
|
|
3567
3647
|
};
|
|
3568
3648
|
const walk = (dir) => {
|
|
3569
|
-
if (!
|
|
3649
|
+
if (!existsSync14(dir) || shouldSkipDir(dir)) return;
|
|
3570
3650
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
3571
3651
|
const path = join13(dir, entry.name);
|
|
3572
3652
|
if (entry.isDirectory()) {
|
|
@@ -3578,7 +3658,7 @@ Fix: ${fix}` : message, ...prompt ? { prompt } : {} });
|
|
|
3578
3658
|
} else if (entry.isFile() && entry.name.endsWith(".md")) markdownFiles.push(path);
|
|
3579
3659
|
}
|
|
3580
3660
|
};
|
|
3581
|
-
if (!
|
|
3661
|
+
if (!existsSync14(docs)) usage(`docs directory not found: ${docs}`);
|
|
3582
3662
|
const validationPolicy = cloneValidationPolicy(memoryConfig.validation ?? DEFAULT_VALIDATION_POLICY);
|
|
3583
3663
|
const maxLines = options.maxLines ?? validationPolicy.markdown.maxLines;
|
|
3584
3664
|
const maxChars = options.maxChars ?? validationPolicy.markdown.maxChars;
|
|
@@ -3609,7 +3689,7 @@ README.md directory indexes, clear HTTP-status filenames such as RESPONSE_200_OK
|
|
|
3609
3689
|
walk(docs);
|
|
3610
3690
|
const dirSiblingMap = /* @__PURE__ */ new Map();
|
|
3611
3691
|
for (const f of markdownFiles) {
|
|
3612
|
-
const dir =
|
|
3692
|
+
const dir = dirname11(f);
|
|
3613
3693
|
if (!dirSiblingMap.has(dir)) dirSiblingMap.set(dir, []);
|
|
3614
3694
|
dirSiblingMap.get(dir).push(basename7(f));
|
|
3615
3695
|
}
|
|
@@ -3618,14 +3698,14 @@ README.md directory indexes, clear HTTP-status filenames such as RESPONSE_200_OK
|
|
|
3618
3698
|
const docsRel = rel(docs, file);
|
|
3619
3699
|
const rootRel = rel(root, file);
|
|
3620
3700
|
if (docsRel && !docsRel.startsWith("..") && !isUpperSnakeMarkdown(name)) addError(file, "FILE_NAMING", `Markdown file must be UPPER_SNAKE_CASE.md or README.md: ${name}`, null, "rename the markdown file to UPPER_SNAKE_CASE.md or README.md and update any links that reference it.");
|
|
3621
|
-
const content =
|
|
3701
|
+
const content = readFileSync14(file, "utf8");
|
|
3622
3702
|
fileCache.set(file, content);
|
|
3623
3703
|
const docArea = docsRel?.split("/")[0];
|
|
3624
3704
|
if (name !== "README.md" && isUpperSnakeMarkdown(name) && FILENAME_QUALITY_AREAS.has(docArea)) {
|
|
3625
3705
|
const fp = validationPolicy.markdown?.filename ?? {};
|
|
3626
3706
|
const allow = fp.allow ?? [];
|
|
3627
3707
|
if (!allow.some((p) => matchPathPattern(rootRel, p)) && (fp.warnNumberedSeries ?? true)) {
|
|
3628
|
-
const siblings = dirSiblingMap.get(
|
|
3708
|
+
const siblings = dirSiblingMap.get(dirname11(file)) ?? [];
|
|
3629
3709
|
if (isNumberedSeriesFilename(name, siblings)) {
|
|
3630
3710
|
const heading = extractFirstHeading(content);
|
|
3631
3711
|
const suggestion = suggestFilenameFromHeading(heading);
|
|
@@ -3667,7 +3747,7 @@ ${traceabilityExample()}`, block.line);
|
|
|
3667
3747
|
}
|
|
3668
3748
|
}
|
|
3669
3749
|
const byDir = /* @__PURE__ */ new Map();
|
|
3670
|
-
for (const file of markdownFiles) byDir.set(
|
|
3750
|
+
for (const file of markdownFiles) byDir.set(dirname11(file), [...byDir.get(dirname11(file)) ?? [], file]);
|
|
3671
3751
|
for (const [dir, files] of byDir) {
|
|
3672
3752
|
if (files.length > 1 && !files.some((file) => basename7(file) === "README.md")) addError(dir, "MISSING_README", "Directory with multiple markdown files must include README.md", null, "add a README.md in this directory that indexes the important markdown files and their purpose.");
|
|
3673
3753
|
}
|
|
@@ -3678,18 +3758,18 @@ ${traceabilityExample()}`, block.line);
|
|
|
3678
3758
|
}
|
|
3679
3759
|
if (options.linkCheck) {
|
|
3680
3760
|
for (const [file, content] of fileCache) {
|
|
3681
|
-
const fileDir =
|
|
3761
|
+
const fileDir = dirname11(file);
|
|
3682
3762
|
for (const { href, line } of extractLinks(content)) {
|
|
3683
3763
|
const hashIndex = href.indexOf("#");
|
|
3684
3764
|
const pathPart = hashIndex === -1 ? href : href.slice(0, hashIndex);
|
|
3685
3765
|
const anchor = hashIndex === -1 ? "" : href.slice(hashIndex + 1);
|
|
3686
|
-
const target = pathPart ?
|
|
3687
|
-
if (pathPart && !
|
|
3766
|
+
const target = pathPart ? resolve9(fileDir, pathPart) : file;
|
|
3767
|
+
if (pathPart && !existsSync14(target)) {
|
|
3688
3768
|
addError(file, "BROKEN_LINK", `Local link target does not exist: ${pathPart}`, line, "update the link target to an existing local file, create the intended file, or remove the stale link.");
|
|
3689
3769
|
continue;
|
|
3690
3770
|
}
|
|
3691
3771
|
if (anchor && extname6(target) === ".md") {
|
|
3692
|
-
const targetContent = fileCache.get(target) ?? (
|
|
3772
|
+
const targetContent = fileCache.get(target) ?? (existsSync14(target) ? readFileSync14(target, "utf8") : "");
|
|
3693
3773
|
if (targetContent && !extractAnchors(targetContent).has(decodeURIComponent(anchor))) addError(file, "BROKEN_ANCHOR", `Local anchor target does not exist: ${href}`, line, "update the fragment to a heading that exists in the target markdown file, or add the missing heading.");
|
|
3694
3774
|
}
|
|
3695
3775
|
}
|
|
@@ -3724,19 +3804,19 @@ ${traceabilityExample()}`, block.line);
|
|
|
3724
3804
|
if (options.includeLocalMemory) {
|
|
3725
3805
|
for (const area2 of ["plan", "archive/plan", "archive/report"]) {
|
|
3726
3806
|
const areaRoot = join13(docs, area2);
|
|
3727
|
-
if (!
|
|
3807
|
+
if (!existsSync14(areaRoot)) continue;
|
|
3728
3808
|
for (const entry of readdirSync3(areaRoot, { withFileTypes: true })) {
|
|
3729
3809
|
if (!entry.isDirectory()) continue;
|
|
3730
3810
|
if (!isKebabCase(entry.name)) addError(join13(areaRoot, entry.name), "SLUG_NAMING", `Archive/plan/report slug must be kebab-case: ${entry.name}`, null, "rename the task/report directory to kebab-case and update any README index links that reference it.");
|
|
3731
3811
|
const readme = join13(areaRoot, entry.name, "README.md");
|
|
3732
|
-
if (!
|
|
3812
|
+
if (!existsSync14(readme)) addError(readme, "MISSING_README", `Expected README.md in ${documentationRoot}/${area2}/${entry.name}/`, null, "add a README.md that summarizes the task/report and links any supporting files.");
|
|
3733
3813
|
}
|
|
3734
3814
|
}
|
|
3735
3815
|
}
|
|
3736
3816
|
const gitignore = join13(root, ".gitignore");
|
|
3737
|
-
if (!
|
|
3817
|
+
if (!existsSync14(gitignore)) addError(gitignore, "MISSING_GITIGNORE", "Expected .gitignore", null, `create .gitignore and include ${documentationRoot}/plan, ${documentationRoot}/archive, and .dotdotgod entries.`);
|
|
3738
3818
|
else {
|
|
3739
|
-
const content =
|
|
3819
|
+
const content = readFileSync14(gitignore, "utf8").split("\n").map((line) => line.trim());
|
|
3740
3820
|
for (const required of [`${documentationRoot}/plan`, `${documentationRoot}/archive`, CACHE_DIR]) if (!content.includes(required)) addError(gitignore, "MISSING_GITIGNORE_ENTRY", `Expected .gitignore entry: ${required}`, null, `add ${required} to .gitignore so local plans, archives, and cache files stay untracked.`);
|
|
3741
3821
|
}
|
|
3742
3822
|
if (options.json) console.log(JSON.stringify({ ok: errors.length === 0, errors, warnings }, null, 2));
|
|
@@ -3761,8 +3841,8 @@ Prompt: ${error.prompt}` : ""}`);
|
|
|
3761
3841
|
}
|
|
3762
3842
|
|
|
3763
3843
|
// packages/cli/src/commands/config.mjs
|
|
3764
|
-
import { existsSync as
|
|
3765
|
-
import { join as join14, resolve as
|
|
3844
|
+
import { existsSync as existsSync15, statSync as statSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3845
|
+
import { join as join14, resolve as resolve10 } from "node:path";
|
|
3766
3846
|
function parseConfigOptions(argv, usageKey = "config") {
|
|
3767
3847
|
const options = { root: ".", json: false, template: null };
|
|
3768
3848
|
let rootSet = false;
|
|
@@ -3780,7 +3860,7 @@ function parseConfigOptions(argv, usageKey = "config") {
|
|
|
3780
3860
|
} else if (!arg.startsWith("-")) usage(`Unexpected argument: ${arg}`, usageKey);
|
|
3781
3861
|
else usage(`Unknown option: ${arg}`, usageKey);
|
|
3782
3862
|
}
|
|
3783
|
-
options.root =
|
|
3863
|
+
options.root = resolve10(options.root);
|
|
3784
3864
|
return options;
|
|
3785
3865
|
}
|
|
3786
3866
|
function configInitError(options, code, message, path = null) {
|
|
@@ -3798,7 +3878,7 @@ function formatConfigOutput(payload) {
|
|
|
3798
3878
|
lines.push(`- path: ${payload.path ?? "none"}`);
|
|
3799
3879
|
lines.push(`- documentation root: ${payload.config.documentation.root}`);
|
|
3800
3880
|
lines.push(`- memory areas: ${payload.config.areas.length}`);
|
|
3801
|
-
lines.push(`- embedding: ${payload.embedding.profile.provider}/${payload.embedding.profile.model} (${payload.embedding.source})`);
|
|
3881
|
+
lines.push(`- embedding: ${payload.embedding.profile ? `${payload.embedding.profile.provider}/${payload.embedding.profile.model}` : "not configured; keyword search"} (${payload.embedding.source})`);
|
|
3802
3882
|
lines.push(`- traceability required: ${(payload.config.traceability.required ?? []).join(", ") || "none"}`);
|
|
3803
3883
|
lines.push(`- traceability exclude: ${(payload.config.traceability.exclude ?? []).join(", ") || "none"}`);
|
|
3804
3884
|
lines.push("- traceability keys:");
|
|
@@ -3822,17 +3902,17 @@ function runConfig(argv) {
|
|
|
3822
3902
|
const isInit = argv[0] === "init";
|
|
3823
3903
|
const options = parseConfigOptions(isInit ? argv.slice(1) : argv, isInit ? "config init" : "config");
|
|
3824
3904
|
if (isInit) {
|
|
3825
|
-
if (!
|
|
3905
|
+
if (!existsSync15(options.root)) configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
|
|
3826
3906
|
try {
|
|
3827
3907
|
if (!statSync6(options.root).isDirectory()) configInitError(options, "ROOT_NOT_DIRECTORY", `Project root is not a directory: ${options.root}`);
|
|
3828
3908
|
} catch {
|
|
3829
3909
|
configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
|
|
3830
3910
|
}
|
|
3831
3911
|
const target = join14(options.root, "dotdotgod.config.json");
|
|
3832
|
-
if (
|
|
3912
|
+
if (existsSync15(target)) configInitError(options, "CONFIG_EXISTS", "dotdotgod.config.json already exists.", target);
|
|
3833
3913
|
const template = resolveInitializationTemplate(options.template);
|
|
3834
3914
|
if (!template.ok) configInitError(options, template.error.code, template.error.message, template.error.path ?? target);
|
|
3835
|
-
|
|
3915
|
+
writeFileSync5(target, template.text);
|
|
3836
3916
|
const payload2 = { ok: true, command: "config init", root: options.root, path: target, created: true, template: { name: template.name, source: template.source, selectedBy: template.selectedBy } };
|
|
3837
3917
|
if (options.json) console.log(JSON.stringify(payload2, null, 2));
|
|
3838
3918
|
else console.log(`dotdotgod config init: created ${target} from ${template.source}:${template.name} (${template.selectedBy})`);
|
|
@@ -3846,7 +3926,7 @@ function runConfig(argv) {
|
|
|
3846
3926
|
embedding = { source: resolved.source, path: resolved.path, profile: sanitizeEmbeddingProfile(resolved.profile) };
|
|
3847
3927
|
} catch (error) {
|
|
3848
3928
|
errors.push({ file: "embedding config", code: "EMBEDDING_CONFIG_INVALID", message: error instanceof Error ? error.message : String(error) });
|
|
3849
|
-
embedding = { source: "invalid", path: null, profile:
|
|
3929
|
+
embedding = { source: "invalid", path: null, profile: null };
|
|
3850
3930
|
}
|
|
3851
3931
|
const payload = { ok: errors.length === 0, command: "config", root: options.root, source: config.source ?? "default", path: config.source && config.source !== "default" ? join14(options.root, config.source) : null, embedding, config: memoryConfigSummary(config), errors };
|
|
3852
3932
|
if (options.json) console.log(JSON.stringify(payload, null, 2));
|
|
@@ -3855,10 +3935,10 @@ function runConfig(argv) {
|
|
|
3855
3935
|
}
|
|
3856
3936
|
|
|
3857
3937
|
// packages/cli/src/commands/query.mjs
|
|
3858
|
-
import { resolve as
|
|
3938
|
+
import { resolve as resolve11 } from "node:path";
|
|
3859
3939
|
|
|
3860
3940
|
// packages/cli/src/query/embedding-runtime.mjs
|
|
3861
|
-
import { existsSync as
|
|
3941
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync15, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
3862
3942
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
3863
3943
|
import { createRequire as createRequire2 } from "node:module";
|
|
3864
3944
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -3891,7 +3971,7 @@ function embeddingRuntimeStatus(options = {}) {
|
|
|
3891
3971
|
const require3 = createRequire2(join15(root, "package.json"));
|
|
3892
3972
|
require3.resolve(EMBEDDING_RUNTIME_PACKAGE);
|
|
3893
3973
|
const packagePath = join15(root, "node_modules", ...EMBEDDING_RUNTIME_PACKAGE.split("/"), "package.json");
|
|
3894
|
-
const version = JSON.parse(
|
|
3974
|
+
const version = JSON.parse(readFileSync15(packagePath, "utf8")).version;
|
|
3895
3975
|
return { ok: true, installed: true, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: version, location: root };
|
|
3896
3976
|
} catch {
|
|
3897
3977
|
return { ok: true, installed: false, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: null, location: root };
|
|
@@ -3901,9 +3981,9 @@ function installEmbeddingRuntime(options = {}) {
|
|
|
3901
3981
|
if (options.confirm !== true) throw new Error("Embedding runtime installation requires explicit confirmation.");
|
|
3902
3982
|
const root = embeddingRuntimeRoot(options);
|
|
3903
3983
|
const lock = join15(root, ".installing");
|
|
3904
|
-
if (
|
|
3905
|
-
|
|
3906
|
-
|
|
3984
|
+
if (existsSync16(lock)) throw new Error("Embedding runtime installation is already in progress.");
|
|
3985
|
+
mkdirSync5(root, { recursive: true });
|
|
3986
|
+
writeFileSync6(lock, `${process.pid}
|
|
3907
3987
|
`, { flag: "wx" });
|
|
3908
3988
|
try {
|
|
3909
3989
|
const spawn = options.spawnImpl ?? spawnSync2;
|
|
@@ -4018,13 +4098,14 @@ async function createEmbedder(profile) {
|
|
|
4018
4098
|
return withInputFormat(embedBatch, profile);
|
|
4019
4099
|
}
|
|
4020
4100
|
async function resolveEmbedder(root, options = {}) {
|
|
4021
|
-
const resolved = options.profile ? { profile: options.profile, source: options.source ?? "injected", path: null } : resolveEmbeddingProfile(root, options);
|
|
4101
|
+
const resolved = options.profile || options.embed ? { profile: options.profile ?? DEFAULT_EMBEDDING_PROFILE, source: options.source ?? "injected", path: null } : resolveEmbeddingProfile(root, options);
|
|
4102
|
+
if (!resolved.profile) throw new Error("Embedding is not configured. Set embedding in project or global configuration to use vector search.");
|
|
4022
4103
|
return { ...resolved, identity: embeddingProfileIdentity(resolved.profile), embed: options.embed ? withInputFormat(options.embed, resolved.profile) : await createEmbedder(resolved.profile) };
|
|
4023
4104
|
}
|
|
4024
4105
|
|
|
4025
4106
|
// packages/cli/src/query/store.mjs
|
|
4026
4107
|
import { createHash as createHash6 } from "node:crypto";
|
|
4027
|
-
import { existsSync as
|
|
4108
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync16, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
4028
4109
|
import { join as join16 } from "node:path";
|
|
4029
4110
|
var VECTOR_SCHEMA_VERSION = 2;
|
|
4030
4111
|
var VECTOR_DIMENSIONS = 384;
|
|
@@ -4050,13 +4131,13 @@ function vectorCachePaths(root) {
|
|
|
4050
4131
|
}
|
|
4051
4132
|
function readVectorCache(root, identity2 = null) {
|
|
4052
4133
|
const paths = vectorCachePaths(root);
|
|
4053
|
-
if (!
|
|
4134
|
+
if (!existsSync17(paths.manifest) || !existsSync17(paths.chunks) || !existsSync17(paths.embeddings)) return null;
|
|
4054
4135
|
try {
|
|
4055
|
-
const manifest = JSON.parse(
|
|
4136
|
+
const manifest = JSON.parse(readFileSync16(paths.manifest, "utf8"));
|
|
4056
4137
|
if (manifest.schemaVersion !== VECTOR_SCHEMA_VERSION || !Number.isInteger(manifest.dimensions) || manifest.dimensions < 1) return null;
|
|
4057
4138
|
if (identity2 && manifest.profileFingerprint !== profileFingerprint(identity2)) return null;
|
|
4058
|
-
const chunks =
|
|
4059
|
-
const buffer =
|
|
4139
|
+
const chunks = readFileSync16(paths.chunks, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
4140
|
+
const buffer = readFileSync16(paths.embeddings);
|
|
4060
4141
|
if (buffer.byteLength !== chunks.length * manifest.dimensions * 4) return null;
|
|
4061
4142
|
const vectors = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4);
|
|
4062
4143
|
if (!isValidNormalizedVectorValues(vectors, manifest.dimensions)) return null;
|
|
@@ -4067,14 +4148,14 @@ function readVectorCache(root, identity2 = null) {
|
|
|
4067
4148
|
}
|
|
4068
4149
|
function atomicWrite(path, data) {
|
|
4069
4150
|
const temporary = `${path}.tmp-${process.pid}`;
|
|
4070
|
-
|
|
4151
|
+
writeFileSync7(temporary, data);
|
|
4071
4152
|
renameSync2(temporary, path);
|
|
4072
4153
|
}
|
|
4073
4154
|
function writeVectorCache(root, chunks, vectors, metadata = {}) {
|
|
4074
4155
|
const paths = vectorCachePaths(root);
|
|
4075
4156
|
const dimensions = metadata.dimensions;
|
|
4076
4157
|
if (!Number.isInteger(dimensions) || dimensions < 1) throw new Error("Vector dimensions must be a positive integer.");
|
|
4077
|
-
|
|
4158
|
+
mkdirSync6(paths.directory, { recursive: true });
|
|
4078
4159
|
if (vectors.length !== chunks.length * dimensions) throw new Error("Vector count does not match chunk metadata.");
|
|
4079
4160
|
if (!isValidNormalizedVectorValues(vectors, dimensions)) throw new Error("Vectors must contain finite normalized values.");
|
|
4080
4161
|
const manifest = { schemaVersion: VECTOR_SCHEMA_VERSION, chunks: chunks.length, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...metadata };
|
|
@@ -4133,11 +4214,6 @@ async function projectCorpus(root, scope) {
|
|
|
4133
4214
|
const outlines = scope === "docs" ? null : await buildOutlines(root);
|
|
4134
4215
|
return { chunks: [...docs, ...outlines ? outlineChunks(outlines) : []], outlines };
|
|
4135
4216
|
}
|
|
4136
|
-
function assertCodeEmbeddingConsent(root, options, chunks) {
|
|
4137
|
-
if (!chunks.some((chunk) => chunk.kind === "symbol")) return;
|
|
4138
|
-
const profile = options.profile ?? resolveEmbeddingProfile(root).profile;
|
|
4139
|
-
if (profile.provider !== "local" && options.allowCodeEmbedding !== true) throw new Error("Remote code-metadata embedding requires --allow-code-embedding; use --search keyword or --scope docs instead.");
|
|
4140
|
-
}
|
|
4141
4217
|
function attachRelationships(results, outlines) {
|
|
4142
4218
|
if (!outlines) return results;
|
|
4143
4219
|
const graph = addOutlineGraph({ nodes: [], edges: [] }, outlines);
|
|
@@ -4152,7 +4228,7 @@ var DEFAULT_LIMIT = 30;
|
|
|
4152
4228
|
var MAX_LIMIT = 100;
|
|
4153
4229
|
var EMBED_BATCH_SIZE = 16;
|
|
4154
4230
|
function parseQueryOptions(argv) {
|
|
4155
|
-
const options = { root: ".", query: "", limit: DEFAULT_LIMIT, json: false, scope: "all", search: "hybrid"
|
|
4231
|
+
const options = { root: ".", query: "", limit: DEFAULT_LIMIT, json: false, scope: "all", search: "hybrid" };
|
|
4156
4232
|
let rootSet = false;
|
|
4157
4233
|
const queryParts = [];
|
|
4158
4234
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -4164,14 +4240,13 @@ function parseQueryOptions(argv) {
|
|
|
4164
4240
|
} else if (arg === "--search") {
|
|
4165
4241
|
options.search = argv[++i];
|
|
4166
4242
|
if (!["hybrid", "keyword", "vector"].includes(options.search)) usage("--search must be hybrid, keyword or vector.", "query");
|
|
4167
|
-
} else if (arg === "--
|
|
4168
|
-
else if (arg === "--limit") {
|
|
4243
|
+
} else if (arg === "--limit") {
|
|
4169
4244
|
const value = Number(argv[++i]);
|
|
4170
4245
|
if (!Number.isInteger(value) || value < 1 || value > MAX_LIMIT) usage(`--limit must be an integer from 1 to ${MAX_LIMIT}.`, "query");
|
|
4171
4246
|
options.limit = value;
|
|
4172
4247
|
} else if (arg.startsWith("-")) usage(`Unknown option: ${arg}`, "query");
|
|
4173
4248
|
else if (!rootSet) {
|
|
4174
|
-
options.root =
|
|
4249
|
+
options.root = resolve11(arg);
|
|
4175
4250
|
rootSet = true;
|
|
4176
4251
|
} else queryParts.push(arg);
|
|
4177
4252
|
}
|
|
@@ -4251,9 +4326,18 @@ async function queryProject(root, query, options = {}) {
|
|
|
4251
4326
|
let vector = [];
|
|
4252
4327
|
let index;
|
|
4253
4328
|
let vectorError;
|
|
4254
|
-
|
|
4329
|
+
let configured = false;
|
|
4330
|
+
if (search !== "keyword") {
|
|
4331
|
+
try {
|
|
4332
|
+
configured = Boolean(options.profile || options.embed || resolveEmbeddingProfile(root, options).profile);
|
|
4333
|
+
} catch (error) {
|
|
4334
|
+
if (search === "vector") throw error;
|
|
4335
|
+
vectorError = error.message;
|
|
4336
|
+
}
|
|
4337
|
+
if (!configured && !vectorError && search === "vector") throw new Error("Embedding is not configured. Set embedding in project or global configuration to use vector search.");
|
|
4338
|
+
}
|
|
4339
|
+
if (search !== "keyword" && configured && corpus.chunks.length) {
|
|
4255
4340
|
try {
|
|
4256
|
-
assertCodeEmbeddingConsent(root, options, corpus.chunks);
|
|
4257
4341
|
index = await buildVectorIndex(root, { ...options, chunks: corpus.chunks });
|
|
4258
4342
|
const [queryVector] = await index.embedding.embed([query], "query");
|
|
4259
4343
|
vector = index.chunks.map((chunk, position) => ({ ...chunk, vectorScore: cosineScore(queryVector, index.vectors, position * index.manifest.dimensions, index.manifest.dimensions) })).sort((a, b) => b.vectorScore - a.vectorScore || a.id.localeCompare(b.id));
|
|
@@ -4304,7 +4388,7 @@ async function runQuery(argv) {
|
|
|
4304
4388
|
}
|
|
4305
4389
|
|
|
4306
4390
|
// packages/cli/src/commands/embedding.mjs
|
|
4307
|
-
import { resolve as
|
|
4391
|
+
import { resolve as resolve12 } from "node:path";
|
|
4308
4392
|
function parse(argv) {
|
|
4309
4393
|
const [action2 = "status", ...rest] = argv;
|
|
4310
4394
|
let root = ".";
|
|
@@ -4316,12 +4400,18 @@ function parse(argv) {
|
|
|
4316
4400
|
else if (!arg.startsWith("-") && root === ".") root = arg;
|
|
4317
4401
|
else usage(`Unknown embedding option: ${arg}`, `embedding ${action2}`);
|
|
4318
4402
|
}
|
|
4319
|
-
return { action: action2, root:
|
|
4403
|
+
return { action: action2, root: resolve12(root), json: json2, confirm };
|
|
4320
4404
|
}
|
|
4321
4405
|
function runEmbedding(argv, options = {}) {
|
|
4322
4406
|
const parsed = parse(argv);
|
|
4323
4407
|
if (!["status", "install"].includes(parsed.action)) usage(`Unknown embedding command: ${parsed.action}`, "embedding");
|
|
4324
4408
|
const resolved = resolveEmbeddingProfile(parsed.root, options);
|
|
4409
|
+
if (!resolved.profile) {
|
|
4410
|
+
if (parsed.action === "install") throw new Error("Configure an explicit local embedding profile before installing its runtime.");
|
|
4411
|
+
const result2 = { ok: true, command: "embedding status", provider: null, required: false, installed: false, installAvailable: false, source: "not-configured" };
|
|
4412
|
+
console.log(parsed.json ? JSON.stringify(result2, null, 2) : "Embedding is not configured; keyword search is used.");
|
|
4413
|
+
return result2;
|
|
4414
|
+
}
|
|
4325
4415
|
if (resolved.profile.provider !== "local") {
|
|
4326
4416
|
const result2 = { ok: true, command: `embedding ${parsed.action}`, provider: resolved.profile.provider, required: false, installed: false, installAvailable: false };
|
|
4327
4417
|
console.log(parsed.json ? JSON.stringify(result2, null, 2) : "Local embedding runtime is not required for the configured remote provider.");
|
|
@@ -4336,11 +4426,11 @@ function runEmbedding(argv, options = {}) {
|
|
|
4336
4426
|
}
|
|
4337
4427
|
|
|
4338
4428
|
// packages/cli/src/commands/map.mjs
|
|
4339
|
-
import { resolve as
|
|
4429
|
+
import { resolve as resolve14 } from "node:path";
|
|
4340
4430
|
|
|
4341
4431
|
// packages/cli/src/memory/documentation-map.mjs
|
|
4342
|
-
import { existsSync as
|
|
4343
|
-
import { join as join17, resolve as
|
|
4432
|
+
import { existsSync as existsSync18, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
|
|
4433
|
+
import { join as join17, resolve as resolve13 } from "node:path";
|
|
4344
4434
|
var SKIPPED_DIRECTORIES2 = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
|
|
4345
4435
|
function node() {
|
|
4346
4436
|
return { directories: /* @__PURE__ */ new Map(), files: [] };
|
|
@@ -4420,8 +4510,8 @@ function discoverMarkdown(root, documentationRoot, exclude) {
|
|
|
4420
4510
|
return paths.sort();
|
|
4421
4511
|
}
|
|
4422
4512
|
function buildDocumentationMap(projectRoot = ".", { depth = 5 } = {}) {
|
|
4423
|
-
const root =
|
|
4424
|
-
if (!
|
|
4513
|
+
const root = resolve13(projectRoot);
|
|
4514
|
+
if (!existsSync18(root)) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
|
|
4425
4515
|
try {
|
|
4426
4516
|
if (!statSync7(root).isDirectory()) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
|
|
4427
4517
|
} catch {
|
|
@@ -4469,7 +4559,7 @@ function parseMapOptions(argv) {
|
|
|
4469
4559
|
} else if (!arg.startsWith("-")) fail(options.json, "UNEXPECTED_ARGUMENT", `Unexpected argument: ${arg}`);
|
|
4470
4560
|
else fail(options.json, "UNKNOWN_OPTION", `Unknown option: ${arg}`);
|
|
4471
4561
|
}
|
|
4472
|
-
options.root =
|
|
4562
|
+
options.root = resolve14(options.root);
|
|
4473
4563
|
return options;
|
|
4474
4564
|
}
|
|
4475
4565
|
function runMap(argv) {
|
|
@@ -4481,7 +4571,7 @@ function runMap(argv) {
|
|
|
4481
4571
|
}
|
|
4482
4572
|
|
|
4483
4573
|
// packages/cli/src/reference/resolve.mjs
|
|
4484
|
-
import { resolve as
|
|
4574
|
+
import { resolve as resolve17 } from "node:path";
|
|
4485
4575
|
|
|
4486
4576
|
// node_modules/.pnpm/leiden-ts@0.1.0/node_modules/leiden-ts/dist/index.js
|
|
4487
4577
|
var GraphValidationError = class extends Error {
|
|
@@ -5852,8 +5942,8 @@ function compareImpactItems(seeds) {
|
|
|
5852
5942
|
}
|
|
5853
5943
|
|
|
5854
5944
|
// packages/cli/src/impact/references.mjs
|
|
5855
|
-
import { readFileSync as
|
|
5856
|
-
import { extname as extname7, resolve as
|
|
5945
|
+
import { readFileSync as readFileSync17, statSync as statSync8 } from "node:fs";
|
|
5946
|
+
import { extname as extname7, resolve as resolve15 } from "node:path";
|
|
5857
5947
|
var WARNING_LIMIT = 20;
|
|
5858
5948
|
var edgeKey = (source, target, relation) => `${source}\0${target}\0${relation}`;
|
|
5859
5949
|
function prepareImpactReferences(index, root, changedPaths) {
|
|
@@ -5865,10 +5955,10 @@ function prepareImpactReferences(index, root, changedPaths) {
|
|
|
5865
5955
|
if (checks.has(path)) return checks.get(path);
|
|
5866
5956
|
let result;
|
|
5867
5957
|
try {
|
|
5868
|
-
const absolute =
|
|
5958
|
+
const absolute = resolve15(root, path);
|
|
5869
5959
|
const stat = statSync8(absolute);
|
|
5870
5960
|
result = { status: "exists" };
|
|
5871
|
-
if (stat.isFile() && extname7(absolute) === ".md") result.anchors = extractAnchors(
|
|
5961
|
+
if (stat.isFile() && extname7(absolute) === ".md") result.anchors = extractAnchors(readFileSync17(absolute, "utf8"));
|
|
5872
5962
|
} catch (error) {
|
|
5873
5963
|
result = { status: ["ENOENT", "ENOTDIR"].includes(error.code) ? "missing" : "unavailable" };
|
|
5874
5964
|
}
|
|
@@ -5930,15 +6020,15 @@ function prepareImpactReferences(index, root, changedPaths) {
|
|
|
5930
6020
|
}
|
|
5931
6021
|
|
|
5932
6022
|
// packages/cli/src/impact/vector-profile.mjs
|
|
5933
|
-
import { lstatSync as
|
|
5934
|
-
import { extname as extname8, relative as
|
|
6023
|
+
import { lstatSync as lstatSync4, readFileSync as readFileSync18, realpathSync as realpathSync3 } from "node:fs";
|
|
6024
|
+
import { extname as extname8, relative as relative7, resolve as resolve16, sep } from "node:path";
|
|
5935
6025
|
var MAX_VECTOR_PROFILE_FILE_BYTES = 64 * 1024;
|
|
5936
6026
|
var MAX_VECTOR_PROFILE_CHARS = 4e3;
|
|
5937
6027
|
var MAX_VECTOR_PROFILE_METADATA_ITEMS = 20;
|
|
5938
6028
|
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".c", ".cc", ".cpp", ".css", ".go", ".h", ".hpp", ".html", ".java", ".js", ".json", ".jsx", ".md", ".mjs", ".py", ".rb", ".rs", ".sh", ".ts", ".tsx", ".txt", ".yaml", ".yml"]);
|
|
5939
6029
|
var GENERATED_SEGMENTS = /* @__PURE__ */ new Set([".dotdotgod", "build", "coverage", "dist", "node_modules"]);
|
|
5940
6030
|
function insideRoot(root, absolute) {
|
|
5941
|
-
const normalizedRoot =
|
|
6031
|
+
const normalizedRoot = resolve16(root);
|
|
5942
6032
|
return absolute === normalizedRoot || absolute.startsWith(`${normalizedRoot}${sep}`);
|
|
5943
6033
|
}
|
|
5944
6034
|
function normalizeChangedPath(path) {
|
|
@@ -5949,17 +6039,17 @@ function normalizeChangedPath(path) {
|
|
|
5949
6039
|
function canonicalizeChangedPath(root, path) {
|
|
5950
6040
|
const normalized = normalizeChangedPath(path);
|
|
5951
6041
|
if (!normalized) return null;
|
|
5952
|
-
const rootAbsolute =
|
|
5953
|
-
const absolute =
|
|
6042
|
+
const rootAbsolute = resolve16(root);
|
|
6043
|
+
const absolute = resolve16(rootAbsolute, normalized);
|
|
5954
6044
|
if (!insideRoot(rootAbsolute, absolute)) return normalized;
|
|
5955
6045
|
try {
|
|
5956
|
-
const canonicalRoot =
|
|
5957
|
-
const canonicalAbsolute =
|
|
6046
|
+
const canonicalRoot = realpathSync3(rootAbsolute);
|
|
6047
|
+
const canonicalAbsolute = realpathSync3(absolute);
|
|
5958
6048
|
if (!insideRoot(canonicalRoot, canonicalAbsolute)) return normalized;
|
|
5959
|
-
const canonical =
|
|
6049
|
+
const canonical = relative7(canonicalRoot, canonicalAbsolute).replaceAll("\\", "/");
|
|
5960
6050
|
return canonical || normalized;
|
|
5961
6051
|
} catch {
|
|
5962
|
-
return
|
|
6052
|
+
return relative7(rootAbsolute, absolute).replaceAll("\\", "/") || normalized;
|
|
5963
6053
|
}
|
|
5964
6054
|
}
|
|
5965
6055
|
function unsafeProfilePath(path) {
|
|
@@ -5981,15 +6071,15 @@ function graphMetadata(graph, seedId) {
|
|
|
5981
6071
|
function safeTextPrefix(root, path) {
|
|
5982
6072
|
if (unsafeProfilePath(path) || !TEXT_EXTENSIONS.has(extname8(path).toLowerCase())) return "";
|
|
5983
6073
|
try {
|
|
5984
|
-
const absolute =
|
|
6074
|
+
const absolute = resolve16(root, path);
|
|
5985
6075
|
if (!insideRoot(root, absolute)) return "";
|
|
5986
|
-
const canonicalAbsolute =
|
|
5987
|
-
if (!insideRoot(
|
|
5988
|
-
const canonicalPath =
|
|
6076
|
+
const canonicalAbsolute = realpathSync3(absolute);
|
|
6077
|
+
if (!insideRoot(realpathSync3(root), canonicalAbsolute)) return "";
|
|
6078
|
+
const canonicalPath = relative7(realpathSync3(root), canonicalAbsolute).replaceAll("\\", "/");
|
|
5989
6079
|
if (canonicalPath !== path || unsafeProfilePath(canonicalPath) || !TEXT_EXTENSIONS.has(extname8(canonicalPath).toLowerCase())) return "";
|
|
5990
|
-
const stats =
|
|
6080
|
+
const stats = lstatSync4(canonicalAbsolute);
|
|
5991
6081
|
if (!stats.isFile() || stats.isSymbolicLink() || stats.size > MAX_VECTOR_PROFILE_FILE_BYTES) return "";
|
|
5992
|
-
const buffer =
|
|
6082
|
+
const buffer = readFileSync18(canonicalAbsolute);
|
|
5993
6083
|
if (buffer.includes(0)) return "";
|
|
5994
6084
|
const text = new TextDecoder("utf-8", { fatal: true }).decode(buffer);
|
|
5995
6085
|
return text.slice(0, MAX_VECTOR_PROFILE_CHARS);
|
|
@@ -6023,6 +6113,7 @@ async function buildVectorImpactOverlay(root, index, changedPaths, options = {})
|
|
|
6023
6113
|
const threshold = semantic.threshold ?? 0.5;
|
|
6024
6114
|
const topK = semantic.topKPerFile ?? 5;
|
|
6025
6115
|
try {
|
|
6116
|
+
if (!options.profile && !options.embed && !options.buildIndex && !resolveEmbeddingProfile(root, options).profile) return { status: "disabled", edges: [], diagnostics: { enabled: false, reason: "Embedding is not configured." } };
|
|
6026
6117
|
const vectorIndex = await (options.buildIndex ?? buildVectorIndex)(root, options);
|
|
6027
6118
|
const edges = [];
|
|
6028
6119
|
const normalizedChangedPaths = [...new Set(changedPaths.map((path) => canonicalizeChangedPath(root, path)).filter(Boolean))];
|
|
@@ -6209,7 +6300,7 @@ function buildCompactImpactReport(impact, limits = {}) {
|
|
|
6209
6300
|
}
|
|
6210
6301
|
|
|
6211
6302
|
// packages/cli/src/reference/extract.mjs
|
|
6212
|
-
import { basename as basename8, dirname as
|
|
6303
|
+
import { basename as basename8, dirname as dirname12, extname as extname9 } from "node:path";
|
|
6213
6304
|
var DEFAULT_REFERENCE_LIMIT = 5;
|
|
6214
6305
|
function extractBracketReferences(prompt = "") {
|
|
6215
6306
|
const refs = [];
|
|
@@ -6260,7 +6351,7 @@ function aliasEntriesForPath(path = "") {
|
|
|
6260
6351
|
const parts = withoutMd.split("/").filter(Boolean);
|
|
6261
6352
|
for (let i = 0; i < parts.length; i += 1) entries.push({ alias: parts.slice(i).join("/"), kind: "path-suffix" });
|
|
6262
6353
|
if (base === "README.md") {
|
|
6263
|
-
const dir =
|
|
6354
|
+
const dir = dirname12(path).replace(/\\/g, "/");
|
|
6264
6355
|
entries.push({ alias: basename8(dir), kind: "path" });
|
|
6265
6356
|
}
|
|
6266
6357
|
for (const alias of [path, withoutMd, base, stem]) entries.push({ alias, kind: "path" });
|
|
@@ -6390,7 +6481,7 @@ function parseReferenceOptions(argv, command) {
|
|
|
6390
6481
|
else filtered.push(arg);
|
|
6391
6482
|
}
|
|
6392
6483
|
const operands = filtered.filter((arg) => !arg.startsWith("-"));
|
|
6393
|
-
return { ...options, root:
|
|
6484
|
+
return { ...options, root: resolve17(operands[0] ?? "."), json: filtered.includes("--json"), rootArgv: operands.slice(1) };
|
|
6394
6485
|
}
|
|
6395
6486
|
function formatReferenceOutput(payload) {
|
|
6396
6487
|
const refreshNote = payload.metadata.cacheRefreshed ? ", refreshed" : "";
|
|
@@ -6439,13 +6530,13 @@ function runExpand(argv) {
|
|
|
6439
6530
|
}
|
|
6440
6531
|
|
|
6441
6532
|
// packages/cli/src/commands/traceability.mjs
|
|
6442
|
-
import { existsSync as
|
|
6443
|
-
import { join as join18, resolve as
|
|
6533
|
+
import { existsSync as existsSync19, readFileSync as readFileSync19, readdirSync as readdirSync5, writeFileSync as writeFileSync8 } from "node:fs";
|
|
6534
|
+
import { join as join18, resolve as resolve18 } from "node:path";
|
|
6444
6535
|
function collectDocsMarkdownFiles(root) {
|
|
6445
6536
|
const docs = join18(root, "docs");
|
|
6446
6537
|
const files = [];
|
|
6447
6538
|
const walk = (dir) => {
|
|
6448
|
-
if (!
|
|
6539
|
+
if (!existsSync19(dir)) return;
|
|
6449
6540
|
for (const entry of readdirSync5(dir, { withFileTypes: true })) {
|
|
6450
6541
|
const path = join18(dir, entry.name);
|
|
6451
6542
|
if (entry.isDirectory()) walk(path);
|
|
@@ -6472,7 +6563,7 @@ function parseTraceabilityOptions(argv) {
|
|
|
6472
6563
|
}
|
|
6473
6564
|
if (options.check && options.write) usage("Choose only one traceability links mode: --check or --write.", "traceability links");
|
|
6474
6565
|
options.check = options.check || !options.write;
|
|
6475
|
-
options.root =
|
|
6566
|
+
options.root = resolve18(options.root);
|
|
6476
6567
|
return options;
|
|
6477
6568
|
}
|
|
6478
6569
|
function runTraceability(argv) {
|
|
@@ -6483,7 +6574,7 @@ function runTraceability(argv) {
|
|
|
6483
6574
|
const errors = [...config.errors ?? []];
|
|
6484
6575
|
for (const file of files) {
|
|
6485
6576
|
if (options.write && errors.length > 0) break;
|
|
6486
|
-
const content =
|
|
6577
|
+
const content = readFileSync19(file, "utf8");
|
|
6487
6578
|
for (const error of validateTraceabilityLinksRegion(content, options.root, file)) errors.push(error);
|
|
6488
6579
|
const blocks = extractDotdotgodTraceabilityBlocks(content).filter((block) => !block.error);
|
|
6489
6580
|
if (blocks.length === 0) continue;
|
|
@@ -6492,7 +6583,7 @@ function runTraceability(argv) {
|
|
|
6492
6583
|
for (const error of result.errors ?? []) errors.push(error);
|
|
6493
6584
|
continue;
|
|
6494
6585
|
}
|
|
6495
|
-
if (options.write && result.changed)
|
|
6586
|
+
if (options.write && result.changed) writeFileSync8(file, result.content);
|
|
6496
6587
|
if (result.changed) results.push({ file: rel(options.root, file), changed: true });
|
|
6497
6588
|
}
|
|
6498
6589
|
const ok = errors.length === 0 && (!options.check || results.length === 0);
|
|
@@ -6513,8 +6604,8 @@ function runTraceability(argv) {
|
|
|
6513
6604
|
|
|
6514
6605
|
// packages/cli/src/graph-view/server.mjs
|
|
6515
6606
|
import { createServer } from "node:http";
|
|
6516
|
-
import { readFileSync as
|
|
6517
|
-
import { dirname as
|
|
6607
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
6608
|
+
import { dirname as dirname13, extname as extname10, join as join19 } from "node:path";
|
|
6518
6609
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6519
6610
|
|
|
6520
6611
|
// packages/cli/src/graph-view/payload.mjs
|
|
@@ -6626,7 +6717,7 @@ function buildImpactGraphPayload(index, impact) {
|
|
|
6626
6717
|
}
|
|
6627
6718
|
|
|
6628
6719
|
// packages/cli/src/graph-view/server.mjs
|
|
6629
|
-
var ASSET_ROOT = join19(
|
|
6720
|
+
var ASSET_ROOT = join19(dirname13(fileURLToPath2(import.meta.url)), "assets");
|
|
6630
6721
|
var CONTENT_TYPES = { ".css": "text/css; charset=utf-8", ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".map": "application/json; charset=utf-8" };
|
|
6631
6722
|
var VENDOR_FILES = {
|
|
6632
6723
|
"/vendor/sigma.js": new URL("../../node_modules/sigma/dist/sigma.min.js", import.meta.url),
|
|
@@ -6639,7 +6730,7 @@ function json(response, status, value) {
|
|
|
6639
6730
|
}
|
|
6640
6731
|
function asset(response, file) {
|
|
6641
6732
|
try {
|
|
6642
|
-
const body =
|
|
6733
|
+
const body = readFileSync20(file);
|
|
6643
6734
|
response.writeHead(200, { "content-type": CONTENT_TYPES[extname10(file)] ?? "application/octet-stream", "cache-control": "no-store" });
|
|
6644
6735
|
response.end(body);
|
|
6645
6736
|
} catch {
|
|
@@ -6676,16 +6767,16 @@ async function startImpactGraphServer({ root, changed, host = "127.0.0.1", port
|
|
|
6676
6767
|
}
|
|
6677
6768
|
}
|
|
6678
6769
|
if (VENDOR_FILES[url.pathname]) return asset(response, fileURLToPath2(VENDOR_FILES[url.pathname]));
|
|
6679
|
-
const
|
|
6680
|
-
if (!["index.html", "app.css", "app.js"].includes(
|
|
6770
|
+
const relative8 = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
6771
|
+
if (!["index.html", "app.css", "app.js"].includes(relative8)) {
|
|
6681
6772
|
response.writeHead(404);
|
|
6682
6773
|
return response.end("Not found");
|
|
6683
6774
|
}
|
|
6684
|
-
return asset(response, join19(ASSET_ROOT,
|
|
6775
|
+
return asset(response, join19(ASSET_ROOT, relative8));
|
|
6685
6776
|
});
|
|
6686
|
-
await new Promise((
|
|
6777
|
+
await new Promise((resolve19, reject) => {
|
|
6687
6778
|
server.once("error", reject);
|
|
6688
|
-
server.listen(port, host,
|
|
6779
|
+
server.listen(port, host, resolve19);
|
|
6689
6780
|
});
|
|
6690
6781
|
const address = server.address();
|
|
6691
6782
|
const actualPort = typeof address === "object" && address ? address.port : port;
|