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
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CortexCodeBridge } from "./utils/cortex-code-bridge.js";
|
|
1
2
|
// Import all tool modules
|
|
2
3
|
import * as cortex from "./tools/cortex";
|
|
3
4
|
import * as worktree from "./tools/worktree";
|
|
@@ -9,6 +10,7 @@ import * as task from "./tools/task";
|
|
|
9
10
|
import * as github from "./tools/github";
|
|
10
11
|
import * as repl from "./tools/repl";
|
|
11
12
|
import * as qualityGate from "./tools/quality-gate";
|
|
13
|
+
import * as engineTools from "./tools/engine";
|
|
12
14
|
// ─── Agent Descriptions (for handover toasts) ───────────────────────────────
|
|
13
15
|
const AGENT_DESCRIPTIONS = {
|
|
14
16
|
implement: "Development mode — ready to implement",
|
|
@@ -149,6 +151,7 @@ function extractErrorMessage(error) {
|
|
|
149
151
|
}
|
|
150
152
|
// ─── Plugin Entry ────────────────────────────────────────────────────────────
|
|
151
153
|
export const CortexPlugin = async (ctx) => {
|
|
154
|
+
const bridge = new CortexCodeBridge();
|
|
152
155
|
return {
|
|
153
156
|
tool: {
|
|
154
157
|
// Cortex tools - .cortex directory management
|
|
@@ -193,6 +196,9 @@ export const CortexPlugin = async (ctx) => {
|
|
|
193
196
|
repl_summary: repl.summary,
|
|
194
197
|
// Quality gate aggregation tool
|
|
195
198
|
quality_gate_summary: qualityGate.qualityGateSummary,
|
|
199
|
+
// Engine-backed tools
|
|
200
|
+
cortex_get_skill: engineTools.getSkill,
|
|
201
|
+
cortex_list_agents: engineTools.listAgents,
|
|
196
202
|
},
|
|
197
203
|
// ── Post-execution toast notifications ────────────────────────────────
|
|
198
204
|
//
|
|
@@ -235,6 +241,53 @@ export const CortexPlugin = async (ctx) => {
|
|
|
235
241
|
},
|
|
236
242
|
// ── Event-driven notifications ───────────────────────────────────────
|
|
237
243
|
async event({ event }) {
|
|
244
|
+
// ── Cortex Code bridge (fire-and-forget, no-op outside Cortex Code) ──
|
|
245
|
+
if (bridge.isActive) {
|
|
246
|
+
// session.status busy → agent started working
|
|
247
|
+
if (event.type === "session.status" &&
|
|
248
|
+
event.properties.status.type === "busy") {
|
|
249
|
+
bridge.taskStarted();
|
|
250
|
+
}
|
|
251
|
+
// session.status idle → agent waiting for user input
|
|
252
|
+
if (event.type === "session.status" &&
|
|
253
|
+
event.properties.status.type === "idle") {
|
|
254
|
+
bridge.interactionNeeded("input");
|
|
255
|
+
}
|
|
256
|
+
// session.idle → all processing complete
|
|
257
|
+
if (event.type === "session.idle") {
|
|
258
|
+
bridge.taskFinished();
|
|
259
|
+
}
|
|
260
|
+
// session.error → forward error
|
|
261
|
+
if (event.type === "session.error") {
|
|
262
|
+
const rawError = event.properties.error;
|
|
263
|
+
const error = rawError &&
|
|
264
|
+
typeof rawError === "object" &&
|
|
265
|
+
"name" in rawError &&
|
|
266
|
+
typeof rawError.name === "string"
|
|
267
|
+
? rawError
|
|
268
|
+
: undefined;
|
|
269
|
+
bridge.error(extractErrorMessage(error));
|
|
270
|
+
}
|
|
271
|
+
// message.part.updated — text content
|
|
272
|
+
if (event.type === "message.part.updated" &&
|
|
273
|
+
event.properties.part.type === "text") {
|
|
274
|
+
bridge.text(event.properties.part.text);
|
|
275
|
+
}
|
|
276
|
+
// message.part.updated — tool call
|
|
277
|
+
if (event.type === "message.part.updated" &&
|
|
278
|
+
event.properties.part.type === "tool") {
|
|
279
|
+
const part = event.properties.part;
|
|
280
|
+
bridge.toolCall(part.callID, part.tool, part.metadata);
|
|
281
|
+
}
|
|
282
|
+
// message.updated — token/cost tracking (assistant messages with usage)
|
|
283
|
+
if (event.type === "message.updated" &&
|
|
284
|
+
event.properties.info.role === "assistant") {
|
|
285
|
+
const msg = event.properties.info;
|
|
286
|
+
if ("tokens" in msg && msg.tokens) {
|
|
287
|
+
bridge.usage(msg.tokens.input ?? 0, msg.tokens.output ?? 0, msg.cost ?? 0, "modelID" in msg ? msg.modelID : undefined);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
238
291
|
try {
|
|
239
292
|
// Agent handover notifications
|
|
240
293
|
if (event.type === "message.part.updated" &&
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AA4iBA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAsDpD"}
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { exec } from "./utils/shell.js";
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
// Get package version for server identification
|
|
10
|
+
const VERSION = JSON.parse(fs.readFileSync(path.resolve(__dirname, "..", "package.json"), "utf-8")).version;
|
|
11
|
+
// ─── Tool Implementations ─────────────────────────────────────────────────────
|
|
12
|
+
// These are stub implementations that demonstrate MCP functionality
|
|
13
|
+
// In production, they would wrap the actual tool implementations from src/tools/
|
|
14
|
+
const TOOL_REGISTRY = {
|
|
15
|
+
// Cortex tools
|
|
16
|
+
cortex_init: {
|
|
17
|
+
description: "Initialize .cortex directory in project root for plan storage, session history, and configuration",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {},
|
|
21
|
+
required: [],
|
|
22
|
+
},
|
|
23
|
+
handler: async (_args, context) => {
|
|
24
|
+
const cortexPath = path.join(context.worktree, ".cortex");
|
|
25
|
+
try {
|
|
26
|
+
if (!fs.existsSync(cortexPath)) {
|
|
27
|
+
fs.mkdirSync(cortexPath, { recursive: true });
|
|
28
|
+
fs.mkdirSync(path.join(cortexPath, "plans"), { recursive: true });
|
|
29
|
+
fs.mkdirSync(path.join(cortexPath, "sessions"), { recursive: true });
|
|
30
|
+
return `✓ Initialized .cortex directory at ${cortexPath}`;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return `✓ .cortex directory already exists at ${cortexPath}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return `✗ Error initializing .cortex: ${error instanceof Error ? error.message : String(error)}`;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
cortex_status: {
|
|
42
|
+
description: "Check .cortex directory status - whether it exists, plan count, session count",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {},
|
|
46
|
+
required: [],
|
|
47
|
+
},
|
|
48
|
+
handler: async (_args, context) => {
|
|
49
|
+
const cortexPath = path.join(context.worktree, ".cortex");
|
|
50
|
+
if (!fs.existsSync(cortexPath)) {
|
|
51
|
+
return `✗ .cortex directory not found at ${cortexPath}\n\nRun cortex_init to initialize.`;
|
|
52
|
+
}
|
|
53
|
+
const plansPath = path.join(cortexPath, "plans");
|
|
54
|
+
const sessionsPath = path.join(cortexPath, "sessions");
|
|
55
|
+
const planCount = fs.existsSync(plansPath)
|
|
56
|
+
? fs.readdirSync(plansPath).filter((f) => f.endsWith(".md")).length
|
|
57
|
+
: 0;
|
|
58
|
+
const sessionCount = fs.existsSync(sessionsPath)
|
|
59
|
+
? fs.readdirSync(sessionsPath).filter((f) => f.endsWith(".md")).length
|
|
60
|
+
: 0;
|
|
61
|
+
return `✓ .cortex directory status
|
|
62
|
+
|
|
63
|
+
Location: ${cortexPath}
|
|
64
|
+
Plans: ${planCount}
|
|
65
|
+
Sessions: ${sessionCount}`;
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
cortex_configure: {
|
|
69
|
+
description: "Configure AI models for this project (primary agents and subagents)",
|
|
70
|
+
inputSchema: {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
scope: {
|
|
74
|
+
type: "string",
|
|
75
|
+
enum: ["project", "global"],
|
|
76
|
+
description: "Configuration scope: project-specific or global",
|
|
77
|
+
},
|
|
78
|
+
primaryModel: {
|
|
79
|
+
type: "string",
|
|
80
|
+
description: "Model ID for primary agents",
|
|
81
|
+
},
|
|
82
|
+
subagentModel: {
|
|
83
|
+
type: "string",
|
|
84
|
+
description: "Model ID for subagents",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ["scope", "primaryModel", "subagentModel"],
|
|
88
|
+
},
|
|
89
|
+
handler: async (args) => {
|
|
90
|
+
return `✓ Configured models\n\nScope: ${args.scope}\nPrimary: ${args.primaryModel}\nSubagent: ${args.subagentModel}`;
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
// Worktree tools
|
|
94
|
+
worktree_list: {
|
|
95
|
+
description: "List all git worktrees for the repository",
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: "object",
|
|
98
|
+
properties: {},
|
|
99
|
+
required: [],
|
|
100
|
+
},
|
|
101
|
+
handler: async (_args, context) => {
|
|
102
|
+
try {
|
|
103
|
+
const result = await exec("git", ["worktree", "list"], { cwd: context.worktree, nothrow: true });
|
|
104
|
+
if (result.exitCode === 0) {
|
|
105
|
+
return `✓ Git worktrees:\n\n${result.stdout}`;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
return `✗ Not a git repository or git worktree list failed`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
return `✗ Error listing worktrees: ${error instanceof Error ? error.message : String(error)}`;
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
worktree_open: {
|
|
117
|
+
description: "Open a git worktree in the default editor or file explorer",
|
|
118
|
+
inputSchema: {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
name: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Worktree name to open",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
required: ["name"],
|
|
127
|
+
},
|
|
128
|
+
handler: async (args, context) => {
|
|
129
|
+
const worktreeName = args.name;
|
|
130
|
+
const worktreePath = path.join(context.worktree, "..", worktreeName);
|
|
131
|
+
if (fs.existsSync(worktreePath)) {
|
|
132
|
+
return `✓ Worktree path: ${worktreePath}`;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
return `✗ Worktree not found at ${worktreePath}`;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
// Branch tools
|
|
140
|
+
branch_status: {
|
|
141
|
+
description: "Get current git branch status",
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: "object",
|
|
144
|
+
properties: {},
|
|
145
|
+
required: [],
|
|
146
|
+
},
|
|
147
|
+
handler: async (_args, context) => {
|
|
148
|
+
try {
|
|
149
|
+
const result = await exec("git", ["branch", "--show-current"], { cwd: context.worktree, nothrow: true });
|
|
150
|
+
if (result.exitCode === 0) {
|
|
151
|
+
const branch = result.stdout.trim();
|
|
152
|
+
return `✓ Current branch: ${branch}`;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
return `✗ Failed to get branch status`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
return `✗ Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
branch_switch: {
|
|
164
|
+
description: "Switch to an existing git branch",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
type: "object",
|
|
167
|
+
properties: {
|
|
168
|
+
branch: {
|
|
169
|
+
type: "string",
|
|
170
|
+
description: "Branch name to switch to",
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
required: ["branch"],
|
|
174
|
+
},
|
|
175
|
+
handler: async (args, context) => {
|
|
176
|
+
const branch = args.branch;
|
|
177
|
+
try {
|
|
178
|
+
const result = await exec("git", ["checkout", branch], { cwd: context.worktree, nothrow: true });
|
|
179
|
+
if (result.exitCode === 0) {
|
|
180
|
+
return `✓ Switched to branch: ${branch}`;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
return `✗ Failed to switch branch: ${result.stderr}`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
return `✗ Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
// Plan tools
|
|
192
|
+
plan_list: {
|
|
193
|
+
description: "List all plans in .cortex/plans/ with preview",
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: "object",
|
|
196
|
+
properties: {
|
|
197
|
+
limit: {
|
|
198
|
+
type: "number",
|
|
199
|
+
description: "Maximum number of plans to return",
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
required: [],
|
|
203
|
+
},
|
|
204
|
+
handler: async (args, context) => {
|
|
205
|
+
const plansPath = path.join(context.worktree, ".cortex", "plans");
|
|
206
|
+
if (!fs.existsSync(plansPath)) {
|
|
207
|
+
return `No plans found. Run cortex_init to initialize.`;
|
|
208
|
+
}
|
|
209
|
+
const files = fs.readdirSync(plansPath).filter((f) => f.endsWith(".md")).sort().reverse();
|
|
210
|
+
const limit = args.limit || 10;
|
|
211
|
+
const limited = files.slice(0, Math.min(limit, files.length));
|
|
212
|
+
if (limited.length === 0) {
|
|
213
|
+
return `No plans saved in .cortex/plans/`;
|
|
214
|
+
}
|
|
215
|
+
let output = `✓ Plans (showing ${limited.length}):\n\n`;
|
|
216
|
+
for (const file of limited) {
|
|
217
|
+
output += ` • ${file}\n`;
|
|
218
|
+
}
|
|
219
|
+
return output;
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
plan_load: {
|
|
223
|
+
description: "Load a full plan by filename",
|
|
224
|
+
inputSchema: {
|
|
225
|
+
type: "object",
|
|
226
|
+
properties: {
|
|
227
|
+
filename: {
|
|
228
|
+
type: "string",
|
|
229
|
+
description: "Plan filename from .cortex/plans/",
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
required: ["filename"],
|
|
233
|
+
},
|
|
234
|
+
handler: async (args, context) => {
|
|
235
|
+
const filename = args.filename;
|
|
236
|
+
const filepath = path.join(context.worktree, ".cortex", "plans", filename);
|
|
237
|
+
if (!fs.existsSync(filepath)) {
|
|
238
|
+
return `✗ Plan not found: ${filename}`;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const content = fs.readFileSync(filepath, "utf-8");
|
|
242
|
+
return `✓ Plan: ${filename}\n\n${content}`;
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
return `✗ Error loading plan: ${error instanceof Error ? error.message : String(error)}`;
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
plan_save: {
|
|
250
|
+
description: "Save an implementation plan to .cortex/plans/ with mermaid diagram support",
|
|
251
|
+
inputSchema: {
|
|
252
|
+
type: "object",
|
|
253
|
+
properties: {
|
|
254
|
+
title: {
|
|
255
|
+
type: "string",
|
|
256
|
+
description: "Plan title",
|
|
257
|
+
},
|
|
258
|
+
type: {
|
|
259
|
+
type: "string",
|
|
260
|
+
enum: ["feature", "bugfix", "refactor", "architecture", "spike", "docs"],
|
|
261
|
+
description: "Plan type",
|
|
262
|
+
},
|
|
263
|
+
content: {
|
|
264
|
+
type: "string",
|
|
265
|
+
description: "Full plan content in markdown",
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
required: ["title", "type", "content"],
|
|
269
|
+
},
|
|
270
|
+
handler: async (args, context) => {
|
|
271
|
+
const { title, type, content } = args;
|
|
272
|
+
const plansPath = path.join(context.worktree, ".cortex", "plans");
|
|
273
|
+
try {
|
|
274
|
+
fs.mkdirSync(plansPath, { recursive: true });
|
|
275
|
+
const date = new Date().toISOString().split("T")[0];
|
|
276
|
+
const slug = title.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-");
|
|
277
|
+
const filename = `${date}-${type}-${slug}.md`;
|
|
278
|
+
const filepath = path.join(plansPath, filename);
|
|
279
|
+
fs.writeFileSync(filepath, `# ${title}\n\n${content}`);
|
|
280
|
+
return `✓ Plan saved: ${filename}`;
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
return `✗ Error saving plan: ${error instanceof Error ? error.message : String(error)}`;
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
plan_delete: {
|
|
288
|
+
description: "Delete a plan file",
|
|
289
|
+
inputSchema: {
|
|
290
|
+
type: "object",
|
|
291
|
+
properties: {
|
|
292
|
+
filename: {
|
|
293
|
+
type: "string",
|
|
294
|
+
description: "Plan filename to delete",
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
required: ["filename"],
|
|
298
|
+
},
|
|
299
|
+
handler: async (args, context) => {
|
|
300
|
+
const filename = args.filename;
|
|
301
|
+
const filepath = path.join(context.worktree, ".cortex", "plans", filename);
|
|
302
|
+
if (!fs.existsSync(filepath)) {
|
|
303
|
+
return `✗ Plan not found: ${filename}`;
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
fs.unlinkSync(filepath);
|
|
307
|
+
return `✓ Deleted plan: ${filename}`;
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
return `✗ Error deleting plan: ${error instanceof Error ? error.message : String(error)}`;
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
// Session tools
|
|
315
|
+
session_list: {
|
|
316
|
+
description: "List recent session summaries from .cortex/sessions/",
|
|
317
|
+
inputSchema: {
|
|
318
|
+
type: "object",
|
|
319
|
+
properties: {
|
|
320
|
+
limit: {
|
|
321
|
+
type: "number",
|
|
322
|
+
description: "Maximum number of sessions to return",
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
required: [],
|
|
326
|
+
},
|
|
327
|
+
handler: async (args, context) => {
|
|
328
|
+
const sessionsPath = path.join(context.worktree, ".cortex", "sessions");
|
|
329
|
+
if (!fs.existsSync(sessionsPath)) {
|
|
330
|
+
return `No sessions found. Sessions are created when you use session_save.`;
|
|
331
|
+
}
|
|
332
|
+
const files = fs.readdirSync(sessionsPath).filter((f) => f.endsWith(".md")).sort().reverse();
|
|
333
|
+
const limit = args.limit || 10;
|
|
334
|
+
const limited = files.slice(0, Math.min(limit, files.length));
|
|
335
|
+
if (limited.length === 0) {
|
|
336
|
+
return `No session summaries found in .cortex/sessions/`;
|
|
337
|
+
}
|
|
338
|
+
let output = `✓ Recent Sessions (showing ${limited.length}):\n\n`;
|
|
339
|
+
for (const file of limited) {
|
|
340
|
+
output += ` • ${file}\n`;
|
|
341
|
+
}
|
|
342
|
+
return output;
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
session_load: {
|
|
346
|
+
description: "Load a session summary by filename",
|
|
347
|
+
inputSchema: {
|
|
348
|
+
type: "object",
|
|
349
|
+
properties: {
|
|
350
|
+
filename: {
|
|
351
|
+
type: "string",
|
|
352
|
+
description: "Session filename",
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
required: ["filename"],
|
|
356
|
+
},
|
|
357
|
+
handler: async (args, context) => {
|
|
358
|
+
const filename = args.filename;
|
|
359
|
+
const filepath = path.join(context.worktree, ".cortex", "sessions", filename);
|
|
360
|
+
if (!fs.existsSync(filepath)) {
|
|
361
|
+
return `✗ Session not found: ${filename}`;
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
const content = fs.readFileSync(filepath, "utf-8");
|
|
365
|
+
return `✓ Session: ${filename}\n\n${content}`;
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
return `✗ Error loading session: ${error instanceof Error ? error.message : String(error)}`;
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
session_save: {
|
|
373
|
+
description: "Save a session summary with key decisions to .cortex/sessions/",
|
|
374
|
+
inputSchema: {
|
|
375
|
+
type: "object",
|
|
376
|
+
properties: {
|
|
377
|
+
summary: {
|
|
378
|
+
type: "string",
|
|
379
|
+
description: "Brief summary of what was accomplished",
|
|
380
|
+
},
|
|
381
|
+
decisions: {
|
|
382
|
+
type: "array",
|
|
383
|
+
items: { type: "string" },
|
|
384
|
+
description: "List of key decisions made",
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
required: ["summary", "decisions"],
|
|
388
|
+
},
|
|
389
|
+
handler: async (args, context) => {
|
|
390
|
+
const { summary, decisions } = args;
|
|
391
|
+
const sessionsPath = path.join(context.worktree, ".cortex", "sessions");
|
|
392
|
+
try {
|
|
393
|
+
fs.mkdirSync(sessionsPath, { recursive: true });
|
|
394
|
+
const date = new Date().toISOString().split("T")[0];
|
|
395
|
+
const sessionId = Math.random().toString(36).substring(2, 10);
|
|
396
|
+
const filename = `${date}-${sessionId}.md`;
|
|
397
|
+
const filepath = path.join(sessionsPath, filename);
|
|
398
|
+
const content = `# Session Summary\n\n${summary}\n\n## Key Decisions\n\n${decisions.map((d) => `- ${d}`).join("\n")}`;
|
|
399
|
+
fs.writeFileSync(filepath, content);
|
|
400
|
+
return `✓ Session saved: ${filename}`;
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
return `✗ Error saving session: ${error instanceof Error ? error.message : String(error)}`;
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
// Documentation tools
|
|
408
|
+
docs_init: {
|
|
409
|
+
description: "Initialize docs directory with decision/feature/flow subdirectories",
|
|
410
|
+
inputSchema: {
|
|
411
|
+
type: "object",
|
|
412
|
+
properties: {},
|
|
413
|
+
required: [],
|
|
414
|
+
},
|
|
415
|
+
handler: async (_args, context) => {
|
|
416
|
+
const docsPath = path.join(context.worktree, "docs");
|
|
417
|
+
try {
|
|
418
|
+
fs.mkdirSync(path.join(docsPath, "decisions"), { recursive: true });
|
|
419
|
+
fs.mkdirSync(path.join(docsPath, "features"), { recursive: true });
|
|
420
|
+
fs.mkdirSync(path.join(docsPath, "flows"), { recursive: true });
|
|
421
|
+
return `✓ Initialized docs directory at ${docsPath}`;
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
return `✗ Error initializing docs: ${error instanceof Error ? error.message : String(error)}`;
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
docs_list: {
|
|
429
|
+
description: "List all documentation files organized by type",
|
|
430
|
+
inputSchema: {
|
|
431
|
+
type: "object",
|
|
432
|
+
properties: {},
|
|
433
|
+
required: [],
|
|
434
|
+
},
|
|
435
|
+
handler: async (_args, context) => {
|
|
436
|
+
const docsPath = path.join(context.worktree, "docs");
|
|
437
|
+
if (!fs.existsSync(docsPath)) {
|
|
438
|
+
return `No docs found. Run docs_init to initialize.`;
|
|
439
|
+
}
|
|
440
|
+
const types = ["decisions", "features", "flows"];
|
|
441
|
+
let output = `✓ Documentation:\n\n`;
|
|
442
|
+
for (const type of types) {
|
|
443
|
+
const typePath = path.join(docsPath, type);
|
|
444
|
+
if (fs.existsSync(typePath)) {
|
|
445
|
+
const files = fs.readdirSync(typePath).filter((f) => f.endsWith(".md"));
|
|
446
|
+
output += `**${type}:** ${files.length} files\n`;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return output;
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
// GitHub tools
|
|
453
|
+
github_status: {
|
|
454
|
+
description: "Check GitHub CLI availability and authentication",
|
|
455
|
+
inputSchema: {
|
|
456
|
+
type: "object",
|
|
457
|
+
properties: {},
|
|
458
|
+
required: [],
|
|
459
|
+
},
|
|
460
|
+
handler: async (_args, context) => {
|
|
461
|
+
try {
|
|
462
|
+
const result = await exec("gh", ["auth", "status"], { cwd: context.worktree, nothrow: true });
|
|
463
|
+
if (result.exitCode === 0) {
|
|
464
|
+
return `✓ GitHub CLI authenticated`;
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
return `✗ GitHub CLI not authenticated. Run: gh auth login`;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
return `✗ GitHub CLI not installed. Install from https://cli.github.com/`;
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
// Task tools
|
|
476
|
+
task_finalize: {
|
|
477
|
+
description: "Finalize implementation - commit, push, and create pull request",
|
|
478
|
+
inputSchema: {
|
|
479
|
+
type: "object",
|
|
480
|
+
properties: {
|
|
481
|
+
commitMessage: {
|
|
482
|
+
type: "string",
|
|
483
|
+
description: "Commit message",
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
required: ["commitMessage"],
|
|
487
|
+
},
|
|
488
|
+
handler: async (args, context) => {
|
|
489
|
+
const message = args.commitMessage;
|
|
490
|
+
try {
|
|
491
|
+
await exec("git", ["add", "-A"], { cwd: context.worktree, nothrow: true });
|
|
492
|
+
const result = await exec("git", ["commit", "-m", message], { cwd: context.worktree, nothrow: true });
|
|
493
|
+
if (result.exitCode === 0) {
|
|
494
|
+
return `✓ Committed: ${message.substring(0, 50)}...`;
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
return `✗ Commit failed: ${result.stderr}`;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
return `✗ Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
};
|
|
506
|
+
// ─── MCP Server ──────────────────────────────────────────────────────────────
|
|
507
|
+
export async function startMCPServer() {
|
|
508
|
+
const mcpServer = new McpServer({
|
|
509
|
+
name: "cortex-agents",
|
|
510
|
+
version: VERSION,
|
|
511
|
+
});
|
|
512
|
+
// Get the current working directory as the worktree root
|
|
513
|
+
const worktree = process.cwd();
|
|
514
|
+
// Register all tools
|
|
515
|
+
for (const [toolName, spec] of Object.entries(TOOL_REGISTRY)) {
|
|
516
|
+
mcpServer.registerTool(toolName, {
|
|
517
|
+
description: spec.description,
|
|
518
|
+
inputSchema: spec.inputSchema,
|
|
519
|
+
}, async (args) => {
|
|
520
|
+
try {
|
|
521
|
+
const context = {
|
|
522
|
+
worktree,
|
|
523
|
+
directory: worktree,
|
|
524
|
+
sessionID: "mcp-session",
|
|
525
|
+
messageID: "mcp-message",
|
|
526
|
+
agent: "mcp",
|
|
527
|
+
};
|
|
528
|
+
const result = await spec.handler(args, context);
|
|
529
|
+
return {
|
|
530
|
+
content: [
|
|
531
|
+
{
|
|
532
|
+
type: "text",
|
|
533
|
+
text: result,
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
catch (error) {
|
|
539
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
540
|
+
return {
|
|
541
|
+
content: [
|
|
542
|
+
{
|
|
543
|
+
type: "text",
|
|
544
|
+
text: `Error: ${errorMessage}`,
|
|
545
|
+
},
|
|
546
|
+
],
|
|
547
|
+
isError: true,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
// Start server on stdio
|
|
553
|
+
const transport = new StdioServerTransport();
|
|
554
|
+
await mcpServer.connect(transport);
|
|
555
|
+
}
|
package/dist/tools/branch.d.ts
CHANGED
|
@@ -12,15 +12,15 @@ export declare function createCreate(client: Client): {
|
|
|
12
12
|
refactor: "refactor";
|
|
13
13
|
feature: "feature";
|
|
14
14
|
bugfix: "bugfix";
|
|
15
|
-
hotfix: "hotfix";
|
|
16
15
|
docs: "docs";
|
|
16
|
+
hotfix: "hotfix";
|
|
17
17
|
test: "test";
|
|
18
18
|
chore: "chore";
|
|
19
19
|
}>;
|
|
20
20
|
};
|
|
21
21
|
execute(args: {
|
|
22
22
|
name: string;
|
|
23
|
-
type: "refactor" | "feature" | "bugfix" | "
|
|
23
|
+
type: "refactor" | "feature" | "bugfix" | "docs" | "hotfix" | "test" | "chore";
|
|
24
24
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
25
25
|
};
|
|
26
26
|
export declare const status: {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const getSkill: {
|
|
2
|
+
description: string;
|
|
3
|
+
args: {
|
|
4
|
+
skillId: import("zod").ZodString;
|
|
5
|
+
};
|
|
6
|
+
execute(args: {
|
|
7
|
+
skillId: string;
|
|
8
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
9
|
+
};
|
|
10
|
+
export declare const listAgents: {
|
|
11
|
+
description: string;
|
|
12
|
+
args: {
|
|
13
|
+
mode: import("zod").ZodOptional<import("zod").ZodEnum<{
|
|
14
|
+
primary: "primary";
|
|
15
|
+
subagent: "subagent";
|
|
16
|
+
}>>;
|
|
17
|
+
};
|
|
18
|
+
execute(args: {
|
|
19
|
+
mode?: "primary" | "subagent" | undefined;
|
|
20
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/tools/engine.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,QAAQ;;;;;;;;CAoBnB,CAAC;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;;CA4BrB,CAAC"}
|