@csark0812/skeleton 1.5.4 → 1.5.6
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 +38 -39
- package/dist/cli.js +28 -11
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -6,11 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
Agent repos get messy fast. Skills get copied around, docs disagree, links go stale, and nobody remembers which file is actually canonical.
|
|
8
8
|
|
|
9
|
-
Skeleton is an SSOT linter for that layer.
|
|
9
|
+
Skeleton is an SSOT linter for that layer. Define the contract once; Skeleton checks it locally and in CI. If a canonical doc disappears, a registry drifts, a skill index stops matching disk, or a generated reference gets edited by hand, the audit fails before merge.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Think ESLint, but for the docs and skills your agents rely on.
|
|
11
|
+
Think ESLint — for the docs and skills your agents rely on.
|
|
14
12
|
|
|
15
13
|
Skeleton is **not** a runtime agent harness. It doesn't execute tools, enforce permissions, or manage memory. It checks whether the repo around those systems still holds together.
|
|
16
14
|
|
|
@@ -18,9 +16,7 @@ Skeleton is **not** a runtime agent harness. It doesn't execute tools, enforce p
|
|
|
18
16
|
|
|
19
17
|
Agents can read the repo. They can't reliably infer which of three conflicting docs wins, whether a synced skill should be edited here, or which validation command actually proves a change.
|
|
20
18
|
|
|
21
|
-
That
|
|
22
|
-
|
|
23
|
-
Skeleton turns those repo conventions into checks:
|
|
19
|
+
That needs to be explicit — and stay true after the next 50 PRs. Skeleton turns those conventions into checks:
|
|
24
20
|
|
|
25
21
|
| Code repos | Agent repos |
|
|
26
22
|
| ------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
@@ -28,47 +24,50 @@ Skeleton turns those repo conventions into checks:
|
|
|
28
24
|
| `eslint --fix` on changed files | `skeleton validate changed` on changed docs and skills |
|
|
29
25
|
| Pre-commit + CI gate | `--staged` pre-commit + `--base` CI gate |
|
|
30
26
|
|
|
31
|
-
Skill linters
|
|
27
|
+
Skill linters ask: _"Is this SKILL.md well-formed?"_
|
|
28
|
+
|
|
29
|
+
Skeleton asks the repo-level question: _"Does this whole thing still agree with itself?"_
|
|
30
|
+
|
|
31
|
+
## Why a clean SSOT helps agents
|
|
32
|
+
|
|
33
|
+
### Question
|
|
32
34
|
|
|
33
|
-
Skeleton
|
|
35
|
+
Does an intact Skeleton contract change agent behavior — grounding on the right doc, picking the right validation lane, and how much work it takes to get there?
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
### What we did
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
We ran a paired live A/B harness with [`@post-print/agent-test`](https://www.npmjs.com/package/@post-print/agent-test): `skeleton-clean` vs `skeleton-messy`. Same prompts and scenario set; the only intentional difference was registry / conflict structure and context profile.
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
- A [2025 METR randomized trial](https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/) found experienced open-source developers took 19% longer with early-2025 AI tools while believing they were faster.
|
|
41
|
+
Scenarios covered contested grounding (conflicting docs), docs-only validation routing, canonical grounding, owned-skill routing, and customize ownership. Protocol: **N=10** sequential paired compares on 2026-07-17; McNemar on paired pass/fail; median token deltas with a bootstrap CI on the mean.
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
Full method: [refs/llm-harness.md](refs/llm-harness.md). Suites: [agent-suites/README.md](agent-suites/README.md). Aggregated numbers: [SUMMARY.md](agent-suites/evidence/SUMMARY.md). Side-by-side excerpts: [evidence/transcripts/](agent-suites/evidence/transcripts/).
|
|
43
44
|
|
|
44
|
-
###
|
|
45
|
+
### What improved
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+

|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
- Suite definitions: [agent-suites/README.md](agent-suites/README.md)
|
|
50
|
-
- Numbers + transcript excerpts: [agent-suites/evidence/](agent-suites/evidence/)
|
|
49
|
+

|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
On tasks that depend on an intact SSOT, the clean fixture was both more accurate and cheaper:
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
- **Contested grounding** — In every paired run, clean settled on the registry canonical; messy never did (McNemar p = 0.002). Clean hops the registry; messy thrashes across conflicting docs.
|
|
54
|
+
- **Docs routing** — Clean consistently chose the correct audit lane; messy invented a non-existent `audit all` path (McNemar p = 0.002).
|
|
55
|
+
- **Token cost** — Across grounding tasks, messy used a median **~312k** more tokens than clean (bootstrap 95% CI on the mean excludes 0). Pass rate alone understates the gap: messy can still luck into an answer while spending far more.
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
| ------ | ----- | ----- | ----- |
|
|
58
|
-
| Contested grounding (`conflicting docs`) | **10/10** | **0/10** | McNemar 10/0, p = 0.002 — registry SoT |
|
|
59
|
-
| Docs routing | **10/10** | **0/10** | McNemar 10/0, p = 0.002 — messy invents `audit all` |
|
|
60
|
-
| Canonical grounding | 10/10 | 8/10 | Pass rate not significant; messy still burns tokens |
|
|
61
|
-
| Skill routing + customize | 10/10 | 10/10 | Tied — caller `AGENTS.md` already teaches both |
|
|
62
|
-
| Grounding tokens (messy − clean) | — | median **~312k** more | Bootstrap 95% CI on mean excludes 0 |
|
|
57
|
+
Two scenarios did **not** show a clean accuracy win:
|
|
63
58
|
|
|
64
|
-
|
|
59
|
+
- **Canonical grounding** — Pass rates were close; the difference was not significant. Cost still favored clean.
|
|
60
|
+
- **Skill routing + customize** — Tied. The caller `AGENTS.md` already encodes both rules, so the fixtures did not separate on those prompts.
|
|
65
61
|
|
|
66
|
-
|
|
62
|
+
Charts regenerate from `SUMMARY.json` via `bun run agent:evidence:charts`.
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- Skill/customize may not separate when
|
|
71
|
-
|
|
64
|
+
### Limits
|
|
65
|
+
|
|
66
|
+
Fixture A/B on Skeleton’s own contract (live model + prompt variance) — not a general coding-task or SWE-bench claim. Skill/customize may not separate when the entry doc already teaches the correct rule.
|
|
67
|
+
|
|
68
|
+
### Industry context
|
|
69
|
+
|
|
70
|
+
Not a Skeleton measurement — broader context research points the same direction: more context isn’t free. A [2026 study of repo-level context files](https://doi.org/10.48550/arxiv.2602.11988) (438 tasks) found human-written files helped ~4% on average, generated files hurt ~3%, and both raised inference cost >20%. A [2025 METR trial](https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/) found experienced OSS developers took 19% longer with early-2025 AI tools while believing they were faster. Those papers motivate small instructions and explicit ownership; they do **not** measure this tool.
|
|
72
71
|
|
|
73
72
|
## Quick start
|
|
74
73
|
|
|
@@ -98,7 +97,7 @@ Flag details: [install](docs/developer/install.md).
|
|
|
98
97
|
- **Prose policy** (optional plugins) — YAML pattern rules; idle with no plugins
|
|
99
98
|
- **Shell / JSON syntax** — lightweight checks on changed `.sh` and `.json` files
|
|
100
99
|
|
|
101
|
-
Skeleton doesn't replace your code gates. Keep TypeScript, Python, Nx, pytest,
|
|
100
|
+
Skeleton doesn't replace your code gates. Keep TypeScript, Python, Nx, pytest, and the rest in the repo that owns them.
|
|
102
101
|
|
|
103
102
|
## The `.skeleton/` contract
|
|
104
103
|
|
|
@@ -123,7 +122,7 @@ Register it:
|
|
|
123
122
|
skeleton register docs/developer/api.md
|
|
124
123
|
```
|
|
125
124
|
|
|
126
|
-
Synced skills stay pristine. Project overrides live in `.skeleton/customize/<slug>.md` and inject when the skill is read
|
|
125
|
+
Synced skills stay pristine. Project overrides live in `.skeleton/customize/<slug>.md` and inject when the skill is read — no editing copied `SKILL.md` files.
|
|
127
126
|
|
|
128
127
|
## Commands
|
|
129
128
|
|
|
@@ -154,8 +153,6 @@ CI: `skeleton validate changed --base origin/main` (global rules first, then cha
|
|
|
154
153
|
|
|
155
154
|
## Ecosystem
|
|
156
155
|
|
|
157
|
-
The model is simple:
|
|
158
|
-
|
|
159
156
|
| Layer | Role |
|
|
160
157
|
| ----------------- | ---------------------------------------------------------------------------------------------- |
|
|
161
158
|
| **Skeleton** | Defines and checks the SSOT contract |
|
|
@@ -187,6 +184,8 @@ bun run check
|
|
|
187
184
|
|
|
188
185
|
`bun run check` = lint + test + typecheck + build + `audit:self`.
|
|
189
186
|
|
|
190
|
-
`validate:changed` is docs/config only for path-scoped work — it skips code/config extensions (see table above). Owned skill-body edits need `audit skills`. All-skip, owned skill paths (alone or mixed with docs), and missing paths exit non-zero.
|
|
187
|
+
`validate:changed` is docs/config only for path-scoped work — it skips code/config extensions (see table above). Owned skill-body edits need `audit skills`. All-skip, owned skill paths (alone or mixed with docs), and missing paths exit non-zero.
|
|
188
|
+
|
|
189
|
+
For code: `bun test`, `bun run typecheck`, `bun run build`.
|
|
191
190
|
|
|
192
191
|
Optional: `brew install pre-commit` (or `pipx install pre-commit`), then `pre-commit install` to wire `.pre-commit-config.yaml`.
|
package/dist/cli.js
CHANGED
|
@@ -17313,7 +17313,8 @@ function expandPatterns(root, patterns, exclude) {
|
|
|
17313
17313
|
cwd: root,
|
|
17314
17314
|
absolute: true,
|
|
17315
17315
|
onlyFiles: true,
|
|
17316
|
-
dot: true
|
|
17316
|
+
dot: true,
|
|
17317
|
+
ignore: exclude
|
|
17317
17318
|
})) {
|
|
17318
17319
|
if (!isMarkdownFile(abs))
|
|
17319
17320
|
continue;
|
|
@@ -17358,7 +17359,8 @@ function collectBannedFiles(config, root) {
|
|
|
17358
17359
|
cwd: root,
|
|
17359
17360
|
absolute: true,
|
|
17360
17361
|
onlyFiles: true,
|
|
17361
|
-
dot: false
|
|
17362
|
+
dot: false,
|
|
17363
|
+
ignore: exclude
|
|
17362
17364
|
})) {
|
|
17363
17365
|
const rel = normalizeRelPath(relative4(root, abs));
|
|
17364
17366
|
if (shouldExclude(rel, exclude))
|
|
@@ -17375,7 +17377,8 @@ function collectCoverageCandidateFiles(root, exclude) {
|
|
|
17375
17377
|
cwd: root,
|
|
17376
17378
|
absolute: true,
|
|
17377
17379
|
onlyFiles: true,
|
|
17378
|
-
dot: false
|
|
17380
|
+
dot: false,
|
|
17381
|
+
ignore: exclude
|
|
17379
17382
|
})) {
|
|
17380
17383
|
const rel = normalizeRelPath(relative4(root, abs));
|
|
17381
17384
|
if (shouldExclude(rel, exclude))
|
|
@@ -17385,6 +17388,11 @@ function collectCoverageCandidateFiles(root, exclude) {
|
|
|
17385
17388
|
}
|
|
17386
17389
|
return [...files];
|
|
17387
17390
|
}
|
|
17391
|
+
function excludeForeignSkillDocMetaPaths(docMetaPaths, skillIndex) {
|
|
17392
|
+
if (!skillIndex)
|
|
17393
|
+
return docMetaPaths;
|
|
17394
|
+
return docMetaPaths.filter((rel) => !isForeignSkillPath(rel, skillIndex));
|
|
17395
|
+
}
|
|
17388
17396
|
function collectDocMetaPaths(config, root, registryPaths, skillIndex) {
|
|
17389
17397
|
const paths = [];
|
|
17390
17398
|
for (const abs of expandPatterns(root, ["docs/*/README.md"], mergedExcludes(config))) {
|
|
@@ -17409,7 +17417,7 @@ function collectDocMetaPaths(config, root, registryPaths, skillIndex) {
|
|
|
17409
17417
|
paths.push(normalizeRelPath(relative4(root, abs)));
|
|
17410
17418
|
}
|
|
17411
17419
|
}
|
|
17412
|
-
return [...new Set(paths)];
|
|
17420
|
+
return excludeForeignSkillDocMetaPaths([...new Set(paths)], skillIndex);
|
|
17413
17421
|
}
|
|
17414
17422
|
function validateScanRoots(config, root) {
|
|
17415
17423
|
const missing = [];
|
|
@@ -17419,11 +17427,11 @@ function validateScanRoots(config, root) {
|
|
|
17419
17427
|
}
|
|
17420
17428
|
return missing;
|
|
17421
17429
|
}
|
|
17422
|
-
function filterDocMetaPaths(docMetaPaths, paths) {
|
|
17430
|
+
function filterDocMetaPaths(docMetaPaths, paths, skillIndex) {
|
|
17423
17431
|
if (paths.length === 0)
|
|
17424
|
-
return docMetaPaths;
|
|
17432
|
+
return excludeForeignSkillDocMetaPaths(docMetaPaths, skillIndex);
|
|
17425
17433
|
const normalizedPaths = paths.map((path) => normalizeRelPath(path));
|
|
17426
|
-
return docMetaPaths.filter((rel) => normalizedPaths.some((path) => rel === path || rel.startsWith(`${path}/`)));
|
|
17434
|
+
return excludeForeignSkillDocMetaPaths(docMetaPaths.filter((rel) => normalizedPaths.some((path) => rel === path || rel.startsWith(`${path}/`))), skillIndex);
|
|
17427
17435
|
}
|
|
17428
17436
|
function filterToPaths(files, paths, root) {
|
|
17429
17437
|
const normalizedPaths = paths.map((path) => normalizeRelPath(path));
|
|
@@ -17516,7 +17524,7 @@ function createContext(options = {}) {
|
|
|
17516
17524
|
root,
|
|
17517
17525
|
config,
|
|
17518
17526
|
files,
|
|
17519
|
-
docMetaPaths: options.paths && options.paths.length > 0 ? filterDocMetaPaths(allDocMetaPaths, options.paths) : allDocMetaPaths,
|
|
17527
|
+
docMetaPaths: options.paths && options.paths.length > 0 ? filterDocMetaPaths(allDocMetaPaths, options.paths, skillIndex) : allDocMetaPaths,
|
|
17520
17528
|
registryPaths: registry.paths,
|
|
17521
17529
|
registryHasTableHeader: registry.hasTableHeader,
|
|
17522
17530
|
retiredSkills: new Set(retiredSkills(config)),
|
|
@@ -28819,10 +28827,15 @@ function destinationConsumesToSliceEnd(slice, afterDest) {
|
|
|
28819
28827
|
if (slice[i] === ")") {
|
|
28820
28828
|
return i === slice.length - 1;
|
|
28821
28829
|
}
|
|
28822
|
-
|
|
28830
|
+
const first = slice[i];
|
|
28831
|
+
if (first === undefined || !/\s/.test(first))
|
|
28823
28832
|
return false;
|
|
28824
|
-
while (i < slice.length
|
|
28833
|
+
while (i < slice.length) {
|
|
28834
|
+
const ch = slice[i];
|
|
28835
|
+
if (ch === undefined || !/\s/.test(ch))
|
|
28836
|
+
break;
|
|
28825
28837
|
i++;
|
|
28838
|
+
}
|
|
28826
28839
|
if (i >= slice.length)
|
|
28827
28840
|
return false;
|
|
28828
28841
|
if (slice[i] === ")") {
|
|
@@ -28838,8 +28851,12 @@ function destinationConsumesToSliceEnd(slice, afterDest) {
|
|
|
28838
28851
|
if (i >= slice.length)
|
|
28839
28852
|
return false;
|
|
28840
28853
|
i++;
|
|
28841
|
-
while (i < slice.length
|
|
28854
|
+
while (i < slice.length) {
|
|
28855
|
+
const ch = slice[i];
|
|
28856
|
+
if (ch === undefined || !/\s/.test(ch))
|
|
28857
|
+
break;
|
|
28842
28858
|
i++;
|
|
28859
|
+
}
|
|
28843
28860
|
return i === slice.length - 1 && slice[i] === ")";
|
|
28844
28861
|
}
|
|
28845
28862
|
function findUrlSpanInSlice(content3, nodeStart, nodeEnd, url) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@csark0812/skeleton",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.6",
|
|
4
4
|
"description": "SSOT audit CLI for agent harness repos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"agent:test:doctor": "agent-test --doctor",
|
|
54
54
|
"agent:test:validate": "agent-test --validate-only --validate-paths --suites-dir agent-suites",
|
|
55
55
|
"agent:evidence:aggregate": "bun scripts/agent-evidence/aggregate-compares.ts",
|
|
56
|
+
"agent:evidence:charts": "bun scripts/agent-evidence/render-charts.ts",
|
|
56
57
|
"agent:evidence:excerpt": "bun scripts/agent-evidence/excerpt-transcript.ts",
|
|
57
58
|
"agent:evidence:archive": "bash scripts/agent-evidence/archive-run.sh",
|
|
58
59
|
"prepack": "bun test && bun run typecheck && bun run build"
|