soturail 0.2.1

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.
Files changed (149) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.js +54 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/bench.d.ts +31 -0
  7. package/dist/commands/bench.js +387 -0
  8. package/dist/commands/bench.js.map +1 -0
  9. package/dist/commands/dedupe.d.ts +2 -0
  10. package/dist/commands/dedupe.js +16 -0
  11. package/dist/commands/dedupe.js.map +1 -0
  12. package/dist/commands/doctor.d.ts +14 -0
  13. package/dist/commands/doctor.js +122 -0
  14. package/dist/commands/doctor.js.map +1 -0
  15. package/dist/commands/expand.d.ts +3 -0
  16. package/dist/commands/expand.js +23 -0
  17. package/dist/commands/expand.js.map +1 -0
  18. package/dist/commands/format.d.ts +6 -0
  19. package/dist/commands/format.js +48 -0
  20. package/dist/commands/format.js.map +1 -0
  21. package/dist/commands/hooks.d.ts +11 -0
  22. package/dist/commands/hooks.js +248 -0
  23. package/dist/commands/hooks.js.map +1 -0
  24. package/dist/commands/index.d.ts +3 -0
  25. package/dist/commands/index.js +28 -0
  26. package/dist/commands/index.js.map +1 -0
  27. package/dist/commands/ingest.d.ts +6 -0
  28. package/dist/commands/ingest.js +52 -0
  29. package/dist/commands/ingest.js.map +1 -0
  30. package/dist/commands/init.d.ts +7 -0
  31. package/dist/commands/init.js +179 -0
  32. package/dist/commands/init.js.map +1 -0
  33. package/dist/commands/memory.d.ts +39 -0
  34. package/dist/commands/memory.js +184 -0
  35. package/dist/commands/memory.js.map +1 -0
  36. package/dist/commands/native.d.ts +2 -0
  37. package/dist/commands/native.js +17 -0
  38. package/dist/commands/native.js.map +1 -0
  39. package/dist/commands/read.d.ts +8 -0
  40. package/dist/commands/read.js +87 -0
  41. package/dist/commands/read.js.map +1 -0
  42. package/dist/commands/rules.d.ts +5 -0
  43. package/dist/commands/rules.js +62 -0
  44. package/dist/commands/rules.js.map +1 -0
  45. package/dist/commands/run.d.ts +22 -0
  46. package/dist/commands/run.js +200 -0
  47. package/dist/commands/run.js.map +1 -0
  48. package/dist/commands/spec.d.ts +11 -0
  49. package/dist/commands/spec.js +217 -0
  50. package/dist/commands/spec.js.map +1 -0
  51. package/dist/commands/stats.d.ts +15 -0
  52. package/dist/commands/stats.js +82 -0
  53. package/dist/commands/stats.js.map +1 -0
  54. package/dist/compressors/agent-response-reducer.d.ts +13 -0
  55. package/dist/compressors/agent-response-reducer.js +160 -0
  56. package/dist/compressors/agent-response-reducer.js.map +1 -0
  57. package/dist/compressors/generic-stream.d.ts +6 -0
  58. package/dist/compressors/generic-stream.js +63 -0
  59. package/dist/compressors/generic-stream.js.map +1 -0
  60. package/dist/compressors/git-reducer.d.ts +2 -0
  61. package/dist/compressors/git-reducer.js +78 -0
  62. package/dist/compressors/git-reducer.js.map +1 -0
  63. package/dist/compressors/index.d.ts +10 -0
  64. package/dist/compressors/index.js +84 -0
  65. package/dist/compressors/index.js.map +1 -0
  66. package/dist/compressors/json-toon.d.ts +14 -0
  67. package/dist/compressors/json-toon.js +149 -0
  68. package/dist/compressors/json-toon.js.map +1 -0
  69. package/dist/compressors/test-reducer.d.ts +2 -0
  70. package/dist/compressors/test-reducer.js +50 -0
  71. package/dist/compressors/test-reducer.js.map +1 -0
  72. package/dist/core/cache-normalizer.d.ts +23 -0
  73. package/dist/core/cache-normalizer.js +170 -0
  74. package/dist/core/cache-normalizer.js.map +1 -0
  75. package/dist/core/config.d.ts +104 -0
  76. package/dist/core/config.js +186 -0
  77. package/dist/core/config.js.map +1 -0
  78. package/dist/core/dedupe-store.d.ts +17 -0
  79. package/dist/core/dedupe-store.js +44 -0
  80. package/dist/core/dedupe-store.js.map +1 -0
  81. package/dist/core/document-ingest.d.ts +15 -0
  82. package/dist/core/document-ingest.js +45 -0
  83. package/dist/core/document-ingest.js.map +1 -0
  84. package/dist/core/file-scanner.d.ts +47 -0
  85. package/dist/core/file-scanner.js +248 -0
  86. package/dist/core/file-scanner.js.map +1 -0
  87. package/dist/core/git.d.ts +2 -0
  88. package/dist/core/git.js +29 -0
  89. package/dist/core/git.js.map +1 -0
  90. package/dist/core/metrics-store.d.ts +20 -0
  91. package/dist/core/metrics-store.js +19 -0
  92. package/dist/core/metrics-store.js.map +1 -0
  93. package/dist/core/native-engine.d.ts +25 -0
  94. package/dist/core/native-engine.js +137 -0
  95. package/dist/core/native-engine.js.map +1 -0
  96. package/dist/core/native-runner-adapter.d.ts +18 -0
  97. package/dist/core/native-runner-adapter.js +60 -0
  98. package/dist/core/native-runner-adapter.js.map +1 -0
  99. package/dist/core/raw-store.d.ts +28 -0
  100. package/dist/core/raw-store.js +57 -0
  101. package/dist/core/raw-store.js.map +1 -0
  102. package/dist/core/response-policy.d.ts +11 -0
  103. package/dist/core/response-policy.js +14 -0
  104. package/dist/core/response-policy.js.map +1 -0
  105. package/dist/core/rule-extractor.d.ts +18 -0
  106. package/dist/core/rule-extractor.js +116 -0
  107. package/dist/core/rule-extractor.js.map +1 -0
  108. package/dist/core/rule-validator.d.ts +9 -0
  109. package/dist/core/rule-validator.js +85 -0
  110. package/dist/core/rule-validator.js.map +1 -0
  111. package/dist/core/safety-policy.d.ts +10 -0
  112. package/dist/core/safety-policy.js +69 -0
  113. package/dist/core/safety-policy.js.map +1 -0
  114. package/dist/core/token-estimator.d.ts +2 -0
  115. package/dist/core/token-estimator.js +7 -0
  116. package/dist/core/token-estimator.js.map +1 -0
  117. package/dist/types/index.d.ts +4 -0
  118. package/dist/types/index.js +2 -0
  119. package/dist/types/index.js.map +1 -0
  120. package/docs/architecture.md +47 -0
  121. package/docs/assets/screenshots/README.md +18 -0
  122. package/docs/assets/screenshots/init.svg +13 -0
  123. package/docs/assets/screenshots/run-expand.svg +13 -0
  124. package/docs/assets/screenshots/stats.svg +11 -0
  125. package/docs/assets/soturail-fox.svg +17 -0
  126. package/docs/assets/soturail-icon.svg +10 -0
  127. package/docs/benchmarking.md +34 -0
  128. package/docs/branding.md +14 -0
  129. package/docs/hooks/claude.md +24 -0
  130. package/docs/hooks/codex.md +5 -0
  131. package/docs/hooks/cursor.md +5 -0
  132. package/docs/hooks/gemini.md +5 -0
  133. package/docs/hooks/host-capabilities.json +22 -0
  134. package/docs/hooks.md +15 -0
  135. package/docs/knowledge-to-rules.md +23 -0
  136. package/docs/metrics.md +35 -0
  137. package/docs/mvp.md +21 -0
  138. package/docs/native-runner.md +52 -0
  139. package/docs/prompt-caching.md +27 -0
  140. package/docs/pt-BR/visao-geral.md +26 -0
  141. package/docs/public-roadmap.md +20 -0
  142. package/docs/reducers.md +19 -0
  143. package/docs/release-checklist.md +13 -0
  144. package/docs/response-compression.md +15 -0
  145. package/docs/rules.md +23 -0
  146. package/docs/security-model.md +32 -0
  147. package/docs/spec-driven-workflow.md +22 -0
  148. package/docs/usage.md +64 -0
  149. package/package.json +71 -0
