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.
Files changed (59) hide show
  1. package/README.md +85 -16
  2. package/dist/cli.js +165 -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/mcp-server.d.ts +2 -0
  48. package/dist/mcp-server.d.ts.map +1 -0
  49. package/dist/mcp-server.js +555 -0
  50. package/dist/tools/branch.d.ts +2 -2
  51. package/dist/tools/engine.d.ts +22 -0
  52. package/dist/tools/engine.d.ts.map +1 -0
  53. package/dist/tools/engine.js +56 -0
  54. package/dist/tools/plan.d.ts +4 -4
  55. package/dist/tools/worktree.d.ts +2 -2
  56. package/dist/utils/cortex-code-bridge.d.ts +21 -0
  57. package/dist/utils/cortex-code-bridge.d.ts.map +1 -0
  58. package/dist/utils/cortex-code-bridge.js +104 -0
  59. 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,2 @@
1
+ export declare function startMCPServer(): Promise<void>;
2
+ //# sourceMappingURL=mcp-server.d.ts.map
@@ -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
+ }
@@ -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" | "hotfix" | "docs" | "test" | "chore";
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"}