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.
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +54 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/bench.d.ts +31 -0
- package/dist/commands/bench.js +387 -0
- package/dist/commands/bench.js.map +1 -0
- package/dist/commands/dedupe.d.ts +2 -0
- package/dist/commands/dedupe.js +16 -0
- package/dist/commands/dedupe.js.map +1 -0
- package/dist/commands/doctor.d.ts +14 -0
- package/dist/commands/doctor.js +122 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/expand.d.ts +3 -0
- package/dist/commands/expand.js +23 -0
- package/dist/commands/expand.js.map +1 -0
- package/dist/commands/format.d.ts +6 -0
- package/dist/commands/format.js +48 -0
- package/dist/commands/format.js.map +1 -0
- package/dist/commands/hooks.d.ts +11 -0
- package/dist/commands/hooks.js +248 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.js +28 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/ingest.d.ts +6 -0
- package/dist/commands/ingest.js +52 -0
- package/dist/commands/ingest.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +179 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/memory.d.ts +39 -0
- package/dist/commands/memory.js +184 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/native.d.ts +2 -0
- package/dist/commands/native.js +17 -0
- package/dist/commands/native.js.map +1 -0
- package/dist/commands/read.d.ts +8 -0
- package/dist/commands/read.js +87 -0
- package/dist/commands/read.js.map +1 -0
- package/dist/commands/rules.d.ts +5 -0
- package/dist/commands/rules.js +62 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/commands/run.d.ts +22 -0
- package/dist/commands/run.js +200 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/spec.d.ts +11 -0
- package/dist/commands/spec.js +217 -0
- package/dist/commands/spec.js.map +1 -0
- package/dist/commands/stats.d.ts +15 -0
- package/dist/commands/stats.js +82 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/compressors/agent-response-reducer.d.ts +13 -0
- package/dist/compressors/agent-response-reducer.js +160 -0
- package/dist/compressors/agent-response-reducer.js.map +1 -0
- package/dist/compressors/generic-stream.d.ts +6 -0
- package/dist/compressors/generic-stream.js +63 -0
- package/dist/compressors/generic-stream.js.map +1 -0
- package/dist/compressors/git-reducer.d.ts +2 -0
- package/dist/compressors/git-reducer.js +78 -0
- package/dist/compressors/git-reducer.js.map +1 -0
- package/dist/compressors/index.d.ts +10 -0
- package/dist/compressors/index.js +84 -0
- package/dist/compressors/index.js.map +1 -0
- package/dist/compressors/json-toon.d.ts +14 -0
- package/dist/compressors/json-toon.js +149 -0
- package/dist/compressors/json-toon.js.map +1 -0
- package/dist/compressors/test-reducer.d.ts +2 -0
- package/dist/compressors/test-reducer.js +50 -0
- package/dist/compressors/test-reducer.js.map +1 -0
- package/dist/core/cache-normalizer.d.ts +23 -0
- package/dist/core/cache-normalizer.js +170 -0
- package/dist/core/cache-normalizer.js.map +1 -0
- package/dist/core/config.d.ts +104 -0
- package/dist/core/config.js +186 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/dedupe-store.d.ts +17 -0
- package/dist/core/dedupe-store.js +44 -0
- package/dist/core/dedupe-store.js.map +1 -0
- package/dist/core/document-ingest.d.ts +15 -0
- package/dist/core/document-ingest.js +45 -0
- package/dist/core/document-ingest.js.map +1 -0
- package/dist/core/file-scanner.d.ts +47 -0
- package/dist/core/file-scanner.js +248 -0
- package/dist/core/file-scanner.js.map +1 -0
- package/dist/core/git.d.ts +2 -0
- package/dist/core/git.js +29 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/metrics-store.d.ts +20 -0
- package/dist/core/metrics-store.js +19 -0
- package/dist/core/metrics-store.js.map +1 -0
- package/dist/core/native-engine.d.ts +25 -0
- package/dist/core/native-engine.js +137 -0
- package/dist/core/native-engine.js.map +1 -0
- package/dist/core/native-runner-adapter.d.ts +18 -0
- package/dist/core/native-runner-adapter.js +60 -0
- package/dist/core/native-runner-adapter.js.map +1 -0
- package/dist/core/raw-store.d.ts +28 -0
- package/dist/core/raw-store.js +57 -0
- package/dist/core/raw-store.js.map +1 -0
- package/dist/core/response-policy.d.ts +11 -0
- package/dist/core/response-policy.js +14 -0
- package/dist/core/response-policy.js.map +1 -0
- package/dist/core/rule-extractor.d.ts +18 -0
- package/dist/core/rule-extractor.js +116 -0
- package/dist/core/rule-extractor.js.map +1 -0
- package/dist/core/rule-validator.d.ts +9 -0
- package/dist/core/rule-validator.js +85 -0
- package/dist/core/rule-validator.js.map +1 -0
- package/dist/core/safety-policy.d.ts +10 -0
- package/dist/core/safety-policy.js +69 -0
- package/dist/core/safety-policy.js.map +1 -0
- package/dist/core/token-estimator.d.ts +2 -0
- package/dist/core/token-estimator.js +7 -0
- package/dist/core/token-estimator.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/docs/architecture.md +47 -0
- package/docs/assets/screenshots/README.md +18 -0
- package/docs/assets/screenshots/init.svg +13 -0
- package/docs/assets/screenshots/run-expand.svg +13 -0
- package/docs/assets/screenshots/stats.svg +11 -0
- package/docs/assets/soturail-fox.svg +17 -0
- package/docs/assets/soturail-icon.svg +10 -0
- package/docs/benchmarking.md +34 -0
- package/docs/branding.md +14 -0
- package/docs/hooks/claude.md +24 -0
- package/docs/hooks/codex.md +5 -0
- package/docs/hooks/cursor.md +5 -0
- package/docs/hooks/gemini.md +5 -0
- package/docs/hooks/host-capabilities.json +22 -0
- package/docs/hooks.md +15 -0
- package/docs/knowledge-to-rules.md +23 -0
- package/docs/metrics.md +35 -0
- package/docs/mvp.md +21 -0
- package/docs/native-runner.md +52 -0
- package/docs/prompt-caching.md +27 -0
- package/docs/pt-BR/visao-geral.md +26 -0
- package/docs/public-roadmap.md +20 -0
- package/docs/reducers.md +19 -0
- package/docs/release-checklist.md +13 -0
- package/docs/response-compression.md +15 -0
- package/docs/rules.md +23 -0
- package/docs/security-model.md +32 -0
- package/docs/spec-driven-workflow.md +22 -0
- package/docs/usage.md +64 -0
- 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,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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"}
|