opencodekit 0.6.7 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/index.js +654 -651
  2. package/dist/template/.opencode/AGENTS.md +97 -13
  3. package/dist/template/.opencode/README.md +18 -16
  4. package/dist/template/.opencode/command/accessibility-check.md +1 -1
  5. package/dist/template/.opencode/command/analyze-mockup.md +1 -1
  6. package/dist/template/.opencode/command/analyze-project.md +11 -9
  7. package/dist/template/.opencode/command/brainstorm.md +1 -1
  8. package/dist/template/.opencode/command/commit.md +1 -1
  9. package/dist/template/.opencode/command/create.md +16 -2
  10. package/dist/template/.opencode/command/design-audit.md +1 -1
  11. package/dist/template/.opencode/command/design.md +1 -1
  12. package/dist/template/.opencode/command/finish.md +20 -8
  13. package/dist/template/.opencode/command/fix-ci.md +14 -9
  14. package/dist/template/.opencode/command/fix-types.md +6 -11
  15. package/dist/template/.opencode/command/fix-ui.md +1 -1
  16. package/dist/template/.opencode/command/fix.md +1 -1
  17. package/dist/template/.opencode/command/handoff.md +8 -6
  18. package/dist/template/.opencode/command/implement.md +33 -3
  19. package/dist/template/.opencode/command/import-plan.md +27 -14
  20. package/dist/template/.opencode/command/integration-test.md +7 -3
  21. package/dist/template/.opencode/command/issue.md +10 -9
  22. package/dist/template/.opencode/command/new-feature.md +6 -6
  23. package/dist/template/.opencode/command/plan.md +5 -5
  24. package/dist/template/.opencode/command/pr.md +4 -4
  25. package/dist/template/.opencode/command/research-and-implement.md +2 -2
  26. package/dist/template/.opencode/command/research-ui.md +1 -1
  27. package/dist/template/.opencode/command/research.md +3 -5
  28. package/dist/template/.opencode/command/resume.md +4 -2
  29. package/dist/template/.opencode/command/revert-feature.md +7 -7
  30. package/dist/template/.opencode/command/review-codebase.md +1 -1
  31. package/dist/template/.opencode/command/skill-create.md +4 -4
  32. package/dist/template/.opencode/command/skill-optimize.md +4 -4
  33. package/dist/template/.opencode/command/status.md +4 -4
  34. package/dist/template/.opencode/command/ui-review.md +2 -2
  35. package/dist/template/.opencode/dcp.jsonc +20 -2
  36. package/dist/template/.opencode/opencode.json +496 -491
  37. package/dist/template/.opencode/package.json +20 -20
  38. package/dist/template/.opencode/plugin/beads.ts +667 -0
  39. package/dist/template/.opencode/plugin/compaction.ts +80 -0
  40. package/dist/template/.opencode/skill/beads/SKILL.md +419 -0
  41. package/dist/template/.opencode/skill/beads/references/BOUNDARIES.md +218 -0
  42. package/dist/template/.opencode/skill/beads/references/DEPENDENCIES.md +130 -0
  43. package/dist/template/.opencode/skill/beads/references/RESUMABILITY.md +180 -0
  44. package/dist/template/.opencode/skill/beads/references/WORKFLOWS.md +222 -0
  45. package/dist/template/.opencode/skill/brainstorming/SKILL.md +2 -2
  46. package/dist/template/.opencode/skill/executing-plans/SKILL.md +1 -1
  47. package/dist/template/.opencode/skill/sharing-skills/SKILL.md +13 -4
  48. package/dist/template/.opencode/skill/subagent-driven-development/SKILL.md +1 -1
  49. package/dist/template/.opencode/skill/systematic-debugging/SKILL.md +2 -2
  50. package/dist/template/.opencode/skill/using-git-worktrees/SKILL.md +27 -18
  51. package/dist/template/.opencode/skill/{using-superpowers → using-skills}/SKILL.md +6 -3
  52. package/dist/template/.opencode/skill/writing-plans/SKILL.md +3 -3
  53. package/dist/template/.opencode/skill/writing-skills/SKILL.md +2 -2
  54. package/package.json +2 -1
  55. package/dist/template/.opencode/memory/handoffs/2025-12-27T103000Z.md +0 -76
  56. package/dist/template/.opencode/plugin/skill.ts +0 -275
  57. package/dist/template/.opencode/skill/systematic-debugging/CREATION-LOG.md +0 -119
  58. package/dist/template/.opencode/skill/systematic-debugging/test-academic.md +0 -14
  59. package/dist/template/.opencode/skill/systematic-debugging/test-pressure-1.md +0 -58
  60. package/dist/template/.opencode/skill/systematic-debugging/test-pressure-2.md +0 -68
  61. package/dist/template/.opencode/skill/systematic-debugging/test-pressure-3.md +0 -69
  62. package/dist/template/.opencode/skill/testing-skills-with-subagents/examples/CLAUDE_MD_TESTING.md +0 -189
package/dist/index.js CHANGED
@@ -750,7 +750,7 @@ var cac = (name = "") => new CAC(name);
750
750
  // package.json
