agentsmesh 0.18.0 → 0.18.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.18.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d7e3a19: Internal refactor of the skill-import dispatch path and watch-test determinism — no user-visible behavior change.
8
+
9
+ **`agentsmesh import` (Cline / Windsurf / Codex CLI)**
10
+
11
+ The per-target `readdir → readFileSafe(SKILL.md) → try recognizer → fall back to skill import` loops, which had drifted independently and kept causing the same class of bug (lessons L132), are consolidated behind one shared orchestrator: `importSkillsDirectory(sourceSkillsDirs, options, recognizers)` plus `projectedAgentRecognizer({ canonicalAgentsDir })` and `commandSkillRecognizer({ canonicalCommandsDir })` factories. Each adapter now collapses to a config object plus a recognizer list. A minor side-effect of the consolidation: Cline now performs the same stale-skill-dir cleanup on re-import that Windsurf and Codex already did, so previously orphaned `.agentsmesh/skills/am-agent-<name>/` directories from buggy earlier runs are removed when a Cline projected-agent skill is re-imported.
12
+
13
+ **`agentsmesh watch`**
14
+
15
+ `runWatch` now accepts an optional `onCycle({ featuresChanged })` callback fired once per completed generate cycle (initial + each debounced regen). This is a deterministic synchronization signal for tests/integrations that previously relied on scraping `'Regenerated.'` log lines. The unrelated, dead `_suppressAgentsmeshDirUntil` parameter is removed from `shouldIgnoreWatchPath`. Watch-test budget bumped from 45s → 60s base (×1.5 under coverage = 90s) to survive full-suite scheduler load on macOS FSEvents.
16
+
17
+ **Developer tooling**
18
+
19
+ New `pnpm flake:watch` script (`scripts/flake-check-watch.ts`) runs the watch unit suite N times under `COVERAGE=1` to validate stability whenever the watch loop or scheduler envelope changes.
20
+
21
+ **Internal**
22
+ - New file: `src/targets/import/shared/skill-import-pipeline.ts` gains `importSkillsDirectory`, `projectedAgentRecognizer`, `commandSkillRecognizer`, `SkillRecognizer`, `SkillRecognizerContext` exports. The dead `SkillImportOptions.sourceSkillsDir` field is removed; `sourceSkillsDirs` is now a required first argument to the orchestrator.
23
+ - Adapter migrations: `cline/skills-adapter.ts`, `windsurf/skills-adapter.ts`, `codex-cli/skills-adapter.ts` reduced to thin wrappers. `cursor/skills-adapter.ts` and `copilot/skills-adapter.ts` shed the now-dead option field.
24
+ - 17 new unit tests; touched-scope branch coverage at 100% (25/25).
25
+ - `runWatch` adds `RunWatchOptions` + `WatchCycleInfo` types. The new third parameter is optional and backwards-compatible.
26
+
3
27
  ## 0.18.0
4
28
 
5
29
  ### Minor Changes
package/dist/canonical.js CHANGED
@@ -5890,55 +5890,117 @@ async function findDirectorySkills(skillsDir) {
5890
5890
  }
5891
5891
  return skills;
5892
5892
  }
