@sna-sdk/core 0.0.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 (45) hide show
  1. package/bin/sna.js +18 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +104 -0
  4. package/dist/core/providers/claude-code.d.ts +9 -0
  5. package/dist/core/providers/claude-code.js +257 -0
  6. package/dist/core/providers/codex.d.ts +18 -0
  7. package/dist/core/providers/codex.js +14 -0
  8. package/dist/core/providers/index.d.ts +14 -0
  9. package/dist/core/providers/index.js +22 -0
  10. package/dist/core/providers/types.d.ts +52 -0
  11. package/dist/core/providers/types.js +0 -0
  12. package/dist/db/schema.d.ts +13 -0
  13. package/dist/db/schema.js +41 -0
  14. package/dist/index.d.ts +15 -0
  15. package/dist/index.js +6 -0
  16. package/dist/lib/logger.d.ts +18 -0
  17. package/dist/lib/logger.js +50 -0
  18. package/dist/lib/sna-run.d.ts +25 -0
  19. package/dist/lib/sna-run.js +74 -0
  20. package/dist/scripts/emit.d.ts +2 -0
  21. package/dist/scripts/emit.js +48 -0
  22. package/dist/scripts/hook.d.ts +2 -0
  23. package/dist/scripts/hook.js +34 -0
  24. package/dist/scripts/init-db.d.ts +2 -0
  25. package/dist/scripts/init-db.js +3 -0
  26. package/dist/scripts/sna.d.ts +2 -0
  27. package/dist/scripts/sna.js +650 -0
  28. package/dist/scripts/workflow.d.ts +112 -0
  29. package/dist/scripts/workflow.js +622 -0
  30. package/dist/server/index.d.ts +30 -0
  31. package/dist/server/index.js +43 -0
  32. package/dist/server/routes/agent.d.ts +8 -0
  33. package/dist/server/routes/agent.js +148 -0
  34. package/dist/server/routes/emit.d.ts +11 -0
  35. package/dist/server/routes/emit.js +15 -0
  36. package/dist/server/routes/events.d.ts +12 -0
  37. package/dist/server/routes/events.js +54 -0
  38. package/dist/server/routes/run.d.ts +19 -0
  39. package/dist/server/routes/run.js +51 -0
  40. package/dist/server/session-manager.d.ts +64 -0
  41. package/dist/server/session-manager.js +101 -0
  42. package/dist/server/standalone.js +820 -0
  43. package/package.json +91 -0
  44. package/skills/sna-down/SKILL.md +23 -0
  45. package/skills/sna-up/SKILL.md +40 -0
