xdrs-core 0.52.1 → 0.52.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.
@@ -75,6 +75,8 @@ Examples:
75
75
 
76
76
  Multiple skills under the same `[subject]/skills/` folder MAY share common instruction modules through a sibling `.assets/` directory placed directly under `skills/` (not inside any individual skill package) — for example `skills/.assets/shared-check.md`, referenced with a relative link from any skill in that folder. This keeps shared procedures DRY without duplicating instructions across skills.
77
77
 
78
+ A skill's own `.assets/` folder and the shared `skills/.assets/` folder are exempt from orphan-asset tracking: their files do not need to be linked from `SKILL.md`, because they may hold scripts, templates, installed dependencies (e.g., `node_modules/`), or other runtime resources used by the skill. When `SKILL.md` does link to an asset, the link MUST point either to the skill's own `.assets/` folder or to the shared `../.assets/` folder.
79
+
78
80
  A skill MAY optionally be distributed as a self-contained standalone package (for example, to share it outside this repository) using the bundling mechanism defined in [`_core-adr-policy-021`](021-skill-bundling.md). Bundling is optional; skills that are always consumed as part of the full package do not need it.
79
81
 
80
82
  **Directory roles**
@@ -84,7 +86,7 @@ A skill MAY optionally be distributed as a self-contained standalone package (fo
84
86
  | `SKILL.md` | Required entry point: frontmatter + instructions. |
85
87
  | `scripts/` | Optional executable scripts the agent may run. |
86
88
  | `references/` | Optional deep reference material split out of `SKILL.md`. |
87
- | `.assets/` | Optional images, templates, and other local resource files. |
89
+ | `.assets/` | Optional images, templates, scripts, dependencies, and other local resource files. Not subject to orphan-asset tracking. |
88
90
 
89
91
  **Skill naming**
90
92
 
package/lib/lint.js CHANGED
@@ -1351,7 +1351,6 @@ function lintSkillsDirectory(xdrsRoot, scopeName, skillsPath, errors, externalSc
1351
1351
  lintNoEmojis(skillContent, skillFilePath, 'Skill', errors);
1352
1352
  lintWordCount(skillContent, skillFilePath, 'Skill', SKILL_MAX_WORDS, errors);
1353
1353
  lintDocumentLinks(skillFilePath, xdrsRoot, scopeName, errors, externalScopes);
1354
- lintOrphanAssets(path.join(entryPath, RESOURCE_DIR_NAME), [skillFilePath], xdrsRoot, errors);
1355
1354
  }
1356
1355
 
1357
1356
  return artifacts;
@@ -2238,6 +2237,8 @@ function lintDocumentLinks(documentPath, xdrsRoot, scopeName, errors, externalSc
2238
2237
  const ignoredLines = findIgnoredMarkdownLines(lines);
2239
2238
  const documentDir = path.dirname(documentPath);
2240
2239
  const resourceDir = path.join(documentDir, RESOURCE_DIR_NAME);
2240
+ const isSkillFile = path.basename(documentPath) === 'SKILL.md' && path.basename(path.dirname(documentDir)) === 'skills';
2241
+ const sharedSkillResourceDir = isSkillFile ? path.join(path.dirname(documentDir), RESOURCE_DIR_NAME) : null;
2241
2242
  const localScopePath = normalizePath(path.join(xdrsRoot, '_local'));
2242
2243
  const repoRoot = path.dirname(xdrsRoot);
2243
2244
 
@@ -2267,7 +2268,8 @@ function lintDocumentLinks(documentPath, xdrsRoot, scopeName, errors, externalSc
2267
2268
  errors.push(`Non-_local document must not link into _local scope in ${toDisplayPath(documentPath)}:${index + 1}: ${link.rawTarget} [_core-adr-policy-001]`);
2268
2269
  }
2269
2270
 
2270
- if (isResourceLink && !isPathInside(resourceDir, link.resolvedPath)) {
2271
+ if (isResourceLink && !isPathInside(resourceDir, link.resolvedPath)
2272
+ && !(sharedSkillResourceDir && isPathInside(sharedSkillResourceDir, link.resolvedPath))) {
2271
2273
  errors.push(`Asset links in ${toDisplayPath(documentPath)} must point to ${toDisplayPath(resourceDir)}: ${link.rawTarget} [_core-adr-policy-001]`);
2272
2274
  }
2273
2275
 
package/lib/lint.test.js CHANGED
@@ -2664,6 +2664,56 @@ test('reports orphan asset in articles .assets directory', () => {
2664
2664
  expect(result.errors.join('\n')).not.toContain('used.png');
2665
2665
  });
2666
2666
 
2667
+ test('does not report orphan asset errors for unlinked files in a skill package .assets directory', () => {
2668
+ const workspaceRoot = createWorkspace('skill-assets-not-orphan', {
2669
+ '.xdrs/index.md': rootIndex(),
2670
+ '.xdrs/_local/adrs/index.md': localAdrIndex([
2671
+ '- [check-links](principles/skills/check-links/SKILL.md) - Check links'
2672
+ ]),
2673
+ '.xdrs/_local/adrs/principles/skills/check-links/SKILL.md': skillDocument('Test instructions.'),
2674
+ '.xdrs/_local/adrs/principles/skills/check-links/.assets/template.json': '{}',
2675
+ '.xdrs/_local/adrs/principles/skills/check-links/.assets/run.sh': 'echo run\n',
2676
+ '.xdrs/_local/adrs/principles/skills/check-links/.assets/node_modules/pkg/index.js': 'module.exports = {};\n',
2677
+ });
2678
+
2679
+ const result = lintWorkspace(workspaceRoot);
2680
+
2681
+ expect(result.errors.join('\n')).not.toContain('Orphan asset file');
2682
+ });
2683
+
2684
+ test('allows SKILL.md links to the shared skills .assets directory', () => {
2685
+ const workspaceRoot = createWorkspace('skill-shared-assets-link', {
2686
+ '.xdrs/index.md': rootIndex(),
2687
+ '.xdrs/_local/adrs/index.md': localAdrIndex([
2688
+ '- [check-links](principles/skills/check-links/SKILL.md) - Check links'
2689
+ ]),
2690
+ '.xdrs/_local/adrs/principles/skills/check-links/SKILL.md': skillDocument('Follow [shared check](../.assets/shared-check.md).'),
2691
+ '.xdrs/_local/adrs/principles/skills/.assets/shared-check.md': '# Shared check\n',
2692
+ });
2693
+
2694
+ const result = lintWorkspace(workspaceRoot);
2695
+
2696
+ expect(result.errors.join('\n')).not.toContain('Asset links in');
2697
+ expect(result.errors.join('\n')).not.toContain('Broken asset link');
2698
+ });
2699
+
2700
+ test('reports SKILL.md links to another skill package .assets directory', () => {
2701
+ const workspaceRoot = createWorkspace('skill-other-assets-link', {
2702
+ '.xdrs/index.md': rootIndex(),
2703
+ '.xdrs/_local/adrs/index.md': localAdrIndex([
2704
+ '- [check-links](principles/skills/check-links/SKILL.md) - Check links',
2705
+ '- [other-skill](principles/skills/other-skill/SKILL.md) - Other skill'
2706
+ ]),
2707
+ '.xdrs/_local/adrs/principles/skills/check-links/SKILL.md': skillDocument('Use [template](../other-skill/.assets/template.md).'),
2708
+ '.xdrs/_local/adrs/principles/skills/other-skill/SKILL.md': skillDocument('Test instructions.').replace('name: check-links', 'name: other-skill'),
2709
+ '.xdrs/_local/adrs/principles/skills/other-skill/.assets/template.md': '# Template\n',
2710
+ });
2711
+
2712
+ const result = lintWorkspace(workspaceRoot);
2713
+
2714
+ expect(result.errors.join('\n')).toContain('Asset links in');
2715
+ });
2716
+
2667
2717
  test('reports unexpected directories inside a skill package', () => {
2668
2718
  const workspaceRoot = createWorkspace('unexpected-skill-package-directory', {
2669
2719
  '.xdrs/index.md': rootIndex(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xdrs-core",
3
- "version": "0.52.1",
3
+ "version": "0.52.2",
4
4
  "description": "XDRS (eXtensible Decision Record System): a framework to structure, compile and distribute Architectural (ADR), Business (BDR), and Engineering (EDR) decision records contents so that AI agents and humans can reliably find and use them with hierarchical scopes and controlled rollout in the format of distributable versioned packages.",
5
5
  "repository": {
6
6
  "type": "git",