@drawbridge/drawbridge-agents 0.1.9 → 0.1.12
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.
|
@@ -35,6 +35,17 @@
|
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
37
|
"hooks": {
|
|
38
|
+
"PreToolUse": [
|
|
39
|
+
{
|
|
40
|
+
"matcher": "Write|Edit|MultiEdit|NotebookEdit",
|
|
41
|
+
"hooks": [
|
|
42
|
+
{
|
|
43
|
+
"type": "command",
|
|
44
|
+
"command": "node ./node_modules/@drawbridge/drawbridge-agents/hooks/guard-superpowers-docs.js"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
],
|
|
38
49
|
"Stop": [
|
|
39
50
|
{
|
|
40
51
|
"matcher": "",
|
|
@@ -20,7 +20,14 @@ knowledge graph, and the Asana handoff so nothing drifts at ship time.
|
|
|
20
20
|
flag the drift explicitly. See `conventions/docs-linkage.md`.
|
|
21
21
|
3. **Confirm doc anchors resolve.** Run `npx drawbridge-agents-check-docs`; fix any unresolved
|
|
22
22
|
`@story` / `@doc` tag. Add an anchor to newly-added feature code where it maps to a story.
|
|
23
|
-
4. **Refresh the graph
|
|
23
|
+
4. **Refresh the knowledge graph — always, on every feature wrap** (not conditional on whether
|
|
24
|
+
structure "obviously" changed). The canonical graph is the committed snapshot in
|
|
25
|
+
`drawbridge-docs`, shared by the whole team; skipping it lets the shared graph drift. From the
|
|
26
|
+
`drawbridge-docs` repo: `npx drawbridge-agents-graph` (rebuilds `knowledge/graphs/global.json`
|
|
27
|
+
from every repo), then `git add knowledge/graphs/global.json && git commit -m "chore: refresh
|
|
28
|
+
knowledge graph (<feature>)" && git push origin main`. If it reports "graphify runtime not
|
|
29
|
+
found", the `uv`/`graphify` binaries are in `~/.local/bin` — `export PATH="$HOME/.local/bin:$PATH"`
|
|
30
|
+
and retry (do NOT run `graphify install`; it rewrites `CLAUDE.md`).
|
|
24
31
|
5. **Hand off the task.** Invoke `drawbridge-asana-ship-handoff` with the Asana task to reassign
|
|
25
32
|
to the last commenter, post the change summary + retest steps, and set the due date.
|
|
26
33
|
|
|
@@ -9,6 +9,11 @@ skill) so it stops living only in tribal memory.
|
|
|
9
9
|
|
|
10
10
|
- `OAUTH_TOKEN_HMAC_KEY` must be identical in **drawbridge-api** and **drawbridge-sync** — both
|
|
11
11
|
`assertEnv` it at boot. A mismatch silently breaks realtime token verification.
|
|
12
|
+
- reCAPTCHA on auth is a key pair: `GOOGLE_RECAPTCHA_SECRET` (**drawbridge-api**) + `NEXT_PUBLIC_GOOGLE_RECAPTCHA_KEY`
|
|
13
|
+
(**drawbridge-app-web**), both `assertEnv`'d at boot. The api verifies the v3 token app-web's
|
|
14
|
+
`/auth` forms attach and enforces it **unconditionally** on `/oauth/{signup,signin}/request`
|
|
15
|
+
(fails closed). Deploy order matters: ship app-web **before** api, or the api blocks every
|
|
16
|
+
signin that arrives without a token. See `drawbridge-docs/user-stories/03-auth`.
|
|
12
17
|
|
|
13
18
|
## Sync owns reactive side effects
|
|
14
19
|
|
|
@@ -34,6 +39,14 @@ skill) so it stops living only in tribal memory.
|
|
|
34
39
|
sync upserts the same invoice from three concurrent webhooks (finalized/payment_succeeded/voided)
|
|
35
40
|
and relies on the index to reject the losing insert so the catch can redo it as an update. Drop
|
|
36
41
|
the index → silent duplicate invoice rows; remove the catch → concurrent events throw + retry.
|
|
42
|
+
- The `contact` collection's **unique multikey index on `leads`** (drawbridge-api
|
|
43
|
+
`schema/contact.js`) and sync's contact resolution (`lib/contact.js` + `queue/contact.js`) are
|
|
44
|
+
one contract: a lead belongs to at most one contact. One submission enqueues two resolve jobs
|
|
45
|
+
(lead insert + totals-update events) that race; the index rejects the losing create and the
|
|
46
|
+
worker retries it into the update path. The merge path must delete duplicate contacts BEFORE
|
|
47
|
+
`$set`-ing the survivor (the expanded `leads` array collides with rows about to be deleted).
|
|
48
|
+
Drop the index → duplicate contacts, doubled segment members, double-billed `contact.create` +
|
|
49
|
+
`segment.join`; reorder the merge → every merge aborts E11000 and duplicates never heal.
|
|
37
50
|
|
|
38
51
|
## `@drawbridge/*` package coordination
|
|
39
52
|
|
|
@@ -49,3 +62,16 @@ skill) so it stops living only in tribal memory.
|
|
|
49
62
|
- Import names against a package's **actual exports** — a missing export resolves to `undefined`
|
|
50
63
|
and throws at call time, not at import. Check `drawbridge-docs/reference/packages.md` (the
|
|
51
64
|
generated export surface) when unsure.
|
|
65
|
+
|
|
66
|
+
## Shopify sales channel
|
|
67
|
+
|
|
68
|
+
- `@drawbridge/shopify` storefront `getProduct`/`getProductVariantsPage` throw the **exact
|
|
69
|
+
message `'Shopify product not found'`** when the Storefront API can't see a product
|
|
70
|
+
(unpublished from the Drawbridge channel / deleted); transport and GraphQL failures throw
|
|
71
|
+
`'Shopify storefront error: …'`. drawbridge-sync's product worker (`queue/product.js`)
|
|
72
|
+
string-matches the not-found message to send publish-this-product ResourceFeedback instead of
|
|
73
|
+
retrying. Reword the message → unpublished products retry-loop forever and merchants get no
|
|
74
|
+
feedback; drawbridge-api's shopifyProduct route relies on the not-found throw staying a throw.
|
|
75
|
+
- The `write_resource_feedbacks` and `read_publications` scopes live in drawbridge-shopify-app's
|
|
76
|
+
`shopify.app.*.toml` but power other repos: sync's product ResourceFeedback sends and the
|
|
77
|
+
embedded app's publishing section. Removing either → silent Shopify userErrors, no crash.
|
|
@@ -13,6 +13,11 @@ This is the **only** allowed home for these working docs. Rules:
|
|
|
13
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
15
|
`../drawbridge-docs/superpowers/{plans,specs}/`, not the repo you're working in.
|
|
16
|
+
- ⚠️ The superpowers **brainstorming** and **writing-plans** skills hardcode a save path of
|
|
17
|
+
`docs/superpowers/{specs,plans}/YYYY-MM-DD-<slug>.md` in the current repo (and tell you to
|
|
18
|
+
commit it). **Override that** — redirect the path to `../drawbridge-docs/superpowers/…`. A
|
|
19
|
+
`PreToolUse` guard hook (`hooks/guard-superpowers-docs.js`) denies writes to `docs/superpowers/`
|
|
20
|
+
and `docs/plans/` outside drawbridge-docs, so following the skill's default path will be blocked.
|
|
16
21
|
- When plan mode or a superpowers skill produces a plan/spec, the finalized document goes into
|
|
17
22
|
drawbridge-docs — regardless of which repo the work targets. Note the target repo in the doc.
|
|
18
23
|
- `.superpowers/` (brainstorm scratch — server state, HTML mockups) is ephemeral. **Never commit
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse hook (Write/Edit/MultiEdit/NotebookEdit): superpowers plans & specs must live in
|
|
3
|
+
// drawbridge-docs, never in a working repo. The superpowers brainstorming / writing-plans skills
|
|
4
|
+
// hardcode a `docs/superpowers/{specs,plans}/…` save path (and tell the agent to commit it),
|
|
5
|
+
// which silently overrides the passive conventions/superpowers-docs.md rule at write time. A
|
|
6
|
+
// convention can't out-argue a skill's explicit step, so this enforces it: deny the write and
|
|
7
|
+
// tell the agent where it belongs. See conventions/superpowers-docs.md.
|
|
8
|
+
const fs = require('fs')
|
|
9
|
+
const path = require('path')
|
|
10
|
+
|
|
11
|
+
const readStdin = () => {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(fs.readFileSync(0, 'utf8') || '{}')
|
|
14
|
+
} catch (error) {
|
|
15
|
+
return {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const deny = (reason) => {
|
|
20
|
+
process.stdout.write(JSON.stringify({
|
|
21
|
+
hookSpecificOutput: {
|
|
22
|
+
hookEventName: 'PreToolUse',
|
|
23
|
+
permissionDecision: 'deny',
|
|
24
|
+
permissionDecisionReason: reason
|
|
25
|
+
}
|
|
26
|
+
}))
|
|
27
|
+
process.exit(0)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Path segments that are never allowed outside drawbridge-docs. These are the superpowers-skill
|
|
31
|
+
// defaults (`docs/superpowers/`) plus the sibling the convention also forbids (`docs/plans/`).
|
|
32
|
+
const FORBIDDEN = [ 'docs/superpowers', 'docs/plans' ]
|
|
33
|
+
|
|
34
|
+
const run = () => {
|
|
35
|
+
const input = readStdin()
|
|
36
|
+
|
|
37
|
+
const tool = input.tool_name || ''
|
|
38
|
+
if (!/^(Write|Edit|MultiEdit|NotebookEdit)$/.test(tool)) process.exit(0)
|
|
39
|
+
|
|
40
|
+
const toolInput = input.tool_input || {}
|
|
41
|
+
const filePath = toolInput.file_path || toolInput.notebook_path
|
|
42
|
+
if (!filePath) process.exit(0)
|
|
43
|
+
|
|
44
|
+
// Normalise to forward slashes so the match is OS-independent.
|
|
45
|
+
const resolved = path.resolve(filePath).split(path.sep).join('/')
|
|
46
|
+
|
|
47
|
+
// drawbridge-docs is the canonical home — anything there is fine.
|
|
48
|
+
if (resolved.split('/').includes('drawbridge-docs')) process.exit(0)
|
|
49
|
+
|
|
50
|
+
const hit = FORBIDDEN.find((seg) => resolved.includes('/' + seg + '/') || resolved.endsWith('/' + seg))
|
|
51
|
+
if (!hit) process.exit(0)
|
|
52
|
+
|
|
53
|
+
deny(
|
|
54
|
+
'Blocked: superpowers plans/specs must live in drawbridge-docs, not in this repo (`' + hit + '/`). ' +
|
|
55
|
+
'The brainstorming / writing-plans skills instruct you to save here and commit — override that path. ' +
|
|
56
|
+
'Write to `../drawbridge-docs/superpowers/{plans,specs}/YYYY-MM-DD-<slug>.md` instead, and note the ' +
|
|
57
|
+
'target repo inside the doc. See conventions/superpowers-docs.md.'
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
run()
|
|
63
|
+
} catch (error) {
|
|
64
|
+
// Never break a user's turn because of a hook bug — fail open.
|
|
65
|
+
process.exit(0)
|
|
66
|
+
}
|
package/package.json
CHANGED