@drawbridge/drawbridge-agents 0.0.6 → 0.0.8

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.
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "sentry": {
4
+ "type": "http",
5
+ "url": "https://mcp.sentry.dev/mcp"
6
+ }
7
+ }
8
+ }
package/README.md CHANGED
@@ -26,8 +26,11 @@ claude/
26
26
  agents/
27
27
  commands/
28
28
 
29
+ .root-template/ ← mirrored into each consumer repo's root
30
+ .mcp.json ← project-level MCP servers (e.g. Sentry)
31
+
29
32
  bin/
30
- sync-claude.js ← drawbridge-agents-sync — mirrors .claude-template/ consumer's .claude/
33
+ sync-claude.js ← drawbridge-agents-sync — mirrors templates into consumer repo
31
34
  ```
32
35
 
33
36
  ## Consuming from a drawbridge-* repo
@@ -55,7 +58,11 @@ bin/
55
58
  "sync": ". \"$HOME/.nvm/nvm.sh\" && nvm use && npm prune && npm install && npx drawbridge-agents-sync"
56
59
  ```
57
60
 
58
- On every `npm run sync`, the contents of this package's `.claude-template/` are copied into the consumer's `.claude/`. Paths that exist in the template are **managed** (overwritten on each sync). Anything else in `.claude/` — including `settings.local.json` and any consumer-only hooks/agents/commands — is left untouched.
61
+ On every `npm run sync`, this package mirrors:
62
+ - `.claude-template/` → consumer's `.claude/`
63
+ - `.root-template/` → consumer's repo root (currently just `.mcp.json`)
64
+
65
+ 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.
59
66
 
60
67
  Claude Code's `@` imports cascade — one import line resolves the whole tree under `claude/CLAUDE.md`.
61
68
 
@@ -3,13 +3,12 @@ const fs = require('fs')
3
3
  const path = require('path')
4
4
 
5
5
  const consumerRoot = process.env.INIT_CWD || process.cwd()
6
- const templateRoot = path.resolve(__dirname, '..', '.claude-template')
7
- const destRoot = path.join(consumerRoot, '.claude')
6
+ const packageRoot = path.resolve(__dirname, '..')
8
7
 
9
- if (!fs.existsSync(templateRoot)) {
10
- console.error(`drawbridge-agents-sync: template not found at ${templateRoot}`)
11
- process.exit(1)
12
- }
8
+ const targets = [
9
+ { src: path.join(packageRoot, '.claude-template'), dst: path.join(consumerRoot, '.claude'), required: true },
10
+ { src: path.join(packageRoot, '.root-template'), dst: consumerRoot, required: false },
11
+ ]
13
12
 
14
13
  const copied = []
15
14
 
@@ -27,7 +26,16 @@ const mirror = (src, dst) => {
27
26
  }
28
27
  }
29
28
 
30
- mirror(templateRoot, destRoot)
29
+ for (const { src, dst, required } of targets) {
30
+ if (!fs.existsSync(src)) {
31
+ if (required) {
32
+ console.error(`drawbridge-agents-sync: template not found at ${src}`)
33
+ process.exit(1)
34
+ }
35
+ continue
36
+ }
37
+ mirror(src, dst)
38
+ }
31
39
 
32
- console.log(`drawbridge-agents-sync: ${copied.length} file(s) mirrored to ${path.relative(consumerRoot, destRoot) || '.claude'}`)
40
+ console.log(`drawbridge-agents-sync: ${copied.length} file(s) mirrored`)
33
41
  for (const p of copied) console.log(` ${p}`)
package/claude/CLAUDE.md CHANGED
@@ -4,3 +4,6 @@
4
4
  @../conventions/jsx-fragments.md
5
5
  @../conventions/transactions.md
6
6
  @../conventions/property-shorthand.md
