@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
package/bin/sna.js ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process'
3
+ import { fileURLToPath } from 'url'
4
+ import { resolve, dirname } from 'path'
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url))
7
+ const entry = resolve(__dirname, '../dist/scripts/sna.js')
8
+
9
+ const child = spawn(process.execPath, [entry, ...process.argv.slice(2)], {
10
+ stdio: 'inherit',
11
+ shell: false,
12
+ })
13
+
14
+ child.on('exit', (code) => process.exit(code ?? 0))
15
+ child.on('error', (err) => {
16
+ console.error('Error:', err.message)
17
+ process.exit(1)
18
+ })
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/cli.js ADDED
@@ -0,0 +1,104 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import chalk from "chalk";
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const PACKAGE_ROOT = path.resolve(__dirname, "..");
7
+ const [, , command] = process.argv;
8
+ switch (command) {
9
+ case "link":
10
+ cmdLink();
11
+ break;
12
+ case "install":
13
+ cmdInstall();
14
+ break;
15
+ default:
16
+ console.log(`
17
+ ${chalk.bold("sna")} \u2014 Skills-Native Application core primitives
18
+
19
+ ${chalk.bold("Usage:")}
20
+ sna link Create/update .claude/skills symlinks
21
+ sna install Add sna to package.json and link skills
22
+ `);
23
+ }
24
+ function cmdLink() {
25
+ const cwd = process.cwd();
26
+ const claudeDir = path.join(cwd, ".claude");
27
+ const skillsDir = path.join(claudeDir, "skills");
28
+ if (!fs.existsSync(skillsDir)) {
29
+ fs.mkdirSync(skillsDir, { recursive: true });
30
+ console.log(chalk.gray(` created .claude/skills/`));
31
+ }
32
+ const coreSkillsDir = path.join(PACKAGE_ROOT, "skills");
33
+ if (!fs.existsSync(coreSkillsDir)) {
34
+ console.error(chalk.red(` \u2717 sna skills directory not found: ${coreSkillsDir}`));
35
+ process.exit(1);
36
+ }
37
+ const skills = fs.readdirSync(coreSkillsDir).filter(
38
+ (f) => fs.statSync(path.join(coreSkillsDir, f)).isDirectory()
39
+ );
40
+ let linked = 0;
41
+ let updated = 0;
42
+ let skipped = 0;
43
+ for (const skill of skills) {
44
+ const linkPath = path.join(skillsDir, skill);
45
+ const target = path.relative(skillsDir, path.join(coreSkillsDir, skill));
46
+ let existing = null;
47
+ try {
48
+ existing = fs.lstatSync(linkPath);
49
+ } catch {
50
+ }
51
+ if (existing) {
52
+ if (existing.isSymbolicLink()) {
53
+ const currentTarget = fs.readlinkSync(linkPath);
54
+ if (currentTarget === target) {
55
+ skipped++;
56
+ continue;
57
+ }
58
+ fs.unlinkSync(linkPath);
59
+ fs.symlinkSync(target, linkPath);
60
+ console.log(chalk.cyan(` updated .claude/skills/${skill}/ \u2192 ${target}`));
61
+ updated++;
62
+ } else {
63
+ console.log(chalk.yellow(` skipped .claude/skills/${skill}/ (not a symlink \u2014 won't overwrite)`));
64
+ skipped++;
65
+ }
66
+ } else {
67
+ fs.symlinkSync(target, linkPath);
68
+ console.log(chalk.green(` linked .claude/skills/${skill}/ \u2192 ${target}`));
69
+ linked++;
70
+ }
71
+ }
72
+ console.log();
73
+ if (linked + updated > 0) {
74
+ console.log(chalk.green(`\u2713 ${linked + updated} skill(s) linked`));
75
+ } else {
76
+ console.log(chalk.gray(`\u2713 Skills already up to date`));
77
+ }
78
+ if (skipped > 0 && linked + updated > 0) {
79
+ console.log(chalk.gray(` (${skipped} unchanged)`));
80
+ }
81
+ }
82
+ function cmdInstall() {
83
+ const cwd = process.cwd();
84
+ const pkgPath = path.join(cwd, "package.json");
85
+ if (!fs.existsSync(pkgPath)) {
86
+ console.error(chalk.red(" \u2717 No package.json found in current directory"));
87
+ process.exit(1);
88
+ }
89
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
90
+ const hasSnaCoreAlready = pkg.dependencies?.["sna"] || pkg.devDependencies?.["sna"];
91
+ if (!hasSnaCoreAlready) {
92
+ const snaPkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8"));
93
+ const version = snaPkg.version ?? "latest";
94
+ pkg.dependencies = pkg.dependencies ?? {};
95
+ pkg.dependencies["sna"] = `^${version}`;
96
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
97
+ console.log(chalk.green(` added "sna": "^${version}" to dependencies`));
98
+ console.log(chalk.gray(` run pnpm install to install`));
99
+ } else {
100
+ console.log(chalk.gray(` sna already in package.json`));
101
+ }
102
+ console.log();
103
+ cmdLink();
104
+ }
@@ -0,0 +1,9 @@
1
+ import { AgentProvider, SpawnOptions, AgentProcess } from './types.js';
2
+
3
+ declare class ClaudeCodeProvider implements AgentProvider {
4
+ readonly name = "claude-code";
5
+ isAvailable(): Promise<boolean>;
6
+ spawn(options: SpawnOptions): AgentProcess;
7
+ }
8
+
9
+ export { ClaudeCodeProvider };
@@ -0,0 +1,257 @@
1
+ import { spawn, execSync } from "child_process";
2
+ import { EventEmitter } from "events";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { logger } from "../../lib/logger.js";
6
+ const SHELL = process.env.SHELL || "/bin/zsh";
7
+ function resolveClaudePath(cwd) {
8
+ const cached = path.join(cwd, ".sna/claude-path");
9
+ if (fs.existsSync(cached)) {
10
+ const p = fs.readFileSync(cached, "utf8").trim();
11
+ if (p) {
12
+ try {
13
+ execSync(`test -x "${p}"`, { stdio: "pipe" });
14
+ return p;
15
+ } catch {
16
+ }
17
+ }
18
+ }
19
+ for (const p of [
20
+ "/opt/homebrew/bin/claude",
21
+ "/usr/local/bin/claude",
22
+ `${process.env.HOME}/.local/bin/claude`
23
+ ]) {
24
+ try {
25
+ execSync(`test -x "${p}"`, { stdio: "pipe" });
26
+ return p;
27
+ } catch {
28
+ }
29
+ }
30
+ try {
31
+ return execSync(`${SHELL} -l -c "which claude"`, { encoding: "utf8" }).trim();
32
+ } catch {
33
+ return "claude";
34
+ }
35
+ }
36
+ class ClaudeCodeProcess {
37
+ constructor(proc, options) {
38
+ this.emitter = new EventEmitter();
39
+ this._alive = true;
40
+ this._sessionId = null;
41
+ this.buffer = "";
42
+ this.proc = proc;
43
+ proc.stdout.on("data", (chunk) => {
44
+ this.buffer += chunk.toString();
45
+ const lines = this.buffer.split("\n");
46
+ this.buffer = lines.pop() ?? "";
47
+ for (const line of lines) {
48
+ if (!line.trim()) continue;
49
+ logger.log("stdout", line);
50
+ try {
51
+ const msg = JSON.parse(line);
52
+ if (msg.session_id && !this._sessionId) {
53
+ this._sessionId = msg.session_id;
54
+ }
55
+ const event = this.normalizeEvent(msg);
56
+ if (event) this.emitter.emit("event", event);
57
+ } catch {
58
+ }
59
+ }
60
+ });
61
+ proc.stderr.on("data", () => {
62
+ });
63
+ proc.on("exit", (code) => {
64
+ this._alive = false;
65
+ if (this.buffer.trim()) {
66
+ try {
67
+ const msg = JSON.parse(this.buffer);
68
+ const event = this.normalizeEvent(msg);
69
+ if (event) this.emitter.emit("event", event);
70
+ } catch {
71
+ }
72
+ }
73
+ this.emitter.emit("exit", code);
74
+ logger.log("agent", `process exited (code=${code})`);
75
+ });
76
+ proc.on("error", (err) => {
77
+ this._alive = false;
78
+ this.emitter.emit("error", err);
79
+ });
80
+ if (options.prompt) {
81
+ this.send(options.prompt);
82
+ }
83
+ }
84
+ get alive() {
85
+ return this._alive;
86
+ }
87
+ get sessionId() {
88
+ return this._sessionId;
89
+ }
90
+ /**
91
+ * Send a user message to the persistent Claude process via stdin.
92
+ */
93
+ send(input) {
94
+ if (!this._alive || !this.proc.stdin.writable) return;
95
+ const msg = JSON.stringify({
96
+ type: "user",
97
+ message: { role: "user", content: input }
98
+ });
99
+ logger.log("stdin", msg.slice(0, 200));
100
+ this.proc.stdin.write(msg + "\n");
101
+ }
102
+ kill() {
103
+ if (this._alive) {
104
+ this._alive = false;
105
+ this.proc.kill("SIGTERM");
106
+ }
107
+ }
108
+ on(event, handler) {
109
+ this.emitter.on(event, handler);
110
+ }
111
+ off(event, handler) {
112
+ this.emitter.off(event, handler);
113
+ }
114
+ normalizeEvent(msg) {
115
+ switch (msg.type) {
116
+ case "system": {
117
+ if (msg.subtype === "init") {
118
+ return {
119
+ type: "init",
120
+ message: `Agent ready (${msg.model ?? "unknown"})`,
121
+ data: { sessionId: msg.session_id, model: msg.model },
122
+ timestamp: Date.now()
123
+ };
124
+ }
125
+ return null;
126
+ }
127
+ case "assistant": {
128
+ const content = msg.message?.content;
129
+ if (!Array.isArray(content)) return null;
130
+ const events = [];
131
+ for (const block of content) {
132
+ if (block.type === "thinking") {
133
+ events.push({
134
+ type: "thinking",
135
+ message: block.thinking ?? "",
136
+ timestamp: Date.now()
137
+ });
138
+ } else if (block.type === "tool_use") {
139
+ events.push({
140
+ type: "tool_use",
141
+ message: block.name,
142
+ data: { toolName: block.name, input: block.input, id: block.id },
143
+ timestamp: Date.now()
144
+ });
145
+ } else if (block.type === "text") {
146
+ const text = (block.text ?? "").trim();
147
+ if (text) {
148
+ events.push({ type: "assistant", message: text, timestamp: Date.now() });
149
+ }
150
+ }
151
+ }
152
+ if (events.length > 0) {
153
+ for (let i = 1; i < events.length; i++) {
154
+ this.emitter.emit("event", events[i]);
155
+ }
156
+ return events[0];
157
+ }
158
+ return null;
159
+ }
160
+ case "user": {
161
+ const userContent = msg.message?.content;
162
+ if (!Array.isArray(userContent)) return null;
163
+ for (const block of userContent) {
164
+ if (block.type === "tool_result") {
165
+ return {
166
+ type: "tool_result",
167
+ message: typeof block.content === "string" ? block.content.slice(0, 300) : JSON.stringify(block.content).slice(0, 300),
168
+ data: { toolUseId: block.tool_use_id, isError: block.is_error },
169
+ timestamp: Date.now()
170
+ };
171
+ }
172
+ }
173
+ return null;
174
+ }
175
+ case "result": {
176
+ if (msg.subtype === "success") {
177
+ const mu = msg.modelUsage ?? {};
178
+ const modelKey = Object.keys(mu)[0] ?? "";
179
+ const u = mu[modelKey] ?? {};
180
+ return {
181
+ type: "complete",
182
+ message: msg.result ?? "Done",
183
+ data: {
184
+ durationMs: msg.duration_ms,
185
+ costUsd: msg.total_cost_usd,
186
+ inputTokens: u.inputTokens ?? 0,
187
+ outputTokens: u.outputTokens ?? 0,
188
+ cacheReadTokens: u.cacheReadInputTokens ?? 0,
189
+ cacheWriteTokens: u.cacheCreationInputTokens ?? 0,
190
+ contextWindow: u.contextWindow ?? 0,
191
+ maxOutputTokens: u.maxOutputTokens ?? 0,
192
+ model: modelKey
193
+ },
194
+ timestamp: Date.now()
195
+ };
196
+ }
197
+ if (msg.subtype === "error" || msg.is_error) {
198
+ return {
199
+ type: "error",
200
+ message: msg.result ?? msg.error ?? "Unknown error",
201
+ timestamp: Date.now()
202
+ };
203
+ }
204
+ return null;
205
+ }
206
+ case "rate_limit_event":
207
+ return null;
208
+ default:
209
+ logger.log("agent", `unhandled event: ${msg.type}`, JSON.stringify(msg).substring(0, 200));
210
+ return null;
211
+ }
212
+ }
213
+ }
214
+ class ClaudeCodeProvider {
215
+ constructor() {
216
+ this.name = "claude-code";
217
+ }
218
+ async isAvailable() {
219
+ try {
220
+ const p = resolveClaudePath(process.cwd());
221
+ execSync(`test -x "${p}"`, { stdio: "pipe" });
222
+ return true;
223
+ } catch {
224
+ return false;
225
+ }
226
+ }
227
+ spawn(options) {
228
+ const claudePath = resolveClaudePath(options.cwd);
229
+ const args = [
230
+ "--output-format",
231
+ "stream-json",
232
+ "--input-format",
233
+ "stream-json",
234
+ "--verbose"
235
+ ];
236
+ if (options.model) {
237
+ args.push("--model", options.model);
238
+ }
239
+ if (options.permissionMode) {
240
+ args.push("--permission-mode", options.permissionMode);
241
+ }
242
+ const cleanEnv = { ...process.env, ...options.env };
243
+ delete cleanEnv.CLAUDECODE;
244
+ delete cleanEnv.CLAUDE_CODE_ENTRYPOINT;
245
+ delete cleanEnv.CLAUDE_CODE_SESSION_ACCESS_TOKEN;
246
+ const proc = spawn(claudePath, args, {
247
+ cwd: options.cwd,
248
+ env: cleanEnv,
249
+ stdio: ["pipe", "pipe", "pipe"]
250
+ });
251
+ logger.log("agent", `spawned claude-code (pid=${proc.pid})`);
252
+ return new ClaudeCodeProcess(proc, options);
253
+ }
254
+ }
255
+ export {
256
+ ClaudeCodeProvider
257
+ };
@@ -0,0 +1,18 @@
1
+ import { AgentProvider, SpawnOptions, AgentProcess } from './types.js';
2
+
3
+ /**
4
+ * Codex provider stub.
5
+ *
6
+ * Codex uses JSONL output: `codex exec --json "prompt"`
7
+ * Event types: thread.started, turn.started, turn.completed,
8
+ * turn.failed, item.completed, error
9
+ *
10
+ * Not yet implemented — placeholder to validate the provider interface.
11
+ */
12
+ declare class CodexProvider implements AgentProvider {
13
+ readonly name = "codex";
14
+ isAvailable(): Promise<boolean>;
15
+ spawn(_options: SpawnOptions): AgentProcess;
16
+ }
17
+
18
+ export { CodexProvider };
@@ -0,0 +1,14 @@
1
+ class CodexProvider {
2
+ constructor() {
3
+ this.name = "codex";
4
+ }
5
+ async isAvailable() {
6
+ return false;
7
+ }
8
+ spawn(_options) {
9
+ throw new Error("Codex provider not yet implemented");
10
+ }
11
+ }
12
+ export {
13
+ CodexProvider
14
+ };
@@ -0,0 +1,14 @@
1
+ import { AgentProvider } from './types.js';
2
+ export { AgentEvent, AgentProcess, SpawnOptions } from './types.js';
3
+ export { ClaudeCodeProvider } from './claude-code.js';
4
+ export { CodexProvider } from './codex.js';
5
+
6
+ /**
7
+ * Get a registered provider by name.
8
+ * @throws if provider not found
9
+ */
10
+ declare function getProvider(name?: string): AgentProvider;
11
+ /** Register a custom provider. */
12
+ declare function registerProvider(provider: AgentProvider): void;
13
+
14
+ export { AgentProvider, getProvider, registerProvider };
@@ -0,0 +1,22 @@
1
+ import { ClaudeCodeProvider } from "./claude-code.js";
2
+ import { CodexProvider } from "./codex.js";
3
+ import { ClaudeCodeProvider as ClaudeCodeProvider2 } from "./claude-code.js";
4
+ import { CodexProvider as CodexProvider2 } from "./codex.js";
5
+ const providers = {
6
+ "claude-code": new ClaudeCodeProvider2(),
7
+ "codex": new CodexProvider2()
8
+ };
9
+ function getProvider(name = "claude-code") {
10
+ const provider = providers[name];
11
+ if (!provider) throw new Error(`Unknown agent provider: ${name}`);
12
+ return provider;
13
+ }
14
+ function registerProvider(provider) {
15
+ providers[provider.name] = provider;
16
+ }
17
+ export {
18
+ ClaudeCodeProvider,
19
+ CodexProvider,
20
+ getProvider,
21
+ registerProvider
22
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Normalized event type emitted by all agent providers.
3
+ *
4
+ * Providers translate their native event format (Claude Code stream-json,
5
+ * Codex JSONL, etc.) into these common types.
6
+ */
7
+ interface AgentEvent {
8
+ type: "init" | "thinking" | "text_delta" | "assistant" | "tool_use" | "tool_result" | "permission_needed" | "milestone" | "error" | "complete";
9
+ message?: string;
10
+ data?: Record<string, unknown>;
11
+ timestamp: number;
12
+ }
13
+ /**
14
+ * A running agent process. Wraps a child_process with typed event handlers.
15
+ */
16
+ interface AgentProcess {
17
+ /** Send a user message to the agent's stdin. */
18
+ send(input: string): void;
19
+ /** Kill the agent process. */
20
+ kill(): void;
21
+ /** Whether the process is still running. */
22
+ readonly alive: boolean;
23
+ /** Session ID assigned by the provider. */
24
+ readonly sessionId: string | null;
25
+ on(event: "event", handler: (e: AgentEvent) => void): void;
26
+ on(event: "exit", handler: (code: number | null) => void): void;
27
+ on(event: "error", handler: (err: Error) => void): void;
28
+ off(event: string, handler: Function): void;
29
+ }
30
+ /**
31
+ * Options for spawning an agent session.
32
+ */
33
+ interface SpawnOptions {
34
+ cwd: string;
35
+ prompt?: string;
36
+ model?: string;
37
+ permissionMode?: "default" | "acceptEdits" | "bypassPermissions" | "plan";
38
+ env?: Record<string, string>;
39
+ }
40
+ /**
41
+ * Agent provider interface. Each backend (Claude Code, Codex, etc.)
42
+ * implements this to provide a unified spawn → events → send API.
43
+ */
44
+ interface AgentProvider {
45
+ readonly name: string;
46
+ /** Check if this provider's CLI is available on the system. */
47
+ isAvailable(): Promise<boolean>;
48
+ /** Spawn a new agent session. */
49
+ spawn(options: SpawnOptions): AgentProcess;
50
+ }
51
+
52
+ export type { AgentEvent, AgentProcess, AgentProvider, SpawnOptions };
File without changes
@@ -0,0 +1,13 @@
1
+ import Database from 'better-sqlite3';
2
+
3
+ declare function getDb(): Database.Database;
4
+ interface SkillEvent {
5
+ id: number;
6
+ skill: string;
7
+ type: "invoked" | "called" | "success" | "failed" | "permission_needed" | "start" | "progress" | "milestone" | "complete" | "error";
8
+ message: string;
9
+ data: string | null;
10
+ created_at: string;
11
+ }
12
+
13
+ export { type SkillEvent, getDb };
@@ -0,0 +1,41 @@
1
+ import { createRequire } from "node:module";
2
+ import path from "path";
3
+ const require2 = createRequire(path.join(process.cwd(), "node_modules", "_"));
4
+ const BetterSqlite3 = require2("better-sqlite3");
5
+ const DB_PATH = path.join(process.cwd(), "data/sna.db");
6
+ let _db = null;
7
+ function getDb() {
8
+ if (!_db) {
9
+ _db = new BetterSqlite3(DB_PATH);
10
+ _db.pragma("journal_mode = WAL");
11
+ initSchema(_db);
12
+ }
13
+ return _db;
14
+ }
15
+ function migrateSkillEvents(db) {
16
+ const row = db.prepare(
17
+ "SELECT sql FROM sqlite_master WHERE type='table' AND name='skill_events'"
18
+ ).get();
19
+ if (row?.sql?.includes("CHECK(type IN")) {
20
+ db.exec("DROP TABLE IF EXISTS skill_events");
21
+ }
22
+ }
23
+ function initSchema(db) {
24
+ migrateSkillEvents(db);
25
+ db.exec(`
26
+ CREATE TABLE IF NOT EXISTS skill_events (
27
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
28
+ skill TEXT NOT NULL,
29
+ type TEXT NOT NULL,
30
+ message TEXT NOT NULL,
31
+ data TEXT,
32
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
33
+ );
34
+
35
+ CREATE INDEX IF NOT EXISTS idx_skill_events_skill ON skill_events(skill);
36
+ CREATE INDEX IF NOT EXISTS idx_skill_events_created ON skill_events(created_at);
37
+ `);
38
+ }
39
+ export {
40
+ getDb
41
+ };
@@ -0,0 +1,15 @@
1
+ export { SkillEvent } from './db/schema.js';
2
+ export { AgentEvent, AgentProcess, AgentProvider, SpawnOptions } from './core/providers/types.js';
3
+ export { Session, SessionInfo, SessionManagerOptions } from './server/session-manager.js';
4
+ import 'better-sqlite3';
5
+
6
+ /**
7
+ * @sna-sdk/core — Skills-Native Application runtime.
8
+ *
9
+ * Server, providers, session management, database, and CLI.
10
+ * No React dependency.
11
+ */
12
+ declare const DEFAULT_SNA_PORT = 3099;
13
+ declare const DEFAULT_SNA_URL = "http://localhost:3099";
14
+
15
+ export { DEFAULT_SNA_PORT, DEFAULT_SNA_URL };
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ const DEFAULT_SNA_PORT = 3099;
2
+ const DEFAULT_SNA_URL = `http://localhost:${DEFAULT_SNA_PORT}`;
3
+ export {
4
+ DEFAULT_SNA_PORT,
5
+ DEFAULT_SNA_URL
6
+ };
@@ -0,0 +1,18 @@
1
+ declare const tags: {
2
+ readonly sna: string;
3
+ readonly req: string;
4
+ readonly agent: string;
5
+ readonly stdin: string;
6
+ readonly stdout: string;
7
+ readonly route: string;
8
+ readonly err: string;
9
+ };
10
+ type Tag = keyof typeof tags;
11
+ declare function log(tag: Tag, ...args: unknown[]): void;
12
+ declare function err(tag: Tag, ...args: unknown[]): void;
13
+ declare const logger: {
14
+ log: typeof log;
15
+ err: typeof err;
16
+ };
17
+
18
+ export { logger };
@@ -0,0 +1,50 @@
1
+ import chalk from "chalk";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ const LOG_PATH = path.join(process.cwd(), ".dev.log");
5
+ try {
6
+ fs.writeFileSync(LOG_PATH, "");
7
+ } catch {
8
+ }
9
+ function tsPlain() {
10
+ return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
11
+ }
12
+ function tsColored() {
13
+ return chalk.gray(tsPlain());
14
+ }
15
+ const tags = {
16
+ sna: chalk.bold.magenta(" SNA "),
17
+ req: chalk.bold.blue(" REQ "),
18
+ agent: chalk.bold.cyan(" AGT "),
19
+ stdin: chalk.bold.green(" IN "),
20
+ stdout: chalk.bold.yellow(" OUT "),
21
+ route: chalk.bold.blue(" API "),
22
+ err: chalk.bold.red(" ERR ")
23
+ };
24
+ const tagPlain = {
25
+ sna: " SNA ",
26
+ req: " REQ ",
27
+ agent: " AGT ",
28
+ stdin: " IN ",
29
+ stdout: " OUT ",
30
+ route: " API ",
31
+ err: " ERR "
32
+ };
33
+ function appendFile(tag, args) {
34
+ const line = `${tsPlain()} ${tag} ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
35
+ `;
36
+ fs.appendFile(LOG_PATH, line, () => {
37
+ });
38
+ }
39
+ function log(tag, ...args) {
40
+ console.log(`${tsColored()} ${tags[tag]}`, ...args);
41
+ appendFile(tagPlain[tag], args);
42
+ }
43
+ function err(tag, ...args) {
44
+ console.error(`${tsColored()} ${tags[tag]}`, ...args);
45
+ appendFile(tagPlain[tag], args);
46
+ }
47
+ const logger = { log, err };
48
+ export {
49
+ logger
50
+ };