5893
+ async function importSkillsDirectory(sourceSkillsDirs, options, recognizers = []) {
5894
+ for (const sourceDir of sourceSkillsDirs) {
5895
+ const absSkillsDir = join(options.projectRoot, sourceDir);
5896
+ const directorySkills = await findDirectorySkills(absSkillsDir);
5897
+ if (directorySkills.size === 0) continue;
5898
+ let importedAny = false;
5899
+ for (const [skillName, skillDir] of directorySkills) {
5900
+ const skillMdPath = join(skillDir, "SKILL.md");
5901
+ const rawContent = await readFileSafe(skillMdPath);
5902
+ if (rawContent === null) continue;
5903
+ importedAny = true;
5904
+ const { frontmatter, body: rawBody } = parseFrontmatter(rawContent);
5905
+ const ctx = {
5906
+ skillName,
5907
+ skillDir,
5908
+ skillMdPath,
5909
+ rawContent,
5910
+ frontmatter,
5911
+ rawBody,
5912
+ options
5913
+ };
5914
+ let handled = false;
5915
+ for (const recognizer of recognizers) {
5916
+ const claimed = await recognizer.recognize(ctx);
5917
+ if (claimed) {
5918
+ handled = true;
5919
+ break;
5920
+ }
5921
+ }
5922
+ if (!handled) {
5923
+ await importDirectorySkill(skillName, skillDir, options);
5924
+ }
5925
+ }
5926
+ if (importedAny) return;
5927
+ }
5928
+ }
5929
+ function projectedAgentRecognizer(config) {
5930
+ return {
5931
+ async recognize(ctx) {
5932
+ const projectedAgent = parseProjectedAgentSkillFrontmatter(ctx.frontmatter, ctx.skillName);
5933
+ if (!projectedAgent) return false;
5934
+ const { options } = ctx;
5935
+ await removePathIfExists(
5936
+ join(options.projectRoot, options.destCanonicalSkillsDir, ctx.skillName)
5937
+ );
5938
+ const destAgentsDir = join(options.projectRoot, config.canonicalAgentsDir);
5939
+ await mkdirp(destAgentsDir);
5940
+ const agentPath = join(destAgentsDir, `${projectedAgent.name}.md`);
5941
+ const normalizedBody = options.normalize(ctx.rawBody, ctx.skillMdPath, agentPath);
5942
+ await writeFileAtomic(agentPath, serializeImportedAgent(projectedAgent, normalizedBody));
5943
+ options.results.push({
5944
+ fromTool: options.targetName,
5945
+ fromPath: ctx.skillMdPath,
5946
+ toPath: `${config.canonicalAgentsDir}/${projectedAgent.name}.md`,
5947
+ feature: "agents"
5948
+ });
5949
+ return true;
5950
+ }
5951
+ };
5952
+ }
5953
+ function commandSkillRecognizer(config) {
5954
+ return {
5955
+ async recognize(ctx) {
5956
+ const command = parseCommandSkillFrontmatter(ctx.frontmatter, ctx.skillName);
5957
+ if (!command) return false;
5958
+ const { options } = ctx;
5959
+ await removePathIfExists(
5960
+ join(options.projectRoot, options.destCanonicalSkillsDir, ctx.skillName)
5961
+ );
5962
+ const destCommandsDir = join(options.projectRoot, config.canonicalCommandsDir);
5963
+ await mkdirp(destCommandsDir);
5964
+ const commandPath = join(destCommandsDir, `${command.name}.md`);
5965
+ const normalizedBody = options.normalize(ctx.rawBody, ctx.skillMdPath, commandPath);
5966
+ await writeFileAtomic(commandPath, serializeImportedCommand(command, normalizedBody));
5967
+ options.results.push({
5968
+ fromTool: options.targetName,
5969
+ fromPath: ctx.skillMdPath,
5970
+ toPath: `${config.canonicalCommandsDir}/${command.name}.md`,
5971
+ feature: "commands"
5972
+ });
5973
+ return true;
5974
+ }
5975
+ };
5976
+ }
5893
5977
  var init_skill_import_pipeline = __esm({
5894
5978
  "src/targets/import/shared/skill-import-pipeline.ts"() {
5895
5979
  init_fs();
5896
5980
  init_markdown();
5897
5981
  init_import_metadata();
5898
5982
  init_reserved();
5983
+ init_projected_agent_skill();
5984
+ init_command_skill();
5985
+ init_scoped_agents_import();
5899
5986
  }
5900
5987
  });
5988
+
5989
+ // src/targets/cline/skills-adapter.ts
5901
5990
  async function importClineSkills(projectRoot, results, normalize, skillsRelDir = CLINE_SKILLS_DIR) {
5902
- const skillsDir = join(projectRoot, skillsRelDir);
5903
- const directorySkills = await findDirectorySkills(skillsDir);
5904
5991
  const options = {
5905
5992
  projectRoot,
5906
- sourceSkillsDir: skillsRelDir,
5907
5993
  destCanonicalSkillsDir: CLINE_CANONICAL_SKILLS_DIR,
5908
5994
  targetName: "cline",
5909
5995
  normalize,
5910
5996
  results
5911
5997
  };
5912
- for (const [skillName, skillDir] of directorySkills) {
5913
- const skillMdPath = join(skillDir, "SKILL.md");
5914
- const content = await readFileSafe(skillMdPath);
5915
- if (!content) continue;
5916
- const rawParsed = parseFrontmatter(content);
5917
- const projectedAgent = parseProjectedAgentSkillFrontmatter(rawParsed.frontmatter, skillName);
5918
- if (projectedAgent) {
5919
- const destAgentsDir = join(projectRoot, CLINE_CANONICAL_AGENTS_DIR);
5920
- await mkdirp(destAgentsDir);
5921
- const agentPath = join(destAgentsDir, `${projectedAgent.name}.md`);
5922
- await writeFileAtomic(
5923
- agentPath,
5924
- serializeImportedAgent(projectedAgent, normalize(rawParsed.body, skillMdPath, agentPath))
5925
- );
5926
- results.push({
5927
- fromTool: "cline",
5928
- fromPath: skillMdPath,
5929
- toPath: `${CLINE_CANONICAL_AGENTS_DIR}/${projectedAgent.name}.md`,
5930
- feature: "agents"
5931
- });
5932
- continue;
5933
- }
5934
- await importDirectorySkill(skillName, skillDir, options);
5935
- }
5998
+ await importSkillsDirectory([skillsRelDir], options, [
5999
+ projectedAgentRecognizer({ canonicalAgentsDir: CLINE_CANONICAL_AGENTS_DIR })
6000
+ ]);
5936
6001
  }