751
751
  var package_default = {
752
752
  name: "opencodekit",
753
- version: "0.6.7",
753
+ version: "0.8.0",
754
754
  description: "CLI tool for bootstrapping and managing OpenCodeKit projects",
755
755
  type: "module",
756
756
  repository: {
@@ -785,6 +785,7 @@ var package_default = {
785
785
  dependencies: {
786
786
  "@clack/prompts": "^0.7.0",
787
787
  "@opencode-ai/plugin": "^1.0.141",
788
+ "beads-village": "^1.3.3",
788
789
  cac: "^6.7.14",
789
790
  "cli-table3": "^0.6.5",
790
791
  ora: "^9.0.0",
@@ -801,16 +802,16 @@ var package_default = {
801
802
  trustedDependencies: ["@beads/bd"]
802
803
  };
803
804
 
804
- // src/commands/init.ts
805
+ // src/commands/agent.ts
805
806
  import {
806
807
  existsSync,
808
+ lstatSync,
807
809
  mkdirSync,
808
810
  readFileSync,
809
- writeFileSync,
810
- readdirSync
811
+ readdirSync,
812
+ writeFileSync
811
813
  } from "node:fs";
812
- import { join, dirname, basename } from "node:path";
813
- import { fileURLToPath } from "node:url";
814
+ import { join } from "node:path";
814
815
 
815
816
  // node_modules/@clack/core/dist/index.mjs
816
817
  var import_sisteransi = __toESM(require_src(), 1);
@@ -1404,149 +1405,16 @@ function me() {
1404
1405
  return new RegExp(r2, "g");
1405
1406
  }
1406
1407
 
1407
- // src/commands/init.ts
1408
- var import_picocolors3 = __toESM(require_picocolors(), 1);
1409
- var EXCLUDED_DIRS = [
1410
- "node_modules",
1411
- ".git",
1412
- "dist",
1413
- ".DS_Store",
1414
- "coverage",
1415
- ".next",
1416
- ".turbo"
1417
- ];
1418
- var EXCLUDED_FILES = [
1419
- "bun.lock",
1420
- "package-lock.json",
1421
- "yarn.lock",
1422
- "pnpm-lock.yaml"
1423
- ];
1424
- function detectMode(targetDir) {
1425
- const opencodeDir = join(targetDir, ".opencode");
1426
- if (existsSync(opencodeDir)) {
1427
- return "already-initialized";
1428
- }
1429
- if (existsSync(targetDir)) {
1430
- const entries = readdirSync(targetDir);
1431
- const hasCode = entries.some((e2) => !e2.startsWith(".") && !EXCLUDED_DIRS.includes(e2) && e2 !== "node_modules");
1432
- if (hasCode) {
1433
- return "add-config";
1434
- }
1435
- }
1436
- return "scaffold";
1437
- }
1438
- function getTemplateRoot() {
1439
- const __filename2 = fileURLToPath(import.meta.url);
1440
- const __dirname2 = dirname(__filename2);
1441
- const possiblePaths = [
1442
- join(__dirname2, "template"),
1443
- join(__dirname2, "..", "..", ".opencode")
1444
- ];
1445
- for (const path of possiblePaths) {
1446
- const opencodeDir = join(path, ".opencode");
1447
- if (existsSync(opencodeDir)) {
1448
- return path;
1449
- }
1450
- }
1451
- return null;
1452
- }
1453
- async function copyDir(src, dest) {
1454
- const { mkdir, readdir } = await import("node:fs/promises");
1455
- await mkdir(dest, { recursive: true });
1456
- for (const entry of await readdir(src, { withFileTypes: true })) {
1457
- if (EXCLUDED_DIRS.includes(entry.name))
1458
- continue;
1459
- if (!entry.isDirectory() && EXCLUDED_FILES.includes(entry.name))
1460
- continue;
1461
- const srcPath = join(src, entry.name);
1462
- const destPath = join(dest, entry.name);
1463
- if (entry.isSymbolicLink()) {
1464
- continue;
1465
- } else if (entry.isDirectory()) {
1466
- await copyDir(srcPath, destPath);
1467
- } else {
1468
- const content = readFileSync(srcPath, "utf-8");
1469
- writeFileSync(destPath, content);
1470
- }
1471
- }
1472
- }
1473
- async function copyOpenCodeOnly(templateRoot, targetDir) {
1474
- const opencodeSrc = join(templateRoot, ".opencode");
1475
- const opencodeDest = join(targetDir, ".opencode");
1476
- if (!existsSync(opencodeSrc)) {
1477
- return false;
1478
- }
1479
- await copyDir(opencodeSrc, opencodeDest);
1480
- return true;
1481
- }
1482
- async function initCommand(options = {}) {
1483
- const targetDir = process.cwd();
1484
- const mode = detectMode(targetDir);
1485
- oe(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" OpenCodeKit ")));
1486
- if (mode === "already-initialized" && !options.force) {
1487
- f2.warn("Already initialized (.opencode/ exists)");
1488
- f2.info(`Use ${import_picocolors3.default.cyan("--force")} to reinitialize`);
1489
- $e("Nothing to do");
1490
- return;
1491
- }
1492
- const templateRoot = getTemplateRoot();
1493
- if (!templateRoot) {
1494
- f2.error("Template not found. Please reinstall opencodekit.");
1495
- $e(import_picocolors3.default.red("Failed"));
1496
- process.exit(1);
1497
- }
1498
- let projectName = basename(targetDir);
1499
- if (mode === "scaffold") {
1500
- const name = await te({
1501
- message: "Project name",
1502
- placeholder: projectName,
1503
- defaultValue: projectName
1504
- });
1505
- if (lD(name)) {
1506
- ue("Cancelled");
1507
- process.exit(0);
1508
- }
1509
- projectName = name || projectName;
1510
- }
1511
- const s = de();
1512
- if (mode === "scaffold") {
1513
- s.start("Scaffolding project");
1514
- mkdirSync(targetDir, { recursive: true });
1515
- } else if (mode === "add-config") {
1516
- s.start("Adding OpenCodeKit");
1517
- } else {
1518
- s.start("Reinitializing");
1519
- }
1520
- const success = await copyOpenCodeOnly(templateRoot, targetDir);
1521
- if (!success) {
1522
- s.stop("Failed");
1523
- $e(import_picocolors3.default.red("Template copy failed"));
1524
- process.exit(1);
1525
- }
1526
- s.stop("Done");
1527
- le(`cd .opencode && bun install`, "Next steps");
1528
- $e(import_picocolors3.default.green("Ready to code!"));
1529
- }
1530
-
1531
1408
  // src/commands/agent.ts
1532
- import {
1533
- existsSync as existsSync2,
1534
- lstatSync,
1535
- mkdirSync as mkdirSync2,
1536
- readFileSync as readFileSync2,
1537
- readdirSync as readdirSync2,
1538
- writeFileSync as writeFileSync2
1539
- } from "node:fs";
1540
- import { join as join2 } from "node:path";
1541
- var import_picocolors5 = __toESM(require_picocolors(), 1);
1409
+ var import_picocolors4 = __toESM(require_picocolors(), 1);
1542
1410
 
1543
1411
  // src/utils/errors.ts
1544
- var import_picocolors4 = __toESM(require_picocolors(), 1);
1412
+ var import_picocolors3 = __toESM(require_picocolors(), 1);
1545
1413
  function showError(message, fix) {
1546
1414
  console.log();
1547
- f2.error(import_picocolors4.default.red("✗") + " " + message);
1415
+ f2.error(import_picocolors3.default.red("✗") + " " + message);
1548
1416
  if (fix) {
1549
- f2.info(" " + import_picocolors4.default.dim("→ " + fix));
1417
+ f2.info(" " + import_picocolors3.default.dim("→ " + fix));
1550
1418
  }
1551
1419
  console.log();
1552
1420
  }
@@ -1561,18 +1429,18 @@ function alreadyExists(resource, name) {
1561
1429
  showError(`${resource} "${name}" already exists`);
1562
1430
  }
1563
1431
  function unknownAction(action, available) {
1564
- showError(`Unknown action: ${action}`, `Available: ${available.map((a3) => import_picocolors4.default.cyan(a3)).join(", ")}`);
1432
+ showError(`Unknown action: ${action}`, `Available: ${available.map((a3) => import_picocolors3.default.cyan(a3)).join(", ")}`);
1565
1433
  }
1566
1434
  function showWarning(message, suggestion) {
1567
- f2.warn(import_picocolors4.default.yellow("!") + " " + message);
1435
+ f2.warn(import_picocolors3.default.yellow("!") + " " + message);
1568
1436
  if (suggestion) {
1569
- f2.info(" " + import_picocolors4.default.dim("→ " + suggestion));
1437
+ f2.info(" " + import_picocolors3.default.dim("→ " + suggestion));
1570
1438
  }
1571
1439
  }
1572
1440
  function showEmpty(resource, createCmd) {
1573
- f2.info(import_picocolors4.default.dim(`No ${resource} found`));
1441
+ f2.info(import_picocolors3.default.dim(`No ${resource} found`));
1574
1442
  if (createCmd) {
1575
- f2.info(import_picocolors4.default.dim(`→ Run: ${import_picocolors4.default.cyan(createCmd)}`));
1443
+ f2.info(import_picocolors3.default.dim(`→ Run: ${import_picocolors3.default.cyan(createCmd)}`));
1576
1444
  }
1577
1445
  }
1578
1446
 
@@ -1590,12 +1458,12 @@ Describe what this agent does.
1590
1458
  Provide specific instructions for this agent.
1591
1459
  `;
1592
1460
  async function agentCommand(action) {
1593
- const opencodePath = join2(process.cwd(), ".opencode");
1594
- if (!existsSync2(opencodePath)) {
1461
+ const opencodePath = join(process.cwd(), ".opencode");
1462
+ if (!existsSync(opencodePath)) {
1595
1463
  notInitialized();
1596
1464
  return;
1597
1465
  }
1598
- const agentPath = join2(opencodePath, "agent");
1466
+ const agentPath = join(opencodePath, "agent");
1599
1467
  switch (action) {
1600
1468
  case "list":
1601
1469
  await listAgents(agentPath);
@@ -1613,37 +1481,37 @@ async function agentCommand(action) {
1613
1481
  }
1614
1482
  }
1615
1483
  async function listAgents(agentPath) {
1616
- if (!existsSync2(agentPath)) {
1484
+ if (!existsSync(agentPath)) {
1617
1485
  showEmpty("agents", "ock agent add");
1618
1486
  return;
1619
1487
  }
1620
- const opencodePath = join2(process.cwd(), ".opencode");
1621
- const configPath = join2(opencodePath, "opencode.json");
1488
+ const opencodePath = join(process.cwd(), ".opencode");
1489
+ const configPath = join(opencodePath, "opencode.json");
1622
1490
  let agentConfigs = {};
1623
- if (existsSync2(configPath)) {
1491
+ if (existsSync(configPath)) {
1624
1492
  try {
1625
- const configContent = readFileSync2(configPath, "utf-8");
1493
+ const configContent = readFileSync(configPath, "utf-8");
1626
1494
  const config = JSON.parse(configContent);
1627
1495
  agentConfigs = config.agent || {};
1628
1496
  } catch {}
1629
1497
  }
1630
- const entries = readdirSync2(agentPath);
1498
+ const entries = readdirSync(agentPath);
1631
1499
  const agents = entries.filter((entry) => {
1632
- const entryPath = join2(agentPath, entry);
1500
+ const entryPath = join(agentPath, entry);
1633
1501
  return lstatSync(entryPath).isFile() && entry.endsWith(".md");
1634
1502
  }).map((file) => file.replace(".md", ""));
1635
1503
  if (agents.length === 0) {
1636
1504
  showEmpty("agents", "ock agent add");
1637
1505
  return;
1638
1506
  }
1639
- f2.info(import_picocolors5.default.bold("Agents"));
1507
+ f2.info(import_picocolors4.default.bold("Agents"));
1640
1508
  for (const agent of agents) {
1641
1509
  let model = agentConfigs[agent]?.model;
1642
1510
  const disabled = agentConfigs[agent]?.disable;
1643
1511
  if (!model) {
1644
- const agentFile = join2(agentPath, `${agent}.md`);
1512
+ const agentFile = join(agentPath, `${agent}.md`);
1645
1513
  try {
1646
- const content = readFileSync2(agentFile, "utf-8");
1514
+ const content = readFileSync(agentFile, "utf-8");
1647
1515
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
1648
1516
  if (frontmatterMatch) {
1649
1517
  const modelMatch = frontmatterMatch[1].match(/^model:\s*(.+)$/m);
@@ -1653,13 +1521,13 @@ async function listAgents(agentPath) {
1653
1521
  }
1654
1522
  } catch {}
1655
1523
  }
1656
- const modelDisplay = model ? import_picocolors5.default.dim(` → ${model}`) : import_picocolors5.default.dim(" → (default)");
1657
- const disabledDisplay = disabled ? import_picocolors5.default.red(" [disabled]") : "";
1658
- console.log(` ${import_picocolors5.default.cyan("•")} ${agent}${modelDisplay}${disabledDisplay}`);
1524
+ const modelDisplay = model ? import_picocolors4.default.dim(` → ${model}`) : import_picocolors4.default.dim(" → (default)");
1525
+ const disabledDisplay = disabled ? import_picocolors4.default.red(" [disabled]") : "";
1526
+ console.log(` ${import_picocolors4.default.cyan("•")} ${agent}${modelDisplay}${disabledDisplay}`);
1659
1527
  }
1660
1528
  }
1661
1529
  async function addAgent(agentPath) {
1662
- oe(import_picocolors5.default.bgMagenta(import_picocolors5.default.black(" Add Agent ")));
1530
+ oe(import_picocolors4.default.bgMagenta(import_picocolors4.default.black(" Add Agent ")));
1663
1531
  const name = await te({
1664
1532
  message: "Agent name",
1665
1533
  placeholder: "e.g. reviewer, planner, researcher",
@@ -1686,18 +1554,18 @@ async function addAgent(agentPath) {
1686
1554
  ue("Cancelled");
1687
1555
  return;
1688
1556
  }
1689
- if (!existsSync2(agentPath)) {
1690
- mkdirSync2(agentPath, { recursive: true });
1557
+ if (!existsSync(agentPath)) {
1558
+ mkdirSync(agentPath, { recursive: true });
1691
1559
  }
1692
- const agentFile = join2(agentPath, `${name}.md`);
1693
- if (existsSync2(agentFile)) {
1560
+ const agentFile = join(agentPath, `${name}.md`);
1561
+ if (existsSync(agentFile)) {
1694
1562
  alreadyExists("Agent", String(name));
1695
- $e(import_picocolors5.default.red("Failed"));
1563
+ $e(import_picocolors4.default.red("Failed"));
1696
1564
  return;
1697
1565
  }
1698
1566
  const content = AGENT_TEMPLATE.replace("{{NAME}}", String(name).charAt(0).toUpperCase() + String(name).slice(1));
1699
- writeFileSync2(agentFile, content);
1700
- f2.success(`Created ${import_picocolors5.default.cyan(`.opencode/agent/${name}.md`)}`);
1567
+ writeFileSync(agentFile, content);
1568
+ f2.success(`Created ${import_picocolors4.default.cyan(`.opencode/agent/${name}.md`)}`);
1701
1569
  if (agentType === "primary") {
1702
1570
  le(`Add to opencode.json:
1703
1571
 
@@ -1708,17 +1576,17 @@ async function addAgent(agentPath) {
1708
1576
  }
1709
1577
  }`, "Next step");
1710
1578
  }
1711
- $e(import_picocolors5.default.green("Done! Edit the file to customize."));
1579
+ $e(import_picocolors4.default.green("Done! Edit the file to customize."));
1712
1580
  }
1713
1581
  async function viewAgent(agentPath, agentName) {
1714
1582
  if (!agentName) {
1715
- if (!existsSync2(agentPath)) {
1583
+ if (!existsSync(agentPath)) {
1716
1584
  showEmpty("agents", "ock agent add");
1717
1585
  return;
1718
1586
  }
1719
- const entries = readdirSync2(agentPath);
1587
+ const entries = readdirSync(agentPath);
1720
1588
  const agents = entries.filter((entry) => {
1721
- const entryPath = join2(agentPath, entry);
1589
+ const entryPath = join(agentPath, entry);
1722
1590
  return lstatSync(entryPath).isFile() && entry.endsWith(".md");
1723
1591
  }).map((file) => file.replace(".md", ""));
1724
1592
  if (agents.length === 0) {
@@ -1734,181 +1602,214 @@ async function viewAgent(agentPath, agentName) {
1734
1602
  }
1735
1603
  agentName = selected;
1736
1604
  }
1737
- const agentFile = join2(agentPath, `${agentName}.md`);
1738
- if (!existsSync2(agentFile)) {
1605
+ const agentFile = join(agentPath, `${agentName}.md`);
1606
+ if (!existsSync(agentFile)) {
1739
1607
  notFound("Agent", agentName);
1740
1608
  return;
1741
1609
  }
1742
- const content = readFileSync2(agentFile, "utf-8");
1610
+ const content = readFileSync(agentFile, "utf-8");
1743
1611
  console.log();
1744
- console.log(import_picocolors5.default.dim("─".repeat(40)));
1612
+ console.log(import_picocolors4.default.dim("─".repeat(40)));
1745
1613
  console.log(content);
1746
- console.log(import_picocolors5.default.dim("─".repeat(40)));
1614
+ console.log(import_picocolors4.default.dim("─".repeat(40)));
1747
1615
  }
1748
1616
 
1749
- // src/commands/skill.ts
1750
- import { join as join3 } from "node:path";
1751
- import { existsSync as existsSync3, readdirSync as readdirSync3, readFileSync as readFileSync3, lstatSync as lstatSync2 } from "node:fs";
1752
- var import_picocolors6 = __toESM(require_picocolors(), 1);
1753
- async function skillCommand(action) {
1754
- const opencodePath = join3(process.cwd(), ".opencode");
1755
- if (!existsSync3(opencodePath)) {
1756
- notInitialized();
1757
- return;
1758
- }
1759
- switch (action) {
1760
- case "list":
1761
- await listSkills(opencodePath);
1762
- break;
1763
- case "add":
1764
- await addSkill(opencodePath);
1765
- break;
1766
- case "view": {
1767
- const skillName = process.argv[4];
1768
- await viewSkill(opencodePath, skillName);
1769
- break;
1770
- }
1771
- default:
1772
- unknownAction(action, ["list", "add", "view"]);
1773
- }
1774
- }
1775
- function collectSkills(basePath) {
1776
- const skills = [];
1777
- if (!existsSync3(basePath))
1778
- return skills;
1779
- const tiers = ["core", "stack", "specialized"];
1780
- let hasTiers = false;
1781
- for (const tier of tiers) {
1782
- const tierPath = join3(basePath, tier);
1783
- if (existsSync3(tierPath)) {
1784
- hasTiers = true;
1785
- const entries = readdirSync3(tierPath);
1786
- for (const entry of entries) {
1787
- const entryPath = join3(tierPath, entry);
1788
- if (lstatSync2(entryPath).isDirectory()) {
1789
- skills.push({ name: entry, path: entryPath, tier });
1790
- }
1791
- }
1792
- }
1793
- }
1794
- if (!hasTiers) {
1795
- const entries = readdirSync3(basePath);
1796
- for (const entry of entries) {
1797
- const entryPath = join3(basePath, entry);
1798
- if (lstatSync2(entryPath).isDirectory()) {
1799
- skills.push({ name: entry, path: entryPath });
1800
- }
1801
- }
1802
- }
1803
- return skills;
1804
- }
1805
- async function listSkills(opencodePath) {
1806
- const locations = [
1807
- { path: join3(opencodePath, "superpowers", "skills"), name: "Superpowers" },
1808
- { path: join3(opencodePath, "skills"), name: "Project" }
1809
- ];
1810
- let totalSkills = 0;
1811
- for (const loc of locations) {
1812
- const skills = collectSkills(loc.path);
1813
- if (skills.length > 0) {
1814
- f2.info(import_picocolors6.default.bold(loc.name));
1815
- for (const skill of skills) {
1816
- const label = skill.tier ? `${skill.tier}/${skill.name}` : skill.name;
1817
- console.log(` ${import_picocolors6.default.cyan("•")} ${label}`);
1818
- }
1819
- totalSkills += skills.length;
1820
- }
1821
- }
1822
- if (totalSkills === 0) {
1823
- showEmpty("skills", "ock skill add");
1824
- }
1617
+ // src/commands/completion.ts
1618
+ var import_picocolors5 = __toESM(require_picocolors(), 1);
1619
+ var BASH_COMPLETION = `# ock bash completion
1620
+ _ock_completion() {
1621
+ local cur prev commands
1622
+ COMPREPLY=()
1623
+ cur="\${COMP_WORDS[COMP_CWORD]}"
1624
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
1625
+
1626
+ commands="init config upgrade agent skill doctor status help"
1627
+
1628
+ case "\${prev}" in
1629
+ ock)
1630
+ COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
1631
+ return 0
1632
+ ;;
1633
+ agent)
1634
+ COMPREPLY=( $(compgen -W "list add view" -- "\${cur}") )
1635
+ return 0
1636
+ ;;
1637
+ skill)
1638
+ COMPREPLY=( $(compgen -W "list add view" -- "\${cur}") )
1639
+ return 0
1640
+ ;;
1641
+ config)
1642
+ COMPREPLY=( $(compgen -W "model mcp permission keybinds show" -- "\${cur}") )
1643
+ return 0
1644
+ ;;
1645
+ init)
1646
+ COMPREPLY=( $(compgen -W "--force" -- "\${cur}") )
1647
+ return 0
1648
+ ;;
1649
+ upgrade)
1650
+ COMPREPLY=( $(compgen -W "--force --check" -- "\${cur}") )
1651
+ return 0
1652
+ ;;
1653
+ *)
1654
+ ;;
1655
+ esac
1825
1656
  }
