@drawbridge/drawbridge-agents 0.1.19 → 0.1.20
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.
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Managed by @drawbridge/drawbridge-agents
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Shared hook scripts are mirrored into every consumer repo's `.claude/hooks/` on `npm run sync`,
|
|
4
|
+
from the package's `hooks/` directory (the single source). They are **tracked** in each consumer
|
|
5
|
+
and invoked by `.claude/settings.json` via `node ./.claude/hooks/<name>.js` — never through
|
|
6
|
+
`node_modules` — so they keep working in fresh git worktrees that haven't run `npm install`.
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
To add or change a shared hook: edit it in `drawbridge-agents/hooks/`, bump the package version,
|
|
9
|
+
publish, then `npm run sync` in each consumer and commit the mirrored `.claude/` changes.
|
|
10
|
+
|
|
11
|
+
Consumer-local hooks can be added directly to the consumer's `.claude/hooks/` with a different
|
|
12
|
+
filename — the sync mirror only overwrites paths that exist in the package.
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"hooks": [
|
|
42
42
|
{
|
|
43
43
|
"type": "command",
|
|
44
|
-
"command": "node
|
|
44
|
+
"command": "node ./.claude/hooks/guard-superpowers-docs.js"
|
|
45
45
|
}
|
|
46
46
|
]
|
|
47
47
|
}
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"hooks": [
|
|
53
53
|
{
|
|
54
54
|
"type": "command",
|
|
55
|
-
"command": "node
|
|
55
|
+
"command": "node ./.claude/hooks/drift-check.js"
|
|
56
56
|
}
|
|
57
57
|
]
|
|
58
58
|
}
|
package/bin/sync-claude.js
CHANGED
|
@@ -43,9 +43,13 @@ const mirror = (src, dst) => {
|
|
|
43
43
|
// 1. Mirror the single source of truth into the consumer. .claude-template holds the entire
|
|
44
44
|
// shared .claude/ setup (settings.json incl. permissions + the drift-check Stop hook, plus
|
|
45
45
|
// agents/commands/hooks/skills); .root-template holds root files (.mcp.json, .editorconfig).
|
|
46
|
+
// The package's hooks/ scripts are mirrored into the consumer's tracked .claude/hooks/ so
|
|
47
|
+
// settings.json can invoke them without node_modules — a fresh worktree that hasn't run
|
|
48
|
+
// npm install would otherwise fail open and lose the guard.
|
|
46
49
|
const mirrorTemplates = () => {
|
|
47
50
|
const targets = [
|
|
48
51
|
{ src: path.join(packageRoot, '.claude-template'), dst: path.join(consumerRoot, '.claude'), required: true },
|
|
52
|
+
{ src: path.join(packageRoot, 'hooks'), dst: path.join(consumerRoot, '.claude', 'hooks'), required: true },
|
|
49
53
|
{ src: path.join(packageRoot, '.root-template'), dst: consumerRoot, required: false }
|
|
50
54
|
]
|
|
51
55
|
for (const { src, dst, required } of targets) {
|
package/hooks/drift-check.js
CHANGED
|
@@ -7,7 +7,16 @@ const path = require('path')
|
|
|
7
7
|
const { execFileSync } = require('child_process')
|
|
8
8
|
|
|
9
9
|
const SOURCE_EXTENSIONS = new Set([ '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs' ])
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
// This file runs from two places: hooks/ inside the package (the
|
|
12
|
+
// drawbridge-agents repo itself, or node_modules) where scripts/ is a sibling,
|
|
13
|
+
// and the mirrored copy in a consumer's tracked .claude/hooks/, where it
|
|
14
|
+
// isn't — there, resolve through the consumer's installed package.
|
|
15
|
+
const resolvePackageRoot = (cwd) => {
|
|
16
|
+
const local = path.resolve(__dirname, '..')
|
|
17
|
+
if (fs.existsSync(path.join(local, 'scripts', 'check-doc-links.js'))) return local
|
|
18
|
+
return path.join(cwd, 'node_modules', '@drawbridge', 'drawbridge-agents')
|
|
19
|
+
}
|
|
11
20
|
|
|
12
21
|
const block = (reason) => {
|
|
13
22
|
process.stdout.write(JSON.stringify({ decision: 'block', reason }))
|
|
@@ -51,17 +60,27 @@ const run = () => {
|
|
|
51
60
|
|
|
52
61
|
const cwd = input.cwd || process.cwd()
|
|
53
62
|
const changed = changedFiles(cwd)
|
|
54
|
-
|
|
63
|
+
|
|
64
|
+
// .claude/ churn is sync-mirror tooling (including this hook's own mirrored
|
|
65
|
+
// copy), not project source — it must not trip the drift checks.
|
|
66
|
+
const changedSource = changed.filter((file) =>
|
|
67
|
+
SOURCE_EXTENSIONS.has(path.extname(file)) && !file.split(path.sep).includes('.claude')
|
|
68
|
+
)
|
|
55
69
|
|
|
56
70
|
// Pure Q&A / non-source turns: nothing to check.
|
|
57
71
|
if (changedSource.length === 0) process.exit(0)
|
|
58
72
|
|
|
59
|
-
// 1. Doc-link validity (blocking) — cheap, precise, quick to fix.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
// 1. Doc-link validity (blocking) — cheap, precise, quick to fix. A missing
|
|
74
|
+
// checker (fresh worktree with no node_modules) is not drift — skip, never
|
|
75
|
+
// block on it.
|
|
76
|
+
const checkDocLinks = path.join(resolvePackageRoot(cwd), 'scripts', 'check-doc-links.js')
|
|
77
|
+
if (fs.existsSync(checkDocLinks)) {
|
|
78
|
+
try {
|
|
79
|
+
execFileSync('node', [ checkDocLinks ], { cwd, encoding: 'utf8', stdio: 'pipe' })
|
|
80
|
+
} catch (error) {
|
|
81
|
+
const detail = ((error.stdout || '') + (error.stderr || '')).trim()
|
|
82
|
+
block(`Documentation drift: one or more @story / @doc tags no longer resolve.\n${ detail }\nFix the tag(s) to point at a real drawbridge-docs story ID / reference anchor before finishing.`)
|
|
83
|
+
}
|
|
65
84
|
}
|
|
66
85
|
|
|
67
86
|
const skipGraph = process.env.DRAWBRIDGE_SKIP_GRAPHIFY === '1'
|
|
@@ -100,7 +119,9 @@ const run = () => {
|
|
|
100
119
|
})
|
|
101
120
|
const touchedDocs = changed.some((file) => file.includes(`${ path.sep }drawbridge-docs${ path.sep }`))
|
|
102
121
|
if (touchedAnchoredCode && !touchedDocs) {
|
|
103
|
-
|
|
122
|
+
// `@story` is backticked so doc-link scanners (including stale node_modules
|
|
123
|
+
// copies that don't skip .claude/) never read this sentence as a real tag.
|
|
124
|
+
nudge('You changed code that implements a documented behaviour (carries an `@story` anchor) without touching drawbridge-docs. Confirm the matching user story still matches, or update it in this change.')
|
|
104
125
|
}
|
|
105
126
|
|
|
106
127
|
process.exit(0)
|
package/package.json
CHANGED
|
@@ -6,7 +6,9 @@ const fs = require('fs')
|
|
|
6
6
|
const path = require('path')
|
|
7
7
|
|
|
8
8
|
const SOURCE_EXTENSIONS = new Set([ '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs' ])
|
|
9
|
-
|
|
9
|
+
// .claude is skipped because the sync mirror places shared tooling there (the drift-check hook's
|
|
10
|
+
// own source mentions the tag syntax) — it's not project source and must never be scanned.
|
|
11
|
+
const SKIP_DIRS = new Set([ 'node_modules', '.git', '.next', 'dist', 'build', 'coverage', '.turbo', 'graphify-out', '.claude' ])
|
|
10
12
|
|
|
11
13
|
// The family root is the directory that contains drawbridge-docs. Walk up from cwd to find it.
|
|
12
14
|
const findFamilyRoot = () => {
|