@arrislink/axon 1.0.1 → 1.0.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.
package/README.md CHANGED
@@ -10,14 +10,15 @@
10
10
 
11
11
  Axon is a unified AI-assisted development environment that solves context loss, wheel reinvention, and planning chaos in AI-powered programming. **Powered by [OpenCode](https://github.com/anomalyco/opencode) and [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)**, Axon orchestrates these powerful tools through specification-driven development and task management.
12
12
 
13
- ## ✨ Features
14
-
15
- - **📝 Spec-Driven Development** - Define clear requirements before coding
16
- - **🔗 Task Traceability** - Every line of code maps to a specific task bead
17
- - **🎯 Knowledge Reuse** - Automatically match and apply validated skill templates
18
- - **💰 Cost Control** - Smart token usage tracking to prevent overspending
19
- - **🤖 Multi-Provider Support** - Integrate with OMO for 75+ LLM providers (Anthropic, OpenAI, Antigravity, etc.)
20
- - **🎭 Agent Orchestration** - Multiple AI agents collaborate intelligently
13
+ ## ✨ Why Axon?
14
+
15
+ **Axon transforms AI from a "Code Autocompleter" into a "Development Partner".**
16
+
17
+ - **🧠 Spec-First**: Don't just chat. Define requirements in `spec.md` to keep the AI focused.
18
+ - **🗺️ Bead Planning**: Complex features are broken into atomic, dependency-sorted tasks (Beads).
19
+ - **🤖 Agentic Execution**: **OpenCode** agents execute tasks one-by-one, ensuring context and quality.
20
+ - **♻️ Skill Reuse**: Automatically apply proven patterns (e.g., "Secure Auth") from your team's library.
21
+ - **🛡️ Enterprise Safe**: Token budgeting, Git safety checks, and multi-provider failover via **OMO**.
21
22
 
22
23
  ## 🎯 Applicable Scenarios
23
24
 
package/README.zh-CN.md CHANGED
@@ -10,14 +10,15 @@
10
10
 
11
11
  Axon 是一个统一的 AI 辅助开发环境,解决 AI 编程中的上下文丢失、重复造轮子和规划失控问题。**由 [OpenCode](https://github.com/anomalyco/opencode) 和 [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) 驱动**,Axon 通过规格驱动开发和任务管理来编排这些强大的工具。
12
12
 
13
- ## ✨ 核心特性
14
-
15
- - **📝 规格驱动开发** - 先定义清晰需求,再编码
16
- - **🔗 任务可追溯性** - 每行代码对应明确的任务珠子
17
- - **🎯 知识复用** - 自动匹配并应用已验证的技能模板
18
- - **💰 成本控制** - 智能追踪 token 消耗,避免超支
19
- - **🤖 多提供商支持** - 集成 OMO,支持 75+ LLM 提供商(Anthropic、OpenAI、Antigravity 等)
20
- - **🎭 Agent 编排** - 多个 AI 代理智能协作
13
+ ## ✨ 为什么选择 Axon?
14
+
15
+ **Axon AI 从“代码补全工具”转变为真正的“开发合作伙伴”。**
16
+
17
+ - **🧠 规格优先**: 拒绝随意对话。在 `spec.md` 中定义需求,让 AI 保持专注。
18
+ - **🗺️ 珠子规划**: 将复杂功能拆解为原子的、按依赖排序的任务 (Beads)。
19
+ - **🤖 代理执行**: **OpenCode** 智能体逐个执行任务,确保上下文完整和代码质量。
20
+ - **♻️ 技能复用**: 自动应用团队库中经过验证的模式 (如“安全认证”)。
21
+ - **🛡️ 企业级安全**: Token 预算控制、Git 安全检查以及通过 **OMO** 实现的多模型故障转移。
21
22
 
22
23
  ## 🎯 适用场景
23
24
 
package/dist/index.js CHANGED
@@ -17439,9 +17439,185 @@ var init_orchestrator = __esm(() => {
17439
17439
  init_errors();
17440
17440
  });
17441
17441
 
17442
+ // src/core/skills/library.ts
17443
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
17444
+ import { readdir, readFile, writeFile } from "fs/promises";
17445
+ import { join as join6, basename as basename2, dirname as dirname3 } from "path";
17446
+ function parseFrontmatter(content) {
17447
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
17448
+ if (!match) {
17449
+ return { metadata: {}, body: content };
17450
+ }
17451
+ const [, frontmatter, body] = match;
17452
+ const metadata = {};
17453
+ for (const line of frontmatter.split(`
17454
+ `)) {
17455
+ const colonIndex = line.indexOf(":");
17456
+ if (colonIndex > 0) {
17457
+ const key = line.slice(0, colonIndex).trim();
17458
+ let value = line.slice(colonIndex + 1).trim();
17459
+ if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) {
17460
+ try {
17461
+ value = JSON.parse(value);
17462
+ } catch {
17463
+ const strValue = value;
17464
+ const inner = strValue.slice(1, -1).trim();
17465
+ if (inner) {
17466
+ value = inner.split(",").map((s) => s.trim());
17467
+ } else {
17468
+ value = [];
17469
+ }
17470
+ }
17471
+ }
17472
+ metadata[key] = value;
17473
+ }
17474
+ }
17475
+ return { metadata, body: body.trim() };
17476
+ }
17477
+
17478
+ class SkillsLibrary {
17479
+ skills = [];
17480
+ indexed = false;
17481
+ paths;
17482
+ constructor(localPath, globalPath) {
17483
+ this.paths = [localPath, globalPath].filter((p) => existsSync7(p));
17484
+ }
17485
+ async index() {
17486
+ this.skills = [];
17487
+ for (const basePath of this.paths) {
17488
+ await this.indexDirectory(basePath);
17489
+ }
17490
+ this.indexed = true;
17491
+ }
17492
+ async indexDirectory(dir) {
17493
+ if (!existsSync7(dir))
17494
+ return;
17495
+ const entries = await readdir(dir, { withFileTypes: true });
17496
+ for (const entry of entries) {
17497
+ const fullPath = join6(dir, entry.name);
17498
+ if (entry.isDirectory()) {
17499
+ await this.indexDirectory(fullPath);
17500
+ } else if (entry.name.endsWith(".md")) {
17501
+ try {
17502
+ const content = await readFile(fullPath, "utf-8");
17503
+ const { metadata, body } = parseFrontmatter(content);
17504
+ this.skills.push({
17505
+ metadata: {
17506
+ name: metadata["name"] || basename2(fullPath, ".md"),
17507
+ description: metadata["description"] || "",
17508
+ tags: metadata["tags"] || [],
17509
+ models: metadata["models"] || [],
17510
+ tokens_avg: Number(metadata["tokens_avg"]) || 2000,
17511
+ difficulty: metadata["difficulty"] || "medium",
17512
+ last_updated: metadata["last_updated"] || new Date().toISOString()
17513
+ },
17514
+ content: body,
17515
+ path: fullPath
17516
+ });
17517
+ } catch {}
17518
+ }
17519
+ }
17520
+ }
17521
+ async search(query, limit = 5) {
17522
+ if (!this.indexed) {
17523
+ await this.index();
17524
+ }
17525
+ const queryLower = query.toLowerCase();
17526
+ const results = [];
17527
+ for (const skill of this.skills) {
17528
+ let score = 0;
17529
+ const matchedOn = [];
17530
+ if (skill.metadata.name.toLowerCase().includes(queryLower)) {
17531
+ score += 100;
17532
+ matchedOn.push("name");
17533
+ }
17534
+ for (const tag of skill.metadata.tags) {
17535
+ if (tag.toLowerCase().includes(queryLower)) {
17536
+ score += 50;
17537
+ if (!matchedOn.includes("tags"))
17538
+ matchedOn.push("tags");
17539
+ }
17540
+ }
17541
+ if (skill.metadata.description.toLowerCase().includes(queryLower)) {
17542
+ score += 30;
17543
+ matchedOn.push("description");
17544
+ }
17545
+ const contentMatches = (skill.content.toLowerCase().match(new RegExp(queryLower, "g")) || []).length;
17546
+ if (contentMatches > 0) {
17547
+ score += contentMatches * 10;
17548
+ matchedOn.push("content");
17549
+ }
17550
+ if (score > 0) {
17551
+ results.push({ skill, score, matchedOn });
17552
+ }
17553
+ }
17554
+ return results.sort((a, b) => b.score - a.score).slice(0, limit);
17555
+ }
17556
+ async getByPath(path) {
17557
+ if (!this.indexed) {
17558
+ await this.index();
17559
+ }
17560
+ return this.skills.find((s) => s.path === path) || null;
17561
+ }
17562
+ async save(skill, targetPath) {
17563
+ const dir = dirname3(targetPath);
17564
+ if (!existsSync7(dir)) {
17565
+ mkdirSync4(dir, { recursive: true });
17566
+ }
17567
+ const frontmatter = [
17568
+ "---",
17569
+ `name: ${skill.metadata.name}`,
17570
+ `description: ${skill.metadata.description}`,
17571
+ `tags: ${JSON.stringify(skill.metadata.tags)}`,
17572
+ `models: ${JSON.stringify(skill.metadata.models)}`,
17573
+ `tokens_avg: ${skill.metadata.tokens_avg}`,
17574
+ `difficulty: ${skill.metadata.difficulty}`,
17575
+ `last_updated: ${new Date().toISOString().split("T")[0]}`,
17576
+ "---",
17577
+ ""
17578
+ ].join(`
17579
+ `);
17580
+ const content = frontmatter + skill.content;
17581
+ await writeFile(targetPath, content, "utf-8");
17582
+ this.indexed = false;
17583
+ }
17584
+ async list(filter) {
17585
+ if (!this.indexed) {
17586
+ await this.index();
17587
+ }
17588
+ let result = [...this.skills];
17589
+ if (filter?.tags?.length) {
17590
+ result = result.filter((s) => s.metadata.tags.some((t) => filter.tags?.includes(t)));
17591
+ }
17592
+ if (filter?.difficulty) {
17593
+ result = result.filter((s) => s.metadata.difficulty === filter.difficulty);
17594
+ }
17595
+ return result;
17596
+ }
17597
+ async getStats() {
17598
+ if (!this.indexed) {
17599
+ await this.index();
17600
+ }
17601
+ const byTag = {};
17602
+ const byDifficulty = {};
17603
+ for (const skill of this.skills) {
17604
+ for (const tag of skill.metadata.tags) {
17605
+ byTag[tag] = (byTag[tag] || 0) + 1;
17606
+ }
17607
+ byDifficulty[skill.metadata.difficulty] = (byDifficulty[skill.metadata.difficulty] || 0) + 1;
17608
+ }
17609
+ return {
17610
+ total: this.skills.length,
17611
+ byTag,
17612
+ byDifficulty
17613
+ };
17614
+ }
17615
+ }
17616
+ var init_library = () => {};
17617
+
17442
17618
  // src/core/beads/executor.ts
17443
- import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
17444
- import { join as join6 } from "path";
17619
+ import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
17620
+ import { join as join7 } from "path";
17445
17621
 
17446
17622
  class BeadsExecutor {
17447
17623
  graph;
@@ -17449,15 +17625,18 @@ class BeadsExecutor {
17449
17625
  config;
17450
17626
  orchestrator;
17451
17627
  git;
17628
+ skillsLibrary;
17452
17629
  constructor(config, projectRoot, apiKey) {
17453
17630
  this.config = config;
17454
- this.graphPath = join6(projectRoot, config.tools.beads.path, "graph.json");
17631
+ this.graphPath = join7(projectRoot, config.tools.beads.path, "graph.json");
17455
17632
  this.graph = this.loadGraph();
17456
17633
  this.orchestrator = new AgentOrchestrator(config, apiKey);
17457
17634
  this.git = new GitOperations(projectRoot);
17635
+ const localSkillsPath = join7(projectRoot, config.tools.skills.local_path);
17636
+ this.skillsLibrary = new SkillsLibrary(localSkillsPath, config.tools.skills.global_path);
17458
17637
  }
17459
17638
  loadGraph() {
17460
- if (!existsSync7(this.graphPath)) {
17639
+ if (!existsSync8(this.graphPath)) {
17461
17640
  throw new BeadsError("\u4EFB\u52A1\u56FE\u6587\u4EF6\u4E0D\u5B58\u5728", ["\u8FD0\u884C `ax plan` \u751F\u6210\u4EFB\u52A1\u56FE"]);
17462
17641
  }
17463
17642
  try {
@@ -17505,12 +17684,22 @@ class BeadsExecutor {
17505
17684
  logger.info(`\uD83D\uDCCB \u63CF\u8FF0: ${bead.description}`);
17506
17685
  this.updateBeadStatus(bead.id, "running");
17507
17686
  try {
17508
- const specPath = join6(this.config.tools.openspec.path, "spec.md");
17509
- const spec = existsSync7(specPath) ? readFileSync4(specPath, "utf-8") : "";
17687
+ const specPath = join7(this.config.tools.openspec.path, "spec.md");
17688
+ const spec = existsSync8(specPath) ? readFileSync4(specPath, "utf-8") : "";
17689
+ let skills = [];
17690
+ if (this.config.tools.skills.enabled) {
17691
+ const query = `${bead.title} ${bead.skills_required.join(" ")}`;
17692
+ const searchResults = await this.skillsLibrary.search(query, 3);
17693
+ skills = searchResults.map((r) => r.skill);
17694
+ if (skills.length > 0) {
17695
+ logger.info(`\uD83D\uDCDA \u5339\u914D\u6280\u80FD\u6A21\u677F:`);
17696
+ skills.forEach((s) => logger.info(` - ${s.metadata.name}`));
17697
+ }
17698
+ }
17510
17699
  const result = await this.orchestrator.execute({
17511
17700
  bead,
17512
17701
  spec,
17513
- skills: []
17702
+ skills
17514
17703
  });
17515
17704
  if (this.config.tools.beads.auto_commit && result.artifacts.files.length > 0) {
17516
17705
  await this.commitChanges(bead, result.artifacts.commits);
@@ -17592,6 +17781,7 @@ var init_executor = __esm(() => {
17592
17781
  init_git();
17593
17782
  init_errors();
17594
17783
  init_logger();
17784
+ init_library();
17595
17785
  });
17596
17786
 
17597
17787
  // src/core/beads/index.ts
@@ -17650,7 +17840,7 @@ async function checkCompatibility() {
17650
17840
  compatible: true,
17651
17841
  required: ">=18.0.0"
17652
17842
  });
17653
- if (process.env.DEBUG) {
17843
+ if (process.env["DEBUG"]) {
17654
17844
  logger.debug("Compatibility Checks:");
17655
17845
  checks.forEach((c) => {
17656
17846
  logger.debug(` ${c.tool}: ${c.version} (Required: ${c.required})`);
@@ -24828,8 +25018,8 @@ specCommand.command("validate").description("\u9A8C\u8BC1\u89C4\u683C\u6587\u686
24828
25018
  });
24829
25019
  // src/commands/plan.ts
24830
25020
  init_source();
24831
- import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
24832
- import { join as join7, dirname as dirname3 } from "path";
25021
+ import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
25022
+ import { join as join8, dirname as dirname4 } from "path";
24833
25023
  init_errors();
24834
25024
  init_logger();
24835
25025
  var planCommand = new Command("plan").description("\u4ECE\u89C4\u683C\u6587\u6863\u751F\u6210\u4EFB\u52A1\u56FE").option("--visualize", "\u5728\u6D4F\u89C8\u5668\u4E2D\u53EF\u89C6\u5316").option("--output <path>", "\u81EA\u5B9A\u4E49\u8F93\u51FA\u8DEF\u5F84").option("--model <name>", "\u6307\u5B9A\u4F7F\u7528\u7684\u6A21\u578B").option("--dry-run", "\u53EA\u9A8C\u8BC1\uFF0C\u4E0D\u751F\u6210").action(async (options) => {
@@ -24844,8 +25034,8 @@ var planCommand = new Command("plan").description("\u4ECE\u89C4\u683C\u6587\u686
24844
25034
  }
24845
25035
  const configManager = new ConfigManager(projectRoot);
24846
25036
  const config = configManager.get();
24847
- const specPath = join7(projectRoot, config.tools.openspec.path, "spec.md");
24848
- if (!existsSync8(specPath)) {
25037
+ const specPath = join8(projectRoot, config.tools.openspec.path, "spec.md");
25038
+ if (!existsSync9(specPath)) {
24849
25039
  throw new AxonError("\u89C4\u683C\u6587\u6863\u4E0D\u5B58\u5728", "PLAN_ERROR", [
24850
25040
  "\u4F7F\u7528 `ax spec init` \u521B\u5EFA\u89C4\u683C\u6587\u6863"
24851
25041
  ]);
@@ -24873,10 +25063,10 @@ var planCommand = new Command("plan").description("\u4ECE\u89C4\u683C\u6587\u686
24873
25063
  throw new AxonError("\u4EFB\u52A1\u56FE\u9A8C\u8BC1\u5931\u8D25", "PLAN_ERROR");
24874
25064
  }
24875
25065
  spinner.succeed("\u4EFB\u52A1\u56FE\u9A8C\u8BC1\u901A\u8FC7");
24876
- const graphPath = options.output || join7(projectRoot, config.tools.beads.path, "graph.json");
24877
- const graphDir = dirname3(graphPath);
24878
- if (!existsSync8(graphDir)) {
24879
- mkdirSync4(graphDir, { recursive: true });
25066
+ const graphPath = options.output || join8(projectRoot, config.tools.beads.path, "graph.json");
25067
+ const graphDir = dirname4(graphPath);
25068
+ if (!existsSync9(graphDir)) {
25069
+ mkdirSync5(graphDir, { recursive: true });
24880
25070
  }
24881
25071
  writeFileSync4(graphPath, JSON.stringify(graph2, null, 2), "utf-8");
24882
25072
  logger.blank();
@@ -25032,180 +25222,9 @@ init_source();
25032
25222
  import { existsSync as existsSync10 } from "fs";
25033
25223
  import { join as join9, basename as basename3 } from "path";
25034
25224
 
25035
- // src/core/skills/library.ts
25036
- import { existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
25037
- import { readdir, readFile, writeFile } from "fs/promises";
25038
- import { join as join8, basename as basename2, dirname as dirname4 } from "path";
25039
- function parseFrontmatter(content) {
25040
- const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
25041
- if (!match) {
25042
- return { metadata: {}, body: content };
25043
- }
25044
- const [, frontmatter, body] = match;
25045
- const metadata = {};
25046
- for (const line of frontmatter.split(`
25047
- `)) {
25048
- const colonIndex = line.indexOf(":");
25049
- if (colonIndex > 0) {
25050
- const key = line.slice(0, colonIndex).trim();
25051
- let value = line.slice(colonIndex + 1).trim();
25052
- if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) {
25053
- try {
25054
- value = JSON.parse(value);
25055
- } catch {
25056
- const strValue = value;
25057
- const inner = strValue.slice(1, -1).trim();
25058
- if (inner) {
25059
- value = inner.split(",").map((s) => s.trim());
25060
- } else {
25061
- value = [];
25062
- }
25063
- }
25064
- }
25065
- metadata[key] = value;
25066
- }
25067
- }
25068
- return { metadata, body: body.trim() };
25069
- }
25225
+ // src/core/skills/index.ts
25226
+ init_library();
25070
25227
 
25071
- class SkillsLibrary {
25072
- skills = [];
25073
- indexed = false;
25074
- paths;
25075
- constructor(localPath, globalPath) {
25076
- this.paths = [localPath, globalPath].filter((p) => existsSync9(p));
25077
- }
25078
- async index() {
25079
- this.skills = [];
25080
- for (const basePath of this.paths) {
25081
- await this.indexDirectory(basePath);
25082
- }
25083
- this.indexed = true;
25084
- }
25085
- async indexDirectory(dir) {
25086
- if (!existsSync9(dir))
25087
- return;
25088
- const entries = await readdir(dir, { withFileTypes: true });
25089
- for (const entry of entries) {
25090
- const fullPath = join8(dir, entry.name);
25091
- if (entry.isDirectory()) {
25092
- await this.indexDirectory(fullPath);
25093
- } else if (entry.name.endsWith(".md")) {
25094
- try {
25095
- const content = await readFile(fullPath, "utf-8");
25096
- const { metadata, body } = parseFrontmatter(content);
25097
- this.skills.push({
25098
- metadata: {
25099
- name: metadata["name"] || basename2(fullPath, ".md"),
25100
- description: metadata["description"] || "",
25101
- tags: metadata["tags"] || [],
25102
- models: metadata["models"] || [],
25103
- tokens_avg: Number(metadata["tokens_avg"]) || 2000,
25104
- difficulty: metadata["difficulty"] || "medium",
25105
- last_updated: metadata["last_updated"] || new Date().toISOString()
25106
- },
25107
- content: body,
25108
- path: fullPath
25109
- });
25110
- } catch {}
25111
- }
25112
- }
25113
- }
25114
- async search(query, limit = 5) {
25115
- if (!this.indexed) {
25116
- await this.index();
25117
- }
25118
- const queryLower = query.toLowerCase();
25119
- const results = [];
25120
- for (const skill of this.skills) {
25121
- let score = 0;
25122
- const matchedOn = [];
25123
- if (skill.metadata.name.toLowerCase().includes(queryLower)) {
25124
- score += 100;
25125
- matchedOn.push("name");
25126
- }
25127
- for (const tag of skill.metadata.tags) {
25128
- if (tag.toLowerCase().includes(queryLower)) {
25129
- score += 50;
25130
- if (!matchedOn.includes("tags"))
25131
- matchedOn.push("tags");
25132
- }
25133
- }
25134
- if (skill.metadata.description.toLowerCase().includes(queryLower)) {
25135
- score += 30;
25136
- matchedOn.push("description");
25137
- }
25138
- const contentMatches = (skill.content.toLowerCase().match(new RegExp(queryLower, "g")) || []).length;
25139
- if (contentMatches > 0) {
25140
- score += contentMatches * 10;
25141
- matchedOn.push("content");
25142
- }
25143
- if (score > 0) {
25144
- results.push({ skill, score, matchedOn });
25145
- }
25146
- }
25147
- return results.sort((a, b) => b.score - a.score).slice(0, limit);
25148
- }
25149
- async getByPath(path) {
25150
- if (!this.indexed) {
25151
- await this.index();
25152
- }
25153
- return this.skills.find((s) => s.path === path) || null;
25154
- }
25155
- async save(skill, targetPath) {
25156
- const dir = dirname4(targetPath);
25157
- if (!existsSync9(dir)) {
25158
- mkdirSync5(dir, { recursive: true });
25159
- }
25160
- const frontmatter = [
25161
- "---",
25162
- `name: ${skill.metadata.name}`,
25163
- `description: ${skill.metadata.description}`,
25164
- `tags: ${JSON.stringify(skill.metadata.tags)}`,
25165
- `models: ${JSON.stringify(skill.metadata.models)}`,
25166
- `tokens_avg: ${skill.metadata.tokens_avg}`,
25167
- `difficulty: ${skill.metadata.difficulty}`,
25168
- `last_updated: ${new Date().toISOString().split("T")[0]}`,
25169
- "---",
25170
- ""
25171
- ].join(`
25172
- `);
25173
- const content = frontmatter + skill.content;
25174
- await writeFile(targetPath, content, "utf-8");
25175
- this.indexed = false;
25176
- }
25177
- async list(filter) {
25178
- if (!this.indexed) {
25179
- await this.index();
25180
- }
25181
- let result = [...this.skills];
25182
- if (filter?.tags?.length) {
25183
- result = result.filter((s) => s.metadata.tags.some((t) => filter.tags?.includes(t)));
25184
- }
25185
- if (filter?.difficulty) {
25186
- result = result.filter((s) => s.metadata.difficulty === filter.difficulty);
25187
- }
25188
- return result;
25189
- }
25190
- async getStats() {
25191
- if (!this.indexed) {
25192
- await this.index();
25193
- }
25194
- const byTag = {};
25195
- const byDifficulty = {};
25196
- for (const skill of this.skills) {
25197
- for (const tag of skill.metadata.tags) {
25198
- byTag[tag] = (byTag[tag] || 0) + 1;
25199
- }
25200
- byDifficulty[skill.metadata.difficulty] = (byDifficulty[skill.metadata.difficulty] || 0) + 1;
25201
- }
25202
- return {
25203
- total: this.skills.length,
25204
- byTag,
25205
- byDifficulty
25206
- };
25207
- }
25208
- }
25209
25228
  // src/commands/skills.ts
25210
25229
  init_logger();
25211
25230
  init_errors();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arrislink/axon",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "AI-Powered Development Operating System with unified LLM provider support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",