soturail 0.2.2 → 0.3.0

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 (76) hide show
  1. package/README.md +55 -10
  2. package/dist/cli.js +11 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/bench.d.ts +1 -1
  5. package/dist/commands/bench.js +126 -2
  6. package/dist/commands/bench.js.map +1 -1
  7. package/dist/commands/context.d.ts +2 -0
  8. package/dist/commands/context.js +27 -0
  9. package/dist/commands/context.js.map +1 -0
  10. package/dist/commands/hooks.d.ts +3 -0
  11. package/dist/commands/hooks.js +32 -8
  12. package/dist/commands/hooks.js.map +1 -1
  13. package/dist/commands/mcp.d.ts +2 -0
  14. package/dist/commands/mcp.js +21 -0
  15. package/dist/commands/mcp.js.map +1 -0
  16. package/dist/commands/memory.d.ts +1 -0
  17. package/dist/commands/memory.js +2 -1
  18. package/dist/commands/memory.js.map +1 -1
  19. package/dist/commands/release.d.ts +3 -0
  20. package/dist/commands/release.js +227 -0
  21. package/dist/commands/release.js.map +1 -0
  22. package/dist/commands/skills.d.ts +2 -0
  23. package/dist/commands/skills.js +43 -0
  24. package/dist/commands/skills.js.map +1 -0
  25. package/dist/core/config.d.ts +5 -0
  26. package/dist/core/config.js +12 -2
  27. package/dist/core/config.js.map +1 -1
  28. package/dist/core/context-pack.d.ts +13 -0
  29. package/dist/core/context-pack.js +102 -0
  30. package/dist/core/context-pack.js.map +1 -0
  31. package/dist/core/mcp-resources.d.ts +13 -0
  32. package/dist/core/mcp-resources.js +46 -0
  33. package/dist/core/mcp-resources.js.map +1 -0
  34. package/dist/core/mcp-server.d.ts +14 -0
  35. package/dist/core/mcp-server.js +81 -0
  36. package/dist/core/mcp-server.js.map +1 -0
  37. package/dist/core/mcp-tools.d.ts +7 -0
  38. package/dist/core/mcp-tools.js +73 -0
  39. package/dist/core/mcp-tools.js.map +1 -0
  40. package/dist/core/release-preflight.d.ts +22 -0
  41. package/dist/core/release-preflight.js +201 -0
  42. package/dist/core/release-preflight.js.map +1 -0
  43. package/dist/core/skill-exporter.d.ts +3 -0
  44. package/dist/core/skill-exporter.js +72 -0
  45. package/dist/core/skill-exporter.js.map +1 -0
  46. package/dist/core/skill-schema.d.ts +55 -0
  47. package/dist/core/skill-schema.js +84 -0
  48. package/dist/core/skill-schema.js.map +1 -0
  49. package/dist/core/skill-store.d.ts +4 -0
  50. package/dist/core/skill-store.js +92 -0
  51. package/dist/core/skill-store.js.map +1 -0
  52. package/dist/core/skill-validator.d.ts +12 -0
  53. package/dist/core/skill-validator.js +94 -0
  54. package/dist/core/skill-validator.js.map +1 -0
  55. package/dist/core/version.d.ts +1 -0
  56. package/dist/core/version.js +3 -0
  57. package/dist/core/version.js.map +1 -0
  58. package/docs/benchmarking.md +6 -1
  59. package/docs/comparisons.md +35 -0
  60. package/docs/context-packs.md +29 -0
  61. package/docs/examples/skills/README.md +11 -0
  62. package/docs/hooks/claude.md +6 -4
  63. package/docs/hooks/codex.md +4 -3
  64. package/docs/hooks/cursor.md +4 -3
  65. package/docs/hooks/gemini.md +4 -3
  66. package/docs/hooks/mcp.md +10 -0
  67. package/docs/hooks.md +12 -16
  68. package/docs/mcp.md +18 -0
  69. package/docs/release-checklist.md +44 -1
  70. package/docs/release-workflow.md +41 -18
  71. package/docs/security-model.md +6 -0
  72. package/docs/skill-rail.md +14 -20
  73. package/docs/usage.md +15 -0
  74. package/docs/windows.md +22 -1
  75. package/docs/workflow-rail.md +1 -1
  76. package/package.json +8 -6
