buildkeel 0.1.0 → 0.2.0
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/README.md +1 -3
- package/antigravity/dist/hooks/main.js +15 -15
- package/antigravity/dist/server/main.js +383 -262
- package/antigravity/package.json +1 -1
- package/antigravity/plugin.json +1 -1
- package/antigravity/skills/bk-approve/SKILL.md +3 -1
- package/antigravity/skills/bk-build/SKILL.md +12 -1
- package/antigravity/skills/bk-capture/SKILL.md +13 -2
- package/antigravity/skills/bk-done/SKILL.md +10 -0
- package/antigravity/skills/bk-impact/SKILL.md +7 -0
- package/antigravity/skills/bk-init/SKILL.md +13 -5
- package/antigravity/skills/bk-new/SKILL.md +16 -3
- package/antigravity/skills/bk-onboard/SKILL.md +21 -2
- package/antigravity/skills/bk-start/SKILL.md +6 -0
- package/antigravity/skills/bk-stats/SKILL.md +1 -1
- package/antigravity/skills/bk-status/SKILL.md +2 -0
- package/antigravity/templates/design.md +1 -0
- package/antigravity/templates/spec-driven/capability.md +28 -0
- package/antigravity/templates/spec-driven/proposal.md +39 -0
- package/antigravity/templates/spec-driven/spec-change.md +43 -0
- package/antigravity/templates/spec-driven/tasks.md +17 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# BuildKeel
|
|
2
2
|
|
|
3
|
-
BuildKeel keeps your
|
|
4
|
-
|
|
5
|
-
> **Early access.** Commands and behavior may change between 0.x releases. Your spec trees keep working across them: a change of format comes with `/bk:migrate`.
|
|
3
|
+
BuildKeel keeps your specs, tests and code connected, and gives your coding agent that picture before it writes anything. It works in Claude Code and Antigravity, in one of two modes: **Spec-driven**, where capability specs say how your system behaves and each piece of work is a change to them, with no approvals; or **Full SDLC**, your team's chain from business idea down, with approvals and gates.
|
|
6
4
|
|
|
7
5
|
## Install in Antigravity
|
|
8
6
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { createRequire as __bkCreateRequire } from 'node:module'; const require = __bkCreateRequire(import.meta.url);
|
|
2
|
-
import{appendFileSync as
|
|
3
|
-
`)}var
|
|
4
|
-
`)}function
|
|
5
|
-
`).filter(Boolean).flatMap(
|
|
6
|
-
`),
|
|
7
|
-
`))if(
|
|
2
|
+
import{appendFileSync as le,readFileSync as de}from"node:fs";var mt=["claude-code","antigravity"],A=t=>mt.includes(t);function y(t,n){return t==="antigravity"?`/bk-${n}`:`/bk:${n}`}function B(t,n){return t.replace(/\{\{bk:([a-z][a-z-]*)\}\}/g,(e,o)=>y(n,o))}import{isAbsolute as H,relative as vt,resolve as D}from"node:path";import{existsSync as K}from"node:fs";import{dirname as kt,join as N,resolve as yt}from"node:path";var wt=".buildkeel.yaml";function v(t){let n=yt(t);for(;;){if(K(N(n,wt)))return n;if(K(N(n,".git")))return null;let e=kt(n);if(e===n)return null;n=e}}var F=["write_to_file","replace_file_content","multi_replace_file_content"],we=F.join("|");var xt={decision:"allow"},j={decision:"ask"},C=(t,n)=>{let e=vt(t,n);return e===""||!e.startsWith("..")&&!H(e)};function $(t,n){let e=(t.workspacePaths??[]).filter(i=>typeof i=="string"&&i!==""),o=n?[...e.filter(i=>C(i,n)),...e.filter(i=>!C(i,n))]:e;return o.find(i=>v(i)!==null)??o[0]??process.cwd()}var L={parse(t,n){let e=t??{};switch(n[0]){case"PreInvocation":return e.conversationId?{kind:"prompt",cwd:$(e),conversation:e.conversationId,newMessage:e.invocationNum===0,transcript:e.transcriptPath??null}:{kind:"ignored"};case"PreToolUse":case"PostToolUse":{let o=e.toolCall?.name??"",i=e.toolCall?.args?.TargetFile,r=F.includes(o)&&typeof i=="string"&&i!==""?i:null;if(n[0]==="PostToolUse"){if(!r||e.error)return{kind:"ignored"};let c=$(e,H(r)?r:void 0);return{kind:"after-edit",cwd:c,tool:o,files:[D(c,r)],...e.conversationId?{deferTo:e.conversationId}:{}}}let a=$(e,r&&H(r)?r:void 0),s=[...e.workspacePaths??[],...e.artifactDirectoryPath?[e.artifactDirectoryPath]:[]].filter(c=>typeof c=="string"&&c!=="");return{kind:"before-edit",cwd:a,tool:o,files:r?[D(a,r)]:[],folders:s}}default:return{kind:"ignored"}}},render(t,n){if(n.kind==="prompt"){let e=t.context?{injectSteps:[{ephemeralMessage:t.context}]}:{};return{stdout:JSON.stringify(e),exitCode:0}}if(n.kind==="before-edit"){let e=n.files.length>0&&n.files.every(i=>(n.folders??[]).some(r=>C(r,i))),o=t.block?{decision:"deny",reason:t.block}:e?xt:j;return{stdout:JSON.stringify(o),exitCode:0}}return{stdout:"{}",exitCode:0}},failed:t=>({stdout:JSON.stringify(t[0]==="PreToolUse"?j:{}),exitCode:0})};var P={Edit:"file_path",Write:"file_path",MultiEdit:"file_path",NotebookEdit:"notebook_path"},xe=Object.keys(P).join("|"),St={startup:"startup",resume:"resume",clear:"clear",compact:"compact",fork:"resume"},U={parse(t){let n=t??{},e=n.cwd??process.cwd();switch(n.hook_event_name){case"SessionStart":{let o=St[n.source??"startup"]??"startup";return{kind:"session-start",cwd:e,reason:o}}case"PreToolUse":case"PostToolUse":{let o=n.tool_name??"",i=P[o],r=i?n.tool_input?.[i]:void 0;return typeof r!="string"?{kind:"ignored"}:{kind:n.hook_event_name==="PreToolUse"?"before-edit":"after-edit",cwd:e,tool:o,files:[r]}}default:return{kind:"ignored"}}},render(t,n){if(n.kind==="session-start")return{stdout:t.context??"",exitCode:0};if(n.kind==="before-edit"&&(t.block||t.context)){let e={hookSpecificOutput:{hookEventName:"PreToolUse",...t.block?{permissionDecision:"deny",permissionDecisionReason:t.block}:{},...t.context?{additionalContext:t.context}:{}}};return{stdout:JSON.stringify(e),exitCode:0}}return n.kind==="after-edit"&&t.context?{stdout:JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:t.context}}),exitCode:0}:{stdout:"",exitCode:0}},failed:()=>({stdout:"",exitCode:1})};import{existsSync as $e,readFileSync as bt,writeFileSync as He}from"node:fs";import{join as J}from"node:path";var p=".buildkeel",$t="hook-cache.json",Ht="context.md",M=t=>J(t,p,Ht);function Ct(t){return J(t,p,$t)}function W(t){try{return JSON.parse(bt(Ct(t),"utf8"))}catch{return null}}var _e=`${p}/`;import{appendFileSync as Tt,closeSync as It,fstatSync as Ot,mkdirSync as q,openSync as Rt,readFileSync as V,readSync as At,renameSync as Bt,rmSync as Kt,statSync as Nt,writeFileSync as Dt}from"node:fs";import{homedir as jt}from"node:os";import{join as S}from"node:path";import{readFileSync as _t}from"node:fs";function _(t,n){let e=[{key:"setup",text:"BuildKeel is set up in this repository. Converse as you would without it: follow the user's lead, explore ideas, options and code freely, and leave BuildKeel's artifacts, IDs and commands out of it until the user runs a BuildKeel command, asks you to change code, or asks to move work forward. BuildKeel's instructions then say how to go on.",fingerprint:""}];if(t.activeUnit){let o=t.activeBranch?` on branch ${t.activeBranch}`:"";e.push({key:"unit",text:`Active unit: ${t.activeUnit}${o}.`,fingerprint:`${t.activeUnit}${o}`})}else t.untrackedSince?e.push({key:"unit",text:`No active unit in this working tree: the user chose to work without one here since ${t.untrackedSince}, so changes made now belong to no unit. Starting a unit with ${y(n,"start")} <ID> ends this.`,fingerprint:`untracked:${t.untrackedSince}`}):e.push({key:"unit",text:"No unit is active in this working tree.",fingerprint:""});if(e.push({key:"rules",text:"Read and change the spec tree only through the BuildKeel tools, never by editing its files.",fingerprint:""}),t.mode==="spec-driven")e.push({key:"stages",text:"This spec tree is Spec-driven: its capability specs say how the system behaves now, and each change says how it will change; nothing in it is approved. Before discussing how the system behaves, look up the capability specs with the BuildKeel query tool. Each BuildKeel command follows its part for a Spec-driven tree.",fingerprint:"spec-driven"},{key:"consistency",text:"When you edit one file of an open change (its proposal, a spec change, its design or its tasks), check the others against the edit, in any direction, and propose the follow-on edits in the same reply, for the user to accept or decline. Never edit code as part of this.",fingerprint:"spec-driven"},{key:"tests",text:"Put the ID of the scenario a test checks in square brackets in the test's name.",fingerprint:"spec-driven"}),t.guide==="on"&&e.push({key:"guide",text:"This person chose to be guided through their first change: at each step of it, from drafting to finishing, add one or two plain lines on what just happened and what comes next, as BuildKeel's tool results say. If they ask you to stop explaining, turn it off with the BuildKeel manage tool (action guide, guide off).",fingerprint:"on"});else if(t.stages){let o=(t.stageExamples??[]).map(r=>`\`${r}\``),i=o.length>1?`${o.slice(0,-1).join(", ")} or ${o.at(-1)}`:o[0]??"";e.push({key:"stages",text:`This spec tree's stages, as the team names them: ${t.stages}. Use these names with the user, in each command's help, questions and output; BuildKeel's tools accept them as types, and the usual names too.`+(i?` Examples of what to draft, in the team's words: ${i}; use these in place of a command's usual examples.`:""),fingerprint:`${t.stages}|${o.join(",")}`})}return e}function Et(t,n,e){if(!n.activeUnit)return null;try{return B(_t(M(t),"utf8").trimEnd(),e)}catch{return null}}function x(t,n,e,o){let i=Et(t,n,e),r=["BuildKeel:"];for(let a of _(n,e)){let s=a.key==="unit"&&n.activeUnit?i?o?" Its context is loaded again below.":" Its context follows.":" BuildKeel has no context for it; call the BuildKeel get_context tool to see why.":"";r.push(`- ${a.text}${s}`)}return i&&r.push("",i),r.join(`
|
|
3
|
+
`)}var G=({event:t,codeRoot:n,cache:e,host:o})=>t.kind!=="session-start"?null:{context:x(n,e,o,t.reason==="compact")};var Y=(t,n,e)=>S(t,p,"conversations",`${n.replace(/[^A-Za-z0-9_-]/g,"_").slice(0,100)}.${e}`),Ft=(t,n)=>Y(t,n,"json"),X=(t,n)=>Y(t,n,"notices");function Z(t,n,e){let o=X(t,n);q(S(o,".."),{recursive:!0}),Tt(o,`${JSON.stringify(e)}
|
|
4
|
+
`)}function Lt(t,n){let e=X(t,n),o;try{o=V(e,"utf8")}catch{return[]}return Kt(e,{force:!0}),o.split(`
|
|
5
|
+
`).filter(Boolean).flatMap(i=>{try{return[String(JSON.parse(i))]}catch{return[]}})}function Pt(t){try{let n=JSON.parse(V(t,"utf8"));return n.version===1?n:null}catch{return null}}function z(t,n){q(S(t,".."),{recursive:!0});let e=`${t}.${process.pid}.tmp`;Dt(e,`${JSON.stringify(n,null,2)}
|
|
6
|
+
`),Bt(e,t)}var Q=t=>t==="~"||t.startsWith("~/")?S(jt(),t.slice(1)):t;function Ut(t,n){let e;try{e=Rt(Q(t),"r")}catch{return{ok:!1,offset:n,checkpoint:null}}try{let o=Ot(e).size,i=o<n?0:n,r=Buffer.alloc(o-i),a=r.length>0?At(e,r,0,r.length,i):0,s=r.subarray(0,a).lastIndexOf(10);if(s<0)return{ok:!0,offset:i,checkpoint:null};let c=!0,l=null;for(let u of r.subarray(0,s).toString("utf8").split(`
|
|
7
|
+
`))if(u.trim())try{let f=JSON.parse(u);typeof f.step_index!="number"||typeof f.type!="string"?c=!1:f.type==="CHECKPOINT"&&(l=Math.max(l??-1,f.step_index))}catch{c=!1}return{ok:c,offset:i+s+1,checkpoint:l}}catch{return{ok:!1,offset:n,checkpoint:null}}finally{It(e)}}function Jt(t){if(!t)return 0;try{return Nt(Q(t)).size}catch{return 0}}var tt=t=>{if(t.event.kind!=="prompt")return null;let n=Lt(t.codeRoot,t.event.conversation),o=[Mt(t)?.context,...n].filter(Boolean).join(`
|
|
8
8
|
|
|
9
|
-
`);return o?{context:o}:null},
|
|
10
|
-
`)),
|
|
9
|
+
`);return o?{context:o}:null},Mt=({event:t,codeRoot:n,cache:e,host:o})=>{if(t.kind!=="prompt")return null;let i=Ft(n,t.conversation),r=_(e,o),a=Object.fromEntries(r.map(h=>[h.key,h.fingerprint])),s=Pt(i);if(!s)return z(i,{version:1,startedAt:new Date().toISOString(),reported:a,transcript:{offset:Jt(t.transcript),checkpoint:null}}),{context:x(n,e,o,!1)};let c=t.transcript?Ut(t.transcript,s.transcript.offset):{ok:!1,offset:s.transcript.offset,checkpoint:null},l=c.checkpoint!==null&&(s.transcript.checkpoint===null||c.checkpoint>s.transcript.checkpoint),u={...s,transcript:{offset:c.offset,checkpoint:l?c.checkpoint:s.transcript.checkpoint}},f=null;if(l)f=x(n,e,o,!0),u.reported=a;else if(t.newMessage){let h=r.filter(k=>s.reported[k.key]!==k.fingerprint).map(k=>`- ${k.text}`);if(!c.ok&&e.activeUnit){let k=`- Active unit: ${e.activeUnit}. If its requirements and test cases are no longer in view, call the BuildKeel get_context tool.`,b=h.findIndex(gt=>gt.startsWith("- Active unit:"));b>=0?h[b]=`${h[b]} If its requirements and test cases are no longer in view, call the BuildKeel get_context tool.`:h.unshift(k)}h.length>0&&(f=["BuildKeel:",...h].join(`
|
|
10
|
+
`)),u.reported=a}return JSON.stringify(u)!==JSON.stringify(s)&&z(i,u),f?{context:f}:null};import{appendFileSync as te,mkdirSync as ee}from"node:fs";import{dirname as ne,join as oe,resolve as re}from"node:path";import{existsSync as Wt,realpathSync as et}from"node:fs";import{basename as at,dirname as nt,isAbsolute as Gt,join as zt,relative as ct,resolve as qt,sep as Vt}from"node:path";var E={versions:"the frozen copy",approvals:"an approval record",completions:"a completion record",repins:"a re-pin decision record"},Yt={"test-plan":"TP",design:"DD"},Xt=process.platform==="win32",d=t=>Xt?t.toLowerCase():t;function g(t,n){let e=ot(t,n);if(e!==null)return e;try{let o=n;for(;!Wt(o)&&nt(o)!==o;)o=nt(o);let i=zt(et(o),ct(o,n));return ot(et(t),i)}catch{return null}}var m=(t,n)=>g(t,n);function ot(t,n){let e=ct(t,n);return e===""||e.startsWith("..")||Gt(e)?null:e.split(Vt).join("/")}var rt="Make the change you want through a new change, drafted with the BuildKeel save_draft tool (type change): finishing it writes the next versions.",Zt="It is kept, read-only, as the history of how the system came to be before this spec tree moved to Full SDLC. Change behavior through the team's stages, as a new version of the artifact it became.";function it(t,n){if(t.conversion!=="finished")return Qt(t,n);if(d(n)===d("onboarding/conversion.yaml"))return`BuildKeel: ${n} records how this spec tree moved to Full SDLC, and where each scenario went. It was finished when the tree switched, and is never edited.`;let[e]=n.split("/").map(d);return e!==d("capabilities")&&e!==d("changes")?null:`BuildKeel: ${n} is part of the capability specs and changes this spec tree had before it moved to Full SDLC. ${Zt}`}function Qt(t,n){let e=n.split("/"),[o,i="",r]=e.map(d);if(o===d("capabilities")&&e.length>=3){let s=/^v(0|[1-9][0-9]*)\.(md|yaml)$/.exec(at(n)),c=s?`${e[1]} v${s[1]}`:e[1]??"",l=s?.[2]==="yaml"?`the record of ${c}`:s?`${c}, a capability spec version`:`part of capability ${c}`;return`BuildKeel: ${n} is ${l}, written when a change was archived. Capability specs are never edited directly. ${rt}`}if(o!==d("changes")||e.length<3)return null;let a=e[1]??"";return e.length===3&&r===d("discarded.yaml")?`BuildKeel: ${n} is the discard record of ${a}. BuildKeel writes it once when a change is discarded, and it is never edited.`:e.length===3&&r===d("archive.yaml")?`BuildKeel: ${n} is the archive record of ${a}, written once when it was archived. It is never edited.`:e.length===4&&r!==void 0&&Object.hasOwn(E,r)?`BuildKeel: ${n} is ${E[r]} of ${a}. BuildKeel writes these records once and they are never edited, by hand or by the agent.`:(t.archivedChanges??[]).some(s=>d(s)===i)?`BuildKeel: ${n} belongs to ${a}, which is archived: its files are its record and are never edited. ${rt}`:null}function w(t,n){let e=m(t.specRoot,n);return e===null?null:/^bugs\/[^/]+\/closed\.yaml$/.test(d(e))?`BuildKeel: ${e} is the closing record of ${e.split("/")[1]}. BuildKeel writes it once when the bug is closed, and it is never edited. A defect that returns is a new bug.`:t.mode==="spec-driven"?it(t,e)??(t.conversion?st(t,e):null):(t.conversion==="finished"?it(t,e):null)??st(t,e)}function st(t,n){let e=n.split("/"),o=t.documents??Yt,i=e.length>=4&&e[2]!==void 0&&Object.hasOwn(o,e[2])?o[e[2]]:void 0,r=i!==void 0,a=e[r?3:2],s=`${e[1]??""}${r?`-${i}`:""}`,c=r?5:4;if(a&&e.length===c&&t.lockedDirs.map(d).includes(d(a))){if(d(a)==="versions"){let f=/^v(0|[1-9][0-9]*)\.md$/.exec(at(n))?.[1],h=f?`${s} v${f}`:s;return`BuildKeel: ${n} is the frozen copy of ${h}, which is approved and cannot be changed. To change ${s}, call the BuildKeel save_draft tool with id ${s} and the whole changed file: that opens the next version as a draft, and the approved version stays in force until the new one is approved.`}let u=E[a.toLowerCase()]??"a record";return`BuildKeel: ${n} is ${u} of ${s}. BuildKeel writes these records once and they are never edited, by hand or by the agent.`+(d(a)==="approvals"?" To approve a new version, use the BuildKeel approve tool.":"")}if((t.lockedSources??[]).find(u=>d(n).startsWith(`${d(u)}/`))){let u=`${e[1]??""}${r?`-${i}`:""}`;return`BuildKeel: ${n} is a source kept with ${u} v0, frozen when version 0 was approved: sources stay exactly as they were read. To change what ${u} says, call the BuildKeel save_draft tool with id ${u} and the whole changed file: that opens its next version as a draft.`}return t.lockedFiles.some(u=>d(u)===d(n))?`BuildKeel: ${n} is the working file of ${s}, which is approved and has no new version open, so it is not edited directly. To change ${s}, call the BuildKeel save_draft tool with id ${s} and the whole changed file: that opens the next version as a draft, and the approved version stays in force until the new one is approved.`:null}var lt=({event:t,cache:n})=>{if(t.kind!=="before-edit"&&t.kind!=="after-edit")return null;let e=n.lockMode??"block";if(!(e==="block"?t.kind==="before-edit":e==="warn"&&t.kind==="after-edit"))return null;let i=t.files.map(r=>w(n,qt(t.cwd,r))).filter(r=>r!==null);return i.length===0?null:t.kind==="before-edit"?{block:i[0]}:{context:i.map(r=>`BuildKeel: this tree's lock gate is set to warn, so the edit went ahead. ${r.replace(/^BuildKeel: /,"")} Validation and the status overview report the change. Tell the user, and undo it unless they asked for it.`).join(`
|
|
11
11
|
|
|
12
|
-
`)}};var
|
|
12
|
+
`)}};var T=(t,n)=>oe(t,p,"journal",`${n}.jsonl`),dt=({event:t,codeRoot:n,cache:e})=>{if(t.kind!=="after-edit"||!e.activeUnit)return null;let o=new Date().toISOString(),i=[];for(let a of t.files){let s=re(t.cwd,a);if(w(e,s)||m(e.specRoot,s)!==null)continue;let c=g(n,s);if(c===null||c===p||c.startsWith(`${p}/`))continue;let l={at:o,tool:t.tool,file:c};i.push(JSON.stringify(l))}if(i.length===0)return null;let r=T(n,e.activeUnit);return ee(ne(r),{recursive:!0}),te(r,`${i.join(`
|
|
13
13
|
`)}
|
|
14
|
-
`),null};import{resolve as
|
|
15
|
-
`).filter(Boolean);return new Set(e.map(o=>JSON.parse(o).file))}catch{return new Set}}var
|
|
14
|
+
`),null};import{resolve as ie}from"node:path";var ut=({event:t,codeRoot:n,cache:e,host:o})=>{if(t.kind!=="before-edit"||e.activeUnit||e.untrackedSince)return null;let i=t.files.flatMap(r=>{let a=ie(t.cwd,r);if(m(e.specRoot,a)!==null)return[];let s=g(n,a);return s===null||s===p||s.startsWith(`${p}/`)?[]:[s]});return i.length===0?null:{block:`BuildKeel: there is no active unit in this working tree, so a change to ${i.join(", ")} would belong to no unit of work. Before changing it, ask the user how to go on, recommending the choice that fits: capture what you discussed with ${y(o,"capture")}, which turns it into the artifacts the team's process needs, to approve and then build; start the unit it belongs to with ${y(o,"start")} <ID>; record it as an ad-hoc unit, with a short title you propose (create it with the BuildKeel save_draft tool, type adhoc, then start it with start_work); or go ahead without a unit, which only the user can choose (then call the BuildKeel manage tool with action untracked). Make the change once one of these is done.`}};import{readFileSync as se}from"node:fs";import{resolve as ae}from"node:path";function I(t,n){return n.some(e=>e.endsWith("/")?t.startsWith(e):t===e||t.startsWith(`${e}/`))}function O(t){let n=t.split("/"),e=n.at(-1)??"";return n.slice(0,-1).some(o=>["test","tests","__tests__","spec"].includes(o))||/[._-](test|spec)\.[^.]+$/.test(e)||/^test_.*\.py$/.test(e)}var R=8;function ce(t,n){try{let e=se(T(t,n),"utf8").split(`
|
|
15
|
+
`).filter(Boolean);return new Set(e.map(o=>JSON.parse(o).file))}catch{return new Set}}var pt=({event:t,codeRoot:n,cache:e})=>{if(t.kind!=="after-edit"||!e.activeUnit||e.scope.length===0)return null;let o=ce(n,e.activeUnit),i=[];for(let s of t.files){let c=ae(t.cwd,s);if(w(e,c)||m(e.specRoot,c)!==null)continue;let l=g(n,c);l===null||l===p||l.startsWith(`${p}/`)||!I(l,e.scope)&&!O(l)&&!o.has(l)&&!i.includes(l)&&i.push(l)}if(i.length===0)return null;let r=e.activeUnit,a=e.scope.slice(0,R).join(", ")+(e.scope.length>R?`, and ${e.scope.length-R} more`:"");return{context:`BuildKeel: ${i.join(", ")} ${i.length===1?"is":"are"} outside the areas ${r} relates to so far (${a}): its scope and its story's, and what its own work and the other work on its story changed. If the change belongs to ${r}, carry on and tell the user it widens what ${r} touches. If it belongs to other work, stop and ask the user which unit it belongs to.`}};function ft(t,n,e="claude-code"){if(t.kind==="ignored")return{};let o=v(t.cwd);if(!o)return{};let i=W(o);if(!i)return{};let r=n.map(c=>c({event:t,codeRoot:o,cache:i,host:e})).filter(c=>c!==null),a=r.find(c=>c.block)?.block,s=r.map(c=>c.context).filter(Boolean).join(`
|
|
16
16
|
|
|
17
|
-
`);return
|
|
18
|
-
`),1;let
|
|
19
|
-
`);let
|
|
20
|
-
`);let{stdout:
|
|
17
|
+
`);return s&&t.kind==="after-edit"&&t.deferTo?(Z(o,t.deferTo,s),a?{block:a}:{}):{...a?{block:a}:{},...s?{context:s}:{}}}var ht=[lt,ut,pt,dt,G,tt];var ue={"claude-code":U,antigravity:L};function pe(t){let n=t.indexOf("--host"),e=n>=0?t[n+1]:void 0,o=e&&A(e)?ue[e]:void 0;if(!o)return process.stderr.write(`BuildKeel hook: unsupported host "${e??""}".
|
|
18
|
+
`),1;let i=t.filter((r,a)=>a!==n&&a!==n+1);try{let r=de(0,"utf8");process.env.BUILDKEEL_HOOK_LOG&&le(process.env.BUILDKEEL_HOOK_LOG,`${JSON.stringify({host:e,args:i,input:r.trim()?JSON.parse(r):null})}
|
|
19
|
+
`);let a=o.parse(r.trim()?JSON.parse(r):{},i),s=ft(a,ht,e),{stdout:c,exitCode:l}=o.render(s,a);return c&&process.stdout.write(c),l}catch(r){process.stderr.write(`BuildKeel hook failed: ${r instanceof Error?r.message:String(r)}
|
|
20
|
+
`);let{stdout:a,exitCode:s}=o.failed(i);return a&&process.stdout.write(a),s}}process.exitCode=pe(process.argv.slice(2));
|