cortex-agents 4.1.2 → 5.0.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/README.md +85 -16
- package/dist/cli.js +165 -13
- package/dist/engine/agents.d.ts +19 -0
- package/dist/engine/agents.d.ts.map +1 -0
- package/dist/engine/agents.js +69 -0
- package/dist/engine/db.d.ts +13 -0
- package/dist/engine/db.d.ts.map +1 -0
- package/dist/engine/db.js +28 -0
- package/dist/engine/index.d.ts +50 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +135 -0
- package/dist/engine/models.d.ts +12 -0
- package/dist/engine/models.d.ts.map +1 -0
- package/dist/engine/models.js +23 -0
- package/dist/engine/renderers/claude.d.ts +18 -0
- package/dist/engine/renderers/claude.d.ts.map +1 -0
- package/dist/engine/renderers/claude.js +226 -0
- package/dist/engine/renderers/codex.d.ts +22 -0
- package/dist/engine/renderers/codex.d.ts.map +1 -0
- package/dist/engine/renderers/codex.js +115 -0
- package/dist/engine/renderers/gemini.d.ts +18 -0
- package/dist/engine/renderers/gemini.d.ts.map +1 -0
- package/dist/engine/renderers/gemini.js +203 -0
- package/dist/engine/renderers/index.d.ts +21 -0
- package/dist/engine/renderers/index.d.ts.map +1 -0
- package/dist/engine/renderers/index.js +13 -0
- package/dist/engine/renderers/opencode.d.ts +18 -0
- package/dist/engine/renderers/opencode.d.ts.map +1 -0
- package/dist/engine/renderers/opencode.js +119 -0
- package/dist/engine/schema.d.ts +13 -0
- package/dist/engine/schema.d.ts.map +1 -0
- package/dist/engine/schema.js +125 -0
- package/dist/engine/seed.d.ts +14 -0
- package/dist/engine/seed.d.ts.map +1 -0
- package/dist/engine/seed.js +398 -0
- package/dist/engine/skills.d.ts +11 -0
- package/dist/engine/skills.d.ts.map +1 -0
- package/dist/engine/skills.js +24 -0
- package/dist/engine/targets.d.ts +17 -0
- package/dist/engine/targets.d.ts.map +1 -0
- package/dist/engine/targets.js +79 -0
- package/dist/engine/types.d.ts +100 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +555 -0
- package/dist/tools/branch.d.ts +2 -2
- package/dist/tools/engine.d.ts +22 -0
- package/dist/tools/engine.d.ts.map +1 -0
- package/dist/tools/engine.js +56 -0
- package/dist/tools/plan.d.ts +4 -4
- package/dist/tools/worktree.d.ts +2 -2
- package/dist/utils/cortex-code-bridge.d.ts +21 -0
- package/dist/utils/cortex-code-bridge.d.ts.map +1 -0
- package/dist/utils/cortex-code-bridge.js +104 -0
- package/package.json +6 -3
|
@@ -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"}
|