5937
6002
  var init_skills_adapter = __esm({
5938
6003
  "src/targets/cline/skills-adapter.ts"() {
5939
- init_fs();
5940
- init_markdown();
5941
- init_projected_agent_skill();
5942
6004
  init_skill_import_pipeline();
5943
6005
  init_constants8();
5944
6006
  }
@@ -6601,82 +6663,23 @@ var init_mcp_helpers = __esm({
6601
6663
  init_constants28();
6602
6664
  }
6603
6665
  });
6666
+
6667
+ // src/targets/codex-cli/skills-adapter.ts
6604
6668
  async function importSkills(projectRoot, results, normalize) {
6605
6669
  const options = {
6606
6670
  projectRoot,
6607
- sourceSkillsDir: CODEX_SKILLS_DIR,
6608
6671
  destCanonicalSkillsDir: CODEX_CANONICAL_SKILLS_DIR,
6609
6672
  targetName: CODEX_TARGET,
6610
6673
  normalize,
6611
6674
  results
6612
6675
  };
6613
- for (const skillsRoot of [CODEX_SKILLS_DIR, CODEX_SKILLS_FALLBACK_DIR]) {
6614
- const skillsDir = join(projectRoot, skillsRoot);
6615
- const entries = await readdir(skillsDir, {
6616
- encoding: "utf8",
6617
- withFileTypes: true
6618
- }).catch(() => null);
6619
- if (entries === null) continue;
6620
- let importedAny = false;
6621
- for (const ent of entries) {
6622
- if (!ent.isDirectory() && !ent.isSymbolicLink()) continue;
6623
- const skillPath = join(skillsDir, ent.name);
6624
- const skillMdPath = join(skillPath, "SKILL.md");
6625
- const skillMdContent = await readFileSafe(skillMdPath);
6626
- if (!skillMdContent) continue;
6627
- importedAny = true;
6628
- const skillName = ent.name;
6629
- const destSkillPath = join(projectRoot, CODEX_CANONICAL_SKILLS_DIR, skillName, "SKILL.md");
6630
- const normalized = normalize(skillMdContent, skillMdPath, destSkillPath);
6631
- const { frontmatter, body } = parseFrontmatter(normalized);
6632
- const command = parseCommandSkillFrontmatter(frontmatter, ent.name);
6633
- if (command) {
6634
- await removePathIfExists(join(projectRoot, CODEX_CANONICAL_SKILLS_DIR, skillName));
6635
- const destCommandsDir = join(projectRoot, CODEX_CANONICAL_COMMANDS_DIR);
6636
- await mkdirp(destCommandsDir);
6637
- const commandPath = join(destCommandsDir, `${command.name}.md`);
6638
- await writeFileAtomic(
6639
- commandPath,
6640
- serializeImportedCommand(command, normalize(body, skillMdPath, commandPath))
6641
- );
6642
- results.push({
6643
- fromTool: CODEX_TARGET,
6644
- fromPath: skillMdPath,
6645
- toPath: `${CODEX_CANONICAL_COMMANDS_DIR}/${command.name}.md`,
6646
- feature: "commands"
6647
- });
6648
- continue;
6649
- }
6650
- const projectedAgent = parseProjectedAgentSkillFrontmatter(frontmatter, ent.name);
6651
- if (projectedAgent) {
6652
- await removePathIfExists(join(projectRoot, CODEX_CANONICAL_SKILLS_DIR, skillName));
6653
- const destAgentsDir = join(projectRoot, CODEX_CANONICAL_AGENTS_DIR);
6654
- await mkdirp(destAgentsDir);
6655
- const agentPath = join(destAgentsDir, `${projectedAgent.name}.md`);
6656
- await writeFileAtomic(
6657
- agentPath,
6658
- serializeImportedAgent(projectedAgent, normalize(body, skillMdPath, agentPath))
6659
- );
6660
- results.push({
6661
- fromTool: CODEX_TARGET,
6662
- fromPath: skillMdPath,
6663
- toPath: `${CODEX_CANONICAL_AGENTS_DIR}/${projectedAgent.name}.md`,
6664
- feature: "agents"
6665
- });
6666
- continue;
6667
- }
6668
- await importDirectorySkill(skillName, skillPath, options);
6669
- }
6670
- if (importedAny) return;
6671
- }
6676
+ await importSkillsDirectory([CODEX_SKILLS_DIR, CODEX_SKILLS_FALLBACK_DIR], options, [
6677
+ commandSkillRecognizer({ canonicalCommandsDir: CODEX_CANONICAL_COMMANDS_DIR }),
6678
+ projectedAgentRecognizer({ canonicalAgentsDir: CODEX_CANONICAL_AGENTS_DIR })
6679
+ ]);
6672
6680
  }
6673
6681
  var init_skills_adapter2 = __esm({
6674
6682
  "src/targets/codex-cli/skills-adapter.ts"() {
6675
- init_fs();
6676
- init_markdown();
6677
- init_command_skill();
6678
- init_projected_agent_skill();
6679
- init_scoped_agents_import();
6680
6683
  init_skill_import_pipeline();
6681
6684
  init_constants28();
6682
6685
  }
@@ -8030,7 +8033,6 @@ async function importSkills2(projectRoot, results, normalize, skillsDirRel = COP
8030
8033
  const directorySkills = await findDirectorySkills(skillsDir);
8031
8034
  const options = {
8032
8035
  projectRoot,
8033
- sourceSkillsDir: skillsDirRel,
8034
8036
  destCanonicalSkillsDir: COPILOT_CANONICAL_SKILLS_DIR,
8035
8037
  targetName: COPILOT_TARGET,
8036
8038
  normalize,
@@ -9549,7 +9551,6 @@ async function importSkills3(projectRoot, results, normalize, skillsRelDir = CUR
9549
9551
  const directorySkills = await findDirectorySkills(skillsDir);
9550
9552
  const options = {
9551
9553
  projectRoot,
9552
- sourceSkillsDir: skillsRelDir,
9553
9554
  destCanonicalSkillsDir: CURSOR_CANONICAL_SKILLS_DIR,
9554
9555
  targetName: "cursor",
9555
9556
  normalize,
@@ -16484,49 +16485,22 @@ var init_importer_workflows = __esm({
16484
16485
  init_constants31();
16485
16486
  }
16486
16487
  });
16488
+
16489
+ // src/targets/windsurf/skills-adapter.ts
16487
16490
  async function importSkills4(projectRoot, results, normalize, skillsRelDir = WINDSURF_SKILLS_DIR) {
16488
- const skillsDir = join(projectRoot, skillsRelDir);
16489
- const directorySkills = await findDirectorySkills(skillsDir);
16490
16491
  const options = {
16491
16492
  projectRoot,
16492
- sourceSkillsDir: skillsRelDir,
16493
16493
  destCanonicalSkillsDir: WINDSURF_CANONICAL_SKILLS_DIR,
16494
16494
  targetName: "windsurf",
16495
16495
  normalize,
16496
16496
  results
16497
16497
  };
16498
- for (const [skillName, skillDir] of directorySkills) {
16499
- const skillMdPath = join(skillDir, "SKILL.md");
16500
- const content = await readFileSafe(skillMdPath);
16501
- if (!content) continue;
16502
- const rawParsed = parseFrontmatter(content);
16503
- const projectedAgent = parseProjectedAgentSkillFrontmatter(rawParsed.frontmatter, skillName);
16504
- if (projectedAgent) {
16505
- await removePathIfExists(join(projectRoot, WINDSURF_CANONICAL_SKILLS_DIR, skillName));
16506
- const destAgentsDir = join(projectRoot, WINDSURF_CANONICAL_AGENTS_DIR);
16507
- await mkdirp(destAgentsDir);
16508
- const agentPath = join(destAgentsDir, `${projectedAgent.name}.md`);
16509
- await writeFileAtomic(
16510
- agentPath,
16511
- serializeImportedAgent(projectedAgent, normalize(rawParsed.body, skillMdPath, agentPath))
16512
- );
16513
- results.push({
16514
- fromTool: "windsurf",
16515
- fromPath: skillMdPath,
16516
- toPath: `${WINDSURF_CANONICAL_AGENTS_DIR}/${projectedAgent.name}.md`,
16517
- feature: "agents"
16518
- });
16519
- continue;
16520
- }
16521
- await importDirectorySkill(skillName, skillDir, options);
16522
- }
16498
+ await importSkillsDirectory([skillsRelDir], options, [
16499
+ projectedAgentRecognizer({ canonicalAgentsDir: WINDSURF_CANONICAL_AGENTS_DIR })
16500
+ ]);
16523
16501
  }
16524
16502
  var init_skills_adapter5 = __esm({
16525
16503
  "src/targets/windsurf/skills-adapter.ts"() {
16526
- init_fs();
16527
- init_markdown();
16528
- init_projected_agent_skill();
16529
- init_scoped_agents_import();
16530
16504
  init_skill_import_pipeline();
16531
16505
  init_constants31();
16532
16506
  }