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,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":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA0BzC,MAAM,WAAW,GAAG,4KAA4K,CAAC;AAEjM,MAAM,UAAU,YAAY,CAAC,QAA0B,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;IACrF,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO;aAC/B,KAAK,CAAC,OAAO,CAAC;aACd,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;aAC7E,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/D,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;YAC/B,MAAM,cAAc,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;YACtD,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;YACnC,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC,CAAC;YAC7E,KAAK,CAAC,IAAI,CAAC;gBACT,EAAE;gBACF,KAAK;gBACL,WAAW,EAAE,SAAS;gBACtB,WAAW,EAAE,QAAQ,CAAC,WAAW;gBACjC,cAAc,EAAE,OAAO,CAAC,KAAK;gBAC7B,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC;gBAClC,eAAe,EAAE,cAAc;gBAC/B,wBAAwB,EAAE,aAAa,CAAC,cAAc,EAAE,KAAK,CAAC;gBAC9D,UAAU,EAAE,GAAG;gBACf,YAAY,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aACnE,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa;IAC7B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,6DAA6D,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7E,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAY;IACvC,IAAI,8CAA8C,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9D,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IACD,IAAI,8DAA8D,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9E,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,IAAI,qDAAqD,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACrE,OAAO,aAAa,CAAC;IACvB,CAAC;IACD,IAAI,qDAAqD,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACrE,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,IAAI,+FAA+F,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/G,OAAO,eAAe,CAAC;IACzB,CAAC;IACD,IAAI,2CAA2C,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3D,OAAO,wBAAwB,CAAC;IAClC,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,SAAS,aAAa,CAAC,IAAoB,EAAE,KAAa;IACxD,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAClG,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,WAAW,CAAC,KAAsB;IACzC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAsB;IAChD,OAAO,GAAG,KAAK;SACZ,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACZ;QACE,SAAS,IAAI,CAAC,EAAE,EAAE;QAClB,YAAY,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QACpC,kBAAkB,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;QAChD,kBAAkB,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;QAChD,qBAAqB,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE;QACtD,eAAe,IAAI,CAAC,QAAQ,EAAE;QAC9B,sBAAsB,IAAI,CAAC,eAAe,EAAE;QAC5C,+BAA+B,IAAI,CAAC,wBAAwB,EAAE;QAC9D,iBAAiB,IAAI,CAAC,UAAU,EAAE;QAClC,mBAAmB,IAAI,CAAC,YAAY,EAAE;KACvC,CAAC,IAAI,CAAC,IAAI,CAAC,CACb;SACA,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACpB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC1B,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;YACxD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACf,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC7B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAW,CAAC,CAAC,CAAC,KAAK,CAAC;YAClF,CAAC;QACH,CAAC;QACD,OAAO,MAAkC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -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":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAW7B,KAAK,UAAU,MAAM,CAAC,IAAY,EAAE,YAAoB;IACtD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAsB,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;IAC9E,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,MAAM,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAmB,EAAE,IAAY;IAC3D,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;IACjD,QAAQ,IAAI,CAAC,eAAe,EAAE,CAAC;QAC7B,KAAK,oBAAoB;YACvB,OAAO,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzC,KAAK,eAAe,CAAC;QACrB,KAAK,wBAAwB;YAC3B,OAAO,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAChD,KAAK,gBAAgB;YACnB,OAAO,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACjD,KAAK,gBAAgB;YACnB,OAAO,sBAAsB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAClD,KAAK,aAAa;YAChB,OAAO,MAAM,CAAC,IAAI,EAAE,MAAM,MAAM,CAAC,IAAI,EAAE,0BAA0B,CAAC,EAAE,kCAAkC,CAAC,CAAC;QAC1G,KAAK,cAAc;YACjB,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,6DAA6D,CAAC,CAAC;IAC7F,CAAC;AACH,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,IAAmB,EAAE,IAAY;IAClE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAoC,CAAC;QACzH,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC;QACrC,MAAM,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,gCAAgC,IAAI,IAAI,SAAS,EAAE,CAAC,CAAC;IAC/E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,iCAAiC,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,IAAmB,EAAE,IAAY,EAAE,IAAY;IACjF,MAAM,UAAU,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,CAAC;SACjI,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACzC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,qEAAqE,CAAC,CAAC;IACnG,CAAC;IACD,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACvI,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,IAAmB,EAAE,IAAY,EAAE,IAAY;IAClF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,iEAAiE,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC;AACtE,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,IAAmB,EAAE,IAAY,EAAE,IAAY;IACnF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC1F,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACxE,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,iDAAiD,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,UAAU,YAAY,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,0BAA0B,QAAQ,EAAE,CAAC,CAAC;AACpI,CAAC;AAED,SAAS,MAAM,CAAC,IAAmB,EAAE,EAAW,EAAE,OAAe;IAC/D,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,EAAE,CAAC;AACrG,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC"}
|
|
@@ -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,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 @@
|
|
|
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>
|