@@ -0,0 +1,50 @@
1
+ export function isTestCommand(command, raw) {
2
+ return /\b(npm|pnpm|yarn|bun)\s+(run\s+)?test\b/i.test(command) ||
3
+ /\b(vitest|jest|pytest|mocha|mvn|gradle)\b/i.test(command) ||
4
+ /\b(FAIL|FAILED|AssertionError|Traceback|Tests?:|BUILD FAILED|There (?:was|were) \d+ failure|Surefire)\b/i.test(raw);
5
+ }
6
+ const IMPORTANT = /\b(FAIL|FAILED|Error|AssertionError|Expected|Received|Traceback|Caused by|at\s+.+:\d+:\d+|\.test\.|\.spec\.|tests?\/|src[\\/].+:\d+|FAILED TESTS|BUILD FAILED|Surefire|Maven|Gradle|passed|failed|skipped|summary|Test Files|Tests|Snapshots|Time)\b/i;
7
+ const NOISE = /^\s*(PASS|✓|✔|ok)\s+/i;
8
+ export function reduceTestOutput(raw, rawId) {
9
+ const lines = raw.split(/\r?\n/);
10
+ const important = [];
11
+ const summary = [];
12
+ const seen = new Set();
13
+ let passedNoise = 0;
14
+ for (const line of lines) {
15
+ const trimmed = line.trim();
16
+ if (trimmed.length === 0) {
17
+ continue;
18
+ }
19
+ if (NOISE.test(trimmed)) {
20
+ passedNoise += 1;
21
+ continue;
22
+ }
23
+ const key = trimmed.replace(/\s+/g, " ");
24
+ if (seen.has(key)) {
25
+ continue;
26
+ }
27
+ seen.add(key);
28
+ if (IMPORTANT.test(trimmed)) {
29
+ important.push(line);
30
+ }
31
+ if (/\b(Test Files|Tests|Snapshots|Time|passed|failed|skipped|errors?)\b/i.test(trimmed)) {
32
+ summary.push(line);
33
+ }
34
+ }
35
+ const sections = [
36
+ "Test Output Summary",
37
+ `Raw log: soturail expand ${rawId}`,
38
+ "",
39
+ "Summary counts:",
40
+ ...(summary.length > 0 ? summary.slice(-20) : ["- no explicit test summary detected"]),
41
+ "",
42
+ "Failures, assertions, stack traces, and file paths:",
43
+ ...(important.length > 0 ? important.slice(0, 160) : ["- no failure-looking lines detected"])
44
+ ];
45
+ if (passedNoise > 0) {
46
+ sections.push("", `[Collapsed ${passedNoise} repeated passed/progress lines. Recover with: soturail expand ${rawId}]`);
47
+ }
48
+ return `${sections.join("\n").trimEnd()}\n`;
49
+ }
50
+ //# sourceMappingURL=test-reducer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-reducer.js","sourceRoot":"","sources":["../../src/compressors/test-reducer.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,GAAW;IACxD,OAAO,0CAA0C,CAAC,IAAI,CAAC,OAAO,CAAC;QAC7D,4CAA4C,CAAC,IAAI,CAAC,OAAO,CAAC;QAC1D,0GAA0G,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzH,CAAC;AAED,MAAM,SAAS,GACb,uPAAuP,CAAC;AAC1P,MAAM,KAAK,GAAG,uBAAuB,CAAC;AAEtC,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,KAAa;IACzD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,WAAW,IAAI,CAAC,CAAC;YACjB,SAAS;QACX,CAAC;QAED,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACzC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEd,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QAED,IAAI,sEAAsE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACzF,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG;QACf,qBAAqB;QACrB,4BAA4B,KAAK,EAAE;QACnC,EAAE;QACF,iBAAiB;QACjB,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC;QACtF,EAAE;QACF,qDAAqD;QACrD,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC;KAC9F,CAAC;IAEF,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,WAAW,kEAAkE,KAAK,GAAG,CAAC,CAAC;IACzH,CAAC;IAED,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,23 @@
1
+ export type CacheBlockType = "static_header" | "governance" | "config" | "repo_map" | "approved_specs" | "approved_memory";
2
+ export interface CacheBlock {
3
+ block_id: string;
4
+ type: CacheBlockType;
5
+ source_path: string;
6
+ sha256: string;
7
+ stable_order: number;
8
+ token_estimate: number;
9
+ updated_at: string;
10
+ }
11
+ export interface CachePayloadSection {
12
+ label: string;
13
+ stable: boolean;
14
+ content: string;
15
+ }
16
+ export interface CachePayloadResult {
17
+ blocks: CacheBlock[];
18
+ sections: CachePayloadSection[];
19
+ payload: string;
20
+ estimated_cache_stability_score: number;
21
+ }
22
+ export declare function buildCachePayload(root?: string, dynamicFooter?: string): Promise<CachePayloadResult>;
23
+ export declare function readCacheBlocks(root?: string): Promise<CacheBlock[]>;
@@ -0,0 +1,170 @@
1
+ import { createHash } from "node:crypto";
2
+ import { promises as fs } from "node:fs";
3
+ import path from "node:path";
4
+ import { appendJsonl, getWorkspacePaths, readJsonl } from "./config.js";
5
+ import { estimateTokens } from "./token-estimator.js";
6
+ function sha256(text) {
7
+ return createHash("sha256").update(text).digest("hex");
8
+ }
9
+ function normalizeSourcePath(root, sourcePath) {
10
+ if (sourcePath.startsWith("internal://")) {
11
+ return sourcePath;
12
+ }
13
+ return path.normalize(path.relative(path.resolve(root), path.resolve(sourcePath))).replace(/\\/g, "/");
14
+ }
15
+ function blockIdFor(type, sourcePath) {
16
+ const safe = sourcePath
17
+ .replace(/^\.soturail\//, "")
18
+ .replace(/[^\w.-]+/g, "-")
19
+ .replace(/^-+|-+$/g, "")
20
+ .slice(0, 80);
21
+ return `${type}-${safe || "main"}`;
22
+ }
23
+ async function readIfExists(filePath) {
24
+ try {
25
+ return await fs.readFile(filePath, "utf8");
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ async function listSpecFiles(specsDir) {
32
+ const results = [];
33
+ try {
34
+ const entries = await fs.readdir(specsDir, { withFileTypes: true });
35
+ for (const entry of entries) {
36
+ const absolute = path.resolve(specsDir, entry.name);
37
+ if (entry.isDirectory()) {
38
+ results.push(...(await listSpecFiles(absolute)));
39
+ }
40
+ else if (entry.isFile() && entry.name === "spec.md") {
41
+ results.push(absolute);
42
+ }
43
+ }
44
+ }
45
+ catch {
46
+ return [];
47
+ }
48
+ return results.sort((left, right) => left.localeCompare(right));
49
+ }
50
+ async function collectStableSources(root) {
51
+ const paths = getWorkspacePaths(root);
52
+ const sources = [
53
+ {
54
+ type: "static_header",
55
+ sourcePath: "internal://soturail/static-header",
56
+ stableOrder: 0,
57
+ content: [
58
+ "# SotuRail Static Header",
59
+ "Local-first Context OS payload. Stable blocks are ordered before dynamic session data.",
60
+ "Metrics are local estimates unless imported provider metadata explicitly exists."
61
+ ].join("\n")
62
+ }
63
+ ];
64
+ const governanceFiles = ["AGENTS.md", "CLAUDE.md", "GEMINI.md"];
65
+ const governanceContent = [];
66
+ for (const file of governanceFiles) {
67
+ const absolute = path.resolve(root, file);
68
+ const content = await readIfExists(absolute);
69
+ if (content) {
70
+ governanceContent.push(`## ${file}\n${content.trim()}`);
71
+ }
72
+ }
73
+ if (governanceContent.length > 0) {
74
+ sources.push({
75
+ type: "governance",
76
+ sourcePath: path.resolve(root, "AGENTS.md"),
77
+ stableOrder: 10,
78
+ content: governanceContent.join("\n\n")
79
+ });
80
+ }
81
+ const config = await readIfExists(paths.configFile);
82
+ if (config) {
83
+ sources.push({ type: "config", sourcePath: paths.configFile, stableOrder: 20, content: config.trim() });
84
+ }
85
+ const repoMapPath = path.resolve(paths.indexesDir, "repo-map.json");
86
+ const repoMap = await readIfExists(repoMapPath);
87
+ if (repoMap) {
88
+ sources.push({ type: "repo_map", sourcePath: repoMapPath, stableOrder: 30, content: repoMap.trim() });
89
+ }
90
+ const specFiles = await listSpecFiles(paths.specsDir);
91
+ specFiles.forEach((specFile, index) => {
92
+ sources.push({
93
+ type: "approved_specs",
94
+ sourcePath: specFile,
95
+ stableOrder: 40 + index,
96
+ content: ""
97
+ });
98
+ });
99
+ for (const source of sources.filter((item) => item.type === "approved_specs")) {
100
+ const content = await readIfExists(source.sourcePath);
101
+ if (content && /\b(Status:\s*Approved|\[approved\]|approved:\s*true)\b/i.test(content)) {
102
+ source.content = content.trim();
103
+ }
104
+ }
105
+ const legacyMemoryRecords = await readJsonl(paths.memoryFile);
106
+ const approvedMemoryRecords = await readJsonl(paths.memoryApprovedFile);
107
+ const memoryRecords = [...legacyMemoryRecords, ...approvedMemoryRecords];
108
+ const approvedMemory = memoryRecords.filter((record) => {
109
+ const content = typeof record.content === "string" ? record.content : "";
110
+ return record.approved === true || /\[approved\]/i.test(content);
111
+ });
112
+ if (approvedMemory.length > 0) {
113
+ sources.push({
114
+ type: "approved_memory",
115
+ sourcePath: paths.memoryApprovedFile,
116
+ stableOrder: 60,
117
+ content: approvedMemory.map((record) => JSON.stringify(record)).join("\n")
118
+ });
119
+ }
120
+ return sources.filter((source) => source.content.trim().length > 0).sort((left, right) => left.stableOrder - right.stableOrder);
121
+ }
122
+ export async function buildCachePayload(root = process.cwd(), dynamicFooter = "Dynamic footer intentionally empty.") {
123
+ const paths = getWorkspacePaths(root);
124
+ const updatedAt = new Date().toISOString();
125
+ const stableSources = await collectStableSources(root);
126
+ const blocks = stableSources.map((source) => {
127
+ const normalizedSource = normalizeSourcePath(root, source.sourcePath);
128
+ return {
129
+ block_id: blockIdFor(source.type, normalizedSource),
130
+ type: source.type,
131
+ source_path: normalizedSource,
132
+ sha256: sha256(source.content),
133
+ stable_order: source.stableOrder,
134
+ token_estimate: estimateTokens(source.content),
135
+ updated_at: updatedAt
136
+ };
137
+ });
138
+ await fs.mkdir(path.dirname(paths.cacheBlocks), { recursive: true });
139
+ await fs.writeFile(paths.cacheBlocks, "", "utf8");
140
+ for (const block of blocks) {
141
+ await appendJsonl(paths.cacheBlocks, block);
142
+ }
143
+ const stableSections = stableSources.map((source) => ({
144
+ label: source.type,
145
+ stable: true,
146
+ content: source.content
147
+ }));
148
+ const sections = [
149
+ ...stableSections,
150
+ {
151
+ label: "dynamic-footer",
152
+ stable: false,
153
+ content: dynamicFooter
154
+ }
155
+ ];
156
+ const payload = sections
157
+ .map((section) => `<!-- soturail:${section.stable ? "stable" : "dynamic"}:${section.label} -->\n${section.content.trim()}`)
158
+ .join("\n\n");
159
+ const stableTokens = blocks.reduce((sum, block) => sum + block.token_estimate, 0);
160
+ const dynamicTokens = estimateTokens(dynamicFooter);
161
+ const estimated_cache_stability_score = stableTokens + dynamicTokens === 0
162
+ ? 0
163
+ : Number((stableTokens / (stableTokens + dynamicTokens)).toFixed(4));
164
+ return { blocks, sections, payload: `${payload}\n`, estimated_cache_stability_score };
165
+ }
166
+ export async function readCacheBlocks(root = process.cwd()) {
167
+ const paths = getWorkspacePaths(root);
168
+ return readJsonl(paths.cacheBlocks);
169
+ }
170
+ //# sourceMappingURL=cache-normalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-normalizer.js","sourceRoot":"","sources":["../../src/core/cache-normalizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { z } from "zod";
2
+ export declare const WORKSPACE_DIR = ".soturail";
3
+ export declare const DEFAULT_BINARY_EXTENSIONS: readonly ["png", "jpg", "jpeg", "gif", "webp", "pdf", "zip", "exe", "dll", "so", "class", "jar", "mp4", "mp3", "wav"];
4
+ export declare const ConfigSchema: z.ZodObject<{
5
+ schema_version: z.ZodDefault<z.ZodLiteral<1>>;
6
+ project_name: z.ZodDefault<z.ZodString>;
7
+ workspace_dir: z.ZodDefault<z.ZodString>;
8
+ max_file_size_kb: z.ZodDefault<z.ZodNumber>;
9
+ ignore_extensions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
10
+ response_compression: z.ZodDefault<z.ZodObject<{
11
+ default_mode: z.ZodDefault<z.ZodEnum<["normal", "concise", "ultra", "review", "commit", "debug", "docs"]>>;
12
+ preserve_code_blocks: z.ZodDefault<z.ZodBoolean>;
13
+ preserve_security_warnings: z.ZodDefault<z.ZodBoolean>;
14
+ }, "strip", z.ZodTypeAny, {
15
+ default_mode: "normal" | "concise" | "ultra" | "review" | "commit" | "debug" | "docs";
16
+ preserve_code_blocks: boolean;
17
+ preserve_security_warnings: boolean;
18
+ }, {
19
+ default_mode?: "normal" | "concise" | "ultra" | "review" | "commit" | "debug" | "docs" | undefined;
20
+ preserve_code_blocks?: boolean | undefined;
21
+ preserve_security_warnings?: boolean | undefined;
22
+ }>>;
23
+ cache: z.ZodDefault<z.ZodObject<{
24
+ dynamic_footer_token_budget: z.ZodDefault<z.ZodNumber>;
25
+ }, "strip", z.ZodTypeAny, {
26
+ dynamic_footer_token_budget: number;
27
+ }, {
28
+ dynamic_footer_token_budget?: number | undefined;
29
+ }>>;
30
+ }, "strip", z.ZodTypeAny, {
31
+ schema_version: 1;
32
+ project_name: string;
33
+ workspace_dir: string;
34
+ max_file_size_kb: number;
35
+ ignore_extensions: string[];
36
+ response_compression: {
37
+ default_mode: "normal" | "concise" | "ultra" | "review" | "commit" | "debug" | "docs";
38
+ preserve_code_blocks: boolean;
39
+ preserve_security_warnings: boolean;
40
+ };
41
+ cache: {
42
+ dynamic_footer_token_budget: number;
43
+ };
44
+ }, {
45
+ schema_version?: 1 | undefined;
46
+ project_name?: string | undefined;
47
+ workspace_dir?: string | undefined;
48
+ max_file_size_kb?: number | undefined;
49
+ ignore_extensions?: string[] | undefined;
50
+ response_compression?: {
51
+ default_mode?: "normal" | "concise" | "ultra" | "review" | "commit" | "debug" | "docs" | undefined;
52
+ preserve_code_blocks?: boolean | undefined;
53
+ preserve_security_warnings?: boolean | undefined;
54
+ } | undefined;
55
+ cache?: {
56
+ dynamic_footer_token_budget?: number | undefined;
57
+ } | undefined;
58
+ }>;
59
+ export type SotuRailConfig = z.infer<typeof ConfigSchema>;
60
+ export declare const defaultConfig: SotuRailConfig;
61
+ export interface WorkspacePaths {
62
+ root: string;
63
+ workspace: string;
64
+ configDir: string;
65
+ configFile: string;
66
+ rawDir: string;
67
+ rawIndex: string;
68
+ indexesDir: string;
69
+ memoryDir: string;
70
+ memoryFile: string;
71
+ specsDir: string;
72
+ metricsDir: string;
73
+ metricsFile: string;
74
+ cacheDir: string;
75
+ cacheBlocks: string;
76
+ dedupeDir: string;
77
+ dedupeIndex: string;
78
+ hooksDir: string;
79
+ hooksHosts: string;
80
+ rulesDir: string;
81
+ rulesFile: string;
82
+ rulesChecklist: string;
83
+ rulesCitations: string;
84
+ rulesValidatorsDir: string;
85
+ memoryPendingFile: string;
86
+ memoryApprovedFile: string;
87
+ }
88
+ export interface EnsureResult {
89
+ created: string[];
90
+ skipped: string[];
91
+ }
92
+ export declare function getWorkspacePaths(root?: string, workspaceDir?: string): WorkspacePaths;
93
+ export declare function relativeToRoot(root: string, absolutePath: string): string;
94
+ export declare function ensureDir(dirPath: string, result?: EnsureResult, root?: string): Promise<void>;
95
+ export declare function writeFileIfMissing(filePath: string, content: string, result?: EnsureResult, root?: string): Promise<"created" | "skipped">;
96
+ export declare function ensureWorkspace(root?: string): Promise<EnsureResult>;
97
+ export declare function loadConfig(root?: string): Promise<SotuRailConfig>;
98
+ export declare function validateConfigFile(root?: string): Promise<{
99
+ ok: boolean;
100
+ message: string;
101
+ }>;
102
+ export declare function appendJsonl(filePath: string, value: unknown): Promise<void>;
103
+ export declare function readJsonl<T>(filePath: string): Promise<T[]>;
104
+ export declare function writeJson(filePath: string, value: unknown): Promise<void>;
@@ -0,0 +1,186 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { z } from "zod";
4
+ export const WORKSPACE_DIR = ".soturail";
5
+ export const DEFAULT_BINARY_EXTENSIONS = [
6
+ "png",
7
+ "jpg",
8
+ "jpeg",
9
+ "gif",
10
+ "webp",
11
+ "pdf",
12
+ "zip",
13
+ "exe",
14
+ "dll",
15
+ "so",
16
+ "class",
17
+ "jar",
18
+ "mp4",
19
+ "mp3",
20
+ "wav"
21
+ ];
22
+ export const ConfigSchema = z.object({
23
+ schema_version: z.literal(1).default(1),
24
+ project_name: z.string().default("soturail-project"),
25
+ workspace_dir: z.string().default(WORKSPACE_DIR),
26
+ max_file_size_kb: z.number().int().positive().max(10240).default(512),
27
+ ignore_extensions: z.array(z.string()).default([...DEFAULT_BINARY_EXTENSIONS]),
28
+ response_compression: z
29
+ .object({
30
+ default_mode: z.enum(["normal", "concise", "ultra", "review", "commit", "debug", "docs"]).default("normal"),
31
+ preserve_code_blocks: z.boolean().default(true),
32
+ preserve_security_warnings: z.boolean().default(true)
33
+ })
34
+ .default({ default_mode: "normal", preserve_code_blocks: true, preserve_security_warnings: true }),
35
+ cache: z
36
+ .object({
37
+ dynamic_footer_token_budget: z.number().int().positive().max(10000).default(800)
38
+ })
39
+ .default({ dynamic_footer_token_budget: 800 })
40
+ });
41
+ export const defaultConfig = ConfigSchema.parse({});
42
+ export function getWorkspacePaths(root = process.cwd(), workspaceDir = WORKSPACE_DIR) {
43
+ const resolvedRoot = path.resolve(root);
44
+ const workspace = path.resolve(resolvedRoot, workspaceDir);
45
+ return {
46
+ root: resolvedRoot,
47
+ workspace,
48
+ configDir: path.resolve(workspace, "config"),
49
+ configFile: path.resolve(workspace, "config", "config.json"),
50
+ rawDir: path.resolve(workspace, "raw"),
51
+ rawIndex: path.resolve(workspace, "raw", "index.jsonl"),
52
+ indexesDir: path.resolve(workspace, "indexes"),
53
+ memoryDir: path.resolve(workspace, "memory"),
54
+ memoryFile: path.resolve(workspace, "memory", "memory.jsonl"),
55
+ specsDir: path.resolve(workspace, "specs"),
56
+ metricsDir: path.resolve(workspace, "metrics"),
57
+ metricsFile: path.resolve(workspace, "metrics", "events.jsonl"),
58
+ cacheDir: path.resolve(workspace, "cache"),
59
+ cacheBlocks: path.resolve(workspace, "cache", "blocks.jsonl"),
60
+ dedupeDir: path.resolve(workspace, "dedupe"),
61
+ dedupeIndex: path.resolve(workspace, "dedupe", "index.jsonl"),
62
+ hooksDir: path.resolve(workspace, "hooks"),
63
+ hooksHosts: path.resolve(workspace, "hooks", "hosts.json"),
64
+ rulesDir: path.resolve(workspace, "rules"),
65
+ rulesFile: path.resolve(workspace, "rules", "rules.yml"),
66
+ rulesChecklist: path.resolve(workspace, "rules", "checklist.md"),
67
+ rulesCitations: path.resolve(workspace, "rules", "citations.json"),
68
+ rulesValidatorsDir: path.resolve(workspace, "rules", "validators"),
69
+ memoryPendingFile: path.resolve(workspace, "memory", "pending.jsonl"),
70
+ memoryApprovedFile: path.resolve(workspace, "memory", "approved.jsonl")
71
+ };
72
+ }
73
+ export function relativeToRoot(root, absolutePath) {
74
+ return path.normalize(path.relative(path.resolve(root), path.resolve(absolutePath)));
75
+ }
76
+ async function exists(filePath) {
77
+ try {
78
+ await fs.access(filePath);
79
+ return true;
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }
85
+ export async function ensureDir(dirPath, result, root) {
86
+ const alreadyExists = await exists(dirPath);
87
+ await fs.mkdir(dirPath, { recursive: true });
88
+ if (result && root) {
89
+ const display = relativeToRoot(root, dirPath);
90
+ if (alreadyExists) {
91
+ result.skipped.push(display);
92
+ }
93
+ else {
94
+ result.created.push(display);
95
+ }
96
+ }
97
+ }
98
+ export async function writeFileIfMissing(filePath, content, result, root) {
99
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
100
+ const display = root ? relativeToRoot(root, filePath) : filePath;
101
+ if (await exists(filePath)) {
102
+ result?.skipped.push(display);
103
+ return "skipped";
104
+ }
105
+ await fs.writeFile(filePath, content, "utf8");
106
+ result?.created.push(display);
107
+ return "created";
108
+ }
109
+ export async function ensureWorkspace(root = process.cwd()) {
110
+ const paths = getWorkspacePaths(root);
111
+ const result = { created: [], skipped: [] };
112
+ const dirs = [
113
+ paths.workspace,
114
+ paths.configDir,
115
+ paths.rawDir,
116
+ paths.indexesDir,
117
+ paths.memoryDir,
118
+ paths.specsDir,
119
+ paths.metricsDir,
120
+ paths.cacheDir,
121
+ paths.dedupeDir,
122
+ paths.hooksDir,
123
+ paths.rulesDir,
124
+ paths.rulesValidatorsDir
125
+ ];
126
+ for (const dir of dirs) {
127
+ await ensureDir(dir, result, paths.root);
128
+ }
129
+ await writeFileIfMissing(paths.configFile, `${JSON.stringify(defaultConfig, null, 2)}\n`, result, paths.root);
130
+ await writeFileIfMissing(paths.rawIndex, "", result, paths.root);
131
+ await writeFileIfMissing(paths.memoryFile, "", result, paths.root);
132
+ await writeFileIfMissing(paths.memoryPendingFile, "", result, paths.root);
133
+ await writeFileIfMissing(paths.memoryApprovedFile, "", result, paths.root);
134
+ await writeFileIfMissing(paths.metricsFile, "", result, paths.root);
135
+ await writeFileIfMissing(paths.cacheBlocks, "", result, paths.root);
136
+ await writeFileIfMissing(paths.dedupeIndex, "", result, paths.root);
137
+ return result;
138
+ }
139
+ export async function loadConfig(root = process.cwd()) {
140
+ const paths = getWorkspacePaths(root);
141
+ try {
142
+ const raw = await fs.readFile(paths.configFile, "utf8");
143
+ return ConfigSchema.parse(JSON.parse(raw));
144
+ }
145
+ catch (error) {
146
+ if (error.code === "ENOENT") {
147
+ return defaultConfig;
148
+ }
149
+ throw error;
150
+ }
151
+ }
152
+ export async function validateConfigFile(root = process.cwd()) {
153
+ const paths = getWorkspacePaths(root);
154
+ try {
155
+ const raw = await fs.readFile(paths.configFile, "utf8");
156
+ ConfigSchema.parse(JSON.parse(raw));
157
+ return { ok: true, message: "config.json is valid" };
158
+ }
159
+ catch (error) {
160
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
161
+ }
162
+ }
163
+ export async function appendJsonl(filePath, value) {
164
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
165
+ await fs.appendFile(filePath, `${JSON.stringify(value)}\n`, "utf8");
166
+ }
167
+ export async function readJsonl(filePath) {
168
+ try {
169
+ const raw = await fs.readFile(filePath, "utf8");
170
+ return raw
171
+ .split(/\r?\n/)
172
+ .filter((line) => line.trim().length > 0)
173
+ .map((line) => JSON.parse(line));
174
+ }
175
+ catch (error) {
176
+ if (error.code === "ENOENT") {
177
+ return [];
178
+ }
179
+ throw error;
180
+ }
181
+ }
182
+ export async function writeJson(filePath, value) {
183
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
184
+ await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
185
+ }
186
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface DedupeRecord {
2
+ output_sha256: string;
3
+ raw_id: string;
4
+ command: string;
5
+ exit_code: number;
6
+ raw_path: string;
7
+ created_at: string;
8
+ }
9
+ export declare function sha256Text(text: string): string;
10
+ export declare class DedupeStore {
11
+ private readonly root;
12
+ constructor(root?: string);
13
+ findReusable(outputHash: string, exitCode: number): Promise<DedupeRecord | null>;
14
+ append(record: DedupeRecord): Promise<void>;
15
+ readAll(): Promise<DedupeRecord[]>;
16
+ private rawExists;
17
+ }
@@ -0,0 +1,44 @@
1
+ import { createHash } from "node:crypto";
2
+ import { promises as fs } from "node:fs";
3
+ import path from "node:path";
4
+ import { appendJsonl, getWorkspacePaths, readJsonl } from "./config.js";
5
+ export function sha256Text(text) {
6
+ return createHash("sha256").update(text).digest("hex");
7
+ }
8
+ export class DedupeStore {
9
+ root;
10
+ constructor(root = process.cwd()) {
11
+ this.root = root;
12
+ }
13
+ async findReusable(outputHash, exitCode) {
14
+ const records = await this.readAll();
15
+ for (const record of [...records].reverse()) {
16
+ if (record.output_sha256 !== outputHash || record.exit_code !== exitCode) {
17
+ continue;
18
+ }
19
+ if (await this.rawExists(record.raw_path)) {
20
+ return record;
21
+ }
22
+ }
23
+ return null;
24
+ }
25
+ async append(record) {
26
+ const paths = getWorkspacePaths(this.root);
27
+ await appendJsonl(paths.dedupeIndex, record);
28
+ }
29
+ async readAll() {
30
+ const paths = getWorkspacePaths(this.root);
31
+ return readJsonl(paths.dedupeIndex);
32
+ }
33
+ async rawExists(rawPath) {
34
+ const paths = getWorkspacePaths(this.root);
35
+ try {
36
+ await fs.access(path.resolve(paths.root, rawPath));
37
+ return true;
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ }
44
+ //# sourceMappingURL=dedupe-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupe-store.js","sourceRoot":"","sources":["../../src/core/dedupe-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAWxE,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,OAAO,WAAW;IACO;IAA7B,YAA6B,OAAO,OAAO,CAAC,GAAG,EAAE;QAApB,SAAI,GAAJ,IAAI,CAAgB;IAAG,CAAC;IAErD,KAAK,CAAC,YAAY,CAAC,UAAkB,EAAE,QAAgB;QACrD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrC,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC5C,IAAI,MAAM,CAAC,aAAa,KAAK,UAAU,IAAI,MAAM,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;gBACzE,SAAS;YACX,CAAC;YACD,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1C,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAAoB;QAC/B,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,WAAW,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,OAAO,SAAS,CAAe,KAAK,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,OAAe;QACrC,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ export type IngestType = "rules" | "requirements" | "docs" | "course";
2
+ export interface DocumentSection {
3
+ title: string;
4
+ content: string;
5
+ start_line: number;
6
+ }
7
+ export interface IngestedDocument {
8
+ source_file: string;
9
+ type: IngestType;
10
+ text: string;
11
+ content_hash: string;
12
+ sections: DocumentSection[];
13
+ }
14
+ export declare function isIngestType(value: string): value is IngestType;
15
+ export declare function ingestDocument(file: string, type: IngestType, root?: string): Promise<IngestedDocument>;