open-agents-ai 0.12.16 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +487 -144
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4489,6 +4489,286 @@ var init_tool_creator = __esm({
4489
4489
  }
4490
4490
  });
4491
4491
 
4492
+ // packages/execution/dist/tools/skill-tools.js
4493
+ import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync8 } from "node:fs";
4494
+ import { join as join12, basename as basename2 } from "node:path";
4495
+ import { homedir as homedir4 } from "node:os";
4496
+ function discoverSkills(repoRoot) {
4497
+ const skills = /* @__PURE__ */ new Map();
4498
+ if (existsSync9(AIWG_FRAMEWORKS_DIR)) {
4499
+ for (const framework of safeReaddir(AIWG_FRAMEWORKS_DIR)) {
4500
+ const skillsDir = join12(AIWG_FRAMEWORKS_DIR, framework, "skills");
4501
+ loadSkillsFromDir(skillsDir, `framework:${framework}`, skills);
4502
+ }
4503
+ }
4504
+ if (existsSync9(AIWG_ADDONS_DIR)) {
4505
+ for (const addon of safeReaddir(AIWG_ADDONS_DIR)) {
4506
+ const skillsDir = join12(AIWG_ADDONS_DIR, addon, "skills");
4507
+ loadSkillsFromDir(skillsDir, `addon:${addon}`, skills);
4508
+ }
4509
+ }
4510
+ if (existsSync9(AIWG_PLUGINS_DIR)) {
4511
+ for (const plugin of safeReaddir(AIWG_PLUGINS_DIR)) {
4512
+ const skillsDir = join12(AIWG_PLUGINS_DIR, plugin, "skills");
4513
+ loadSkillsFromDir(skillsDir, `plugin:${plugin}`, skills);
4514
+ }
4515
+ }
4516
+ const projectAiwgSkills = join12(repoRoot, ".aiwg", "skills");
4517
+ loadSkillsFromDir(projectAiwgSkills, "project", skills);
4518
+ const projectOaSkills = join12(repoRoot, ".oa", "skills");
4519
+ loadSkillsFromDir(projectOaSkills, "local", skills);
4520
+ return Array.from(skills.values());
4521
+ }
4522
+ function loadSkillContent(skillPath) {
4523
+ try {
4524
+ return readFileSync8(skillPath, "utf-8");
4525
+ } catch {
4526
+ return null;
4527
+ }
4528
+ }
4529
+ function buildSkillsSummary(skills) {
4530
+ if (skills.length === 0)
4531
+ return "";
4532
+ const lines = [
4533
+ "## Available Skills",
4534
+ "",
4535
+ "You have access to skills via the `skill_list` and `skill_execute` tools.",
4536
+ "Skills provide detailed behavioral guidance for specific tasks.",
4537
+ ""
4538
+ ];
4539
+ const bySource = /* @__PURE__ */ new Map();
4540
+ for (const s of skills) {
4541
+ const group = bySource.get(s.source) ?? [];
4542
+ group.push(s);
4543
+ bySource.set(s.source, group);
4544
+ }
4545
+ for (const [source, group] of bySource) {
4546
+ lines.push(`### ${source} (${group.length} skills)`);
4547
+ for (const s of group) {
4548
+ const triggers = s.triggers.length > 0 ? ` \u2014 triggers: ${s.triggers.slice(0, 3).join(", ")}` : "";
4549
+ lines.push(`- **${s.name}**: ${s.description}${triggers}`);
4550
+ }
4551
+ lines.push("");
4552
+ }
4553
+ lines.push("Use `skill_execute` with a skill name to load its full instructions when a task matches a trigger pattern.");
4554
+ return lines.join("\n");
4555
+ }
4556
+ function safeReaddir(dir) {
4557
+ try {
4558
+ return readdirSync5(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
4559
+ } catch {
4560
+ return [];
4561
+ }
4562
+ }
4563
+ function loadSkillsFromDir(dir, source, out) {
4564
+ if (!existsSync9(dir))
4565
+ return;
4566
+ const manifest = loadManifest(dir);
4567
+ const entries = safeReaddir(dir);
4568
+ for (const entry of entries) {
4569
+ const skillMd = join12(dir, entry, "SKILL.md");
4570
+ if (!existsSync9(skillMd))
4571
+ continue;
4572
+ const manifestEntry = manifest.get(entry);
4573
+ const info = {
4574
+ name: entry,
4575
+ description: manifestEntry?.description ?? parseDescription(skillMd),
4576
+ triggers: manifestEntry?.triggers ?? parseTriggers(skillMd),
4577
+ source,
4578
+ filePath: skillMd
4579
+ };
4580
+ out.set(entry, info);
4581
+ }
4582
+ }
4583
+ function loadManifest(dir) {
4584
+ const result = /* @__PURE__ */ new Map();
4585
+ const manifestPath = join12(dir, "manifest.json");
4586
+ if (!existsSync9(manifestPath))
4587
+ return result;
4588
+ try {
4589
+ const content = readFileSync8(manifestPath, "utf-8");
4590
+ const data = JSON.parse(content);
4591
+ if (Array.isArray(data.skills)) {
4592
+ for (const s of data.skills) {
4593
+ if (s.name) {
4594
+ result.set(s.name, s);
4595
+ }
4596
+ }
4597
+ }
4598
+ } catch {
4599
+ }
4600
+ return result;
4601
+ }
4602
+ function parseDescription(filePath) {
4603
+ try {
4604
+ const content = readFileSync8(filePath, "utf-8");
4605
+ const lines = content.split("\n");
4606
+ let pastHeading = false;
4607
+ for (const line of lines) {
4608
+ if (line.startsWith("# ")) {
4609
+ pastHeading = true;
4610
+ continue;
4611
+ }
4612
+ if (pastHeading && line.trim()) {
4613
+ return line.trim().slice(0, 200);
4614
+ }
4615
+ }
4616
+ } catch {
4617
+ }
4618
+ return "";
4619
+ }
4620
+ function parseTriggers(filePath) {
4621
+ const triggers = [];
4622
+ try {
4623
+ const content = readFileSync8(filePath, "utf-8");
4624
+ const lines = content.split("\n");
4625
+ let inTriggers = false;
4626
+ for (const line of lines) {
4627
+ if (/^##\s+Triggers/i.test(line)) {
4628
+ inTriggers = true;
4629
+ continue;
4630
+ }
4631
+ if (inTriggers && line.startsWith("## ")) {
4632
+ break;
4633
+ }
4634
+ if (inTriggers && line.startsWith("- ")) {
4635
+ const trigger = line.slice(2).replace(/^["']|["']$/g, "").trim();
4636
+ if (trigger)
4637
+ triggers.push(trigger);
4638
+ }
4639
+ }
4640
+ } catch {
4641
+ }
4642
+ return triggers;
4643
+ }
4644
+ var AIWG_DATA_DIR, AIWG_FRAMEWORKS_DIR, AIWG_ADDONS_DIR, AIWG_PLUGINS_DIR, SkillListTool, SkillExecuteTool;
4645
+ var init_skill_tools = __esm({
4646
+ "packages/execution/dist/tools/skill-tools.js"() {
4647
+ "use strict";
4648
+ AIWG_DATA_DIR = join12(homedir4(), ".local", "share", "ai-writing-guide");
4649
+ AIWG_FRAMEWORKS_DIR = join12(AIWG_DATA_DIR, "agentic", "code", "frameworks");
4650
+ AIWG_ADDONS_DIR = join12(AIWG_DATA_DIR, "addons");
4651
+ AIWG_PLUGINS_DIR = join12(AIWG_DATA_DIR, "plugins");
4652
+ SkillListTool = class {
4653
+ name = "skill_list";
4654
+ description = "List all available AIWG skills with their descriptions and trigger patterns. Use this to discover skills that can help with the current task.";
4655
+ parameters = {
4656
+ type: "object",
4657
+ properties: {
4658
+ filter: {
4659
+ type: "string",
4660
+ description: "Optional keyword to filter skills by name or description"
4661
+ },
4662
+ source: {
4663
+ type: "string",
4664
+ description: "Optional source filter (e.g. 'framework:sdlc-complete', 'addon:ralph', 'project', 'local')"
4665
+ }
4666
+ },
4667
+ required: []
4668
+ };
4669
+ repoRoot;
4670
+ constructor(repoRoot) {
4671
+ this.repoRoot = repoRoot;
4672
+ }
4673
+ async execute(args) {
4674
+ const start = performance.now();
4675
+ const filter = typeof args["filter"] === "string" ? args["filter"].toLowerCase() : "";
4676
+ const sourceFilter = typeof args["source"] === "string" ? args["source"] : "";
4677
+ let skills = discoverSkills(this.repoRoot);
4678
+ if (filter) {
4679
+ skills = skills.filter((s) => s.name.toLowerCase().includes(filter) || s.description.toLowerCase().includes(filter) || s.triggers.some((t) => t.toLowerCase().includes(filter)));
4680
+ }
4681
+ if (sourceFilter) {
4682
+ skills = skills.filter((s) => s.source.includes(sourceFilter));
4683
+ }
4684
+ if (skills.length === 0) {
4685
+ return {
4686
+ success: true,
4687
+ output: filter || sourceFilter ? `No skills found matching filter "${filter || sourceFilter}".` : "No skills found. Install AIWG (npm i -g aiwg && aiwg use sdlc) or add skills to .oa/skills/.",
4688
+ durationMs: performance.now() - start
4689
+ };
4690
+ }
4691
+ const lines = [`Found ${skills.length} skills:
4692
+ `];
4693
+ for (const s of skills) {
4694
+ lines.push(` ${s.name}`);
4695
+ lines.push(` ${s.description}`);
4696
+ if (s.triggers.length > 0) {
4697
+ lines.push(` Triggers: ${s.triggers.slice(0, 5).join(" | ")}`);
4698
+ }
4699
+ lines.push(` Source: ${s.source}`);
4700
+ lines.push("");
4701
+ }
4702
+ return {
4703
+ success: true,
4704
+ output: lines.join("\n"),
4705
+ durationMs: performance.now() - start
4706
+ };
4707
+ }
4708
+ };
4709
+ SkillExecuteTool = class {
4710
+ name = "skill_execute";
4711
+ description = "Load a skill's full instructions by name. Returns the SKILL.md content which provides detailed behavioral guidance, trigger patterns, CLI commands, and output formats. Use this when the current task matches a skill's trigger pattern.";
4712
+ parameters = {
4713
+ type: "object",
4714
+ properties: {
4715
+ name: {
4716
+ type: "string",
4717
+ description: "Skill name (e.g. 'artifact-lookup', 'test-coverage', 'security-assessment')"
4718
+ }
4719
+ },
4720
+ required: ["name"]
4721
+ };
4722
+ repoRoot;
4723
+ constructor(repoRoot) {
4724
+ this.repoRoot = repoRoot;
4725
+ }
4726
+ async execute(args) {
4727
+ const start = performance.now();
4728
+ const name = String(args["name"] ?? "").trim();
4729
+ if (!name) {
4730
+ return {
4731
+ success: false,
4732
+ output: "",
4733
+ error: "Skill name is required. Use skill_list to see available skills.",
4734
+ durationMs: performance.now() - start
4735
+ };
4736
+ }
4737
+ const skills = discoverSkills(this.repoRoot);
4738
+ const skill = skills.find((s) => s.name === name);
4739
+ if (!skill) {
4740
+ const fuzzy = skills.filter((s) => s.name.includes(name) || name.includes(s.name));
4741
+ const suggestion = fuzzy.length > 0 ? `
4742
+ Did you mean: ${fuzzy.map((s) => s.name).join(", ")}?` : "\nUse skill_list to see available skills.";
4743
+ return {
4744
+ success: false,
4745
+ output: "",
4746
+ error: `Skill "${name}" not found.${suggestion}`,
4747
+ durationMs: performance.now() - start
4748
+ };
4749
+ }
4750
+ const content = loadSkillContent(skill.filePath);
4751
+ if (!content) {
4752
+ return {
4753
+ success: false,
4754
+ output: "",
4755
+ error: `Could not read skill file: ${skill.filePath}`,
4756
+ durationMs: performance.now() - start
4757
+ };
4758
+ }
4759
+ return {
4760
+ success: true,
4761
+ output: `# Skill: ${skill.name}
4762
+ Source: ${skill.source}
4763
+
4764
+ ${content}`,
4765
+ durationMs: performance.now() - start
4766
+ };
4767
+ }
4768
+ };
4769
+ }
4770
+ });
4771
+
4492
4772
  // packages/execution/dist/shellRunner.js
4493
4773
  var init_shellRunner = __esm({
4494
4774
  "packages/execution/dist/shellRunner.js"() {
@@ -4591,6 +4871,7 @@ var init_dist2 = __esm({
4591
4871
  init_image();
4592
4872
  init_custom_tool();
4593
4873
  init_tool_creator();
4874
+ init_skill_tools();
4594
4875
  init_shellRunner();
4595
4876
  init_gitWorktree();
4596
4877
  init_patchApplier();
@@ -5722,7 +6003,7 @@ var init_code_retriever = __esm({
5722
6003
  import { execFile as execFile4 } from "node:child_process";
5723
6004
  import { promisify as promisify4 } from "node:util";
5724
6005
  import { readFile as readFile7, readdir, stat } from "node:fs/promises";
5725
- import { join as join12, extname as extname3 } from "node:path";
6006
+ import { join as join13, extname as extname3 } from "node:path";
5726
6007
  async function searchByPath(pathPattern, options) {
5727
6008
  const allFiles = await collectFiles(options.rootDir, options.includeGlobs ?? DEFAULT_INCLUDE_GLOBS, options.excludeGlobs ?? DEFAULT_EXCLUDE_GLOBS);
5728
6009
  const pattern = options.caseInsensitive ? pathPattern.toLowerCase() : pathPattern;
@@ -5864,7 +6145,7 @@ async function walkForFiles(rootDir, dir, excludeGlobs, results) {
5864
6145
  continue;
5865
6146
  if (excludeGlobs.some((g) => entry.name === g || matchesGlob(entry.name, g)))
5866
6147
  continue;
5867
- const absPath = join12(dir, entry.name);
6148
+ const absPath = join13(dir, entry.name);
5868
6149
  if (entry.isDirectory()) {
5869
6150
  await walkForFiles(rootDir, absPath, excludeGlobs, results);
5870
6151
  } else if (entry.isFile()) {
@@ -6039,7 +6320,7 @@ var init_graphExpand = __esm({
6039
6320
 
6040
6321
  // packages/retrieval/dist/snippetPacker.js
6041
6322
  import { readFile as readFile8 } from "node:fs/promises";
6042
- import { join as join13 } from "node:path";
6323
+ import { join as join14 } from "node:path";
6043
6324
  async function packSnippets(requests, opts = {}) {
6044
6325
  const maxTokens = opts.maxTokens ?? DEFAULT_MAX_TOKENS;
6045
6326
  const contextLines = opts.contextLines ?? DEFAULT_CONTEXT_LINES;
@@ -6065,7 +6346,7 @@ async function packSnippets(requests, opts = {}) {
6065
6346
  return { packed, dropped, totalTokens };
6066
6347
  }
6067
6348
  async function extractSnippet(req, repoRoot, contextLines = DEFAULT_CONTEXT_LINES) {
6068
- const absPath = req.filePath.startsWith("/") ? req.filePath : join13(repoRoot, req.filePath);
6349
+ const absPath = req.filePath.startsWith("/") ? req.filePath : join14(repoRoot, req.filePath);
6069
6350
  let content;
6070
6351
  try {
6071
6352
  content = await readFile8(absPath, "utf-8");
@@ -7247,6 +7528,13 @@ var init_agenticRunner = __esm({
7247
7528
  - task_stop: Kill a running background task
7248
7529
  - sub_agent: Delegate a sub-task to an independent agent with its own context
7249
7530
 
7531
+ ## Skills (AIWG)
7532
+
7533
+ - skill_list: Discover available skills \u2014 shows descriptions and trigger patterns. Use filter param to search.
7534
+ - skill_execute: Load a skill's full instructions by name. Returns the SKILL.md content with detailed behavioral guidance.
7535
+
7536
+ When a user request matches a skill trigger pattern (listed in your context or discovered via skill_list), call skill_execute to load the skill instructions, then follow them.
7537
+
7250
7538
  Use background_run for long-running commands (builds, test suites) so you can continue other work.
7251
7539
  Use sub_agent to parallelize independent sub-tasks or explore different approaches simultaneously.
7252
7540
  Check task_status periodically and read task_output when tasks complete.
@@ -8639,6 +8927,8 @@ function renderSlashHelp() {
8639
8927
  ["/dream stop", "Wake up \u2014 stop dreaming"],
8640
8928
  ["/bruteforce", "Toggle brute-force mode (auto re-engage on turn limit)"],
8641
8929
  ["/tools", "List agent-created custom tools"],
8930
+ ["/skills", "List available AIWG skills"],
8931
+ ["/skills <keyword>", "Filter skills by name or trigger"],
8642
8932
  ["/verbose", "Toggle verbose mode"],
8643
8933
  ["/clear", "Clear the screen"],
8644
8934
  ["/help", "Show this help"],
@@ -8933,52 +9223,52 @@ var init_render = __esm({
8933
9223
  });
8934
9224
 
8935
9225
  // packages/cli/dist/tui/oa-directory.js
8936
- import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync5, statSync as statSync5, unlinkSync } from "node:fs";
8937
- import { join as join14, relative as relative2, basename as basename2, extname as extname4 } from "node:path";
8938
- import { homedir as homedir4 } from "node:os";
9226
+ import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4, readdirSync as readdirSync6, statSync as statSync5, unlinkSync } from "node:fs";
9227
+ import { join as join15, relative as relative2, basename as basename3, extname as extname4 } from "node:path";
9228
+ import { homedir as homedir5 } from "node:os";
8939
9229
  function initOaDirectory(repoRoot) {
8940
- const oaPath = join14(repoRoot, OA_DIR);
9230
+ const oaPath = join15(repoRoot, OA_DIR);
8941
9231
  for (const sub of SUBDIRS) {
8942
- mkdirSync4(join14(oaPath, sub), { recursive: true });
9232
+ mkdirSync4(join15(oaPath, sub), { recursive: true });
8943
9233
  }
8944
9234
  return oaPath;
8945
9235
  }
8946
9236
  function hasOaDirectory(repoRoot) {
8947
- return existsSync9(join14(repoRoot, OA_DIR, "index"));
9237
+ return existsSync10(join15(repoRoot, OA_DIR, "index"));
8948
9238
  }
8949
9239
  function loadProjectSettings(repoRoot) {
8950
- const settingsPath = join14(repoRoot, OA_DIR, "settings.json");
9240
+ const settingsPath = join15(repoRoot, OA_DIR, "settings.json");
8951
9241
  try {
8952
- if (existsSync9(settingsPath)) {
8953
- return JSON.parse(readFileSync8(settingsPath, "utf-8"));
9242
+ if (existsSync10(settingsPath)) {
9243
+ return JSON.parse(readFileSync9(settingsPath, "utf-8"));
8954
9244
  }
8955
9245
  } catch {
8956
9246
  }
8957
9247
  return {};
8958
9248
  }
8959
9249
  function saveProjectSettings(repoRoot, settings) {
8960
- const oaPath = join14(repoRoot, OA_DIR);
9250
+ const oaPath = join15(repoRoot, OA_DIR);
8961
9251
  mkdirSync4(oaPath, { recursive: true });
8962
9252
  const existing = loadProjectSettings(repoRoot);
8963
9253
  const merged = { ...existing, ...settings };
8964
- writeFileSync4(join14(oaPath, "settings.json"), JSON.stringify(merged, null, 2) + "\n", "utf-8");
9254
+ writeFileSync4(join15(oaPath, "settings.json"), JSON.stringify(merged, null, 2) + "\n", "utf-8");
8965
9255
  }
8966
9256
  function loadGlobalSettings() {
8967
- const settingsPath = join14(homedir4(), ".open-agents", "settings.json");
9257
+ const settingsPath = join15(homedir5(), ".open-agents", "settings.json");
8968
9258
  try {
8969
- if (existsSync9(settingsPath)) {
8970
- return JSON.parse(readFileSync8(settingsPath, "utf-8"));
9259
+ if (existsSync10(settingsPath)) {
9260
+ return JSON.parse(readFileSync9(settingsPath, "utf-8"));
8971
9261
  }
8972
9262
  } catch {
8973
9263
  }
8974
9264
  return {};
8975
9265
  }
8976
9266
  function saveGlobalSettings(settings) {
8977
- const dir = join14(homedir4(), ".open-agents");
9267
+ const dir = join15(homedir5(), ".open-agents");
8978
9268
  mkdirSync4(dir, { recursive: true });
8979
9269
  const existing = loadGlobalSettings();
8980
9270
  const merged = { ...existing, ...settings };
8981
- writeFileSync4(join14(dir, "settings.json"), JSON.stringify(merged, null, 2) + "\n", "utf-8");
9271
+ writeFileSync4(join15(dir, "settings.json"), JSON.stringify(merged, null, 2) + "\n", "utf-8");
8982
9272
  }
8983
9273
  function resolveSettings(repoRoot) {
8984
9274
  const global = loadGlobalSettings();
@@ -8993,12 +9283,12 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
8993
9283
  while (dir && !visited.has(dir)) {
8994
9284
  visited.add(dir);
8995
9285
  for (const name of CONTEXT_FILES) {
8996
- const filePath = join14(dir, name);
9286
+ const filePath = join15(dir, name);
8997
9287
  const normalizedName = name.toLowerCase();
8998
- if (existsSync9(filePath) && !seen.has(filePath)) {
9288
+ if (existsSync10(filePath) && !seen.has(filePath)) {
8999
9289
  seen.add(filePath);
9000
9290
  try {
9001
- let content = readFileSync8(filePath, "utf-8");
9291
+ let content = readFileSync9(filePath, "utf-8");
9002
9292
  if (content.length > maxContentLen) {
9003
9293
  content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
9004
9294
  }
@@ -9012,11 +9302,11 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
9012
9302
  }
9013
9303
  }
9014
9304
  }
9015
- const projectMap = join14(dir, OA_DIR, "context", "project-map.md");
9016
- if (existsSync9(projectMap) && !seen.has(projectMap)) {
9305
+ const projectMap = join15(dir, OA_DIR, "context", "project-map.md");
9306
+ if (existsSync10(projectMap) && !seen.has(projectMap)) {
9017
9307
  seen.add(projectMap);
9018
9308
  try {
9019
- let content = readFileSync8(projectMap, "utf-8");
9309
+ let content = readFileSync9(projectMap, "utf-8");
9020
9310
  if (content.length > maxContentLen) {
9021
9311
  content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
9022
9312
  }
@@ -9028,7 +9318,7 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
9028
9318
  } catch {
9029
9319
  }
9030
9320
  }
9031
- const parent = join14(dir, "..");
9321
+ const parent = join15(dir, "..");
9032
9322
  if (parent === dir)
9033
9323
  break;
9034
9324
  dir = parent;
@@ -9046,16 +9336,16 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
9046
9336
  return found;
9047
9337
  }
9048
9338
  function readIndexMeta(repoRoot) {
9049
- const metaPath = join14(repoRoot, OA_DIR, "index", "meta.json");
9339
+ const metaPath = join15(repoRoot, OA_DIR, "index", "meta.json");
9050
9340
  try {
9051
- return JSON.parse(readFileSync8(metaPath, "utf-8"));
9341
+ return JSON.parse(readFileSync9(metaPath, "utf-8"));
9052
9342
  } catch {
9053
9343
  return null;
9054
9344
  }
9055
9345
  }
9056
9346
  function generateProjectMap(repoRoot) {
9057
9347
  const sections = [];
9058
- const repoName2 = basename2(repoRoot);
9348
+ const repoName2 = basename3(repoRoot);
9059
9349
  sections.push(`# Project Map: ${repoName2}
9060
9350
  `);
9061
9351
  sections.push(`> Auto-generated by open-agents. Updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
@@ -9099,28 +9389,28 @@ ${tree}\`\`\`
9099
9389
  sections.push("");
9100
9390
  }
9101
9391
  const content = sections.join("\n");
9102
- const contextDir = join14(repoRoot, OA_DIR, "context");
9392
+ const contextDir = join15(repoRoot, OA_DIR, "context");
9103
9393
  mkdirSync4(contextDir, { recursive: true });
9104
- writeFileSync4(join14(contextDir, "project-map.md"), content, "utf-8");
9394
+ writeFileSync4(join15(contextDir, "project-map.md"), content, "utf-8");
9105
9395
  return content;
9106
9396
  }
9107
9397
  function saveSession(repoRoot, session) {
9108
- const historyDir = join14(repoRoot, OA_DIR, "history");
9398
+ const historyDir = join15(repoRoot, OA_DIR, "history");
9109
9399
  mkdirSync4(historyDir, { recursive: true });
9110
- writeFileSync4(join14(historyDir, `${session.id}.json`), JSON.stringify(session, null, 2), "utf-8");
9400
+ writeFileSync4(join15(historyDir, `${session.id}.json`), JSON.stringify(session, null, 2), "utf-8");
9111
9401
  }
9112
9402
  function loadRecentSessions(repoRoot, limit = 5) {
9113
- const historyDir = join14(repoRoot, OA_DIR, "history");
9114
- if (!existsSync9(historyDir))
9403
+ const historyDir = join15(repoRoot, OA_DIR, "history");
9404
+ if (!existsSync10(historyDir))
9115
9405
  return [];
9116
9406
  try {
9117
- const files = readdirSync5(historyDir).filter((f) => f.endsWith(".json")).map((f) => {
9118
- const stat3 = statSync5(join14(historyDir, f));
9407
+ const files = readdirSync6(historyDir).filter((f) => f.endsWith(".json")).map((f) => {
9408
+ const stat3 = statSync5(join15(historyDir, f));
9119
9409
  return { file: f, mtime: stat3.mtimeMs };
9120
9410
  }).sort((a, b) => b.mtime - a.mtime).slice(0, limit);
9121
9411
  return files.map((f) => {
9122
9412
  try {
9123
- return JSON.parse(readFileSync8(join14(historyDir, f.file), "utf-8"));
9413
+ return JSON.parse(readFileSync9(join15(historyDir, f.file), "utf-8"));
9124
9414
  } catch {
9125
9415
  return null;
9126
9416
  }
@@ -9130,16 +9420,16 @@ function loadRecentSessions(repoRoot, limit = 5) {
9130
9420
  }
9131
9421
  }
9132
9422
  function savePendingTask(repoRoot, task) {
9133
- const historyDir = join14(repoRoot, OA_DIR, "history");
9423
+ const historyDir = join15(repoRoot, OA_DIR, "history");
9134
9424
  mkdirSync4(historyDir, { recursive: true });
9135
- writeFileSync4(join14(historyDir, PENDING_TASK_FILE), JSON.stringify(task, null, 2) + "\n", "utf-8");
9425
+ writeFileSync4(join15(historyDir, PENDING_TASK_FILE), JSON.stringify(task, null, 2) + "\n", "utf-8");
9136
9426
  }
9137
9427
  function loadPendingTask(repoRoot) {
9138
- const filePath = join14(repoRoot, OA_DIR, "history", PENDING_TASK_FILE);
9428
+ const filePath = join15(repoRoot, OA_DIR, "history", PENDING_TASK_FILE);
9139
9429
  try {
9140
- if (!existsSync9(filePath))
9430
+ if (!existsSync10(filePath))
9141
9431
  return null;
9142
- const data = JSON.parse(readFileSync8(filePath, "utf-8"));
9432
+ const data = JSON.parse(readFileSync9(filePath, "utf-8"));
9143
9433
  try {
9144
9434
  unlinkSync(filePath);
9145
9435
  } catch {
@@ -9167,12 +9457,12 @@ function detectManifests(repoRoot) {
9167
9457
  { file: "docker-compose.yaml", type: "Docker Compose" }
9168
9458
  ];
9169
9459
  for (const check of checks) {
9170
- const filePath = join14(repoRoot, check.file);
9171
- if (existsSync9(filePath)) {
9460
+ const filePath = join15(repoRoot, check.file);
9461
+ if (existsSync10(filePath)) {
9172
9462
  let name;
9173
9463
  if (check.nameField) {
9174
9464
  try {
9175
- const data = JSON.parse(readFileSync8(filePath, "utf-8"));
9465
+ const data = JSON.parse(readFileSync9(filePath, "utf-8"));
9176
9466
  name = data[check.nameField];
9177
9467
  } catch {
9178
9468
  }
@@ -9201,7 +9491,7 @@ function findKeyFiles(repoRoot) {
9201
9491
  { pattern: "CLAUDE.md", description: "Claude Code context" }
9202
9492
  ];
9203
9493
  for (const check of checks) {
9204
- if (existsSync9(join14(repoRoot, check.pattern))) {
9494
+ if (existsSync10(join15(repoRoot, check.pattern))) {
9205
9495
  keyFiles.push({ path: check.pattern, description: check.description });
9206
9496
  }
9207
9497
  }
@@ -9212,7 +9502,7 @@ function buildDirTree(root, maxDepth, prefix = "", depth = 0) {
9212
9502
  return "";
9213
9503
  let result = "";
9214
9504
  try {
9215
- const entries = readdirSync5(root, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") || e.name === ".github").filter((e) => !SKIP_DIRS.has(e.name)).sort((a, b) => {
9505
+ const entries = readdirSync6(root, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") || e.name === ".github").filter((e) => !SKIP_DIRS.has(e.name)).sort((a, b) => {
9216
9506
  if (a.isDirectory() && !b.isDirectory())
9217
9507
  return -1;
9218
9508
  if (!a.isDirectory() && b.isDirectory())
@@ -9227,12 +9517,12 @@ function buildDirTree(root, maxDepth, prefix = "", depth = 0) {
9227
9517
  if (entry.isDirectory()) {
9228
9518
  let fileCount = 0;
9229
9519
  try {
9230
- fileCount = readdirSync5(join14(root, entry.name)).filter((f) => !f.startsWith(".")).length;
9520
+ fileCount = readdirSync6(join15(root, entry.name)).filter((f) => !f.startsWith(".")).length;
9231
9521
  } catch {
9232
9522
  }
9233
9523
  result += `${prefix}${connector}${entry.name}/ (${fileCount})
9234
9524
  `;
9235
- result += buildDirTree(join14(root, entry.name), maxDepth, childPrefix, depth + 1);
9525
+ result += buildDirTree(join15(root, entry.name), maxDepth, childPrefix, depth + 1);
9236
9526
  } else if (depth < maxDepth) {
9237
9527
  result += `${prefix}${connector}${entry.name}
9238
9528
  `;
@@ -9387,6 +9677,51 @@ async function handleSlashCommand(input, ctx) {
9387
9677
  }
9388
9678
  return "handled";
9389
9679
  }
9680
+ case "skills":
9681
+ case "skill": {
9682
+ const skills = discoverSkills(ctx.repoRoot);
9683
+ if (skills.length === 0) {
9684
+ renderInfo("No skills found.");
9685
+ renderInfo("Install AIWG to get skills: npm i -g aiwg && aiwg use sdlc");
9686
+ renderInfo("Or add skills manually to .oa/skills/{name}/SKILL.md");
9687
+ } else {
9688
+ let filtered = skills;
9689
+ if (arg) {
9690
+ const q = arg.toLowerCase();
9691
+ filtered = skills.filter((s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) || s.triggers.some((t) => t.toLowerCase().includes(q)));
9692
+ }
9693
+ if (filtered.length === 0) {
9694
+ renderWarning(`No skills matching "${arg}". Showing all ${skills.length} skills:`);
9695
+ filtered = skills;
9696
+ }
9697
+ const bySource = /* @__PURE__ */ new Map();
9698
+ for (const s of filtered) {
9699
+ const group = bySource.get(s.source) ?? [];
9700
+ group.push(s);
9701
+ bySource.set(s.source, group);
9702
+ }
9703
+ process.stdout.write(`
9704
+ ${c2.bold(`Available Skills (${filtered.length}):`)}
9705
+ `);
9706
+ for (const [source, group] of bySource) {
9707
+ process.stdout.write(`
9708
+ ${c2.dim(`\u2500\u2500 ${source} (${group.length}) \u2500\u2500`)}
9709
+ `);
9710
+ for (const s of group) {
9711
+ process.stdout.write(` ${c2.cyan(s.name.padEnd(32))} ${s.description.slice(0, 60)}
9712
+ `);
9713
+ if (s.triggers.length > 0) {
9714
+ process.stdout.write(` ${"".padEnd(32)} ${c2.dim(`triggers: ${s.triggers.slice(0, 3).join(" | ")}`)}
9715
+ `);
9716
+ }
9717
+ }
9718
+ }
9719
+ process.stdout.write("\n");
9720
+ renderInfo("The agent can use these via the skill_execute tool.");
9721
+ renderInfo("Filter with: /skills <keyword>");
9722
+ }
9723
+ return "handled";
9724
+ }
9390
9725
  case "dream": {
9391
9726
  if (arg === "stop" || arg === "wake") {
9392
9727
  if (ctx.isDreaming?.()) {
@@ -9545,17 +9880,17 @@ async function handleUpdate(subcommand, repoRoot, savePendingTaskState) {
9545
9880
  try {
9546
9881
  const { createRequire: createRequire4 } = await import("node:module");
9547
9882
  const { fileURLToPath: fileURLToPath3 } = await import("node:url");
9548
- const { dirname: dirname4, join: join25 } = await import("node:path");
9549
- const { existsSync: existsSync16 } = await import("node:fs");
9883
+ const { dirname: dirname4, join: join26 } = await import("node:path");
9884
+ const { existsSync: existsSync17 } = await import("node:fs");
9550
9885
  const req = createRequire4(import.meta.url);
9551
9886
  const thisDir = dirname4(fileURLToPath3(import.meta.url));
9552
9887
  const candidates = [
9553
- join25(thisDir, "..", "package.json"),
9554
- join25(thisDir, "..", "..", "package.json"),
9555
- join25(thisDir, "..", "..", "..", "package.json")
9888
+ join26(thisDir, "..", "package.json"),
9889
+ join26(thisDir, "..", "..", "package.json"),
9890
+ join26(thisDir, "..", "..", "..", "package.json")
9556
9891
  ];
9557
9892
  for (const pkgPath of candidates) {
9558
- if (existsSync16(pkgPath)) {
9893
+ if (existsSync17(pkgPath)) {
9559
9894
  const pkg = req(pkgPath);
9560
9895
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
9561
9896
  currentVersion = pkg.version ?? "0.0.0";
@@ -9640,9 +9975,9 @@ var init_commands = __esm({
9640
9975
  // packages/cli/dist/tui/setup.js
9641
9976
  import * as readline from "node:readline";
9642
9977
  import { execSync as execSync8 } from "node:child_process";
9643
- import { existsSync as existsSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "node:fs";
9644
- import { join as join15 } from "node:path";
9645
- import { homedir as homedir5 } from "node:os";
9978
+ import { existsSync as existsSync11, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "node:fs";
9979
+ import { join as join16 } from "node:path";
9980
+ import { homedir as homedir6 } from "node:os";
9646
9981
  function detectSystemSpecs() {
9647
9982
  let totalRamGB = 0;
9648
9983
  let availableRamGB = 0;
@@ -9937,9 +10272,9 @@ async function doSetup(config, rl) {
9937
10272
  `PARAMETER num_predict 16384`,
9938
10273
  `PARAMETER stop "<|endoftext|>"`
9939
10274
  ].join("\n");
9940
- const modelDir2 = join15(homedir5(), ".open-agents", "models");
10275
+ const modelDir2 = join16(homedir6(), ".open-agents", "models");
9941
10276
  mkdirSync5(modelDir2, { recursive: true });
9942
- const modelfilePath = join15(modelDir2, `Modelfile.${customName}`);
10277
+ const modelfilePath = join16(modelDir2, `Modelfile.${customName}`);
9943
10278
  writeFileSync5(modelfilePath, modelfileContent + "\n", "utf8");
9944
10279
  process.stdout.write(` ${c2.dim("Creating model...")} `);
9945
10280
  execSync8(`ollama create ${customName} -f ${modelfilePath}`, {
@@ -9985,7 +10320,7 @@ async function isModelAvailable(config) {
9985
10320
  }
9986
10321
  function isFirstRun() {
9987
10322
  try {
9988
- return !existsSync10(join15(homedir5(), ".open-agents", "config.json"));
10323
+ return !existsSync11(join16(homedir6(), ".open-agents", "config.json"));
9989
10324
  } catch {
9990
10325
  return true;
9991
10326
  }
@@ -10025,10 +10360,10 @@ var init_setup = __esm({
10025
10360
  });
10026
10361
 
10027
10362
  // packages/cli/dist/tui/project-context.js
10028
- import { existsSync as existsSync11, readFileSync as readFileSync9, readdirSync as readdirSync6 } from "node:fs";
10029
- import { join as join16, basename as basename3 } from "node:path";
10363
+ import { existsSync as existsSync12, readFileSync as readFileSync10, readdirSync as readdirSync7 } from "node:fs";
10364
+ import { join as join17, basename as basename4 } from "node:path";
10030
10365
  import { execSync as execSync9 } from "node:child_process";
10031
- import { homedir as homedir6, platform, release } from "node:os";
10366
+ import { homedir as homedir7, platform, release } from "node:os";
10032
10367
  function loadProjectFiles(repoRoot) {
10033
10368
  const discovered = discoverContextFiles(repoRoot);
10034
10369
  if (discovered.length === 0)
@@ -10046,10 +10381,10 @@ function loadProjectMap(repoRoot) {
10046
10381
  if (!hasOaDirectory(repoRoot)) {
10047
10382
  initOaDirectory(repoRoot);
10048
10383
  }
10049
- const mapPath = join16(repoRoot, OA_DIR, "context", "project-map.md");
10050
- if (existsSync11(mapPath)) {
10384
+ const mapPath = join17(repoRoot, OA_DIR, "context", "project-map.md");
10385
+ if (existsSync12(mapPath)) {
10051
10386
  try {
10052
- const content = readFileSync9(mapPath, "utf-8");
10387
+ const content = readFileSync10(mapPath, "utf-8");
10053
10388
  return content;
10054
10389
  } catch {
10055
10390
  }
@@ -10090,33 +10425,33 @@ ${log}`);
10090
10425
  }
10091
10426
  function loadMemoryContext(repoRoot) {
10092
10427
  const sections = [];
10093
- const oaMemDir = join16(repoRoot, OA_DIR, "memory");
10428
+ const oaMemDir = join17(repoRoot, OA_DIR, "memory");
10094
10429
  const oaEntries = loadMemoryDir(oaMemDir, "project");
10095
10430
  if (oaEntries)
10096
10431
  sections.push(oaEntries);
10097
- const legacyMemDir = join16(repoRoot, ".open-agents", "memory");
10098
- if (legacyMemDir !== oaMemDir && existsSync11(legacyMemDir)) {
10432
+ const legacyMemDir = join17(repoRoot, ".open-agents", "memory");
10433
+ if (legacyMemDir !== oaMemDir && existsSync12(legacyMemDir)) {
10099
10434
  const legacyEntries = loadMemoryDir(legacyMemDir, "project/legacy");
10100
10435
  if (legacyEntries)
10101
10436
  sections.push(legacyEntries);
10102
10437
  }
10103
- const globalMemDir = join16(homedir6(), ".open-agents", "memory");
10438
+ const globalMemDir = join17(homedir7(), ".open-agents", "memory");
10104
10439
  const globalEntries = loadMemoryDir(globalMemDir, "global");
10105
10440
  if (globalEntries)
10106
10441
  sections.push(globalEntries);
10107
10442
  return sections.join("\n\n");
10108
10443
  }
10109
10444
  function loadMemoryDir(memDir, scope) {
10110
- if (!existsSync11(memDir))
10445
+ if (!existsSync12(memDir))
10111
10446
  return "";
10112
10447
  const lines = [];
10113
10448
  try {
10114
- const files = readdirSync6(memDir).filter((f) => f.endsWith(".json"));
10449
+ const files = readdirSync7(memDir).filter((f) => f.endsWith(".json"));
10115
10450
  for (const file of files.slice(0, 10)) {
10116
10451
  try {
10117
- const raw = readFileSync9(join16(memDir, file), "utf-8");
10452
+ const raw = readFileSync10(join17(memDir, file), "utf-8");
10118
10453
  const entries = JSON.parse(raw);
10119
- const topic = basename3(file, ".json");
10454
+ const topic = basename4(file, ".json");
10120
10455
  const keys = Object.keys(entries);
10121
10456
  if (keys.length === 0)
10122
10457
  continue;
@@ -10239,7 +10574,8 @@ function buildProjectContext(repoRoot, stores) {
10239
10574
  environment: getEnvironment(repoRoot),
10240
10575
  taskMemories: stores?.taskMemoryStore ? loadTaskMemories(repoRoot, stores.taskMemoryStore) : "",
10241
10576
  failurePatterns: stores?.failureStore ? loadFailurePatterns(stores.failureStore) : "",
10242
- patternSuggestions: stores?.toolPatternStore ? loadPatternSuggestions(repoRoot, stores.toolPatternStore) : ""
10577
+ patternSuggestions: stores?.toolPatternStore ? loadPatternSuggestions(repoRoot, stores.toolPatternStore) : "",
10578
+ skillsSummary: buildSkillsSummary(discoverSkills(repoRoot))
10243
10579
  };
10244
10580
  }
10245
10581
  function formatContextForPrompt(ctx) {
@@ -10295,12 +10631,16 @@ ${ctx.patternSuggestions}
10295
10631
 
10296
10632
  These patterns have been repeated 3+ times. Consider using create_tool to automate them.`);
10297
10633
  }
10634
+ if (ctx.skillsSummary) {
10635
+ sections.push(ctx.skillsSummary);
10636
+ }
10298
10637
  return sections.join("\n\n");
10299
10638
  }
10300
10639
  var init_project_context = __esm({
10301
10640
  "packages/cli/dist/tui/project-context.js"() {
10302
10641
  "use strict";
10303
10642
  init_oa_directory();
10643
+ init_dist2();
10304
10644
  }
10305
10645
  });
10306
10646
 
@@ -11120,19 +11460,19 @@ var init_carousel = __esm({
11120
11460
  });
11121
11461
 
11122
11462
  // packages/cli/dist/tui/voice.js
11123
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6, readFileSync as readFileSync10, unlinkSync as unlinkSync2 } from "node:fs";
11124
- import { join as join17 } from "node:path";
11125
- import { homedir as homedir7, tmpdir as tmpdir2, platform as platform2 } from "node:os";
11463
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6, readFileSync as readFileSync11, unlinkSync as unlinkSync2 } from "node:fs";
11464
+ import { join as join18 } from "node:path";
11465
+ import { homedir as homedir8, tmpdir as tmpdir2, platform as platform2 } from "node:os";
11126
11466
  import { execSync as execSync10, spawn as nodeSpawn } from "node:child_process";
11127
11467
  import { createRequire } from "node:module";
11128
11468
  function modelDir(id) {
11129
- return join17(MODELS_DIR, id);
11469
+ return join18(MODELS_DIR, id);
11130
11470
  }
11131
11471
  function modelOnnxPath(id) {
11132
- return join17(modelDir(id), "model.onnx");
11472
+ return join18(modelDir(id), "model.onnx");
11133
11473
  }
11134
11474
  function modelConfigPath(id) {
11135
- return join17(modelDir(id), "config.json");
11475
+ return join18(modelDir(id), "config.json");
11136
11476
  }
11137
11477
  function describeToolCall(toolName, args) {
11138
11478
  const path = args["path"];
@@ -11247,8 +11587,8 @@ var init_voice = __esm({
11247
11587
  configUrl: "https://raw.githubusercontent.com/robit-man/combine_overwatch_onnx/main/overwatch.onnx.json"
11248
11588
  }
11249
11589
  };
11250
- VOICE_DIR = join17(homedir7(), ".open-agents", "voice");
11251
- MODELS_DIR = join17(VOICE_DIR, "models");
11590
+ VOICE_DIR = join18(homedir8(), ".open-agents", "voice");
11591
+ MODELS_DIR = join18(VOICE_DIR, "models");
11252
11592
  VoiceEngine = class {
11253
11593
  enabled = false;
11254
11594
  modelId = "glados";
@@ -11407,7 +11747,7 @@ var init_voice = __esm({
11407
11747
  const audioData = result["output"].data;
11408
11748
  if (audioData.length === 0)
11409
11749
  return;
11410
- const wavPath = join17(tmpdir2(), `oa-voice-${Date.now()}.wav`);
11750
+ const wavPath = join18(tmpdir2(), `oa-voice-${Date.now()}.wav`);
11411
11751
  this.writeWav(audioData, this.config.audio.sample_rate, wavPath);
11412
11752
  await this.playWav(wavPath);
11413
11753
  try {
@@ -11560,14 +11900,14 @@ var init_voice = __esm({
11560
11900
  if (this.ort)
11561
11901
  return;
11562
11902
  mkdirSync6(VOICE_DIR, { recursive: true });
11563
- const pkgPath = join17(VOICE_DIR, "package.json");
11903
+ const pkgPath = join18(VOICE_DIR, "package.json");
11564
11904
  const expectedDeps = {
11565
11905
  "onnxruntime-node": "^1.21.0",
11566
11906
  "phonemizer": "^1.2.1"
11567
11907
  };
11568
- if (existsSync12(pkgPath)) {
11908
+ if (existsSync13(pkgPath)) {
11569
11909
  try {
11570
- const existing = JSON.parse(readFileSync10(pkgPath, "utf8"));
11910
+ const existing = JSON.parse(readFileSync11(pkgPath, "utf8"));
11571
11911
  if (!existing.dependencies?.["phonemizer"]) {
11572
11912
  existing.dependencies = { ...existing.dependencies, ...expectedDeps };
11573
11913
  writeFileSync6(pkgPath, JSON.stringify(existing, null, 2));
@@ -11575,14 +11915,14 @@ var init_voice = __esm({
11575
11915
  } catch {
11576
11916
  }
11577
11917
  }
11578
- if (!existsSync12(pkgPath)) {
11918
+ if (!existsSync13(pkgPath)) {
11579
11919
  writeFileSync6(pkgPath, JSON.stringify({
11580
11920
  name: "open-agents-voice",
11581
11921
  private: true,
11582
11922
  dependencies: expectedDeps
11583
11923
  }, null, 2));
11584
11924
  }
11585
- const voiceRequire = createRequire(join17(VOICE_DIR, "index.js"));
11925
+ const voiceRequire = createRequire(join18(VOICE_DIR, "index.js"));
11586
11926
  try {
11587
11927
  this.ort = voiceRequire("onnxruntime-node");
11588
11928
  } catch {
@@ -11628,10 +11968,10 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
11628
11968
  const dir = modelDir(id);
11629
11969
  const onnxPath = modelOnnxPath(id);
11630
11970
  const configPath = modelConfigPath(id);
11631
- if (existsSync12(onnxPath) && existsSync12(configPath))
11971
+ if (existsSync13(onnxPath) && existsSync13(configPath))
11632
11972
  return;
11633
11973
  mkdirSync6(dir, { recursive: true });
11634
- if (!existsSync12(configPath)) {
11974
+ if (!existsSync13(configPath)) {
11635
11975
  renderInfo(`Downloading ${model.label} voice config...`);
11636
11976
  const configResp = await fetch(model.configUrl);
11637
11977
  if (!configResp.ok)
@@ -11639,7 +11979,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
11639
11979
  const configText = await configResp.text();
11640
11980
  writeFileSync6(configPath, configText);
11641
11981
  }
11642
- if (!existsSync12(onnxPath)) {
11982
+ if (!existsSync13(onnxPath)) {
11643
11983
  renderInfo(`Downloading ${model.label} voice model (this may take a minute)...`);
11644
11984
  const onnxResp = await fetch(model.onnxUrl);
11645
11985
  if (!onnxResp.ok)
@@ -11675,10 +12015,10 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
11675
12015
  throw new Error("ONNX runtime not loaded");
11676
12016
  const onnxPath = modelOnnxPath(this.modelId);
11677
12017
  const configPath = modelConfigPath(this.modelId);
11678
- if (!existsSync12(onnxPath) || !existsSync12(configPath)) {
12018
+ if (!existsSync13(onnxPath) || !existsSync13(configPath)) {
11679
12019
  throw new Error(`Model files not found for ${this.modelId}`);
11680
12020
  }
11681
- this.config = JSON.parse(readFileSync10(configPath, "utf8"));
12021
+ this.config = JSON.parse(readFileSync11(configPath, "utf8"));
11682
12022
  renderInfo("Loading voice model...");
11683
12023
  this.session = await this.ort.InferenceSession.create(onnxPath, {
11684
12024
  executionProviders: ["cpu"],
@@ -11952,10 +12292,10 @@ var init_stream_renderer = __esm({
11952
12292
 
11953
12293
  // packages/cli/dist/tui/edit-history.js
11954
12294
  import { appendFileSync, mkdirSync as mkdirSync7 } from "node:fs";
11955
- import { join as join18 } from "node:path";
12295
+ import { join as join19 } from "node:path";
11956
12296
  function createEditHistoryLogger(repoRoot, sessionId) {
11957
- const historyDir = join18(repoRoot, ".oa", "history");
11958
- const logPath = join18(historyDir, "edits.jsonl");
12297
+ const historyDir = join19(repoRoot, ".oa", "history");
12298
+ const logPath = join19(historyDir, "edits.jsonl");
11959
12299
  try {
11960
12300
  mkdirSync7(historyDir, { recursive: true });
11961
12301
  } catch {
@@ -12066,8 +12406,8 @@ var init_edit_history = __esm({
12066
12406
  });
12067
12407
 
12068
12408
  // packages/cli/dist/tui/dream-engine.js
12069
- import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, readFileSync as readFileSync11, existsSync as existsSync13, cpSync, rmSync, readdirSync as readdirSync7 } from "node:fs";
12070
- import { join as join19, basename as basename4 } from "node:path";
12409
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, readFileSync as readFileSync12, existsSync as existsSync14, cpSync, rmSync, readdirSync as readdirSync8 } from "node:fs";
12410
+ import { join as join20, basename as basename5 } from "node:path";
12071
12411
  import { execSync as execSync11 } from "node:child_process";
12072
12412
  function adaptTool(tool) {
12073
12413
  return {
@@ -12242,12 +12582,12 @@ var init_dream_engine = __esm({
12242
12582
  const content = String(args["content"] ?? "");
12243
12583
  if (!rawPath)
12244
12584
  return { success: false, output: "", error: "path is required", durationMs: Date.now() - start };
12245
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join19(this.dreamsDir, basename4(rawPath)) : join19(this.dreamsDir, rawPath);
12585
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join20(this.dreamsDir, basename5(rawPath)) : join20(this.dreamsDir, rawPath);
12246
12586
  if (!targetPath.startsWith(this.dreamsDir)) {
12247
12587
  return { success: false, output: "", error: "Dream mode: writes are confined to .oa/dreams/", durationMs: Date.now() - start };
12248
12588
  }
12249
12589
  try {
12250
- const dir = join19(targetPath, "..");
12590
+ const dir = join20(targetPath, "..");
12251
12591
  mkdirSync8(dir, { recursive: true });
12252
12592
  writeFileSync7(targetPath, content, "utf-8");
12253
12593
  return { success: true, output: `Wrote ${content.length} bytes to ${rawPath}`, durationMs: Date.now() - start };
@@ -12277,15 +12617,15 @@ var init_dream_engine = __esm({
12277
12617
  const rawPath = String(args["path"] ?? "");
12278
12618
  const oldStr = String(args["old_string"] ?? "");
12279
12619
  const newStr = String(args["new_string"] ?? "");
12280
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join19(this.dreamsDir, basename4(rawPath)) : join19(this.dreamsDir, rawPath);
12620
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join20(this.dreamsDir, basename5(rawPath)) : join20(this.dreamsDir, rawPath);
12281
12621
  if (!targetPath.startsWith(this.dreamsDir)) {
12282
12622
  return { success: false, output: "", error: "Dream mode: edits are confined to .oa/dreams/", durationMs: Date.now() - start };
12283
12623
  }
12284
12624
  try {
12285
- if (!existsSync13(targetPath)) {
12625
+ if (!existsSync14(targetPath)) {
12286
12626
  return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
12287
12627
  }
12288
- let content = readFileSync11(targetPath, "utf-8");
12628
+ let content = readFileSync12(targetPath, "utf-8");
12289
12629
  if (!content.includes(oldStr)) {
12290
12630
  return { success: false, output: "", error: "old_string not found in file", durationMs: Date.now() - start };
12291
12631
  }
@@ -12344,7 +12684,7 @@ var init_dream_engine = __esm({
12344
12684
  constructor(config, repoRoot) {
12345
12685
  this.config = config;
12346
12686
  this.repoRoot = repoRoot;
12347
- this.dreamsDir = join19(repoRoot, ".oa", "dreams");
12687
+ this.dreamsDir = join20(repoRoot, ".oa", "dreams");
12348
12688
  this.state = {
12349
12689
  mode: "default",
12350
12690
  active: false,
@@ -12416,7 +12756,7 @@ ${result.summary}`;
12416
12756
  if (mode !== "default" || cycle === totalCycles) {
12417
12757
  renderDreamContraction(cycle);
12418
12758
  const cycleSummary = this.buildCycleSummary(cycle, previousFindings);
12419
- const summaryPath = join19(this.dreamsDir, `cycle-${cycle}-summary.md`);
12759
+ const summaryPath = join20(this.dreamsDir, `cycle-${cycle}-summary.md`);
12420
12760
  writeFileSync7(summaryPath, cycleSummary, "utf-8");
12421
12761
  }
12422
12762
  if (mode === "lucid" && !this.abortController.signal.aborted) {
@@ -12537,7 +12877,7 @@ Dreams directory: ${this.dreamsDir}`);
12537
12877
  }
12538
12878
  /** Save workspace backup for lucid mode */
12539
12879
  saveVersionCheckpoint(cycle) {
12540
- const checkpointDir = join19(this.dreamsDir, "checkpoints", `cycle-${cycle}`);
12880
+ const checkpointDir = join20(this.dreamsDir, "checkpoints", `cycle-${cycle}`);
12541
12881
  try {
12542
12882
  mkdirSync8(checkpointDir, { recursive: true });
12543
12883
  try {
@@ -12556,10 +12896,10 @@ Dreams directory: ${this.dreamsDir}`);
12556
12896
  encoding: "utf-8",
12557
12897
  timeout: 5e3
12558
12898
  }).trim();
12559
- writeFileSync7(join19(checkpointDir, "git-status.txt"), gitStatus, "utf-8");
12560
- writeFileSync7(join19(checkpointDir, "git-diff.patch"), gitDiff, "utf-8");
12561
- writeFileSync7(join19(checkpointDir, "git-hash.txt"), gitHash, "utf-8");
12562
- writeFileSync7(join19(checkpointDir, "checkpoint.json"), JSON.stringify({
12899
+ writeFileSync7(join20(checkpointDir, "git-status.txt"), gitStatus, "utf-8");
12900
+ writeFileSync7(join20(checkpointDir, "git-diff.patch"), gitDiff, "utf-8");
12901
+ writeFileSync7(join20(checkpointDir, "git-hash.txt"), gitHash, "utf-8");
12902
+ writeFileSync7(join20(checkpointDir, "checkpoint.json"), JSON.stringify({
12563
12903
  cycle,
12564
12904
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12565
12905
  gitHash,
@@ -12567,7 +12907,7 @@ Dreams directory: ${this.dreamsDir}`);
12567
12907
  }, null, 2), "utf-8");
12568
12908
  renderInfo(`Checkpoint saved: cycle ${cycle} (${gitHash.slice(0, 8)})`);
12569
12909
  } catch {
12570
- writeFileSync7(join19(checkpointDir, "checkpoint.json"), JSON.stringify({ cycle, timestamp: (/* @__PURE__ */ new Date()).toISOString(), mode: this.state.mode }, null, 2), "utf-8");
12910
+ writeFileSync7(join20(checkpointDir, "checkpoint.json"), JSON.stringify({ cycle, timestamp: (/* @__PURE__ */ new Date()).toISOString(), mode: this.state.mode }, null, 2), "utf-8");
12571
12911
  renderInfo(`Checkpoint saved: cycle ${cycle} (no git)`);
12572
12912
  }
12573
12913
  } catch (err) {
@@ -12604,7 +12944,7 @@ Each proposal includes implementation entrypoints and estimated effort.
12604
12944
  /** Update the master proposal index */
12605
12945
  updateProposalIndex() {
12606
12946
  try {
12607
- const files = readdirSync7(this.dreamsDir).filter((f) => f.endsWith(".md") && f !== "PROPOSAL-INDEX.md" && f !== "dream-state.json").sort();
12947
+ const files = readdirSync8(this.dreamsDir).filter((f) => f.endsWith(".md") && f !== "PROPOSAL-INDEX.md" && f !== "dream-state.json").sort();
12608
12948
  const index = `# Dream Proposals Index
12609
12949
 
12610
12950
  **Last updated**: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
@@ -12625,14 +12965,14 @@ ${files.map((f) => `- [\`${f}\`](./${f})`).join("\n")}
12625
12965
  ---
12626
12966
  *Auto-generated by open-agents dream engine*
12627
12967
  `;
12628
- writeFileSync7(join19(this.dreamsDir, "PROPOSAL-INDEX.md"), index, "utf-8");
12968
+ writeFileSync7(join20(this.dreamsDir, "PROPOSAL-INDEX.md"), index, "utf-8");
12629
12969
  } catch {
12630
12970
  }
12631
12971
  }
12632
12972
  /** Save dream state for resume/inspection */
12633
12973
  saveDreamState() {
12634
12974
  try {
12635
- writeFileSync7(join19(this.dreamsDir, "dream-state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
12975
+ writeFileSync7(join20(this.dreamsDir, "dream-state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
12636
12976
  } catch {
12637
12977
  }
12638
12978
  }
@@ -12921,23 +13261,23 @@ var init_status_bar = __esm({
12921
13261
  import * as readline2 from "node:readline";
12922
13262
  import { Writable } from "node:stream";
12923
13263
  import { cwd } from "node:process";
12924
- import { resolve as resolve12, join as join20, dirname as dirname2 } from "node:path";
13264
+ import { resolve as resolve12, join as join21, dirname as dirname2 } from "node:path";
12925
13265
  import { createRequire as createRequire2 } from "node:module";
12926
13266
  import { fileURLToPath } from "node:url";
12927
- import { readFileSync as readFileSync12 } from "node:fs";
12928
- import { existsSync as existsSync14 } from "node:fs";
13267
+ import { readFileSync as readFileSync13 } from "node:fs";
13268
+ import { existsSync as existsSync15 } from "node:fs";
12929
13269
  import { extname as extname5 } from "node:path";
12930
13270
  function getVersion() {
12931
13271
  try {
12932
13272
  const require2 = createRequire2(import.meta.url);
12933
13273
  const thisDir = dirname2(fileURLToPath(import.meta.url));
12934
13274
  const candidates = [
12935
- join20(thisDir, "..", "package.json"),
12936
- join20(thisDir, "..", "..", "package.json"),
12937
- join20(thisDir, "..", "..", "..", "package.json")
13275
+ join21(thisDir, "..", "package.json"),
13276
+ join21(thisDir, "..", "..", "package.json"),
13277
+ join21(thisDir, "..", "..", "..", "package.json")
12938
13278
  ];
12939
13279
  for (const pkgPath of candidates) {
12940
- if (existsSync14(pkgPath)) {
13280
+ if (existsSync15(pkgPath)) {
12941
13281
  const pkg = require2(pkgPath);
12942
13282
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
12943
13283
  return pkg.version ?? "0.0.0";
@@ -13011,7 +13351,10 @@ function buildTools(repoRoot, config) {
13011
13351
  new CreateToolTool(repoRoot),
13012
13352
  new ManageToolsTool(repoRoot),
13013
13353
  // Load agent-created custom tools from .oa/tools/ and ~/.open-agents/tools/
13014
- ...buildCustomTools(repoRoot)
13354
+ ...buildCustomTools(repoRoot),
13355
+ // Skill system (AIWG skills — discovery and execution)
13356
+ new SkillListTool(repoRoot),
13357
+ new SkillExecuteTool(repoRoot)
13015
13358
  ];
13016
13359
  return [
13017
13360
  ...executionTools.map(adaptTool2),
@@ -13582,12 +13925,12 @@ ${c2.dim("Goodbye!")}
13582
13925
  }
13583
13926
  }
13584
13927
  const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
13585
- const isImage = isImagePath(cleanPath) && existsSync14(resolve12(repoRoot, cleanPath));
13928
+ const isImage = isImagePath(cleanPath) && existsSync15(resolve12(repoRoot, cleanPath));
13586
13929
  if (activeTask) {
13587
13930
  if (isImage) {
13588
13931
  try {
13589
13932
  const imgPath = resolve12(repoRoot, cleanPath);
13590
- const imgBuffer = readFileSync12(imgPath);
13933
+ const imgBuffer = readFileSync13(imgPath);
13591
13934
  const base64 = imgBuffer.toString("base64");
13592
13935
  const ext = extname5(cleanPath).toLowerCase();
13593
13936
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
@@ -13807,7 +14150,7 @@ import { glob } from "glob";
13807
14150
  import ignore from "ignore";
13808
14151
  import { readFile as readFile9, stat as stat2 } from "node:fs/promises";
13809
14152
  import { createHash } from "node:crypto";
13810
- import { join as join21, relative as relative3, extname as extname6, basename as basename5 } from "node:path";
14153
+ import { join as join22, relative as relative3, extname as extname6, basename as basename6 } from "node:path";
13811
14154
  var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
13812
14155
  var init_codebase_indexer = __esm({
13813
14156
  "packages/indexer/dist/codebase-indexer.js"() {
@@ -13851,7 +14194,7 @@ var init_codebase_indexer = __esm({
13851
14194
  const ig = ignore.default();
13852
14195
  if (this.config.respectGitignore) {
13853
14196
  try {
13854
- const gitignoreContent = await readFile9(join21(this.config.rootDir, ".gitignore"), "utf-8");
14197
+ const gitignoreContent = await readFile9(join22(this.config.rootDir, ".gitignore"), "utf-8");
13855
14198
  ig.add(gitignoreContent);
13856
14199
  } catch {
13857
14200
  }
@@ -13866,7 +14209,7 @@ var init_codebase_indexer = __esm({
13866
14209
  for (const relativePath of files) {
13867
14210
  if (ig.ignores(relativePath))
13868
14211
  continue;
13869
- const fullPath = join21(this.config.rootDir, relativePath);
14212
+ const fullPath = join22(this.config.rootDir, relativePath);
13870
14213
  try {
13871
14214
  const fileStat = await stat2(fullPath);
13872
14215
  if (fileStat.size > this.config.maxFileSize)
@@ -13889,7 +14232,7 @@ var init_codebase_indexer = __esm({
13889
14232
  }
13890
14233
  buildTree(files) {
13891
14234
  const root = {
13892
- name: basename5(this.config.rootDir),
14235
+ name: basename6(this.config.rootDir),
13893
14236
  path: this.config.rootDir,
13894
14237
  type: "directory",
13895
14238
  children: []
@@ -13912,7 +14255,7 @@ var init_codebase_indexer = __esm({
13912
14255
  if (!child) {
13913
14256
  child = {
13914
14257
  name: part,
13915
- path: join21(current.path, part),
14258
+ path: join22(current.path, part),
13916
14259
  type: "directory",
13917
14260
  children: []
13918
14261
  };
@@ -13987,13 +14330,13 @@ __export(index_repo_exports, {
13987
14330
  indexRepoCommand: () => indexRepoCommand
13988
14331
  });
13989
14332
  import { resolve as resolve13 } from "node:path";
13990
- import { existsSync as existsSync15, statSync as statSync6 } from "node:fs";
14333
+ import { existsSync as existsSync16, statSync as statSync6 } from "node:fs";
13991
14334
  import { cwd as cwd2 } from "node:process";
13992
14335
  async function indexRepoCommand(opts, _config) {
13993
14336
  const repoRoot = resolve13(opts.repoPath ?? cwd2());
13994
14337
  printHeader("Index Repository");
13995
14338
  printInfo(`Indexing: ${repoRoot}`);
13996
- if (!existsSync15(repoRoot)) {
14339
+ if (!existsSync16(repoRoot)) {
13997
14340
  printError(`Path does not exist: ${repoRoot}`);
13998
14341
  process.exit(1);
13999
14342
  }
@@ -14239,8 +14582,8 @@ var config_exports = {};
14239
14582
  __export(config_exports, {
14240
14583
  configCommand: () => configCommand
14241
14584
  });
14242
- import { join as join22, resolve as resolve14 } from "node:path";
14243
- import { homedir as homedir8 } from "node:os";
14585
+ import { join as join23, resolve as resolve14 } from "node:path";
14586
+ import { homedir as homedir9 } from "node:os";
14244
14587
  import { cwd as cwd3 } from "node:process";
14245
14588
  function coerceForSettings(key, value) {
14246
14589
  if (INT_KEYS.has(key))
@@ -14292,7 +14635,7 @@ function handleShow(opts, config) {
14292
14635
  }
14293
14636
  }
14294
14637
  printSection("Config File");
14295
- printInfo(`~/.open-agents/config.json (${join22(homedir8(), ".open-agents", "config.json")})`);
14638
+ printInfo(`~/.open-agents/config.json (${join23(homedir9(), ".open-agents", "config.json")})`);
14296
14639
  printSection("Priority Chain");
14297
14640
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
14298
14641
  printInfo(" 2. Project .oa/settings.json (--local)");
@@ -14331,7 +14674,7 @@ function handleSet(opts, _config) {
14331
14674
  const coerced = coerceForSettings(key, value);
14332
14675
  saveProjectSettings(repoRoot, { [key]: coerced });
14333
14676
  printSuccess(`Project override set: ${key} = ${value}`);
14334
- printInfo(`Saved to ${join22(repoRoot, ".oa", "settings.json")}`);
14677
+ printInfo(`Saved to ${join23(repoRoot, ".oa", "settings.json")}`);
14335
14678
  printInfo("This override applies only when running in this workspace.");
14336
14679
  } catch (err) {
14337
14680
  printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
@@ -14550,7 +14893,7 @@ __export(eval_exports, {
14550
14893
  });
14551
14894
  import { tmpdir as tmpdir3 } from "node:os";
14552
14895
  import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "node:fs";
14553
- import { join as join23 } from "node:path";
14896
+ import { join as join24 } from "node:path";
14554
14897
  async function evalCommand(opts, config) {
14555
14898
  const suiteName = opts.suite ?? "basic";
14556
14899
  const suite = SUITES[suiteName];
@@ -14671,9 +15014,9 @@ async function evalCommand(opts, config) {
14671
15014
  process.exit(failed > 0 ? 1 : 0);
14672
15015
  }
14673
15016
  function createTempEvalRepo() {
14674
- const dir = join23(tmpdir3(), `open-agents-eval-${Date.now()}`);
15017
+ const dir = join24(tmpdir3(), `open-agents-eval-${Date.now()}`);
14675
15018
  mkdirSync9(dir, { recursive: true });
14676
- writeFileSync8(join23(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
15019
+ writeFileSync8(join24(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
14677
15020
  return dir;
14678
15021
  }
14679
15022
  var BASIC_SUITE, FULL_SUITE, SUITES;
@@ -14733,7 +15076,7 @@ init_updater();
14733
15076
  import { parseArgs as nodeParseArgs2 } from "node:util";
14734
15077
  import { createRequire as createRequire3 } from "node:module";
14735
15078
  import { fileURLToPath as fileURLToPath2 } from "node:url";
14736
- import { dirname as dirname3, join as join24 } from "node:path";
15079
+ import { dirname as dirname3, join as join25 } from "node:path";
14737
15080
 
14738
15081
  // packages/cli/dist/cli.js
14739
15082
  import { createInterface } from "node:readline";
@@ -14840,7 +15183,7 @@ init_output();
14840
15183
  function getVersion2() {
14841
15184
  try {
14842
15185
  const require2 = createRequire3(import.meta.url);
14843
- const pkgPath = join24(dirname3(fileURLToPath2(import.meta.url)), "..", "package.json");
15186
+ const pkgPath = join25(dirname3(fileURLToPath2(import.meta.url)), "..", "package.json");
14844
15187
  const pkg = require2(pkgPath);
14845
15188
  return pkg.version;
14846
15189
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.12.16",
3
+ "version": "0.13.1",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",