7
+ @../conventions/drawbridge-packages.md
8
+ @../conventions/sentry-sdk.md
9
+ @../conventions/app-web-forms.md
@@ -0,0 +1,28 @@
1
+ # app-web form conventions
2
+
3
+ Applies to `drawbridge-app-web` only.
4
+
5
+ ## Always use react-hook-form
6
+
7
+ Every form is built with **react-hook-form** (`useForm` + `Controller`,
8
+ with the `yup` resolver from `@/lib/yup` where validation is needed).
9
+ Never hand-rolled `useState` field handling. Mirror existing forms like
10
+ `components/form-profile.js` and `components/form-auth.js`.
11
+
12
+ This applies even to **field-less submit/action forms** — a form that's
13
+ just a button, e.g. a "send code" or confirm step. Use
14
+ `useForm()` + `handleSubmit(onSubmit)` and drive disabled/loader state
15
+ off `formState.isSubmitting`. Do NOT use a manual
16
+ `const [submitting, setSubmitting] = useState(false)` +
17
+ `event.preventDefault()`. Reference pattern: `components/form-action.js`.
18
+
19
+ ## Secondary forms launch in the global modal
20
+
21
+ For a form triggered from within another form (e.g. "change email" from
22
+ the profile form), render it inside the **global modal** via
23
+ `useModal({ queryClient })` → `modal.open({ title, message: <TheForm/> })`.
24
+ Do not nest a second `<form>` inside an existing `<form>`.
25
+
26
+ The modal portals to the top level, so the inner `<form>` is not a DOM
27
+ descendant of the outer one. Nested `<form>` is invalid HTML and breaks
28
+ submit propagation. Reference pattern: `components/form-verify-prize.js`.
@@ -0,0 +1,66 @@
1
+ # Drawbridge package workflow
2
+
3
+ Conventions for working on the `@drawbridge/*` npm packages
4
+ (`drawbridge-utils`, `drawbridge-components`, `drawbridge-mongodb`,
5
+ `drawbridge-redis`, `drawbridge-stripe`, `drawbridge-shopify`,
6
+ `drawbridge-telemetry`) and the apps that consume them.
7
+
8
+ ## Build + publish script
9
+
10
+ All `@drawbridge/*` packages standardize on:
11
+
12
+ ```json
13
+ "scripts": {
14
+ "sync": ". \"$HOME/.nvm/nvm.sh\" && nvm use && npm prune && npm install",
15
+ "build": "tsup && npm publish"
16
+ }
17
+ ```
18
+
19
+ **Do NOT add a `prepare: tsup` script.** `prepare` runs automatically on
20
+ `npm publish`, so it duplicates the work `build` already does. Worse: if
21
+ `npm publish` is invoked from a shell where the wrong Node version is
22
+ active (e.g. system Node before `nvm use`), `prepare` triggers `tsup`
23
+ under that wrong Node and fails with cryptic syntax errors (optional
24
+ chaining unrecognized, etc.) even though `npm publish` itself would have
25
+ worked fine.
26
+
27
+ ## Coordinating peer dependencies on bump
28
+
29
+ When bumping any `@drawbridge/*` package, audit every sibling package's
30
+ `peerDependencies` for a stale pin. Peer pins are version-exact (e.g.
31
+ `"@drawbridge/shopify": "0.0.3"`), so bumping the package in consumer
32
+ apps while leaving a sibling's peer dep on the old version triggers
33
+ `ERESOLVE` at install time.
34
+
35
+ Before publishing a bump, grep peerDependencies across the family:
36
+
37
+ ```sh
38
+ grep -A8 peerDependencies /Users/darrenshea/Projects/drawbridge-*/package.json
39
+ ```
40
+
41
+ If a sibling package peer-pins something you just bumped, that sibling
42
+ needs a coordinated peer-dep update + republish in the same release.
43
+
44
+ ## Check for duplicate logic before renaming across repos
45
+
46
+ When changing a value at the api side (enum keys, schema shape, operator
47
+ vocabulary, etc.), grep every sibling repo for identical-purpose code
48
+ paths that operate on that value. There are known copy-paste duplicates
49
+ that drift silently when only one side is updated.
50
+
51
+ ```sh
52
+ grep -rn "<old-value>" /Users/darrenshea/Projects/drawbridge-* --include="*.js"
53
+ grep -rn "<file-name-or-export>" /Users/darrenshea/Projects/drawbridge-* --include="*.js"
54
+ ```
55
+
56
+ Specifically watch for:
57
+ - segment / rules evaluation
58
+ - mongo schema-shape consumers
59
+ - credential / connection field reads
60
+ - any "pure helper" pattern that may have been copied rather than shared
61
+
62
+ If a duplicate is found, prefer extracting the live copy and deleting
63
+ the stale one over leaving both. Don't reflexively reach for
64
+ `drawbridge-utils` — utils is for code with multiple live consumers;
65
+ if only one repo actually imports the duplicate, the others are dead
66
+ code that should just be deleted.
@@ -53,3 +53,7 @@ const run = async () => {
53
53
  - Single quotes for strings
54
54
 
55
55
  When adding or removing a wrapper block (e.g. a transaction callback, an `if` block), re-indent all enclosed lines to match the new nesting level. If the indentation shift is large or the block is long, prefer rewriting the whole function with the Write tool rather than using multiple Edit calls.
56
+
57
+ ## No underscores in identifiers or prose
58
+
59
+ The drawbridge codebases use camelCase identifiers — never snake_case. This applies to both code and prose in comments, commit messages, and chat. Say "worker thread" (two words) in prose, not "worker_thread". The only acceptable underscores are literal external API names that require them (e.g. the Node module `worker_threads` in a `require`), and even then refer to the concept as "worker thread" in surrounding prose.
@@ -0,0 +1,39 @@
1
+ # Sentry SDK encapsulation
2
+
3
+ Shared `@drawbridge/*` packages that wrap a stateful SDK (one that
4
+ maintains module-scope state — Sentry scopes, registries, singletons)
5
+ must depend on that SDK as a **direct dependency** with a pinned exact
6
+ version. Do NOT declare it as a peer dep with a loose range like
7
+ `">=10"`.
8
+
9
+ ## Why
10
+
11
+ Loose peer-dep ranges let npm resolve two module instances with
12
+ independent state. The previous incident: `drawbridge-telemetry`
13
+ declared `@sentry/core: ">=10"` as a peer. Consumers' `@sentry/node`
14
+ had a strict `@sentry/core@<exact>` dep, but the loose peer range
15
+ happily resolved to a hoisted `@sentry/core@<different version>` from
16
+ elsewhere in the tree. npm kept both: a hoisted copy used by
17
+ `drawbridge-telemetry` and a nested copy under `@sentry/node` used by
18
+ service code. Each held its own `getIsolationScope()` registry, so
19
+ `Sentry.setTag(...)` writes from the wrapper landed on one scope while
20
+ `captureException` from service code read from the other —
21
+ silently-broken telemetry that took an entire debug session to
22
+ root-cause.
23
+
24
+ ## How to apply
25
+
26
+ - New shared package wrapping `@sentry/*`, `redis`, `mongodb`,
27
+ `opentelemetry`, or any other SDK with module-state: direct-dep the
28
+ SDK, pinned to an exact version. Export the SDK namespace if
29
+ consumers need the full surface
30
+ (`export { Sentry }; export * from 'X';`).
31
+ - New consumer of such a wrapper: NEVER `require('@sentry/node')` or
32
+ similar in service code. Route everything through the wrapper.
33
+ - For heavy SDKs only some consumers need (e.g. `@sentry/nextjs`),
34
+ use an *optional* peer dep at an exact pinned version, and document
35
+ in the wrapper README that consumers must install that version.
36
+ - When stateful behavior fails mysteriously — scope tags missing,
37
+ registry lookups empty, singleton mismatch — `npm ls <sdk-package>`
38
+ is the first diagnostic. There must be exactly one entry, fully
39
+ deduped.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawbridge/drawbridge-agents",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Shared agent-instruction content (rules, code style, conventions) for the drawbridge-* monorepo.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -17,6 +17,7 @@
17
17
  "claude",
18
18
  "conventions",
19
19
  ".claude-template",
20
+ ".root-template",
20
21
  "bin",
21
22
  "README.md"
22
23
  ],
@@ -1,9 +0,0 @@
1
- {
2
- "servers": {
3
- "sentry": {
4
- "serverUrl": "https://mcp.sentry.dev/mcp",
5
- "type": "http"
6
- }
7
- },
8
- "inputs": []
9
- }