opencode-swarm 7.55.0 → 7.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "opencode-swarm",
55
- version: "7.55.0",
55
+ version: "7.56.0",
56
56
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
57
57
  main: "dist/index.js",
58
58
  types: "dist/index.d.ts",
@@ -36811,7 +36811,7 @@ function clusterEntries(entries) {
36811
36811
  function uniqueStrings(arr) {
36812
36812
  return [...new Set(arr.filter((s) => typeof s === "string" && s.length > 0))];
36813
36813
  }
36814
- function renderSkillMarkdown(cluster, mode = "active") {
36814
+ function renderSkillMarkdown(cluster, mode = "active", generatedAt = new Date().toISOString()) {
36815
36815
  const description = cluster.title.length > 200 ? `${cluster.title.slice(0, 197)}\u2026` : cluster.title;
36816
36816
  const ids = cluster.entries.map((e) => ` - ${e.id}`).join(`
36817
36817
  `);
@@ -36821,6 +36821,9 @@ function renderSkillMarkdown(cluster, mode = "active") {
36821
36821
  lines.push(`description: ${escapeYaml(description)}`);
36822
36822
  lines.push("generated_from_knowledge:");
36823
36823
  lines.push(ids);
36824
+ lines.push("source_knowledge_ids:");
36825
+ lines.push(ids);
36826
+ lines.push(`generated_at: ${generatedAt}`);
36824
36827
  lines.push(`confidence: ${cluster.avgConfidence.toFixed(2)}`);
36825
36828
  lines.push(`status: ${mode === "active" ? "active" : "draft"}`);
36826
36829
  lines.push("---");
@@ -37009,6 +37012,62 @@ async function stampSourceEntries(directory, slug, ids) {
37009
37012
  if (touchedHive)
37010
37013
  await rewriteKnowledge(hivePath, hive);
37011
37014
  }
37015
+ function parseDraftFrontmatter(content) {
37016
+ const stripped = content.charCodeAt(0) === 65279 ? content.slice(1) : content;
37017
+ const openFence = stripped.match(/^---[ \t]*\r?\n/);
37018
+ if (!openFence)
37019
+ return null;
37020
+ const fenceLen = openFence[0].length;
37021
+ const closeFence = stripped.slice(fenceLen).match(/\n---[ \t]*(\r?\n|$)/);
37022
+ if (!closeFence)
37023
+ return null;
37024
+ const closeStart = fenceLen + (closeFence.index ?? 0);
37025
+ const body = stripped.slice(fenceLen, closeStart).replace(/\r\n/g, `
37026
+ `);
37027
+ const lines = body.split(`
37028
+ `);
37029
+ const out = {
37030
+ sourceKnowledgeIds: []
37031
+ };
37032
+ let inLegacyIdsList = false;
37033
+ let inSourceIdsList = false;
37034
+ for (const raw of lines) {
37035
+ const line = raw;
37036
+ if (inLegacyIdsList || inSourceIdsList) {
37037
+ const m = line.match(/^\s+-\s+(\S{1,64})\s*$/);
37038
+ if (m) {
37039
+ out.sourceKnowledgeIds.push(m[1]);
37040
+ continue;
37041
+ }
37042
+ inLegacyIdsList = false;
37043
+ inSourceIdsList = false;
37044
+ }
37045
+ const nm = line.match(/^name:\s*(\S+)\s*$/);
37046
+ if (nm) {
37047
+ out.name = nm[1];
37048
+ continue;
37049
+ }
37050
+ const st = line.match(/^status:\s*(\S+)\s*$/);
37051
+ if (st) {
37052
+ out.status = st[1];
37053
+ continue;
37054
+ }
37055
+ const ga = line.match(/^generated_at:\s*(\S+)\s*$/);
37056
+ if (ga) {
37057
+ out.generatedAt = ga[1];
37058
+ continue;
37059
+ }
37060
+ if (/^generated_from_knowledge:\s*$/.test(line)) {
37061
+ inLegacyIdsList = true;
37062
+ continue;
37063
+ }
37064
+ if (/^source_knowledge_ids:\s*$/.test(line)) {
37065
+ out.sourceKnowledgeIds = [];
37066
+ inSourceIdsList = true;
37067
+ }
37068
+ }
37069
+ return out;
37070
+ }
37012
37071
  async function listSkills(directory) {
37013
37072
  const result = {
37014
37073
  proposals: [],
@@ -38338,7 +38397,7 @@ var init_skill_improver_quota = __esm(() => {
38338
38397
 
38339
38398
  // src/services/skill-improver.ts
38340
38399
  import { existsSync as existsSync12 } from "fs";
38341
- import { mkdir as mkdir7, rename as rename5, writeFile as writeFile6 } from "fs/promises";
38400
+ import { mkdir as mkdir7, readFile as readFile7, rename as rename5, writeFile as writeFile6 } from "fs/promises";
38342
38401
  import * as path18 from "path";
38343
38402
  function timestampSlug(d) {
38344
38403
  return d.toISOString().replace(/[:.]/g, "-");
@@ -38355,15 +38414,63 @@ async function gatherInventory(directory) {
38355
38414
  const hive = existsSync12(hivePath) ? await readKnowledge(hivePath) : [];
38356
38415
  const archived = [...swarm, ...hive].filter((e) => e.status === "archived").length;
38357
38416
  const skills = await listSkills(directory);
38417
+ const knowledgeById = new Map([...swarm, ...hive].map((entry) => [entry.id, entry]));
38418
+ const staleActiveSkills = [];
38419
+ let metadataReadable = 0;
38420
+ for (const skill of skills.active) {
38421
+ let content;
38422
+ try {
38423
+ content = await readFile7(skill.path, "utf-8");
38424
+ } catch {
38425
+ continue;
38426
+ }
38427
+ const fm = parseDraftFrontmatter(content);
38428
+ if (!fm)
38429
+ continue;
38430
+ metadataReadable += 1;
38431
+ const reasons = [];
38432
+ if (fm.sourceKnowledgeIds.length === 0) {
38433
+ reasons.push("missing_source_knowledge_ids");
38434
+ }
38435
+ if (!fm.generatedAt) {
38436
+ reasons.push("missing_generated_at");
38437
+ } else {
38438
+ const generatedAtMs = Date.parse(fm.generatedAt);
38439
+ if (!Number.isFinite(generatedAtMs)) {
38440
+ reasons.push("invalid_generated_at");
38441
+ } else {
38442
+ for (const id of fm.sourceKnowledgeIds) {
38443
+ const source = knowledgeById.get(id);
38444
+ if (!source) {
38445
+ reasons.push(`missing_source:${id}`);
38446
+ continue;
38447
+ }
38448
+ const updatedAtMs = Date.parse(source.updated_at);
38449
+ if (Number.isFinite(updatedAtMs) && updatedAtMs > generatedAtMs) {
38450
+ reasons.push(`updated_after_generation:${id}`);
38451
+ }
38452
+ }
38453
+ }
38454
+ }
38455
+ if (reasons.length > 0) {
38456
+ staleActiveSkills.push({
38457
+ slug: skill.slug,
38458
+ reasons: reasons.slice(0, 6)
38459
+ });
38460
+ }
38461
+ }
38358
38462
  const matureCandidates = swarm.concat(hive).filter((e) => e.status !== "archived" && e.confidence >= 0.85 && !e.generated_skill_slug && (e.confirmed_by ?? []).length >= 2);
38359
38463
  return {
38360
38464
  knowledge: { swarm: swarm.length, hive: hive.length, archived },
38361
38465
  skills: {
38362
38466
  proposals: skills.proposals.length,
38363
- active: skills.active.length
38467
+ active: skills.active.length,
38468
+ stale: staleActiveSkills.length,
38469
+ metadataReadable
38364
38470
  },
38365
38471
  highConfidenceClusters: matureCandidates.length,
38366
- matureCandidates
38472
+ matureCandidates,
38473
+ staleActiveSkills
38367
38474
  };
38368
38475
  }
38369
38476
  function buildSystemPrompt(targets, cfg) {
@@ -38387,10 +38494,16 @@ hive_entries: ${inv.knowledge.hive}
38387
38494
  archived: ${inv.knowledge.archived}
38388
38495
  draft_skills: ${inv.skills.proposals}
38389
38496
  active_skills: ${inv.skills.active}
38497
+ active_skills_with_readable_metadata: ${inv.skills.metadataReadable}
38498
+ stale_active_skills: ${inv.skills.stale}
38390
38499
  mature_uncompiled_clusters: ${inv.highConfidenceClusters}
38391
38500
 
38392
38501
  TOP MATURE CANDIDATES (first 25):
38393
38502
  ${matureRows || "(none)"}
38503
+
38504
+ STALE ACTIVE SKILLS (first 10):
38505
+ ${inv.staleActiveSkills.slice(0, 10).map((s) => `- ${s.slug} | ${s.reasons.join(", ")}`).join(`
38506
+ `) || "(none)"}
38394
38507
  `;
38395
38508
  }
38396
38509
  function isAbortError(err) {
@@ -38419,7 +38532,11 @@ function buildDeterministicProposal(args) {
38419
38532
  lines.push("## Inventory snapshot");
38420
38533
  lines.push(`- Knowledge entries: swarm=${args.inventory.knowledge.swarm}, hive=${args.inventory.knowledge.hive}, archived=${args.inventory.knowledge.archived}`);
38421
38534
  lines.push(`- Generated skills: proposals=${args.inventory.skills.proposals}, active=${args.inventory.skills.active}`);
38535
+ lines.push(`- Active skills with readable metadata: ${args.inventory.skills.metadataReadable} (stale=${args.inventory.skills.stale})`);
38422
38536
  lines.push(`- High-confidence un-skill'd clusters: ${args.inventory.highConfidenceClusters}`);
38537
+ if (args.inventory.staleActiveSkills.length > 0) {
38538
+ lines.push(`- Stale active skills: ${args.inventory.staleActiveSkills.map((s) => s.slug).join(", ")}`);
38539
+ }
38423
38540
  lines.push("");
38424
38541
  lines.push("## Recommendations");
38425
38542
  if (args.inventory.highConfidenceClusters > 0) {
@@ -39914,7 +40031,7 @@ var init_curate = __esm(() => {
39914
40031
  // src/tools/co-change-analyzer.ts
39915
40032
  import * as child_process3 from "child_process";
39916
40033
  import { randomUUID as randomUUID2 } from "crypto";
39917
- import { readdir, readFile as readFile7, stat as stat2 } from "fs/promises";
40034
+ import { readdir, readFile as readFile8, stat as stat2 } from "fs/promises";
39918
40035
  import * as path21 from "path";
39919
40036
  import { promisify } from "util";
39920
40037
  function getExecFileAsync() {
@@ -40041,7 +40158,7 @@ async function getStaticEdges(directory) {
40041
40158
  const sourceFiles = await scanSourceFiles(directory);
40042
40159
  for (const sourceFile of sourceFiles) {
40043
40160
  try {
40044
- const content = await readFile7(sourceFile, "utf-8");
40161
+ const content = await readFile8(sourceFile, "utf-8");
40045
40162
  const importRegex = /(?:import|require)\s*(?:\(?\s*['"`]|.*?from\s+['"`])([^'"`]+)['"`]/g;
40046
40163
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
40047
40164
  const importPath = match[1].trim();
@@ -40825,11 +40942,11 @@ var init_version_check = __esm(() => {
40825
40942
 
40826
40943
  // src/services/knowledge-diagnostics.ts
40827
40944
  import { existsSync as existsSync14 } from "fs";
40828
- import { readFile as readFile8 } from "fs/promises";
40945
+ import { readFile as readFile9 } from "fs/promises";
40829
40946
  async function readRawLines(filePath) {
40830
40947
  if (!existsSync14(filePath))
40831
40948
  return { entries: [], corrupt: 0 };
40832
- const content = await readFile8(filePath, "utf-8");
40949
+ const content = await readFile9(filePath, "utf-8");
40833
40950
  const entries = [];
40834
40951
  let corrupt = 0;
40835
40952
  for (const line of content.split(`
@@ -46049,7 +46166,7 @@ var KNOWLEDGE_SCHEMA_VERSION = 2;
46049
46166
  // src/hooks/knowledge-migrator.ts
46050
46167
  import { randomUUID as randomUUID3 } from "crypto";
46051
46168
  import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
46052
- import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
46169
+ import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile7 } from "fs/promises";
46053
46170
  import * as path29 from "path";
46054
46171
  async function migrateKnowledgeToExternal(_directory, _config) {
46055
46172
  return {
@@ -46082,7 +46199,7 @@ async function migrateContextToKnowledge(directory, config3) {
46082
46199
  skippedReason: "no-context-file"
46083
46200
  };
46084
46201
  }
46085
- const contextContent = await readFile9(contextPath, "utf-8");
46202
+ const contextContent = await readFile10(contextPath, "utf-8");
46086
46203
  if (contextContent.trim().length === 0) {
46087
46204
  return {
46088
46205
  migrated: false,
@@ -47331,7 +47448,7 @@ import { existsSync as existsSync20 } from "fs";
47331
47448
  import {
47332
47449
  appendFile as appendFile5,
47333
47450
  mkdir as mkdir9,
47334
- readFile as readFile10,
47451
+ readFile as readFile11,
47335
47452
  rename as rename6,
47336
47453
  writeFile as writeFile8
47337
47454
  } from "fs/promises";
@@ -47680,7 +47797,7 @@ function validateLoadedProposals(values, config3) {
47680
47797
  async function readJsonl(filePath) {
47681
47798
  if (!existsSync20(filePath))
47682
47799
  return [];
47683
- const content = await readFile10(filePath, "utf-8");
47800
+ const content = await readFile11(filePath, "utf-8");
47684
47801
  const records = [];
47685
47802
  for (const line of content.split(`
47686
47803
  `)) {
@@ -47765,7 +47882,7 @@ var init_prompt_block = __esm(() => {
47765
47882
 
47766
47883
  // src/memory/jsonl-migration.ts
47767
47884
  import { existsSync as existsSync21 } from "fs";
47768
- import { copyFile, mkdir as mkdir10, readFile as readFile11, stat as stat3, writeFile as writeFile9 } from "fs/promises";
47885
+ import { copyFile, mkdir as mkdir10, readFile as readFile12, stat as stat3, writeFile as writeFile9 } from "fs/promises";
47769
47886
  import * as path31 from "path";
47770
47887
  function resolveMemoryStorageDir(rootDirectory, config3 = {}) {
47771
47888
  const resolved = resolveConfig(config3);
@@ -47829,7 +47946,7 @@ async function readMigrationReport(rootDirectory, config3 = {}) {
47829
47946
  if (!existsSync21(reportPath))
47830
47947
  return null;
47831
47948
  try {
47832
- return JSON.parse(await readFile11(reportPath, "utf-8"));
47949
+ return JSON.parse(await readFile12(reportPath, "utf-8"));
47833
47950
  } catch {
47834
47951
  return null;
47835
47952
  }
@@ -47930,7 +48047,7 @@ async function readJsonlRows(filePath) {
47930
48047
  if (!existsSync21(filePath)) {
47931
48048
  return { rows: [], invalidRows: [], totalRows: 0 };
47932
48049
  }
47933
- const content = await readFile11(filePath, "utf-8");
48050
+ const content = await readFile12(filePath, "utf-8");
47934
48051
  const rows = [];
47935
48052
  const invalidRows = [];
47936
48053
  let totalRows = 0;
package/dist/index.js CHANGED
@@ -69,7 +69,7 @@ var package_default;
69
69
  var init_package = __esm(() => {
70
70
  package_default = {
71
71
  name: "opencode-swarm",
72
- version: "7.55.0",
72
+ version: "7.56.0",
73
73
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
74
74
  main: "dist/index.js",
75
75
  types: "dist/index.d.ts",
@@ -57856,7 +57856,7 @@ function clusterEntries(entries) {
57856
57856
  function uniqueStrings(arr) {
57857
57857
  return [...new Set(arr.filter((s) => typeof s === "string" && s.length > 0))];
57858
57858
  }
57859
- function renderSkillMarkdown(cluster, mode = "active") {
57859
+ function renderSkillMarkdown(cluster, mode = "active", generatedAt = new Date().toISOString()) {
57860
57860
  const description = cluster.title.length > 200 ? `${cluster.title.slice(0, 197)}…` : cluster.title;
57861
57861
  const ids = cluster.entries.map((e) => ` - ${e.id}`).join(`
57862
57862
  `);
@@ -57866,6 +57866,9 @@ function renderSkillMarkdown(cluster, mode = "active") {
57866
57866
  lines.push(`description: ${escapeYaml(description)}`);
57867
57867
  lines.push("generated_from_knowledge:");
57868
57868
  lines.push(ids);
57869
+ lines.push("source_knowledge_ids:");
57870
+ lines.push(ids);
57871
+ lines.push(`generated_at: ${generatedAt}`);
57869
57872
  lines.push(`confidence: ${cluster.avgConfidence.toFixed(2)}`);
57870
57873
  lines.push(`status: ${mode === "active" ? "active" : "draft"}`);
57871
57874
  lines.push("---");
@@ -58071,16 +58074,18 @@ function parseDraftFrontmatter(content) {
58071
58074
  const out2 = {
58072
58075
  sourceKnowledgeIds: []
58073
58076
  };
58074
- let inIdsList = false;
58077
+ let inLegacyIdsList = false;
58078
+ let inSourceIdsList = false;
58075
58079
  for (const raw of lines) {
58076
58080
  const line = raw;
58077
- if (inIdsList) {
58081
+ if (inLegacyIdsList || inSourceIdsList) {
58078
58082
  const m = line.match(/^\s+-\s+(\S{1,64})\s*$/);
58079
58083
  if (m) {
58080
58084
  out2.sourceKnowledgeIds.push(m[1]);
58081
58085
  continue;
58082
58086
  }
58083
- inIdsList = false;
58087
+ inLegacyIdsList = false;
58088
+ inSourceIdsList = false;
58084
58089
  }
58085
58090
  const nm = line.match(/^name:\s*(\S+)\s*$/);
58086
58091
  if (nm) {
@@ -58092,8 +58097,18 @@ function parseDraftFrontmatter(content) {
58092
58097
  out2.status = st[1];
58093
58098
  continue;
58094
58099
  }
58100
+ const ga = line.match(/^generated_at:\s*(\S+)\s*$/);
58101
+ if (ga) {
58102
+ out2.generatedAt = ga[1];
58103
+ continue;
58104
+ }
58095
58105
  if (/^generated_from_knowledge:\s*$/.test(line)) {
58096
- inIdsList = true;
58106
+ inLegacyIdsList = true;
58107
+ continue;
58108
+ }
58109
+ if (/^source_knowledge_ids:\s*$/.test(line)) {
58110
+ out2.sourceKnowledgeIds = [];
58111
+ inSourceIdsList = true;
58097
58112
  }
58098
58113
  }
58099
58114
  return out2;
@@ -60763,7 +60778,7 @@ var init_skill_improver_quota = __esm(() => {
60763
60778
 
60764
60779
  // src/services/skill-improver.ts
60765
60780
  import { existsSync as existsSync18 } from "node:fs";
60766
- import { mkdir as mkdir8, rename as rename5, writeFile as writeFile6 } from "node:fs/promises";
60781
+ import { mkdir as mkdir8, readFile as readFile9, rename as rename5, writeFile as writeFile6 } from "node:fs/promises";
60767
60782
  import * as path31 from "node:path";
60768
60783
  function timestampSlug(d) {
60769
60784
  return d.toISOString().replace(/[:.]/g, "-");
@@ -60780,15 +60795,63 @@ async function gatherInventory(directory) {
60780
60795
  const hive = existsSync18(hivePath) ? await readKnowledge(hivePath) : [];
60781
60796
  const archived = [...swarm, ...hive].filter((e) => e.status === "archived").length;
60782
60797
  const skills = await listSkills(directory);
60798
+ const knowledgeById = new Map([...swarm, ...hive].map((entry) => [entry.id, entry]));
60799
+ const staleActiveSkills = [];
60800
+ let metadataReadable = 0;
60801
+ for (const skill of skills.active) {
60802
+ let content;
60803
+ try {
60804
+ content = await readFile9(skill.path, "utf-8");
60805
+ } catch {
60806
+ continue;
60807
+ }
60808
+ const fm = parseDraftFrontmatter(content);
60809
+ if (!fm)
60810
+ continue;
60811
+ metadataReadable += 1;
60812
+ const reasons = [];
60813
+ if (fm.sourceKnowledgeIds.length === 0) {
60814
+ reasons.push("missing_source_knowledge_ids");
60815
+ }
60816
+ if (!fm.generatedAt) {
60817
+ reasons.push("missing_generated_at");
60818
+ } else {
60819
+ const generatedAtMs = Date.parse(fm.generatedAt);
60820
+ if (!Number.isFinite(generatedAtMs)) {
60821
+ reasons.push("invalid_generated_at");
60822
+ } else {
60823
+ for (const id of fm.sourceKnowledgeIds) {
60824
+ const source = knowledgeById.get(id);
60825
+ if (!source) {
60826
+ reasons.push(`missing_source:${id}`);
60827
+ continue;
60828
+ }
60829
+ const updatedAtMs = Date.parse(source.updated_at);
60830
+ if (Number.isFinite(updatedAtMs) && updatedAtMs > generatedAtMs) {
60831
+ reasons.push(`updated_after_generation:${id}`);
60832
+ }
60833
+ }
60834
+ }
60835
+ }
60836
+ if (reasons.length > 0) {
60837
+ staleActiveSkills.push({
60838
+ slug: skill.slug,
60839
+ reasons: reasons.slice(0, 6)
60840
+ });
60841
+ }
60842
+ }
60783
60843
  const matureCandidates = swarm.concat(hive).filter((e) => e.status !== "archived" && e.confidence >= 0.85 && !e.generated_skill_slug && (e.confirmed_by ?? []).length >= 2);
60784
60844
  return {
60785
60845
  knowledge: { swarm: swarm.length, hive: hive.length, archived },
60786
60846
  skills: {
60787
60847
  proposals: skills.proposals.length,
60788
- active: skills.active.length
60848
+ active: skills.active.length,
60849
+ stale: staleActiveSkills.length,
60850
+ metadataReadable
60789
60851
  },
60790
60852
  highConfidenceClusters: matureCandidates.length,
60791
- matureCandidates
60853
+ matureCandidates,
60854
+ staleActiveSkills
60792
60855
  };
60793
60856
  }
60794
60857
  function buildSystemPrompt(targets, cfg) {
@@ -60812,10 +60875,16 @@ hive_entries: ${inv.knowledge.hive}
60812
60875
  archived: ${inv.knowledge.archived}
60813
60876
  draft_skills: ${inv.skills.proposals}
60814
60877
  active_skills: ${inv.skills.active}
60878
+ active_skills_with_readable_metadata: ${inv.skills.metadataReadable}
60879
+ stale_active_skills: ${inv.skills.stale}
60815
60880
  mature_uncompiled_clusters: ${inv.highConfidenceClusters}
60816
60881
 
60817
60882
  TOP MATURE CANDIDATES (first 25):
60818
60883
  ${matureRows || "(none)"}
60884
+
60885
+ STALE ACTIVE SKILLS (first 10):
60886
+ ${inv.staleActiveSkills.slice(0, 10).map((s) => `- ${s.slug} | ${s.reasons.join(", ")}`).join(`
60887
+ `) || "(none)"}
60819
60888
  `;
60820
60889
  }
60821
60890
  function isAbortError(err2) {
@@ -60844,7 +60913,11 @@ function buildDeterministicProposal(args2) {
60844
60913
  lines.push("## Inventory snapshot");
60845
60914
  lines.push(`- Knowledge entries: swarm=${args2.inventory.knowledge.swarm}, hive=${args2.inventory.knowledge.hive}, archived=${args2.inventory.knowledge.archived}`);
60846
60915
  lines.push(`- Generated skills: proposals=${args2.inventory.skills.proposals}, active=${args2.inventory.skills.active}`);
60916
+ lines.push(`- Active skills with readable metadata: ${args2.inventory.skills.metadataReadable} (stale=${args2.inventory.skills.stale})`);
60847
60917
  lines.push(`- High-confidence un-skill'd clusters: ${args2.inventory.highConfidenceClusters}`);
60918
+ if (args2.inventory.staleActiveSkills.length > 0) {
60919
+ lines.push(`- Stale active skills: ${args2.inventory.staleActiveSkills.map((s) => s.slug).join(", ")}`);
60920
+ }
60848
60921
  lines.push("");
60849
60922
  lines.push("## Recommendations");
60850
60923
  if (args2.inventory.highConfidenceClusters > 0) {
@@ -62350,7 +62423,7 @@ __export(exports_co_change_analyzer, {
62350
62423
  });
62351
62424
  import * as child_process3 from "node:child_process";
62352
62425
  import { randomUUID as randomUUID5 } from "node:crypto";
62353
- import { readdir as readdir2, readFile as readFile9, stat as stat3 } from "node:fs/promises";
62426
+ import { readdir as readdir2, readFile as readFile10, stat as stat3 } from "node:fs/promises";
62354
62427
  import * as path34 from "node:path";
62355
62428
  import { promisify } from "node:util";
62356
62429
  function getExecFileAsync() {
@@ -62477,7 +62550,7 @@ async function getStaticEdges(directory) {
62477
62550
  const sourceFiles = await scanSourceFiles(directory);
62478
62551
  for (const sourceFile of sourceFiles) {
62479
62552
  try {
62480
- const content = await readFile9(sourceFile, "utf-8");
62553
+ const content = await readFile10(sourceFile, "utf-8");
62481
62554
  const importRegex = /(?:import|require)\s*(?:\(?\s*['"`]|.*?from\s+['"`])([^'"`]+)['"`]/g;
62482
62555
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
62483
62556
  const importPath = match[1].trim();
@@ -63239,11 +63312,11 @@ var init_version_check = __esm(() => {
63239
63312
 
63240
63313
  // src/services/knowledge-diagnostics.ts
63241
63314
  import { existsSync as existsSync20 } from "node:fs";
63242
- import { readFile as readFile10 } from "node:fs/promises";
63315
+ import { readFile as readFile11 } from "node:fs/promises";
63243
63316
  async function readRawLines(filePath) {
63244
63317
  if (!existsSync20(filePath))
63245
63318
  return { entries: [], corrupt: 0 };
63246
- const content = await readFile10(filePath, "utf-8");
63319
+ const content = await readFile11(filePath, "utf-8");
63247
63320
  const entries = [];
63248
63321
  let corrupt = 0;
63249
63322
  for (const line of content.split(`
@@ -68601,7 +68674,7 @@ var KNOWLEDGE_SCHEMA_VERSION = 2;
68601
68674
  // src/hooks/knowledge-migrator.ts
68602
68675
  import { randomUUID as randomUUID6 } from "node:crypto";
68603
68676
  import { existsSync as existsSync25, readFileSync as readFileSync14 } from "node:fs";
68604
- import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile7 } from "node:fs/promises";
68677
+ import { mkdir as mkdir9, readFile as readFile12, writeFile as writeFile7 } from "node:fs/promises";
68605
68678
  import * as path42 from "node:path";
68606
68679
  async function migrateKnowledgeToExternal(_directory, _config) {
68607
68680
  return {
@@ -68634,7 +68707,7 @@ async function migrateContextToKnowledge(directory, config3) {
68634
68707
  skippedReason: "no-context-file"
68635
68708
  };
68636
68709
  }
68637
- const contextContent = await readFile11(contextPath, "utf-8");
68710
+ const contextContent = await readFile12(contextPath, "utf-8");
68638
68711
  if (contextContent.trim().length === 0) {
68639
68712
  return {
68640
68713
  migrated: false,
@@ -69912,7 +69985,7 @@ import { existsSync as existsSync26 } from "node:fs";
69912
69985
  import {
69913
69986
  appendFile as appendFile6,
69914
69987
  mkdir as mkdir10,
69915
- readFile as readFile12,
69988
+ readFile as readFile13,
69916
69989
  rename as rename6,
69917
69990
  writeFile as writeFile8
69918
69991
  } from "node:fs/promises";
@@ -70261,7 +70334,7 @@ function validateLoadedProposals(values, config3) {
70261
70334
  async function readJsonl(filePath) {
70262
70335
  if (!existsSync26(filePath))
70263
70336
  return [];
70264
- const content = await readFile12(filePath, "utf-8");
70337
+ const content = await readFile13(filePath, "utf-8");
70265
70338
  const records = [];
70266
70339
  for (const line of content.split(`
70267
70340
  `)) {
@@ -70420,7 +70493,7 @@ var init_prompt_block = __esm(() => {
70420
70493
 
70421
70494
  // src/memory/jsonl-migration.ts
70422
70495
  import { existsSync as existsSync27 } from "node:fs";
70423
- import { copyFile, mkdir as mkdir11, readFile as readFile13, stat as stat4, writeFile as writeFile9 } from "node:fs/promises";
70496
+ import { copyFile, mkdir as mkdir11, readFile as readFile14, stat as stat4, writeFile as writeFile9 } from "node:fs/promises";
70424
70497
  import * as path44 from "node:path";
70425
70498
  function resolveMemoryStorageDir(rootDirectory, config3 = {}) {
70426
70499
  const resolved = resolveConfig(config3);
@@ -70484,7 +70557,7 @@ async function readMigrationReport(rootDirectory, config3 = {}) {
70484
70557
  if (!existsSync27(reportPath))
70485
70558
  return null;
70486
70559
  try {
70487
- return JSON.parse(await readFile13(reportPath, "utf-8"));
70560
+ return JSON.parse(await readFile14(reportPath, "utf-8"));
70488
70561
  } catch {
70489
70562
  return null;
70490
70563
  }
@@ -70585,7 +70658,7 @@ async function readJsonlRows(filePath) {
70585
70658
  if (!existsSync27(filePath)) {
70586
70659
  return { rows: [], invalidRows: [], totalRows: 0 };
70587
70660
  }
70588
- const content = await readFile13(filePath, "utf-8");
70661
+ const content = await readFile14(filePath, "utf-8");
70589
70662
  const rows = [];
70590
70663
  const invalidRows = [];
70591
70664
  let totalRows = 0;
@@ -91719,7 +91792,7 @@ import * as fs65 from "node:fs";
91719
91792
  import {
91720
91793
  mkdir as mkdir16,
91721
91794
  readdir as readdir6,
91722
- readFile as readFile16,
91795
+ readFile as readFile17,
91723
91796
  realpath as realpath3,
91724
91797
  stat as stat7,
91725
91798
  writeFile as writeFile12
@@ -91797,7 +91870,7 @@ async function scanDocIndex(directory) {
91797
91870
  ];
91798
91871
  const allPatterns = [...defaultPatterns, ...extraPatterns];
91799
91872
  try {
91800
- const manifestContent = await readFile16(manifestPath, "utf-8");
91873
+ const manifestContent = await readFile17(manifestPath, "utf-8");
91801
91874
  const existingManifest = JSON.parse(manifestContent);
91802
91875
  if (existingManifest.schema_version === 1 && existingManifest.files) {
91803
91876
  let cacheValid = true;
@@ -91878,7 +91951,7 @@ async function scanDocIndex(directory) {
91878
91951
  }
91879
91952
  let content;
91880
91953
  try {
91881
- content = await readFile16(fullPath, "utf-8");
91954
+ content = await readFile17(fullPath, "utf-8");
91882
91955
  } catch {
91883
91956
  continue;
91884
91957
  }
@@ -91961,7 +92034,7 @@ async function extractDocConstraints(directory, taskFiles, taskDescription) {
91961
92034
  const manifestPath = path102.join(directory, ".swarm", "doc-manifest.json");
91962
92035
  let manifest;
91963
92036
  try {
91964
- const content = await readFile16(manifestPath, "utf-8");
92037
+ const content = await readFile17(manifestPath, "utf-8");
91965
92038
  manifest = JSON.parse(content);
91966
92039
  } catch {
91967
92040
  const result = await scanDocIndex(directory);
@@ -91984,7 +92057,7 @@ async function extractDocConstraints(directory, taskFiles, taskDescription) {
91984
92057
  }
91985
92058
  let fullContent;
91986
92059
  try {
91987
- fullContent = await readFile16(path102.join(directory, docFile.path), "utf-8");
92060
+ fullContent = await readFile17(path102.join(directory, docFile.path), "utf-8");
91988
92061
  } catch {
91989
92062
  skippedCount++;
91990
92063
  continue;
@@ -92134,7 +92207,7 @@ var init_doc_scan = __esm(() => {
92134
92207
 
92135
92208
  // src/hooks/knowledge-reader.ts
92136
92209
  import { existsSync as existsSync57 } from "node:fs";
92137
- import { readFile as readFile17 } from "node:fs/promises";
92210
+ import { readFile as readFile18 } from "node:fs/promises";
92138
92211
  import * as path103 from "node:path";
92139
92212
  function inferCategoriesFromPhase(phaseDescription) {
92140
92213
  const lower = phaseDescription.toLowerCase();
@@ -92184,7 +92257,7 @@ async function transactShownFile(shownFile, mutate) {
92184
92257
  if (!existsSync57(filePath))
92185
92258
  return {};
92186
92259
  try {
92187
- const content = await readFile17(filePath, "utf-8");
92260
+ const content = await readFile18(filePath, "utf-8");
92188
92261
  return JSON.parse(content);
92189
92262
  } catch {
92190
92263
  return {};
@@ -92311,7 +92384,7 @@ async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
92311
92384
  }
92312
92385
  let shownIds;
92313
92386
  try {
92314
- const content = await readFile17(shownFile, "utf-8");
92387
+ const content = await readFile18(shownFile, "utf-8");
92315
92388
  const shownData = JSON.parse(content);
92316
92389
  shownIds = shownData[phaseInfo];
92317
92390
  } catch {
@@ -104427,7 +104500,7 @@ init_logger();
104427
104500
  init_knowledge_store();
104428
104501
  var import_proper_lockfile8 = __toESM(require_proper_lockfile(), 1);
104429
104502
  import { existsSync as existsSync61 } from "node:fs";
104430
- import { appendFile as appendFile9, mkdir as mkdir17, readFile as readFile18 } from "node:fs/promises";
104503
+ import { appendFile as appendFile9, mkdir as mkdir17, readFile as readFile19 } from "node:fs/promises";
104431
104504
  import * as path107 from "node:path";
104432
104505
  function resolveApplicationLogPath(directory) {
104433
104506
  return path107.join(directory, ".swarm", "knowledge-application.jsonl");
@@ -42,7 +42,7 @@ export declare function selectCandidateEntries(directory: string, opts: Candidat
42
42
  */
43
43
  declare function jaccardSimilarity(setA: string[], setB: string[]): number;
44
44
  export declare function clusterEntries(entries: KnowledgeEntryBase[]): KnowledgeCluster[];
45
- export declare function renderSkillMarkdown(cluster: KnowledgeCluster, mode?: GenerateMode): string;
45
+ export declare function renderSkillMarkdown(cluster: KnowledgeCluster, mode?: GenerateMode, generatedAt?: string): string;
46
46
  export type GenerateMode = 'draft' | 'active';
47
47
  export interface GenerateRequest {
48
48
  directory: string;
@@ -84,6 +84,7 @@ declare function stampSourceEntries(directory: string, slug: string, ids: string
84
84
  export declare function parseDraftFrontmatter(content: string): {
85
85
  name?: string;
86
86
  status?: string;
87
+ generatedAt?: string;
87
88
  sourceKnowledgeIds: string[];
88
89
  } | null;
89
90
  export declare function activateProposal(directory: string, slug: string, force?: boolean): Promise<{
@@ -72,9 +72,15 @@ interface InventorySnapshot {
72
72
  skills: {
73
73
  proposals: number;
74
74
  active: number;
75
+ stale: number;
76
+ metadataReadable: number;
75
77
  };
76
78
  highConfidenceClusters: number;
77
79
  matureCandidates: SwarmKnowledgeEntry[];
80
+ staleActiveSkills: Array<{
81
+ slug: string;
82
+ reasons: string[];
83
+ }>;
78
84
  }
79
85
  declare function gatherInventory(directory: string): Promise<InventorySnapshot>;
80
86
  declare function buildSystemPrompt(targets: SkillImproverTarget[], cfg: SkillImproverConfigInput): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.55.0",
3
+ "version": "7.56.0",
4
4
  "description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",