1826
- async function addSkill(opencodePath) {
1827
- oe(import_picocolors6.default.bgYellow(import_picocolors6.default.black(" Add Skill ")));
1828
- const superpowersPath = join3(opencodePath, "superpowers", "skills");
1829
- const skills = collectSkills(superpowersPath);
1830
- if (skills.length === 0) {
1831
- showEmpty("skills available to install");
1832
- $e("Done");
1833
- return;
1834
- }
1835
- const selected = await ie({
1836
- message: "Select skill to install",
1837
- options: skills.map((s) => ({
1838
- value: s.name,
1839
- label: s.tier ? `${s.name} (${s.tier})` : s.name
1840
- }))
1841
- });
1842
- if (lD(selected)) {
1843
- ue("Cancelled");
1844
- return;
1845
- }
1846
- const skill = skills.find((s) => s.name === selected);
1847
- if (!skill)
1848
- return;
1849
- const skillMdPath = join3(skill.path, "SKILL.md");
1850
- if (!existsSync3(skillMdPath)) {
1851
- notFound("SKILL.md in", skill.name);
1852
- $e(import_picocolors6.default.red("Failed"));
1853
- return;
1854
- }
1855
- const content = readFileSync3(skillMdPath, "utf-8");
1856
- const lines = content.split(`
1857
- `).slice(0, 10);
1858
- console.log();
1859
- console.log(import_picocolors6.default.dim("─".repeat(40)));
1860
- console.log(lines.join(`
1861
- `));
1862
- if (content.split(`
1863
- `).length > 10) {
1864
- console.log(import_picocolors6.default.dim("..."));
1865
- }
1866
- console.log(import_picocolors6.default.dim("─".repeat(40)));
1867
- console.log();
1868
- f2.success(`Skill "${skill.name}" is available at:`);
1869
- f2.info(import_picocolors6.default.cyan(skill.path));
1870
- le(`Skills in superpowers/ are auto-loaded.
1871
- Use ${import_picocolors6.default.cyan("find_skills")} + ${import_picocolors6.default.cyan("use_skill")} in opencode.`, "Usage");
1872
- $e(import_picocolors6.default.green("Done!"));
1657
+ complete -F _ock_completion ock
1658
+ `;
1659
+ var ZSH_COMPLETION = `#compdef ock
1660
+
1661
+ _ock() {
1662
+ local -a commands
1663
+ local -a agent_actions
1664
+ local -a skill_actions
1665
+ local -a config_actions
1666
+
1667
+ commands=(
1668
+ 'init:Initialize OpenCodeKit in current directory'
1669
+ 'config:Edit opencode.json'
1670
+ 'upgrade:Update templates to latest version'
1671
+ 'agent:Manage agents'
1672
+ 'skill:Manage skills'
1673
+ 'doctor:Check project health'
1674
+ 'status:Show project overview'
1675
+ 'help:Show help'
1676
+ )
1677
+
1678
+ agent_actions=(
1679
+ 'list:List all agents'
1680
+ 'add:Create a new agent'
1681
+ 'view:View agent details'
1682
+ )
1683
+
1684
+ skill_actions=(
1685
+ 'list:List all skills'
1686
+ 'add:Install a skill'
1687
+ 'view:View skill details'
1688
+ )
1689
+
1690
+ config_actions=(
1691
+ 'model:Change default model'
1692
+ 'mcp:Manage MCP servers'
1693
+ 'permission:Edit permissions'
1694
+ 'keybinds:Edit keyboard shortcuts'
1695
+ 'show:View current config'
1696
+ )
1697
+
1698
+ case "$words[2]" in
1699
+ agent)
1700
+ _describe -t actions 'agent action' agent_actions
1701
+ ;;
1702
+ skill)
1703
+ _describe -t actions 'skill action' skill_actions
1704
+ ;;
1705
+ config)
1706
+ _describe -t actions 'config action' config_actions
1707
+ ;;
1708
+ init)
1709
+ _arguments '--force[Reinitialize even if already exists]'
1710
+ ;;
1711
+ upgrade)
1712
+ _arguments \\
1713
+ '--force[Force upgrade even if up to date]' \\
1714
+ '--check[Check for updates only]'
1715
+ ;;
1716
+ *)
1717
+ _describe -t commands 'command' commands
1718
+ ;;
1719
+ esac
1873
1720
  }
1874
- async function viewSkill(opencodePath, skillName) {
1875
- const superpowersPath = join3(opencodePath, "superpowers", "skills");
1876
- const skills = collectSkills(superpowersPath);
1877
- if (skills.length === 0) {
1878
- showEmpty("skills");
1879
- return;
1880
- }
1881
- if (!skillName) {
1721
+
1722
+ _ock "$@"
1723
+ `;
1724
+ var FISH_COMPLETION = `# ock fish completion
1725
+ complete -c ock -n "__fish_use_subcommand" -a "init" -d "Initialize OpenCodeKit"
1726
+ complete -c ock -n "__fish_use_subcommand" -a "config" -d "Edit opencode.json"
1727
+ complete -c ock -n "__fish_use_subcommand" -a "upgrade" -d "Update templates"
1728
+ complete -c ock -n "__fish_use_subcommand" -a "agent" -d "Manage agents"
1729
+ complete -c ock -n "__fish_use_subcommand" -a "skill" -d "Manage skills"
1730
+ complete -c ock -n "__fish_use_subcommand" -a "doctor" -d "Check project health"
1731
+ complete -c ock -n "__fish_use_subcommand" -a "status" -d "Show project overview"
1732
+
1733
+ # agent subcommands
1734
+ complete -c ock -n "__fish_seen_subcommand_from agent" -a "list" -d "List agents"
1735
+ complete -c ock -n "__fish_seen_subcommand_from agent" -a "add" -d "Add agent"
1736
+ complete -c ock -n "__fish_seen_subcommand_from agent" -a "view" -d "View agent"
1737
+
1738
+ # skill subcommands
1739
+ complete -c ock -n "__fish_seen_subcommand_from skill" -a "list" -d "List skills"
1740
+ complete -c ock -n "__fish_seen_subcommand_from skill" -a "add" -d "Add skill"
1741
+ complete -c ock -n "__fish_seen_subcommand_from skill" -a "view" -d "View skill"
1742
+
1743
+ # config subcommands
1744
+ complete -c ock -n "__fish_seen_subcommand_from config" -a "model" -d "Change model"
1745
+ complete -c ock -n "__fish_seen_subcommand_from config" -a "mcp" -d "Manage MCP"
1746
+ complete -c ock -n "__fish_seen_subcommand_from config" -a "permission" -d "Edit permissions"
1747
+ complete -c ock -n "__fish_seen_subcommand_from config" -a "keybinds" -d "Edit keybinds"
1748
+ complete -c ock -n "__fish_seen_subcommand_from config" -a "show" -d "View config"
1749
+
1750
+ # init options
1751
+ complete -c ock -n "__fish_seen_subcommand_from init" -l force -d "Reinitialize"
1752
+
1753
+ # upgrade options
1754
+ complete -c ock -n "__fish_seen_subcommand_from upgrade" -l force -d "Force upgrade"
1755
+ complete -c ock -n "__fish_seen_subcommand_from upgrade" -l check -d "Check only"
1756
+ `;
1757
+ async function completionCommand(shell) {
1758
+ if (!shell) {
1759
+ oe(import_picocolors5.default.bgCyan(import_picocolors5.default.black(" Shell Completion ")));
1882
1760
  const selected = await ie({
1883
- message: "Select skill to view",
1884
- options: skills.map((s) => ({ value: s.name, label: s.name }))
1761
+ message: "Select your shell",
1762
+ options: [
1763
+ { value: "bash", label: "Bash" },
1764
+ { value: "zsh", label: "Zsh" },
1765
+ { value: "fish", label: "Fish" }
1766
+ ]
1885
1767
  });
1886
1768
  if (lD(selected)) {
1769
+ ue("Cancelled");
1887
1770
  return;
1888
1771
  }
1889
- skillName = selected;
1890
- }
1891
- const skill = skills.find((s) => s.name === skillName);
1892
- if (!skill) {
1893
- notFound("Skill", skillName);
1894
- return;
1772
+ shell = selected;
1895
1773
  }
1896
- const skillMdPath = join3(skill.path, "SKILL.md");
1897
- if (!existsSync3(skillMdPath)) {
1898
- notFound("SKILL.md");
1899
- return;
1774
+ let script;
1775
+ let setupInstructions;
1776
+ switch (shell) {
1777
+ case "bash":
1778
+ script = BASH_COMPLETION;
1779
+ setupInstructions = `Add to ~/.bashrc:
1780
+ source <(ock completion bash)
1781
+
1782
+ Or save to file:
1783
+ ock completion bash > ~/.local/share/bash-completion/completions/ock`;
1784
+ break;
1785
+ case "zsh":
1786
+ script = ZSH_COMPLETION;
1787
+ setupInstructions = `Add to ~/.zshrc (before compinit):
1788
+ source <(ock completion zsh)
1789
+
1790
+ Or save to fpath:
1791
+ ock completion zsh > ~/.zsh/completions/_ock
1792
+ # Then add to ~/.zshrc: fpath=(~/.zsh/completions $fpath)`;
1793
+ break;
1794
+ case "fish":
1795
+ script = FISH_COMPLETION;
1796
+ setupInstructions = `Save to fish completions:
1797
+ ock completion fish > ~/.config/fish/completions/ock.fish`;
1798
+ break;
1799
+ default:
1800
+ f2.error(`Unknown shell: ${shell}`);
1801
+ return;
1900
1802
  }
1901
- const content = readFileSync3(skillMdPath, "utf-8");
1902
- console.log();
1903
- console.log(import_picocolors6.default.dim("".repeat(40)));
1904
- console.log(content);
1905
- console.log(import_picocolors6.default.dim("─".repeat(40)));
1803
+ console.log(script);
1804
+ console.error();
1805
+ console.error(import_picocolors5.default.bold("Setup:"));
1806
+ console.error(import_picocolors5.default.dim(setupInstructions));
1906
1807
  }
