@drawbridge/drawbridge-agents 0.1.2 → 0.1.4

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.
@@ -25,6 +25,7 @@
25
25
  "../drawbridge-redis",
26
26
  "../drawbridge-share",
27
27
  "../drawbridge-shopify",
28
+ "../drawbridge-shopify-app",
28
29
  "../drawbridge-stripe",
29
30
  "../drawbridge-sync",
30
31
  "../drawbridge-telemetry",
@@ -32,5 +33,18 @@
32
33
  "../drawbridge-webhooks",
33
34
  "../drawbridge-website"
34
35
  ]
36
+ },
37
+ "hooks": {
38
+ "Stop": [
39
+ {
40
+ "matcher": "",
41
+ "hooks": [
42
+ {
43
+ "type": "command",
44
+ "command": "node ./node_modules/@drawbridge/drawbridge-agents/hooks/drift-check.js"
45
+ }
46
+ ]
47
+ }
48
+ ]
35
49
  }
36
50
  }
@@ -3,6 +3,10 @@
3
3
  "sentry": {
4
4
  "type": "http",
5
5
  "url": "https://mcp.sentry.dev/mcp"
6
+ },
7
+ "asana": {
8
+ "type": "sse",
9
+ "url": "https://mcp.asana.com/sse"
6
10
  }
7
11
  }
8
12
  }
