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,48 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { ensureWorkspace, loadConfig } from "../core/config.js";
4
+ import { MetricsStore } from "../core/metrics-store.js";
5
+ import { isResponseMode } from "../core/response-policy.js";
6
+ import { reduceAgentResponse } from "../compressors/agent-response-reducer.js";
7
+ export async function formatFile(file, options = {}, root = process.cwd()) {
8
+ await ensureWorkspace(root);
9
+ const config = await loadConfig(root);
10
+ const modeCandidate = options.mode ?? config.response_compression.default_mode;
11
+ if (!isResponseMode(modeCandidate)) {
12
+ throw new Error(`Unknown response compression mode "${modeCandidate}".`);
13
+ }
14
+ const absolute = path.resolve(root, file);
15
+ const input = await fs.readFile(absolute, "utf8");
16
+ const result = reduceAgentResponse(input, modeCandidate);
17
+ await new MetricsStore(root).append({
18
+ type: "response_format",
19
+ details: {
20
+ file,
21
+ mode: result.mode,
22
+ raw_tokens: result.raw_tokens,
23
+ compressed_tokens: result.compressed_tokens,
24
+ reduction_percent: result.reduction_percent,
25
+ preserved_commands_count: result.preserved_commands_count,
26
+ preserved_paths_count: result.preserved_paths_count,
27
+ preserved_code_blocks_count: result.preserved_code_blocks_count,
28
+ preserved_security_warnings_count: result.preserved_security_warnings_count
29
+ }
30
+ });
31
+ return [
32
+ `SotuRail format mode: ${result.mode}`,
33
+ `Estimated tokens: ${result.raw_tokens} -> ${result.compressed_tokens} (${result.reduction_percent}% reduction)`,
34
+ "",
35
+ result.output
36
+ ].join("\n");
37
+ }
38
+ export function registerFormatCommand(program) {
39
+ program
40
+ .command("format")
41
+ .description("Compress verbose AI or documentation output into task-oriented modes.")
42
+ .argument("<input-file>", "Input text file")
43
+ .option("--mode <mode>", "normal, concise, ultra, review, commit, debug, or docs", "normal")
44
+ .action(async (file, options) => {
45
+ process.stdout.write(await formatFile(file, options));
46
+ });
47
+ }
48
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/commands/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,cAAc,EAAqB,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAM/E,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,UAAyB,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;IAC9F,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,oBAAoB,CAAC,YAAY,CAAC;IAC/E,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,sCAAsC,aAAa,IAAI,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,EAAE,aAA6B,CAAC,CAAC;IACzE,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QAClC,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE;YACP,IAAI;YACJ,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;YAC3C,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;YAC3C,wBAAwB,EAAE,MAAM,CAAC,wBAAwB;YACzD,qBAAqB,EAAE,MAAM,CAAC,qBAAqB;YACnD,2BAA2B,EAAE,MAAM,CAAC,2BAA2B;YAC/D,iCAAiC,EAAE,MAAM,CAAC,iCAAiC;SAC5E;KACF,CAAC,CAAC;IACH,OAAO;QACL,yBAAyB,MAAM,CAAC,IAAI,EAAE;QACtC,qBAAqB,MAAM,CAAC,UAAU,OAAO,MAAM,CAAC,iBAAiB,KAAK,MAAM,CAAC,iBAAiB,cAAc;QAChH,EAAE;QACF,MAAM,CAAC,MAAM;KACd,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,uEAAuE,CAAC;SACpF,QAAQ,CAAC,cAAc,EAAE,iBAAiB,CAAC;SAC3C,MAAM,CAAC,eAAe,EAAE,wDAAwD,EAAE,QAAQ,CAAC;SAC3F,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,OAAsB,EAAE,EAAE;QACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Command } from "commander";
2
+ export type HookHost = "claude" | "codex" | "gemini" | "cursor";
3
+ export interface HookInstallOptions {
4
+ dryRun?: boolean;
5
+ }
6
+ export declare function listHooks(): Promise<string>;
7
+ export declare function hooksDoctor(root?: string): Promise<string>;
8
+ export declare function installHooks(hostValue: string, options?: HookInstallOptions, root?: string): Promise<string>;
9
+ export declare function uninstallHooks(hostValue: string, root?: string): Promise<string>;
10
+ export declare function promptOnly(hostValue: string): Promise<string>;
11
+ export declare function registerHooksCommand(program: Command): void;
@@ -0,0 +1,248 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { ensureWorkspace, getWorkspacePaths, writeJson } from "../core/config.js";
4
+ import { MetricsStore } from "../core/metrics-store.js";
5
+ const hosts = ["claude", "codex", "gemini", "cursor"];
6
+ const claudePreToolHook = `#!/usr/bin/env node
7
+ const fs = require("node:fs");
8
+
9
+ const input = fs.readFileSync(0, "utf8");
10
+ let payload = {};
11
+ try {
12
+ payload = input.trim() ? JSON.parse(input) : {};
13
+ } catch {
14
+ payload = { raw: input };
15
+ }
16
+
17
+ const text = JSON.stringify(payload);
18
+ const dangerous = [
19
+ /\\brm\\s+-[^\\n]*r[^\\n]*f/i,
20
+ /(^|[;&|]\\s*)sudo\\b/i,
21
+ /(^|[;&|]\\s*)git\\s+push\\b/i,
22
+ /\\bcurl\\b[^|]*\\|\\s*(sh|bash)\\b/i,
23
+ /\\bdd\\s+[^\\n]*\\bif=/i,
24
+ /(^|[;&|]\\s*)del\\s+\\/s\\b/i
25
+ ];
26
+
27
+ if (dangerous.some((pattern) => pattern.test(text))) {
28
+ console.error("SotuRail blocked a destructive Claude shell command. Do not route git push through soturail run.");
29
+ process.exit(2);
30
+ }
31
+
32
+ if (/\\b(npm|pnpm|yarn|bun|cargo|pytest|vitest|jest|tsc|node)\\b/.test(text)) {
33
+ console.error("SotuRail suggestion: run tests/builds/log commands through soturail run so raw logs are recoverable.");
34
+ }
35
+ process.exit(0);
36
+ `;
37
+ const claudePostToolHook = `#!/usr/bin/env node
38
+ const fs = require("node:fs");
39
+ const input = fs.readFileSync(0, "utf8");
40
+ if (/raw_id/.test(input)) {
41
+ console.error("SotuRail note: use soturail expand <raw_id> when the compressed summary lacks needed evidence.");
42
+ }
43
+ process.exit(0);
44
+ `;
45
+ function rules(host) {
46
+ return `# SotuRail prompt-only rules for ${host}
47
+
48
+ - Use soturail index before large repository changes.
49
+ - Use soturail read <file> --query "goal" instead of reading giant files directly.
50
+ - Use soturail run for tests, builds and logs so raw output is recoverable.
51
+ - Use soturail expand <raw_id> only when the compressed summary lacks needed information.
52
+ - Never use soturail run for git push.
53
+ `;
54
+ }
55
+ function targetFor(host) {
56
+ switch (host) {
57
+ case "claude":
58
+ return { host, file: "CLAUDE.md", mode: "adapter-template", content: `${rules(host)}\n## Pre-tool adapter template\n\nWhen supported by host hooks, route shell commands through: soturail run <command...>\n` };
59
+ case "codex":
60
+ return { host, file: "AGENTS.md", mode: "prompt-only", content: rules(host) };
61
+ case "gemini":
62
+ return { host, file: "GEMINI.md", mode: "prompt-only", content: rules(host) };
63
+ case "cursor":
64
+ return { host, file: ".cursor/rules/soturail.mdc", mode: "prompt-only", content: rules(host) };
65
+ }
66
+ }
67
+ function parseHost(value) {
68
+ if (value === "all") {
69
+ return "all";
70
+ }
71
+ if (hosts.includes(value)) {
72
+ return value;
73
+ }
74
+ throw new Error(`Unknown hook host "${value}".`);
75
+ }
76
+ async function installTarget(target, root, dryRun) {
77
+ const absolute = path.resolve(root, target.file);
78
+ const actions = [`${dryRun ? "Would update" : "Update"} ${target.file} (${target.mode})`];
79
+ try {
80
+ await fs.access(absolute);
81
+ actions.push(`${dryRun ? "Would create" : "Create"} backup ${target.file}.soturail.bak`);
82
+ if (!dryRun) {
83
+ await fs.copyFile(absolute, `${absolute}.soturail.bak`);
84
+ const existing = await fs.readFile(absolute, "utf8");
85
+ const marker = "<!-- soturail-hooks -->";
86
+ const next = existing.includes(marker) ? existing : `${existing.trimEnd()}\n\n${marker}\n${target.content}`;
87
+ await fs.writeFile(absolute, `${next.trimEnd()}\n`, "utf8");
88
+ }
89
+ }
90
+ catch {
91
+ actions.push(`${dryRun ? "Would create" : "Create"} ${target.file}`);
92
+ if (!dryRun) {
93
+ await fs.mkdir(path.dirname(absolute), { recursive: true });
94
+ await fs.writeFile(absolute, `<!-- soturail-hooks -->\n${target.content}`, "utf8");
95
+ }
96
+ }
97
+ return actions;
98
+ }
99
+ async function writeWithBackup(filePath, content, root, dryRun) {
100
+ const relative = path.normalize(path.relative(root, filePath)).replace(/\\/g, "/");
101
+ const actions = [`${dryRun ? "Would write" : "Write"} ${relative}`];
102
+ try {
103
+ await fs.access(filePath);
104
+ actions.push(`${dryRun ? "Would create" : "Create"} backup ${relative}.soturail.bak`);
105
+ if (!dryRun) {
106
+ await fs.copyFile(filePath, `${filePath}.soturail.bak`);
107
+ }
108
+ }
109
+ catch {
110
+ actions.push(`${dryRun ? "Would create" : "Create"} ${relative}`);
111
+ }
112
+ if (!dryRun) {
113
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
114
+ await fs.writeFile(filePath, content, "utf8");
115
+ }
116
+ return actions;
117
+ }
118
+ async function installClaude(root, dryRun) {
119
+ const settingsPath = path.resolve(root, ".claude", "settings.json");
120
+ const preHookPath = path.resolve(root, ".claude", "hooks", "soturail-pre-tool-use.js");
121
+ const postHookPath = path.resolve(root, ".claude", "hooks", "soturail-post-tool-use.js");
122
+ const settings = await buildClaudeSettings(settingsPath);
123
+ return [
124
+ ...(await writeWithBackup(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, root, dryRun)),
125
+ ...(await writeWithBackup(preHookPath, claudePreToolHook, root, dryRun)),
126
+ ...(await writeWithBackup(postHookPath, claudePostToolHook, root, dryRun)),
127
+ "Claude hook template is conservative; verify the schema against your installed Claude Code version."
128
+ ];
129
+ }
130
+ async function buildClaudeSettings(settingsPath) {
131
+ const existing = await readJsonObject(settingsPath);
132
+ const hooks = isRecord(existing.hooks) ? { ...existing.hooks } : {};
133
+ hooks.PreToolUse = appendClaudeHook(hooks.PreToolUse, "Bash", "node .claude/hooks/soturail-pre-tool-use.js");
134
+ hooks.PostToolUse = appendClaudeHook(hooks.PostToolUse, "Bash", "node .claude/hooks/soturail-post-tool-use.js");
135
+ return {
136
+ ...existing,
137
+ hooks,
138
+ soturail: {
139
+ ...(isRecord(existing.soturail) ? existing.soturail : {}),
140
+ mode: "conservative-template",
141
+ note: "If your Claude Code version uses a different hook schema, copy these commands into the supported hook slots manually."
142
+ }
143
+ };
144
+ }
145
+ async function readJsonObject(filePath) {
146
+ try {
147
+ const parsed = JSON.parse(await fs.readFile(filePath, "utf8"));
148
+ return isRecord(parsed) ? parsed : {};
149
+ }
150
+ catch {
151
+ return {};
152
+ }
153
+ }
154
+ function appendClaudeHook(value, matcher, command) {
155
+ const entries = Array.isArray(value) ? [...value] : [];
156
+ if (!entries.some((entry) => JSON.stringify(entry).includes(command))) {
157
+ entries.push({ matcher, hooks: [{ type: "command", command }] });
158
+ }
159
+ return entries;
160
+ }
161
+ function isRecord(value) {
162
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
163
+ }
164
+ export async function listHooks() {
165
+ return `${hosts
166
+ .map((host) => {
167
+ const target = targetFor(host);
168
+ return `${host}: ${target.mode}, target ${target.file}`;
169
+ })
170
+ .join("\n")}\n`;
171
+ }
172
+ export async function hooksDoctor(root = process.cwd()) {
173
+ const paths = getWorkspacePaths(root);
174
+ const registryExists = await fs.access(paths.hooksHosts).then(() => true).catch(() => false);
175
+ return [
176
+ "SotuRail hooks doctor",
177
+ `supported_hosts: ${hosts.join(", ")}`,
178
+ `registry: ${registryExists ? ".soturail/hooks/hosts.json exists" : "not installed yet"}`,
179
+ "host APIs vary; prompt-only fallback is always available"
180
+ ].join("\n") + "\n";
181
+ }
182
+ export async function installHooks(hostValue, options = {}, root = process.cwd()) {
183
+ await ensureWorkspace(root);
184
+ const parsed = parseHost(hostValue);
185
+ const selected = parsed === "all" ? hosts : [parsed];
186
+ const dryRun = options.dryRun === true;
187
+ const lines = [`SotuRail hooks install ${hostValue}${dryRun ? " --dry-run" : ""}`];
188
+ const installed = [];
189
+ for (const host of selected) {
190
+ if (host === "claude") {
191
+ lines.push(...(await installClaude(root, dryRun)));
192
+ installed.push({ host, target: ".claude/settings.json", mode: "adapter-template", installed_at: new Date().toISOString() });
193
+ }
194
+ else {
195
+ const target = targetFor(host);
196
+ lines.push(...(await installTarget(target, root, dryRun)));
197
+ installed.push({ host, target: target.file, mode: target.mode, installed_at: new Date().toISOString() });
198
+ }
199
+ }
200
+ if (!dryRun) {
201
+ const paths = getWorkspacePaths(root);
202
+ await writeJson(paths.hooksHosts, { hosts: installed });
203
+ await new MetricsStore(root).append({ type: "hook_install", details: { host: hostValue, count: installed.length } });
204
+ }
205
+ return `${lines.join("\n")}\n`;
206
+ }
207
+ export async function uninstallHooks(hostValue, root = process.cwd()) {
208
+ const parsed = parseHost(hostValue);
209
+ const selected = parsed === "all" ? hosts : [parsed];
210
+ const lines = [`SotuRail hooks uninstall ${hostValue}`];
211
+ for (const host of selected) {
212
+ const target = targetFor(host);
213
+ const absolute = path.resolve(root, target.file);
214
+ const backup = `${absolute}.soturail.bak`;
215
+ if (await fs.access(backup).then(() => true).catch(() => false)) {
216
+ await fs.copyFile(backup, absolute);
217
+ lines.push(`Restored backup for ${target.file}`);
218
+ }
219
+ else {
220
+ lines.push(`No backup found for ${target.file}; left file unchanged`);
221
+ }
222
+ }
223
+ return `${lines.join("\n")}\n`;
224
+ }
225
+ export async function promptOnly(hostValue) {
226
+ const parsed = parseHost(hostValue);
227
+ const selected = parsed === "all" ? hosts : [parsed];
228
+ return selected.map((host) => targetFor(host).content).join("\n---\n") + "\n";
229
+ }
230
+ export function registerHooksCommand(program) {
231
+ const hooks = program.command("hooks").description("Install or inspect agent hook and prompt-only integrations.");
232
+ hooks.command("list").description("List supported hook hosts.").action(async () => {
233
+ process.stdout.write(await listHooks());
234
+ });
235
+ hooks.command("doctor").description("Check hook registry state.").action(async () => {
236
+ process.stdout.write(await hooksDoctor());
237
+ });
238
+ hooks.command("install").description("Install hook rules for a host.").argument("<host>", "claude, codex, gemini, cursor, or all").option("--dry-run", "Print changes without writing").action(async (host, options) => {
239
+ process.stdout.write(await installHooks(host, options));
240
+ });
241
+ hooks.command("uninstall").description("Restore backed up host files when available.").argument("<host>", "claude, codex, gemini, cursor, or all").action(async (host) => {
242
+ process.stdout.write(await uninstallHooks(host));
243
+ });
244
+ hooks.command("prompt-only").description("Print prompt-only fallback rules.").argument("<host>", "claude, codex, gemini, cursor, or all").action(async (host) => {
245
+ process.stdout.write(await promptOnly(host));
246
+ });
247
+ }
248
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function runIndex(root?: string): Promise<string>;
3
+ export declare function registerIndexCommand(program: Command): void;
@@ -0,0 +1,28 @@
1
+ import path from "node:path";
2
+ import { ensureWorkspace, loadConfig, relativeToRoot } from "../core/config.js";
3
+ import { scanRepository, writeRepoMap } from "../core/file-scanner.js";
4
+ export async function runIndex(root = process.cwd()) {
5
+ await ensureWorkspace(root);
6
+ const config = await loadConfig(root);
7
+ const repoMap = await scanRepository(root, config);
8
+ const written = await writeRepoMap(root, repoMap);
9
+ const lines = [
10
+ "Heuristic Repo Map written.",
11
+ `Files indexed: ${repoMap.total_files}`,
12
+ `Ignored files: ${repoMap.stats.ignored_files}`,
13
+ `Ignored directories: ${repoMap.stats.ignored_directories}`,
14
+ `Repo map: ${relativeToRoot(root, written.repoMapPath)}`,
15
+ `Tree: ${relativeToRoot(root, written.treePath)}`
16
+ ];
17
+ return `${lines.join("\n")}\n`;
18
+ }
19
+ export function registerIndexCommand(program) {
20
+ program
21
+ .command("index")
22
+ .description("Scan the repository and write a Heuristic Repo Map.")
23
+ .action(async () => {
24
+ const output = await runIndex(path.resolve(process.cwd()));
25
+ process.stdout.write(output);
26
+ });
27
+ }
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;IACjD,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG;QACZ,6BAA6B;QAC7B,kBAAkB,OAAO,CAAC,WAAW,EAAE;QACvC,kBAAkB,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE;QAC/C,wBAAwB,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE;QAC3D,aAAa,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,EAAE;QACxD,SAAS,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE;KAClD,CAAC;IACF,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,qDAAqD,CAAC;SAClE,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Command } from "commander";
2
+ export interface IngestOptions {
3
+ type?: string;
4
+ }
5
+ export declare function ingestCommand(file: string, options?: IngestOptions, root?: string): Promise<string>;
6
+ export declare function registerIngestCommand(program: Command): void;
@@ -0,0 +1,52 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { ensureWorkspace, getWorkspacePaths, writeJson } from "../core/config.js";
4
+ import { ingestDocument, isIngestType } from "../core/document-ingest.js";
5
+ import { MetricsStore } from "../core/metrics-store.js";
6
+ import { extractRules, rulesToYaml } from "../core/rule-extractor.js";
7
+ export async function ingestCommand(file, options = {}, root = process.cwd()) {
8
+ await ensureWorkspace(root);
9
+ const type = options.type ?? "docs";
10
+ if (!isIngestType(type)) {
11
+ throw new Error(`Unknown ingest type "${type}".`);
12
+ }
13
+ const paths = getWorkspacePaths(root);
14
+ const document = await ingestDocument(file, type, root);
15
+ const rules = extractRules(document);
16
+ await fs.mkdir(paths.rulesValidatorsDir, { recursive: true });
17
+ await fs.writeFile(paths.rulesFile, rulesToYaml(rules), "utf8");
18
+ await fs.writeFile(paths.rulesChecklist, `# SotuRail Rules Checklist\n\n${rules.map((rule) => `- [ ] ${rule.title} (${rule.severity})`).join("\n")}\n`, "utf8");
19
+ await writeJson(paths.rulesCitations, rules.map((rule) => ({
20
+ id: rule.id,
21
+ source_file: rule.source_file,
22
+ source_section: rule.source_section,
23
+ content_hash: rule.content_hash
24
+ })));
25
+ await new MetricsStore(root).append({
26
+ type: "rules_ingest",
27
+ details: {
28
+ file: path.normalize(file).replace(/\\/g, "/"),
29
+ ingest_type: type,
30
+ extracted_rules_count: rules.length,
31
+ citations_count: rules.length
32
+ }
33
+ });
34
+ return [
35
+ `Ingested ${document.source_file} as ${type}.`,
36
+ `Extracted rules: ${rules.length}`,
37
+ `Rules: ${path.normalize(path.relative(root, paths.rulesFile)).replace(/\\/g, "/")}`,
38
+ `Checklist: ${path.normalize(path.relative(root, paths.rulesChecklist)).replace(/\\/g, "/")}`,
39
+ `Citations: ${path.normalize(path.relative(root, paths.rulesCitations)).replace(/\\/g, "/")}`
40
+ ].join("\n") + "\n";
41
+ }
42
+ export function registerIngestCommand(program) {
43
+ program
44
+ .command("ingest")
45
+ .description("Ingest Markdown, TXT, JSON or YAML into structured local rules.")
46
+ .argument("<file>", "Source file")
47
+ .option("--type <type>", "rules, requirements, docs, or course", "docs")
48
+ .action(async (file, options) => {
49
+ process.stdout.write(await ingestCommand(file, options));
50
+ });
51
+ }
52
+ //# sourceMappingURL=ingest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/commands/ingest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Command } from "commander";
2
+ import { type EnsureResult } from "../core/config.js";
3
+ export interface InitReport extends EnsureResult {
4
+ }
5
+ export declare function runInit(root?: string): Promise<InitReport>;
6
+ export declare function formatInitReport(report: InitReport): string;
7
+ export declare function registerInitCommand(program: Command): void;
@@ -0,0 +1,179 @@
1
+ import path from "node:path";
2
+ import { ensureWorkspace, writeFileIfMissing } from "../core/config.js";
3
+ const text = {
4
+ agents: `# AGENTS.md
5
+
6
+ SotuRail context rails for AI coding agents.
7
+
8
+ - Prefer soturail index before asking an agent to reason about the whole repo.
9
+ - Use soturail read <file> --query "goal" for large files.
10
+ - Run commands through soturail run so raw logs remain recoverable.
11
+ - Do not run git push automatically.
12
+ `,
13
+ claude: `# CLAUDE.md
14
+
15
+ Claude Code should treat SotuRail output as scoped context.
16
+
17
+ Stable project blocks belong before dynamic session data. Raw logs are recoverable with soturail expand <raw_id>.
18
+ `,
19
+ gemini: `# GEMINI.md
20
+
21
+ Gemini CLI should use SotuRail as a local context boundary.
22
+
23
+ Prefer repo maps and progressive reads over dumping entire repositories into prompts.
24
+ `,
25
+ architecture: `# Architecture
26
+
27
+ SotuRail v0.1.0 is local-first TypeScript/Node.js software.
28
+
29
+ Core loops:
30
+
31
+ 1. Scan the repository into a Heuristic Repo Map.
32
+ 2. Read large files progressively.
33
+ 3. Run commands through a safe tee-stream adapter.
34
+ 4. Store raw logs locally and summarize them for agent context.
35
+ 5. Keep stable prompt blocks before dynamic session data.
36
+ `,
37
+ mvp: `# MVP
38
+
39
+ SotuRail v0.1.0 focuses on functional local rails:
40
+
41
+ - init
42
+ - index
43
+ - read
44
+ - run
45
+ - expand
46
+ - spec
47
+ - memory
48
+ - doctor
49
+ - stats
50
+ `,
51
+ usage: `# Usage
52
+
53
+ \`\`\`bash
54
+ soturail init
55
+ soturail index
56
+ soturail read src/cli.ts --query "commands"
57
+ soturail run npm test
58
+ soturail expand <raw_id>
59
+ \`\`\`
60
+ `,
61
+ security: `# Security Model
62
+
63
+ SotuRail blocks dangerous commands by default, including rm -rf, sudo, format, dd if=, curl | sh, del /s and git push.
64
+
65
+ Bypass requires the exact unsafe confirmation phrase and should be used rarely.
66
+ `,
67
+ caching: `# Prompt Caching
68
+
69
+ SotuRail separates stable and dynamic blocks. Dynamic data such as timestamps, raw ids, command status and recent logs belongs after stable governance, config, repo maps, approved specs and approved memory.
70
+
71
+ v0.1.0 reports estimated cache stability only. It does not claim provider cache hits.
72
+ `,
73
+ metrics: `# Metrics
74
+
75
+ Metrics are append-only local JSONL events. Token counts are deterministic estimates and not provider billing numbers.
76
+ `,
77
+ release: `# Release Checklist
78
+
79
+ - npm install
80
+ - npm run build
81
+ - npm test
82
+ - Review docs and security model
83
+ - Confirm no telemetry exists
84
+ - Do not git push automatically
85
+ `,
86
+ screenshots: `# Screenshots
87
+
88
+ Add terminal screenshots or Markdown previews here when preparing releases.
89
+ `,
90
+ ptBr: `# Visao geral
91
+
92
+ SotuRail e um Context OS local-first para agentes de IA de desenvolvimento. Ele indexa repositorios, le arquivos progressivamente, executa comandos com logs brutos recuperaveis e organiza contexto estavel para prompt caching.
93
+ `,
94
+ benchmarking: `# Benchmarking
95
+
96
+ Run soturail bench prepare, soturail bench run and soturail bench report to generate local benchmark evidence.
97
+ `,
98
+ hooks: `# Agent Hooks
99
+
100
+ SotuRail provides dry-run hook installers and prompt-only fallbacks for Claude, Codex, Gemini and Cursor.
101
+ `,
102
+ nativeRunner: `# Native Runner
103
+
104
+ The optional Rust reducer engine is detected automatically and TypeScript reducers remain the fallback.
105
+ `,
106
+ reducers: `# Reducers
107
+
108
+ SotuRail reducers preserve raw-log recovery while compressing Git, test, JSON and generic output.
109
+ `,
110
+ responseCompression: `# Agent Response Compression
111
+
112
+ soturail format compresses verbose AI responses into deterministic task-oriented modes.
113
+ `,
114
+ knowledgeToRules: `# Knowledge-to-Rules
115
+
116
+ soturail ingest turns Markdown, TXT, JSON and YAML into structured local rules and validators.
117
+ `,
118
+ rules: `# Rules
119
+
120
+ Rules are stored under .soturail/rules and can be listed, checked and exported.
121
+ `,
122
+ specWorkflow: `# Spec-Driven Workflow
123
+
124
+ soturail spec manages constitution, spec, plan, tasks, verification, context budget and security impact files.
125
+ `
126
+ };
127
+ function documentationTemplates(root) {
128
+ return [
129
+ { path: path.resolve(root, "AGENTS.md"), content: text.agents },
130
+ { path: path.resolve(root, "CLAUDE.md"), content: text.claude },
131
+ { path: path.resolve(root, "GEMINI.md"), content: text.gemini },
132
+ { path: path.resolve(root, "docs", "architecture.md"), content: text.architecture },
133
+ { path: path.resolve(root, "docs", "mvp.md"), content: text.mvp },
134
+ { path: path.resolve(root, "docs", "usage.md"), content: text.usage },
135
+ { path: path.resolve(root, "docs", "security-model.md"), content: text.security },
136
+ { path: path.resolve(root, "docs", "prompt-caching.md"), content: text.caching },
137
+ { path: path.resolve(root, "docs", "metrics.md"), content: text.metrics },
138
+ { path: path.resolve(root, "docs", "benchmarking.md"), content: text.benchmarking },
139
+ { path: path.resolve(root, "docs", "hooks.md"), content: text.hooks },
140
+ { path: path.resolve(root, "docs", "native-runner.md"), content: text.nativeRunner },
141
+ { path: path.resolve(root, "docs", "reducers.md"), content: text.reducers },
142
+ { path: path.resolve(root, "docs", "response-compression.md"), content: text.responseCompression },
143
+ { path: path.resolve(root, "docs", "knowledge-to-rules.md"), content: text.knowledgeToRules },
144
+ { path: path.resolve(root, "docs", "rules.md"), content: text.rules },
145
+ { path: path.resolve(root, "docs", "spec-driven-workflow.md"), content: text.specWorkflow },
146
+ { path: path.resolve(root, "docs", "release-checklist.md"), content: text.release },
147
+ { path: path.resolve(root, "docs", "assets", "screenshots", "README.md"), content: text.screenshots },
148
+ { path: path.resolve(root, "docs", "pt-BR", "visao-geral.md"), content: text.ptBr }
149
+ ];
150
+ }
151
+ export async function runInit(root = process.cwd()) {
152
+ const report = await ensureWorkspace(root);
153
+ for (const template of documentationTemplates(root)) {
154
+ await writeFileIfMissing(template.path, template.content, report, root);
155
+ }
156
+ return report;
157
+ }
158
+ export function formatInitReport(report) {
159
+ const lines = [
160
+ "SotuRail workspace initialized.",
161
+ "",
162
+ `Created paths (${report.created.length}):`,
163
+ ...(report.created.length > 0 ? report.created.map((item) => ` + ${item}`) : [" none"]),
164
+ "",
165
+ `Skipped existing paths (${report.skipped.length}):`,
166
+ ...(report.skipped.length > 0 ? report.skipped.map((item) => ` = ${item}`) : [" none"])
167
+ ];
168
+ return `${lines.join("\n")}\n`;
169
+ }
170
+ export function registerInitCommand(program) {
171
+ program
172
+ .command("init")
173
+ .description("Create a non-destructive .soturail workspace and starter docs.")
174
+ .action(async () => {
175
+ const report = await runInit();
176
+ process.stdout.write(formatInitReport(report));
177
+ });
178
+ }
179
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}