@@ -0,0 +1,72 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { ensureWorkspace, getWorkspacePaths } from "./config.js";
4
+ import { readSkills } from "./skill-store.js";
5
+ import { validateSkills } from "./skill-validator.js";
6
+ export async function exportSkills(target, root = process.cwd()) {
7
+ await ensureWorkspace(root);
8
+ const validation = await validateSkills(root);
9
+ if (!validation.ok) {
10
+ throw new Error("Cannot export invalid skills. Run soturail skills validate for details.");
11
+ }
12
+ const skills = await readSkills(root);
13
+ const paths = getWorkspacePaths(root);
14
+ const targetDir = path.join(paths.skillExportsDir, target);
15
+ await fs.mkdir(targetDir, { recursive: true });
16
+ const written = [];
17
+ for (const skill of skills) {
18
+ const filePath = path.join(targetDir, `${skill.metadata.id}.md`);
19
+ await fs.writeFile(filePath, renderTargetSkill(target, skill.metadata.id, skill.markdown), "utf8");
20
+ written.push(path.normalize(path.relative(root, filePath)));
21
+ }
22
+ const indexPath = path.join(targetDir, "index.md");
23
+ await fs.writeFile(indexPath, renderExportIndex(target, skills.map((skill) => skill.metadata.id)), "utf8");
24
+ written.push(path.normalize(path.relative(root, indexPath)));
25
+ return `Exported ${skills.length} skill(s) for ${target}:\n${written.join("\n")}\n`;
26
+ }
27
+ export async function packSkills(format, root = process.cwd()) {
28
+ await ensureWorkspace(root);
29
+ const skills = await readSkills(root);
30
+ const paths = getWorkspacePaths(root);
31
+ const filePath = path.join(paths.skillExportsDir, `skills-pack.${format === "json" ? "json" : "md"}`);
32
+ const payload = format === "json"
33
+ ? `${JSON.stringify(skills.map((skill) => ({ metadata: skill.metadata, markdown: skill.markdown })), null, 2)}\n`
34
+ : skills.map((skill) => renderTargetSkill("generic", skill.metadata.id, skill.markdown)).join("\n---\n");
35
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
36
+ await fs.writeFile(filePath, payload, "utf8");
37
+ return `Skill pack written: ${path.normalize(path.relative(root, filePath))}\n`;
38
+ }
39
+ function renderTargetSkill(target, id, markdown) {
40
+ const header = [
41
+ `# SotuRail Skill: ${id}`,
42
+ "",
43
+ `Target: ${target}`,
44
+ "Generated by SotuRail Skill Rail as an independent local-first export.",
45
+ "",
46
+ "Safety constraints:",
47
+ "- Do not run destructive shell commands.",
48
+ "- Do not exfiltrate secrets.",
49
+ "- Ask for human approval before remote writes or destructive actions.",
50
+ "- Treat this export as project-local guidance, not a marketplace install.",
51
+ ""
52
+ ];
53
+ const targetNote = {
54
+ claude: "Claude usage: place this in a reviewed skill/rules location supported by your installed Claude Code version.",
55
+ codex: "Codex usage: include this in AGENTS.md or project rules after review.",
56
+ gemini: "Gemini usage: include this as concise project guidance after review.",
57
+ cursor: "Cursor usage: include this in .cursor/rules after review.",
58
+ generic: "Generic usage: portable Markdown guidance for local AI agent workflows."
59
+ };
60
+ return `${header.join("\n")}${targetNote[target]}\n\n${markdown.trim()}\n`;
61
+ }
62
+ function renderExportIndex(target, ids) {
63
+ return [
64
+ `# SotuRail ${target} Skill Export`,
65
+ "",
66
+ "Review all generated files before enabling them in an agent host.",
67
+ "",
68
+ ...ids.map((id) => `- [${id}](./${id}.md)`),
69
+ ""
70
+ ].join("\n");
71
+ }
72
+ //# sourceMappingURL=skill-exporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-exporter.js","sourceRoot":"","sources":["../../src/core/skill-exporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { z } from "zod";
2
+ export declare const SkillTargetSchema: z.ZodEnum<["claude", "codex", "gemini", "cursor", "generic"]>;
3
+ export type SkillTarget = z.infer<typeof SkillTargetSchema>;
4
+ export declare const SkillMetadataSchema: z.ZodObject<{
5
+ id: z.ZodString;
6
+ name: z.ZodString;
7
+ description: z.ZodString;
8
+ version: z.ZodString;
9
+ author: z.ZodString;
10
+ risk_level: z.ZodEnum<["low", "medium", "high"]>;
11
+ targets: z.ZodArray<z.ZodEnum<["claude", "codex", "gemini", "cursor", "generic"]>, "many">;
12
+ allowed_tools: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
13
+ forbidden_patterns: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
14
+ requires_human_approval: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
15
+ created_at: z.ZodString;
16
+ content_hash: z.ZodString;
17
+ }, "strip", z.ZodTypeAny, {
18
+ version: string;
19
+ id: string;
20
+ name: string;
21
+ description: string;
22
+ author: string;
23
+ risk_level: "low" | "medium" | "high";
24
+ targets: ("claude" | "codex" | "gemini" | "cursor" | "generic")[];
25
+ allowed_tools: string[];
26
+ forbidden_patterns: string[];
27
+ requires_human_approval: string[];
28
+ created_at: string;
29
+ content_hash: string;
30
+ }, {
31
+ version: string;
32
+ id: string;
33
+ name: string;
34
+ description: string;
35
+ author: string;
36
+ risk_level: "low" | "medium" | "high";
37
+ targets: ("claude" | "codex" | "gemini" | "cursor" | "generic")[];
38
+ created_at: string;
39
+ content_hash: string;
40
+ allowed_tools?: string[] | undefined;
41
+ forbidden_patterns?: string[] | undefined;
42
+ requires_human_approval?: string[] | undefined;
43
+ }>;
44
+ export type SkillMetadata = z.infer<typeof SkillMetadataSchema>;
45
+ export interface SkillRecord {
46
+ metadata: SkillMetadata;
47
+ markdown: string;
48
+ dir: string;
49
+ }
50
+ export declare const defaultForbiddenPatterns: string[];
51
+ export declare const defaultHumanApprovals: string[];
52
+ export declare function slugifySkillName(name: string): string;
53
+ export declare function stableSkillHash(metadata: Omit<SkillMetadata, "content_hash">, markdown: string): string;
54
+ export declare function stringifySkillYaml(metadata: SkillMetadata): string;
55
+ export declare function parseSkillYaml(text: string): Record<string, unknown>;
@@ -0,0 +1,84 @@
1
+ import { createHash } from "node:crypto";
2
+ import { z } from "zod";
3
+ export const SkillTargetSchema = z.enum(["claude", "codex", "gemini", "cursor", "generic"]);
4
+ export const SkillMetadataSchema = z.object({
5
+ id: z.string().min(1).regex(/^[a-z0-9][a-z0-9-]*$/),
6
+ name: z.string().min(1),
7
+ description: z.string().min(1),
8
+ version: z.string().min(1),
9
+ author: z.string().min(1),
10
+ risk_level: z.enum(["low", "medium", "high"]),
11
+ targets: z.array(SkillTargetSchema).min(1),
12
+ allowed_tools: z.array(z.string()).default([]),
13
+ forbidden_patterns: z.array(z.string()).default([]),
14
+ requires_human_approval: z.array(z.string()).default([]),
15
+ created_at: z.string().min(1),
16
+ content_hash: z.string().regex(/^[a-f0-9]{64}$/)
17
+ });
18
+ export const defaultForbiddenPatterns = ["rm -rf", "curl | sh", "git push"];
19
+ export const defaultHumanApprovals = ["remote_write", "destructive_command"];
20
+ export function slugifySkillName(name) {
21
+ return name
22
+ .trim()
23
+ .toLowerCase()
24
+ .replace(/[^a-z0-9]+/g, "-")
25
+ .replace(/^-+|-+$/g, "")
26
+ .slice(0, 64) || "skill";
27
+ }
28
+ export function stableSkillHash(metadata, markdown) {
29
+ const normalized = JSON.stringify({ ...metadata, content_hash: undefined, markdown });
30
+ return createHash("sha256").update(normalized).digest("hex");
31
+ }
32
+ export function stringifySkillYaml(metadata) {
33
+ const scalar = (value) => value.includes(":") || value.includes("#") ? JSON.stringify(value) : value;
34
+ const lines = [];
35
+ for (const [key, value] of Object.entries(metadata)) {
36
+ if (Array.isArray(value)) {
37
+ lines.push(`${key}:`);
38
+ for (const item of value)
39
+ lines.push(` - ${scalar(item)}`);
40
+ }
41
+ else {
42
+ lines.push(`${key}: ${scalar(String(value))}`);
43
+ }
44
+ }
45
+ return `${lines.join("\n")}\n`;
46
+ }
47
+ export function parseSkillYaml(text) {
48
+ const result = {};
49
+ let currentArray = null;
50
+ for (const rawLine of text.split(/\r?\n/)) {
51
+ const line = rawLine.trimEnd();
52
+ if (!line.trim() || line.trimStart().startsWith("#"))
53
+ continue;
54
+ const arrayMatch = line.match(/^([A-Za-z0-9_]+):\s*$/);
55
+ if (arrayMatch?.[1]) {
56
+ currentArray = arrayMatch[1];
57
+ result[currentArray] = [];
58
+ continue;
59
+ }
60
+ const itemMatch = line.match(/^\s*-\s*(.+)$/);
61
+ if (itemMatch?.[1] && currentArray && Array.isArray(result[currentArray])) {
62
+ result[currentArray].push(unquote(itemMatch[1].trim()));
63
+ continue;
64
+ }
65
+ const kv = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
66
+ if (kv?.[1]) {
67
+ currentArray = null;
68
+ result[kv[1]] = unquote(kv[2] ?? "");
69
+ }
70
+ }
71
+ return result;
72
+ }
73
+ function unquote(value) {
74
+ if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
75
+ try {
76
+ return JSON.parse(value);
77
+ }
78
+ catch {
79
+ return value.slice(1, -1);
80
+ }
81
+ }
82
+ return value;
83
+ }
84
+ //# sourceMappingURL=skill-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-schema.js","sourceRoot":"","sources":["../../src/core/skill-schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type SkillRecord } from "./skill-schema.js";
2
+ export declare function createSkill(name: string, root?: string): Promise<SkillRecord>;
3
+ export declare function readSkills(root?: string): Promise<SkillRecord[]>;
4
+ export declare function renderSkillList(skills: SkillRecord[]): string;
@@ -0,0 +1,92 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { ensureWorkspace, getWorkspacePaths } from "./config.js";
4
+ import { defaultForbiddenPatterns, defaultHumanApprovals, parseSkillYaml, SkillMetadataSchema, slugifySkillName, stableSkillHash, stringifySkillYaml } from "./skill-schema.js";
5
+ export async function createSkill(name, root = process.cwd()) {
6
+ await ensureWorkspace(root);
7
+ const paths = getWorkspacePaths(root);
8
+ const id = slugifySkillName(name);
9
+ const dir = path.join(paths.skillsDir, id);
10
+ const markdown = [
11
+ `# ${name}`,
12
+ "",
13
+ "Use this skill when the task matches the description and the repository owner has approved it.",
14
+ "",
15
+ "## Safety",
16
+ "",
17
+ "- Do not run destructive shell commands.",
18
+ "- Do not exfiltrate secrets.",
19
+ "- Ask for human approval before remote writes or destructive actions.",
20
+ ""
21
+ ].join("\n");
22
+ const base = {
23
+ id,
24
+ name,
25
+ description: `Safe local-first workflow skill for ${name}.`,
26
+ version: "0.1.0",
27
+ author: "Rafael Ryan Ramos de Souza",
28
+ risk_level: "low",
29
+ targets: ["claude", "codex", "gemini", "cursor"],
30
+ allowed_tools: ["read", "search"],
31
+ forbidden_patterns: defaultForbiddenPatterns,
32
+ requires_human_approval: defaultHumanApprovals,
33
+ created_at: new Date().toISOString()
34
+ };
35
+ const metadata = { ...base, content_hash: stableSkillHash(base, markdown) };
36
+ await fs.mkdir(path.join(dir, "examples"), { recursive: true });
37
+ await fs.mkdir(path.join(dir, "validators"), { recursive: true });
38
+ await fs.writeFile(path.join(dir, "skill.yml"), stringifySkillYaml(metadata), "utf8");
39
+ await fs.writeFile(path.join(dir, "SKILL.md"), markdown, "utf8");
40
+ return { metadata, markdown, dir };
41
+ }
42
+ export async function readSkills(root = process.cwd()) {
43
+ const paths = getWorkspacePaths(root);
44
+ let entries = [];
45
+ try {
46
+ entries = await fs.readdir(paths.skillsDir);
47
+ }
48
+ catch {
49
+ return [];
50
+ }
51
+ const skills = [];
52
+ for (const entry of entries) {
53
+ const dir = path.join(paths.skillsDir, entry);
54
+ const stat = await fs.stat(dir).catch(() => null);
55
+ if (!stat?.isDirectory())
56
+ continue;
57
+ const yaml = await fs.readFile(path.join(dir, "skill.yml"), "utf8").catch(() => "");
58
+ const markdown = await fs.readFile(path.join(dir, "SKILL.md"), "utf8").catch(() => "");
59
+ const parsed = parseSkillYaml(yaml);
60
+ const result = SkillMetadataSchema.safeParse(parsed);
61
+ if (result.success) {
62
+ skills.push({ metadata: result.data, markdown, dir });
63
+ }
64
+ else {
65
+ skills.push({
66
+ metadata: {
67
+ id: entry,
68
+ name: entry,
69
+ description: "Invalid skill metadata",
70
+ version: "0.0.0",
71
+ author: "unknown",
72
+ risk_level: "high",
73
+ targets: ["generic"],
74
+ allowed_tools: [],
75
+ forbidden_patterns: [],
76
+ requires_human_approval: [],
77
+ created_at: "invalid",
78
+ content_hash: "0".repeat(64)
79
+ },
80
+ markdown,
81
+ dir
82
+ });
83
+ }
84
+ }
85
+ return skills.sort((left, right) => left.metadata.id.localeCompare(right.metadata.id));
86
+ }
87
+ export function renderSkillList(skills) {
88
+ if (skills.length === 0)
89
+ return "No skills found. Run soturail skills init <name> first.\n";
90
+ return skills.map((skill) => `${skill.metadata.id} [${skill.metadata.risk_level}] ${skill.metadata.name}`).join("\n") + "\n";
91
+ }
92
+ //# sourceMappingURL=skill-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-store.js","sourceRoot":"","sources":["../../src/core/skill-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface SkillValidationIssue {
2
+ skill_id: string;
3
+ severity: "error" | "warning";
4
+ message: string;
5
+ }
6
+ export interface SkillValidationResult {
7
+ ok: boolean;
8
+ skills_count: number;
9
+ issues: SkillValidationIssue[];
10
+ }
11
+ export declare function validateSkills(root?: string): Promise<SkillValidationResult>;
12
+ export declare function formatSkillValidation(result: SkillValidationResult): string;
@@ -0,0 +1,94 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { parseSkillYaml, SkillMetadataSchema, stableSkillHash } from "./skill-schema.js";
4
+ import { readSkills } from "./skill-store.js";
5
+ const destructivePatterns = [
6
+ /\brm\s+-[^"\n]*r[^"\n]*f\b/i,
7
+ /\bcurl\b[^|\n]*\|\s*(sh|bash)\b/i,
8
+ /\bwget\b[^|\n]*\|\s*(sh|bash)\b/i,
9
+ /\bgit\s+push\b/i,
10
+ /\bdd\s+[^"\n]*\bif=/i,
11
+ /\bdel\s+\/s\b/i
12
+ ];
13
+ const promptInjectionPatterns = [
14
+ /ignore (all )?(previous|prior) instructions/i,
15
+ /disregard (all )?(previous|prior) instructions/i,
16
+ /you are no longer bound/i
17
+ ];
18
+ const secretPatterns = [
19
+ /sk-[A-Za-z0-9_-]{20,}/,
20
+ /AKIA[0-9A-Z]{16}/,
21
+ /-----BEGIN (RSA |OPENSSH |EC )?PRIVATE KEY-----/,
22
+ /(password|api[_-]?key|secret)\s*[:=]\s*["']?[A-Za-z0-9_\-]{16,}/i
23
+ ];
24
+ export async function validateSkills(root = process.cwd()) {
25
+ const skills = await readSkills(root);
26
+ const issues = [];
27
+ const seen = new Set();
28
+ for (const skill of skills) {
29
+ await validateSkillFiles(skill, issues);
30
+ if (seen.has(skill.metadata.id)) {
31
+ issues.push({ skill_id: skill.metadata.id, severity: "error", message: "Duplicate skill id." });
32
+ }
33
+ seen.add(skill.metadata.id);
34
+ const combined = `${skill.markdown}\n${JSON.stringify({ ...skill.metadata, forbidden_patterns: [] })}`;
35
+ for (const pattern of destructivePatterns) {
36
+ if (pattern.test(skill.markdown)) {
37
+ issues.push({ skill_id: skill.metadata.id, severity: "error", message: `Hidden destructive command pattern detected: ${pattern}` });
38
+ }
39
+ }
40
+ for (const pattern of promptInjectionPatterns) {
41
+ if (pattern.test(combined)) {
42
+ issues.push({ skill_id: skill.metadata.id, severity: "error", message: `Prompt-injection style instruction detected: ${pattern}` });
43
+ }
44
+ }
45
+ for (const pattern of secretPatterns) {
46
+ if (pattern.test(combined)) {
47
+ issues.push({ skill_id: skill.metadata.id, severity: "error", message: "Probable embedded secret detected." });
48
+ }
49
+ }
50
+ const { content_hash: _hash, ...withoutHash } = skill.metadata;
51
+ const actual = stableSkillHash(withoutHash, skill.markdown);
52
+ if (actual !== skill.metadata.content_hash) {
53
+ issues.push({ skill_id: skill.metadata.id, severity: "error", message: "content_hash does not match skill.yml and SKILL.md content." });
54
+ }
55
+ }
56
+ return {
57
+ ok: issues.every((issue) => issue.severity !== "error"),
58
+ skills_count: skills.length,
59
+ issues
60
+ };
61
+ }
62
+ export function formatSkillValidation(result) {
63
+ const lines = [
64
+ `Skill validation: ${result.ok ? "passed" : "failed"}`,
65
+ `skills_count: ${result.skills_count}`
66
+ ];
67
+ if (result.issues.length === 0) {
68
+ lines.push("issues: none");
69
+ }
70
+ else {
71
+ for (const issue of result.issues) {
72
+ lines.push(`- ${issue.severity.toUpperCase()} ${issue.skill_id}: ${issue.message}`);
73
+ }
74
+ }
75
+ return `${lines.join("\n")}\n`;
76
+ }
77
+ async function validateSkillFiles(skill, issues) {
78
+ const yamlPath = path.join(skill.dir, "skill.yml");
79
+ const markdownPath = path.join(skill.dir, "SKILL.md");
80
+ const rawYaml = await fs.readFile(yamlPath, "utf8").catch(() => "");
81
+ const parsed = parseSkillYaml(rawYaml);
82
+ const result = SkillMetadataSchema.safeParse(parsed);
83
+ if (!result.success) {
84
+ issues.push({
85
+ skill_id: skill.metadata.id,
86
+ severity: "error",
87
+ message: `Invalid skill.yml: ${result.error.issues.map((issue) => issue.path.join(".")).join(", ")}`
88
+ });
89
+ }
90
+ if (!await fs.access(markdownPath).then(() => true).catch(() => false)) {
91
+ issues.push({ skill_id: skill.metadata.id, severity: "error", message: "Missing SKILL.md." });
92
+ }
93
+ }
94
+ //# sourceMappingURL=skill-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-validator.js","sourceRoot":"","sources":["../../src/core/skill-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,eAAe,EAAoB,MAAM,mBAAmB,CAAC;AAC3G,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAc9C,MAAM,mBAAmB,GAAG;IAC1B,6BAA6B;IAC7B,kCAAkC;IAClC,kCAAkC;IAClC,iBAAiB;IACjB,sBAAsB;IACtB,gBAAgB;CACjB,CAAC;AAEF,MAAM,uBAAuB,GAAG;IAC9B,8CAA8C;IAC9C,iDAAiD;IACjD,0BAA0B;CAC3B,CAAC;AAEF,MAAM,cAAc,GAAG;IACrB,uBAAuB;IACvB,kBAAkB;IAClB,iDAAiD;IACjD,kEAAkE;CACnE,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;IACvD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAE5B,MAAM,QAAQ,GAAG,GAAG,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,kBAAkB,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;QACvG,KAAK,MAAM,OAAO,IAAI,mBAAmB,EAAE,CAAC;YAC1C,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,gDAAgD,OAAO,EAAE,EAAE,CAAC,CAAC;YACtI,CAAC;QACH,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,uBAAuB,EAAE,CAAC;YAC9C,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,gDAAgD,OAAO,EAAE,EAAE,CAAC,CAAC;YACtI,CAAC;QACH,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;YACrC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,oCAAoC,EAAE,CAAC,CAAC;YACjH,CAAC;QACH,CAAC;QAED,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,WAAW,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC/D,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC5D,IAAI,MAAM,KAAK,KAAK,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,6DAA6D,EAAE,CAAC,CAAC;QAC1I,CAAC;IACH,CAAC;IAED,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC;QACvD,YAAY,EAAE,MAAM,CAAC,MAAM;QAC3B,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAA6B;IACjE,MAAM,KAAK,GAAG;QACZ,qBAAqB,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;QACtD,iBAAiB,MAAM,CAAC,YAAY,EAAE;KACvC,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,KAAkB,EAAE,MAA8B;IAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACnD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC;YACV,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE;YAC3B,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,sBAAsB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SACrG,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;QACvE,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAChG,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const SOTURAIL_VERSION = "0.3.0";
@@ -0,0 +1,3 @@
1
+ // Generated by scripts/sync-version.mjs from package.json. Do not edit manually.
2
+ export const SOTURAIL_VERSION = "0.3.0";
3
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC"}
@@ -20,7 +20,12 @@ The suite groups results as:
20
20
  - agent response compression;
21
21
  - knowledge structuring;
22
22
  - cache stability;
23
- - native engine availability/performance when available.
23
+ - native engine availability/performance when available;
24
+ - skill rail validation/export;
25
+ - MCP resource listing/reading;
26
+ - context pack generation;
27
+ - agent hook export;
28
+ - memory approval workflow.
24
29
 
25
30
  Terminal compression includes npm install noise, Vitest failures, TypeScript diagnostics, git diff/status noise and JSON/tool payload output.
26
31
 
@@ -0,0 +1,35 @@
1
+ # Comparison Philosophy
2
+
3
+ SotuRail aims to unify these workflow ideas into one local-first, auditable, cache-friendly developer tool.
4
+
5
+ SotuRail is an independent implementation. It does not vendor or depend on RTK, Squeez, Caveman, MemPalace, Spec Kit, agent-skills, SkillsMP, Compozy, Superpowers, OpenSpec or Istara.
6
+
7
+ ## Terminal Compression
8
+
9
+ RTK-like terminal compression is conceptually related. SotuRail focuses on reversible raw logs, `raw_id` recovery and local safety policy. It does not claim to outperform RTK without reproducible benchmark evidence.
10
+
11
+ ## Hooks And Dedupe
12
+
13
+ Squeez-like hooks and dedupe are adjacent ideas. SotuRail has prompt-only fallbacks, conservative Claude hooks, context packs and local dedupe as an independent implementation.
14
+
15
+ ## Response Compression
16
+
17
+ Caveman-like response compression inspired the idea of shorter agent output. SotuRail implements professional deterministic modes that preserve code blocks, commands, paths and warnings.
18
+
19
+ ## Spec Workflows
20
+
21
+ Spec Kit-like workflows are related to SotuRail specs. SotuRail integrates specs with logs, metrics, memory and context packs.
22
+
23
+ ## Local Memory
24
+
25
+ MemPalace-like memory/evidence ideas are related. SotuRail uses local JSONL memory with approval workflow and stale marking, not full semantic memory yet.
26
+
27
+ ## Knowledge To Rules
28
+
29
+ Nicole-style knowledge-to-rules workflows are related. SotuRail has `ingest` and `rules` with future hardened PDF extraction.
30
+
31
+ ## Skills And Workflow Orchestration
32
+
33
+ Agent-skills and SkillsMP-like ecosystems are related to Skill Rail exports. Compozy, Superpowers and OpenSpec-style orchestration are future Workflow Rail territory.
34
+
35
+ SotuRail should not be described as better than these projects unless a specific local benchmark proves a specific metric.
@@ -0,0 +1,29 @@
1
+ # Context Packs
2
+
3
+ Context packs are target-aware Markdown payloads for AI coding agents.
4
+
5
+ ```bash
6
+ soturail context pack --target claude
7
+ soturail context pack --target codex
8
+ soturail context pack --target gemini
9
+ soturail context pack --target cursor
10
+ soturail context pack --target generic
11
+ soturail context explain
12
+ soturail context doctor
13
+ ```
14
+
15
+ Generated files live in `.soturail/context/`.
16
+
17
+ Stable-cache order:
18
+
19
+ 1. Static SotuRail header.
20
+ 2. Governance files summary.
21
+ 3. Project config.
22
+ 4. Repo map summary.
23
+ 5. Approved rules.
24
+ 6. Approved specs.
25
+ 7. Approved memory.
26
+ 8. Skills summary.
27
+ 9. Dynamic footer with timestamps, current commit, raw IDs and recent command notes.
28
+
29
+ Dynamic data never appears before stable blocks.
@@ -0,0 +1,11 @@
1
+ # Skill Examples
2
+
3
+ Generate local examples with:
4
+
5
+ ```bash
6
+ soturail skills init demo-skill
7
+ soturail skills validate
8
+ soturail skills export --target generic
9
+ ```
10
+
11
+ Generated skills live in `.soturail/skills/` and exports live in `.soturail/exports/skills/`.
@@ -1,11 +1,13 @@
1
1
  # Claude Hooks
2
2
 
3
- SotuRail v0.2.1 includes a conservative Claude Code hook template.
3
+ SotuRail v0.3.0 includes conservative Claude safe-hooks and MCP guidance.
4
4
 
5
5
  ```bash
6
- soturail hooks install claude --dry-run
7
- soturail hooks install claude
8
- soturail hooks uninstall claude
6
+ soturail hooks install --agent claude --mode safe-hooks --dry-run
7
+ soturail hooks install --agent claude --mode safe-hooks
8
+ soturail hooks install --agent claude --mode mcp
9
+ soturail hooks uninstall --agent claude
10
+ soturail hooks export --agent claude
9
11
  soturail hooks prompt-only claude
10
12
  ```
11
13
 
@@ -7,9 +7,10 @@ SotuRail does not assume private Codex host hook APIs. The fallback rules descri
7
7
  Useful commands:
8
8
 
9
9
  ```bash
10
- soturail hooks install codex --dry-run
11
- soturail hooks install codex
12
- soturail hooks uninstall codex
10
+ soturail hooks install --agent codex --mode prompt-only --dry-run
11
+ soturail hooks install --agent codex --mode prompt-only
12
+ soturail hooks uninstall --agent codex
13
+ soturail hooks export --agent codex
13
14
  soturail hooks prompt-only codex
14
15
  ```
15
16
 
@@ -7,9 +7,10 @@ Existing files are backed up before SotuRail adds its rules.
7
7
  Useful commands:
8
8
 
9
9
  ```bash
10
- soturail hooks install cursor --dry-run
11
- soturail hooks install cursor
12
- soturail hooks uninstall cursor
10
+ soturail hooks install --agent cursor --mode prompt-only --dry-run
11
+ soturail hooks install --agent cursor --mode prompt-only
12
+ soturail hooks uninstall --agent cursor
13
+ soturail hooks export --agent cursor
13
14
  soturail hooks prompt-only cursor
14
15
  ```
15
16
 
@@ -7,9 +7,10 @@ The generated rules keep repository scans, progressive file reads and raw log re
7
7
  Useful commands:
8
8
 
9
9
  ```bash
10
- soturail hooks install gemini --dry-run
11
- soturail hooks install gemini
12
- soturail hooks uninstall gemini
10
+ soturail hooks install --agent gemini --mode prompt-only --dry-run
11
+ soturail hooks install --agent gemini --mode prompt-only
12
+ soturail hooks uninstall --agent gemini
13
+ soturail hooks export --agent gemini
13
14
  soturail hooks prompt-only gemini
14
15
  ```
15
16
 
@@ -0,0 +1,10 @@
1
+ # MCP Hook Integration
2
+
3
+ Use MCP mode when an agent host supports reviewed local MCP server configuration.
4
+
5
+ ```bash
6
+ soturail hooks install --agent claude --mode mcp --dry-run
7
+ soturail mcp serve --transport stdio
8
+ ```
9
+
10
+ Review generated instructions before enabling. SotuRail's MCP server exposes read-only resources and safe tools; it does not expose arbitrary shell execution in v0.3.0.