cortex-agents 4.1.2 → 5.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 (53) hide show
  1. package/README.md +85 -16
  2. package/dist/cli.js +146 -13
  3. package/dist/engine/agents.d.ts +19 -0
  4. package/dist/engine/agents.d.ts.map +1 -0
  5. package/dist/engine/agents.js +69 -0
  6. package/dist/engine/db.d.ts +13 -0
  7. package/dist/engine/db.d.ts.map +1 -0
  8. package/dist/engine/db.js +28 -0
  9. package/dist/engine/index.d.ts +50 -0
  10. package/dist/engine/index.d.ts.map +1 -0
  11. package/dist/engine/index.js +135 -0
  12. package/dist/engine/models.d.ts +12 -0
  13. package/dist/engine/models.d.ts.map +1 -0
  14. package/dist/engine/models.js +23 -0
  15. package/dist/engine/renderers/claude.d.ts +18 -0
  16. package/dist/engine/renderers/claude.d.ts.map +1 -0
  17. package/dist/engine/renderers/claude.js +226 -0
  18. package/dist/engine/renderers/codex.d.ts +22 -0
  19. package/dist/engine/renderers/codex.d.ts.map +1 -0
  20. package/dist/engine/renderers/codex.js +115 -0
  21. package/dist/engine/renderers/gemini.d.ts +18 -0
  22. package/dist/engine/renderers/gemini.d.ts.map +1 -0
  23. package/dist/engine/renderers/gemini.js +203 -0
  24. package/dist/engine/renderers/index.d.ts +21 -0
  25. package/dist/engine/renderers/index.d.ts.map +1 -0
  26. package/dist/engine/renderers/index.js +13 -0
  27. package/dist/engine/renderers/opencode.d.ts +18 -0
  28. package/dist/engine/renderers/opencode.d.ts.map +1 -0
  29. package/dist/engine/renderers/opencode.js +119 -0
  30. package/dist/engine/schema.d.ts +13 -0
  31. package/dist/engine/schema.d.ts.map +1 -0
  32. package/dist/engine/schema.js +125 -0
  33. package/dist/engine/seed.d.ts +14 -0
  34. package/dist/engine/seed.d.ts.map +1 -0
  35. package/dist/engine/seed.js +398 -0
  36. package/dist/engine/skills.d.ts +11 -0
  37. package/dist/engine/skills.d.ts.map +1 -0
  38. package/dist/engine/skills.js +24 -0
  39. package/dist/engine/targets.d.ts +17 -0
  40. package/dist/engine/targets.d.ts.map +1 -0
  41. package/dist/engine/targets.js +79 -0
  42. package/dist/engine/types.d.ts +100 -0
  43. package/dist/engine/types.d.ts.map +1 -0
  44. package/dist/engine/types.js +5 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +53 -0
  47. package/dist/tools/engine.d.ts +22 -0
  48. package/dist/tools/engine.d.ts.map +1 -0
  49. package/dist/tools/engine.js +56 -0
  50. package/dist/utils/cortex-code-bridge.d.ts +21 -0
  51. package/dist/utils/cortex-code-bridge.d.ts.map +1 -0
  52. package/dist/utils/cortex-code-bridge.js +104 -0
  53. package/package.json +4 -2