package/README.md CHANGED
@@ -19,16 +19,17 @@ conventions/ ← canonical, agent-neutral content (@-imported int
19
19
  claude/
20
20
  CLAUDE.md ← aggregator — @-imports every conventions/*.md
21
21
 
22
- .claude-template/ ← mirrored into each consumer repo's .claude/
23
- settings.json ← shared permissions/env
22
+ .claude-template/ ← the complete shared .claude/, mirrored into each consumer repo
23
+ settings.json ← permissions, additionalDirectories, drift-check Stop hook
24
+ skills/ ← shared skills (drawbridge-ship-feature, -asana-ship-handoff, ...)
24
25
  hooks/ agents/ commands/
25
26
 
26
27
  .root-template/ ← mirrored into each consumer repo's root
27
- .mcp.json ← project-level MCP servers (e.g. Sentry) — gitignored in consumers
28
+ .mcp.json ← project-level MCP servers (Sentry, Asana)
28
29
  .editorconfig ← family-wide editor defaults (tabs, lf, utf-8)
29
30
 
30
31
  bin/
31
- sync-claude.js ← drawbridge-agents-sync — mirrors templates + installs hook/skills + graphify preflight
32
+ sync-claude.js ← drawbridge-agents-sync — mirrors templates + graphify preflight
32
33
  graph.js ← drawbridge-agents-graph — builds the merged family knowledge graph
33
34
  preflight-graphify.js ← ensures the graphify runtime is installed (called by sync)
34
35
 
@@ -36,13 +37,7 @@ scripts/
36
37
  check-doc-links.js ← drawbridge-agents-check-docs — validates @story/@doc tags resolve
37
38
 
38
39
  hooks/
39
- drift-check.js ← Stop hook blocks on unresolved doc tags, nudges on stale graph
40
-
41
- skills/ ← shared skills, installed to user-level ~/.claude/skills on sync
42
- drawbridge-asana-ship-handoff/ drawbridge-ship-feature/
43
- drawbridge-explore-ecosystem/ drawbridge-record-contract/
44
-
45
- graph/ ← generated (gitignored) merged.json + manifest.json; see graph/README.md
40
+ drift-check.js ← Stop hook (wired via .claude-template/settings.json)
46
41
  ```
47
42
 
48
43
  ## Consuming from a drawbridge-* repo
@@ -72,19 +67,23 @@ graph/ ← generated (gitignored) merged.json + manifest.js
72
67
  "sync": ". \"$HOME/.nvm/nvm.sh\" && nvm use && npm prune && npm install && npx drawbridge-agents-sync"
73
68
  ```
74
69
 
75
- On every `npm run sync`, this package mirrors:
76
- - `.claude-template/` → consumer's `.claude/`
70
+ On every `npm run sync`, this package mirrors the single source of truth into the consumer:
71
+ - `.claude-template/` → consumer's `.claude/` — the **complete** shared setup: `settings.json`
72
+ (permissions, `additionalDirectories`, the drift-check Stop hook), plus `skills/`,
73
+ `agents/`, `commands/`, `hooks/`.
77
74
  - `.root-template/` → consumer's repo root (`.mcp.json`, `.editorconfig`)
78
75
 
79
- Paths that exist in the templates are **managed** (overwritten on each sync). Anything else in `.claude/` or at the repo root — including `settings.local.json` and any consumer-only hooks/agents/commands — is left untouched.
76
+ Paths that exist in the templates are **managed** (overwritten on each sync) every repo ends
77
+ up with an identical `.claude/`. Only `settings.local.json` (each dev's personal overrides) and
78
+ any consumer-only files are left untouched.
80
79
 
81
80
  Claude Code's `@` imports cascade — one import line resolves the whole tree under `claude/CLAUDE.md`.
82
81
 
83
82
  ## Ecosystem knowledge, graph, and drift checks
84
83
 
85
84
  Beyond the conventions, this package gives agents cross-repo visibility and keeps code, docs,
86
- and the knowledge graph from drifting. Everything below is committed **only in this repo** and
87
- reaches consumers without committing anything to them (see "Isolation").
85
+ and the knowledge graph from drifting. This package is the single source; each repo gets an
86
+ identical copy via the sync mirror (see "Delivery model").
88
87
 
89
88
  - **Family knowledge graph (Graphify).** `npx drawbridge-agents-graph` runs code-only AST
90
89
  extraction over every `drawbridge-*` repo and writes the merged graph to the committed, shared
@@ -99,24 +98,28 @@ reaches consumers without committing anything to them (see "Isolation").
99
98
  reference/<file>#<anchor>` anchors (see `conventions/docs-linkage.md`).
100
99
  `npx drawbridge-agents-check-docs` scans every repo and fails if any anchor no longer resolves
101
100
  against `drawbridge-docs`.
102
- - **Drift-check Stop hook.** `hooks/drift-check.js` runs at each task-turn end (installed into the
103
- consumer's gitignored `.claude/settings.local.json`). It **blocks** on an unresolved doc
104
- anchor, and **nudges** (non-blocking) when the graph looks stale or docs may need updating. It
105
- skips plan-mode and turns with no source changes.
106
- - **Shared skills.** Installed to user-level `~/.claude/skills/` on sync:
101
+ - **Drift-check Stop hook.** `hooks/drift-check.js` runs at each task-turn end, wired via the
102
+ drift-check `Stop` hook in the mirrored `.claude/settings.json`. It **blocks** on an unresolved
103
+ doc anchor, and **nudges** (non-blocking) when the graph looks stale or docs may need updating.
104
+ It skips plan-mode and turns with no source changes.
105
+ - **Shared skills.** Mirrored into every repo's `.claude/skills/` (from `.claude-template/skills/`):
107
106
  `drawbridge-ship-feature` (verify → docs → graph → handoff), `drawbridge-asana-ship-handoff`,
108
107
  `drawbridge-explore-ecosystem`, `drawbridge-record-contract`.
109
108
 
110
- ### Isolation (nothing ships to DigitalOcean)
109
+ ### Delivery model
111
110
 
112
- - The package is a **devDependency** not installed in production.
113
- - The graph and CLI are per-machine (`~/.graphify/`); nothing graph-related is written into the
114
- consumer repo, and `graphify install` (which edits `CLAUDE.md`) is never run.
115
- - The Stop hook is merged into `.claude/settings.local.json` (gitignored).
116
- - Skills install to `~/.claude/skills/` (per-machine, outside any repo).
117
- - The only in-repo footprint in consumers is inert `@story`/`@doc` source comments.
111
+ Every repo has an **identical, normalized** agent setup, all sourced from this one package:
118
112
 
119
- After `npm run sync`, `git status` in a consumer should show **no new tracked files**.
113
+ - **`CLAUDE.md`** one-line `@import` (live from `node_modules`).
114
+ - **`.claude/`** (settings.json + permissions + hook, skills, agents/commands/hooks) → **mirrored**
115
+ from `.claude-template/` and committed per repo.
116
+ - **`.mcp.json`, `.editorconfig`** → mirrored from `.root-template/` and committed per repo.
117
+ - Config files (`.claude/`, `.mcp.json`) are copied rather than imported because only markdown
118
+ (`CLAUDE.md`) supports `@import`; the mirror keeps the copies identical from one source.
119
+ - The package is a **devDependency** → never installed in production / on DigitalOcean.
120
+ - The knowledge graph is committed once in `drawbridge-docs/knowledge/graphs/`; the graphify
121
+ runtime + build cache are per-machine under `~/.graphify/` (gitignored). `graphify install`
122
+ (which edits `CLAUDE.md`) is never run.
120
123
 
121
124
  ## Updating a rule or shared `.claude/` file
122
125
 
package/bin/graph.js CHANGED
@@ -70,7 +70,11 @@ for (const name of repos) {
70
70
  )
71
71
  // graphify still drops a graphify-out/ in the target repo despite --out; remove it so the
72
72
  // build never dirties a source repo (the incremental cache lives under ~/.graphify/build).
73
- fs.rmSync(path.join(repoPath, 'graphify-out'), { recursive: true, force: true })
73
+ const stray = path.join(repoPath, 'graphify-out')
74
+ if (fs.existsSync(stray)) {
75
+ if (fs.rmSync) fs.rmSync(stray, { recursive: true, force: true })
76
+ else fs.rmdirSync(stray, { recursive: true })
77
+ }
74
78
  built.push(name)
75
79
  }
76
80
 
@@ -7,6 +7,13 @@ const { execFileSync } = require('child_process')
7
7
  const consumerRoot = process.env.INIT_CWD || process.cwd()
8
8
  const packageRoot = path.resolve(__dirname, '..')
9
9
 
10
+ // Recursive remove that works on Node 12+ (fs.rmSync is 14.14+).
11
+ const rmrf = (p) => {
12
+ if (!fs.existsSync(p)) return
13
+ if (fs.rmSync) fs.rmSync(p, { recursive: true, force: true })
14
+ else fs.rmdirSync(p, { recursive: true })
15
+ }
16
+
10
17
  const copied = []
11
18
 
12
19
  const mirror = (src, dst) => {
@@ -23,7 +30,9 @@ const mirror = (src, dst) => {
23
30
  }
24
31
  }
25
32
 
26
- // 1. Mirror the committed template dirs into the consumer (existing behaviour).
33
+ // 1. Mirror the single source of truth into the consumer. .claude-template holds the entire
34
+ // shared .claude/ setup (settings.json incl. permissions + the drift-check Stop hook, plus
35
+ // agents/commands/hooks/skills); .root-template holds root files (.mcp.json, .editorconfig).
27
36
  const mirrorTemplates = () => {
28
37
  const targets = [
29
38
  { src: path.join(packageRoot, '.claude-template'), dst: path.join(consumerRoot, '.claude'), required: true },
@@ -40,63 +49,42 @@ const mirrorTemplates = () => {
40
49
  mirror(src, dst)
41
50
  }
42
51
  console.log(`drawbridge-agents-sync: ${ copied.length } file(s) mirrored`)
43
- for (const p of copied) console.log(` ${ p }`)
44
52
  }
45
53
 
46
- // 2. Merge the drift-check Stop hook into the consumer's gitignored .claude/settings.local.json,
47
- // so nothing is committed to the consumer repo (see conventions/docs-linkage.md).
48
- const installStopHook = () => {
49
- const command = 'node ./node_modules/@drawbridge/drawbridge-agents/hooks/drift-check.js'
50
- const settingsPath = path.join(consumerRoot, '.claude', 'settings.local.json')
51
-
52
- let settings = {}
53
- if (fs.existsSync(settingsPath)) {
54
+ // 2. Migration cleanup from the previous delivery model: the drift hook used to be merged into
55
+ // the gitignored settings.local.json and skills installed to user-level ~/.claude/skills.
56
+ // Both now ship via the mirror above, so remove the old copies to avoid duplicates.
57
+ const migrateOldDelivery = () => {
58
+ const localSettings = path.join(consumerRoot, '.claude', 'settings.local.json')
59
+ if (fs.existsSync(localSettings)) {
54
60
  try {
55
- settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'))
61
+ const settings = JSON.parse(fs.readFileSync(localSettings, 'utf8'))
62
+ if (settings.hooks && Array.isArray(settings.hooks.Stop)) {
63
+ settings.hooks.Stop = settings.hooks.Stop.filter((entry) =>
64
+ !(entry.hooks || []).some((hook) => typeof hook.command === 'string' && hook.command.includes('drawbridge-agents/hooks/drift-check.js'))
65
+ )
66
+ if (settings.hooks.Stop.length === 0) delete settings.hooks.Stop
67
+ if (Object.keys(settings.hooks).length === 0) delete settings.hooks
68
+ fs.writeFileSync(localSettings, JSON.stringify(settings, null, 2) + '\n')
69
+ }
56
70
  } catch (error) {
57
- console.warn('drawbridge-agents-sync: could not parse .claude/settings.local.json leaving Stop hook uninstalled')
58
- return
71
+ // leave settings.local.json untouched on parse trouble
59
72
  }
60
73
  }
61
74
 
62
- settings.hooks = settings.hooks || {}
63
- settings.hooks.Stop = settings.hooks.Stop || []
64
-
65
- const already = settings.hooks.Stop.some((entry) =>
66
- (entry.hooks || []).some((hook) => typeof hook.command === 'string' && hook.command.includes('drawbridge-agents/hooks/drift-check.js'))
67
- )
68
- if (already) return
69
-
70
- settings.hooks.Stop.push({
71
- matcher: '',
72
- hooks: [ { type: 'command', command } ]
73
- })
74
- fs.mkdirSync(path.dirname(settingsPath), { recursive: true })
75
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n')
76
- console.log('drawbridge-agents-sync: drift-check Stop hook installed in .claude/settings.local.json')
77
- }
78
-
79
- // 3. Install the shared skills to the user-level ~/.claude/skills/ (per-machine, so nothing is
80
- // committed to any consumer repo). Only drawbridge-* skills are managed.
81
- const installSkills = () => {
82
- const src = path.join(packageRoot, 'skills')
83
- if (!fs.existsSync(src)) return
84
- const dst = path.join(os.homedir(), '.claude', 'skills')
85
- let count = 0
86
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
87
- if (!entry.isDirectory() || !entry.name.startsWith('drawbridge-')) continue
88
- mirror(path.join(src, entry.name), path.join(dst, entry.name))
89
- count += 1
75
+ const userSkills = path.join(os.homedir(), '.claude', 'skills')
76
+ if (fs.existsSync(userSkills)) {
77
+ for (const name of fs.readdirSync(userSkills)) {
78
+ if (name.startsWith('drawbridge-')) rmrf(path.join(userSkills, name))
79
+ }
90
80
  }
91
- if (count > 0) console.log(`drawbridge-agents-sync: ${ count } shared skill(s) installed to ~/.claude/skills/`)
92
81
  }
93
82
 
94
- // 4. Ensure the graphify runtime is present + MCP registered (hard-fails if it can't be).
83
+ // 3. Ensure the graphify runtime is present + reachable (hard-fails if it can't be).
95
84
  const graphifyPreflight = () => {
96
85
  execFileSync('node', [ path.join(packageRoot, 'bin', 'preflight-graphify.js') ], { stdio: 'inherit' })
97
86
  }
98
87
 
99
88
  mirrorTemplates()
100
- installStopHook()
101
- installSkills()
89
+ migrateOldDelivery()
102
90
  graphifyPreflight()
@@ -1,18 +1,18 @@
1
1
  # Superpowers working docs live in drawbridge-docs
2
2
 
3
3
  All superpowers **plans** and **specs** — the outputs of the brainstorming / writing-plans /
4
- spec workflows — live in one canonical place:
4
+ spec workflows — live in one canonical place, at the **root** of drawbridge-docs:
5
5
 
6
6
  ```
7
- drawbridge-docs/docs/superpowers/plans/YYYY-MM-DD-<slug>.md
8
- drawbridge-docs/docs/superpowers/specs/YYYY-MM-DD-<slug>.md
7
+ drawbridge-docs/superpowers/plans/YYYY-MM-DD-<slug>.md
8
+ drawbridge-docs/superpowers/specs/YYYY-MM-DD-<slug>.md
9
9
  ```
10
10
 
11
11
  This is the **only** allowed home for these working docs. Rules:
12
12
 
13
- - Do **not** create or keep `docs/superpowers/`, `docs/plans/`, or any plan/spec working docs
13
+ - Do **not** create or keep superpowers plans/specs (or `docs/superpowers/`, `docs/plans/`)
14
14
  inside any other repo. When you finalize a plan or spec, write it under
15
- `../drawbridge-docs/docs/superpowers/{plans,specs}/`, not the repo you're working in.
15
+ `../drawbridge-docs/superpowers/{plans,specs}/`, not the repo you're working in.
16
16
  - When plan mode or a superpowers skill produces a plan/spec, the finalized document goes into
17
17
  drawbridge-docs — regardless of which repo the work targets. Note the target repo in the doc.
18
18
  - `.superpowers/` (brainstorm scratch — server state, HTML mockups) is ephemeral. **Never commit
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawbridge/drawbridge-agents",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Shared agent-instruction content (rules, code style, conventions) for the drawbridge-* monorepo.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -23,7 +23,6 @@
23
23
  "bin",
24
24
  "hooks",
25
25
  "scripts",
26
- "skills",
27
26
  "graph",
28
27
  "README.md"
29
28
  ],