@zcy2nn/agent-forge 1.1.0 → 1.1.2

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * A custom skill bundled in this repository.
3
- * Unlike npx-installed skills, these are copied from src/skills/ to the OpenCode skills directory
3
+ * Built-in skills are read directly from the package at runtime (no file copy needed).
4
4
  */
5
5
  export interface CustomSkill {
6
6
  /** Skill name (folder name) */
@@ -16,14 +16,3 @@ export interface CustomSkill {
16
16
  * Registry of custom skills bundled in this repository.
17
17
  */
18
18
  export declare const CUSTOM_SKILLS: CustomSkill[];
19
- /**
20
- * Get the target directory for custom skills installation.
21
- */
22
- export declare function getCustomSkillsDir(): string;
23
- /**
24
- * Install a custom skill by copying from src/skills/ to the OpenCode skills directory
25
- * @param skill - The custom skill to install
26
- * @param projectRoot - Root directory of agent-forge project
27
- * @returns True if installation succeeded, false otherwise
28
- */
29
- export declare function installCustomSkill(skill: CustomSkill): boolean;
package/dist/cli/index.js CHANGED
@@ -13,14 +13,14 @@ import * as path from "node:path";
13
13
 
14
14
  // src/cli/config-io.ts
15
15
  import {
16
- copyFileSync as copyFileSync2,
17
- existsSync as existsSync3,
16
+ copyFileSync,
17
+ existsSync as existsSync2,
18
18
  readFileSync,
19
19
  renameSync,
20
- statSync as statSync2,
20
+ statSync,
21
21
  writeFileSync
22
22
  } from "node:fs";
23
- import { dirname as dirname3, join as join3 } from "node:path";
23
+ import { dirname as dirname2, join as join2 } from "node:path";
24
24
 
25
25
  // src/cli/paths.ts
26
26
  import { existsSync, mkdirSync } from "node:fs";
@@ -390,15 +390,6 @@ var DEFAULT_AGENT_MCPS = {
390
390
  };
391
391
 
392
392
  // src/cli/custom-skills.ts
393
- import {
394
- copyFileSync,
395
- existsSync as existsSync2,
396
- mkdirSync as mkdirSync2,
397
- readdirSync,
398
- statSync
399
- } from "node:fs";
400
- import { dirname as dirname2, join as join2 } from "node:path";
401
- import { fileURLToPath } from "node:url";
402
393
  var CUSTOM_SKILLS = [
403
394
  {
404
395
  name: "simplify",
@@ -491,45 +482,6 @@ var CUSTOM_SKILLS = [
491
482
  sourcePath: "src/skills/writing-skills"
492
483
  }
493
484
  ];
494
- function getCustomSkillsDir() {
495
- return join2(getConfigDir(), "skills");
496
- }
497
- function copyDirRecursive(src, dest) {
498
- if (!existsSync2(dest)) {
499
- mkdirSync2(dest, { recursive: true });
500
- }
501
- const entries = readdirSync(src);
502
- for (const entry of entries) {
503
- const srcPath = join2(src, entry);
504
- const destPath = join2(dest, entry);
505
- const stat = statSync(srcPath);
506
- if (stat.isDirectory()) {
507
- copyDirRecursive(srcPath, destPath);
508
- } else {
509
- const destDir = dirname2(destPath);
510
- if (!existsSync2(destDir)) {
511
- mkdirSync2(destDir, { recursive: true });
512
- }
513
- copyFileSync(srcPath, destPath);
514
- }
515
- }
516
- }
517
- function installCustomSkill(skill) {
518
- try {
519
- const packageRoot = fileURLToPath(new URL("../..", import.meta.url));
520
- const sourcePath = join2(packageRoot, skill.sourcePath);
521
- const targetPath = join2(getCustomSkillsDir(), skill.name);
522
- if (!existsSync2(sourcePath)) {
523
- console.error(`Custom skill source not found: ${sourcePath}`);
524
- return false;
525
- }
526
- copyDirRecursive(sourcePath, targetPath);
527
- return true;
528
- } catch (error) {
529
- console.error(`Failed to install custom skill: ${skill.name}`, error);
530
- return false;
531
- }
532
- }
533
485
 
534
486
  // src/cli/skills.ts
535
487
  import { spawnSync } from "node:child_process";
@@ -642,7 +594,7 @@ function generateLiteConfig(installConfig) {
642
594
  ...RECOMMENDED_SKILLS.filter((s) => s.allowedAgents.includes("*") || s.allowedAgents.includes(agentName)).map((s) => s.skillName),
643
595
  ...CUSTOM_SKILLS.filter((s) => s.allowedAgents.includes("*") || s.allowedAgents.includes(agentName)).map((s) => s.name)
644
596
  ];
645
- if (agentName === "implementer" && !skills.includes("agent-browser")) {
597
+ if (installConfig.installSkills && agentName === "implementer" && !skills.includes("agent-browser")) {
646
598
  skills.push("agent-browser");
647
599
  }
648
600
  return {
@@ -696,10 +648,10 @@ function normalizePathForMatch(path) {
696
648
  return path.replaceAll("\\", "/");
697
649
  }
698
650
  function findPackageRoot(startPath) {
699
- let currentPath = dirname3(startPath);
651
+ let currentPath = dirname2(startPath);
700
652
  while (true) {
701
- const packageJsonPath = join3(currentPath, "package.json");
702
- if (existsSync3(packageJsonPath)) {
653
+ const packageJsonPath = join2(currentPath, "package.json");
654
+ if (existsSync2(packageJsonPath)) {
703
655
  try {
704
656
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
705
657
  if (packageJson.name === PACKAGE_NAME) {
@@ -707,7 +659,7 @@ function findPackageRoot(startPath) {
707
659
  }
708
660
  } catch {}
709
661
  }
710
- const parentPath = dirname3(currentPath);
662
+ const parentPath = dirname2(currentPath);
711
663
  if (parentPath === currentPath) {
712
664
  return null;
713
665
  }
@@ -722,8 +674,8 @@ function isLocalPackageRootEntry(entry) {
722
674
  if (!entry || entry.startsWith("file://")) {
723
675
  return false;
724
676
  }
725
- const packageJsonPath = join3(entry, "package.json");
726
- if (!existsSync3(packageJsonPath)) {
677
+ const packageJsonPath = join2(entry, "package.json");
678
+ if (!existsSync2(packageJsonPath)) {
727
679
  return false;
728
680
  }
729
681
  try {
@@ -762,9 +714,9 @@ function stripJsonComments(json) {
762
714
  }
763
715
  function parseConfigFile(path) {
764
716
  try {
765
- if (!existsSync3(path))
717
+ if (!existsSync2(path))
766
718
  return { config: null };
767
- const stat = statSync2(path);
719
+ const stat = statSync(path);
768
720
  if (stat.size === 0)
769
721
  return { config: null };
770
722
  const content = readFileSync(path, "utf-8");
@@ -793,8 +745,8 @@ function writeConfig(configPath, config) {
793
745
  const bakPath = `${configPath}.bak`;
794
746
  const content = `${JSON.stringify(config, null, 2)}
795
747
  `;
796
- if (existsSync3(configPath)) {
797
- copyFileSync2(configPath, bakPath);
748
+ if (existsSync2(configPath)) {
749
+ copyFileSync(configPath, bakPath);
798
750
  }
799
751
  writeFileSync(tmpPath, content);
800
752
  renameSync(tmpPath, configPath);
@@ -880,8 +832,8 @@ function writeLiteConfig(installConfig, targetPath) {
880
832
  const bakPath = `${configPath}.bak`;
881
833
  const content = `${JSON.stringify(config, null, 2)}
882
834
  `;
883
- if (existsSync3(configPath)) {
884
- copyFileSync2(configPath, bakPath);
835
+ if (existsSync2(configPath)) {
836
+ copyFileSync(configPath, bakPath);
885
837
  }
886
838
  writeFileSync(tmpPath, content);
887
839
  renameSync(tmpPath, configPath);
@@ -1256,11 +1208,12 @@ Options:
1256
1208
  }
1257
1209
 
1258
1210
  // src/cli/install.ts
1259
- import { existsSync as existsSync5 } from "node:fs";
1211
+ import { existsSync as existsSync6 } from "node:fs";
1212
+ import { join as join6 } from "node:path";
1260
1213
  import { createInterface } from "node:readline/promises";
1261
1214
  // src/cli/system.ts
1262
1215
  import { spawnSync as spawnSync2 } from "node:child_process";
1263
- import { statSync as statSync4 } from "node:fs";
1216
+ import { statSync as statSync3 } from "node:fs";
1264
1217
 
1265
1218
  // src/utils/compat.ts
1266
1219
  import { spawn as nodeSpawn } from "node:child_process";
@@ -1381,7 +1334,7 @@ function resolveOpenCodePath() {
1381
1334
  if (opencodePath === "opencode")
1382
1335
  continue;
1383
1336
  try {
1384
- const stat = statSync4(opencodePath);
1337
+ const stat = statSync3(opencodePath);
1385
1338
  if (stat.isFile()) {
1386
1339
  cachedOpenCodePath = opencodePath;
1387
1340
  return opencodePath;
@@ -1433,6 +1386,97 @@ function getOpenCodePath() {
1433
1386
  const path2 = resolveOpenCodePath();
1434
1387
  return path2 === "opencode" ? null : path2;
1435
1388
  }
1389
+ // src/cli/skill-sync.ts
1390
+ import {
1391
+ cpSync,
1392
+ existsSync as existsSync5,
1393
+ lstatSync,
1394
+ mkdirSync as mkdirSync2,
1395
+ readlinkSync,
1396
+ rmSync,
1397
+ symlinkSync
1398
+ } from "node:fs";
1399
+ import { join as join5 } from "node:path";
1400
+
1401
+ // src/utils/package-root.ts
1402
+ import { existsSync as existsSync4 } from "node:fs";
1403
+ import { join as join4 } from "node:path";
1404
+ var _cachedPackageRoot;
1405
+ function resolvePackageRoot() {
1406
+ if (_cachedPackageRoot)
1407
+ return _cachedPackageRoot;
1408
+ const distDir = import.meta.dirname;
1409
+ if (!distDir) {
1410
+ throw new Error("import.meta.dirname is not available");
1411
+ }
1412
+ const npmRoot = join4(distDir, "..", "..");
1413
+ if (existsSync4(join4(npmRoot, "src", "skills"))) {
1414
+ _cachedPackageRoot = npmRoot;
1415
+ return npmRoot;
1416
+ }
1417
+ const localRoot = join4(distDir, "..");
1418
+ if (existsSync4(join4(localRoot, "src", "skills"))) {
1419
+ _cachedPackageRoot = localRoot;
1420
+ return localRoot;
1421
+ }
1422
+ _cachedPackageRoot = npmRoot;
1423
+ return npmRoot;
1424
+ }
1425
+
1426
+ // src/cli/skill-sync.ts
1427
+ function syncBuiltinSkills(packageRoot, skillsDirOverride) {
1428
+ const resolvedPackageRoot = packageRoot ?? resolvePackageRoot();
1429
+ const skillsDir = skillsDirOverride ?? join5(getConfigDir(), "skills");
1430
+ mkdirSync2(skillsDir, { recursive: true });
1431
+ let installed = 0;
1432
+ let skipped = 0;
1433
+ let unchanged = 0;
1434
+ let copied = 0;
1435
+ for (const skill of CUSTOM_SKILLS) {
1436
+ const sourcePath = join5(resolvedPackageRoot, skill.sourcePath);
1437
+ const linkPath = join5(skillsDir, skill.name);
1438
+ if (!existsSync5(sourcePath)) {
1439
+ skipped++;
1440
+ continue;
1441
+ }
1442
+ try {
1443
+ if (lstatSync(linkPath).isSymbolicLink()) {
1444
+ const currentTarget = getSymlinkTarget(linkPath);
1445
+ if (currentTarget === sourcePath) {
1446
+ unchanged++;
1447
+ continue;
1448
+ }
1449
+ rmSync(linkPath, { recursive: true, force: true });
1450
+ } else if (existsSync5(linkPath)) {
1451
+ rmSync(linkPath, { recursive: true, force: true });
1452
+ }
1453
+ } catch {
1454
+ try {
1455
+ rmSync(linkPath, { recursive: true, force: true });
1456
+ } catch {}
1457
+ }
1458
+ try {
1459
+ symlinkSync(sourcePath, linkPath, "junction");
1460
+ installed++;
1461
+ } catch {
1462
+ try {
1463
+ cpSync(sourcePath, linkPath, { recursive: true, force: true });
1464
+ copied++;
1465
+ } catch {
1466
+ skipped++;
1467
+ }
1468
+ }
1469
+ }
1470
+ return { installed, skipped, unchanged, copied };
1471
+ }
1472
+ function getSymlinkTarget(linkPath) {
1473
+ try {
1474
+ return readlinkSync(linkPath, "utf-8");
1475
+ } catch {
1476
+ return "";
1477
+ }
1478
+ }
1479
+
1436
1480
  // src/cli/install.ts
1437
1481
  var GREEN = "\x1B[32m";
1438
1482
  var BLUE = "\x1B[34m";
@@ -1482,6 +1526,9 @@ async function confirm(message, defaultYes = true) {
1482
1526
  rl.close();
1483
1527
  }
1484
1528
  }
1529
+ function installBuiltinSkills() {
1530
+ return syncBuiltinSkills();
1531
+ }
1485
1532
  async function askToStarRepo(config) {
1486
1533
  if (!config.promptForStar || config.dryRun || !process.stdin.isTTY)
1487
1534
  return;
@@ -1529,11 +1576,9 @@ async function runInstall(config) {
1529
1576
  const detected = detectCurrentConfig();
1530
1577
  const isUpdate = detected.isInstalled;
1531
1578
  printHeader(isUpdate);
1532
- let totalSteps = 6;
1579
+ let totalSteps = 7;
1533
1580
  if (config.installSkills)
1534
1581
  totalSteps += 1;
1535
- if (config.installCustomSkills)
1536
- totalSteps += 1;
1537
1582
  let step = 1;
1538
1583
  printStep(step++, totalSteps, "Checking OpenCode installation...");
1539
1584
  if (config.dryRun) {
@@ -1587,7 +1632,7 @@ ${JSON.stringify(liteConfig, null, 2)}
1587
1632
  `);
1588
1633
  } else {
1589
1634
  const configPath2 = getExistingLiteConfigPath();
1590
- const configExists = existsSync5(configPath2);
1635
+ const configExists = existsSync6(configPath2);
1591
1636
  if (configExists && !config.reset) {
1592
1637
  printInfo(`Configuration already exists at ${configPath2}. Use --reset to overwrite.`);
1593
1638
  } else {
@@ -1596,6 +1641,24 @@ ${JSON.stringify(liteConfig, null, 2)}
1596
1641
  return 1;
1597
1642
  }
1598
1643
  }
1644
+ printStep(step++, totalSteps, "Installing built-in skills...");
1645
+ if (config.dryRun) {
1646
+ printInfo("Dry run mode - would install built-in skills:");
1647
+ for (const skill of CUSTOM_SKILLS) {
1648
+ printInfo(` - ${skill.name}`);
1649
+ }
1650
+ } else {
1651
+ const result = installBuiltinSkills();
1652
+ if (result.installed > 0) {
1653
+ printSuccess(`${result.installed} built-in skill(s) symlinked to ${join6(getConfigDir(), "skills")}`);
1654
+ }
1655
+ if (result.copied > 0) {
1656
+ printInfo(`${result.copied} skill(s) copied (symlink unavailable — auto-update will NOT sync these)`);
1657
+ }
1658
+ if (result.skipped > 0) {
1659
+ printInfo(`${result.skipped} skill(s) skipped`);
1660
+ }
1661
+ }
1599
1662
  if (config.installSkills) {
1600
1663
  printStep(step++, totalSteps, "Installing recommended skills...");
1601
1664
  if (config.dryRun) {
@@ -1616,28 +1679,8 @@ ${JSON.stringify(liteConfig, null, 2)}
1616
1679
  }
1617
1680
  printSuccess(`${skillsInstalled}/${RECOMMENDED_SKILLS.length} skills processed`);
1618
1681
  }
1619
- }
1620
- if (config.installCustomSkills) {
1621
- printStep(step++, totalSteps, "Installing custom skills...");
1622
- if (config.dryRun) {
1623
- printInfo("Dry run mode - would install custom skills:");
1624
- for (const skill of CUSTOM_SKILLS) {
1625
- printInfo(` - ${skill.name}`);
1626
- }
1627
- } else {
1628
- let customSkillsInstalled = 0;
1629
- for (const skill of CUSTOM_SKILLS) {
1630
- printInfo(`Installing ${skill.name}...`);
1631
- if (installCustomSkill(skill)) {
1632
- printSuccess(`Installed: ${skill.name}`);
1633
- customSkillsInstalled++;
1634
- } else {
1635
- printInfo(`Skipped: ${skill.name} (already installed)`);
1636
- }
1637
- }
1638
- const totalCustom = CUSTOM_SKILLS.length;
1639
- printSuccess(`${customSkillsInstalled}/${totalCustom} custom skills processed`);
1640
- }
1682
+ } else {
1683
+ printInfo("Recommended skills (e.g. agent-browser) not installed. Use --skills=yes to install them.");
1641
1684
  }
1642
1685
  const statusMsg = isUpdate ? "Configuration updated!" : "Installation complete!";
1643
1686
  console.log(`${SYMBOLS.star} ${BOLD}${GREEN}${statusMsg}${RESET}`);
@@ -1674,7 +1717,6 @@ async function install(args) {
1674
1717
  const config = {
1675
1718
  hasTmux: false,
1676
1719
  installSkills: args.skills === "yes",
1677
- installCustomSkills: args.skills === "yes",
1678
1720
  preset: args.preset,
1679
1721
  promptForStar: args.tui,
1680
1722
  dryRun: args.dryRun,
@@ -1696,7 +1738,7 @@ function getGeneratedPresetNames2() {
1696
1738
  function parseArgs(args) {
1697
1739
  const result = {
1698
1740
  tui: true,
1699
- skills: "yes"
1741
+ skills: "no"
1700
1742
  };
1701
1743
  for (const arg of args) {
1702
1744
  if (arg === "--no-tui") {
@@ -1730,7 +1772,7 @@ Usage:
1730
1772
  bunx @zcy2nn/agent-forge doctor [OPTIONS]
1731
1773
 
1732
1774
  Options:
1733
- --skills=yes|no Install recommended and bundled skills (default: yes)
1775
+ --skills=yes|no Install recommended skills like agent-browser (default: no)
1734
1776
  --preset=<name> Active generated config preset (default: openai)
1735
1777
  --no-tui Non-interactive mode
1736
1778
  --dry-run Simulate install without writing files
@@ -0,0 +1,23 @@
1
+ export interface SkillSyncResult {
2
+ installed: number;
3
+ skipped: number;
4
+ /** Skills that were already correctly symlinked (no change needed) */
5
+ unchanged: number;
6
+ /** Skills installed via file copy (symlink unavailable) */
7
+ copied: number;
8
+ }
9
+ /**
10
+ * Ensure all built-in skills are symlinked into OpenCode's skills directory.
11
+ *
12
+ * - If a symlink already points to the correct source → skip (unchanged).
13
+ * - If a directory or stale symlink exists → replace with fresh symlink.
14
+ * - If symlink creation fails (e.g. Windows without junction support) →
15
+ * fall back to file copy.
16
+ * - If the source path doesn't exist in the package → skip.
17
+ *
18
+ * @param packageRoot - Absolute path to the agent-forge package root
19
+ * (the directory containing `src/skills/`). Defaults to auto-detected via resolvePackageRoot().
20
+ * @param skillsDirOverride - Override for the target skills directory
21
+ * (useful in tests). Defaults to `<configDir>/skills`.
22
+ */
23
+ export declare function syncBuiltinSkills(packageRoot?: string, skillsDirOverride?: string): SkillSyncResult;
@@ -15,7 +15,6 @@ export interface OpenCodeConfig {
15
15
  export interface InstallConfig {
16
16
  hasTmux: boolean;
17
17
  installSkills: boolean;
18
- installCustomSkills: boolean;
19
18
  preset?: string;
20
19
  promptForStar?: boolean;
21
20
  dryRun?: boolean;
@@ -1,7 +1,8 @@
1
1
  /**
2
- * Filter available_skills block based on the current agent's permission.skill rules.
3
- * OpenCode core injects `<available_skills>` globally, so this hook rewrites that
4
- * block before the prompt is sent.
2
+ * Filter and inject available_skills block based on the current agent's permission.skill rules.
3
+ * OpenCode core injects `<available_skills>` from installed skills, so this hook rewrites that
4
+ * block before the prompt is sent. Built-in skills from the package are also injected so they
5
+ * are always visible even without being copied to the OpenCode skills directory.
5
6
  */
6
7
  import type { PluginInput } from '@opencode-ai/plugin';
7
8
  import { type PluginConfig } from '../../config';
@@ -19,6 +20,14 @@ interface MessageWithParts {
19
20
  parts: MessagePart[];
20
21
  }
21
22
  type SkillRule = 'allow' | 'ask' | 'deny';
23
+ interface SkillEntry {
24
+ name: string;
25
+ block: string;
26
+ }
27
+ /**
28
+ * Merge installed skill entries with built-in entries (dedup by name, installed takes precedence).
29
+ */
30
+ declare function mergeWithBuiltinSkills(installedEntries: SkillEntry[]): SkillEntry[];
22
31
  declare function filterAvailableSkillsText(text: string, permissionRules: Record<string, SkillRule>): string;
23
32
  /**
24
33
  * Creates the experimental.chat.messages.transform hook for filtering available skills.
@@ -29,4 +38,4 @@ export declare function createFilterAvailableSkillsHook(_ctx: PluginInput, confi
29
38
  messages: MessageWithParts[];
30
39
  }) => Promise<void>;
31
40
  };
32
- export { filterAvailableSkillsText };
41
+ export { filterAvailableSkillsText, mergeWithBuiltinSkills };
package/dist/index.js CHANGED
@@ -30,8 +30,175 @@ var __toESM = (mod, isNodeMode, target) => {
30
30
  return to;
31
31
  };
32
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
37
+ var __export = (target, all) => {
38
+ for (var name in all)
39
+ __defProp(target, name, {
40
+ get: all[name],
41
+ enumerable: true,
42
+ configurable: true,
43
+ set: __exportSetter.bind(all, name)
44
+ });
45
+ };
46
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
33
47
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
34
48
 
49
+ // src/cli/custom-skills.ts
50
+ var CUSTOM_SKILLS;
51
+ var init_custom_skills = __esm(() => {
52
+ CUSTOM_SKILLS = [
53
+ {
54
+ name: "simplify",
55
+ description: "Code simplification and readability-focused refactoring",
56
+ allowedAgents: ["reviewer"],
57
+ sourcePath: "src/skills/simplify"
58
+ },
59
+ {
60
+ name: "codemap",
61
+ description: "Repository understanding and hierarchical codemap generation",
62
+ allowedAgents: ["orchestrator"],
63
+ sourcePath: "src/skills/codemap"
64
+ },
65
+ {
66
+ name: "brainstorming",
67
+ description: "Turn ideas into designs through collaborative dialogue before implementation",
68
+ allowedAgents: ["orchestrator"],
69
+ sourcePath: "src/skills/brainstorming"
70
+ },
71
+ {
72
+ name: "writing-plans",
73
+ description: "Create comprehensive implementation plans from specs before touching code",
74
+ allowedAgents: ["orchestrator"],
75
+ sourcePath: "src/skills/writing-plans"
76
+ },
77
+ {
78
+ name: "executing-plans",
79
+ description: "Execute written implementation plans with review checkpoints",
80
+ allowedAgents: ["orchestrator"],
81
+ sourcePath: "src/skills/executing-plans"
82
+ },
83
+ {
84
+ name: "subagent-driven-development",
85
+ description: "Execute plans by dispatching fresh subagent per task with two-stage review",
86
+ allowedAgents: ["orchestrator"],
87
+ sourcePath: "src/skills/subagent-driven-development"
88
+ },
89
+ {
90
+ name: "dispatching-parallel-agents",
91
+ description: "Dispatch parallel agents for independent tasks without shared state",
92
+ allowedAgents: ["orchestrator"],
93
+ sourcePath: "src/skills/dispatching-parallel-agents"
94
+ },
95
+ {
96
+ name: "using-git-worktrees",
97
+ description: "Create isolated git worktrees before executing implementation plans",
98
+ allowedAgents: ["orchestrator"],
99
+ sourcePath: "src/skills/using-git-worktrees"
100
+ },
101
+ {
102
+ name: "finishing-a-development-branch",
103
+ description: "Guide completion of development work with structured merge/PR options",
104
+ allowedAgents: ["orchestrator"],
105
+ sourcePath: "src/skills/finishing-a-development-branch"
106
+ },
107
+ {
108
+ name: "requesting-code-review",
109
+ description: "Dispatch code reviewer subagent to catch issues before merging",
110
+ allowedAgents: ["orchestrator"],
111
+ sourcePath: "src/skills/requesting-code-review"
112
+ },
113
+ {
114
+ name: "test-driven-development",
115
+ description: "Write failing tests first, then minimal code to pass, before any implementation",
116
+ allowedAgents: ["implementer"],
117
+ sourcePath: "src/skills/test-driven-development"
118
+ },
119
+ {
120
+ name: "verification-before-completion",
121
+ description: "Run verification commands and confirm output before claiming work is complete",
122
+ allowedAgents: ["implementer"],
123
+ sourcePath: "src/skills/verification-before-completion"
124
+ },
125
+ {
126
+ name: "systematic-debugging",
127
+ description: "Find root cause before attempting fixes for any bug or test failure",
128
+ allowedAgents: ["reviewer"],
129
+ sourcePath: "src/skills/systematic-debugging"
130
+ },
131
+ {
132
+ name: "receiving-code-review",
133
+ description: "Evaluate code review feedback with technical rigor before implementing suggestions",
134
+ allowedAgents: ["reviewer"],
135
+ sourcePath: "src/skills/receiving-code-review"
136
+ },
137
+ {
138
+ name: "writing-skills",
139
+ description: "Create and edit skills using TDD methodology for process documentation",
140
+ allowedAgents: ["orchestrator"],
141
+ sourcePath: "src/skills/writing-skills"
142
+ }
143
+ ];
144
+ });
145
+
146
+ // src/cli/paths.ts
147
+ import { homedir } from "node:os";
148
+ import { dirname, join } from "node:path";
149
+ function getDefaultOpenCodeConfigDir() {
150
+ const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
151
+ return join(userConfigDir, "opencode");
152
+ }
153
+ function getCustomOpenCodeConfigDir() {
154
+ const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
155
+ return configDir || undefined;
156
+ }
157
+ function getConfigDir() {
158
+ const customConfigDir = getCustomOpenCodeConfigDir();
159
+ if (customConfigDir) {
160
+ return customConfigDir;
161
+ }
162
+ return getDefaultOpenCodeConfigDir();
163
+ }
164
+ function getConfigSearchDirs() {
165
+ const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
166
+ return dirs.filter((dir, index) => {
167
+ return Boolean(dir) && dirs.indexOf(dir) === index;
168
+ });
169
+ }
170
+ function getOpenCodeConfigPaths() {
171
+ const configDir = getDefaultOpenCodeConfigDir();
172
+ return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
173
+ }
174
+ var init_paths = () => {};
175
+
176
+ // src/utils/package-root.ts
177
+ import { existsSync as existsSync10 } from "node:fs";
178
+ import { join as join13 } from "node:path";
179
+ function resolvePackageRoot() {
180
+ if (_cachedPackageRoot)
181
+ return _cachedPackageRoot;
182
+ const distDir = import.meta.dirname;
183
+ if (!distDir) {
184
+ throw new Error("import.meta.dirname is not available");
185
+ }
186
+ const npmRoot = join13(distDir, "..", "..");
187
+ if (existsSync10(join13(npmRoot, "src", "skills"))) {
188
+ _cachedPackageRoot = npmRoot;
189
+ return npmRoot;
190
+ }
191
+ const localRoot = join13(distDir, "..");
192
+ if (existsSync10(join13(localRoot, "src", "skills"))) {
193
+ _cachedPackageRoot = localRoot;
194
+ return localRoot;
195
+ }
196
+ _cachedPackageRoot = npmRoot;
197
+ return npmRoot;
198
+ }
199
+ var _cachedPackageRoot;
200
+ var init_package_root = () => {};
201
+
35
202
  // node_modules/@mozilla/readability/Readability.js
36
203
  var require_Readability = __commonJS((exports, module) => {
37
204
  function Readability(doc, options) {
@@ -18185,136 +18352,81 @@ var require_turndown_cjs = __commonJS((exports, module) => {
18185
18352
  module.exports = TurndownService;
18186
18353
  });
18187
18354
 
18188
- // src/cli/custom-skills.ts
18189
- import { dirname as dirname2, join as join2 } from "node:path";
18190
-
18191
- // src/cli/paths.ts
18192
- import { homedir } from "node:os";
18193
- import { dirname, join } from "node:path";
18194
- function getDefaultOpenCodeConfigDir() {
18195
- const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
18196
- return join(userConfigDir, "opencode");
18197
- }
18198
- function getCustomOpenCodeConfigDir() {
18199
- const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
18200
- return configDir || undefined;
18201
- }
18202
- function getConfigDir() {
18203
- const customConfigDir = getCustomOpenCodeConfigDir();
18204
- if (customConfigDir) {
18205
- return customConfigDir;
18355
+ // src/cli/skill-sync.ts
18356
+ var exports_skill_sync = {};
18357
+ __export(exports_skill_sync, {
18358
+ syncBuiltinSkills: () => syncBuiltinSkills
18359
+ });
18360
+ import {
18361
+ cpSync,
18362
+ existsSync as existsSync13,
18363
+ lstatSync,
18364
+ mkdirSync as mkdirSync7,
18365
+ readlinkSync,
18366
+ rmSync as rmSync2,
18367
+ symlinkSync
18368
+ } from "node:fs";
18369
+ import { join as join15 } from "node:path";
18370
+ function syncBuiltinSkills(packageRoot, skillsDirOverride) {
18371
+ const resolvedPackageRoot = packageRoot ?? resolvePackageRoot();
18372
+ const skillsDir = skillsDirOverride ?? join15(getConfigDir(), "skills");
18373
+ mkdirSync7(skillsDir, { recursive: true });
18374
+ let installed = 0;
18375
+ let skipped = 0;
18376
+ let unchanged = 0;
18377
+ let copied = 0;
18378
+ for (const skill of CUSTOM_SKILLS) {
18379
+ const sourcePath = join15(resolvedPackageRoot, skill.sourcePath);
18380
+ const linkPath = join15(skillsDir, skill.name);
18381
+ if (!existsSync13(sourcePath)) {
18382
+ skipped++;
18383
+ continue;
18384
+ }
18385
+ try {
18386
+ if (lstatSync(linkPath).isSymbolicLink()) {
18387
+ const currentTarget = getSymlinkTarget(linkPath);
18388
+ if (currentTarget === sourcePath) {
18389
+ unchanged++;
18390
+ continue;
18391
+ }
18392
+ rmSync2(linkPath, { recursive: true, force: true });
18393
+ } else if (existsSync13(linkPath)) {
18394
+ rmSync2(linkPath, { recursive: true, force: true });
18395
+ }
18396
+ } catch {
18397
+ try {
18398
+ rmSync2(linkPath, { recursive: true, force: true });
18399
+ } catch {}
18400
+ }
18401
+ try {
18402
+ symlinkSync(sourcePath, linkPath, "junction");
18403
+ installed++;
18404
+ } catch {
18405
+ try {
18406
+ cpSync(sourcePath, linkPath, { recursive: true, force: true });
18407
+ copied++;
18408
+ } catch {
18409
+ skipped++;
18410
+ }
18411
+ }
18206
18412
  }
18207
- return getDefaultOpenCodeConfigDir();
18208
- }
18209
- function getConfigSearchDirs() {
18210
- const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
18211
- return dirs.filter((dir, index) => {
18212
- return Boolean(dir) && dirs.indexOf(dir) === index;
18213
- });
18413
+ return { installed, skipped, unchanged, copied };
18214
18414
  }
18215
- function getOpenCodeConfigPaths() {
18216
- const configDir = getDefaultOpenCodeConfigDir();
18217
- return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
18218
- }
18219
-
18220
- // src/cli/custom-skills.ts
18221
- var CUSTOM_SKILLS = [
18222
- {
18223
- name: "simplify",
18224
- description: "Code simplification and readability-focused refactoring",
18225
- allowedAgents: ["reviewer"],
18226
- sourcePath: "src/skills/simplify"
18227
- },
18228
- {
18229
- name: "codemap",
18230
- description: "Repository understanding and hierarchical codemap generation",
18231
- allowedAgents: ["orchestrator"],
18232
- sourcePath: "src/skills/codemap"
18233
- },
18234
- {
18235
- name: "brainstorming",
18236
- description: "Turn ideas into designs through collaborative dialogue before implementation",
18237
- allowedAgents: ["orchestrator"],
18238
- sourcePath: "src/skills/brainstorming"
18239
- },
18240
- {
18241
- name: "writing-plans",
18242
- description: "Create comprehensive implementation plans from specs before touching code",
18243
- allowedAgents: ["orchestrator"],
18244
- sourcePath: "src/skills/writing-plans"
18245
- },
18246
- {
18247
- name: "executing-plans",
18248
- description: "Execute written implementation plans with review checkpoints",
18249
- allowedAgents: ["orchestrator"],
18250
- sourcePath: "src/skills/executing-plans"
18251
- },
18252
- {
18253
- name: "subagent-driven-development",
18254
- description: "Execute plans by dispatching fresh subagent per task with two-stage review",
18255
- allowedAgents: ["orchestrator"],
18256
- sourcePath: "src/skills/subagent-driven-development"
18257
- },
18258
- {
18259
- name: "dispatching-parallel-agents",
18260
- description: "Dispatch parallel agents for independent tasks without shared state",
18261
- allowedAgents: ["orchestrator"],
18262
- sourcePath: "src/skills/dispatching-parallel-agents"
18263
- },
18264
- {
18265
- name: "using-git-worktrees",
18266
- description: "Create isolated git worktrees before executing implementation plans",
18267
- allowedAgents: ["orchestrator"],
18268
- sourcePath: "src/skills/using-git-worktrees"
18269
- },
18270
- {
18271
- name: "finishing-a-development-branch",
18272
- description: "Guide completion of development work with structured merge/PR options",
18273
- allowedAgents: ["orchestrator"],
18274
- sourcePath: "src/skills/finishing-a-development-branch"
18275
- },
18276
- {
18277
- name: "requesting-code-review",
18278
- description: "Dispatch code reviewer subagent to catch issues before merging",
18279
- allowedAgents: ["orchestrator"],
18280
- sourcePath: "src/skills/requesting-code-review"
18281
- },
18282
- {
18283
- name: "test-driven-development",
18284
- description: "Write failing tests first, then minimal code to pass, before any implementation",
18285
- allowedAgents: ["implementer"],
18286
- sourcePath: "src/skills/test-driven-development"
18287
- },
18288
- {
18289
- name: "verification-before-completion",
18290
- description: "Run verification commands and confirm output before claiming work is complete",
18291
- allowedAgents: ["implementer"],
18292
- sourcePath: "src/skills/verification-before-completion"
18293
- },
18294
- {
18295
- name: "systematic-debugging",
18296
- description: "Find root cause before attempting fixes for any bug or test failure",
18297
- allowedAgents: ["reviewer"],
18298
- sourcePath: "src/skills/systematic-debugging"
18299
- },
18300
- {
18301
- name: "receiving-code-review",
18302
- description: "Evaluate code review feedback with technical rigor before implementing suggestions",
18303
- allowedAgents: ["reviewer"],
18304
- sourcePath: "src/skills/receiving-code-review"
18305
- },
18306
- {
18307
- name: "writing-skills",
18308
- description: "Create and edit skills using TDD methodology for process documentation",
18309
- allowedAgents: ["orchestrator"],
18310
- sourcePath: "src/skills/writing-skills"
18415
+ function getSymlinkTarget(linkPath) {
18416
+ try {
18417
+ return readlinkSync(linkPath, "utf-8");
18418
+ } catch {
18419
+ return "";
18311
18420
  }
18312
- ];
18313
- function getCustomSkillsDir() {
18314
- return join2(getConfigDir(), "skills");
18315
18421
  }
18422
+ var init_skill_sync = __esm(() => {
18423
+ init_package_root();
18424
+ init_custom_skills();
18425
+ init_paths();
18426
+ });
18316
18427
 
18317
18428
  // src/cli/skills.ts
18429
+ init_custom_skills();
18318
18430
  var RECOMMENDED_SKILLS = [
18319
18431
  {
18320
18432
  name: "agent-browser",
@@ -18474,6 +18586,9 @@ var CouncilConfigSchema = z.object({
18474
18586
  import * as fs from "node:fs";
18475
18587
  import * as path from "node:path";
18476
18588
 
18589
+ // src/cli/config-io.ts
18590
+ init_paths();
18591
+
18477
18592
  // src/config/agent-mcps.ts
18478
18593
  var DEFAULT_AGENT_MCPS = {
18479
18594
  orchestrator: ["*", "!context7"],
@@ -18506,6 +18621,9 @@ function getAgentMcpList(agentName, config) {
18506
18621
  return defaultMcps ?? [];
18507
18622
  }
18508
18623
 
18624
+ // src/cli/providers.ts
18625
+ init_custom_skills();
18626
+
18509
18627
  // src/cli/config-io.ts
18510
18628
  function stripJsonComments(json) {
18511
18629
  const commentPattern = /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g;
@@ -18513,6 +18631,9 @@ function stripJsonComments(json) {
18513
18631
  return json.replace(commentPattern, (match, commentGroup) => commentGroup ? "" : match).replace(trailingCommaPattern, (match, comma, closing) => comma ? closing : match);
18514
18632
  }
18515
18633
 
18634
+ // src/config/loader.ts
18635
+ init_paths();
18636
+
18516
18637
  // src/config/schema.ts
18517
18638
  import { z as z2 } from "zod";
18518
18639
  var ProviderModelIdSchema = z2.string().regex(/^[^/\s]+\/[^\s]+$/, "Expected provider/model format (provider/.../model)");
@@ -21837,6 +21958,10 @@ async function crossWrite(path6, data) {
21837
21958
  // src/hooks/auto-update-checker/cache.ts
21838
21959
  import * as fs5 from "node:fs";
21839
21960
  import * as path8 from "node:path";
21961
+
21962
+ // src/cli/config-manager.ts
21963
+ init_paths();
21964
+
21840
21965
  // src/hooks/auto-update-checker/checker.ts
21841
21966
  import * as fs4 from "node:fs";
21842
21967
  import * as path7 from "node:path";
@@ -22808,8 +22933,22 @@ ${buildRetryGuidance(detected)}`;
22808
22933
  };
22809
22934
  }
22810
22935
  // src/hooks/filter-available-skills/index.ts
22936
+ init_custom_skills();
22811
22937
  var AVAILABLE_SKILLS_BLOCK_REGEX = /<available_skills>\s*([\s\S]*?)\s*<\/available_skills>/g;
22812
22938
  var SKILL_NAME_REGEX = /<name>([^<]+)<\/name>/;
22939
+ function builtinSkillBlock(name, description) {
22940
+ return `<skill>
22941
+ <name>${name}</name>
22942
+ <description>${description}</description>
22943
+ <location>builtin</location>
22944
+ </skill>`;
22945
+ }
22946
+ function getBuiltinSkillEntries() {
22947
+ return CUSTOM_SKILLS.map((skill) => ({
22948
+ name: skill.name,
22949
+ block: builtinSkillBlock(skill.name, skill.description)
22950
+ }));
22951
+ }
22813
22952
  function getCurrentAgent(messages) {
22814
22953
  for (let index = messages.length - 1;index >= 0; index -= 1) {
22815
22954
  const message = messages[index];
@@ -22842,9 +22981,21 @@ function isSkillAllowed(skillName, permissionRules) {
22842
22981
  }
22843
22982
  return permissionRules["*"] === "allow";
22844
22983
  }
22984
+ function mergeWithBuiltinSkills(installedEntries) {
22985
+ const builtinEntries = getBuiltinSkillEntries();
22986
+ const installedNames = new Set(installedEntries.map((e) => e.name));
22987
+ for (const entry of builtinEntries) {
22988
+ if (!installedNames.has(entry.name)) {
22989
+ installedEntries.push(entry);
22990
+ }
22991
+ }
22992
+ return installedEntries;
22993
+ }
22845
22994
  function filterAvailableSkillsText(text, permissionRules) {
22846
22995
  return text.replace(AVAILABLE_SKILLS_BLOCK_REGEX, (_fullMatch, blockContent) => {
22847
- const allowedEntries = extractSkillEntries(blockContent).filter((entry) => isSkillAllowed(entry.name, permissionRules));
22996
+ let allEntries = extractSkillEntries(blockContent);
22997
+ allEntries = mergeWithBuiltinSkills(allEntries);
22998
+ const allowedEntries = allEntries.filter((entry) => isSkillAllowed(entry.name, permissionRules));
22848
22999
  if (allowedEntries.length === 0) {
22849
23000
  return `<available_skills>
22850
23001
  No skills available.
@@ -22856,6 +23007,22 @@ ${allowedEntries.map((entry) => entry.block).join(`
22856
23007
  </available_skills>`;
22857
23008
  });
22858
23009
  }
23010
+ function injectBuiltinSkillsIfMissing(text, permissionRules) {
23011
+ if (text.includes("<available_skills>")) {
23012
+ return text;
23013
+ }
23014
+ const builtinEntries = getBuiltinSkillEntries();
23015
+ const allowedEntries = builtinEntries.filter((entry) => isSkillAllowed(entry.name, permissionRules));
23016
+ if (allowedEntries.length === 0) {
23017
+ return text;
23018
+ }
23019
+ const block = `<available_skills>
23020
+ ${allowedEntries.map((entry) => entry.block).join(`
23021
+ `)}
23022
+ </available_skills>`;
23023
+ return `${text}
23024
+ ${block}`;
23025
+ }
22859
23026
  function createFilterAvailableSkillsHook(_ctx, config) {
22860
23027
  const permissionRulesByAgent = new Map;
22861
23028
  const getPermissionRules = (agentName) => {
@@ -22878,10 +23045,14 @@ function createFilterAvailableSkillsHook(_ctx, config) {
22878
23045
  const permissionRules = getPermissionRules(agentName);
22879
23046
  for (const message of messages) {
22880
23047
  for (const part of message.parts) {
22881
- if (part.type !== "text" || !part.text || !part.text.includes("<available_skills>")) {
23048
+ if (part.type !== "text" || !part.text) {
22882
23049
  continue;
22883
23050
  }
22884
- part.text = filterAvailableSkillsText(part.text, permissionRules);
23051
+ if (part.text.includes("<available_skills>")) {
23052
+ part.text = filterAvailableSkillsText(part.text, permissionRules);
23053
+ } else {
23054
+ part.text = injectBuiltinSkillsIfMissing(part.text, permissionRules);
23055
+ }
22885
23056
  }
22886
23057
  }
22887
23058
  }
@@ -23123,7 +23294,7 @@ import {
23123
23294
  unlinkSync as unlinkSync2,
23124
23295
  writeFileSync as writeFileSync3
23125
23296
  } from "node:fs";
23126
- import { basename as basename2, extname, join as join8 } from "node:path";
23297
+ import { basename as basename2, extname, join as join7 } from "node:path";
23127
23298
  var lastCleanupByDir = new Map;
23128
23299
  var CLEANUP_INTERVAL = 10 * 60 * 1000;
23129
23300
  function isImagePart(p) {
@@ -23171,7 +23342,7 @@ function cleanupAllSessions(saveDir) {
23171
23342
  const dirsToScan = [];
23172
23343
  try {
23173
23344
  for (const entry of readdirSync2(saveDir, { withFileTypes: true })) {
23174
- const fp = join8(saveDir, entry.name);
23345
+ const fp = join7(saveDir, entry.name);
23175
23346
  if (entry.isDirectory()) {
23176
23347
  dirsToScan.push(fp);
23177
23348
  } else {
@@ -23188,7 +23359,7 @@ function cleanupAllSessions(saveDir) {
23188
23359
  let allRemoved = true;
23189
23360
  for (const f of readdirSync2(dir)) {
23190
23361
  isEmpty = false;
23191
- const fp = join8(dir, f);
23362
+ const fp = join7(dir, f);
23192
23363
  try {
23193
23364
  if (now - statSync3(fp).mtimeMs > maxAge) {
23194
23365
  unlinkSync2(fp);
@@ -23210,7 +23381,7 @@ function cleanupAllSessions(saveDir) {
23210
23381
  function writeUniqueFile(dir, name, data, log2) {
23211
23382
  const ext = extname(name);
23212
23383
  const base = basename2(name, ext) || name;
23213
- let candidate = join8(dir, name);
23384
+ let candidate = join7(dir, name);
23214
23385
  if (existsSync5(candidate)) {
23215
23386
  return candidate;
23216
23387
  }
@@ -23223,7 +23394,7 @@ function writeUniqueFile(dir, name, data, log2) {
23223
23394
  } catch (e) {
23224
23395
  if (e instanceof Error && e.code === "EEXIST") {
23225
23396
  counter += 1;
23226
- candidate = join8(dir, `${base}-${counter}${ext}`);
23397
+ candidate = join7(dir, `${base}-${counter}${ext}`);
23227
23398
  continue;
23228
23399
  }
23229
23400
  log2(`[image-hook] failed to save image: ${e}`);
@@ -23247,13 +23418,13 @@ function processImageAttachments(args) {
23247
23418
  messagesWithImages.push({ msg, imageParts });
23248
23419
  }
23249
23420
  }
23250
- const saveDir = join8(workDir, ".opencode", "images");
23421
+ const saveDir = join7(workDir, ".opencode", "images");
23251
23422
  if (messagesWithImages.length === 0) {
23252
23423
  if (existsSync5(saveDir))
23253
23424
  cleanupAllSessions(saveDir);
23254
23425
  return;
23255
23426
  }
23256
- const gitignorePath = join8(workDir, ".opencode", ".gitignore");
23427
+ const gitignorePath = join7(workDir, ".opencode", ".gitignore");
23257
23428
  try {
23258
23429
  mkdirSync3(saveDir, { recursive: true });
23259
23430
  if (!existsSync5(gitignorePath))
@@ -23265,7 +23436,7 @@ function processImageAttachments(args) {
23265
23436
  cleanupAllSessions(saveDir);
23266
23437
  for (const { msg, imageParts } of messagesWithImages) {
23267
23438
  const sessionSubdir = msg.info.sessionID ? sanitizeFilename(msg.info.sessionID) : undefined;
23268
- const targetDir = sessionSubdir ? join8(saveDir, sessionSubdir) : saveDir;
23439
+ const targetDir = sessionSubdir ? join7(saveDir, sessionSubdir) : saveDir;
23269
23440
  try {
23270
23441
  mkdirSync3(targetDir, { recursive: true });
23271
23442
  } catch (e) {
@@ -29236,13 +29407,13 @@ import { existsSync as existsSync9 } from "node:fs";
29236
29407
  // src/tools/ast-grep/constants.ts
29237
29408
  import { existsSync as existsSync8, statSync as statSync4 } from "node:fs";
29238
29409
  import { createRequire as createRequire3 } from "node:module";
29239
- import { dirname as dirname7, join as join12 } from "node:path";
29410
+ import { dirname as dirname6, join as join11 } from "node:path";
29240
29411
 
29241
29412
  // src/tools/ast-grep/downloader.ts
29242
29413
  import { chmodSync, existsSync as existsSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "node:fs";
29243
29414
  import { createRequire as createRequire2 } from "node:module";
29244
29415
  import { homedir as homedir5 } from "node:os";
29245
- import { join as join11 } from "node:path";
29416
+ import { join as join10 } from "node:path";
29246
29417
  var REPO = "ast-grep/ast-grep";
29247
29418
  var DEFAULT_VERSION = "0.40.0";
29248
29419
  function getAstGrepVersion() {
@@ -29266,18 +29437,18 @@ var PLATFORM_MAP = {
29266
29437
  function getCacheDir2() {
29267
29438
  if (process.platform === "win32") {
29268
29439
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
29269
- const base2 = localAppData || join11(homedir5(), "AppData", "Local");
29270
- return join11(base2, "agent-forge", "bin");
29440
+ const base2 = localAppData || join10(homedir5(), "AppData", "Local");
29441
+ return join10(base2, "agent-forge", "bin");
29271
29442
  }
29272
29443
  const xdgCache = process.env.XDG_CACHE_HOME;
29273
- const base = xdgCache || join11(homedir5(), ".cache");
29274
- return join11(base, "agent-forge", "bin");
29444
+ const base = xdgCache || join10(homedir5(), ".cache");
29445
+ return join10(base, "agent-forge", "bin");
29275
29446
  }
29276
29447
  function getBinaryName() {
29277
29448
  return process.platform === "win32" ? "sg.exe" : "sg";
29278
29449
  }
29279
29450
  function getCachedBinaryPath() {
29280
- const binaryPath = join11(getCacheDir2(), getBinaryName());
29451
+ const binaryPath = join10(getCacheDir2(), getBinaryName());
29281
29452
  return existsSync7(binaryPath) ? binaryPath : null;
29282
29453
  }
29283
29454
  async function downloadAstGrep(version = DEFAULT_VERSION) {
@@ -29289,7 +29460,7 @@ async function downloadAstGrep(version = DEFAULT_VERSION) {
29289
29460
  }
29290
29461
  const cacheDir = getCacheDir2();
29291
29462
  const binaryName = getBinaryName();
29292
- const binaryPath = join11(cacheDir, binaryName);
29463
+ const binaryPath = join10(cacheDir, binaryName);
29293
29464
  if (existsSync7(binaryPath)) {
29294
29465
  return binaryPath;
29295
29466
  }
@@ -29305,7 +29476,7 @@ async function downloadAstGrep(version = DEFAULT_VERSION) {
29305
29476
  if (!response.ok) {
29306
29477
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
29307
29478
  }
29308
- const archivePath = join11(cacheDir, assetName);
29479
+ const archivePath = join10(cacheDir, assetName);
29309
29480
  const arrayBuffer = await response.arrayBuffer();
29310
29481
  await crossWrite(archivePath, arrayBuffer);
29311
29482
  await extractZip(archivePath, cacheDir);
@@ -29393,8 +29564,8 @@ function findSgCliPathSync() {
29393
29564
  try {
29394
29565
  const require2 = createRequire3(import.meta.url);
29395
29566
  const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
29396
- const cliDir = dirname7(cliPkgPath);
29397
- const sgPath = join12(cliDir, binaryName);
29567
+ const cliDir = dirname6(cliPkgPath);
29568
+ const sgPath = join11(cliDir, binaryName);
29398
29569
  if (existsSync8(sgPath) && isValidBinary(sgPath)) {
29399
29570
  return sgPath;
29400
29571
  }
@@ -29404,9 +29575,9 @@ function findSgCliPathSync() {
29404
29575
  try {
29405
29576
  const require2 = createRequire3(import.meta.url);
29406
29577
  const pkgPath = require2.resolve(`${platformPkg}/package.json`);
29407
- const pkgDir = dirname7(pkgPath);
29578
+ const pkgDir = dirname6(pkgPath);
29408
29579
  const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
29409
- const binaryPath = join12(pkgDir, astGrepName);
29580
+ const binaryPath = join11(pkgDir, astGrepName);
29410
29581
  if (existsSync8(binaryPath) && isValidBinary(binaryPath)) {
29411
29582
  return binaryPath;
29412
29583
  }
@@ -30072,18 +30243,15 @@ Usage: /preset <name> to switch.`);
30072
30243
  };
30073
30244
  }
30074
30245
  // src/tools/skill.ts
30075
- import { existsSync as existsSync10, readFileSync as readFileSync5 } from "node:fs";
30246
+ import { existsSync as existsSync11, readFileSync as readFileSync5 } from "node:fs";
30076
30247
  import { join as join14 } from "node:path";
30077
30248
  import { tool as tool4 } from "@opencode-ai/plugin/tool";
30249
+ init_package_root();
30078
30250
  var z5 = tool4.schema;
30079
30251
  function resolveSkillPath(skillName) {
30080
- const installedPath = join14(getCustomSkillsDir(), skillName, "SKILL.md");
30081
- if (existsSync10(installedPath)) {
30082
- return installedPath;
30083
- }
30084
- const packageRoot = join14(import.meta.dirname, "..", "..");
30252
+ const packageRoot = resolvePackageRoot();
30085
30253
  const bundledPath = join14(packageRoot, "src", "skills", skillName, "SKILL.md");
30086
- if (existsSync10(bundledPath)) {
30254
+ if (existsSync11(bundledPath)) {
30087
30255
  return bundledPath;
30088
30256
  }
30089
30257
  return;
@@ -31739,9 +31907,10 @@ function isInvalidLlmsResult(fetchResult) {
31739
31907
  }
31740
31908
 
31741
31909
  // src/tools/smartfetch/secondary-model.ts
31742
- import { existsSync as existsSync11 } from "node:fs";
31910
+ import { existsSync as existsSync12 } from "node:fs";
31743
31911
  import { readFile as readFile4 } from "node:fs/promises";
31744
31912
  import path17 from "node:path";
31913
+ init_paths();
31745
31914
  function parseModelRef(value) {
31746
31915
  if (!value)
31747
31916
  return;
@@ -31768,7 +31937,7 @@ function pickAgentModelRef(value) {
31768
31937
  function findPreferredOpenCodeConfigPath(baseDir) {
31769
31938
  for (const file of ["opencode.jsonc", "opencode.json"]) {
31770
31939
  const fullPath = path17.join(baseDir, file);
31771
- if (existsSync11(fullPath))
31940
+ if (existsSync12(fullPath))
31772
31941
  return fullPath;
31773
31942
  }
31774
31943
  return;
@@ -32610,6 +32779,15 @@ var OhMyOpenCodeLite = async (ctx) => {
32610
32779
  mcps = createBuiltinMcps(config.disabled_mcps, config.websearch);
32611
32780
  webfetch = createWebfetchTool(ctx);
32612
32781
  skillTools = createSkillTool(config);
32782
+ try {
32783
+ const { syncBuiltinSkills: syncBuiltinSkills2 } = await Promise.resolve().then(() => (init_skill_sync(), exports_skill_sync));
32784
+ const syncResult = syncBuiltinSkills2();
32785
+ if (syncResult.installed > 0 || syncResult.skipped > 0) {
32786
+ log("[plugin] skill sync:", syncResult);
32787
+ }
32788
+ } catch (err) {
32789
+ log("[plugin] skill sync failed (non-fatal):", err);
32790
+ }
32613
32791
  multiplexerSessionManager = new MultiplexerSessionManager(ctx, multiplexerConfig);
32614
32792
  autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
32615
32793
  autoUpdate: config.autoUpdate ?? true
package/dist/tui.js CHANGED
@@ -30,6 +30,20 @@ var __toESM = (mod, isNodeMode, target) => {
30
30
  return to;
31
31
  };
32
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
37
+ var __export = (target, all) => {
38
+ for (var name in all)
39
+ __defProp(target, name, {
40
+ get: all[name],
41
+ enumerable: true,
42
+ configurable: true,
43
+ set: __exportSetter.bind(all, name)
44
+ });
45
+ };
46
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
33
47
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
34
48
 
35
49
  // src/tui.ts
@@ -0,0 +1 @@
1
+ export declare function resolvePackageRoot(): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zcy2nn/agent-forge",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",