@@ -0,0 +1,25 @@
1
+ /**
2
+ * sna-run — Awaitable skill invocation for SNA pipelines.
3
+ *
4
+ * Lets you chain Claude Code skills programmatically using async/await.
5
+ *
6
+ * @example
7
+ * import { sna } from "sna/lib/sna-run";
8
+ *
9
+ * await sna.run("/devlog-collect");
10
+ * await sna.run("/devlog-analyze --week");
11
+ * await sna.run("/devlog-report");
12
+ */
13
+ declare const sna: {
14
+ /**
15
+ * Invoke a Claude Code skill by slash command and await its completion.
16
+ *
17
+ * @param command - e.g. "/devlog-collect --since 7d"
18
+ * @param opts.timeout - Max wait time in ms (default: 5 minutes)
19
+ */
20
+ run: (command: string, opts?: {
21
+ timeout?: number;
22
+ }) => Promise<void>;
23
+ };
24
+
25
+ export { sna };
@@ -0,0 +1,74 @@
1
+ import { spawn } from "child_process";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { getDb } from "../db/schema.js";
5
+ const ROOT = process.cwd();
6
+ const STATE_DIR = path.join(ROOT, ".sna");
7
+ const CLAUDE_PATH_FILE = path.join(STATE_DIR, "claude-path");
8
+ const POLL_INTERVAL_MS = 500;
9
+ const DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
10
+ function getClaudePath() {
11
+ if (fs.existsSync(CLAUDE_PATH_FILE)) {
12
+ return fs.readFileSync(CLAUDE_PATH_FILE, "utf-8").trim();
13
+ }
14
+ for (const p of ["/usr/local/bin/claude", "/opt/homebrew/bin/claude"]) {
15
+ if (fs.existsSync(p)) return p;
16
+ }
17
+ throw new Error("Claude binary not found. Run /sna-up first.");
18
+ }
19
+ function parseSkillName(command) {
20
+ return command.trim().replace(/^\//, "").split(/\s+/)[0];
21
+ }
22
+ function waitForComplete(skill, afterId, timeoutMs) {
23
+ return new Promise((resolve, reject) => {
24
+ const deadline = Date.now() + timeoutMs;
25
+ const timer = setInterval(() => {
26
+ if (Date.now() > deadline) {
27
+ clearInterval(timer);
28
+ reject(new Error(`Skill "${skill}" timed out after ${timeoutMs / 1e3}s`));
29
+ return;
30
+ }
31
+ const db = getDb();
32
+ const row = db.prepare(`
33
+ SELECT type, message FROM skill_events
34
+ WHERE skill = ? AND id > ? AND type IN ('complete', 'success', 'error', 'failed')
35
+ ORDER BY id ASC LIMIT 1
36
+ `).get(skill, afterId);
37
+ if (!row) return;
38
+ clearInterval(timer);
39
+ if (row.type === "complete" || row.type === "success") {
40
+ resolve();
41
+ } else {
42
+ reject(new Error(`Skill "${skill}" failed: ${row.message}`));
43
+ }
44
+ }, POLL_INTERVAL_MS);
45
+ });
46
+ }
47
+ function getLatestEventId() {
48
+ const db = getDb();
49
+ const row = db.prepare("SELECT MAX(id) as id FROM skill_events").get();
50
+ return row.id ?? 0;
51
+ }
52
+ const sna = {
53
+ /**
54
+ * Invoke a Claude Code skill by slash command and await its completion.
55
+ *
56
+ * @param command - e.g. "/devlog-collect --since 7d"
57
+ * @param opts.timeout - Max wait time in ms (default: 5 minutes)
58
+ */
59
+ run: async (command, opts) => {
60
+ const skillName = parseSkillName(command);
61
+ const timeoutMs = opts?.timeout ?? DEFAULT_TIMEOUT_MS;
62
+ const afterId = getLatestEventId();
63
+ const claudePath = getClaudePath();
64
+ spawn(claudePath, ["--print", command], {
65
+ cwd: ROOT,
66
+ stdio: "inherit",
67
+ env: { ...process.env }
68
+ });
69
+ await waitForComplete(skillName, afterId, timeoutMs);
70
+ }
71
+ };
72
+ export {
73
+ sna
74
+ };
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,48 @@
1
+ import { getDb } from "../db/schema.js";
2
+ function parseArgs(args2) {
3
+ const result = {};
4
+ for (let i = 0; i < args2.length; i += 2) {
5
+ const key = args2[i]?.replace(/^--/, "");
6
+ if (key) result[key] = args2[i + 1] ?? "";
7
+ }
8
+ return result;
9
+ }
10
+ const [, , ...args] = process.argv;
11
+ const flags = parseArgs(args);
12
+ const VALID_TYPES = [
13
+ "called",
14
+ "success",
15
+ "failed",
16
+ "permission_needed",
17
+ "start",
18
+ "progress",
19
+ "milestone",
20
+ "complete",
21
+ "error"
22
+ ];
23
+ if (!flags.skill || !flags.type || !flags.message) {
24
+ console.error("Usage: tsx node_modules/sna/src/scripts/emit.ts --skill <name> --type <type> --message <text> [--data <json>]");
25
+ process.exit(1);
26
+ }
27
+ if (!VALID_TYPES.includes(flags.type)) {
28
+ console.error(`Invalid type: ${flags.type}. Must be one of: ${VALID_TYPES.join(", ")}`);
29
+ process.exit(1);
30
+ }
31
+ const db = getDb();
32
+ db.prepare(`
33
+ INSERT INTO skill_events (skill, type, message, data)
34
+ VALUES (?, ?, ?, ?)
35
+ `).run(flags.skill, flags.type, flags.message, flags.data ?? null);
36
+ const prefix = {
37
+ called: "\u2192",
38
+ success: "\u2713",
39
+ failed: "\u2717",
40
+ permission_needed: "\u26A0",
41
+ start: "\u25B6",
42
+ progress: "\xB7",
43
+ milestone: "\u25C6",
44
+ complete: "\u2713",
45
+ error: "\u2717"
46
+ };
47
+ const p = prefix[flags.type] ?? "\xB7";
48
+ console.log(`${p} [${flags.skill}] ${flags.message}`);
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,34 @@
1
+ import { getDb } from "../db/schema.js";
2
+ const chunks = [];
3
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
4
+ process.stdin.on("end", () => {
5
+ try {
6
+ const raw = Buffer.concat(chunks).toString().trim();
7
+ if (!raw) process.exit(0);
8
+ const input = JSON.parse(raw);
9
+ const toolName = input.tool_name ?? "unknown";
10
+ const toolInput = input.tool_input ?? {};
11
+ const db = getDb();
12
+ const latestCalled = db.prepare(`
13
+ SELECT skill FROM skill_events
14
+ WHERE type = 'called'
15
+ AND id > COALESCE(
16
+ (SELECT MAX(id) FROM skill_events WHERE type IN ('success', 'failed')),
17
+ 0
18
+ )
19
+ ORDER BY id DESC LIMIT 1
20
+ `).get();
21
+ const skillName = latestCalled?.skill ?? "system";
22
+ const summary = toolName === "Bash" ? String(toolInput.command ?? "").slice(0, 120) : toolName === "Write" ? String(toolInput.file_path ?? "") : toolName === "Edit" || toolName === "MultiEdit" ? String(toolInput.file_path ?? "") : JSON.stringify(toolInput).slice(0, 120);
23
+ db.prepare(
24
+ `INSERT INTO skill_events (skill, type, message, data) VALUES (?, ?, ?, ?)`
25
+ ).run(
26
+ skillName,
27
+ "permission_needed",
28
+ `${toolName}: ${summary}`,
29
+ JSON.stringify({ tool_name: toolName, tool_input: toolInput })
30
+ );
31
+ } catch {
32
+ }
33
+ process.exit(0);
34
+ });
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,3 @@
1
+ import { getDb } from "../db/schema.js";
2
+ getDb();
3
+ console.log("\u2713 Database initialized");
@@ -0,0 +1,2 @@
1
+
2
+ export { }