@nurix/etna 0.1.3 → 0.2.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/README.md +34 -27
- package/bin/etna.js +82 -56
- package/data/harness-index.json +0 -7
- package/data/harnesses/code/agents/change-review.md +80 -0
- package/data/harnesses/code/agents/contract-monitor.md +65 -0
- package/data/harnesses/code/agents/dep-health.md +148 -0
- package/data/harnesses/code/agents/git-history.md +201 -0
- package/data/harnesses/code/agents/maintenance.md +112 -0
- package/data/harnesses/code/agents/review.md +109 -0
- package/data/harnesses/code/agents/spec-mode.md +56 -0
- package/data/harnesses/code/rules/general.md +96 -0
- package/data/harnesses/code/rules/nomenclature.md +34 -0
- package/data/harnesses/code/scripts/build-ownership-index.sh +138 -0
- package/data/harnesses/code/scripts/check-doc-drift.sh +125 -0
- package/data/harnesses/code/skills/documentation/SKILL.md +238 -0
- package/data/harnesses/code/skills/documentation/references/examples.md +193 -0
- package/data/harnesses/code/skills/documentation/references/style-guide.md +288 -0
- package/data/harnesses/code/skills/documentation/references/templates.md +901 -0
- package/data/harnesses/code/skills/introspect/SKILL.md +135 -0
- package/data/harnesses/code/skills/workflow-mapper/SKILL.md +430 -0
- package/data/harnesses/code/skills/workflow-mapper/references/patterns.md +214 -0
- package/data/harnesses/kb-agi/agents/kb-agi-init.md +276 -0
- package/data/harnesses/kb-agi/agents/kb-catch-stale.md +22 -0
- package/data/harnesses/kb-agi/agents/kb-find-gaps.md +27 -0
- package/data/harnesses/kb-agi/agents/kb-fix-links.md +22 -0
- package/data/harnesses/kb-agi/agents/kb-refresh-frontdoor.md +22 -0
- package/data/harnesses/kb-agi/agents/kb-refresh-summaries.md +22 -0
- package/data/harnesses/kb-agi/agents/kb-sync-metadata.md +25 -0
- package/data/harnesses/kb-agi/rules/kb.md +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
# @nurix/etna
|
|
2
2
|
|
|
3
|
-
CLI to bootstrap a project with a Nurix harness.
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
/plugin marketplace add nurixlabs/skills
|
|
7
|
-
/plugin install nurix-harness@nurixlabs
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
inside Claude Code, plus drops the harness's `CLAUDE.md` at the project root. See [`../../harness/README.md`](../../harness/README.md) for what a harness is and why the install model is designed this way.
|
|
3
|
+
CLI to bootstrap a project with a Nurix harness. Copies the harness's `CLAUDE.md`, skills, agents, rules, and scripts into the project so they live with the code — version-controllable, editable per project, and visible without leaving the repo.
|
|
11
4
|
|
|
12
5
|
## Usage
|
|
13
6
|
|
|
@@ -17,46 +10,60 @@ From the root of the project you want to bootstrap:
|
|
|
17
10
|
npx @nurix/etna nurix-harness
|
|
18
11
|
```
|
|
19
12
|
|
|
20
|
-
After this completes
|
|
13
|
+
After this completes:
|
|
21
14
|
|
|
22
15
|
```
|
|
23
16
|
your-project/
|
|
24
|
-
├── CLAUDE.md
|
|
17
|
+
├── CLAUDE.md ← harness policy (backup of any existing file is kept)
|
|
25
18
|
└── .claude/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
├── skills/ ← documentation, introspect, workflow-mapper
|
|
20
|
+
├── agents/ ← spec-mode, maintenance, contract-monitor, …
|
|
21
|
+
├── rules/ ← general.md, nomenclature.md
|
|
22
|
+
└── scripts/ ← build-ownership-index.sh, check-doc-drift.sh
|
|
29
23
|
```
|
|
30
24
|
|
|
31
|
-
That's the entire footprint.
|
|
25
|
+
That's the entire footprint. Nothing is written outside the project. No marketplace registration, no `enabledPlugins` entry, no global cache dependency — Claude Code picks up project-scoped agents/skills/rules/scripts on its next session start.
|
|
32
26
|
|
|
33
|
-
|
|
27
|
+
Re-run `npx @nurix/etna nurix-harness` anytime to refresh the harness content with what the latest published package ships. Existing same-named files are overwritten; files you've added that aren't part of the harness are left alone. The previous `CLAUDE.md` is backed up to `CLAUDE.md.bak.<ISO-timestamp>`.
|
|
28
|
+
|
|
29
|
+
## Migrating from 0.1.x
|
|
30
|
+
|
|
31
|
+
`0.1.x` registered the harness as a Claude Code plugin via the `nurixlabs/skills` marketplace. `0.2.0` switches to direct file installation. When you re-run `etna` on a project that has a `0.1.x` install, the CLI removes the stale `enabledPlugins["<plugin>@nurixlabs"]` entry from `<project>/.claude/settings.json` so Claude Code doesn't double-load the same content from the global plugin cache.
|
|
32
|
+
|
|
33
|
+
The `nurixlabs` marketplace entry in `~/.claude/settings.json` is harmless to leave (it doesn't load anything by itself) — remove it manually if you want.
|
|
34
34
|
|
|
35
35
|
## Available harnesses
|
|
36
36
|
|
|
37
37
|
Listed by `npx @nurix/etna` (no args) using the bundled `data/harness-index.json`.
|
|
38
38
|
|
|
39
|
-
| Plugin name |
|
|
39
|
+
| Plugin name | Source in etna | What it ships |
|
|
40
40
|
|---|---|---|
|
|
41
|
-
| `nurix-harness` | [`harness
|
|
41
|
+
| `nurix-harness` | [`plugins/nurix-harness.md`](../../plugins/nurix-harness.md) → top-level catalogs | Governance agents, baseline workflow skills, ownership/drift scripts |
|
|
42
|
+
| `kb-agi` | [`plugins/kb-agi.md`](../../plugins/kb-agi.md) → top-level catalogs | Knowledge-vault scaffolding |
|
|
42
43
|
|
|
43
44
|
## How it works
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
4. Adds `<plugin-name>@<marketplace>: true` to `./.claude/settings.json` under `enabledPlugins`.
|
|
46
|
+
Each harness has a folder under `etna/harness/<name>/` with:
|
|
47
|
+
- `CLAUDE.md` — the project-root policy file
|
|
48
|
+
- `index.md` — symlinked to `../../plugins/<plugin>.md` (the manifest)
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
The plugin manifest's YAML frontmatter declares which top-level `agents/`, `skills/`, `rules/`, and `scripts/` belong to that plugin:
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
```yaml
|
|
53
|
+
---
|
|
54
|
+
name: nurix-harness
|
|
55
|
+
skills: [documentation, introspect, workflow-mapper]
|
|
56
|
+
agents: [spec-mode, maintenance, contract-monitor, …]
|
|
57
|
+
scripts: [build-ownership-index, check-doc-drift]
|
|
58
|
+
rules: [general, nomenclature]
|
|
59
|
+
---
|
|
60
|
+
```
|
|
53
61
|
|
|
54
|
-
-
|
|
55
|
-
- **`CLAUDE.md`** is bundled in this npm package, versioned with the package. A plugin cannot ship a project-root `CLAUDE.md` (Claude Code's plugin spec doesn't support it), so the CLI ships that half itself. Harness policy changes warrant a package version bump.
|
|
62
|
+
`scripts/build-data.js` (run by `prepack`) resolves each manifest against the catalogs and bundles the result into `data/harnesses/<folder>/`. The installer (`bin/etna.js`) reads from that bundle at install time. No GitHub fetch at runtime — the published npm package contains everything.
|
|
56
63
|
|
|
57
64
|
## Building / contributing
|
|
58
65
|
|
|
59
|
-
`data/` is generated. Don't edit by hand. To regenerate after
|
|
66
|
+
`data/` is generated. Don't edit by hand. To regenerate after editing the etna catalogs or manifests:
|
|
60
67
|
|
|
61
68
|
```sh
|
|
62
69
|
cd packages/cli
|
|
@@ -67,4 +74,4 @@ npm run build:data
|
|
|
67
74
|
|
|
68
75
|
## Publishing
|
|
69
76
|
|
|
70
|
-
CI auto-publishes on push to `dev` when `packages/cli/**` or `harness/**` change — see [`.github/workflows/publish-etna-to-npm.yml`](../../.github/workflows/publish-etna-to-npm.yml). The `publishConfig.access` is `restricted` so the scoped package is private by default.
|
|
77
|
+
CI auto-publishes on push to `dev` when `packages/cli/**` or `harness/**` change — see [`.github/workflows/publish-etna-to-npm.yml`](../../.github/workflows/publish-etna-to-npm.yml). The `publishConfig.access` is `restricted` so the scoped package is private by default. The `NPM_PUBLISH_TOKEN` GitHub Actions secret must be a granular access token with **Bypass 2FA** enabled — classic automation tokens are rejected by the npmjs.com 2FA policy on the `nurix-tools` account.
|
package/bin/etna.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// packages/cli/bin/etna.js
|
|
3
|
-
// `npx @nurix/etna <plugin-name>` —
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
3
|
+
// `npx @nurix/etna <plugin-name>` — copy a Nurix harness into the current
|
|
4
|
+
// directory. Drops the harness's CLAUDE.md at the project root and copies its
|
|
5
|
+
// skills, agents, rules, and scripts into <project>/.claude/. No marketplace,
|
|
6
|
+
// no plugin enablement — files live in the project, version-controlled and
|
|
7
|
+
// editable per project. Re-run `etna` to refresh.
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
existsSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
renameSync,
|
|
15
|
+
copyFileSync,
|
|
16
|
+
cpSync,
|
|
17
|
+
readdirSync,
|
|
18
|
+
lstatSync,
|
|
19
|
+
} from 'node:fs';
|
|
10
20
|
import { join, dirname, resolve } from 'node:path';
|
|
11
21
|
import { fileURLToPath } from 'node:url';
|
|
12
22
|
|
|
@@ -14,17 +24,18 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
14
24
|
const pkgRoot = resolve(__dirname, '..');
|
|
15
25
|
const dataDir = join(pkgRoot, 'data');
|
|
16
26
|
|
|
27
|
+
// Subfolders bundled inside each harness payload that map 1:1 onto
|
|
28
|
+
// <project>/.claude/<name>/. Order matches Claude Code's own folder layout.
|
|
29
|
+
const PLUGIN_SUBDIRS = ['skills', 'agents', 'rules', 'scripts'];
|
|
30
|
+
|
|
17
31
|
function usage() {
|
|
18
32
|
const idx = loadHarnessIndex();
|
|
19
33
|
console.log(`\nUsage: npx @nurix/etna <plugin-name>\n`);
|
|
20
34
|
const names = Object.keys(idx.plugins);
|
|
21
35
|
if (names.length) {
|
|
22
|
-
console.log(`Available harnesses
|
|
23
|
-
for (const name of names) {
|
|
24
|
-
console.log(` ${name.padEnd(20)} → ${idx.marketplace.name}`);
|
|
25
|
-
}
|
|
36
|
+
console.log(`Available harnesses:`);
|
|
37
|
+
for (const name of names) console.log(` ${name}`);
|
|
26
38
|
}
|
|
27
|
-
console.log(`\nMarketplace: ${idx.marketplace.source.repo} (github)`);
|
|
28
39
|
console.log();
|
|
29
40
|
}
|
|
30
41
|
|
|
@@ -41,6 +52,14 @@ function die(msg, code = 1) {
|
|
|
41
52
|
process.exit(code);
|
|
42
53
|
}
|
|
43
54
|
|
|
55
|
+
function isSymlink(path) {
|
|
56
|
+
try {
|
|
57
|
+
return lstatSync(path).isSymbolicLink();
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
// Move <path> to <path>.bak.<timestamp> if it exists. Returns the backup path, or null.
|
|
45
64
|
function backupIfExists(path) {
|
|
46
65
|
if (!existsSync(path) && !isSymlink(path)) return null;
|
|
@@ -50,14 +69,6 @@ function backupIfExists(path) {
|
|
|
50
69
|
return backup;
|
|
51
70
|
}
|
|
52
71
|
|
|
53
|
-
function isSymlink(path) {
|
|
54
|
-
try {
|
|
55
|
-
return lstatSync(path).isSymbolicLink();
|
|
56
|
-
} catch {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
72
|
function readJson(path) {
|
|
62
73
|
if (!existsSync(path)) return {};
|
|
63
74
|
try {
|
|
@@ -73,31 +84,44 @@ function writeJson(path, value) {
|
|
|
73
84
|
writeFileSync(path, JSON.stringify(value, null, 2) + '\n');
|
|
74
85
|
}
|
|
75
86
|
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
// Copy the harness's bundled .claude/<subdir>/ contents into <project>/.claude/<subdir>/,
|
|
88
|
+
// merging with whatever's already there. Existing same-named entries are overwritten so
|
|
89
|
+
// re-running `etna` always refreshes harness-provided content. Returns count copied.
|
|
90
|
+
function copyHarnessSubdir(bundledSubdir, projectSubdir) {
|
|
91
|
+
if (!existsSync(bundledSubdir)) return 0;
|
|
92
|
+
mkdirSync(projectSubdir, { recursive: true });
|
|
93
|
+
let copied = 0;
|
|
94
|
+
for (const entry of readdirSync(bundledSubdir, { withFileTypes: true })) {
|
|
95
|
+
const src = join(bundledSubdir, entry.name);
|
|
96
|
+
const dest = join(projectSubdir, entry.name);
|
|
97
|
+
cpSync(src, dest, { recursive: true, force: true });
|
|
98
|
+
copied += 1;
|
|
99
|
+
}
|
|
100
|
+
return copied;
|
|
89
101
|
}
|
|
90
102
|
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
// Clean up any leftover marketplace/enabledPlugins entries from a previous 0.1.x
|
|
104
|
+
// install. After 0.2.0 we don't use the marketplace flow at all — leaving stale
|
|
105
|
+
// entries causes Claude Code to also resolve the plugin from the global cache,
|
|
106
|
+
// double-loading the same skills under a different namespace.
|
|
107
|
+
function migrateFromPluginInstall(targetDir, pluginName) {
|
|
108
|
+
const removed = [];
|
|
109
|
+
const projectSettingsPath = join(targetDir, '.claude', 'settings.json');
|
|
110
|
+
if (existsSync(projectSettingsPath)) {
|
|
111
|
+
const s = readJson(projectSettingsPath);
|
|
112
|
+
if (s.enabledPlugins) {
|
|
113
|
+
const keys = Object.keys(s.enabledPlugins).filter((k) =>
|
|
114
|
+
k.startsWith(`${pluginName}@`),
|
|
115
|
+
);
|
|
116
|
+
for (const k of keys) {
|
|
117
|
+
delete s.enabledPlugins[k];
|
|
118
|
+
removed.push(`enabledPlugins["${k}"] in ${projectSettingsPath}`);
|
|
119
|
+
}
|
|
120
|
+
if (Object.keys(s.enabledPlugins).length === 0) delete s.enabledPlugins;
|
|
121
|
+
writeJson(projectSettingsPath, s);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return removed;
|
|
101
125
|
}
|
|
102
126
|
|
|
103
127
|
async function main(args) {
|
|
@@ -115,34 +139,36 @@ async function main(args) {
|
|
|
115
139
|
);
|
|
116
140
|
}
|
|
117
141
|
|
|
118
|
-
const
|
|
119
|
-
|
|
142
|
+
const bundledHarnessDir = join(dataDir, 'harnesses', harnessFolder);
|
|
143
|
+
const bundledClaudeMd = join(bundledHarnessDir, 'CLAUDE.md');
|
|
144
|
+
if (!existsSync(bundledClaudeMd)) {
|
|
120
145
|
die(`Bundled CLAUDE.md missing for harness '${harnessFolder}'. Rebuild package data.`);
|
|
121
146
|
}
|
|
122
147
|
|
|
123
148
|
const targetDir = process.cwd();
|
|
124
149
|
const claudeMdDest = join(targetDir, 'CLAUDE.md');
|
|
125
|
-
const marketplace = idx.marketplace;
|
|
126
|
-
const fqKey = `${pluginName}@${marketplace.name}`;
|
|
127
150
|
|
|
128
|
-
console.log(`\nInstalling ${
|
|
151
|
+
console.log(`\nInstalling ${pluginName} into ${targetDir}`);
|
|
129
152
|
|
|
130
153
|
const claudeBackup = backupIfExists(claudeMdDest);
|
|
131
154
|
if (claudeBackup) console.log(` backed up existing CLAUDE.md → ${claudeBackup}`);
|
|
132
155
|
|
|
133
|
-
copyFileSync(
|
|
156
|
+
copyFileSync(bundledClaudeMd, claudeMdDest);
|
|
134
157
|
console.log(` installed CLAUDE.md → ${claudeMdDest}`);
|
|
135
158
|
|
|
136
|
-
const
|
|
137
|
-
|
|
159
|
+
for (const sub of PLUGIN_SUBDIRS) {
|
|
160
|
+
const count = copyHarnessSubdir(
|
|
161
|
+
join(bundledHarnessDir, sub),
|
|
162
|
+
join(targetDir, '.claude', sub),
|
|
163
|
+
);
|
|
164
|
+
if (count > 0) console.log(` copied ${count} ${sub} → ${targetDir}/.claude/${sub}/`);
|
|
165
|
+
}
|
|
138
166
|
|
|
139
|
-
const
|
|
140
|
-
console.log(`
|
|
167
|
+
const migrated = migrateFromPluginInstall(targetDir, pluginName);
|
|
168
|
+
for (const entry of migrated) console.log(` removed stale ${entry}`);
|
|
141
169
|
|
|
142
170
|
console.log(`\nDone. Open Claude Code in ${targetDir}.`);
|
|
143
|
-
console.log(
|
|
144
|
-
`It will fetch ${marketplace.source.repo}, install ${pluginName}, and load it on next session start.\n`,
|
|
145
|
-
);
|
|
171
|
+
console.log(`Re-run \`npx @nurix/etna ${pluginName}\` anytime to refresh harness content.\n`);
|
|
146
172
|
}
|
|
147
173
|
|
|
148
174
|
main(process.argv.slice(2)).catch((e) => {
|
package/data/harness-index.json
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: change-review
|
|
3
|
+
description: Velocity analysis — produces commit heatmap, risk-zone identification (high churn + low guardrail coverage), stabilization signals, team distribution. Consumes `git-history`'s CSV output rather than reanalyzing commits. Use when planning where to invest in better guardrails or rules.
|
|
4
|
+
tools: Read, Glob, Grep, Bash
|
|
5
|
+
model: inherit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Change-Review Agent
|
|
9
|
+
|
|
10
|
+
You analyze code-change velocity over time to identify risk zones. Where `git-history` classifies *what* changed (module, impact, severity), you ask *where is change accelerating or decelerating* — and whether guardrail coverage matches the risk.
|
|
11
|
+
|
|
12
|
+
You answer: "where should the tech lead invest in better guardrails or rules next?"
|
|
13
|
+
|
|
14
|
+
## Before Starting
|
|
15
|
+
|
|
16
|
+
1. If `./docs/memory/change-review.md` exists, read it for historical velocity snapshots, prior risk-zone flags, and known concentration risks.
|
|
17
|
+
2. If `./docs/memory/git-history.md` or a recent CSV from the git-history agent exists, prefer it as input — don't re-classify commits. If no recent run exists, ask the user to run `git-history` first or fall back to a direct `git log` analysis.
|
|
18
|
+
3. Read `./docs/operations/guardrails.md` if it exists. Risk-zone scoring is "high churn + low guardrail coverage" — without guardrails, you can only produce a heatmap, not risk zones.
|
|
19
|
+
4. Read `./docs/OWNERSHIP_INDEX.md` if it exists, to map churn to owning feature docs.
|
|
20
|
+
|
|
21
|
+
## Step 1: Velocity Heatmap
|
|
22
|
+
|
|
23
|
+
Compute commits-per-module over rolling windows:
|
|
24
|
+
|
|
25
|
+
- **1 week** — what's hot right now
|
|
26
|
+
- **1 month** — current rhythm
|
|
27
|
+
- **3 months** — quarter-level pattern
|
|
28
|
+
|
|
29
|
+
Aggregate by top-level module (`src/{module}/`). Report:
|
|
30
|
+
|
|
31
|
+
| Module | Commits 1w | Commits 1m | Commits 3m | Trend |
|
|
32
|
+
|--------|-----------:|-----------:|-----------:|-------|
|
|
33
|
+
|
|
34
|
+
Trend column: ↑ rising, ↓ falling, → steady, ↑↑ sharply rising, ↓↓ sharply falling.
|
|
35
|
+
|
|
36
|
+
## Step 2: Risk Zones
|
|
37
|
+
|
|
38
|
+
For each module with high churn (top quartile in 1m window), check guardrail coverage:
|
|
39
|
+
|
|
40
|
+
- Does `docs/operations/guardrails.md` mention this module?
|
|
41
|
+
- Is the module's `owns` doc up to date? (cross-reference with maintenance agent's last drift check if available)
|
|
42
|
+
- Does the module appear in `docs/routing.md`?
|
|
43
|
+
|
|
44
|
+
Flag risk zones: high churn × low coverage. These are where silent breakage is most likely.
|
|
45
|
+
|
|
46
|
+
## Step 3: Stabilization Signals
|
|
47
|
+
|
|
48
|
+
Identify modules where 1-week and 1-month velocity are both decreasing. Candidates for reducing review overhead — fewer guardrails needed where churn is dropping.
|
|
49
|
+
|
|
50
|
+
## Step 4: Team Distribution
|
|
51
|
+
|
|
52
|
+
Per module, list which authors made the most commits in the 3-month window. Surface concentration risks:
|
|
53
|
+
|
|
54
|
+
- One author > 70% of commits in a critical module → bus-factor flag
|
|
55
|
+
- One author owns all changes touching a guardrail → review-coverage flag
|
|
56
|
+
|
|
57
|
+
## Step 5: Report & Track
|
|
58
|
+
|
|
59
|
+
### Report format
|
|
60
|
+
|
|
61
|
+
- **Velocity heatmap** (table above)
|
|
62
|
+
- **Risk zones**: top 5 high-churn / low-coverage modules with specific guardrails to consider adding
|
|
63
|
+
- **Stabilization signals**: modules where review overhead can decrease
|
|
64
|
+
- **Concentration risks**: bus-factor and review-coverage flags
|
|
65
|
+
- **Recommendations**: prioritized list — typically "add guardrail for X", "update routing.md to cover Y", "spread reviews of Z across more authors"
|
|
66
|
+
|
|
67
|
+
### Update tracking
|
|
68
|
+
|
|
69
|
+
1. Write snapshot to `docs/memory/change-review.md` with date and all four sections above
|
|
70
|
+
2. Append items to `docs/history/backlog.md`:
|
|
71
|
+
- `- **[P1]** No guardrail coverage for high-churn module \`src/{name}/\` — discovered by change-review`
|
|
72
|
+
3. Report summary to the user
|
|
73
|
+
|
|
74
|
+
## Rules
|
|
75
|
+
|
|
76
|
+
- Prefer git-history's CSV as input; only fall back to direct `git log` analysis if no recent CSV exists
|
|
77
|
+
- Read-only for source code
|
|
78
|
+
- May write to `docs/memory/change-review.md` and `docs/history/backlog.md`
|
|
79
|
+
- Risk-zone identification requires guardrails — if `guardrails.md` doesn't exist, produce heatmap only and tell the user that risk-zone scoring needs documented guardrails
|
|
80
|
+
- A clean report (no risk zones) is valid for a low-churn, well-covered project
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: contract-monitor
|
|
3
|
+
description: Post-implementation contract auditor. Scans the live codebase for violations of cross-module contracts documented in `docs/operations/guardrails.md` — unregistered entity types, string-coupled constants, cross-entity references without propagation, schema mismatches, undocumented endpoints. Run on request after significant merges. Pairs with the `introspect` skill (which runs pre-implementation).
|
|
4
|
+
tools: Read, Glob, Grep, Edit, Write, Bash
|
|
5
|
+
model: inherit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Contract Monitor
|
|
9
|
+
|
|
10
|
+
You scan the codebase for cross-module contract violations. Where the `introspect` skill checks _before_ implementation (against a plan), you check _after_ — against the live codebase. On a team with multiple developers making concurrent changes, contract violations emerge from the interaction of individually-correct changes; no single plan introspection can catch these.
|
|
11
|
+
|
|
12
|
+
## Before Starting
|
|
13
|
+
|
|
14
|
+
1. Read `./docs/operations/guardrails.md` — this is your source of truth for which contracts exist. If it doesn't exist, stop and tell the user a guardrails doc is required.
|
|
15
|
+
2. If `./docs/memory/contract-monitor.md` exists, read it for known exceptions, historical violation counts, and patterns the user has chosen not to fix.
|
|
16
|
+
3. If `./docs/routing.md` exists, read it for module ownership context.
|
|
17
|
+
|
|
18
|
+
## Scan
|
|
19
|
+
|
|
20
|
+
For each guardrail section in `guardrails.md`:
|
|
21
|
+
|
|
22
|
+
1. Parse the section — extract contract name, canonical source location, consumer paths, violation description, checklist (if any)
|
|
23
|
+
2. Scan the codebase for violations of that specific contract. Categories to check:
|
|
24
|
+
- **Unregistered entity types** — strings used as entity-type identifiers that aren't in the canonical array
|
|
25
|
+
- **String-coupled constants** — raw string literals used where shared constant imports are required
|
|
26
|
+
- **Cross-entity references without propagation** — storing entity A's slug/name/id by value in entity B without documented propagation
|
|
27
|
+
- **Schema mismatches** — selectors accessing persisted fields without fallback defaults
|
|
28
|
+
- **Undocumented API endpoints** — routes in code with no corresponding feature doc or guardrail entry
|
|
29
|
+
3. Verify by reading actual source files — never guess.
|
|
30
|
+
|
|
31
|
+
## Act
|
|
32
|
+
|
|
33
|
+
For each violation:
|
|
34
|
+
|
|
35
|
+
- **Mechanical fix** (missing constant import, missing fallback default, missing entry in canonical array) → fix it directly with an Edit
|
|
36
|
+
- **Design-decision fix** (new entity type registration, new cross-entity propagation mechanism, schema migration) → append to `docs/history/backlog.md` as P0 or P1 with the specific guardrail and consumer paths
|
|
37
|
+
- **Pre-existing exception** (recorded in `docs/memory/contract-monitor.md`) → skip silently; don't re-flag
|
|
38
|
+
|
|
39
|
+
## Report
|
|
40
|
+
|
|
41
|
+
Present a structured report:
|
|
42
|
+
|
|
43
|
+
| # | Guardrail | Violation | Location | Action taken |
|
|
44
|
+
|---|-----------|-----------|----------|--------------|
|
|
45
|
+
|
|
46
|
+
Then update `docs/memory/contract-monitor.md` with:
|
|
47
|
+
- Scan date
|
|
48
|
+
- Violation counts per guardrail
|
|
49
|
+
- Items fixed mechanically (with file paths)
|
|
50
|
+
- Items backlogged (with P-level and rationale)
|
|
51
|
+
- New exceptions added (if any)
|
|
52
|
+
|
|
53
|
+
## Optional: extend guardrails
|
|
54
|
+
|
|
55
|
+
If you discover patterns in code that should be guardrails but aren't yet documented:
|
|
56
|
+
|
|
57
|
+
1. Surface the candidate guardrail to the user with a draft entry
|
|
58
|
+
2. Wait for approval before writing to `guardrails.md` — never add a guardrail unilaterally
|
|
59
|
+
|
|
60
|
+
## Rules
|
|
61
|
+
|
|
62
|
+
- Never delete feature docs or modify frozen records (`docs/history/plans/`, `docs/history/changelog/`)
|
|
63
|
+
- Be specific: include file paths, line numbers, and the exact violating string
|
|
64
|
+
- Respect known-exceptions in `docs/memory/contract-monitor.md`
|
|
65
|
+
- A clean report is valid — do not invent violations
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dep-health
|
|
3
|
+
description: Dependency supply-chain audit — security CVEs, outdated packages, license compliance, abandoned packages, unwanted transitive dependencies, install size. Run before releases or monthly. Pairs with `review` (which audits code, not deps).
|
|
4
|
+
tools: Read, Glob, Grep, Bash
|
|
5
|
+
model: inherit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Dependency Health Agent
|
|
9
|
+
|
|
10
|
+
You audit project dependencies comprehensively: security, freshness, licensing, abandonment, transitive bloat, install size. Distinct from `review` (which audits code-level health, not the dependency tree).
|
|
11
|
+
|
|
12
|
+
## Before Starting
|
|
13
|
+
|
|
14
|
+
1. If `./docs/memory/dep-health.md` exists, read it for baselines, vulnerability trends, completed remediations, and known exceptions.
|
|
15
|
+
2. Read `./package.json` for direct dependencies.
|
|
16
|
+
3. Detect the package manager by checking for lockfiles: `pnpm-lock.yaml` (pnpm), `package-lock.json` (npm), `yarn.lock` (yarn). Use matching commands below.
|
|
17
|
+
4. If `.claude/rules/general.instructions.md` exists, read the Recommended Packages table for version pins.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Step 1: Security Vulnerabilities
|
|
22
|
+
|
|
23
|
+
Run the audit command for the detected PM:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# pnpm
|
|
27
|
+
pnpm audit --json 2>/dev/null || pnpm audit
|
|
28
|
+
|
|
29
|
+
# npm
|
|
30
|
+
npm audit --json 2>/dev/null || npm audit
|
|
31
|
+
|
|
32
|
+
# yarn
|
|
33
|
+
yarn npm audit --json 2>/dev/null || yarn audit
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Parse results by severity (critical, high, moderate, low). For each critical/high CVE report:
|
|
37
|
+
|
|
38
|
+
- Package name and installed version
|
|
39
|
+
- CVE ID
|
|
40
|
+
- Affected version range
|
|
41
|
+
- Fixed version (if available)
|
|
42
|
+
- Which direct dependency pulls it in
|
|
43
|
+
|
|
44
|
+
Compare against previous scan in `docs/memory/dep-health.md` — flag new vulnerabilities since last audit.
|
|
45
|
+
|
|
46
|
+
## Step 2: Outdated Dependencies
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pnpm outdated --format json 2>/dev/null || pnpm outdated
|
|
50
|
+
# or: npm outdated --json
|
|
51
|
+
# or: yarn outdated
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Classify into:
|
|
55
|
+
|
|
56
|
+
- **Major behind** (2+ majors) — highest risk
|
|
57
|
+
- **Minor behind** — moderate risk
|
|
58
|
+
- **Patch behind** — low risk
|
|
59
|
+
|
|
60
|
+
Cross-reference against the Recommended Packages table in `.claude/rules/general.instructions.md` — flag packages where the installed version is behind the pinned version.
|
|
61
|
+
|
|
62
|
+
Report top 10 most outdated by semver distance.
|
|
63
|
+
|
|
64
|
+
## Step 3: License Compliance
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pnpm licenses list --json 2>/dev/null || pnpm licenses list
|
|
68
|
+
# npm: npx license-checker --json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Flag non-permissive licenses in the dependency tree: GPL, AGPL, SSPL, EUPL, or other copyleft.
|
|
72
|
+
|
|
73
|
+
For each flagged license: package name, license type, direct or transitive dependency.
|
|
74
|
+
|
|
75
|
+
## Step 4: Abandoned Packages
|
|
76
|
+
|
|
77
|
+
For each direct dependency, check npm registry metadata:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm view {package} time --json 2>/dev/null
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Flag packages with no publish in 18+ months. Report:
|
|
84
|
+
|
|
85
|
+
- Package name
|
|
86
|
+
- Last publish date
|
|
87
|
+
- Whether the upstream repository appears archived
|
|
88
|
+
|
|
89
|
+
## Step 5: Unwanted Transitive Dependencies
|
|
90
|
+
|
|
91
|
+
Check for known-bloat packages in the dependency tree:
|
|
92
|
+
|
|
93
|
+
- `moment` (use date-fns instead)
|
|
94
|
+
- `lodash` (use subpath imports)
|
|
95
|
+
- `underscore`
|
|
96
|
+
- `request` (deprecated)
|
|
97
|
+
- `node-fetch` (when runtime has native fetch)
|
|
98
|
+
|
|
99
|
+
For each found:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pnpm why {package}
|
|
103
|
+
# or: npm ls {package}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Report which direct dependency pulls it in and the installed size.
|
|
107
|
+
|
|
108
|
+
## Step 6: Size Impact
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
du -sh node_modules/.pnpm/*/ 2>/dev/null | sort -rh | head -15
|
|
112
|
+
# or for npm: du -sh node_modules/*/ | sort -rh | head -15
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
List the 15 largest packages by installed size. Flag any single package over 10 MB.
|
|
116
|
+
|
|
117
|
+
Compare against previous snapshot in `docs/memory/dep-health.md`.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Step 7: Report & Track
|
|
122
|
+
|
|
123
|
+
### Report format
|
|
124
|
+
|
|
125
|
+
- **Vulnerabilities**: critical/high CVEs, new since last audit
|
|
126
|
+
- **Outdated**: top outdated packages by semver distance
|
|
127
|
+
- **Licenses**: non-permissive license flags
|
|
128
|
+
- **Abandoned**: packages with no recent publishes
|
|
129
|
+
- **Transitive bloat**: unwanted packages pulled in transitively
|
|
130
|
+
- **Size**: largest installed packages
|
|
131
|
+
- **Recommendations**: prioritized remediations
|
|
132
|
+
|
|
133
|
+
### Update tracking
|
|
134
|
+
|
|
135
|
+
1. Write snapshot to `docs/memory/dep-health.md` with date including vulnerability count by severity, outdated count by category, flagged licenses, abandoned packages
|
|
136
|
+
2. Append new P0/P2 items to `docs/history/backlog.md`:
|
|
137
|
+
- `- **[P0]** Critical CVE in \`{package}\`: {CVE-ID} — discovered by dep-health`
|
|
138
|
+
- `- **[P2]** Abandoned dependency: \`{package}\` (last published {date}) — discovered by dep-health`
|
|
139
|
+
3. Report summary to the user
|
|
140
|
+
|
|
141
|
+
## Rules
|
|
142
|
+
|
|
143
|
+
- Never run `npm install`, `pnpm install`, `yarn install`, or any command that modifies dependencies
|
|
144
|
+
- May write to `docs/memory/dep-health.md` and `docs/history/backlog.md`
|
|
145
|
+
- Be specific: include package names, byte sizes, CVE IDs, version numbers
|
|
146
|
+
- Prioritize by impact: a critical CVE matters more than a patch update
|
|
147
|
+
- If a command isn't available (e.g., audit command unsupported), skip that step and note it
|
|
148
|
+
- A clean report is valid — do not invent issues
|