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,60 @@
1
+ import { spawn } from "node:child_process";
2
+ export class NativeRunnerAdapter {
3
+ async run(options) {
4
+ const cwd = options.cwd ?? process.cwd();
5
+ const stdoutTarget = options.terminalStdout ?? process.stdout;
6
+ const stderrTarget = options.terminalStderr ?? process.stderr;
7
+ const chunks = [];
8
+ return new Promise((resolve, reject) => {
9
+ const child = spawn(options.command, {
10
+ cwd,
11
+ shell: true,
12
+ stdio: ["pipe", "pipe", "pipe"],
13
+ env: options.env ?? process.env,
14
+ windowsHide: !options.interactive
15
+ });
16
+ const tee = (chunk, target) => {
17
+ chunks.push(Buffer.from(chunk));
18
+ options.rawLogStream.write(chunk);
19
+ target.write(chunk);
20
+ };
21
+ child.stdout?.on("data", (chunk) => tee(chunk, stdoutTarget));
22
+ child.stderr?.on("data", (chunk) => tee(chunk, stderrTarget));
23
+ if (options.interactive && child.stdin && process.stdin.readable) {
24
+ if (process.stdin.isTTY) {
25
+ process.stdin.resume();
26
+ }
27
+ process.stdin.pipe(child.stdin);
28
+ }
29
+ else {
30
+ child.stdin?.end();
31
+ }
32
+ const forwardSigint = () => {
33
+ child.kill("SIGINT");
34
+ };
35
+ process.once("SIGINT", forwardSigint);
36
+ child.once("error", (error) => {
37
+ process.off("SIGINT", forwardSigint);
38
+ if (options.interactive && child.stdin) {
39
+ process.stdin.unpipe(child.stdin);
40
+ }
41
+ options.rawLogStream.end();
42
+ reject(error);
43
+ });
44
+ child.once("close", (code, signal) => {
45
+ process.off("SIGINT", forwardSigint);
46
+ if (options.interactive && child.stdin) {
47
+ process.stdin.unpipe(child.stdin);
48
+ }
49
+ options.rawLogStream.end(() => {
50
+ resolve({
51
+ exitCode: code ?? (signal ? 130 : 1),
52
+ signal,
53
+ rawText: Buffer.concat(chunks).toString("utf8")
54
+ });
55
+ });
56
+ });
57
+ });
58
+ }
59
+ }
60
+ //# sourceMappingURL=native-runner-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-runner-adapter.js","sourceRoot":"","sources":["../../src/core/native-runner-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { WriteStream } from "node:fs";
2
+ export interface RawRunRecord {
3
+ raw_id: string;
4
+ path: string;
5
+ command: string;
6
+ exit_code: number;
7
+ created_at: string;
8
+ compressor: string;
9
+ raw_tokens_estimated: number;
10
+ compressed_tokens_estimated: number;
11
+ }
12
+ export interface RawLogHandle {
13
+ rawId: string;
14
+ absolutePath: string;
15
+ relativePath: string;
16
+ createdAt: string;
17
+ stream: WriteStream;
18
+ }
19
+ export declare class RawStore {
20
+ private readonly root;
21
+ constructor(root?: string);
22
+ createLog(command: string, date?: Date): Promise<RawLogHandle>;
23
+ appendRunRecord(record: RawRunRecord): Promise<void>;
24
+ readManifest(): Promise<RawRunRecord[]>;
25
+ find(rawId: string): Promise<RawRunRecord | null>;
26
+ readRaw(rawId: string): Promise<Buffer | null>;
27
+ getAbsolutePath(record: RawRunRecord): string;
28
+ }
@@ -0,0 +1,57 @@
1
+ import { createWriteStream, promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { randomBytes } from "node:crypto";
4
+ import { appendJsonl, getWorkspacePaths, readJsonl, relativeToRoot } from "./config.js";
5
+ function dayStamp(date) {
6
+ return date.toISOString().slice(0, 10);
7
+ }
8
+ function fileStamp(date) {
9
+ return date.toISOString().replace(/[-:.]/g, "").replace("T", "T").replace("Z", "Z");
10
+ }
11
+ export class RawStore {
12
+ root;
13
+ constructor(root = process.cwd()) {
14
+ this.root = root;
15
+ }
16
+ async createLog(command, date = new Date()) {
17
+ const paths = getWorkspacePaths(this.root);
18
+ const rawId = randomBytes(4).toString("hex");
19
+ const dir = path.resolve(paths.rawDir, dayStamp(date));
20
+ await fs.mkdir(dir, { recursive: true });
21
+ const absolutePath = path.resolve(dir, `${fileStamp(date)}-${rawId}.log`);
22
+ const stream = createWriteStream(absolutePath, { flags: "wx" });
23
+ return {
24
+ rawId,
25
+ absolutePath,
26
+ relativePath: relativeToRoot(paths.root, absolutePath),
27
+ createdAt: date.toISOString(),
28
+ stream
29
+ };
30
+ }
31
+ async appendRunRecord(record) {
32
+ const paths = getWorkspacePaths(this.root);
33
+ await appendJsonl(paths.rawIndex, record);
34
+ }
35
+ async readManifest() {
36
+ const paths = getWorkspacePaths(this.root);
37
+ return readJsonl(paths.rawIndex);
38
+ }
39
+ async find(rawId) {
40
+ const records = await this.readManifest();
41
+ return records.find((record) => record.raw_id === rawId) ?? null;
42
+ }
43
+ async readRaw(rawId) {
44
+ const record = await this.find(rawId);
45
+ if (!record) {
46
+ return null;
47
+ }
48
+ const paths = getWorkspacePaths(this.root);
49
+ const absolutePath = path.resolve(paths.root, record.path);
50
+ return fs.readFile(absolutePath);
51
+ }
52
+ getAbsolutePath(record) {
53
+ const paths = getWorkspacePaths(this.root);
54
+ return path.resolve(paths.root, record.path);
55
+ }
56
+ }
57
+ //# sourceMappingURL=raw-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raw-store.js","sourceRoot":"","sources":["../../src/core/raw-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type ResponseMode = "normal" | "concise" | "ultra" | "review" | "commit" | "debug" | "docs";
2
+ export declare const responseModes: ResponseMode[];
3
+ export interface ResponsePolicy {
4
+ mode: ResponseMode;
5
+ preserveCodeBlocks: boolean;
6
+ preserveCommands: boolean;
7
+ preservePaths: boolean;
8
+ preserveSecurityWarnings: boolean;
9
+ }
10
+ export declare function buildResponsePolicy(mode: ResponseMode): ResponsePolicy;
11
+ export declare function isResponseMode(value: string): value is ResponseMode;
@@ -0,0 +1,14 @@
1
+ export const responseModes = ["normal", "concise", "ultra", "review", "commit", "debug", "docs"];
2
+ export function buildResponsePolicy(mode) {
3
+ return {
4
+ mode,
5
+ preserveCodeBlocks: true,
6
+ preserveCommands: true,
7
+ preservePaths: true,
8
+ preserveSecurityWarnings: true
9
+ };
10
+ }
11
+ export function isResponseMode(value) {
12
+ return responseModes.includes(value);
13
+ }
14
+ //# sourceMappingURL=response-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-policy.js","sourceRoot":"","sources":["../../src/core/response-policy.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAmB,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAUjH,MAAM,UAAU,mBAAmB,CAAC,IAAkB;IACpD,OAAO;QACL,IAAI;QACJ,kBAAkB,EAAE,IAAI;QACxB,gBAAgB,EAAE,IAAI;QACtB,aAAa,EAAE,IAAI;QACnB,wBAAwB,EAAE,IAAI;KAC/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,aAAa,CAAC,QAAQ,CAAC,KAAqB,CAAC,CAAC;AACvD,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { IngestedDocument } from "./document-ingest.js";
2
+ export type RuleSeverity = "low" | "medium" | "high";
3
+ export type ValidationType = "package_json_check" | "required_file" | "forbidden_file" | "readme_section" | "ci_workflow" | "documentation_presence" | "manual_check";
4
+ export interface ExtractedRule {
5
+ id: string;
6
+ title: string;
7
+ description: string;
8
+ source_file: string;
9
+ source_section: string;
10
+ severity: RuleSeverity;
11
+ validation_type: ValidationType;
12
+ suggested_validator_name: string;
13
+ created_at: string;
14
+ content_hash: string;
15
+ }
16
+ export declare function extractRules(document: IngestedDocument, now?: string): ExtractedRule[];
17
+ export declare function rulesToYaml(rules: ExtractedRule[]): string;
18
+ export declare function parseRulesYaml(yaml: string): ExtractedRule[];
@@ -0,0 +1,116 @@
1
+ import { createHash } from "node:crypto";
2
+ const RULE_SIGNAL = /\b(must|shall|required|requires|should|forbid|forbidden|never|minimum|include|presence|license|changelog|readme|node\.?js|ci workflow|agents\.md|claude\.md|gemini\.md)\b/i;
3
+ export function extractRules(document, now = new Date().toISOString()) {
4
+ const rules = [];
5
+ for (const section of document.sections) {
6
+ const candidates = section.content
7
+ .split(/\r?\n/)
8
+ .map((line) => line.trim().replace(/^[-*]\s+/, "").replace(/^\d+[.)]\s+/, ""))
9
+ .filter((line) => line.length > 0 && RULE_SIGNAL.test(line));
10
+ candidates.forEach((candidate) => {
11
+ const validationType = inferValidationType(candidate);
12
+ const title = titleFrom(candidate);
13
+ const id = stableId(`${document.source_file}:${section.title}:${candidate}`);
14
+ rules.push({
15
+ id,
16
+ title,
17
+ description: candidate,
18
+ source_file: document.source_file,
19
+ source_section: section.title,
20
+ severity: inferSeverity(candidate),
21
+ validation_type: validationType,
22
+ suggested_validator_name: validatorName(validationType, title),
23
+ created_at: now,
24
+ content_hash: createHash("sha256").update(candidate).digest("hex")
25
+ });
26
+ });
27
+ }
28
+ return dedupeRules(rules);
29
+ }
30
+ function stableId(input) {
31
+ return createHash("sha256").update(input).digest("hex").slice(0, 12);
32
+ }
33
+ function titleFrom(line) {
34
+ const clean = line.replace(/[`*_]/g, "").replace(/[.:;]$/, "");
35
+ const words = clean.split(/\s+/).slice(0, 8).join(" ");
36
+ return words.charAt(0).toUpperCase() + words.slice(1);
37
+ }
38
+ function inferSeverity(line) {
39
+ if (/\b(must|shall|required|never|forbidden|security|minimum)\b/i.test(line)) {
40
+ return "high";
41
+ }
42
+ if (/\b(should|recommended|include)\b/i.test(line)) {
43
+ return "medium";
44
+ }
45
+ return "low";
46
+ }
47
+ function inferValidationType(line) {
48
+ if (/\bpackage\.json|node\.?js|version|engines\b/i.test(line)) {
49
+ return "package_json_check";
50
+ }
51
+ if (/\b(readme section|section named|README required sections)\b/i.test(line)) {
52
+ return "readme_section";
53
+ }
54
+ if (/\bci workflow|\.github\/workflows|github actions\b/i.test(line)) {
55
+ return "ci_workflow";
56
+ }
57
+ if (/\b(forbidden file|must not include|never commit)\b/i.test(line)) {
58
+ return "forbidden_file";
59
+ }
60
+ if (/\b(README|LICENSE|CHANGELOG|AGENTS\.md|CLAUDE\.md|GEMINI\.md|SECURITY\.md|CONTRIBUTING\.md)\b/.test(line)) {
61
+ return "required_file";
62
+ }
63
+ if (/\bdocs|documentation|changelog|license\b/i.test(line)) {
64
+ return "documentation_presence";
65
+ }
66
+ return "manual_check";
67
+ }
68
+ function validatorName(type, title) {
69
+ const safe = title.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 40);
70
+ return `${type}_${safe || "rule"}`;
71
+ }
72
+ function dedupeRules(rules) {
73
+ const seen = new Set();
74
+ const result = [];
75
+ for (const rule of rules) {
76
+ if (!seen.has(rule.id)) {
77
+ seen.add(rule.id);
78
+ result.push(rule);
79
+ }
80
+ }
81
+ return result;
82
+ }
83
+ export function rulesToYaml(rules) {
84
+ return `${rules
85
+ .map((rule) => [
86
+ `- id: ${rule.id}`,
87
+ ` title: ${yamlString(rule.title)}`,
88
+ ` description: ${yamlString(rule.description)}`,
89
+ ` source_file: ${yamlString(rule.source_file)}`,
90
+ ` source_section: ${yamlString(rule.source_section)}`,
91
+ ` severity: ${rule.severity}`,
92
+ ` validation_type: ${rule.validation_type}`,
93
+ ` suggested_validator_name: ${rule.suggested_validator_name}`,
94
+ ` created_at: ${rule.created_at}`,
95
+ ` content_hash: ${rule.content_hash}`
96
+ ].join("\n"))
97
+ .join("\n")}\n`;
98
+ }
99
+ function yamlString(value) {
100
+ return JSON.stringify(value);
101
+ }
102
+ export function parseRulesYaml(yaml) {
103
+ const blocks = yaml.split(/\n(?=- id: )/).filter((block) => block.trim().length > 0);
104
+ return blocks.map((block) => {
105
+ const record = {};
106
+ for (const line of block.split(/\r?\n/)) {
107
+ const match = line.match(/^\s*-?\s*([a-z_]+):\s*(.*)$/);
108
+ if (match?.[1]) {
109
+ const value = match[2] ?? "";
110
+ record[match[1]] = value.startsWith("\"") ? JSON.parse(value) : value;
111
+ }
112
+ }
113
+ return record;
114
+ });
115
+ }
116
+ //# sourceMappingURL=rule-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-extractor.js","sourceRoot":"","sources":["../../src/core/rule-extractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ExtractedRule } from "./rule-extractor.js";
2
+ export interface RuleCheckResult {
3
+ rule_id: string;
4
+ title: string;
5
+ ok: boolean;
6
+ validation_type: string;
7
+ message: string;
8
+ }
9
+ export declare function validateRules(rules: ExtractedRule[], root?: string): Promise<RuleCheckResult[]>;
@@ -0,0 +1,85 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ async function exists(root, relativePath) {
4
+ try {
5
+ await fs.access(path.resolve(root, relativePath));
6
+ return true;
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ export async function validateRules(rules, root = process.cwd()) {
13
+ const results = [];
14
+ for (const rule of rules) {
15
+ results.push(await validateRule(rule, root));
16
+ }
17
+ return results;
18
+ }
19
+ async function validateRule(rule, root) {
20
+ const text = `${rule.title} ${rule.description}`;
21
+ switch (rule.validation_type) {
22
+ case "package_json_check":
23
+ return validatePackageJson(rule, root);
24
+ case "required_file":
25
+ case "documentation_presence":
26
+ return validateRequiredFile(rule, root, text);
27
+ case "forbidden_file":
28
+ return validateForbiddenFile(rule, root, text);
29
+ case "readme_section":
30
+ return validateReadmeSections(rule, root, text);
31
+ case "ci_workflow":
32
+ return result(rule, await exists(root, ".github/workflows/ci.yml"), "checked .github/workflows/ci.yml");
33
+ case "manual_check":
34
+ return result(rule, true, "manual check recorded; no deterministic validator available");
35
+ }
36
+ }
37
+ async function validatePackageJson(rule, root) {
38
+ try {
39
+ const pkg = JSON.parse(await fs.readFile(path.resolve(root, "package.json"), "utf8"));
40
+ const node = pkg.engines?.node ?? "";
41
+ const ok = /20|>=20|22|24/.test(node);
42
+ return result(rule, ok, `package.json engines.node is ${node || "missing"}`);
43
+ }
44
+ catch {
45
+ return result(rule, false, "package.json missing or invalid");
46
+ }
47
+ }
48
+ async function validateRequiredFile(rule, root, text) {
49
+ const candidates = ["README.md", "LICENSE", "CHANGELOG.md", "AGENTS.md", "CLAUDE.md", "GEMINI.md", "SECURITY.md", "CONTRIBUTING.md"]
50
+ .filter((file) => text.includes(file));
51
+ if (candidates.length === 0) {
52
+ return result(rule, true, "no exact required file name detected; manual review may still apply");
53
+ }
54
+ const missing = [];
55
+ for (const file of candidates) {
56
+ if (!(await exists(root, file))) {
57
+ missing.push(file);
58
+ }
59
+ }
60
+ return result(rule, missing.length === 0, missing.length === 0 ? `found ${candidates.join(", ")}` : `missing ${missing.join(", ")}`);
61
+ }
62
+ async function validateForbiddenFile(rule, root, text) {
63
+ const match = text.match(/(?:file|include|commit)\s+([.\w/-]+)/i);
64
+ const file = match?.[1];
65
+ if (!file) {
66
+ return result(rule, true, "no exact forbidden file detected; manual review may still apply");
67
+ }
68
+ return result(rule, !(await exists(root, file)), `checked ${file}`);
69
+ }
70
+ async function validateReadmeSections(rule, root, text) {
71
+ const readme = await fs.readFile(path.resolve(root, "README.md"), "utf8").catch(() => "");
72
+ const sectionMatch = text.match(/section named\s+["']?([^"'.]+)["']?/i);
73
+ const required = sectionMatch?.[1]?.trim();
74
+ if (!required) {
75
+ return result(rule, readme.length > 0, "README.md exists; specific section not detected");
76
+ }
77
+ return result(rule, new RegExp(`^#+\\s+${escapeRegExp(required)}\\s*$`, "im").test(readme), `checked README section ${required}`);
78
+ }
79
+ function result(rule, ok, message) {
80
+ return { rule_id: rule.id, title: rule.title, ok, validation_type: rule.validation_type, message };
81
+ }
82
+ function escapeRegExp(value) {
83
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
84
+ }
85
+ //# sourceMappingURL=rule-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-validator.js","sourceRoot":"","sources":["../../src/core/rule-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare const UNSAFE_CONFIRMATION = "I_UNDERSTAND_THIS_CAN_DESTROY_DATA";
2
+ export interface ValidationResult {
3
+ ok: boolean;
4
+ blocked: boolean;
5
+ bypassed: boolean;
6
+ reason?: string;
7
+ matchedPattern?: string;
8
+ }
9
+ export declare function isDangerousCommand(command: string): boolean;
10
+ export declare function validateCommand(command: string, unsafeConfirm?: string): ValidationResult;
@@ -0,0 +1,69 @@
1
+ export const UNSAFE_CONFIRMATION = "I_UNDERSTAND_THIS_CAN_DESTROY_DATA";
2
+ const dangerousPatterns = [
3
+ {
4
+ name: "rm -rf",
5
+ pattern: /\brm\s+(?:-[^\s]*r[^\s]*f|-[^\s]*f[^\s]*r)\b/i,
6
+ reason: "recursive forced deletion is blocked by default"
7
+ },
8
+ {
9
+ name: "sudo",
10
+ pattern: /(^|[;&|]\s*)sudo\b/i,
11
+ reason: "privileged commands are blocked by default"
12
+ },
13
+ {
14
+ name: "format",
15
+ pattern: /(^|[;&|]\s*)format(?:\.com)?\b/i,
16
+ reason: "disk formatting commands are blocked by default"
17
+ },
18
+ {
19
+ name: "dd if=",
20
+ pattern: /\bdd\s+[^\n\r]*\bif=/i,
21
+ reason: "raw disk copy commands are blocked by default"
22
+ },
23
+ {
24
+ name: "curl | sh",
25
+ pattern: /\bcurl\b[^\n\r|]*\|\s*(?:sh|bash|zsh|fish)\b/i,
26
+ reason: "piping downloaded scripts into a shell is blocked by default"
27
+ },
28
+ {
29
+ name: "wget | sh",
30
+ pattern: /\bwget\b[^\n\r|]*\|\s*(?:sh|bash|zsh|fish)\b/i,
31
+ reason: "piping downloaded scripts into a shell is blocked by default"
32
+ },
33
+ {
34
+ name: "del /s",
35
+ pattern: /(^|[;&|]\s*)del\s+\/s\b/i,
36
+ reason: "recursive Windows deletion is blocked by default"
37
+ },
38
+ {
39
+ name: "git push",
40
+ pattern: /(^|[;&|]\s*)git\s+push\b/i,
41
+ reason: "automatic git push is blocked by SotuRail"
42
+ }
43
+ ];
44
+ export function isDangerousCommand(command) {
45
+ return dangerousPatterns.some((entry) => entry.pattern.test(command));
46
+ }
47
+ export function validateCommand(command, unsafeConfirm) {
48
+ const match = dangerousPatterns.find((entry) => entry.pattern.test(command));
49
+ if (!match) {
50
+ return { ok: true, blocked: false, bypassed: false };
51
+ }
52
+ if (unsafeConfirm === UNSAFE_CONFIRMATION) {
53
+ return {
54
+ ok: true,
55
+ blocked: false,
56
+ bypassed: true,
57
+ reason: match.reason,
58
+ matchedPattern: match.name
59
+ };
60
+ }
61
+ return {
62
+ ok: false,
63
+ blocked: true,
64
+ bypassed: false,
65
+ reason: `${match.reason}. To bypass, pass --unsafe-confirm "${UNSAFE_CONFIRMATION}" exactly.`,
66
+ matchedPattern: match.name
67
+ };
68
+ }
69
+ //# sourceMappingURL=safety-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safety-policy.js","sourceRoot":"","sources":["../../src/core/safety-policy.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,oCAAoC,CAAC;AAUxE,MAAM,iBAAiB,GAA6D;IAClF;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,+CAA+C;QACxD,MAAM,EAAE,iDAAiD;KAC1D;IACD;QACE,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,qBAAqB;QAC9B,MAAM,EAAE,4CAA4C;KACrD;IACD;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,iCAAiC;QAC1C,MAAM,EAAE,iDAAiD;KAC1D;IACD;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,uBAAuB;QAChC,MAAM,EAAE,+CAA+C;KACxD;IACD;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,+CAA+C;QACxD,MAAM,EAAE,8DAA8D;KACvE;IACD;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,+CAA+C;QACxD,MAAM,EAAE,8DAA8D;KACvE;IACD;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,0BAA0B;QACnC,MAAM,EAAE,kDAAkD;KAC3D;IACD;QACE,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,2BAA2B;QACpC,MAAM,EAAE,2CAA2C;KACpD;CACF,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,aAAsB;IACrE,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7E,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACvD,CAAC;IAED,IAAI,aAAa,KAAK,mBAAmB,EAAE,CAAC;QAC1C,OAAO;YACL,EAAE,EAAE,IAAI;YACR,OAAO,EAAE,KAAK;YACd,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,cAAc,EAAE,KAAK,CAAC,IAAI;SAC3B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,KAAK;QACT,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,uCAAuC,mBAAmB,YAAY;QAC7F,cAAc,EAAE,KAAK,CAAC,IAAI;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function estimateTokens(text: string): number;
2
+ export declare function tokenEstimateNote(): string;
@@ -0,0 +1,7 @@
1
+ export function estimateTokens(text) {
2
+ return Math.ceil(text.length / 4);
3
+ }
4
+ export function tokenEstimateNote() {
5
+ return "Token counts are deterministic local estimates using Math.ceil(text.length / 4); they are not provider billing numbers.";
6
+ }
7
+ //# sourceMappingURL=token-estimator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-estimator.js","sourceRoot":"","sources":["../../src/core/token-estimator.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,yHAAyH,CAAC;AACnI,CAAC"}
@@ -0,0 +1,4 @@
1
+ export type JsonPrimitive = string | number | boolean | null;
2
+ export type JsonValue = JsonPrimitive | JsonValue[] | {
3
+ [key: string]: JsonValue;
4
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ # Architecture
2
+
3
+ SotuRail v0.2.1 is a TypeScript/Node.js local-first CLI. It stores all runtime state in `.soturail/` inside the repository. An optional Rust reducer and runner binary can be used when present, but TypeScript remains the orchestration and fallback layer.
4
+
5
+ ## Runtime Areas
6
+
7
+ - `.soturail/config/config.json` - validated Zod config.
8
+ - `.soturail/indexes/` - Heuristic Repo Map artifacts.
9
+ - `.soturail/raw/` - raw command logs and manifest.
10
+ - `.soturail/metrics/events.jsonl` - append-only local events.
11
+ - `.soturail/specs/` - Spec-Driven Development artifacts.
12
+ - `.soturail/memory/memory.jsonl` - Git-linked local memory.
13
+ - `.soturail/memory/pending.jsonl` and `approved.jsonl` - memory approval workflow.
14
+ - `.soturail/cache/blocks.jsonl` - stable prompt block manifest.
15
+ - `.soturail/rules/` - extracted rules, checklists and citations.
16
+ - `.soturail/hooks/hosts.json` - installed hook registry.
17
+
18
+ ## Flow
19
+
20
+ ```mermaid
21
+ sequenceDiagram
22
+ participant User
23
+ participant CLI as soturail
24
+ participant Policy as safety-policy
25
+ participant Runner as NativeRunnerAdapter
26
+ participant Raw as raw-store
27
+ participant Reducer as compressors
28
+ User->>CLI: soturail run npm test
29
+ CLI->>Policy: validate command
30
+ Policy-->>CLI: ok
31
+ CLI->>Raw: create log file
32
+ CLI->>Runner: spawn shell command with pipes
33
+ Runner-->>User: live stdout/stderr
34
+ Runner-->>Raw: same chunks
35
+ Runner-->>CLI: exit code and captured text
36
+ CLI->>Reducer: compress summary
37
+ CLI->>Raw: append manifest
38
+ CLI-->>User: summary + raw_id
39
+ ```
40
+
41
+ ## Native Runner Boundary
42
+
43
+ `NativeRunnerAdapter` remains the TypeScript command execution seam. The optional `soturail-native` crate implements reducer paths and a native tee-stream runner hot path. Future versions can package prebuilt native binaries for npm.
44
+
45
+ ## Repo Map
46
+
47
+ The indexer is intentionally a Heuristic Repo Map. It uses regex-based MVP extraction for TypeScript/JavaScript, Python and Java symbols. It does not claim full AST support.
@@ -0,0 +1,18 @@
1
+ # Screenshots
2
+
3
+ Place release screenshots and Markdown previews here.
4
+
5
+ Suggested captures:
6
+
7
+ - `soturail init`
8
+ - `soturail index`
9
+ - `soturail run npm test`
10
+ - `soturail stats`
11
+
12
+ Included SVG examples:
13
+
14
+ - `init.svg`
15
+ - `run-expand.svg`
16
+ - `stats.svg`
17
+
18
+ These are static documentation examples and do not contain benchmark claims.
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 340" role="img" aria-labelledby="title desc">
2
+ <title id="title">SotuRail init documentation example</title>
3
+ <desc id="desc">Static terminal-style documentation example for soturail init.</desc>
4
+ <rect width="760" height="340" rx="14" fill="#0f172a"/>
5
+ <circle cx="28" cy="26" r="7" fill="#ef4444"/><circle cx="52" cy="26" r="7" fill="#f59e0b"/><circle cx="76" cy="26" r="7" fill="#22c55e"/>
6
+ <text x="28" y="70" fill="#e2e8f0" font-family="Consolas, monospace" font-size="18">$ soturail init</text>
7
+ <text x="28" y="108" fill="#fb923c" font-family="Consolas, monospace" font-size="18">SotuRail workspace initialized.</text>
8
+ <text x="28" y="150" fill="#e2e8f0" font-family="Consolas, monospace" font-size="16">Created paths:</text>
9
+ <text x="48" y="180" fill="#86efac" font-family="Consolas, monospace" font-size="16">+ .soturail/config/config.json</text>
10
+ <text x="48" y="208" fill="#86efac" font-family="Consolas, monospace" font-size="16">+ .soturail/raw/index.jsonl</text>
11
+ <text x="48" y="236" fill="#86efac" font-family="Consolas, monospace" font-size="16">+ docs/architecture.md</text>
12
+ <text x="28" y="286" fill="#94a3b8" font-family="Consolas, monospace" font-size="14">Documentation example; exact paths depend on your workspace.</text>
13
+ </svg>