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.
|
|
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",
|