1907
1808
 
1908
1809
  // src/commands/config.ts
1909
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
1910
- import { join as join4 } from "node:path";
1911
- var import_picocolors7 = __toESM(require_picocolors(), 1);
1810
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
1811
+ import { join as join2 } from "node:path";
1812
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
1912
1813
  var DEFAULT_SERVER_PORT = 4096;
1913
1814
  var MODELS_DEV_URL = "https://models.dev/api.json";
1914
1815
  async function fetchFromServer(endpoint) {
@@ -1938,13 +1839,13 @@ async function getAgentsFromServer() {
1938
1839
  return fetchFromServer("/agent");
1939
1840
  }
1940
1841
  async function configCommand(action) {
1941
- const opencodePath = join4(process.cwd(), ".opencode");
1942
- const configPath = join4(opencodePath, "opencode.json");
1943
- if (!existsSync4(opencodePath)) {
1842
+ const opencodePath = join2(process.cwd(), ".opencode");
1843
+ const configPath = join2(opencodePath, "opencode.json");
1844
+ if (!existsSync2(opencodePath)) {
1944
1845
  notInitialized();
1945
1846
  return;
1946
1847
  }
1947
- if (!existsSync4(configPath)) {
1848
+ if (!existsSync2(configPath)) {
1948
1849
  showError("opencode.json not found", "ock init --force");
1949
1850
  return;
1950
1851
  }
@@ -1999,66 +1900,66 @@ async function configCommand(action) {
1999
1900
  }
2000
1901
  }
2001
1902
  function loadConfig(configPath) {
2002
- const content = readFileSync4(configPath, "utf-8");
1903
+ const content = readFileSync2(configPath, "utf-8");
2003
1904
  const jsonWithoutComments = content.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
2004
1905
  return JSON.parse(jsonWithoutComments);
2005
1906
  }
2006
1907
  function saveConfig(configPath, config) {
2007
- writeFileSync3(configPath, JSON.stringify(config, null, 2) + `
1908
+ writeFileSync2(configPath, JSON.stringify(config, null, 2) + `
2008
1909
  `);
2009
1910
  }
2010
1911
  function showConfig(configPath) {
2011
1912
  const config = loadConfig(configPath);
2012
1913
  console.log();
2013
- console.log(import_picocolors7.default.bold(" Model"));
2014
- console.log(` ${import_picocolors7.default.cyan(config.model || "not set")}`);
1914
+ console.log(import_picocolors6.default.bold(" Model"));
1915
+ console.log(` ${import_picocolors6.default.cyan(config.model || "not set")}`);
2015
1916
  if (config.small_model) {
2016
- console.log(import_picocolors7.default.bold(" Small Model"));
2017
- console.log(` ${import_picocolors7.default.cyan(config.small_model)}`);
1917
+ console.log(import_picocolors6.default.bold(" Small Model"));
1918
+ console.log(` ${import_picocolors6.default.cyan(config.small_model)}`);
2018
1919
  }
2019
- console.log(import_picocolors7.default.bold(" Theme"));
2020
- console.log(` ${import_picocolors7.default.cyan(config.theme || "system")}`);
2021
- console.log(import_picocolors7.default.bold(" Share"));
2022
- console.log(` ${import_picocolors7.default.cyan(config.share || "manual")}`);
1920
+ console.log(import_picocolors6.default.bold(" Theme"));
1921
+ console.log(` ${import_picocolors6.default.cyan(config.theme || "system")}`);
1922
+ console.log(import_picocolors6.default.bold(" Share"));
1923
+ console.log(` ${import_picocolors6.default.cyan(config.share || "manual")}`);
2023
1924
  const autoupdateDisplay = config.autoupdate === "notify" ? "notify" : config.autoupdate === false ? "disabled" : "enabled";
2024
- console.log(import_picocolors7.default.bold(" Autoupdate"));
2025
- console.log(` ${import_picocolors7.default.cyan(autoupdateDisplay)}`);
1925
+ console.log(import_picocolors6.default.bold(" Autoupdate"));
1926
+ console.log(` ${import_picocolors6.default.cyan(autoupdateDisplay)}`);
2026
1927
  if (config.tui && Object.keys(config.tui).length > 0) {
2027
- console.log(import_picocolors7.default.bold(" TUI"));
1928
+ console.log(import_picocolors6.default.bold(" TUI"));
2028
1929
  if (config.tui.scroll_speed !== undefined) {
2029
- console.log(` scroll_speed: ${import_picocolors7.default.cyan(String(config.tui.scroll_speed))}`);
1930
+ console.log(` scroll_speed: ${import_picocolors6.default.cyan(String(config.tui.scroll_speed))}`);
2030
1931
  }
2031
1932
  if (config.tui.scroll_acceleration !== undefined) {
2032
- console.log(` scroll_acceleration: ${import_picocolors7.default.cyan(String(config.tui.scroll_acceleration))}`);
1933
+ console.log(` scroll_acceleration: ${import_picocolors6.default.cyan(String(config.tui.scroll_acceleration))}`);
2033
1934
  }
2034
1935
  if (config.tui.diff_style) {
2035
- console.log(` diff_style: ${import_picocolors7.default.cyan(config.tui.diff_style)}`);
1936
+ console.log(` diff_style: ${import_picocolors6.default.cyan(config.tui.diff_style)}`);
2036
1937
  }
2037
1938
  }
2038
1939
  if (config.mcp && Object.keys(config.mcp).length > 0) {
2039
- console.log(import_picocolors7.default.bold(" MCP Servers"));
1940
+ console.log(import_picocolors6.default.bold(" MCP Servers"));
2040
1941
  for (const [name, server] of Object.entries(config.mcp)) {
2041
- const status = server.enabled === false ? import_picocolors7.default.dim("off") : import_picocolors7.default.green("on");
1942
+ const status = server.enabled === false ? import_picocolors6.default.dim("off") : import_picocolors6.default.green("on");
2042
1943
  console.log(` ${status} ${name}`);
2043
1944
  }
2044
1945
  }
2045
1946
  if (config.tools && Object.keys(config.tools).length > 0) {
2046
- console.log(import_picocolors7.default.bold(" Tools"));
1947
+ console.log(import_picocolors6.default.bold(" Tools"));
2047
1948
  for (const [tool, enabled] of Object.entries(config.tools)) {
2048
- const status = enabled === false ? import_picocolors7.default.red("off") : import_picocolors7.default.green("on");
1949
+ const status = enabled === false ? import_picocolors6.default.red("off") : import_picocolors6.default.green("on");
2049
1950
  console.log(` ${status} ${tool}`);
2050
1951
  }
2051
1952
  }
2052
1953
  if (config.keybinds && Object.keys(config.keybinds).length > 0) {
2053
- console.log(import_picocolors7.default.bold(" Keybinds"));
1954
+ console.log(import_picocolors6.default.bold(" Keybinds"));
2054
1955
  for (const [action, key] of Object.entries(config.keybinds)) {
2055
- console.log(` ${import_picocolors7.default.dim(action)}: ${import_picocolors7.default.cyan(key)}`);
1956
+ console.log(` ${import_picocolors6.default.dim(action)}: ${import_picocolors6.default.cyan(key)}`);
2056
1957
  }
2057
1958
  }
2058
1959
  console.log();
2059
1960
  }
2060
1961
  async function configMenu(configPath) {
2061
- oe(import_picocolors7.default.bgCyan(import_picocolors7.default.black(" Configuration ")));
1962
+ oe(import_picocolors6.default.bgCyan(import_picocolors6.default.black(" Configuration ")));
2062
1963
  while (true) {
2063
1964
  const action = await ie({
2064
1965
  message: "What do you want to configure?",
@@ -2133,8 +2034,8 @@ async function editModel(configPath, modelType = "model") {
2133
2034
  const config = loadConfig(configPath);
2134
2035
  const currentModel = config[modelType] || "not set";
2135
2036
  const modelLabel = modelType === "small_model" ? "Small Model" : "Model";
2136
- f2.info(`Current ${modelLabel}: ${import_picocolors7.default.cyan(currentModel)}`);
2137
- f2.info(import_picocolors7.default.dim("Fetching models from models.dev..."));
2037
+ f2.info(`Current ${modelLabel}: ${import_picocolors6.default.cyan(currentModel)}`);
2038
+ f2.info(import_picocolors6.default.dim("Fetching models from models.dev..."));
2138
2039
  const modelsDevData = await fetchFromModelsDev();
2139
2040
  if (!modelsDevData || Object.keys(modelsDevData).length === 0) {
2140
2041
  f2.warn("models.dev unavailable - please enter manually");
@@ -2155,7 +2056,7 @@ async function editModel(configPath, modelType = "model") {
2155
2056
  }
2156
2057
  config[modelType] = newModel;
2157
2058
  saveConfig(configPath, config);
2158
- f2.success(`${modelLabel} set to ${import_picocolors7.default.cyan(newModel)}`);
2059
+ f2.success(`${modelLabel} set to ${import_picocolors6.default.cyan(newModel)}`);
2159
2060
  return;
2160
2061
  }
2161
2062
  f2.success("Fetched models from models.dev");
@@ -2218,7 +2119,7 @@ async function editModel(configPath, modelType = "model") {
2218
2119
  }
2219
2120
  config[modelType] = customModel;
2220
2121
  saveConfig(configPath, config);
2221
- f2.success(`${modelLabel} set to ${import_picocolors7.default.cyan(customModel)}`);
2122
+ f2.success(`${modelLabel} set to ${import_picocolors6.default.cyan(customModel)}`);
2222
2123
  return;
2223
2124
  }
2224
2125
  if (providerId === "__search__") {
@@ -2283,7 +2184,7 @@ async function editModel(configPath, modelType = "model") {
2283
2184
  const fullModelId = `${providerId}/${selectedModel}`;
2284
2185
  config[modelType] = fullModelId;
2285
2186
  saveConfig(configPath, config);
2286
- f2.success(`${modelLabel} set to ${import_picocolors7.default.cyan(fullModelId)}`);
2187
+ f2.success(`${modelLabel} set to ${import_picocolors6.default.cyan(fullModelId)}`);
2287
2188
  }
2288
2189
  function getModelsForProvider(provider) {
2289
2190
  const options = [];
@@ -2331,7 +2232,7 @@ async function editAgentModel(configPath) {
2331
2232
  let agentNames = [];
2332
2233
  const agentInfoMap = new Map;
2333
2234
  if (serverAgents && serverAgents.length > 0) {
2334
- f2.info(import_picocolors7.default.dim("Fetched agents from OpenCode server"));
2235
+ f2.info(import_picocolors6.default.dim("Fetched agents from OpenCode server"));
2335
2236
  for (const agent of serverAgents) {
2336
2237
  agentNames.push(agent.name);
2337
2238
  agentInfoMap.set(agent.name, {
@@ -2355,14 +2256,14 @@ async function editAgentModel(configPath) {
2355
2256
  return;
2356
2257
  }
2357
2258
  console.log();
2358
- console.log(import_picocolors7.default.bold(" Agent Models"));
2259
+ console.log(import_picocolors6.default.bold(" Agent Models"));
2359
2260
  for (const name of agentNames) {
2360
2261
  const info = agentInfoMap.get(name);
2361
- const model = info?.model || import_picocolors7.default.dim("(default)");
2362
- const builtIn = info?.builtIn ? import_picocolors7.default.dim(" [built-in]") : "";
2262
+ const model = info?.model || import_picocolors6.default.dim("(default)");
2263
+ const builtIn = info?.builtIn ? import_picocolors6.default.dim(" [built-in]") : "";
2363
2264
  const localConfig = config.agent[name];
2364
- const disabled = localConfig?.disable ? import_picocolors7.default.red(" [disabled]") : "";
2365
- console.log(` ${import_picocolors7.default.cyan(name)}: ${model}${builtIn}${disabled}`);
2265
+ const disabled = localConfig?.disable ? import_picocolors6.default.red(" [disabled]") : "";
2266
+ console.log(` ${import_picocolors6.default.cyan(name)}: ${model}${builtIn}${disabled}`);
2366
2267
  }
2367
2268
  console.log();
2368
2269
  const agentOptions = [
@@ -2410,7 +2311,7 @@ async function editAgentModel(configPath) {
2410
2311
  }
2411
2312
  config.agent[agentName].disable = !currentAgent.disable;
2412
2313
  saveConfig(configPath, config);
2413
- const status = config.agent[agentName].disable ? import_picocolors7.default.red("disabled") : import_picocolors7.default.green("enabled");
2314
+ const status = config.agent[agentName].disable ? import_picocolors6.default.red("disabled") : import_picocolors6.default.green("enabled");
2414
2315
  f2.success(`Agent ${agentName}: ${status}`);
2415
2316
  return;
2416
2317
  }
@@ -2431,8 +2332,8 @@ async function editAgentModel(configPath) {
2431
2332
  return;
2432
2333
  }
2433
2334
  const agentInfo = agentInfoMap.get(agentName);
2434
- f2.info(`Current model: ${import_picocolors7.default.cyan(agentInfo?.model || currentAgent.model || "(default)")}`);
2435
- f2.info(import_picocolors7.default.dim("Fetching models from models.dev..."));
2335
+ f2.info(`Current model: ${import_picocolors6.default.cyan(agentInfo?.model || currentAgent.model || "(default)")}`);
2336
+ f2.info(import_picocolors6.default.dim("Fetching models from models.dev..."));
2436
2337
  const modelsDevData = await fetchFromModelsDev();
2437
2338
  if (!modelsDevData || Object.keys(modelsDevData).length === 0) {
2438
2339
  f2.warn("models.dev unavailable - please enter manually");
@@ -2454,7 +2355,7 @@ async function editAgentModel(configPath) {
2454
2355
  }
2455
2356
  config.agent[agentName].model = newModel;
2456
2357
  saveConfig(configPath, config);
2457
- f2.success(`${agentName} model set to ${import_picocolors7.default.cyan(newModel)}`);
2358
+ f2.success(`${agentName} model set to ${import_picocolors6.default.cyan(newModel)}`);
2458
2359
  return;
2459
2360
  }
2460
2361
  const localProviders = config.provider || {};
@@ -2512,7 +2413,7 @@ async function editAgentModel(configPath) {
2512
2413
  agentConfig.model = undefined;
2513
2414
  }
2514
2415
  saveConfig(configPath, config);
2515
- f2.success(`${agentName} model set to ${import_picocolors7.default.cyan("(default)")}`);
2416
+ f2.success(`${agentName} model set to ${import_picocolors6.default.cyan("(default)")}`);
2516
2417
  return;
2517
2418
  }
2518
2419
  if (providerId === "__custom__") {
@@ -2534,7 +2435,7 @@ async function editAgentModel(configPath) {
2534
2435
  }
2535
2436
  config.agent[agentName].model = customModel;
2536
2437
  saveConfig(configPath, config);
2537
- f2.success(`${agentName} model set to ${import_picocolors7.default.cyan(customModel)}`);
2438
+ f2.success(`${agentName} model set to ${import_picocolors6.default.cyan(customModel)}`);
2538
2439
  return;
2539
2440
  }
2540
2441
  if (providerId === "__search__") {
@@ -2589,7 +2490,7 @@ async function editAgentModel(configPath) {
2589
2490
  }
2590
2491
  config.agent[agentName].model = fullModelId;
2591
2492
  saveConfig(configPath, config);
2592
- f2.success(`${agentName} model set to ${import_picocolors7.default.cyan(fullModelId)}`);
2493
+ f2.success(`${agentName} model set to ${import_picocolors6.default.cyan(fullModelId)}`);
2593
2494
  }
2594
2495
  async function editMCP(configPath) {
2595
2496
  const config = loadConfig(configPath);
@@ -2608,11 +2509,11 @@ async function editMCP(configPath) {
2608
2509
  return;
2609
2510
  }
2610
2511
  console.log();
2611
- console.log(import_picocolors7.default.bold(" MCP Servers"));
2512
+ console.log(import_picocolors6.default.bold(" MCP Servers"));
2612
2513
  for (const [name, server] of Object.entries(config.mcp)) {
2613
- const status = server.enabled === false ? import_picocolors7.default.red("off") : import_picocolors7.default.green("on");
2614
- const type = server.type === "remote" ? import_picocolors7.default.dim("remote") : import_picocolors7.default.dim("local");
2615
- const endpoint = server.type === "remote" ? import_picocolors7.default.dim(` → ${server.url}`) : import_picocolors7.default.dim(` → ${(server.command || []).join(" ")}`);
2514
+ const status = server.enabled === false ? import_picocolors6.default.red("off") : import_picocolors6.default.green("on");
2515
+ const type = server.type === "remote" ? import_picocolors6.default.dim("remote") : import_picocolors6.default.dim("local");
2516
+ const endpoint = server.type === "remote" ? import_picocolors6.default.dim(` → ${server.url}`) : import_picocolors6.default.dim(` → ${(server.command || []).join(" ")}`);
2616
2517
  console.log(` ${status} ${name} ${type}${endpoint}`);
2617
2518
  }
2618
2519
  console.log();
@@ -2658,7 +2559,7 @@ async function toggleMCP(configPath, config) {
2658
2559
  const wasEnabled = server.enabled !== false;
2659
2560
  server.enabled = !wasEnabled;
2660
2561
  saveConfig(configPath, config);
2661
- const status = server.enabled ? import_picocolors7.default.green("enabled") : import_picocolors7.default.dim("disabled");
2562
+ const status = server.enabled ? import_picocolors6.default.green("enabled") : import_picocolors6.default.dim("disabled");
2662
2563
  f2.success(`${serverName}: ${status}`);
2663
2564
  }
2664
2565
  async function addMCPServer(configPath, config) {
@@ -2724,7 +2625,7 @@ async function addMCPServer(configPath, config) {
2724
2625
  };
2725
2626
  }
2726
2627
  saveConfig(configPath, config);
2727
- f2.success(`Added MCP server: ${import_picocolors7.default.cyan(serverName)}`);
2628
+ f2.success(`Added MCP server: ${import_picocolors6.default.cyan(serverName)}`);
2728
2629
  }
2729
2630
  async function removeMCP(configPath, config) {
2730
2631
  const servers = Object.keys(config.mcp || {}).map((name) => ({
@@ -2755,13 +2656,13 @@ async function editPermissions(configPath) {
2755
2656
  config.permission = {};
2756
2657
  }
2757
2658
  console.log();
2758
- console.log(import_picocolors7.default.bold(" Current Permissions"));
2759
- console.log(` edit: ${import_picocolors7.default.cyan(config.permission.edit || "ask")}`);
2760
- console.log(` external_directory: ${import_picocolors7.default.cyan(config.permission.external_directory || "ask")}`);
2659
+ console.log(import_picocolors6.default.bold(" Current Permissions"));
2660
+ console.log(` edit: ${import_picocolors6.default.cyan(config.permission.edit || "ask")}`);
2661
+ console.log(` external_directory: ${import_picocolors6.default.cyan(config.permission.external_directory || "ask")}`);
2761
2662
  if (config.permission.bash) {
2762
- console.log(import_picocolors7.default.bold(" Bash"));
2663
+ console.log(import_picocolors6.default.bold(" Bash"));
2763
2664
  for (const [pattern, perm] of Object.entries(config.permission.bash)) {
2764
- console.log(` ${import_picocolors7.default.dim(pattern)}: ${import_picocolors7.default.cyan(perm)}`);
2665
+ console.log(` ${import_picocolors6.default.dim(pattern)}: ${import_picocolors6.default.cyan(perm)}`);
2765
2666
  }
2766
2667
  }
2767
2668
  console.log();
@@ -2823,9 +2724,9 @@ async function editKeybinds(configPath) {
2823
2724
  }
2824
2725
  if (Object.keys(config.keybinds).length > 0) {
2825
2726
  console.log();
2826
- console.log(import_picocolors7.default.bold(" Current Keybinds"));
2727
+ console.log(import_picocolors6.default.bold(" Current Keybinds"));
2827
2728
  for (const [action2, key] of Object.entries(config.keybinds)) {
2828
- console.log(` ${import_picocolors7.default.dim(action2)}: ${import_picocolors7.default.cyan(key)}`);
2729
+ console.log(` ${import_picocolors6.default.dim(action2)}: ${import_picocolors6.default.cyan(key)}`);
2829
2730
  }
2830
2731
  console.log();
2831
2732
  }
@@ -2878,7 +2779,7 @@ async function editKeybinds(configPath) {
2878
2779
  }
2879
2780
  config.keybinds[keybindName] = newKey;
2880
2781
  saveConfig(configPath, config);
2881
- f2.success(`${keybindName} → ${import_picocolors7.default.cyan(newKey)}`);
2782
+ f2.success(`${keybindName} → ${import_picocolors6.default.cyan(newKey)}`);
2882
2783
  }
2883
2784
  var OPENCODE_THEMES = [
2884
2785
  { value: "system", label: "System", hint: "follows OS preference" },
@@ -2918,7 +2819,7 @@ var OPENCODE_THEMES = [
2918
2819
  async function editTheme(configPath) {
2919
2820
  const config = loadConfig(configPath);
2920
2821
  const currentTheme = config.theme || "system";
2921
- f2.info(`Current theme: ${import_picocolors7.default.cyan(currentTheme)}`);
2822
+ f2.info(`Current theme: ${import_picocolors6.default.cyan(currentTheme)}`);
2922
2823
  const validTheme = OPENCODE_THEMES.some((t) => t.value === currentTheme) ? currentTheme : "system";
2923
2824
  const selectedTheme = await ie({
2924
2825
  message: "Select theme",
@@ -2935,7 +2836,7 @@ async function editTheme(configPath) {
2935
2836
  }
2936
2837
  config.theme = selectedTheme;
2937
2838
  saveConfig(configPath, config);
2938
- f2.success(`Theme set to ${import_picocolors7.default.cyan(selectedTheme)}`);
2839
+ f2.success(`Theme set to ${import_picocolors6.default.cyan(selectedTheme)}`);
2939
2840
  }
2940
2841
  async function editTui(configPath) {
2941
2842
  const config = loadConfig(configPath);
@@ -2983,7 +2884,7 @@ async function editTui(configPath) {
2983
2884
  }
2984
2885
  config.tui.diff_style = style;
2985
2886
  saveConfig(configPath, config);
2986
- f2.success(`Diff style set to ${import_picocolors7.default.cyan(style)}`);
2887
+ f2.success(`Diff style set to ${import_picocolors6.default.cyan(style)}`);
2987
2888
  } else {
2988
2889
  const settingKey = setting;
2989
2890
  const value = await te({
@@ -3000,7 +2901,7 @@ async function editTui(configPath) {
3000
2901
  }
3001
2902
  config.tui[settingKey] = value ? Number(value) : undefined;
3002
2903
  saveConfig(configPath, config);
3003
- f2.success(`${settingKey} set to ${import_picocolors7.default.cyan(value || "default")}`);
2904
+ f2.success(`${settingKey} set to ${import_picocolors6.default.cyan(value || "default")}`);
3004
2905
  }
3005
2906
  }
3006
2907
  async function editTools(configPath) {
@@ -3020,9 +2921,9 @@ async function editTools(configPath) {
3020
2921
  "todoread"
3021
2922
  ];
3022
2923
  console.log();
3023
- console.log(import_picocolors7.default.bold(" Tool Status"));
2924
+ console.log(import_picocolors6.default.bold(" Tool Status"));
3024
2925
  for (const tool of commonTools) {
3025
- const status = config.tools[tool] === false ? import_picocolors7.default.red("off") : import_picocolors7.default.green("on");
2926
+ const status = config.tools[tool] === false ? import_picocolors6.default.red("off") : import_picocolors6.default.green("on");
3026
2927
  console.log(` ${status} ${tool}`);
3027
2928
  }
3028
2929
  console.log();
@@ -3056,7 +2957,7 @@ async function editTools(configPath) {
3056
2957
  const wasEnabled = config.tools[toolName] !== false;
3057
2958
  config.tools[toolName] = !wasEnabled;
3058
2959
  saveConfig(configPath, config);
3059
- const status = config.tools[toolName] ? import_picocolors7.default.green("enabled") : import_picocolors7.default.red("disabled");
2960
+ const status = config.tools[toolName] ? import_picocolors6.default.green("enabled") : import_picocolors6.default.red("disabled");
3060
2961
  f2.success(`${toolName}: ${status}`);
3061
2962
  } else {
3062
2963
  const toolName = await te({
@@ -3079,13 +2980,13 @@ async function editTools(configPath) {
3079
2980
  }
3080
2981
  config.tools[toolName] = enabled;
3081
2982
  saveConfig(configPath, config);
3082
- f2.success(`Added ${toolName}: ${enabled ? import_picocolors7.default.green("on") : import_picocolors7.default.red("off")}`);
2983
+ f2.success(`Added ${toolName}: ${enabled ? import_picocolors6.default.green("on") : import_picocolors6.default.red("off")}`);
3083
2984
  }
3084
2985
  }
3085
2986
  async function editShare(configPath) {
3086
2987
  const config = loadConfig(configPath);
3087
2988
  const current = config.share || "manual";
3088
- f2.info(`Current share setting: ${import_picocolors7.default.cyan(current)}`);
2989
+ f2.info(`Current share setting: ${import_picocolors6.default.cyan(current)}`);
3089
2990
  const selected = await ie({
3090
2991
  message: "Share setting",
3091
2992
  options: [
@@ -3108,13 +3009,13 @@ async function editShare(configPath) {
3108
3009
  const shareValue = selected;
3109
3010
  config.share = shareValue;
3110
3011
  saveConfig(configPath, config);
3111
- f2.success(`Share set to ${import_picocolors7.default.cyan(shareValue)}`);
3012
+ f2.success(`Share set to ${import_picocolors6.default.cyan(shareValue)}`);
3112
3013
  }
3113
3014
  async function editAutoupdate(configPath) {
3114
3015
  const config = loadConfig(configPath);
3115
3016
  const current = config.autoupdate ?? true;
3116
3017
  const display = current === "notify" ? "notify" : current ? "enabled" : "disabled";
3117
- f2.info(`Current autoupdate: ${import_picocolors7.default.cyan(display)}`);
3018
+ f2.info(`Current autoupdate: ${import_picocolors6.default.cyan(display)}`);
3118
3019
  const selected = await ie({
3119
3020
  message: "Autoupdate behavior",
3120
3021
  options: [
@@ -3145,69 +3046,366 @@ async function editAutoupdate(configPath) {
3145
3046
  config.autoupdate = selectedValue === "true";
3146
3047
  }
3147
3048
  saveConfig(configPath, config);
3148
- f2.success(`Autoupdate set to ${import_picocolors7.default.cyan(selectedValue)}`);
3049
+ f2.success(`Autoupdate set to ${import_picocolors6.default.cyan(selectedValue)}`);
3149
3050
  }
3150
3051
 
3151
- // src/commands/upgrade.ts
3152
- import { join as join5, dirname as dirname2 } from "node:path";
3153
- import { fileURLToPath as fileURLToPath2 } from "node:url";
3052
+ // src/commands/init.ts
3154
3053
  import {
3155
- existsSync as existsSync5,
3156
- readFileSync as readFileSync5,
3157
- writeFileSync as writeFileSync4,
3158
- readdirSync as readdirSync4,
3159
- mkdirSync as mkdirSync3,
3160
- copyFileSync
3054
+ existsSync as existsSync3,
3055
+ mkdirSync as mkdirSync2,
3056
+ readFileSync as readFileSync3,
3057
+ writeFileSync as writeFileSync3,
3058
+ readdirSync as readdirSync2
3161
3059
  } from "node:fs";
3162
- var import_picocolors8 = __toESM(require_picocolors(), 1);
3163
- var PRESERVE_FILES = [
3164
- "opencode.json"
3060
+ import { join as join3, dirname, basename } from "node:path";
3061
+ import { fileURLToPath } from "node:url";
3062
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
3063
+ var EXCLUDED_DIRS = [
3064
+ "node_modules",
3065
+ ".git",
3066
+ "dist",
3067
+ ".DS_Store",
3068
+ "coverage",
3069
+ ".next",
3070
+ ".turbo"
3165
3071
  ];
3166
- var PRESERVE_DIRS = [
3167
- "agent",
3168
- "command",
3169
- "memory"
3072
+ var EXCLUDED_FILES = [
3073
+ "bun.lock",
3074
+ "package-lock.json",
3075
+ "yarn.lock",
3076
+ "pnpm-lock.yaml"
3170
3077
  ];
3171
- var SKIP_DIRS = ["node_modules", ".git", "dist", "coverage"];
3172
- function getTemplateRoot2() {
3173
- const __filename2 = fileURLToPath2(import.meta.url);
3174
- const __dirname2 = dirname2(__filename2);
3078
+ function detectMode(targetDir) {
3079
+ const opencodeDir = join3(targetDir, ".opencode");
3080
+ if (existsSync3(opencodeDir)) {
3081
+ return "already-initialized";
3082
+ }
3083
+ if (existsSync3(targetDir)) {
3084
+ const entries = readdirSync2(targetDir);
3085
+ const hasCode = entries.some((e2) => !e2.startsWith(".") && !EXCLUDED_DIRS.includes(e2) && e2 !== "node_modules");
3086
+ if (hasCode) {
3087
+ return "add-config";
3088
+ }
3089
+ }
3090
+ return "scaffold";
3091
+ }
3092
+ function getTemplateRoot() {
3093
+ const __filename2 = fileURLToPath(import.meta.url);
3094
+ const __dirname2 = dirname(__filename2);
3175
3095
  const possiblePaths = [
3176
- join5(__dirname2, "template"),
3177
- join5(__dirname2, "..", "..", ".opencode")
3096
+ join3(__dirname2, "template"),
3097
+ join3(__dirname2, "..", "..", ".opencode")
3178
3098
  ];
3179
3099
  for (const path of possiblePaths) {
3180
- const opencodeDir = join5(path, ".opencode");
3181
- if (existsSync5(opencodeDir)) {
3100
+ const opencodeDir = join3(path, ".opencode");
3101
+ if (existsSync3(opencodeDir)) {
3182
3102
  return path;
3183
3103
  }
3184
3104
  }
3185
3105
  return null;
3186
3106
  }
3187
- function getCurrentVersion(opencodeDir) {
3188
- const versionFile = join5(opencodeDir, ".version");
3189
- if (existsSync5(versionFile)) {
3190
- return readFileSync5(versionFile, "utf-8").trim();
3191
- }
3192
- return null;
3193
- }
3194
- function getPackageVersion() {
3195
- const __filename2 = fileURLToPath2(import.meta.url);
3196
- const __dirname2 = dirname2(__filename2);
3197
- const pkgPaths = [
3198
- join5(__dirname2, "..", "..", "package.json"),
3199
- join5(__dirname2, "..", "package.json")
3200
- ];
3201
- for (const pkgPath of pkgPaths) {
3202
- if (existsSync5(pkgPath)) {
3203
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
3204
- return pkg.version;
3107
+ async function copyDir(src, dest) {
3108
+ const { mkdir, readdir } = await import("node:fs/promises");
3109
+ await mkdir(dest, { recursive: true });
3110
+ for (const entry of await readdir(src, { withFileTypes: true })) {
3111
+ if (EXCLUDED_DIRS.includes(entry.name))
3112
+ continue;
3113
+ if (!entry.isDirectory() && EXCLUDED_FILES.includes(entry.name))
3114
+ continue;
3115
+ const srcPath = join3(src, entry.name);
3116
+ const destPath = join3(dest, entry.name);
3117
+ if (entry.isSymbolicLink()) {
3118
+ continue;
3119
+ } else if (entry.isDirectory()) {
3120
+ await copyDir(srcPath, destPath);
3121
+ } else {
3122
+ const content = readFileSync3(srcPath, "utf-8");
3123
+ writeFileSync3(destPath, content);
3205
3124
  }
3206
3125
  }
3207
- return "unknown";
3208
3126
  }
3209
- async function checkVersion(opencodeDir) {
3210
- const current = getCurrentVersion(opencodeDir);
3127
+ async function copyOpenCodeOnly(templateRoot, targetDir) {
3128
+ const opencodeSrc = join3(templateRoot, ".opencode");
3129
+ const opencodeDest = join3(targetDir, ".opencode");
3130
+ if (!existsSync3(opencodeSrc)) {
3131
+ return false;
3132
+ }
3133
+ await copyDir(opencodeSrc, opencodeDest);
3134
+ return true;
3135
+ }
3136
+ async function initCommand(options = {}) {
3137
+ const targetDir = process.cwd();
3138
+ const mode = detectMode(targetDir);
3139
+ oe(import_picocolors7.default.bgCyan(import_picocolors7.default.black(" OpenCodeKit ")));
3140
+ if (mode === "already-initialized" && !options.force) {
3141
+ f2.warn("Already initialized (.opencode/ exists)");
3142
+ f2.info(`Use ${import_picocolors7.default.cyan("--force")} to reinitialize`);
3143
+ $e("Nothing to do");
3144
+ return;
3145
+ }
3146
+ const templateRoot = getTemplateRoot();
3147
+ if (!templateRoot) {
3148
+ f2.error("Template not found. Please reinstall opencodekit.");
3149
+ $e(import_picocolors7.default.red("Failed"));
3150
+ process.exit(1);
3151
+ }
3152
+ let projectName = basename(targetDir);
3153
+ if (mode === "scaffold") {
3154
+ const name = await te({
3155
+ message: "Project name",
3156
+ placeholder: projectName,
3157
+ defaultValue: projectName
3158
+ });
3159
+ if (lD(name)) {
3160
+ ue("Cancelled");
3161
+ process.exit(0);
3162
+ }
3163
+ projectName = name || projectName;
3164
+ }
3165
+ const s = de();
3166
+ if (mode === "scaffold") {
3167
+ s.start("Scaffolding project");
3168
+ mkdirSync2(targetDir, { recursive: true });
3169
+ } else if (mode === "add-config") {
3170
+ s.start("Adding OpenCodeKit");
3171
+ } else {
3172
+ s.start("Reinitializing");
3173
+ }
3174
+ const success = await copyOpenCodeOnly(templateRoot, targetDir);
3175
+ if (!success) {
3176
+ s.stop("Failed");
3177
+ $e(import_picocolors7.default.red("Template copy failed"));
3178
+ process.exit(1);
3179
+ }
3180
+ s.stop("Done");
3181
+ le(`cd .opencode && bun install`, "Next steps");
3182
+ $e(import_picocolors7.default.green("Ready to code!"));
3183
+ }
3184
+
3185
+ // src/commands/menu.ts
3186
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
3187
+ import { existsSync as existsSync6, readdirSync as readdirSync5, lstatSync as lstatSync3 } from "node:fs";
3188
+ import { join as join6, basename as basename2 } from "node:path";
3189
+
3190
+ // src/commands/skill.ts
3191
+ import { join as join4 } from "node:path";
3192
+ import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync4, lstatSync as lstatSync2 } from "node:fs";
3193
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
3194
+ async function skillCommand(action) {
3195
+ const opencodePath = join4(process.cwd(), ".opencode");
3196
+ if (!existsSync4(opencodePath)) {
3197
+ notInitialized();
3198
+ return;
3199
+ }
3200
+ switch (action) {
3201
+ case "list":
3202
+ await listSkills(opencodePath);
3203
+ break;
3204
+ case "add":
3205
+ await addSkill(opencodePath);
3206
+ break;
3207
+ case "view": {
3208
+ const skillName = process.argv[4];
3209
+ await viewSkill(opencodePath, skillName);
3210
+ break;
3211
+ }
3212
+ default:
3213
+ unknownAction(action, ["list", "add", "view"]);
3214
+ }
3215
+ }
3216
+ function collectSkills(basePath) {
3217
+ const skills = [];
3218
+ if (!existsSync4(basePath))
3219
+ return skills;
3220
+ const tiers = ["core", "stack", "specialized"];
3221
+ let hasTiers = false;
3222
+ for (const tier of tiers) {
3223
+ const tierPath = join4(basePath, tier);
3224
+ if (existsSync4(tierPath)) {
3225
+ hasTiers = true;
3226
+ const entries = readdirSync3(tierPath);
3227
+ for (const entry of entries) {
3228
+ const entryPath = join4(tierPath, entry);
3229
+ if (lstatSync2(entryPath).isDirectory()) {
3230
+ skills.push({ name: entry, path: entryPath, tier });
3231
+ }
3232
+ }
3233
+ }
3234
+ }
3235
+ if (!hasTiers) {
3236
+ const entries = readdirSync3(basePath);
3237
+ for (const entry of entries) {
3238
+ const entryPath = join4(basePath, entry);
3239
+ if (lstatSync2(entryPath).isDirectory()) {
3240
+ skills.push({ name: entry, path: entryPath });
3241
+ }
3242
+ }
3243
+ }
3244
+ return skills;
3245
+ }
3246
+ async function listSkills(opencodePath) {
3247
+ const locations = [
3248
+ { path: join4(opencodePath, "superpowers", "skills"), name: "Superpowers" },
3249
+ { path: join4(opencodePath, "skills"), name: "Project" }
3250
+ ];
3251
+ let totalSkills = 0;
3252
+ for (const loc of locations) {
3253
+ const skills = collectSkills(loc.path);
3254
+ if (skills.length > 0) {
3255
+ f2.info(import_picocolors8.default.bold(loc.name));
3256
+ for (const skill of skills) {
3257
+ const label = skill.tier ? `${skill.tier}/${skill.name}` : skill.name;
3258
+ console.log(` ${import_picocolors8.default.cyan("•")} ${label}`);
3259
+ }
3260
+ totalSkills += skills.length;
3261
+ }
3262
+ }
3263
+ if (totalSkills === 0) {
3264
+ showEmpty("skills", "ock skill add");
3265
+ }
3266
+ }
3267
+ async function addSkill(opencodePath) {
3268
+ oe(import_picocolors8.default.bgYellow(import_picocolors8.default.black(" Add Skill ")));
3269
+ const superpowersPath = join4(opencodePath, "superpowers", "skills");
3270
+ const skills = collectSkills(superpowersPath);
3271
+ if (skills.length === 0) {
3272
+ showEmpty("skills available to install");
3273
+ $e("Done");
3274
+ return;
3275
+ }
3276
+ const selected = await ie({
3277
+ message: "Select skill to install",
3278
+ options: skills.map((s) => ({
3279
+ value: s.name,
3280
+ label: s.tier ? `${s.name} (${s.tier})` : s.name
3281
+ }))
3282
+ });
3283
+ if (lD(selected)) {
3284
+ ue("Cancelled");
3285
+ return;
3286
+ }
3287
+ const skill = skills.find((s) => s.name === selected);
3288
+ if (!skill)
3289
+ return;
3290
+ const skillMdPath = join4(skill.path, "SKILL.md");
3291
+ if (!existsSync4(skillMdPath)) {
3292
+ notFound("SKILL.md in", skill.name);
3293
+ $e(import_picocolors8.default.red("Failed"));
3294
+ return;
3295
+ }
3296
+ const content = readFileSync4(skillMdPath, "utf-8");
3297
+ const lines = content.split(`
3298
+ `).slice(0, 10);
3299
+ console.log();
3300
+ console.log(import_picocolors8.default.dim("─".repeat(40)));
3301
+ console.log(lines.join(`
3302
+ `));
3303
+ if (content.split(`
3304
+ `).length > 10) {
3305
+ console.log(import_picocolors8.default.dim("..."));
3306
+ }
3307
+ console.log(import_picocolors8.default.dim("─".repeat(40)));
3308
+ console.log();
3309
+ f2.success(`Skill "${skill.name}" is available at:`);
3310
+ f2.info(import_picocolors8.default.cyan(skill.path));
3311
+ le(`Skills in superpowers/ are auto-loaded.
3312
+ Use ${import_picocolors8.default.cyan("find_skills")} + ${import_picocolors8.default.cyan("use_skill")} in opencode.`, "Usage");
3313
+ $e(import_picocolors8.default.green("Done!"));
3314
+ }
3315
+ async function viewSkill(opencodePath, skillName) {
3316
+ const superpowersPath = join4(opencodePath, "superpowers", "skills");
3317
+ const skills = collectSkills(superpowersPath);
3318
+ if (skills.length === 0) {
3319
+ showEmpty("skills");
3320
+ return;
3321
+ }
3322
+ if (!skillName) {
3323
+ const selected = await ie({
3324
+ message: "Select skill to view",
3325
+ options: skills.map((s) => ({ value: s.name, label: s.name }))
3326
+ });
3327
+ if (lD(selected)) {
3328
+ return;
3329
+ }
3330
+ skillName = selected;
3331
+ }
3332
+ const skill = skills.find((s) => s.name === skillName);
3333
+ if (!skill) {
3334
+ notFound("Skill", skillName);
3335
+ return;
3336
+ }
3337
+ const skillMdPath = join4(skill.path, "SKILL.md");
3338
+ if (!existsSync4(skillMdPath)) {
3339
+ notFound("SKILL.md");
3340
+ return;
3341
+ }
3342
+ const content = readFileSync4(skillMdPath, "utf-8");
3343
+ console.log();
3344
+ console.log(import_picocolors8.default.dim("─".repeat(40)));
3345
+ console.log(content);
3346
+ console.log(import_picocolors8.default.dim("─".repeat(40)));
3347
+ }
3348
+
3349
+ // src/commands/upgrade.ts
3350
+ import { join as join5, dirname as dirname2 } from "node:path";
3351
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
3352
+ import {
3353
+ existsSync as existsSync5,
3354
+ readFileSync as readFileSync5,
3355
+ writeFileSync as writeFileSync4,
3356
+ readdirSync as readdirSync4,
3357
+ mkdirSync as mkdirSync3,
3358
+ copyFileSync
3359
+ } from "node:fs";
3360
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
3361
+ var PRESERVE_FILES = [
3362
+ "opencode.json"
3363
+ ];
3364
+ var PRESERVE_DIRS = [
3365
+ "agent",
3366
+ "command",
3367
+ "memory"
3368
+ ];
3369
+ var SKIP_DIRS = ["node_modules", ".git", "dist", "coverage"];
3370
+ function getTemplateRoot2() {
3371
+ const __filename2 = fileURLToPath2(import.meta.url);
3372
+ const __dirname2 = dirname2(__filename2);
3373
+ const possiblePaths = [
3374
+ join5(__dirname2, "template"),
3375
+ join5(__dirname2, "..", "..", ".opencode")
3376
+ ];
3377
+ for (const path of possiblePaths) {
3378
+ const opencodeDir = join5(path, ".opencode");
3379
+ if (existsSync5(opencodeDir)) {
3380
+ return path;
3381
+ }
3382
+ }
3383
+ return null;
3384
+ }
3385
+ function getCurrentVersion(opencodeDir) {
3386
+ const versionFile = join5(opencodeDir, ".version");
3387
+ if (existsSync5(versionFile)) {
3388
+ return readFileSync5(versionFile, "utf-8").trim();
3389
+ }
3390
+ return null;
3391
+ }
3392
+ function getPackageVersion() {
3393
+ const __filename2 = fileURLToPath2(import.meta.url);
3394
+ const __dirname2 = dirname2(__filename2);
3395
+ const pkgPaths = [
3396
+ join5(__dirname2, "..", "..", "package.json"),
3397
+ join5(__dirname2, "..", "package.json")
3398
+ ];
3399
+ for (const pkgPath of pkgPaths) {
3400
+ if (existsSync5(pkgPath)) {
3401
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
3402
+ return pkg.version;
3403
+ }
3404
+ }
3405
+ return "unknown";
3406
+ }
3407
+ async function checkVersion(opencodeDir) {
3408
+ const current = getCurrentVersion(opencodeDir);
3211
3409
  const latest = getPackageVersion();
3212
3410
  return {
3213
3411
  current,
@@ -3283,23 +3481,23 @@ async function upgradeCommand(options = {}) {
3283
3481
  notInitialized();
3284
3482
  return;
3285
3483
  }
3286
- oe(import_picocolors8.default.bgCyan(import_picocolors8.default.black(" Upgrade ")));
3484
+ oe(import_picocolors9.default.bgCyan(import_picocolors9.default.black(" Upgrade ")));
3287
3485
  const versionInfo = await checkVersion(opencodeDir);
3288
3486
  console.log();
3289
- console.log(` ${import_picocolors8.default.bold("Installed")} ${import_picocolors8.default.cyan(versionInfo.current || "unknown")}`);
3290
- console.log(` ${import_picocolors8.default.bold("Latest")} ${import_picocolors8.default.cyan(versionInfo.latest || "unknown")}`);
3487
+ console.log(` ${import_picocolors9.default.bold("Installed")} ${import_picocolors9.default.cyan(versionInfo.current || "unknown")}`);
3488
+ console.log(` ${import_picocolors9.default.bold("Latest")} ${import_picocolors9.default.cyan(versionInfo.latest || "unknown")}`);
3291
3489
  console.log();
3292
3490
  if (options.check) {
3293
3491
  if (versionInfo.needsUpdate) {
3294
- f2.info(`Update available: ${import_picocolors8.default.cyan(versionInfo.latest)}`);
3295
- $e(`Run ${import_picocolors8.default.cyan("ock upgrade")} to update`);
3492
+ f2.info(`Update available: ${import_picocolors9.default.cyan(versionInfo.latest)}`);
3493
+ $e(`Run ${import_picocolors9.default.cyan("ock upgrade")} to update`);
3296
3494
  } else {
3297
- $e(import_picocolors8.default.green("Already up to date"));
3495
+ $e(import_picocolors9.default.green("Already up to date"));
3298
3496
  }
3299
3497
  return;
3300
3498
  }
3301
3499
  if (!versionInfo.needsUpdate && !options.force) {
3302
- $e(import_picocolors8.default.green("Already up to date"));
3500
+ $e(import_picocolors9.default.green("Already up to date"));
3303
3501
  return;
3304
3502
  }
3305
3503
  const templateRoot = getTemplateRoot2();
@@ -3335,205 +3533,10 @@ async function upgradeCommand(options = {}) {
3335
3533
  f2.info(`Preserved ${result.preserved.length} user files`);
3336
3534
  }
3337
3535
  le("cd .opencode && bun install", "Run if dependencies changed");
3338
- $e(import_picocolors8.default.green(`Upgraded to ${versionInfo.latest}`));
3339
- }
3340
-
3341
- // src/commands/completion.ts
3342
- var import_picocolors9 = __toESM(require_picocolors(), 1);
3343
- var BASH_COMPLETION = `# ock bash completion
3344
- _ock_completion() {
3345
- local cur prev commands
3346
- COMPREPLY=()
3347
- cur="\${COMP_WORDS[COMP_CWORD]}"
3348
- prev="\${COMP_WORDS[COMP_CWORD-1]}"
3349
-
3350
- commands="init config upgrade agent skill doctor status help"
3351
-
3352
- case "\${prev}" in
3353
- ock)
3354
- COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
3355
- return 0
3356
- ;;
3357
- agent)
3358
- COMPREPLY=( $(compgen -W "list add view" -- "\${cur}") )
3359
- return 0
3360
- ;;
3361
- skill)
3362
- COMPREPLY=( $(compgen -W "list add view" -- "\${cur}") )
3363
- return 0
3364
- ;;
3365
- config)
3366
- COMPREPLY=( $(compgen -W "model mcp permission keybinds show" -- "\${cur}") )
3367
- return 0
3368
- ;;
3369
- init)
3370
- COMPREPLY=( $(compgen -W "--force" -- "\${cur}") )
3371
- return 0
3372
- ;;
3373
- upgrade)
3374
- COMPREPLY=( $(compgen -W "--force --check" -- "\${cur}") )
3375
- return 0
3376
- ;;
3377
- *)
3378
- ;;
3379
- esac
3380
- }
3381
- complete -F _ock_completion ock
3382
- `;
3383
- var ZSH_COMPLETION = `#compdef ock
3384
-
3385
- _ock() {
3386
- local -a commands
3387
- local -a agent_actions
3388
- local -a skill_actions
3389
- local -a config_actions
3390
-
3391
- commands=(
3392
- 'init:Initialize OpenCodeKit in current directory'
3393
- 'config:Edit opencode.json'
3394
- 'upgrade:Update templates to latest version'
3395
- 'agent:Manage agents'
3396
- 'skill:Manage skills'
3397
- 'doctor:Check project health'
3398
- 'status:Show project overview'
3399
- 'help:Show help'
3400
- )
3401
-
3402
- agent_actions=(
3403
- 'list:List all agents'
3404
- 'add:Create a new agent'
3405
- 'view:View agent details'
3406
- )
3407
-
3408
- skill_actions=(
3409
- 'list:List all skills'
3410
- 'add:Install a skill'
3411
- 'view:View skill details'
3412
- )
3413
-
3414
- config_actions=(
3415
- 'model:Change default model'
3416
- 'mcp:Manage MCP servers'
3417
- 'permission:Edit permissions'
3418
- 'keybinds:Edit keyboard shortcuts'
3419
- 'show:View current config'
3420
- )
3421
-
3422
- case "$words[2]" in
3423
- agent)
3424
- _describe -t actions 'agent action' agent_actions
3425
- ;;
3426
- skill)
3427
- _describe -t actions 'skill action' skill_actions
3428
- ;;
3429
- config)
3430
- _describe -t actions 'config action' config_actions
3431
- ;;
3432
- init)
3433
- _arguments '--force[Reinitialize even if already exists]'
3434
- ;;
3435
- upgrade)
3436
- _arguments \\
3437
- '--force[Force upgrade even if up to date]' \\
3438
- '--check[Check for updates only]'
3439
- ;;
3440
- *)
3441
- _describe -t commands 'command' commands
3442
- ;;
3443
- esac
3444
- }
3445
-
3446
- _ock "$@"
3447
- `;
3448
- var FISH_COMPLETION = `# ock fish completion
3449
- complete -c ock -n "__fish_use_subcommand" -a "init" -d "Initialize OpenCodeKit"
3450
- complete -c ock -n "__fish_use_subcommand" -a "config" -d "Edit opencode.json"
3451
- complete -c ock -n "__fish_use_subcommand" -a "upgrade" -d "Update templates"
3452
- complete -c ock -n "__fish_use_subcommand" -a "agent" -d "Manage agents"
3453
- complete -c ock -n "__fish_use_subcommand" -a "skill" -d "Manage skills"
3454
- complete -c ock -n "__fish_use_subcommand" -a "doctor" -d "Check project health"
3455
- complete -c ock -n "__fish_use_subcommand" -a "status" -d "Show project overview"
3456
-
3457
- # agent subcommands
3458
- complete -c ock -n "__fish_seen_subcommand_from agent" -a "list" -d "List agents"
3459
- complete -c ock -n "__fish_seen_subcommand_from agent" -a "add" -d "Add agent"
3460
- complete -c ock -n "__fish_seen_subcommand_from agent" -a "view" -d "View agent"
3461
-
3462
- # skill subcommands
3463
- complete -c ock -n "__fish_seen_subcommand_from skill" -a "list" -d "List skills"
3464
- complete -c ock -n "__fish_seen_subcommand_from skill" -a "add" -d "Add skill"
3465
- complete -c ock -n "__fish_seen_subcommand_from skill" -a "view" -d "View skill"
3466
-
3467
- # config subcommands
3468
- complete -c ock -n "__fish_seen_subcommand_from config" -a "model" -d "Change model"
3469
- complete -c ock -n "__fish_seen_subcommand_from config" -a "mcp" -d "Manage MCP"
3470
- complete -c ock -n "__fish_seen_subcommand_from config" -a "permission" -d "Edit permissions"
3471
- complete -c ock -n "__fish_seen_subcommand_from config" -a "keybinds" -d "Edit keybinds"
3472
- complete -c ock -n "__fish_seen_subcommand_from config" -a "show" -d "View config"
3473
-
3474
- # init options
3475
- complete -c ock -n "__fish_seen_subcommand_from init" -l force -d "Reinitialize"
3476
-
3477
- # upgrade options
3478
- complete -c ock -n "__fish_seen_subcommand_from upgrade" -l force -d "Force upgrade"
3479
- complete -c ock -n "__fish_seen_subcommand_from upgrade" -l check -d "Check only"
3480
- `;
3481
- async function completionCommand(shell) {
3482
- if (!shell) {
3483
- oe(import_picocolors9.default.bgCyan(import_picocolors9.default.black(" Shell Completion ")));
3484
- const selected = await ie({
3485
- message: "Select your shell",
3486
- options: [
3487
- { value: "bash", label: "Bash" },
3488
- { value: "zsh", label: "Zsh" },
3489
- { value: "fish", label: "Fish" }
3490
- ]
3491
- });
3492
- if (lD(selected)) {
3493
- ue("Cancelled");
3494
- return;
3495
- }
3496
- shell = selected;
3497
- }
3498
- let script;
3499
- let setupInstructions;
3500
- switch (shell) {
3501
- case "bash":
3502
- script = BASH_COMPLETION;
3503
- setupInstructions = `Add to ~/.bashrc:
3504
- source <(ock completion bash)
3505
-
3506
- Or save to file:
3507
- ock completion bash > ~/.local/share/bash-completion/completions/ock`;
3508
- break;
3509
- case "zsh":
3510
- script = ZSH_COMPLETION;
3511
- setupInstructions = `Add to ~/.zshrc (before compinit):
3512
- source <(ock completion zsh)
3513
-
3514
- Or save to fpath:
3515
- ock completion zsh > ~/.zsh/completions/_ock
3516
- # Then add to ~/.zshrc: fpath=(~/.zsh/completions $fpath)`;
3517
- break;
3518
- case "fish":
3519
- script = FISH_COMPLETION;
3520
- setupInstructions = `Save to fish completions:
3521
- ock completion fish > ~/.config/fish/completions/ock.fish`;
3522
- break;
3523
- default:
3524
- f2.error(`Unknown shell: ${shell}`);
3525
- return;
3526
- }
3527
- console.log(script);
3528
- console.error();
3529
- console.error(import_picocolors9.default.bold("Setup:"));
3530
- console.error(import_picocolors9.default.dim(setupInstructions));
3536
+ $e(import_picocolors9.default.green(`Upgraded to ${versionInfo.latest}`));
3531
3537
  }
3532
3538
 
3533
3539
  // src/commands/menu.ts
3534
- var import_picocolors10 = __toESM(require_picocolors(), 1);
3535
- import { existsSync as existsSync6, readdirSync as readdirSync5, lstatSync as lstatSync3 } from "node:fs";
3536
- import { join as join6, basename as basename2 } from "node:path";
3537
3540
  async function interactiveMenu(version) {
3538
3541
  oe(import_picocolors10.default.bgCyan(import_picocolors10.default.black(` OpenCodeKit v${version} `)));
3539
3542
  const action = await ie({