@@ -0,0 +1,203 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Cortex Engine — Gemini CLI renderer
3
+ // Produces ~/.gemini/agents/*.md (YAML frontmatter + system prompt)
4
+ // and ~/.gemini/GEMINI.md instructions file.
5
+ // ---------------------------------------------------------------------------
6
+ import * as fs from "fs";
7
+ import * as path from "path";
8
+ import * as os from "os";
9
+ import { AgentStore } from "../agents.js";
10
+ import { SkillStore } from "../skills.js";
11
+ import { TargetStore } from "../targets.js";
12
+ import { registerRenderer } from "./index.js";
13
+ // ---------------------------------------------------------------------------
14
+ // Tool-name mapping: OpenCode name -> Gemini CLI tool name
15
+ // ---------------------------------------------------------------------------
16
+ const NATIVE_TOOL_MAP = {
17
+ read: "read_file",
18
+ write: "write_file",
19
+ edit: "edit_file",
20
+ bash: "run_shell_command",
21
+ glob: "glob_tool",
22
+ grep: "grep_search",
23
+ };
24
+ const NATIVE_TOOL_NAMES = new Set(Object.keys(NATIVE_TOOL_MAP));
25
+ /**
26
+ * Map an OpenCode tool name to its Gemini CLI representation.
27
+ *
28
+ * - Native tools (read, write, ...) become Gemini equivalents.
29
+ * - Everything else is a Cortex MCP tool: `mcp_cortex-agents_{name}`.
30
+ */
31
+ function mapToolName(toolName) {
32
+ if (NATIVE_TOOL_NAMES.has(toolName)) {
33
+ return NATIVE_TOOL_MAP[toolName];
34
+ }
35
+ return `mcp_cortex-agents_${toolName}`;
36
+ }
37
+ // ---------------------------------------------------------------------------
38
+ // YAML helpers — lightweight, no external dep
39
+ // ---------------------------------------------------------------------------
40
+ /** Escape a YAML string value. Wraps in quotes when necessary. */
41
+ function yamlValue(v) {
42
+ if (v === "" ||
43
+ v.includes(":") ||
44
+ v.includes("#") ||
45
+ v.includes("{") ||
46
+ v.includes("}") ||
47
+ v.includes("[") ||
48
+ v.includes("]") ||
49
+ v.includes(",") ||
50
+ v.includes("&") ||
51
+ v.includes("*") ||
52
+ v.includes("!") ||
53
+ v.includes("|") ||
54
+ v.includes(">") ||
55
+ v.includes("'") ||
56
+ v.includes('"') ||
57
+ v.includes("%") ||
58
+ v.includes("@") ||
59
+ v.includes("`") ||
60
+ v.startsWith(" ") ||
61
+ v.endsWith(" ")) {
62
+ return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
63
+ }
64
+ return v;
65
+ }
66
+ // ---------------------------------------------------------------------------
67
+ // GeminiRenderer
68
+ // ---------------------------------------------------------------------------
69
+ class GeminiRenderer {
70
+ db;
71
+ agents;
72
+ skills;
73
+ targets;
74
+ constructor(db) {
75
+ this.db = db;
76
+ this.agents = new AgentStore(db);
77
+ this.skills = new SkillStore(db);
78
+ this.targets = new TargetStore(db);
79
+ }
80
+ // ---- renderAgent ----------------------------------------------------------
81
+ renderAgent(agentId) {
82
+ const agent = this.agents.get(agentId);
83
+ if (!agent)
84
+ throw new Error(`Agent not found: ${agentId}`);
85
+ const targetConfig = this.targets.getAgentTargetConfig(agentId, "gemini");
86
+ const agentTools = this.agents.getTools(agentId);
87
+ // ----- Determine tools list -----
88
+ let toolsList;
89
+ if (targetConfig?.tools_override) {
90
+ toolsList = targetConfig.tools_override;
91
+ }
92
+ else {
93
+ toolsList = agentTools
94
+ .filter((t) => t.allowed)
95
+ .map((t) => mapToolName(t.tool_name));
96
+ }
97
+ // ----- Model -----
98
+ const model = targetConfig?.model_override;
99
+ // ----- Build frontmatter -----
100
+ const lines = ["---"];
101
+ lines.push(`name: ${yamlValue(agentId)}`);
102
+ lines.push(`description: ${yamlValue(agent.description)}`);
103
+ lines.push("kind: local");
104
+ if (toolsList.length > 0) {
105
+ lines.push("tools:");
106
+ for (const tool of toolsList) {
107
+ lines.push(` - ${yamlValue(tool)}`);
108
+ }
109
+ }
110
+ if (model && model !== "inherit") {
111
+ lines.push(`model: ${yamlValue(model)}`);
112
+ }
113
+ lines.push(`temperature: ${agent.temperature}`);
114
+ lines.push("max_turns: 15");
115
+ lines.push("---");
116
+ // ----- System prompt body -----
117
+ return lines.join("\n") + "\n" + agent.system_prompt;
118
+ }
119
+ // ---- renderInstructions ---------------------------------------------------
120
+ renderInstructions() {
121
+ const skills = this.skills.list();
122
+ const agents = this.agents.list();
123
+ const subagents = agents.filter((a) => a.mode === "subagent");
124
+ return `# Cortex Agents — Global Instructions
125
+
126
+ > Auto-generated by \`npx cortex-agents install --target gemini\`. Do not edit manually.
127
+
128
+ ## Overview
129
+
130
+ Cortex Agents provides structured development workflows: plan \u2192 build \u2192 quality gate \u2192 ship.
131
+ All 33 tools are available via MCP server (\`npx cortex-agents mcp\`).
132
+
133
+ ## Default Workflow
134
+
135
+ When starting a new task or session, always default to the architect workflow first to plan the work before implementing. Only skip planning for trivial changes (typo fixes, single-line edits).
136
+
137
+ 1. **Plan** \u2014 Analyze requirements and create an implementation plan
138
+ 2. **Implement** \u2014 Execute the plan with iterative build+test verification
139
+ 3. **Fix** \u2014 Quick bug fixes with minimal changes
140
+
141
+ ## Available Skills
142
+
143
+ ${skills.map((s) => ` - ${s.id}`).join("\n")}
144
+
145
+ ## Custom Agents
146
+
147
+ ${subagents.map((a) => a.id).join(", ")}
148
+
149
+ ## Quality Gate
150
+
151
+ After implementation, assess change scope and launch parallel tool calls:
152
+ - **Trivial** (docs only): Skip quality gate
153
+ - **Low** (tests/config): Testing agent only
154
+ - **Standard** (normal code): Testing + Security + Audit + Docs agents
155
+ - **High** (auth/payments/infra): All agents including Perf and DevOps
156
+ `;
157
+ }
158
+ // ---- sync -----------------------------------------------------------------
159
+ sync(opts) {
160
+ const target = this.targets.getTarget("gemini");
161
+ if (!target)
162
+ throw new Error("Gemini target not configured");
163
+ const configDir = target.config_dir.replace("~", os.homedir());
164
+ const agentsDir = path.join(configDir, "agents");
165
+ fs.mkdirSync(agentsDir, { recursive: true });
166
+ const result = {
167
+ target: "gemini",
168
+ agentsWritten: [],
169
+ skillsWritten: [],
170
+ instructionsWritten: false,
171
+ errors: [],
172
+ };
173
+ // Render each agent
174
+ const agents = this.agents.list();
175
+ for (const agent of agents) {
176
+ try {
177
+ const content = this.renderAgent(agent.id);
178
+ const filePath = path.join(agentsDir, `${agent.id}.md`);
179
+ fs.writeFileSync(filePath, content);
180
+ result.agentsWritten.push(agent.id);
181
+ }
182
+ catch (err) {
183
+ result.errors.push(`Agent ${agent.id}: ${err.message}`);
184
+ }
185
+ }
186
+ // Render instructions file
187
+ if (target.instructions_file) {
188
+ try {
189
+ const content = this.renderInstructions();
190
+ const filePath = path.join(configDir, target.instructions_file);
191
+ fs.writeFileSync(filePath, content);
192
+ result.instructionsWritten = true;
193
+ }
194
+ catch (err) {
195
+ result.errors.push(`Instructions: ${err.message}`);
196
+ }
197
+ }
198
+ return result;
199
+ }
200
+ }
201
+ // Self-register on import.
202
+ registerRenderer("gemini", (db) => new GeminiRenderer(db));
203
+ export { GeminiRenderer };
@@ -0,0 +1,21 @@
1
+ import type BetterSqlite3 from "better-sqlite3";
2
+ import type { SyncResult } from "../types.js";
3
+ /** A renderer knows how to turn DB rows into on-disk files for one target. */
4
+ export interface Renderer {
5
+ /** Render a single agent to its target format string. */
6
+ renderAgent(agentId: string): string;
7
+ /** Render the instructions file content (CLAUDE.md, etc.). */
8
+ renderInstructions(): string;
9
+ /** Sync all agents + instructions to disk, return results. */
10
+ sync(opts?: {
11
+ scope?: string;
12
+ projectPath?: string;
13
+ }): SyncResult;
14
+ }
15
+ /** Factory that creates a Renderer backed by the given database. */
16
+ export interface RendererFactory {
17
+ (db: BetterSqlite3.Database): Renderer;
18
+ }
19
+ export declare function registerRenderer(targetId: string, factory: RendererFactory): void;
20
+ export declare function getRenderer(targetId: string, db: BetterSqlite3.Database): Renderer | null;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/renderers/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,yDAAyD;IACzD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,8DAA8D;IAC9D,kBAAkB,IAAI,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU,CAAC;CACnE;AAED,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC9B,CAAC,EAAE,EAAE,aAAa,CAAC,QAAQ,GAAG,QAAQ,CAAC;CACxC;AAID,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,GACvB,IAAI,CAEN;AAED,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,aAAa,CAAC,QAAQ,GACzB,QAAQ,GAAG,IAAI,CAGjB"}
@@ -0,0 +1,13 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Cortex Engine — Renderer registry
3
+ // Each target (claude, opencode, etc.) registers a factory that can produce
4
+ // rendered agent files and instructions from the canonical DB representation.
5
+ // ---------------------------------------------------------------------------
6
+ const registry = new Map();
7
+ export function registerRenderer(targetId, factory) {
8
+ registry.set(targetId, factory);
9
+ }
10
+ export function getRenderer(targetId, db) {
11
+ const factory = registry.get(targetId);
12
+ return factory ? factory(db) : null;
13
+ }
@@ -0,0 +1,18 @@
1
+ import type BetterSqlite3 from "better-sqlite3";
2
+ import type { Renderer } from "./index.js";
3
+ import type { SyncResult } from "../types.js";
4
+ declare class OpencodeRenderer implements Renderer {
5
+ private db;
6
+ private agents;
7
+ private skills;
8
+ private targets;
9
+ constructor(db: BetterSqlite3.Database);
10
+ renderAgent(agentId: string): string;
11
+ renderInstructions(): string;
12
+ sync(opts?: {
13
+ scope?: string;
14
+ projectPath?: string;
15
+ }): SyncResult;
16
+ }
17
+ export { OpencodeRenderer };
18
+ //# sourceMappingURL=opencode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/engine/renderers/opencode.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAU9C,cAAM,gBAAiB,YAAW,QAAQ;IAK5B,OAAO,CAAC,EAAE;IAJtB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAc;gBAET,EAAE,EAAE,aAAa,CAAC,QAAQ;IAM9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAkDpC,kBAAkB,IAAI,MAAM;IAI5B,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU;CAgDlE;AAKD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,119 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Cortex Engine — OpenCode renderer
3
+ // Produces ~/.config/opencode/agents/*.md and skills/*/SKILL.md
4
+ // ---------------------------------------------------------------------------
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ import * as os from "os";
8
+ import { AgentStore } from "../agents.js";
9
+ import { SkillStore } from "../skills.js";
10
+ import { TargetStore } from "../targets.js";
11
+ import { registerRenderer } from "./index.js";
12
+ // ---------------------------------------------------------------------------
13
+ // OpencodeRenderer
14
+ // ---------------------------------------------------------------------------
15
+ class OpencodeRenderer {
16
+ db;
17
+ agents;
18
+ skills;
19
+ targets;
20
+ constructor(db) {
21
+ this.db = db;
22
+ this.agents = new AgentStore(db);
23
+ this.skills = new SkillStore(db);
24
+ this.targets = new TargetStore(db);
25
+ }
26
+ renderAgent(agentId) {
27
+ const agent = this.agents.get(agentId);
28
+ if (!agent)
29
+ throw new Error(`Agent not found: ${agentId}`);
30
+ const tools = this.agents.getTools(agentId);
31
+ const bashPerms = this.agents.getBashPermissions(agentId);
32
+ const lines = ["---"];
33
+ lines.push(`description: ${agent.description}`);
34
+ lines.push(`mode: ${agent.mode}`);
35
+ lines.push(`temperature: ${agent.temperature}`);
36
+ // Tools block
37
+ if (tools.length > 0) {
38
+ lines.push("tools:");
39
+ for (const t of tools) {
40
+ lines.push(` ${t.tool_name}: ${t.allowed ? "true" : "false"}`);
41
+ }
42
+ }
43
+ // Permission block
44
+ const editPerm = bashPerms.find((p) => p.pattern === "__edit__");
45
+ const bashPatterns = bashPerms.filter((p) => p.pattern !== "__edit__");
46
+ if (editPerm || bashPatterns.length > 0) {
47
+ lines.push("permission:");
48
+ if (editPerm) {
49
+ lines.push(` edit: ${editPerm.permission}`);
50
+ }
51
+ if (bashPatterns.length === 1 && bashPatterns[0].pattern === "*") {
52
+ // Simple string form: bash: ask
53
+ lines.push(` bash: ${bashPatterns[0].permission}`);
54
+ }
55
+ else if (bashPatterns.length > 0) {
56
+ // Object form with patterns
57
+ lines.push(" bash:");
58
+ for (const bp of bashPatterns) {
59
+ const pattern = bp.pattern.includes("*") || bp.pattern.includes(" ")
60
+ ? `"${bp.pattern}"` : bp.pattern;
61
+ lines.push(` ${pattern}: ${bp.permission}`);
62
+ }
63
+ }
64
+ }
65
+ lines.push("---");
66
+ return lines.join("\n") + "\n" + agent.system_prompt;
67
+ }
68
+ renderInstructions() {
69
+ return ""; // OpenCode doesn't use an instructions file
70
+ }
71
+ sync(opts) {
72
+ const target = this.targets.getTarget("opencode");
73
+ if (!target)
74
+ throw new Error("OpenCode target not configured");
75
+ const configDir = target.config_dir.replace("~", os.homedir());
76
+ const agentsDir = path.join(configDir, "agents");
77
+ const skillsDir = path.join(configDir, "skills");
78
+ fs.mkdirSync(agentsDir, { recursive: true });
79
+ fs.mkdirSync(skillsDir, { recursive: true });
80
+ const result = {
81
+ target: "opencode",
82
+ agentsWritten: [],
83
+ skillsWritten: [],
84
+ instructionsWritten: false,
85
+ errors: [],
86
+ };
87
+ // Render each agent
88
+ const agents = this.agents.list();
89
+ for (const agent of agents) {
90
+ try {
91
+ const content = this.renderAgent(agent.id);
92
+ const filePath = path.join(agentsDir, `${agent.id}.md`);
93
+ fs.writeFileSync(filePath, content);
94
+ result.agentsWritten.push(agent.id);
95
+ }
96
+ catch (err) {
97
+ result.errors.push(`Agent ${agent.id}: ${err.message}`);
98
+ }
99
+ }
100
+ // Write skills
101
+ const skills = this.skills.list();
102
+ for (const skill of skills) {
103
+ try {
104
+ const skillDir = path.join(skillsDir, skill.id);
105
+ fs.mkdirSync(skillDir, { recursive: true });
106
+ const filePath = path.join(skillDir, "SKILL.md");
107
+ fs.writeFileSync(filePath, skill.content);
108
+ result.skillsWritten.push(skill.id);
109
+ }
110
+ catch (err) {
111
+ result.errors.push(`Skill ${skill.id}: ${err.message}`);
112
+ }
113
+ }
114
+ return result;
115
+ }
116
+ }
117
+ // Self-register on import.
118
+ registerRenderer("opencode", (db) => new OpencodeRenderer(db));
119
+ export { OpencodeRenderer };
@@ -0,0 +1,13 @@
1
+ import type Database from "better-sqlite3";
2
+ export declare const CURRENT_SCHEMA_VERSION = 1;
3
+ /**
4
+ * Reads the current schema version from the database.
5
+ * Returns 0 if the schema_version table does not exist yet (fresh database).
6
+ */
7
+ export declare function getSchemaVersion(db: Database.Database): number;
8
+ /**
9
+ * Creates all tables (if they don't already exist) and records the schema version.
10
+ * All DDL runs inside a transaction for atomicity.
11
+ */
12
+ export declare function initializeSchema(db: Database.Database): void;
13
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/engine/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AA0FxC;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAiB9D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAqB5D"}
@@ -0,0 +1,125 @@
1
+ export const CURRENT_SCHEMA_VERSION = 1;
2
+ const CREATE_TABLES_SQL = `
3
+ CREATE TABLE IF NOT EXISTS agents (
4
+ id TEXT PRIMARY KEY,
5
+ description TEXT NOT NULL,
6
+ mode TEXT NOT NULL CHECK(mode IN ('primary','subagent')),
7
+ temperature REAL DEFAULT 0.3,
8
+ system_prompt TEXT NOT NULL,
9
+ created_at TEXT DEFAULT (datetime('now')),
10
+ updated_at TEXT DEFAULT (datetime('now'))
11
+ );
12
+
13
+ CREATE TABLE IF NOT EXISTS agent_tools (
14
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
15
+ tool_name TEXT NOT NULL,
16
+ allowed INTEGER NOT NULL DEFAULT 1,
17
+ PRIMARY KEY (agent_id, tool_name)
18
+ );
19
+
20
+ CREATE TABLE IF NOT EXISTS agent_bash_permissions (
21
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
22
+ pattern TEXT NOT NULL,
23
+ permission TEXT NOT NULL CHECK(permission IN ('allow','ask','deny')),
24
+ PRIMARY KEY (agent_id, pattern)
25
+ );
26
+
27
+ CREATE TABLE IF NOT EXISTS skills (
28
+ id TEXT PRIMARY KEY,
29
+ name TEXT NOT NULL,
30
+ description TEXT,
31
+ content TEXT NOT NULL,
32
+ created_at TEXT DEFAULT (datetime('now')),
33
+ updated_at TEXT DEFAULT (datetime('now'))
34
+ );
35
+
36
+ CREATE TABLE IF NOT EXISTS agent_skills (
37
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
38
+ skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
39
+ binding TEXT NOT NULL CHECK(binding IN ('auto','recommended')),
40
+ PRIMARY KEY (agent_id, skill_id)
41
+ );
42
+
43
+ CREATE TABLE IF NOT EXISTS cli_targets (
44
+ id TEXT PRIMARY KEY,
45
+ display_name TEXT NOT NULL,
46
+ config_dir TEXT NOT NULL,
47
+ agent_file_format TEXT NOT NULL,
48
+ instructions_file TEXT
49
+ );
50
+
51
+ CREATE TABLE IF NOT EXISTS agent_target_config (
52
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
53
+ target_id TEXT NOT NULL REFERENCES cli_targets(id) ON DELETE CASCADE,
54
+ native_name TEXT,
55
+ tools_override TEXT,
56
+ disallowed_tools TEXT,
57
+ model_override TEXT,
58
+ extra_frontmatter TEXT,
59
+ PRIMARY KEY (agent_id, target_id)
60
+ );
61
+
62
+ CREATE TABLE IF NOT EXISTS models (
63
+ id TEXT PRIMARY KEY,
64
+ name TEXT NOT NULL,
65
+ provider TEXT NOT NULL,
66
+ tier TEXT NOT NULL CHECK(tier IN ('premium','standard','fast')),
67
+ description TEXT NOT NULL
68
+ );
69
+
70
+ CREATE TABLE IF NOT EXISTS user_config (
71
+ key TEXT PRIMARY KEY,
72
+ value TEXT NOT NULL
73
+ );
74
+
75
+ CREATE TABLE IF NOT EXISTS installations (
76
+ target_id TEXT NOT NULL REFERENCES cli_targets(id),
77
+ scope TEXT NOT NULL CHECK(scope IN ('global','project')),
78
+ path TEXT NOT NULL,
79
+ installed_at TEXT DEFAULT (datetime('now')),
80
+ version TEXT NOT NULL,
81
+ PRIMARY KEY (target_id, scope, path)
82
+ );
83
+
84
+ CREATE TABLE IF NOT EXISTS schema_version (
85
+ version INTEGER PRIMARY KEY,
86
+ applied_at TEXT DEFAULT (datetime('now'))
87
+ );
88
+ `;
89
+ /**
90
+ * Reads the current schema version from the database.
91
+ * Returns 0 if the schema_version table does not exist yet (fresh database).
92
+ */
93
+ export function getSchemaVersion(db) {
94
+ // Check whether the schema_version table exists
95
+ const tableExists = db
96
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'")
97
+ .get();
98
+ if (!tableExists) {
99
+ return 0;
100
+ }
101
+ const row = db
102
+ .prepare("SELECT MAX(version) AS version FROM schema_version")
103
+ .get();
104
+ return row?.version ?? 0;
105
+ }
106
+ /**
107
+ * Creates all tables (if they don't already exist) and records the schema version.
108
+ * All DDL runs inside a transaction for atomicity.
109
+ */
110
+ export function initializeSchema(db) {
111
+ const migrate = db.transaction(() => {
112
+ // Create all tables
113
+ db.exec(CREATE_TABLES_SQL);
114
+ // Check current version and apply migrations
115
+ const version = getSchemaVersion(db);
116
+ if (version === 0) {
117
+ // Fresh database — record initial schema version
118
+ db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(CURRENT_SCHEMA_VERSION);
119
+ }
120
+ // Future migrations would go here:
121
+ // if (version < 2) { migrateTo2(db); }
122
+ // if (version < 3) { migrateTo3(db); }
123
+ });
124
+ migrate();
125
+ }
@@ -0,0 +1,14 @@
1
+ import type Database from "better-sqlite3";
2
+ export interface SeedResult {
3
+ agents: number;
4
+ skills: number;
5
+ models: number;
6
+ targets: number;
7
+ }
8
+ /**
9
+ * Populate the database from .opencode/ markdown files and static registries.
10
+ * All operations are idempotent (INSERT OR REPLACE) and wrapped in a single
11
+ * transaction for atomicity.
12
+ */
13
+ export declare function seedDatabase(db: Database.Database, opencodeDir: string): SeedResult;
14
+ //# sourceMappingURL=seed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../src/engine/seed.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAU3C,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAkBnF"}