cortex-agents 2.1.0 → 2.3.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.
- package/.opencode/agents/build.md +179 -21
- package/.opencode/agents/debug.md +97 -11
- package/.opencode/agents/devops.md +75 -7
- package/.opencode/agents/fullstack.md +89 -1
- package/.opencode/agents/plan.md +83 -6
- package/.opencode/agents/security.md +60 -1
- package/.opencode/agents/testing.md +45 -1
- package/README.md +292 -356
- package/dist/cli.js +230 -65
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -5
- package/dist/tools/branch.d.ts +7 -1
- package/dist/tools/branch.d.ts.map +1 -1
- package/dist/tools/branch.js +88 -53
- package/dist/tools/cortex.d.ts +19 -0
- package/dist/tools/cortex.d.ts.map +1 -1
- package/dist/tools/cortex.js +110 -1
- package/dist/tools/session.d.ts.map +1 -1
- package/dist/tools/session.js +3 -1
- package/dist/tools/task.d.ts +20 -0
- package/dist/tools/task.d.ts.map +1 -0
- package/dist/tools/task.js +310 -0
- package/dist/tools/worktree.d.ts +42 -2
- package/dist/tools/worktree.d.ts.map +1 -1
- package/dist/tools/worktree.js +573 -98
- package/dist/utils/plan-extract.d.ts +37 -0
- package/dist/utils/plan-extract.d.ts.map +1 -0
- package/dist/utils/plan-extract.js +137 -0
- package/dist/utils/propagate.d.ts +22 -0
- package/dist/utils/propagate.d.ts.map +1 -0
- package/dist/utils/propagate.js +64 -0
- package/dist/utils/shell.d.ts +53 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +118 -0
- package/dist/utils/terminal.d.ts +66 -0
- package/dist/utils/terminal.d.ts.map +1 -0
- package/dist/utils/terminal.js +627 -0
- package/dist/utils/worktree-detect.d.ts +20 -0
- package/dist/utils/worktree-detect.d.ts.map +1 -0
- package/dist/utils/worktree-detect.js +43 -0
- package/package.json +13 -9
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as path from "path";
|
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import prompts from "prompts";
|
|
6
6
|
import { PRIMARY_AGENTS, SUBAGENTS, ALL_AGENTS, getPrimaryChoices, getSubagentChoices, } from "./registry.js";
|
|
7
|
-
const VERSION = "2.
|
|
7
|
+
const VERSION = "2.3.0";
|
|
8
8
|
const PLUGIN_NAME = "cortex-agents";
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
10
|
const __dirname = path.dirname(__filename);
|
|
@@ -38,6 +38,66 @@ function writeConfig(configPath, config) {
|
|
|
38
38
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
39
39
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
40
40
|
}
|
|
41
|
+
// ─── Per-Project Model Config (.opencode/models.json) ───────────────────────
|
|
42
|
+
const PROJECT_MODELS_FILE = "models.json";
|
|
43
|
+
function getProjectModelsPath() {
|
|
44
|
+
return path.join(process.cwd(), ".opencode", PROJECT_MODELS_FILE);
|
|
45
|
+
}
|
|
46
|
+
function hasProjectModelsConfig() {
|
|
47
|
+
return fs.existsSync(getProjectModelsPath());
|
|
48
|
+
}
|
|
49
|
+
function readProjectModels() {
|
|
50
|
+
const modelsPath = getProjectModelsPath();
|
|
51
|
+
if (!fs.existsSync(modelsPath))
|
|
52
|
+
return null;
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(fs.readFileSync(modelsPath, "utf-8"));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function writeProjectModels(primary, subagent) {
|
|
61
|
+
const modelsPath = getProjectModelsPath();
|
|
62
|
+
const dir = path.dirname(modelsPath);
|
|
63
|
+
if (!fs.existsSync(dir)) {
|
|
64
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
const config = {
|
|
67
|
+
primary: { model: primary },
|
|
68
|
+
subagent: { model: subagent },
|
|
69
|
+
agents: {},
|
|
70
|
+
};
|
|
71
|
+
for (const name of PRIMARY_AGENTS) {
|
|
72
|
+
config.agents[name] = { model: primary };
|
|
73
|
+
}
|
|
74
|
+
for (const name of SUBAGENTS) {
|
|
75
|
+
config.agents[name] = { model: subagent };
|
|
76
|
+
}
|
|
77
|
+
fs.writeFileSync(modelsPath, JSON.stringify(config, null, 2) + "\n");
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Sync .opencode/models.json → local opencode.json agent model settings.
|
|
81
|
+
* Creates or merges into local opencode.json at project root.
|
|
82
|
+
*/
|
|
83
|
+
function syncProjectModelsToConfig() {
|
|
84
|
+
const models = readProjectModels();
|
|
85
|
+
if (!models)
|
|
86
|
+
return false;
|
|
87
|
+
const localPath = path.join(process.cwd(), "opencode.json");
|
|
88
|
+
const config = fs.existsSync(localPath)
|
|
89
|
+
? readConfig(localPath)
|
|
90
|
+
: { $schema: "https://opencode.ai/config.json" };
|
|
91
|
+
if (!config.agent)
|
|
92
|
+
config.agent = {};
|
|
93
|
+
for (const [name, entry] of Object.entries(models.agents)) {
|
|
94
|
+
if (!config.agent[name])
|
|
95
|
+
config.agent[name] = {};
|
|
96
|
+
config.agent[name].model = entry.model;
|
|
97
|
+
}
|
|
98
|
+
writeConfig(localPath, config);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
41
101
|
// ─── File Copy Helpers ───────────────────────────────────────────────────────
|
|
42
102
|
function copyDirRecursive(src, dest) {
|
|
43
103
|
fs.mkdirSync(dest, { recursive: true });
|
|
@@ -137,8 +197,16 @@ function install() {
|
|
|
137
197
|
// Copy agents and skills into the global opencode config dir
|
|
138
198
|
console.log("Installing agents and skills...");
|
|
139
199
|
installAgentsAndSkills(globalDir);
|
|
200
|
+
// Sync per-project models if .opencode/models.json exists
|
|
201
|
+
if (hasProjectModelsConfig()) {
|
|
202
|
+
console.log("\nPer-project model config found (.opencode/models.json)");
|
|
203
|
+
if (syncProjectModelsToConfig()) {
|
|
204
|
+
console.log(" Synced model settings to local opencode.json");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
140
207
|
console.log("\nDone! Next steps:");
|
|
141
|
-
console.log(` 1. Run 'npx ${PLUGIN_NAME} configure' to select your models`);
|
|
208
|
+
console.log(` 1. Run 'npx ${PLUGIN_NAME} configure' to select your models (global)`);
|
|
209
|
+
console.log(` Or 'npx ${PLUGIN_NAME} configure --project' for per-project config`);
|
|
142
210
|
console.log(" 2. Restart OpenCode to load the plugin\n");
|
|
143
211
|
}
|
|
144
212
|
function uninstall() {
|
|
@@ -180,23 +248,82 @@ function uninstall() {
|
|
|
180
248
|
async function configure() {
|
|
181
249
|
const args = process.argv.slice(3);
|
|
182
250
|
const isReset = args.includes("--reset");
|
|
251
|
+
const isProject = args.includes("--project");
|
|
183
252
|
// Handle --reset flag
|
|
184
253
|
if (isReset) {
|
|
185
|
-
return configureReset();
|
|
254
|
+
return configureReset(isProject);
|
|
255
|
+
}
|
|
256
|
+
const scope = isProject ? "Per-Project" : "Global";
|
|
257
|
+
console.log(`\n🔧 Cortex Agents — ${scope} Model Configuration\n`);
|
|
258
|
+
if (isProject) {
|
|
259
|
+
const opencodeDir = path.join(process.cwd(), ".opencode");
|
|
260
|
+
if (!fs.existsSync(opencodeDir)) {
|
|
261
|
+
console.log(`⚠ No .opencode/ directory found in ${process.cwd()}.`);
|
|
262
|
+
console.log(` Run 'npx ${PLUGIN_NAME} install' first, or use 'configure' without --project for global config.\n`);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
console.log(`Project: ${process.cwd()}`);
|
|
266
|
+
console.log(`Config: .opencode/models.json + opencode.json\n`);
|
|
186
267
|
}
|
|
187
|
-
|
|
188
|
-
// Ensure plugin is installed first
|
|
268
|
+
// Ensure plugin is installed first (for global mode)
|
|
189
269
|
const configInfo = findOpencodeConfig();
|
|
190
270
|
const config = configInfo
|
|
191
271
|
? readConfig(configInfo.path)
|
|
192
272
|
: { $schema: "https://opencode.ai/config.json" };
|
|
193
|
-
if (!config.plugin?.includes(PLUGIN_NAME)) {
|
|
273
|
+
if (!isProject && !config.plugin?.includes(PLUGIN_NAME)) {
|
|
194
274
|
console.log(`⚠ Plugin not installed. Adding '${PLUGIN_NAME}' to config first.\n`);
|
|
195
275
|
if (!config.plugin)
|
|
196
276
|
config.plugin = [];
|
|
197
277
|
config.plugin.push(PLUGIN_NAME);
|
|
198
278
|
}
|
|
199
279
|
// ── Primary model selection ────────────────────────────────
|
|
280
|
+
const { primary, subagent } = await promptModelSelection();
|
|
281
|
+
// ── Write config ───────────────────────────────────────────
|
|
282
|
+
if (isProject) {
|
|
283
|
+
// Per-project: write .opencode/models.json + sync to local opencode.json
|
|
284
|
+
writeProjectModels(primary, subagent);
|
|
285
|
+
syncProjectModelsToConfig();
|
|
286
|
+
const modelsPath = getProjectModelsPath();
|
|
287
|
+
const localConfigPath = path.join(process.cwd(), "opencode.json");
|
|
288
|
+
console.log("━".repeat(50));
|
|
289
|
+
console.log(`✓ Per-project config saved:\n`);
|
|
290
|
+
console.log(` Models: ${modelsPath}`);
|
|
291
|
+
console.log(` Runtime: ${localConfigPath}\n`);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
// Global: write to opencode.json (existing behavior)
|
|
295
|
+
if (!config.agent)
|
|
296
|
+
config.agent = {};
|
|
297
|
+
for (const name of PRIMARY_AGENTS) {
|
|
298
|
+
if (!config.agent[name])
|
|
299
|
+
config.agent[name] = {};
|
|
300
|
+
config.agent[name].model = primary;
|
|
301
|
+
}
|
|
302
|
+
for (const name of SUBAGENTS) {
|
|
303
|
+
if (!config.agent[name])
|
|
304
|
+
config.agent[name] = {};
|
|
305
|
+
config.agent[name].model = subagent;
|
|
306
|
+
}
|
|
307
|
+
const targetPath = configInfo?.path || path.join(getGlobalDir(), "opencode.json");
|
|
308
|
+
writeConfig(targetPath, config);
|
|
309
|
+
console.log("━".repeat(50));
|
|
310
|
+
console.log(`✓ Configuration saved to ${targetPath}\n`);
|
|
311
|
+
}
|
|
312
|
+
console.log(" Primary agents:");
|
|
313
|
+
for (const name of PRIMARY_AGENTS) {
|
|
314
|
+
console.log(` ${name.padEnd(10)} → ${primary}`);
|
|
315
|
+
}
|
|
316
|
+
console.log("\n Subagents:");
|
|
317
|
+
for (const name of SUBAGENTS) {
|
|
318
|
+
console.log(` ${name.padEnd(10)} → ${subagent}`);
|
|
319
|
+
}
|
|
320
|
+
console.log("\nRestart OpenCode to apply changes.\n");
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Interactive prompt for primary + subagent model selection.
|
|
324
|
+
* Shared between global and per-project configure flows.
|
|
325
|
+
*/
|
|
326
|
+
async function promptModelSelection() {
|
|
200
327
|
const primaryChoices = getPrimaryChoices();
|
|
201
328
|
primaryChoices.push({
|
|
202
329
|
title: "Enter custom model ID",
|
|
@@ -211,7 +338,6 @@ async function configure() {
|
|
|
211
338
|
choices: primaryChoices,
|
|
212
339
|
hint: "Use arrow keys, Enter to confirm",
|
|
213
340
|
});
|
|
214
|
-
// User cancelled (Ctrl+C)
|
|
215
341
|
if (primaryModel === undefined) {
|
|
216
342
|
console.log("\nConfiguration cancelled.\n");
|
|
217
343
|
process.exit(0);
|
|
@@ -265,36 +391,54 @@ async function configure() {
|
|
|
265
391
|
subagent = custom;
|
|
266
392
|
}
|
|
267
393
|
console.log(`✓ Subagent model: ${subagent}\n`);
|
|
268
|
-
|
|
269
|
-
if (!config.agent)
|
|
270
|
-
config.agent = {};
|
|
271
|
-
for (const name of PRIMARY_AGENTS) {
|
|
272
|
-
if (!config.agent[name])
|
|
273
|
-
config.agent[name] = {};
|
|
274
|
-
config.agent[name].model = primary;
|
|
275
|
-
}
|
|
276
|
-
for (const name of SUBAGENTS) {
|
|
277
|
-
if (!config.agent[name])
|
|
278
|
-
config.agent[name] = {};
|
|
279
|
-
config.agent[name].model = subagent;
|
|
280
|
-
}
|
|
281
|
-
const targetPath = configInfo?.path || path.join(getGlobalDir(), "opencode.json");
|
|
282
|
-
writeConfig(targetPath, config);
|
|
283
|
-
// ── Print summary ──────────────────────────────────────────
|
|
284
|
-
console.log("━".repeat(50));
|
|
285
|
-
console.log(`✓ Configuration saved to ${targetPath}\n`);
|
|
286
|
-
console.log(" Primary agents:");
|
|
287
|
-
for (const name of PRIMARY_AGENTS) {
|
|
288
|
-
console.log(` ${name.padEnd(10)} → ${primary}`);
|
|
289
|
-
}
|
|
290
|
-
console.log("\n Subagents:");
|
|
291
|
-
for (const name of SUBAGENTS) {
|
|
292
|
-
console.log(` ${name.padEnd(10)} → ${subagent}`);
|
|
293
|
-
}
|
|
294
|
-
console.log("\nRestart OpenCode to apply changes.\n");
|
|
394
|
+
return { primary, subagent };
|
|
295
395
|
}
|
|
296
|
-
function configureReset() {
|
|
297
|
-
|
|
396
|
+
function configureReset(isProject = false) {
|
|
397
|
+
const scope = isProject ? "Per-Project" : "Global";
|
|
398
|
+
console.log(`\n🔧 Cortex Agents — Reset ${scope} Model Configuration\n`);
|
|
399
|
+
if (isProject) {
|
|
400
|
+
// Reset per-project config
|
|
401
|
+
const modelsPath = getProjectModelsPath();
|
|
402
|
+
let removedModels = false;
|
|
403
|
+
if (fs.existsSync(modelsPath)) {
|
|
404
|
+
fs.unlinkSync(modelsPath);
|
|
405
|
+
console.log(`✓ Removed ${modelsPath}`);
|
|
406
|
+
removedModels = true;
|
|
407
|
+
}
|
|
408
|
+
// Also clean agent models from local opencode.json
|
|
409
|
+
const localConfigPath = path.join(process.cwd(), "opencode.json");
|
|
410
|
+
if (fs.existsSync(localConfigPath)) {
|
|
411
|
+
const config = readConfig(localConfigPath);
|
|
412
|
+
if (config.agent) {
|
|
413
|
+
let resetCount = 0;
|
|
414
|
+
for (const name of ALL_AGENTS) {
|
|
415
|
+
if (config.agent[name]?.model) {
|
|
416
|
+
delete config.agent[name].model;
|
|
417
|
+
resetCount++;
|
|
418
|
+
if (Object.keys(config.agent[name]).length === 0) {
|
|
419
|
+
delete config.agent[name];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (Object.keys(config.agent).length === 0) {
|
|
424
|
+
delete config.agent;
|
|
425
|
+
}
|
|
426
|
+
if (resetCount > 0) {
|
|
427
|
+
writeConfig(localConfigPath, config);
|
|
428
|
+
console.log(`✓ Removed ${resetCount} agent model entries from ${localConfigPath}`);
|
|
429
|
+
removedModels = true;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (!removedModels) {
|
|
434
|
+
console.log("No per-project model configuration found. Nothing to reset.\n");
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
console.log("\nAgents will now fall back to global or OpenCode default models.");
|
|
438
|
+
console.log("Restart OpenCode to apply changes.\n");
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
// Global reset (existing behavior)
|
|
298
442
|
const configInfo = findOpencodeConfig();
|
|
299
443
|
if (!configInfo) {
|
|
300
444
|
console.log("No OpenCode config found. Nothing to reset.\n");
|
|
@@ -355,12 +499,20 @@ function status() {
|
|
|
355
499
|
: 0;
|
|
356
500
|
console.log(`\nAgents installed: ${agentCount}`);
|
|
357
501
|
console.log(`Skills installed: ${skillCount}`);
|
|
358
|
-
// Show model configuration
|
|
502
|
+
// Show per-project model configuration
|
|
503
|
+
const projectModels = readProjectModels();
|
|
504
|
+
if (projectModels) {
|
|
505
|
+
console.log("\nPer-Project Models (.opencode/models.json):");
|
|
506
|
+
console.log(` Primary agents: ${projectModels.primary.model}`);
|
|
507
|
+
console.log(` Subagents: ${projectModels.subagent.model}`);
|
|
508
|
+
}
|
|
509
|
+
// Show global/active model configuration
|
|
359
510
|
if (config.agent) {
|
|
360
511
|
const primaryModels = PRIMARY_AGENTS.map((n) => config.agent?.[n]?.model).filter(Boolean);
|
|
361
512
|
const subagentModels = SUBAGENTS.map((n) => config.agent?.[n]?.model).filter(Boolean);
|
|
362
513
|
if (primaryModels.length > 0 || subagentModels.length > 0) {
|
|
363
|
-
|
|
514
|
+
const source = projectModels ? "Active" : "Global";
|
|
515
|
+
console.log(`\n${source} Model Configuration (opencode.json):`);
|
|
364
516
|
if (primaryModels.length > 0) {
|
|
365
517
|
const unique = [...new Set(primaryModels)];
|
|
366
518
|
console.log(` Primary agents: ${unique.join(", ")}`);
|
|
@@ -370,14 +522,16 @@ function status() {
|
|
|
370
522
|
console.log(` Subagents: ${unique.join(", ")}`);
|
|
371
523
|
}
|
|
372
524
|
}
|
|
373
|
-
else {
|
|
525
|
+
else if (!projectModels) {
|
|
374
526
|
console.log("\nModels: Not configured (using OpenCode defaults)");
|
|
375
|
-
console.log(` Run 'npx ${PLUGIN_NAME} configure'
|
|
527
|
+
console.log(` Run 'npx ${PLUGIN_NAME} configure' for global config`);
|
|
528
|
+
console.log(` Run 'npx ${PLUGIN_NAME} configure --project' for per-project config`);
|
|
376
529
|
}
|
|
377
530
|
}
|
|
378
|
-
else {
|
|
531
|
+
else if (!projectModels) {
|
|
379
532
|
console.log("\nModels: Not configured (using OpenCode defaults)");
|
|
380
|
-
console.log(` Run 'npx ${PLUGIN_NAME} configure'
|
|
533
|
+
console.log(` Run 'npx ${PLUGIN_NAME} configure' for global config`);
|
|
534
|
+
console.log(` Run 'npx ${PLUGIN_NAME} configure --project' for per-project config`);
|
|
381
535
|
}
|
|
382
536
|
if (!isInstalled) {
|
|
383
537
|
console.log(`\nRun 'npx ${PLUGIN_NAME} install' to add to config.`);
|
|
@@ -387,48 +541,59 @@ function status() {
|
|
|
387
541
|
function help() {
|
|
388
542
|
console.log(`${PLUGIN_NAME} v${VERSION}
|
|
389
543
|
|
|
390
|
-
|
|
391
|
-
|
|
544
|
+
Supercharge OpenCode with structured workflows, intelligent agents,
|
|
545
|
+
and automated development practices.
|
|
392
546
|
|
|
393
547
|
USAGE:
|
|
394
548
|
npx ${PLUGIN_NAME} <command> [options]
|
|
395
549
|
|
|
396
550
|
COMMANDS:
|
|
397
551
|
install Install plugin, agents, and skills into OpenCode config
|
|
398
|
-
configure Interactive model selection
|
|
552
|
+
configure Interactive model selection (global)
|
|
553
|
+
configure --project Interactive model selection (per-project, saves to .opencode/)
|
|
399
554
|
configure --reset Reset model configuration to OpenCode defaults
|
|
555
|
+
configure --project --reset Reset per-project model configuration
|
|
400
556
|
uninstall Remove plugin, agents, skills, and model config
|
|
401
557
|
status Show installation and model configuration status
|
|
402
558
|
help Show this help message
|
|
403
559
|
|
|
404
560
|
EXAMPLES:
|
|
405
|
-
npx ${PLUGIN_NAME} install
|
|
406
|
-
npx ${PLUGIN_NAME} configure
|
|
407
|
-
npx ${PLUGIN_NAME} configure --
|
|
408
|
-
npx ${PLUGIN_NAME}
|
|
561
|
+
npx ${PLUGIN_NAME} install # Install plugin
|
|
562
|
+
npx ${PLUGIN_NAME} configure # Global model selection
|
|
563
|
+
npx ${PLUGIN_NAME} configure --project # Per-project models (.opencode/models.json)
|
|
564
|
+
npx ${PLUGIN_NAME} configure --reset # Reset global models
|
|
565
|
+
npx ${PLUGIN_NAME} configure --project --reset # Reset per-project models
|
|
566
|
+
npx ${PLUGIN_NAME} status # Check status
|
|
409
567
|
|
|
410
|
-
|
|
411
|
-
Primary
|
|
568
|
+
AGENTS:
|
|
569
|
+
Primary (build, plan, debug):
|
|
412
570
|
Handle complex tasks — select your best model.
|
|
413
571
|
|
|
414
572
|
Subagents (fullstack, testing, security, devops):
|
|
415
573
|
Handle focused tasks — a fast/cheap model works great.
|
|
416
574
|
|
|
417
|
-
|
|
418
|
-
cortex_init, cortex_status
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
575
|
+
TOOLS (23):
|
|
576
|
+
cortex_init, cortex_status .cortex directory management
|
|
577
|
+
cortex_configure Per-project model configuration
|
|
578
|
+
worktree_create, worktree_list Git worktree management
|
|
579
|
+
worktree_remove, worktree_open
|
|
580
|
+
worktree_launch Launch worktree (terminal/PTY/background)
|
|
581
|
+
branch_create, branch_status Git branch operations
|
|
582
|
+
branch_switch
|
|
583
|
+
plan_save, plan_list Plan persistence
|
|
584
|
+
plan_load, plan_delete
|
|
585
|
+
session_save, session_list Session management
|
|
586
|
+
session_load
|
|
587
|
+
docs_init, docs_save Mermaid documentation
|
|
588
|
+
docs_list, docs_index
|
|
589
|
+
task_finalize Commit, push, and create PR
|
|
428
590
|
|
|
429
|
-
|
|
430
|
-
web-development,
|
|
431
|
-
|
|
591
|
+
SKILLS (14):
|
|
592
|
+
web-development, frontend-development, backend-development,
|
|
593
|
+
mobile-development, desktop-development, database-design,
|
|
594
|
+
api-design, architecture-patterns, design-patterns,
|
|
595
|
+
testing-strategies, security-hardening, deployment-automation,
|
|
596
|
+
performance-optimization, code-quality, git-workflow
|
|
432
597
|
`);
|
|
433
598
|
}
|
|
434
599
|
// ─── CLI Entry Point ─────────────────────────────────────────────────────────
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAsBlD,eAAO,MAAM,YAAY,EAAE,MAmE1B,CAAC;AAGF,eAAe,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import * as branch from "./tools/branch";
|
|
|
5
5
|
import * as plan from "./tools/plan";
|
|
6
6
|
import * as session from "./tools/session";
|
|
7
7
|
import * as docs from "./tools/docs";
|
|
8
|
+
import * as task from "./tools/task";
|
|
8
9
|
// Agent descriptions for handover toast notifications
|
|
9
10
|
const AGENT_DESCRIPTIONS = {
|
|
10
11
|
build: "Development mode — ready to implement",
|
|
@@ -21,13 +22,15 @@ export const CortexPlugin = async (ctx) => {
|
|
|
21
22
|
// Cortex tools - .cortex directory management
|
|
22
23
|
cortex_init: cortex.init,
|
|
23
24
|
cortex_status: cortex.status,
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
cortex_configure: cortex.configure,
|
|
26
|
+
// Worktree tools - git worktree management (factories for toast notifications)
|
|
27
|
+
worktree_create: worktree.createCreate(ctx.client),
|
|
26
28
|
worktree_list: worktree.list,
|
|
27
|
-
worktree_remove: worktree.
|
|
29
|
+
worktree_remove: worktree.createRemove(ctx.client),
|
|
28
30
|
worktree_open: worktree.open,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
worktree_launch: worktree.createLaunch(ctx.client, ctx.$),
|
|
32
|
+
// Branch tools - git branch operations (factory for toast notifications)
|
|
33
|
+
branch_create: branch.createCreate(ctx.client),
|
|
31
34
|
branch_status: branch.status,
|
|
32
35
|
branch_switch: branch.switch_,
|
|
33
36
|
// Plan tools - implementation plan persistence
|
|
@@ -44,6 +47,8 @@ export const CortexPlugin = async (ctx) => {
|
|
|
44
47
|
docs_save: docs.save,
|
|
45
48
|
docs_list: docs.list,
|
|
46
49
|
docs_index: docs.index,
|
|
50
|
+
// Task tools - finalize workflow (commit, push, PR)
|
|
51
|
+
task_finalize: task.finalize,
|
|
47
52
|
},
|
|
48
53
|
// Agent handover toast notifications
|
|
49
54
|
async event({ event }) {
|
package/dist/tools/branch.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
type Client = PluginInput["client"];
|
|
3
|
+
/**
|
|
4
|
+
* Factory function that creates the branch_create tool with access
|
|
5
|
+
* to the OpenCode client for toast notifications.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createCreate(client: Client): {
|
|
2
8
|
description: string;
|
|
3
9
|
args: {
|
|
4
10
|
name: import("zod").ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/tools/branch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/tools/branch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAMvD,KAAK,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEpC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;EA0E1C;AAED,eAAO,MAAM,MAAM;;;;CAkGjB,CAAC;AAEH,eAAO,MAAM,OAAO;;;;;;;;CAmClB,CAAC;AAGH,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,CAAC"}
|
package/dist/tools/branch.js
CHANGED
|
@@ -1,57 +1,91 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import { git } from "../utils/shell.js";
|
|
2
3
|
const PROTECTED_BRANCHES = ["main", "master", "develop", "production", "staging"];
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Factory function that creates the branch_create tool with access
|
|
6
|
+
* to the OpenCode client for toast notifications.
|
|
7
|
+
*/
|
|
8
|
+
export function createCreate(client) {
|
|
9
|
+
return tool({
|
|
10
|
+
description: "Create and checkout a new git branch with proper naming convention",
|
|
11
|
+
args: {
|
|
12
|
+
name: tool.schema
|
|
13
|
+
.string()
|
|
14
|
+
.describe("Branch name slug (e.g., 'user-authentication', 'fix-login')"),
|
|
15
|
+
type: tool.schema
|
|
16
|
+
.enum(["feature", "bugfix", "hotfix", "refactor", "docs", "test", "chore"])
|
|
17
|
+
.describe("Branch type - determines prefix"),
|
|
18
|
+
},
|
|
19
|
+
async execute(args, context) {
|
|
20
|
+
const { name, type } = args;
|
|
21
|
+
const branchName = `${type}/${name}`;
|
|
22
|
+
// Check if we're in a git repository
|
|
23
|
+
try {
|
|
24
|
+
await git(context.worktree, "rev-parse", "--git-dir");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return "✗ Error: Not in a git repository";
|
|
28
|
+
}
|
|
29
|
+
// Check if branch already exists
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await git(context.worktree, "branch", "--list", branchName);
|
|
32
|
+
if (stdout.trim()) {
|
|
33
|
+
return `✗ Error: Branch '${branchName}' already exists.
|
|
28
34
|
|
|
29
35
|
Use branch_switch to switch to it, or choose a different name.`;
|
|
36
|
+
}
|
|
30
37
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
catch {
|
|
39
|
+
// Ignore error, branch check is optional
|
|
40
|
+
}
|
|
41
|
+
// Create and checkout the branch
|
|
42
|
+
try {
|
|
43
|
+
await git(context.worktree, "checkout", "-b", branchName);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
try {
|
|
47
|
+
await client.tui.showToast({
|
|
48
|
+
body: {
|
|
49
|
+
title: `Branch: ${branchName}`,
|
|
50
|
+
message: `Failed to create: ${error.message || error}`,
|
|
51
|
+
variant: "error",
|
|
52
|
+
duration: 8000,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Toast failure is non-fatal
|
|
58
|
+
}
|
|
59
|
+
return `✗ Error creating branch: ${error.message || error}`;
|
|
60
|
+
}
|
|
61
|
+
// Notify via toast
|
|
62
|
+
try {
|
|
63
|
+
await client.tui.showToast({
|
|
64
|
+
body: {
|
|
65
|
+
title: `Branch: ${branchName}`,
|
|
66
|
+
message: `Created and checked out`,
|
|
67
|
+
variant: "success",
|
|
68
|
+
duration: 4000,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Toast failure is non-fatal
|
|
74
|
+
}
|
|
75
|
+
return `✓ Created and switched to branch: ${branchName}
|
|
43
76
|
|
|
44
77
|
You are now on branch '${branchName}'.
|
|
45
78
|
Make your changes and commit when ready.`;
|
|
46
|
-
|
|
47
|
-
});
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
48
82
|
export const status = tool({
|
|
49
83
|
description: "Get current git branch status - branch name, uncommitted changes, and whether on protected branch",
|
|
50
84
|
args: {},
|
|
51
85
|
async execute(args, context) {
|
|
52
86
|
// Check if we're in a git repository
|
|
53
87
|
try {
|
|
54
|
-
await
|
|
88
|
+
await git(context.worktree, "rev-parse", "--git-dir");
|
|
55
89
|
}
|
|
56
90
|
catch {
|
|
57
91
|
return "✗ Not in a git repository";
|
|
@@ -64,7 +98,8 @@ export const status = tool({
|
|
|
64
98
|
let aheadBehind = "";
|
|
65
99
|
// Get current branch
|
|
66
100
|
try {
|
|
67
|
-
|
|
101
|
+
const { stdout } = await git(context.worktree, "branch", "--show-current");
|
|
102
|
+
currentBranch = stdout.trim();
|
|
68
103
|
if (!currentBranch) {
|
|
69
104
|
currentBranch = "(detached HEAD)";
|
|
70
105
|
}
|
|
@@ -76,17 +111,17 @@ export const status = tool({
|
|
|
76
111
|
isProtected = PROTECTED_BRANCHES.includes(currentBranch);
|
|
77
112
|
// Check for changes
|
|
78
113
|
try {
|
|
79
|
-
const
|
|
80
|
-
const lines =
|
|
114
|
+
const { stdout } = await git(context.worktree, "status", "--porcelain");
|
|
115
|
+
const lines = stdout.trim().split("\n").filter((l) => l);
|
|
81
116
|
for (const line of lines) {
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
117
|
+
const st = line.substring(0, 2);
|
|
118
|
+
if (st[0] !== " " && st[0] !== "?") {
|
|
84
119
|
stagedChanges = true;
|
|
85
120
|
}
|
|
86
|
-
if (
|
|
121
|
+
if (st[1] !== " " && st[1] !== "?") {
|
|
87
122
|
hasChanges = true;
|
|
88
123
|
}
|
|
89
|
-
if (
|
|
124
|
+
if (st === "??") {
|
|
90
125
|
untrackedFiles = true;
|
|
91
126
|
}
|
|
92
127
|
}
|
|
@@ -96,8 +131,8 @@ export const status = tool({
|
|
|
96
131
|
}
|
|
97
132
|
// Check ahead/behind
|
|
98
133
|
try {
|
|
99
|
-
const
|
|
100
|
-
const [ahead, behind] =
|
|
134
|
+
const { stdout } = await git(context.worktree, "rev-list", "--left-right", "--count", "HEAD...@{upstream}");
|
|
135
|
+
const [ahead, behind] = stdout.trim().split(/\s+/);
|
|
101
136
|
if (parseInt(ahead) > 0 || parseInt(behind) > 0) {
|
|
102
137
|
aheadBehind = `Ahead: ${ahead}, Behind: ${behind}`;
|
|
103
138
|
}
|
|
@@ -146,10 +181,10 @@ export const switch_ = tool({
|
|
|
146
181
|
const { branch } = args;
|
|
147
182
|
// Check if branch exists
|
|
148
183
|
try {
|
|
149
|
-
const
|
|
150
|
-
if (!
|
|
184
|
+
const { stdout } = await git(context.worktree, "branch", "--list", branch);
|
|
185
|
+
if (!stdout.trim()) {
|
|
151
186
|
// Try remote branch
|
|
152
|
-
const remoteBranches = await
|
|
187
|
+
const { stdout: remoteBranches } = await git(context.worktree, "branch", "-r", "--list", `origin/${branch}`);
|
|
153
188
|
if (!remoteBranches.trim()) {
|
|
154
189
|
return `✗ Error: Branch '${branch}' not found locally or on origin.
|
|
155
190
|
|
|
@@ -162,7 +197,7 @@ Use branch_create to create a new branch.`;
|
|
|
162
197
|
}
|
|
163
198
|
// Switch to branch
|
|
164
199
|
try {
|
|
165
|
-
await
|
|
200
|
+
await git(context.worktree, "checkout", branch);
|
|
166
201
|
}
|
|
167
202
|
catch (error) {
|
|
168
203
|
return `✗ Error switching branch: ${error.message || error}
|