trybeacon 0.1.14 → 0.1.15
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +5 -5
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/api/map/describe/route.js +1 -1
- package/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/docs.html +1 -1
- package/.next/server/app/docs.rsc +1 -1
- package/.next/server/app/docs.segments/_full.segment.rsc +1 -1
- package/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/docs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/.next/server/app/feedback/page_client-reference-manifest.js +1 -1
- package/.next/server/app/feedback.html +1 -1
- package/.next/server/app/feedback.rsc +1 -1
- package/.next/server/app/feedback.segments/_full.segment.rsc +1 -1
- package/.next/server/app/feedback.segments/_head.segment.rsc +1 -1
- package/.next/server/app/feedback.segments/_index.segment.rsc +1 -1
- package/.next/server/app/feedback.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/feedback.segments/feedback/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/feedback.segments/feedback.segment.rsc +1 -1
- package/.next/server/app/help/page_client-reference-manifest.js +1 -1
- package/.next/server/app/help.html +1 -1
- package/.next/server/app/help.rsc +1 -1
- package/.next/server/app/help.segments/_full.segment.rsc +1 -1
- package/.next/server/app/help.segments/_head.segment.rsc +1 -1
- package/.next/server/app/help.segments/_index.segment.rsc +1 -1
- package/.next/server/app/help.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/help.segments/help/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/help.segments/help.segment.rsc +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +1 -1
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/map/page.js +2 -2
- package/.next/server/app/map/page.js.nft.json +1 -1
- package/.next/server/app/map/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/plan/page.js +2 -2
- package/.next/server/app/plan/page.js.nft.json +1 -1
- package/.next/server/app/plan/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +5 -5
- package/.next/server/chunks/1130.js +1 -1
- package/.next/server/chunks/9159.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{3973-c52f930a38c6c787.js → 5354-31b73f8e3dfd92b7.js} +1 -1
- package/.next/static/chunks/app/map/{page-834f326619f2477b.js → page-3fe31101c63d9668.js} +1 -1
- package/.next/static/chunks/app/plan/{page-86a130ff444297a1.js → page-51bc3f04374541b9.js} +1 -1
- package/.next/trace +37 -37
- package/.next/trace-build +1 -1
- package/dist/bin/doctor.js +5 -3
- package/dist/bin/hook.js +5 -3
- package/dist/bin/mcp.js +30 -28
- package/dist/bin/plan.js +28 -22
- package/dist/bin/uninstall.js +5 -3
- package/dist/lib/assets.js +5 -3
- package/dist/lib/global-install.js +5 -3
- package/package.json +1 -1
- package/.next/server/chunks/8465.js +0 -1
- /package/.next/static/{dmwr94-U2a-24WsqmRue9 → ct96P0zTV3e7YqJ2NLI_X}/_buildManifest.js +0 -0
- /package/.next/static/{dmwr94-U2a-24WsqmRue9 → ct96P0zTV3e7YqJ2NLI_X}/_ssgManifest.js +0 -0
package/dist/bin/plan.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
var
|
|
3
|
+
var wQ=Object.defineProperty;var kQ=(Q)=>Q;function _Q(Q,X){this[Q]=kQ.bind(null,X)}var r=(Q,X)=>{for(var Z in X)wQ(Q,Z,{get:X[Z],enumerable:!0,configurable:!0,set:_Q.bind(X,Z)})};var o=(Q,X)=>()=>(Q&&(X=Q(Q=0)),X);var ZQ={};r(ZQ,{removeRepoAssets:()=>mQ,installRefreshSkill:()=>gQ,installPlanSkill:()=>bQ,installInitSkill:()=>LQ,ensureWorkflowDoc:()=>yQ,ensureMcp:()=>SQ,auditRepo:()=>hQ,REFRESH_SKILL:()=>QQ,PLAN_SKILL:()=>XQ,INIT_SKILL:()=>e});import{existsSync as k,mkdirSync as _,readFileSync as B,rmSync as w,writeFileSync as W}from"fs";import{join as q}from"path";function LQ(Q){let X=q(Q,".claude","skills","beacon-init");_(X,{recursive:!0});let Z=q(X,"SKILL.md");return W(Z,e),Z}function gQ(Q){let X=q(Q,".claude","skills","beacon-refresh");_(X,{recursive:!0});let Z=q(X,"SKILL.md");return W(Z,QQ),Z}function bQ(Q){let X=q(Q,".claude","skills","beacon-plan");_(X,{recursive:!0});let Z=q(X,"SKILL.md");return W(Z,XQ),Z}function yQ(Q){let X=q(Q,"AGENTS.md"),Z="";try{Z=B(X,"utf8")}catch{}let $=new RegExp(`${O}[\\s\\S]*?${L}`);Z=$.test(Z)?Z.replace($,i):`${Z.trim()}
|
|
4
4
|
|
|
5
5
|
${i}
|
|
6
|
-
`.trimStart(),
|
|
6
|
+
`.trimStart(),W(X,Z.endsWith(`
|
|
7
7
|
`)?Z:`${Z}
|
|
8
|
-
`);let Y=q(Q,"CLAUDE.md"),J="";try{J=B(Y,"utf8")}catch{}if(!/@AGENTS\.md/.test(J))
|
|
8
|
+
`);let Y=q(Q,"CLAUDE.md"),J="";try{J=B(Y,"utf8")}catch{}if(!/@AGENTS\.md/.test(J))W(Y,`${J?`${J.trim()}
|
|
9
9
|
|
|
10
10
|
`:""}@AGENTS.md
|
|
11
|
-
`)}function
|
|
12
|
-
`),{path:X,added:!0}}function
|
|
13
|
-
`)}}catch{}let Y=!1,J=q(Q,"AGENTS.md");try{let z=B(J,"utf8"),N=new RegExp(`\\n?${
|
|
11
|
+
`)}function SQ(Q){let X=q(Q,".mcp.json"),Z={};try{Z=JSON.parse(B(X,"utf8"))}catch{}if(Z.mcpServers=Z.mcpServers??{},Z.mcpServers.beacon)return{path:X,added:!1};return Z.mcpServers.beacon={command:"beacon",args:["mcp"]},W(X,JSON.stringify(Z,null,2)+`
|
|
12
|
+
`),{path:X,added:!0}}function hQ(Q){let X=q(Q,".mcp.json"),Z=!1;try{Z=!!JSON.parse(B(X,"utf8")).mcpServers?.beacon}catch{}let $=!1;try{$=B(q(Q,"AGENTS.md"),"utf8").includes(O)}catch{}let Y=!1;try{Y=/@AGENTS\.md/.test(B(q(Q,"CLAUDE.md"),"utf8"))}catch{}return{repo:Q,mcpRegistered:Z,agentsMdBlock:$,claudeMdImport:Y,skills:{"beacon-init":k(q(Q,".claude","skills","beacon-init","SKILL.md")),"beacon-refresh":k(q(Q,".claude","skills","beacon-refresh","SKILL.md"))}}}function mQ(Q){let X=[];for(let z of["beacon-init","beacon-refresh","beacon-db-design"]){let N=q(Q,".claude","skills",z);if(k(N))w(N,{recursive:!0,force:!0}),X.push(z)}let Z=!1,$=q(Q,".mcp.json");try{let z=JSON.parse(B($,"utf8"));if(z.mcpServers?.beacon){if(delete z.mcpServers.beacon,Z=!0,z.mcpServers&&Object.keys(z.mcpServers).length===0)delete z.mcpServers;if(Object.keys(z).length===0)w($,{force:!0});else W($,JSON.stringify(z,null,2)+`
|
|
13
|
+
`)}}catch{}let Y=!1,J=q(Q,"AGENTS.md");try{let z=B(J,"utf8"),N=new RegExp(`\\n?${O}[\\s\\S]*?${L}\\n?`);if(N.test(z)){let P=z.replace(N,`
|
|
14
14
|
`).replace(/\n{3,}/g,`
|
|
15
15
|
|
|
16
|
-
`).trimStart();
|
|
17
|
-
`)?
|
|
18
|
-
`),Y=!0}}catch{}let H=!1,V=q(Q,"CLAUDE.md");try{let z=B(V,"utf8"),N=z.replace(/^[ \t]*@AGENTS\.md[ \t]*\n?/m,"");if(N!==z){if(!N.trim())
|
|
16
|
+
`).trimStart();W(J,P.endsWith(`
|
|
17
|
+
`)?P:`${P}
|
|
18
|
+
`),Y=!0}}catch{}let H=!1,V=q(Q,"CLAUDE.md");try{let z=B(V,"utf8"),N=z.replace(/^[ \t]*@AGENTS\.md[ \t]*\n?/m,"");if(N!==z){if(!N.trim())w(V,{force:!0});else W(V,N);H=!0}}catch{}return{skillsRemoved:X,mcpUnregistered:Z,agentsBlockRemoved:Y,claudeImportRemoved:H}}var e=`---
|
|
19
19
|
name: beacon-init
|
|
20
20
|
description: Read this repository and map its architecture, schema, and roadmap into Beacon (the local visual planning panel). Use when the user runs /beacon-init, asks to "set up Beacon for this repo", or asks to initialize/bootstrap Beacon's map of this codebase.
|
|
21
21
|
---
|
|
@@ -140,7 +140,7 @@ If it returns feedback, revise and call it again.
|
|
|
140
140
|
|
|
141
141
|
If \`beacon_present_plan\` isn't available, the panel isn't wired here \u2014 fall back to presenting via
|
|
142
142
|
ExitPlanMode with the same \`\`\`beacon block, or tell the user to run \`beacon\` in this repo once.
|
|
143
|
-
`,
|
|
143
|
+
`,O="<!-- beacon:workflow:start -->",L="<!-- beacon:workflow:end -->",i;var $Q=o(()=>{i=`${O}
|
|
144
144
|
## Beacon \u2014 feature workflow
|
|
145
145
|
|
|
146
146
|
This project uses Beacon (a local planning/visualization panel; run \`beacon\` to open it). When you start work on a FEATURE \u2014 whether referenced via an \`@beacon:feature://\u2026\` mention, an \`@beacon:note://\u2026\` note the user wrote in the Notes panel (treat its checkbox \`- [ ]\` todos as subtasks and order features by their dependencies), or just described in chat \u2014 follow these steps in order:
|
|
@@ -181,16 +181,18 @@ Beacon extracts it deterministically and **strips the block from the prose** (it
|
|
|
181
181
|
|
|
182
182
|
**The board is built ONLY from the block \u2014 prose is NOT parsed.** If your plan describes ANY database models/tables/columns in the prose (e.g. "Model \`legal_precedent.py\` \u2014 natural key (court, \u2026)"), you MUST also put them in the block's \`tables\` array (with \`columns\`), or the /db tab will be empty for that plan. Same for endpoints (\`endpoints\` with \`uses:[{table,access}]\`) and features (\`features\`). A plan that lists five tables in prose but ships a block with only \`features\` renders an empty database board \u2014 exactly the "I described models but the DB tab is empty" failure. Mirror every DB entity you mention into the block.
|
|
183
183
|
|
|
184
|
-
### 3. At the end, register the work
|
|
184
|
+
### 3. At the end, register the work \u2014 in ONE call
|
|
185
185
|
|
|
186
|
-
Call \`beacon_describe_feature\`
|
|
186
|
+
Call \`beacon_describe_feature\` **ONCE** with a \`features\` array \u2014 one entry per feature the plan created \u2014 each with the files you touched and a short markdown description. This flips each one to **Done** and keeps \`beacon_context_for_feature\` accurate for the next session.
|
|
187
187
|
|
|
188
|
-
|
|
188
|
+
Key each entry by its node \`id\`: the ids are handed back to you when the plan is approved (in the approval message / additionalContext), so you don't fuzzy-match titles or pay a disambiguation round-trip. If you don't have an id, \`title\` still works.
|
|
189
|
+
|
|
190
|
+
Register them all in that single batched call. If a plan added five features, that's ONE \`beacon_describe_feature\` call with five entries \u2014 NOT five calls, and NOT just an umbrella ("Harden auth"), which leaves the individual features stuck on **Pending**.
|
|
189
191
|
|
|
190
192
|
If the feature added or materially changed a REAL architectural component (a subsystem \u2014 NOT a file), also pass \`architecture: [{ title, domain, role, \u2026 }]\` so the Architecture map stays accurate. It upserts curated components by title; never list files as components.
|
|
191
193
|
|
|
192
194
|
Pull raw planning data anytime with \`beacon_entities\` (features / architecture / tables / endpoints).
|
|
193
|
-
${
|
|
195
|
+
${L}`});var HQ={};r(HQ,{provisionDb:()=>NX});import{createClient as QX}from"@libsql/client";import{drizzle as XX}from"drizzle-orm/libsql";import{migrate as ZX}from"drizzle-orm/libsql/migrator";import{readMigrationFiles as $X}from"drizzle-orm/migrator";import{dirname as c,join as YX}from"path";import{fileURLToPath as JX}from"url";async function E(Q,X){return(await Q.execute({sql:"SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?",args:[X]})).rows.length>0}async function GX(Q,X){let Z=await Q.execute(`PRAGMA table_info("${X}")`);return new Set(Z.rows.map(($)=>String($.name)))}async function GQ(Q){if(!await E(Q,"__drizzle_migrations"))return 0;let X=await Q.execute('SELECT count(*) AS n FROM "__drizzle_migrations"');return Number(X.rows[0]?.n??0)}async function HX(Q){for(let[X,Z]of Object.entries(qX)){if(!await E(Q,X))continue;let $=await GX(Q,X);for(let Y of Z){if(!$.has(Y))continue;await Q.execute(`UPDATE "${X}" SET "${Y}" = CAST((julianday("${Y}") - 2440587.5) * 86400000 AS INTEGER) WHERE typeof("${Y}") = 'text'`)}}}async function NX(Q,X=zX){let Z=QX({url:Q});try{await Z.execute("PRAGMA journal_mode = WAL;"),await Z.execute("PRAGMA foreign_keys = ON;");let $=await E(Z,"__drizzle_migrations"),Y=await E(Z,"Node"),J=!1;if(Y&&!$){let V=$X({migrationsFolder:X})[0];if(V)await Z.execute('CREATE TABLE IF NOT EXISTS "__drizzle_migrations" (id SERIAL PRIMARY KEY, hash text NOT NULL, created_at numeric)'),await Z.execute({sql:'INSERT INTO "__drizzle_migrations" ("hash", "created_at") VALUES (?, ?)',args:[V.hash,V.folderMillis]}),J=!0}let H=await GQ(Z);if(await ZX(XX(Z),{migrationsFolder:X}),await GQ(Z)>H)J=!0;return await HX(Z),{migrated:J}}finally{Z.close()}}var VX,zX,qX;var NQ=o(async()=>{VX=c(c(c(JX(import.meta.url)))),zX=process.env.BEACON_MIGRATIONS_DIR||YX(VX,"drizzle"),qX={Node:["createdAt","updatedAt"],Note:["createdAt","updatedAt"],DbTable:["createdAt","updatedAt"],Endpoint:["createdAt","updatedAt"],DraftTable:["createdAt"],SyncState:["codeGraphSyncedAt","updatedAt"],AppSetting:["updatedAt"],ProjectMeta:["updatedAt"],CodeFile:["updatedAt"]}});import{existsSync as b,mkdirSync as y,readFileSync as I,rmSync as pQ,writeFileSync as D}from"fs";import{homedir as cQ}from"os";import{join as x}from"path";function lQ(){return process.env.HOME||process.env.USERPROFILE||cQ()}var T=()=>x(lQ(),".claude"),S=()=>x(T(),"skills"),YQ=()=>x(T(),"settings.json"),C=()=>x(T(),"CLAUDE.md"),F="<!-- beacon:global:start -->",g="<!-- beacon:global:end -->",h=["beacon-init","beacon-refresh","beacon-plan"],m=[{event:"PostToolUse",matcher:"Edit|Write|MultiEdit",command:"beacon hook",description:"Report file edits to Beacon's active feature so the map stays fresh."},{event:"PermissionRequest",matcher:"ExitPlanMode",command:"beacon plan",description:"Pipe the agent's plan markdown into Beacon's /plan canvas for review."},{event:"UserPromptSubmit",matcher:"*",command:"beacon prompt",description:"On feature-y prompts in a Beacon-wired repo, remind the agent to run the context\u2192propose\u2192describe loop (no-op otherwise)."},{event:"Stop",matcher:"*",command:"beacon stop-hook",description:"When the agent ends a turn asking for plan approval in prose (instead of presenting it), nudge it to present the plan on Beacon's /plan canvas. Bounded by stop_hook_active \u2014 at most one nudge."}],dQ=`## Beacon (visual planning panel)
|
|
194
196
|
|
|
195
197
|
This machine has Beacon installed \u2014 a local visual planning surface for the terminal-side
|
|
196
198
|
agent. Beacon proposes feature plans (roadmap features + DB schema + endpoints) via MCP,
|
|
@@ -211,18 +213,22 @@ the user reviews on a canvas at /plan, and feedback flows back as the next round
|
|
|
211
213
|
Feedback bundles inline annotations on the markdown PLUS any edits the user made directly
|
|
212
214
|
on the /map and /db boards (added features, attached subtasks, edited columns, new
|
|
213
215
|
endpoints). Treat board edits as the user's revision intent \u2014 re-propose matching them
|
|
214
|
-
verbatim. Do NOT implement until the tool returns approval.`;function
|
|
215
|
-
`)}function
|
|
216
|
+
verbatim. Do NOT implement until the tool returns approval.`;function nQ(Q,X){let Z=x(S(),Q);y(Z,{recursive:!0});let $=x(Z,"SKILL.md");return D($,X),$}function JQ(Q){return b(x(S(),Q,"SKILL.md"))}function aQ(Q){let X=x(S(),Q);if(!b(X))return!1;return pQ(X,{recursive:!0,force:!0}),!0}function p(){try{return JSON.parse(I(YQ(),"utf8"))}catch{return{}}}function VQ(Q){y(T(),{recursive:!0}),D(YQ(),JSON.stringify(Q,null,2)+`
|
|
217
|
+
`)}function tQ(Q){let X=p();X.hooks=X.hooks??{},X.hooks[Q.event]=X.hooks[Q.event]??[];let Z=X.hooks[Q.event];if(Z.some((Y)=>Y.matcher===Q.matcher&&Y.hooks?.some((J)=>J.command===Q.command)))return!1;return Z.push({matcher:Q.matcher,hooks:[{type:"command",command:Q.command}]}),VQ(X),!0}function sQ(Q){return(p().hooks?.[Q.event]??[]).some(($)=>$.hooks?.some((Y)=>Y.command===Q.command))}function rQ(Q){let X=p(),Z=X.hooks?.[Q.event];if(!Z)return!1;let $=!1,Y=Z.map((J)=>{let H=J.hooks?.length??0,V=(J.hooks??[]).filter((z)=>z.command!==Q.command);if(V.length!==H)$=!0;return{...J,hooks:V}}).filter((J)=>(J.hooks??[]).length>0);if(!$)return!1;if(Y.length)X.hooks[Q.event]=Y;else delete X.hooks[Q.event];if(X.hooks&&Object.keys(X.hooks).length===0)delete X.hooks;return VQ(X),!0}function oQ(Q){let X=`${F}
|
|
216
218
|
${Q.trim()}
|
|
217
|
-
${
|
|
219
|
+
${g}`,Z="";try{Z=I(C(),"utf8")}catch{}let $=new RegExp(`${F}[\\s\\S]*?${g}`);Z=$.test(Z)?Z.replace($,X):Z.trim()?`${Z.trim()}
|
|
218
220
|
|
|
219
221
|
${X}
|
|
220
222
|
`:`${X}
|
|
221
|
-
`,y(
|
|
223
|
+
`,y(T(),{recursive:!0}),D(C(),Z.endsWith(`
|
|
222
224
|
`)?Z:`${Z}
|
|
223
|
-
`)}function zQ(){try{return
|
|
225
|
+
`)}function zQ(){try{return I(C(),"utf8").includes(F)}catch{return!1}}function iQ(){let Q="";try{Q=I(C(),"utf8")}catch{return!1}let X=new RegExp(`${F}[\\s\\S]*?${g}\\n?`);if(!X.test(Q))return!1;let Z=Q.replace(X,"").replace(/\n{3,}/g,`
|
|
226
|
+
|
|
227
|
+
`).trimStart();return D(C(),Z),!0}function eX(){let Q={};for(let Z of h)Q[Z]=JQ(Z);let X={};for(let Z of m)X[Z.event]=sQ({event:Z.event,command:Z.command});return{homeExists:b(T()),skills:Q,hooks:X,claudeMdBlock:zQ()}}async function eQ(){let{INIT_SKILL:Q,REFRESH_SKILL:X,PLAN_SKILL:Z}=await Promise.resolve().then(() => ($Q(),ZQ)),$={"beacon-init":Q,"beacon-refresh":X,"beacon-plan":Z},Y=[];for(let V of h){if(!JQ(V))Y.push(V);nQ(V,$[V])}let J=0;for(let V of m)if(tQ({event:V.event,matcher:V.matcher,command:V.command}))J++;let H=zQ();return oQ(dQ),{skillsAdded:Y,hooksAdded:J,claudeMdBlockTouched:!H}}async function qQ(){try{return{ok:!0,...await eQ()}}catch(Q){return{ok:!1,error:Q instanceof Error?Q.message:String(Q),skillsAdded:[],hooksAdded:0,claudeMdBlockTouched:!1}}}function QZ(){let Q=[];for(let $ of h)if(aQ($))Q.push($);let X=0;for(let $ of m)if(rQ($))X++;let Z=iQ();return{skillsRemoved:Q,hooksRemoved:X,claudeMdBlockRemoved:Z}}import{AsyncLocalStorage as BX}from"async_hooks";import{spawnSync as WX}from"child_process";import{createHash as xX}from"crypto";import{existsSync as WQ,mkdirSync as f,readFileSync as xQ,writeFileSync as UQ}from"fs";import{homedir as UX}from"os";import{basename as KX,join as M}from"path";function K(){return process.env.BEACON_HOME||M(UX(),".beacon")}function l(){return M(K(),"workspaces.json")}function KQ(Q){return xX("sha256").update(Q).digest("hex").slice(0,12)}function xZ(Q=process.cwd()){let X=WX("git",["rev-parse","--show-toplevel"],{cwd:Q,stdio:["ignore","pipe","ignore"]});if(X.status===0){let Z=X.stdout?.toString().trim();if(Z)return Z}return Q}function d(Q){return M(K(),Q)}function jX(Q){return`file:${M(d(Q),"db.sqlite")}`}function v(){try{let Q=JSON.parse(xQ(l(),"utf8"));return Array.isArray(Q)?Q:[]}catch{return[]}}function n(Q){f(K(),{recursive:!0}),UQ(l(),JSON.stringify(Q,null,2))}function TX(){return v().sort((Q,X)=>X.lastOpenedAt.localeCompare(Q.lastOpenedAt))}function U(Q){return v().find((X)=>X.id===Q)??null}function MX(Q,X,Z=new Date().toISOString()){let $=KQ(Q),Y=v(),J=Y.find((z)=>z.id===$),H={id:$,path:Q,name:X||J?.name||KX(Q),lastOpenedAt:Z},V=[H,...Y.filter((z)=>z.id!==$)];return f(d($),{recursive:!0}),n(V),H}function UZ(Q,X=new Date().toISOString()){let Z=v(),$=Z.find((Y)=>Y.id===Q);if(!$)return;$.lastOpenedAt=X,n(Z)}function KZ(Q){if(n(v().filter((X)=>X.id!==Q)),a()===Q)PX(TX()[0]?.id??null)}function jQ(){return M(K(),"active")}function a(){try{return xQ(jQ(),"utf8").trim()||null}catch{return null}}function PX(Q){f(K(),{recursive:!0}),UQ(jQ(),Q??"")}function jZ(){let Q=a();return Q?U(Q):null}function TZ(){let Q=vX()??a();return Q?U(Q):null}function MZ(Q){return U(Q)?.path??null}function PZ(){return WQ(l())}var TQ="beacon_ws";function MQ(Q,X){let Z=Q.headers.get("cookie");if(!Z)return null;for(let $ of Z.split(";")){let Y=$.indexOf("=");if(Y===-1)continue;if($.slice(0,Y).trim()===X)return decodeURIComponent($.slice(Y+1).trim())}return null}function CZ(Q){let X=Q.headers.get("x-beacon-workspace");if(X&&U(X))return X;let Z=MQ(Q,TQ);if(Z&&U(Z))return Z;return null}var CX="x-beacon-workspace-path";async function vZ(Q){let X=Q.headers.get("x-beacon-workspace");if(X&&U(X))return X;let Z=Q.headers.get(CX);if(Z&&(!X||KQ(Z)===X)){let Y=MX(Z);return await OX(Y.id),Y.id}let $=MQ(Q,TQ);if($&&U($))return $;return null}var PQ=new BX;function OZ(Q,X){return Q?PQ.run(Q,X):X()}function vX(){return PQ.getStore()??null}var BQ=new Set;async function OX(Q){let X=d(Q),Z=M(X,"db.sqlite"),$=WQ(Z);if($&&BQ.has(Z))return{ok:!0,created:!1,migrated:!1};try{f(X,{recursive:!0});let{provisionDb:Y}=await NQ().then(() => HQ),J=await Y(jX(Q));return BQ.add(Z),{ok:!0,created:!$,migrated:$&&J.migrated}}catch(Y){return{ok:!1,created:!1,migrated:!1,error:Y instanceof Error?Y.message:String(Y)}}}import{execSync as RQ,spawn as fX}from"child_process";import{mkdirSync as RX,openSync as uX,readFileSync as AX,writeFileSync as wX}from"fs";import{homedir as kX,platform as EQ}from"os";import{dirname as _X,join as u}from"path";import{fileURLToPath as LX}from"url";import{createHash as gX}from"crypto";var CQ=1500,vQ=345600000,IZ=1800000,DZ=3000;import{mkdirSync as uZ,readFileSync as IX,writeFileSync as AZ}from"fs";import{join as DX}from"path";var FX=["default","acceptEdits","bypassPermissions","auto"];function OQ(Q){return typeof Q==="string"&&FX.includes(Q)}function FQ(Q,X){let Z={behavior:"allow"};if(Q)Z.updatedPermissions=[{type:"setMode",mode:Q,destination:"session"}];let $={hookEventName:"PermissionRequest",decision:Z};if(X?.trim())$.additionalContext=X;return{hookSpecificOutput:$}}function EX(){return DX(K(),"preferences.json")}function IQ(){try{let Q=JSON.parse(IX(EX(),"utf8"));return{planApprovalMode:OQ(Q.planApprovalMode)?Q.planApprovalMode:void 0,planApprovalModeConfigured:Q.planApprovalModeConfigured===!0}}catch{return{}}}function DQ(Q){if(!Q?.length)return"";let X=Q.map(($)=>$.id?` \u2022 ${$.title} \u2014 id: ${$.id}`:` \u2022 ${$.title}`),Z=Q.some(($)=>$.id);return`
|
|
224
228
|
|
|
225
|
-
|
|
226
|
-
|
|
229
|
+
This plan created these roadmap feature(s):
|
|
230
|
+
`+X.join(`
|
|
231
|
+
`)+"\n\nWhen the work ships, register them in ONE call: `beacon_describe_feature` with a "+"`features` array \u2014 one entry per feature"+(Z?", each keyed by the `id` above (no title-matching needed)":"")+", each with the files you touched + a short markdown summary. Do them all in that single "+"batched call so every feature flips to Done \u2014 do NOT register only the umbrella, and do "+"NOT make one call per feature."}var bX=u(_X(LX(import.meta.url)),".."),s=process.env.BEACON_HOME||u(kX(),".beacon"),uQ=u(s,"server.json"),fQ=process.env.PORT||"4319";function yX(Q){try{return process.kill(Q,0),!0}catch{return!1}}function SX(Q){try{return JSON.parse(AX(Q,"utf8"))}catch{return null}}async function AQ(Q){try{return(await fetch(Q)).ok}catch{return!1}}async function hX(Q,X=60){for(let Z=0;Z<X;Z++){if(await AQ(Q))return!0;await new Promise(($)=>setTimeout($,500))}return!1}function mX(){RX(s,{recursive:!0});let Q=uX(u(s,"server.log"),"a"),X={...process.env,PORT:fQ,BEACON_NO_OPEN:"1"};delete X.BEACON_REPO,delete X.BEACON_DATA_DIR,delete X.DATABASE_URL;let Z=fX("bun",["run","dev"],{cwd:bX,env:X,detached:!0,stdio:["ignore",Q,Q]});Z.unref();let $={pid:Z.pid??0,port:fQ};return wX(uQ,JSON.stringify($)),$}async function pX(){let Q=SX(uQ);if(Q&&yX(Q.pid)&&await AQ(`http://localhost:${Q.port}/api/workspace`))return Q.port;let{port:X}=mX();return await hX(`http://localhost:${X}/api/workspace`),X}function cX(Q){if(process.env.BEACON_NO_OPEN)return;let X=EQ()==="darwin"?"open":EQ()==="win32"?"start":"xdg-open";try{RQ(`${X} "${Q}"`,{stdio:"ignore"})}catch{}}function lX(){try{return RQ("git rev-parse --show-toplevel",{cwd:process.cwd(),stdio:["ignore","pipe","ignore"]}).toString().trim()}catch{return""}}function dX(Q){return gX("sha256").update(Q).digest("hex").slice(0,12)}async function nX(){if(process.stdin.isTTY)return null;let Q=[];for await(let Z of process.stdin)Q.push(Z);let X=Buffer.concat(Q).toString("utf8").trim();if(!X)return null;try{return JSON.parse(X)}catch{return null}}function t(Q,X){return FQ(Q,X)}function R(Q){return{hookSpecificOutput:{hookEventName:"PermissionRequest",decision:{behavior:"deny",message:Q}}}}function j(Q){process.stdout.write(JSON.stringify(Q)),process.exit(0)}(async()=>{await qQ();let X=(await nX())?.tool_input?.plan;if(!X||!X.trim())j(t());let $=`http://localhost:${await pX()}`,Y=lX()||process.cwd(),J=dX(Y);await fetch(`${$}/api/workspace/activate?id=${J}`).catch(()=>{});let H=X.split(`
|
|
232
|
+
`,1)[0]?.replace(/^#+\s*/,"").slice(0,160)||"Plan review",V=await fetch(`${$}/api/plan`,{method:"POST",headers:{"content-type":"application/json","x-beacon-workspace":J},body:JSON.stringify({description:H,markdown:X})});if(V.status===422){let G=await V.json().catch(()=>({}));j(R(G.error??"Beacon rejected the plan: every roadmap feature needs a category and a priority. Add them and re-present."))}if(!V.ok)j(t());if(!await fetch(`${$}/api/plan/presence`,{headers:{"x-beacon-workspace":J}}).then((G)=>G.json()).then((G)=>!!G?.live).catch(()=>!1)){let G=encodeURIComponent(`/plan?ws=${J}`);cX(`${$}/api/workspace/activate?id=${J}&redirect=${G}`)}let N=Date.now()+vQ,P=(G)=>new Promise((A)=>setTimeout(A,G));while(Date.now()<N){await P(CQ);let G=await fetch(`${$}/api/plan/verdict`,{headers:{"x-beacon-workspace":J}}).then((A)=>A.json()).catch(()=>null);if(!G)continue;if(G.kind==="approved")j(t(IQ().planApprovalMode,DQ(G.features)));if(G.kind==="discarded")j(R("The user discarded the plan in Beacon. Ask what they want to adjust before re-presenting."));if(G.kind==="feedback")j(R(`The user left feedback on the plan in Beacon (inline comments and/or edits on the /map and /db boards). Revise the plan based on the feedback below, then re-present (call ExitPlanMode again):
|
|
227
233
|
|
|
228
|
-
`+G.feedback))}
|
|
234
|
+
`+G.feedback))}j(R("Beacon plan review timed out without a verdict. Re-present the plan and ask the user to review it."))})();
|
package/dist/bin/uninstall.js
CHANGED
|
@@ -181,11 +181,13 @@ Beacon extracts it deterministically and **strips the block from the prose** (it
|
|
|
181
181
|
|
|
182
182
|
**The board is built ONLY from the block \u2014 prose is NOT parsed.** If your plan describes ANY database models/tables/columns in the prose (e.g. "Model \`legal_precedent.py\` \u2014 natural key (court, \u2026)"), you MUST also put them in the block's \`tables\` array (with \`columns\`), or the /db tab will be empty for that plan. Same for endpoints (\`endpoints\` with \`uses:[{table,access}]\`) and features (\`features\`). A plan that lists five tables in prose but ships a block with only \`features\` renders an empty database board \u2014 exactly the "I described models but the DB tab is empty" failure. Mirror every DB entity you mention into the block.
|
|
183
183
|
|
|
184
|
-
### 3. At the end, register the work
|
|
184
|
+
### 3. At the end, register the work \u2014 in ONE call
|
|
185
185
|
|
|
186
|
-
Call \`beacon_describe_feature\`
|
|
186
|
+
Call \`beacon_describe_feature\` **ONCE** with a \`features\` array \u2014 one entry per feature the plan created \u2014 each with the files you touched and a short markdown description. This flips each one to **Done** and keeps \`beacon_context_for_feature\` accurate for the next session.
|
|
187
187
|
|
|
188
|
-
|
|
188
|
+
Key each entry by its node \`id\`: the ids are handed back to you when the plan is approved (in the approval message / additionalContext), so you don't fuzzy-match titles or pay a disambiguation round-trip. If you don't have an id, \`title\` still works.
|
|
189
|
+
|
|
190
|
+
Register them all in that single batched call. If a plan added five features, that's ONE \`beacon_describe_feature\` call with five entries \u2014 NOT five calls, and NOT just an umbrella ("Harden auth"), which leaves the individual features stuck on **Pending**.
|
|
189
191
|
|
|
190
192
|
If the feature added or materially changed a REAL architectural component (a subsystem \u2014 NOT a file), also pass \`architecture: [{ title, domain, role, \u2026 }]\` so the Architecture map stays accurate. It upserts curated components by title; never list files as components.
|
|
191
193
|
|
package/dist/lib/assets.js
CHANGED
|
@@ -180,11 +180,13 @@ Beacon extracts it deterministically and **strips the block from the prose** (it
|
|
|
180
180
|
|
|
181
181
|
**The board is built ONLY from the block \u2014 prose is NOT parsed.** If your plan describes ANY database models/tables/columns in the prose (e.g. "Model \`legal_precedent.py\` \u2014 natural key (court, \u2026)"), you MUST also put them in the block's \`tables\` array (with \`columns\`), or the /db tab will be empty for that plan. Same for endpoints (\`endpoints\` with \`uses:[{table,access}]\`) and features (\`features\`). A plan that lists five tables in prose but ships a block with only \`features\` renders an empty database board \u2014 exactly the "I described models but the DB tab is empty" failure. Mirror every DB entity you mention into the block.
|
|
182
182
|
|
|
183
|
-
### 3. At the end, register the work
|
|
183
|
+
### 3. At the end, register the work \u2014 in ONE call
|
|
184
184
|
|
|
185
|
-
Call \`beacon_describe_feature\`
|
|
185
|
+
Call \`beacon_describe_feature\` **ONCE** with a \`features\` array \u2014 one entry per feature the plan created \u2014 each with the files you touched and a short markdown description. This flips each one to **Done** and keeps \`beacon_context_for_feature\` accurate for the next session.
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
Key each entry by its node \`id\`: the ids are handed back to you when the plan is approved (in the approval message / additionalContext), so you don't fuzzy-match titles or pay a disambiguation round-trip. If you don't have an id, \`title\` still works.
|
|
188
|
+
|
|
189
|
+
Register them all in that single batched call. If a plan added five features, that's ONE \`beacon_describe_feature\` call with five entries \u2014 NOT five calls, and NOT just an umbrella ("Harden auth"), which leaves the individual features stuck on **Pending**.
|
|
188
190
|
|
|
189
191
|
If the feature added or materially changed a REAL architectural component (a subsystem \u2014 NOT a file), also pass \`architecture: [{ title, domain, role, \u2026 }]\` so the Architecture map stays accurate. It upserts curated components by title; never list files as components.
|
|
190
192
|
|
|
@@ -180,11 +180,13 @@ Beacon extracts it deterministically and **strips the block from the prose** (it
|
|
|
180
180
|
|
|
181
181
|
**The board is built ONLY from the block \u2014 prose is NOT parsed.** If your plan describes ANY database models/tables/columns in the prose (e.g. "Model \`legal_precedent.py\` \u2014 natural key (court, \u2026)"), you MUST also put them in the block's \`tables\` array (with \`columns\`), or the /db tab will be empty for that plan. Same for endpoints (\`endpoints\` with \`uses:[{table,access}]\`) and features (\`features\`). A plan that lists five tables in prose but ships a block with only \`features\` renders an empty database board \u2014 exactly the "I described models but the DB tab is empty" failure. Mirror every DB entity you mention into the block.
|
|
182
182
|
|
|
183
|
-
### 3. At the end, register the work
|
|
183
|
+
### 3. At the end, register the work \u2014 in ONE call
|
|
184
184
|
|
|
185
|
-
Call \`beacon_describe_feature\`
|
|
185
|
+
Call \`beacon_describe_feature\` **ONCE** with a \`features\` array \u2014 one entry per feature the plan created \u2014 each with the files you touched and a short markdown description. This flips each one to **Done** and keeps \`beacon_context_for_feature\` accurate for the next session.
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
Key each entry by its node \`id\`: the ids are handed back to you when the plan is approved (in the approval message / additionalContext), so you don't fuzzy-match titles or pay a disambiguation round-trip. If you don't have an id, \`title\` still works.
|
|
188
|
+
|
|
189
|
+
Register them all in that single batched call. If a plan added five features, that's ONE \`beacon_describe_feature\` call with five entries \u2014 NOT five calls, and NOT just an umbrella ("Harden auth"), which leaves the individual features stuck on **Pending**.
|
|
188
190
|
|
|
189
191
|
If the feature added or materially changed a REAL architectural component (a subsystem \u2014 NOT a file), also pass \`architecture: [{ title, domain, role, \u2026 }]\` so the Architecture map stays accurate. It upserts curated components by title; never list files as components.
|
|
190
192
|
|