@vpxa/aikit 0.1.185 → 0.1.187

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vpxa/aikit",
3
- "version": "0.1.185",
3
+ "version": "0.1.187",
4
4
  "type": "module",
5
5
  "description": "Local-first AI developer toolkit — knowledge base, code analysis, context management, and developer tools for LLM agents",
6
6
  "license": "MIT",
@@ -1,4 +1,4 @@
1
- import{createRequire as e}from"node:module";import{createHash as t,randomUUID as n}from"node:crypto";import{homedir as r}from"node:os";import{join as i}from"node:path";import{execFileSync as a}from"node:child_process";import{existsSync as o,readdirSync as s}from"node:fs";import{Buffer as c}from"node:buffer";import{z as l}from"zod";var u=class{#e;#t;#n=0;#r=0;constructor(e=200){if(!Number.isInteger(e)||e<1)throw RangeError(`CircularBuffer capacity must be a positive integer.`);this.#e=e,this.#t=Array(e)}push(e){this.#t[this.#n]=e,this.#n=(this.#n+1)%this.#e,this.#r<this.#e&&(this.#r+=1)}toArray(){if(this.#r===0)return[];let e=(this.#n-this.#r+this.#e)%this.#e,t=[];for(let n=0;n<this.#r;n+=1)t.push(this.#t[(e+n)%this.#e]);return t}clear(){this.#t=Array(this.#e),this.#n=0,this.#r=0}get size(){return this.#r}get capacity(){return this.#e}isEmpty(){return this.#r===0}};const d=[`xhr`,`fetch`,`websocket`,`document`],f=[/^authorization$/i,/^cookie$/i,/^set-cookie$/i,/^x-api-key$/i,/^x-access-token$/i,/^x-auth-token$/i,/^x-session$/i,/^x-secret$/i,/^x-password$/i,/^proxy-authorization$/i,/^www-authenticate$/i,/^x-csrf-token$/i,/^x-xsrf-token$/i];function p(e,t){if(t?.showSensitive)return{...e};let n=[...f,...t?.additionalPatterns??[]],r={};for(let[t,i]of Object.entries(e))r[t]=n.some(e=>e.test(t))?`[REDACTED]`:i;return r}function m(e,t=512){if(e)return e.length<=t?e:`${e.slice(0,t)}...[truncated]`}function ee(){return{networkBuffer:new u(200),consoleBuffer:new u(1e3),networkEnabled:!1,consoleEnabled:!1,networkFilter:{},nextRequestSequence:0,pendingRequests:new Map}}function te(e,t){if(e.capacity===t)return e;let n=new u(t),r=e.toArray(),i=Math.max(0,r.length-t);for(let e of r.slice(i))n.push(e);return n}function ne(e){switch(e){case`error`:case`info`:case`log`:case`debug`:return e;case`warning`:return`warn`;default:return`log`}}function re(e){if(e)return Buffer.byteLength(e)<=5242880?m(e):void 0}function ie(e){if(!e)return{};let t=Buffer.byteLength(e);return{bodySize:t,bodyPreview:t<=5242880?m(e):void 0}}function ae(e,t){return!t.resourceTypes.has(e.resourceType)||t.urlPattern&&!t.urlPattern.test(e.url)?!1:!t.excludeUrls.some(t=>t.test(e.url))}function h(e){if(e.length>200)throw Error(`Network filter regex patterns must be 200 characters or fewer`);try{let t=new RegExp(e);return t.test(`a`.repeat(100)),t}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Invalid network filter regex pattern: ${t}`)}}function g(e={}){return{resourceTypes:new Set(e.resourceTypes??d),urlPattern:e.urlPattern?h(e.urlPattern):void 0,excludeUrls:(e.excludeUrls??[]).map(e=>h(e))}}function oe(e){return e.nextRequestSequence+=1,`req-${e.nextRequestSequence}`}function se(e,t,n){let r={request:t,previous:e.pendingRequestTail};e.pendingRequestTail?e.pendingRequestTail.next=r:e.pendingRequestHead=r,e.pendingRequestTail=r,e.pendingRequests.set(t,{entry:n,node:r})}function _(e,t){t.previous?t.previous.next=t.next:e.pendingRequestHead=t.next,t.next?t.next.previous=t.previous:e.pendingRequestTail=t.previous,delete t.previous,delete t.next}function ce(e){for(;e.pendingRequests.size>e.networkBuffer.capacity;){let t=e.pendingRequestHead;if(!t)return;_(e,t),e.pendingRequests.delete(t.request)}}function v(e){e.pendingRequests.clear(),e.pendingRequestHead=void 0,e.pendingRequestTail=void 0}var le=class{states=new Map;getOrCreate(e){let t=this.states.get(e);if(t)return t;let n=ee();return this.states.set(e,n),n}get(e){return this.states.get(e)}remove(e){let t=this.states.get(e);t&&(t.networkEnabled=!1,t.consoleEnabled=!1,this.detachNetworkListeners(t),this.detachConsoleListeners(t),v(t),this.states.delete(e))}clear(){for(let e of this.states.values())e.networkEnabled=!1,e.consoleEnabled=!1,this.detachNetworkListeners(e),this.detachConsoleListeners(e),v(e);this.states.clear()}enableNetwork(e,t,n,r){let i=this.getOrCreate(e);i.networkEnabled&&this.disableNetwork(e,t),i.page=t,i.networkEnabled=!0,i.networkFilter=n?{...n}:{},i.compiledNetworkFilter=g(i.networkFilter),r?.bufferSize&&(i.networkBuffer=te(i.networkBuffer,r.bufferSize));let a=e=>{let t=i.compiledNetworkFilter??g(i.networkFilter),n={resourceType:e.resourceType(),url:e.url()};if(!ae(n,t))return;let a={...e.headers()},o=Date.now(),s={id:oe(i),url:n.url,method:e.method(),resourceType:n.resourceType,headers:r?.showSensitive?a:p(a),postData:r?.includeBody?re(e.postData()??void 0):void 0,timestamp:o};i.networkBuffer.push(s),se(i,e,s),ce(i)},o=async e=>{let t=e.request(),n=i.pendingRequests.get(t);if(!n)return;i.pendingRequests.delete(t),_(i,n.node);let a;if(r?.includeBody)try{a=ie(await e.text())}catch{a=void 0}let o={...e.headers()};n.entry.response={status:e.status(),statusText:e.statusText(),headers:r?.showSensitive?o:p(o),bodySize:a?.bodySize,bodyPreview:a?.bodyPreview,timing:Date.now()-n.entry.timestamp}};i.listenerRefs={...i.listenerRefs,requestHandler:a,responseHandler:o},this.ensurePageCloseHandler(e,i,t),t.on(`request`,a),t.on(`response`,o)}disableNetwork(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.networkEnabled=!1,this.detachNetworkListeners(n),v(n))}enableConsole(e,t,n){let r=this.getOrCreate(e);r.consoleEnabled&&this.disableConsole(e,t),r.page=t,r.consoleEnabled=!0,n?.bufferSize&&(r.consoleBuffer=te(r.consoleBuffer,n.bufferSize));let i=e=>{let t=e.location();r.consoleBuffer.push({level:ne(e.type()),text:e.text(),timestamp:Date.now(),location:t.url?{url:t.url,line:t.lineNumber,column:t.columnNumber}:void 0,args:e.args().map(e=>e.toString())})};r.listenerRefs={...r.listenerRefs,consoleHandler:i},this.ensurePageCloseHandler(e,r,t),t.on(`console`,i)}disableConsole(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.consoleEnabled=!1,this.detachConsoleListeners(n))}detachNetworkListeners(e){let t=e.page,n=e.listenerRefs?.requestHandler,r=e.listenerRefs?.responseHandler;t&&n&&t.off(`request`,n),t&&r&&t.off(`response`,r),e.listenerRefs&&(delete e.listenerRefs.requestHandler,delete e.listenerRefs.responseHandler),this.detachPageCloseHandlerIfUnused(e)}detachConsoleListeners(e){let t=e.page,n=e.listenerRefs?.consoleHandler;t&&n&&t.off(`console`,n),e.listenerRefs&&delete e.listenerRefs.consoleHandler,this.detachPageCloseHandlerIfUnused(e)}ensurePageCloseHandler(e,t,n){if(t.listenerRefs?.pageCloseHandler)return;let r=()=>{this.remove(e)};t.listenerRefs={...t.listenerRefs,pageCloseHandler:r},n.on(`close`,r)}detachPageCloseHandlerIfUnused(e){if(e.networkEnabled||e.consoleEnabled)return;let t=e.page,n=e.listenerRefs?.pageCloseHandler;t&&n&&t.off(`close`,n),e.listenerRefs&&delete e.listenerRefs.pageCloseHandler,e.listenerRefs&&!e.listenerRefs.requestHandler&&!e.listenerRefs.responseHandler&&!e.listenerRefs.consoleHandler&&!e.listenerRefs.pageCloseHandler&&delete e.listenerRefs}};let ue=null;function y(){return ue||=new le,ue}const de=e(import.meta.url);function fe(e){return typeof e==`string`?e.trim():e instanceof Buffer?e.toString(`utf8`).trim():``}function pe(e){process.env.PLAYWRIGHT_BROWSERS_PATH=e}async function me(e){pe(e);let{chromium:t}=await import(`playwright-core`),n=t.executablePath();if(!n)throw Error(`Chromium executable not found in ${e}`);return n}function he(){return i(r(),`.aikit`,`browsers`)}function b(e){return e.browsersPath??process.env.PLAYWRIGHT_BROWSERS_PATH??he()}function x(e){return o(e)?s(e,{withFileTypes:!0}).some(e=>e.isDirectory()&&e.name.toLowerCase().startsWith(`chromium-`)):!1}async function S(e,t){let n=b(e);if(pe(n),t?.(`Using Chromium cache at ${n}`),x(n))return t?.(`Chromium already installed`),me(n);let r=i(de.resolve(`playwright-core`),`..`,`cli.js`);t?.(`Installing Chromium via playwright-core`);try{a(process.execPath,[r,`install`,`chromium`],{env:{...process.env,PLAYWRIGHT_BROWSERS_PATH:n},encoding:`utf8`,stdio:`pipe`})}catch(e){let t=e,n=fe(t.stderr),r=fe(t.stdout),i=n||r||t.message||`Unknown playwright install failure`;throw Error(`Failed to install Chromium: ${i}`)}return t?.(`Chromium install complete`),me(n)}function C(){return process.platform===`win32`||process.platform===`darwin`?!0:!!(process.env.DISPLAY||process.env.WAYLAND_DISPLAY)}function w(e){return e===`headless`||!C()?`headless`:e}function T(e){switch(e){case`headless`:return{headless:!0,args:[]};default:return{headless:!1,args:[]}}}var E=class{pages=new Map;registerPage(e,t,r,i){let a=n();return this.pages.set(a,{page:e,url:t,title:r,label:i,createdAt:new Date}),a}getPageByLabel(e){for(let t of this.pages.values())if(t.label===e)return t.page;throw Error(`Page not found: ${e}`)}resolvePageId(e){if(this.pages.has(e))return e;for(let[t,n]of this.pages.entries())if(n.label===e)return t;throw Error(`Page not found: ${e}`)}getPage(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.page}getPageInfo(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return{pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}}setSnapshot(e,t){let n=this.pages.get(e);if(!n)throw Error(`Page not found: ${e}`);this.pages.set(e,{...n,lastSnapshot:t})}getSnapshot(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.lastSnapshot}updatePageInfo(e,t,n){let r=this.pages.get(e);if(!r)throw Error(`Page not found: ${e}`);this.pages.set(e,{...r,url:t,title:n})}async removePage(e){let t=this.pages.get(e);t&&(await t.page.close(),this.pages.delete(e))}listPages(){return[...this.pages.entries()].map(([e,t])=>({pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}))}async closeAll(){await Promise.allSettled([...this.pages.keys()].map(async e=>this.removePage(e)))}get size(){return this.pages.size}};const D={defaultMode:`ui`,browsersPath:null,userDataDirRoot:null,idleShutdownMinutes:10,allowInternalSchemes:!1,evalTimeoutMs:1e4,evalMaxResultBytes:262144,redactPasswordFieldsInScreenshots:!0};var O=class{browser=null;context=null;idleTimer=null;launchPromise=null;currentMode=null;session=new E;config;constructor(e={}){this.config={...D,...e}}async launch(e,t){let n=w(e??this.config.defaultMode);if(this.context&&this.currentMode!==n&&await this.close(),!this.context){if(this.launchPromise)return this.launchPromise;this.launchPromise=this.doLaunch(n,t);try{await this.launchPromise}finally{this.launchPromise=null}}}async doLaunch(e,n){if(this.browser&&this.context)return;let a=b(this.config);process.env.PLAYWRIGHT_BROWSERS_PATH=a;let{chromium:o}=await import(`playwright-core`),s=await S(this.config,n),c=T(e),l=t(`sha256`).update(process.cwd()).digest(`hex`).slice(0,12),u=i(this.config.userDataDirRoot??i(r(),`.aikit`,`profiles`),l);if(n?.(`Launching Chromium in ${e} mode`),this.context=await o.launchPersistentContext(u,{headless:c.headless,args:c.args,executablePath:s}),this.browser=this.context.browser(),!this.browser)throw await this.context.close(),this.context=null,Error(`Failed to acquire Chromium browser instance`);this.currentMode=e,this.browser.on(`disconnected`,()=>{this.browser=null,this.context=null,this.currentMode=null,y().clear(),this.session.closeAll().catch(()=>{}),this.stopIdleTimer()}),this.resetIdleTimer()}stopIdleTimer(){this.idleTimer&&=(clearTimeout(this.idleTimer),null)}resetIdleTimer(){this.stopIdleTimer();let e=setTimeout(()=>{this.close()},this.config.idleShutdownMinutes*6e4);e.unref?.(),this.idleTimer=e}async close(){this.stopIdleTimer(),this.launchPromise=null;let e=this.context,t=this.browser;this.context=null,this.browser=null,this.currentMode=null;let n;try{y().clear()}catch(e){n=e}try{await this.session.closeAll()}catch(e){n??=e}try{e&&await e.close()}catch(e){n??=e}finally{try{t&&await t.close()}catch(e){n??=e}}if(n)throw n}getContext(){if(!this.context)throw Error(`Browser not launched. Call launch() first.`);return this.context}getConfig(){return this.config}isLaunched(){return this.browser!==null}};let k=null;function A(e){return k||=new O(e),k}async function ge(){k&&=(await k.close(),null)}const _e=[`file:`,`chrome:`,`chrome-extension:`,`data:`,`javascript:`],ve=[`169.254.169.254`,`metadata.google.internal`,`metadata.google.com`],j=[`localhost`,`127.0.0.1`,`::1`,`[::1]`];function ye(e){return e.replace(/^\[(.*)\]$/,`$1`).toLowerCase()}function be(e){let t=new WeakSet,n=JSON.stringify(e,(e,n)=>{if(typeof n==`bigint`)return n.toString();if(typeof n==`function`)return`[Function ${n.name||`anonymous`}]`;if(typeof n==`symbol`)return n.toString();if(n instanceof Error)return{name:n.name,message:n.message,stack:n.stack};if(typeof n==`object`&&n){if(t.has(n))return`[Circular]`;t.add(n)}return n});return n===void 0?e===void 0?`undefined`:String(e):n}function xe(e,t){let n=c.from(e,`utf8`);return n.byteLength<=t?e:n.subarray(0,t).toString(`utf8`)}function M(e,t){let n;try{n=new URL(e)}catch{return{allowed:!1,reason:`Invalid URL: ${e}`}}let r=n.protocol.toLowerCase();if(_e.includes(r)&&!t.allowInternalSchemes)return{allowed:!1,reason:`Blocked URL scheme: ${r}`};let i=ye(n.hostname);return ve.includes(i)?{allowed:!1,reason:`Blocked host: ${n.hostname}`}:(j.includes(i)||j.includes(n.hostname),{allowed:!0})}function N(e){return e?{allowed:!0}:{allowed:!1,reason:`Cookie access requires explicit confirmation (confirm: true)`}}function P(e,t){if(t<=0)return{valid:!1,reason:`maxBytes must be greater than 0`};try{let n=be(e);return c.byteLength(n,`utf8`)<=t?{valid:!0,result:n,truncated:!1}:{valid:!0,result:`${xe(n,Math.max(t-3,0))}...`,truncated:!0,reason:`Result exceeded ${t} bytes and was truncated`}}catch(e){return{valid:!1,reason:`Unable to serialize eval result: ${e instanceof Error?e.message:String(e)}`}}}function Se(e){return e.replace(/<input\b[^>]*>/gi,e=>/\btype\s*=\s*(["'])password\1/i.test(e)?/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i.test(e)?e.replace(/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i,`value="***"`):e.replace(/\s*\/?>$/,e=>` value="***"${e}`):e)}const Ce=[`ui`,`headless`];function we(e){return typeof e==`object`&&e?e:null}function F(e){return typeof e==`string`&&e.length>0?e:void 0}function I(e){return typeof e==`boolean`?e:void 0}function L(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function R(e){return typeof e==`string`&&Ce.includes(e)?e:void 0}function z(e){let t=process.env[e];if(!t)return;let n=Number(t);return Number.isFinite(n)?n:void 0}function Te(e){let t=we(e.browser)??{};return{...D,defaultMode:R(process.env.AIKIT_BROWSER_DEFAULT_MODE)??R(t.defaultMode)??D.defaultMode,browsersPath:F(process.env.AIKIT_BROWSER_PATH)??F(process.env.AIKIT_BROWSER_BROWSERS_PATH)??F(t.browsersPath)??D.browsersPath,userDataDirRoot:F(t.userDataDirRoot)??D.userDataDirRoot,idleShutdownMinutes:z(`AIKIT_BROWSER_IDLE_MINUTES`)??L(t.idleShutdownMinutes)??D.idleShutdownMinutes,allowInternalSchemes:I(t.allowInternalSchemes)??D.allowInternalSchemes,evalTimeoutMs:z(`AIKIT_BROWSER_EVAL_TIMEOUT_MS`)??L(t.evalTimeoutMs)??D.evalTimeoutMs,evalMaxResultBytes:L(t.evalMaxResultBytes)??D.evalMaxResultBytes,redactPasswordFieldsInScreenshots:I(t.redactPasswordFieldsInScreenshots)??D.redactPasswordFieldsInScreenshots}}function B(e,t){return{content:[{type:`text`,text:e}],structuredContent:t}}function V(e,t){let n=e.selector??e.ref??(e.element?`text=${e.element}`:void 0);if(!n)throw Error(`${t} requires selector, ref, or element`);return n}async function Ee(e,t,n){let r=null;try{return await Promise.race([e,new Promise((e,i)=>{r=setTimeout(()=>{i(Error(`${n} timed out after ${t}ms`))},t)})])}finally{r&&clearTimeout(r)}}l.string().describe(`Tracked browser page identifier`),l.enum([`click`,`type`,`press`,`hover`,`drag`,`select`,`scroll`,`upload`]).describe(`Interaction kind: click, type, press, hover, drag, select, scroll, upload`),l.string().optional().describe(`Target ref alias`),l.string().optional().describe(`Playwright selector`),l.string().optional().describe(`Human-readable element label`),l.string().optional().describe(`Text to type`),l.string().optional().describe(`Key to press`),l.string().optional().describe(`Option value, drag target, scroll spec, or file path(s)`),l.string().optional().describe(`Drag source ref`),l.string().optional().describe(`Drag source selector`),l.string().optional().describe(`Drag target ref`),l.string().optional().describe(`Drag target selector`);function De(e){return async({pageId:t,kind:n,ref:r,selector:i,element:a,text:o,key:s,value:c,fromRef:l,fromSelector:u,toRef:d,toSelector:f})=>{let p=A(e),m=p.session.getPage(t);switch(n){case`click`:{let e=V({ref:r,selector:i,element:a},`browser_act(click)`);await m.click(e);break}case`type`:{let e=V({ref:r,selector:i,element:a},`browser_act(type)`);await m.fill(e,o??``);break}case`press`:{let e=V({ref:r,selector:i,element:a},`browser_act(press)`);if(!s)throw Error(`browser_act(press) requires key`);await m.press(e,s);break}case`hover`:{let e=V({ref:r,selector:i,element:a},`browser_act(hover)`);await m.hover(e);break}case`drag`:{let e=V({ref:l??r,selector:u??i,element:a},`browser_act(drag) source`),t=V({ref:d,selector:f??c,element:a},`browser_act(drag) target`);await m.dragAndDrop(e,t);break}case`select`:{let e=V({ref:r,selector:i,element:a},`browser_act(select)`);if(c===void 0)throw Error(`browser_act(select) requires value`);await m.selectOption(e,c);break}case`scroll`:{if(i||r||a){let e=V({ref:r,selector:i,element:a},`browser_act(scroll)`);await m.locator(e).scrollIntoViewIfNeeded();break}let e=c??`down 500`;await m.evaluate(e=>{if(e===`bottom`){window.scrollTo(0,document.body.scrollHeight);return}if(e===`top`){window.scrollTo(0,0);return}let t=e.split(` `),n=t[0]||`down`,r=Number.parseInt(t[1]||`500`,10),i=n===`left`?-r:n===`right`?r:0,a=n===`up`?-r:n===`down`?r:0;window.scrollBy(i,a)},e);break}case`upload`:{let e=V({ref:r,selector:i,element:a},`browser_act(upload)`);if(!c)throw Error(`value (file path or JSON array of paths) required for upload`);let t;try{let e=JSON.parse(c);t=Array.isArray(e)?e:[c]}catch{t=[c]}await m.locator(e).setInputFiles(t);break}}return p.resetIdleTimer(),B(`ok`,{ok:!0})}}function Oe(e,t){return t?e.filter(e=>e.level===t):e}function ke(e){return e.map(e=>{let t=e.location?` (${e.location.url}:${e.location.line})`:``;return`[${e.level.toUpperCase()}] ${e.text}${t}`}).join(`
1
+ import{createRequire as e}from"node:module";import{createHash as t,randomUUID as n}from"node:crypto";import{homedir as r}from"node:os";import{join as i}from"node:path";import{execFileSync as a}from"node:child_process";import{existsSync as o,readdirSync as s}from"node:fs";import{Buffer as c}from"node:buffer";import{z as l}from"zod";var u=class{#e;#t;#n=0;#r=0;constructor(e=200){if(!Number.isInteger(e)||e<1)throw RangeError(`CircularBuffer capacity must be a positive integer.`);this.#e=e,this.#t=Array(e)}push(e){this.#t[this.#n]=e,this.#n=(this.#n+1)%this.#e,this.#r<this.#e&&(this.#r+=1)}toArray(){if(this.#r===0)return[];let e=(this.#n-this.#r+this.#e)%this.#e,t=[];for(let n=0;n<this.#r;n+=1)t.push(this.#t[(e+n)%this.#e]);return t}clear(){this.#t=Array(this.#e),this.#n=0,this.#r=0}get size(){return this.#r}get capacity(){return this.#e}isEmpty(){return this.#r===0}};const d=[`xhr`,`fetch`,`websocket`,`document`],f=[/^authorization$/i,/^cookie$/i,/^set-cookie$/i,/^x-api-key$/i,/^x-access-token$/i,/^x-auth-token$/i,/^x-session$/i,/^x-secret$/i,/^x-password$/i,/^proxy-authorization$/i,/^www-authenticate$/i,/^x-csrf-token$/i,/^x-xsrf-token$/i];function p(e,t){if(t?.showSensitive)return{...e};let n=[...f,...t?.additionalPatterns??[]],r={};for(let[t,i]of Object.entries(e))r[t]=n.some(e=>e.test(t))?`[REDACTED]`:i;return r}function m(e,t=512){if(e)return e.length<=t?e:`${e.slice(0,t)}...[truncated]`}function ee(){return{networkBuffer:new u(200),consoleBuffer:new u(1e3),networkEnabled:!1,consoleEnabled:!1,networkFilter:{},nextRequestSequence:0,pendingRequests:new Map}}function te(e,t){if(e.capacity===t)return e;let n=new u(t),r=e.toArray(),i=Math.max(0,r.length-t);for(let e of r.slice(i))n.push(e);return n}function ne(e){switch(e){case`error`:case`info`:case`log`:case`debug`:return e;case`warning`:return`warn`;default:return`log`}}function re(e){if(e)return Buffer.byteLength(e)<=5242880?m(e):void 0}function ie(e){if(!e)return{};let t=Buffer.byteLength(e);return{bodySize:t,bodyPreview:t<=5242880?m(e):void 0}}function ae(e,t){return!t.resourceTypes.has(e.resourceType)||t.urlPattern&&!t.urlPattern.test(e.url)?!1:!t.excludeUrls.some(t=>t.test(e.url))}function h(e){if(e.length>200)throw Error(`Network filter regex patterns must be 200 characters or fewer`);try{let t=new RegExp(e);return t.test(`a`.repeat(100)),t}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Invalid network filter regex pattern: ${t}`)}}function g(e={}){return{resourceTypes:new Set(e.resourceTypes??d),urlPattern:e.urlPattern?h(e.urlPattern):void 0,excludeUrls:(e.excludeUrls??[]).map(e=>h(e))}}function oe(e){return e.nextRequestSequence+=1,`req-${e.nextRequestSequence}`}function se(e,t,n){let r={request:t,previous:e.pendingRequestTail};e.pendingRequestTail?e.pendingRequestTail.next=r:e.pendingRequestHead=r,e.pendingRequestTail=r,e.pendingRequests.set(t,{entry:n,node:r})}function _(e,t){t.previous?t.previous.next=t.next:e.pendingRequestHead=t.next,t.next?t.next.previous=t.previous:e.pendingRequestTail=t.previous,delete t.previous,delete t.next}function ce(e){for(;e.pendingRequests.size>e.networkBuffer.capacity;){let t=e.pendingRequestHead;if(!t)return;_(e,t),e.pendingRequests.delete(t.request)}}function v(e){e.pendingRequests.clear(),e.pendingRequestHead=void 0,e.pendingRequestTail=void 0}var le=class{states=new Map;getOrCreate(e){let t=this.states.get(e);if(t)return t;let n=ee();return this.states.set(e,n),n}get(e){return this.states.get(e)}remove(e){let t=this.states.get(e);t&&(t.networkEnabled=!1,t.consoleEnabled=!1,this.detachNetworkListeners(t),this.detachConsoleListeners(t),v(t),this.states.delete(e))}clear(){for(let e of this.states.values())e.networkEnabled=!1,e.consoleEnabled=!1,this.detachNetworkListeners(e),this.detachConsoleListeners(e),v(e);this.states.clear()}enableNetwork(e,t,n,r){let i=this.getOrCreate(e);i.networkEnabled&&this.disableNetwork(e,t),i.page=t,i.networkEnabled=!0,i.networkFilter=n?{...n}:{},i.compiledNetworkFilter=g(i.networkFilter),r?.bufferSize&&(i.networkBuffer=te(i.networkBuffer,r.bufferSize));let a=e=>{let t=i.compiledNetworkFilter??g(i.networkFilter),n={resourceType:e.resourceType(),url:e.url()};if(!ae(n,t))return;let a={...e.headers()},o=Date.now(),s={id:oe(i),url:n.url,method:e.method(),resourceType:n.resourceType,headers:r?.showSensitive?a:p(a),postData:r?.includeBody?re(e.postData()??void 0):void 0,timestamp:o};i.networkBuffer.push(s),se(i,e,s),ce(i)},o=async e=>{let t=e.request(),n=i.pendingRequests.get(t);if(!n)return;i.pendingRequests.delete(t),_(i,n.node);let a;if(r?.includeBody)try{a=ie(await e.text())}catch{a=void 0}let o={...e.headers()};n.entry.response={status:e.status(),statusText:e.statusText(),headers:r?.showSensitive?o:p(o),bodySize:a?.bodySize,bodyPreview:a?.bodyPreview,timing:Date.now()-n.entry.timestamp}};i.listenerRefs={...i.listenerRefs,requestHandler:a,responseHandler:o},this.ensurePageCloseHandler(e,i,t),t.on(`request`,a),t.on(`response`,o)}disableNetwork(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.networkEnabled=!1,this.detachNetworkListeners(n),v(n))}enableConsole(e,t,n){let r=this.getOrCreate(e);r.consoleEnabled&&this.disableConsole(e,t),r.page=t,r.consoleEnabled=!0,n?.bufferSize&&(r.consoleBuffer=te(r.consoleBuffer,n.bufferSize));let i=e=>{let t=e.location();r.consoleBuffer.push({level:ne(e.type()),text:e.text(),timestamp:Date.now(),location:t.url?{url:t.url,line:t.lineNumber,column:t.columnNumber}:void 0,args:e.args().map(e=>e.toString())})};r.listenerRefs={...r.listenerRefs,consoleHandler:i},this.ensurePageCloseHandler(e,r,t),t.on(`console`,i)}disableConsole(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.consoleEnabled=!1,this.detachConsoleListeners(n))}detachNetworkListeners(e){let t=e.page,n=e.listenerRefs?.requestHandler,r=e.listenerRefs?.responseHandler;t&&n&&t.off(`request`,n),t&&r&&t.off(`response`,r),e.listenerRefs&&(delete e.listenerRefs.requestHandler,delete e.listenerRefs.responseHandler),this.detachPageCloseHandlerIfUnused(e)}detachConsoleListeners(e){let t=e.page,n=e.listenerRefs?.consoleHandler;t&&n&&t.off(`console`,n),e.listenerRefs&&delete e.listenerRefs.consoleHandler,this.detachPageCloseHandlerIfUnused(e)}ensurePageCloseHandler(e,t,n){if(t.listenerRefs?.pageCloseHandler)return;let r=()=>{this.remove(e)};t.listenerRefs={...t.listenerRefs,pageCloseHandler:r},n.on(`close`,r)}detachPageCloseHandlerIfUnused(e){if(e.networkEnabled||e.consoleEnabled)return;let t=e.page,n=e.listenerRefs?.pageCloseHandler;t&&n&&t.off(`close`,n),e.listenerRefs&&delete e.listenerRefs.pageCloseHandler,e.listenerRefs&&!e.listenerRefs.requestHandler&&!e.listenerRefs.responseHandler&&!e.listenerRefs.consoleHandler&&!e.listenerRefs.pageCloseHandler&&delete e.listenerRefs}};let ue=null;function y(){return ue||=new le,ue}const de=e(import.meta.url);function fe(e){return typeof e==`string`?e.trim():e instanceof Buffer?e.toString(`utf8`).trim():``}function pe(e){process.env.PLAYWRIGHT_BROWSERS_PATH=e}async function me(e){pe(e);let{chromium:t}=await import(`playwright-core`),n=t.executablePath();if(!n)throw Error(`Chromium executable not found in ${e}`);return n}function he(){return i(r(),`.aikit`,`browsers`)}function b(e){return e.browsersPath??process.env.PLAYWRIGHT_BROWSERS_PATH??he()}function x(e){return o(e)?s(e,{withFileTypes:!0}).some(e=>e.isDirectory()&&e.name.toLowerCase().startsWith(`chromium-`)):!1}async function S(e,t){let n=b(e);if(pe(n),t?.(`Using Chromium cache at ${n}`),x(n))return t?.(`Chromium already installed`),me(n);let r=i(de.resolve(`playwright-core`),`..`,`cli.js`);t?.(`Installing Chromium via playwright-core`);try{a(process.execPath,[r,`install`,`chromium`],{env:{...process.env,PLAYWRIGHT_BROWSERS_PATH:n},encoding:`utf8`,stdio:`pipe`})}catch(e){let t=e,n=fe(t.stderr),r=fe(t.stdout),i=n||r||t.message||`Unknown playwright install failure`;throw Error(`Failed to install Chromium: ${i}`)}return t?.(`Chromium install complete`),me(n)}function C(){return process.platform===`win32`||process.platform===`darwin`?!0:!!(process.env.DISPLAY||process.env.WAYLAND_DISPLAY)}function w(e){return e===`headless`||!C()?`headless`:e}function T(e){switch(e){case`headless`:return{headless:!0,args:[]};default:return{headless:!1,args:[]}}}var E=class{pages=new Map;registerPage(e,t,r,i){let a=n();return this.pages.set(a,{page:e,url:t,title:r,label:i,createdAt:new Date}),a}getPageByLabel(e){for(let t of this.pages.values())if(t.label===e)return t.page;throw Error(`Page not found: ${e}`)}resolvePageId(e){if(this.pages.has(e))return e;for(let[t,n]of this.pages.entries())if(n.label===e)return t;throw Error(`Page not found: ${e}`)}getPage(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.page}getPageInfo(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return{pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}}setSnapshot(e,t){let n=this.pages.get(e);if(!n)throw Error(`Page not found: ${e}`);this.pages.set(e,{...n,lastSnapshot:t})}getSnapshot(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.lastSnapshot}updatePageInfo(e,t,n){let r=this.pages.get(e);if(!r)throw Error(`Page not found: ${e}`);this.pages.set(e,{...r,url:t,title:n})}async removePage(e){let t=this.pages.get(e);t&&(await t.page.close(),this.pages.delete(e))}listPages(){return[...this.pages.entries()].map(([e,t])=>({pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}))}async closeAll(){await Promise.allSettled([...this.pages.keys()].map(async e=>this.removePage(e)))}get size(){return this.pages.size}};const D={defaultMode:`ui`,browsersPath:null,userDataDirRoot:null,idleShutdownMinutes:10,allowInternalSchemes:!1,evalTimeoutMs:1e4,evalMaxResultBytes:262144,redactPasswordFieldsInScreenshots:!0};var O=class{browser=null;context=null;idleTimer=null;launchPromise=null;currentMode=null;session=new E;config;constructor(e={}){this.config={...D,...e}}async launch(e,t){let n=w(e??this.config.defaultMode);if(this.context&&this.currentMode!==n&&await this.close(),!this.context){if(this.launchPromise)return this.launchPromise;this.launchPromise=this.doLaunch(n,t);try{await this.launchPromise}finally{this.launchPromise=null}}}async doLaunch(e,n){if(this.browser&&this.context)return;let a=b(this.config);process.env.PLAYWRIGHT_BROWSERS_PATH=a;let{chromium:o}=await import(`playwright-core`),s=await S(this.config,n),c=T(e),l=t(`sha256`).update(process.cwd()).digest(`hex`).slice(0,12),u=i(this.config.userDataDirRoot??i(r(),`.aikit`,`profiles`),l);if(n?.(`Launching Chromium in ${e} mode`),this.context=await o.launchPersistentContext(u,{headless:c.headless,args:c.args,executablePath:s,viewport:null}),this.browser=this.context.browser(),!this.browser)throw await this.context.close(),this.context=null,Error(`Failed to acquire Chromium browser instance`);this.currentMode=e,this.browser.on(`disconnected`,()=>{this.browser=null,this.context=null,this.currentMode=null,y().clear(),this.session.closeAll().catch(()=>{}),this.stopIdleTimer()}),this.resetIdleTimer()}stopIdleTimer(){this.idleTimer&&=(clearTimeout(this.idleTimer),null)}resetIdleTimer(){this.stopIdleTimer();let e=setTimeout(()=>{this.close()},this.config.idleShutdownMinutes*6e4);e.unref?.(),this.idleTimer=e}async close(){this.stopIdleTimer(),this.launchPromise=null;let e=this.context,t=this.browser;this.context=null,this.browser=null,this.currentMode=null;let n;try{y().clear()}catch(e){n=e}try{await this.session.closeAll()}catch(e){n??=e}try{e&&await e.close()}catch(e){n??=e}finally{try{t&&await t.close()}catch(e){n??=e}}if(n)throw n}getContext(){if(!this.context)throw Error(`Browser not launched. Call launch() first.`);return this.context}getConfig(){return this.config}isLaunched(){return this.browser!==null}};let k=null;function A(e){return k||=new O(e),k}async function ge(){k&&=(await k.close(),null)}const _e=[`file:`,`chrome:`,`chrome-extension:`,`data:`,`javascript:`],ve=[`169.254.169.254`,`metadata.google.internal`,`metadata.google.com`],j=[`localhost`,`127.0.0.1`,`::1`,`[::1]`];function ye(e){return e.replace(/^\[(.*)\]$/,`$1`).toLowerCase()}function be(e){let t=new WeakSet,n=JSON.stringify(e,(e,n)=>{if(typeof n==`bigint`)return n.toString();if(typeof n==`function`)return`[Function ${n.name||`anonymous`}]`;if(typeof n==`symbol`)return n.toString();if(n instanceof Error)return{name:n.name,message:n.message,stack:n.stack};if(typeof n==`object`&&n){if(t.has(n))return`[Circular]`;t.add(n)}return n});return n===void 0?e===void 0?`undefined`:String(e):n}function xe(e,t){let n=c.from(e,`utf8`);return n.byteLength<=t?e:n.subarray(0,t).toString(`utf8`)}function M(e,t){let n;try{n=new URL(e)}catch{return{allowed:!1,reason:`Invalid URL: ${e}`}}let r=n.protocol.toLowerCase();if(_e.includes(r)&&!t.allowInternalSchemes)return{allowed:!1,reason:`Blocked URL scheme: ${r}`};let i=ye(n.hostname);return ve.includes(i)?{allowed:!1,reason:`Blocked host: ${n.hostname}`}:(j.includes(i)||j.includes(n.hostname),{allowed:!0})}function N(e){return e?{allowed:!0}:{allowed:!1,reason:`Cookie access requires explicit confirmation (confirm: true)`}}function P(e,t){if(t<=0)return{valid:!1,reason:`maxBytes must be greater than 0`};try{let n=be(e);return c.byteLength(n,`utf8`)<=t?{valid:!0,result:n,truncated:!1}:{valid:!0,result:`${xe(n,Math.max(t-3,0))}...`,truncated:!0,reason:`Result exceeded ${t} bytes and was truncated`}}catch(e){return{valid:!1,reason:`Unable to serialize eval result: ${e instanceof Error?e.message:String(e)}`}}}function Se(e){return e.replace(/<input\b[^>]*>/gi,e=>/\btype\s*=\s*(["'])password\1/i.test(e)?/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i.test(e)?e.replace(/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i,`value="***"`):e.replace(/\s*\/?>$/,e=>` value="***"${e}`):e)}const Ce=[`ui`,`headless`];function we(e){return typeof e==`object`&&e?e:null}function F(e){return typeof e==`string`&&e.length>0?e:void 0}function I(e){return typeof e==`boolean`?e:void 0}function L(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function R(e){return typeof e==`string`&&Ce.includes(e)?e:void 0}function z(e){let t=process.env[e];if(!t)return;let n=Number(t);return Number.isFinite(n)?n:void 0}function Te(e){let t=we(e.browser)??{};return{...D,defaultMode:R(process.env.AIKIT_BROWSER_DEFAULT_MODE)??R(t.defaultMode)??D.defaultMode,browsersPath:F(process.env.AIKIT_BROWSER_PATH)??F(process.env.AIKIT_BROWSER_BROWSERS_PATH)??F(t.browsersPath)??D.browsersPath,userDataDirRoot:F(t.userDataDirRoot)??D.userDataDirRoot,idleShutdownMinutes:z(`AIKIT_BROWSER_IDLE_MINUTES`)??L(t.idleShutdownMinutes)??D.idleShutdownMinutes,allowInternalSchemes:I(t.allowInternalSchemes)??D.allowInternalSchemes,evalTimeoutMs:z(`AIKIT_BROWSER_EVAL_TIMEOUT_MS`)??L(t.evalTimeoutMs)??D.evalTimeoutMs,evalMaxResultBytes:L(t.evalMaxResultBytes)??D.evalMaxResultBytes,redactPasswordFieldsInScreenshots:I(t.redactPasswordFieldsInScreenshots)??D.redactPasswordFieldsInScreenshots}}function B(e,t){return{content:[{type:`text`,text:e}],structuredContent:t}}function V(e,t){let n=e.selector??e.ref??(e.element?`text=${e.element}`:void 0);if(!n)throw Error(`${t} requires selector, ref, or element`);return n}async function Ee(e,t,n){let r=null;try{return await Promise.race([e,new Promise((e,i)=>{r=setTimeout(()=>{i(Error(`${n} timed out after ${t}ms`))},t)})])}finally{r&&clearTimeout(r)}}l.string().describe(`Tracked browser page identifier`),l.enum([`click`,`type`,`press`,`hover`,`drag`,`select`,`scroll`,`upload`]).describe(`Interaction kind: click, type, press, hover, drag, select, scroll, upload`),l.string().optional().describe(`Target ref alias`),l.string().optional().describe(`Playwright selector`),l.string().optional().describe(`Human-readable element label`),l.string().optional().describe(`Text to type`),l.string().optional().describe(`Key to press`),l.string().optional().describe(`Option value, drag target, scroll spec, or file path(s)`),l.string().optional().describe(`Drag source ref`),l.string().optional().describe(`Drag source selector`),l.string().optional().describe(`Drag target ref`),l.string().optional().describe(`Drag target selector`);function De(e){return async({pageId:t,kind:n,ref:r,selector:i,element:a,text:o,key:s,value:c,fromRef:l,fromSelector:u,toRef:d,toSelector:f})=>{let p=A(e),m=p.session.getPage(t);switch(n){case`click`:{let e=V({ref:r,selector:i,element:a},`browser_act(click)`);await m.click(e);break}case`type`:{let e=V({ref:r,selector:i,element:a},`browser_act(type)`);await m.fill(e,o??``);break}case`press`:{let e=V({ref:r,selector:i,element:a},`browser_act(press)`);if(!s)throw Error(`browser_act(press) requires key`);await m.press(e,s);break}case`hover`:{let e=V({ref:r,selector:i,element:a},`browser_act(hover)`);await m.hover(e);break}case`drag`:{let e=V({ref:l??r,selector:u??i,element:a},`browser_act(drag) source`),t=V({ref:d,selector:f??c,element:a},`browser_act(drag) target`);await m.dragAndDrop(e,t);break}case`select`:{let e=V({ref:r,selector:i,element:a},`browser_act(select)`);if(c===void 0)throw Error(`browser_act(select) requires value`);await m.selectOption(e,c);break}case`scroll`:{if(i||r||a){let e=V({ref:r,selector:i,element:a},`browser_act(scroll)`);await m.locator(e).scrollIntoViewIfNeeded();break}let e=c??`down 500`;await m.evaluate(e=>{if(e===`bottom`){window.scrollTo(0,document.body.scrollHeight);return}if(e===`top`){window.scrollTo(0,0);return}let t=e.split(` `),n=t[0]||`down`,r=Number.parseInt(t[1]||`500`,10),i=n===`left`?-r:n===`right`?r:0,a=n===`up`?-r:n===`down`?r:0;window.scrollBy(i,a)},e);break}case`upload`:{let e=V({ref:r,selector:i,element:a},`browser_act(upload)`);if(!c)throw Error(`value (file path or JSON array of paths) required for upload`);let t;try{let e=JSON.parse(c);t=Array.isArray(e)?e:[c]}catch{t=[c]}await m.locator(e).setInputFiles(t);break}}return p.resetIdleTimer(),B(`ok`,{ok:!0})}}function Oe(e,t){return t?e.filter(e=>e.level===t):e}function ke(e){return e.map(e=>{let t=e.location?` (${e.location.url}:${e.location.line})`:``;return`[${e.level.toUpperCase()}] ${e.text}${t}`}).join(`
2
2
  `)}async function Ae(e,t){let n=y();switch(t.subAction){case`enable`:return n.enableConsole(t.pageId,e,{bufferSize:t.bufferSize}),B(`Console capture enabled for page ${t.pageId}`,{enabled:!0,bufferSize:t.bufferSize??1e3});case`get`:{let e=n.get(t.pageId);if(!e?.consoleEnabled)return B(`Console capture not enabled. Call with subAction: "enable" first.`,{enabled:!1,entries:[]});let r=Oe(e.consoleBuffer.toArray(),t.level);return B(ke(r)||`No console messages captured yet.`,{enabled:!0,count:r.length,entries:r})}case`clear`:{let e=n.get(t.pageId);return e&&e.consoleBuffer.clear(),B(`Console buffer cleared.`,{cleared:!0})}}}function je(e){return async t=>{let n=A(e),r=await Ae(n.session.getPage(t.pageId),t);return n.resetIdleTimer(),r}}l.string().describe(`Tracked browser page identifier`),l.boolean().describe(`Whether to accept the next dialog`),l.string().optional().describe(`Text to provide when accepting a prompt dialog`);function Me(e){return async({pageId:t,accept:n,promptText:r})=>{let i=A(e);return i.session.getPage(t).once(`dialog`,async e=>{n?await e.accept(r):await e.dismiss()}),i.resetIdleTimer(),B(`ok`,{ok:!0})}}function Ne(e){return async({pageId:t})=>{let n=A(e),r=await n.session.getPage(t).accessibility.snapshot()??{},i=JSON.stringify(r,null,2),a=n.session.getSnapshot(t);if(n.session.setSnapshot(t,i),n.resetIdleTimer(),!a)return B(`First snapshot captured (no previous to diff against).\n${i}`,{pageId:t,hasChanges:!1,isFirst:!0,snapshot:r});if(a===i)return B(`No changes detected.`,{pageId:t,hasChanges:!1});let o=a.split(`
3
3
  `),s=i.split(`
4
4
  `),c=new Set(o),l=new Set(s),u=s.filter(e=>!c.has(e)),d=o.filter(e=>!l.has(e)),f=u.length>0||d.length>0;return B([u.length>0?`Added:\n${u.join(`
@@ -1371,7 +1371,7 @@ function main() {
1371
1371
  main();
1372
1372
  `},{file:`SKILL.md`,content:`---
1373
1373
  name: adr-skill
1374
- description: Create and maintain Architecture Decision Records (ADRs) optimized for agentic coding workflows. Use when you need to propose, write, update, accept/reject, deprecate, or supersede an ADR; bootstrap an adr folder and index; consult existing ADRs before implementing changes; or enforce ADR conventions. This skill uses Socratic questioning to capture intent before drafting, and validates output against an agent-readiness checklist.
1374
+ description: Create and maintain Architecture Decision Records (ADRs) optimized for agentic coding workflows. Use when you need to propose, write, update, accept/reject, deprecate, or supersede an ADR; bootstrap an adr folder and index; consult existing ADRs before implementing changes; or enforce ADR conventions. Trigger on: architecture decisions, technology choices, significant design tradeoffs, decisions that affect >3 files or >1 team, irreversible choices. This skill uses Socratic questioning to capture intent before drafting, and validates output against an agent-readiness checklist.
1375
1375
  metadata:
1376
1376
  category: cross-cutting
1377
1377
  domain: general
@@ -1400,23 +1400,26 @@ metadata:
1400
1400
  - **Simple** (≤3 options, single-team) — Context → Decision → Consequences → Implementation Plan → Alternatives
1401
1401
  - **MADR** (complex, multi-team) — Full template with Decision Drivers, Pros/Cons matrix, detailed Implementation Plan
1402
1402
 
1403
- **Commands:**
1404
- | Action | What to do |
1405
- |--------|-----------|
1406
- | Create | Run 4-phase workflow → save to \`docs/decisions/NNNN-slug.md\` |
1407
- | Consult | \`search({ query: "ADR <topic>" })\` before implementing |
1408
- | Update | Edit content, change status in YAML frontmatter |
1409
- | Deprecate | Set \`status: deprecated\`, add superseded-by link |
1410
- | Bootstrap | Create \`docs/decisions/\` + \`index.md\` if missing |
1403
+ **Commands:** Create → run 4-phase workflow and save to \`docs/decisions/NNNN-slug.md\`. Consult → \`search({ query: "ADR <topic>" })\` before implementing. Update → edit content and YAML status. Deprecate → set \`status: deprecated\` and add a superseded-by link. Bootstrap → create \`docs/decisions/\` plus \`index.md\` if missing.
1411
1404
 
1412
1405
  **Agent-readiness gate:** Every ADR scores ≥ 80% on: Specificity, Testability, Completeness, Independence, Actionability.
1413
1406
 
1407
+ ## Mindset
1408
+
1409
+ ADRs serve two audiences separated by time: the CURRENT team making the decision, and the FUTURE team wondering "why the hell did they do it this way?"
1410
+
1411
+ The future audience is more important. They have zero context about your constraints, discussions, and tradeoffs. Write for them.
1412
+
1413
+ An ADR is NOT meeting minutes. It captures:
1414
+ - The FORCES that drove the decision (constraints, requirements, team skills)
1415
+ - The ALTERNATIVES genuinely considered (with honest evaluation)
1416
+ - The IRREVERSIBILITY assessment (one-way door vs two-way door)
1417
+ - The CONSEQUENCES acknowledged upfront (technical debt accepted, capability traded)
1418
+
1414
1419
  ## Philosophy
1415
1420
 
1416
1421
  ADRs created with this skill are **executable specifications for coding agents**. A human approves the decision; an agent implements it. The ADR must contain everything the agent needs to write correct code without asking follow-up questions.
1417
1422
 
1418
- This means:
1419
-
1420
1423
  - Constraints must be explicit and measurable, not vibes
1421
1424
  - Decisions must be specific enough to act on ("use PostgreSQL 16 with pgvector" not "use a database")
1422
1425
  - Consequences must map to concrete follow-up tasks
@@ -1424,6 +1427,16 @@ This means:
1424
1427
  - The ADR must be self-contained — no tribal knowledge assumptions
1425
1428
  - **The ADR must include an implementation plan** — which files to touch, which patterns to follow, which tests to write, and how to verify the decision was implemented correctly
1426
1429
 
1430
+ ## ADR Quality Criteria
1431
+
1432
+ An ADR is agent-ready when it passes:
1433
+ - [ ] **Context is self-contained** — a reader unfamiliar with the project can understand the problem
1434
+ - [ ] **Options include at least 2 genuine alternatives** (no strawmen)
1435
+ - [ ] **Decision rationale cites concrete evidence** — performance numbers, blast radius, team constraints
1436
+ - [ ] **Consequences are bidirectional** — both benefits AND costs/risks
1437
+ - [ ] **Status is explicit** — proposed/accepted/deprecated/superseded with date
1438
+ - [ ] **Superseded ADRs link to their replacement** — no orphans
1439
+
1427
1440
  ## When to Write an ADR
1428
1441
 
1429
1442
  Write an ADR when a decision:
@@ -1433,12 +1446,15 @@ Write an ADR when a decision:
1433
1446
  - **Affects other people or agents** who will work in this codebase later
1434
1447
  - **Has real alternatives** that were considered and rejected
1435
1448
 
1436
- Do NOT write an ADR for:
1449
+ ## NEVER
1437
1450
 
1438
- - Routine implementation choices within an established pattern
1439
- - Bug fixes or typo corrections
1440
- - Decisions already captured in an existing ADR (update it instead)
1441
- - Style preferences already covered by linters or formatters
1451
+ - **NEVER write an ADR after implementation** unless documenting a discovery — ADRs capture the decision moment, not post-hoc rationalization. If the code already exists, you're writing documentation, not a decision record.
1452
+ - **NEVER include only one option** — a "decision" with one choice isn't a decision. If there's genuinely only one approach, you don't need an ADR.
1453
+ - **NEVER use vague consequences** like "improves maintainability" cite specific metrics, affected files, or observable outcomes.
1454
+ - **NEVER let ADRs accumulate without review** — stale "proposed" ADRs that languish >2 weeks should be accepted or withdrawn.
1455
+ - **NEVER modify an accepted ADR** — if the decision changes, SUPERSEDE with a new ADR that links back. History matters.
1456
+ - **NEVER write an ADR for trivial decisions** — library version bumps, formatting changes, and routine maintenance don't warrant the ceremony. Reserve ADRs for decisions that are hard to reverse or have team-wide impact.
1457
+ - **NEVER skip the "rejected alternatives" section** — documenting WHY you didn't pick option B is often more valuable than explaining why you picked A.
1442
1458
 
1443
1459
  When in doubt: if a future agent working in this codebase would benefit from knowing _why_ this choice was made, write the ADR.
1444
1460
 
@@ -1460,62 +1476,33 @@ Every ADR goes through four phases. Do not skip phases.
1460
1476
 
1461
1477
  ### Phase 0: Scan the Codebase
1462
1478
 
1463
- Before asking any questions, gather context from the repo:
1479
+ Before asking questions, gather enough repo context to avoid contradictory ADRs:
1464
1480
 
1465
- 1. **Find existing ADRs.** Check \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`decisions/\` for existing records. Read them. Note:
1466
-
1467
- - Existing conventions (directory, naming, template style)
1468
- - Decisions that relate to or constrain the current one
1469
- - Any ADRs this new decision might supersede
1470
-
1471
- 2. **Check the tech stack.** Read \`package.json\`, \`go.mod\`, \`requirements.txt\`, \`Cargo.toml\`, or equivalent. Note relevant dependencies and versions.
1472
-
1473
- 3. **Find related code patterns.** If the decision involves a specific area (e.g., "how we handle auth"), scan for existing implementations. Identify the specific files, directories, and patterns that will be affected by the decision.
1474
-
1475
- 4. **Check for ADR references in code.** Look for ADR references in comments and docs (see "Code ↔ ADR Linking" below). This reveals which existing decisions govern which parts of the codebase.
1476
-
1477
- 5. **Note what you found.** Carry this context into Phase 1 — it will sharpen your questions and prevent the ADR from contradicting existing decisions.
1481
+ 1. **Find existing ADRs.** Check \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`decisions/\`. Note directory, naming, status style, related decisions, and anything this ADR might supersede.
1482
+ 2. **Check the tech stack.** Read \`package.json\`, \`go.mod\`, \`requirements.txt\`, \`Cargo.toml\`, or equivalent for relevant dependencies and versions.
1483
+ 3. **Find related code patterns.** Identify the files, directories, and implementation patterns this decision will affect.
1484
+ 4. **Look for ADR references in code.** Comments and docs often reveal which decisions already govern an area.
1478
1485
 
1479
1486
  ### Phase 1: Capture Intent (Socratic)
1480
1487
 
1481
- Interview the human to understand the decision space. Ask questions **one at a time**, building on previous answers. Do not dump a list of questions.
1488
+ Interview the human to understand the decision space. Ask questions **one at a time**, building on previous answers.
1482
1489
 
1483
1490
  **Core questions** (ask in roughly this order, skip what's already clear from context or Phase 0):
1484
1491
 
1485
- 1. **What are you deciding?** Get a short, specific title. Push for a verb phrase ("Choose X", "Adopt Y", "Replace Z with W").
1486
- 2. **Why now?** What broke, what's changing, or what will break if you do nothing? This is the trigger.
1487
- 3. **What constraints exist?** Tech stack, timeline, budget, team size, existing code, compliance. Be concrete. Reference what you found in Phase 0 ("I see you're already using X — does that constrain this?").
1488
- 4. **What does success look like?** Measurable outcomes. Push past "it works" to specifics (latency, throughput, DX, maintenance burden).
1489
- 5. **What options have you considered?** At least two. For each: what's the core tradeoff? If they only have one option, help them articulate why alternatives were rejected.
1490
- 6. **What's your current lean?** Capture gut intuition early. Often reveals unstated priorities.
1491
- 7. **Who needs to know or approve?** Decision-makers, consulted experts, informed stakeholders.
1492
- 8. **What would an agent need to implement this?** Which files/directories are affected? What existing patterns should it follow? What should it avoid? What tests would prove it's working? This directly feeds the Implementation Plan.
1493
-
1494
- **Adaptive follow-ups**: Based on answers, probe deeper where the decision is fuzzy. Common follow-ups:
1495
-
1496
- - "What's the worst-case outcome if this decision is wrong?"
1497
- - "What would make you revisit this in 6 months?"
1498
- - "Is there anything you're explicitly choosing NOT to do?"
1499
- - "What prior art or existing patterns in the codebase does this relate to?"
1500
- - "I found [existing ADR/pattern] — does this new decision interact with it?"
1492
+ 1. **What are you deciding?** Push for a short verb phrase title ("Choose X", "Adopt Y", "Replace Z with W").
1493
+ 2. **Why now?** What changed, broke, or will break if you do nothing?
1494
+ 3. **What constraints exist?** Tech stack, timeline, budget, team skills, compliance, and existing decisions.
1495
+ 4. **What does success look like?** Turn "it works" into measurable outcomes.
1496
+ 5. **What options were considered?** At least two real alternatives, each with a core tradeoff.
1497
+ 6. **What's the current lean?** Capture the instinct and the reason.
1498
+ 7. **Who decides?** List decision-makers, consulted experts, and informed stakeholders.
1499
+ 8. **What would an agent need to implement this?** Affected files, patterns to follow/avoid, config changes, and verification.
1501
1500
 
1502
- **When to stop**: You have enough when you can fill every section of the ADR including the Implementation Plan without making things up. If you're guessing at any section, ask another question.
1501
+ **Adaptive follow-ups**: probe where the decision is fuzzy. Useful prompts: "What's the worst-case outcome if this is wrong?", "What would make you revisit this in 6 months?", and "I found [existing ADR/pattern] — does this interact with it?"
1503
1502
 
1504
- **Intent Summary Gate**: Before moving to Phase 2, present a structured summary of what you captured and ask the human to confirm or correct it:
1503
+ **When to stop**: stop only when every ADR section, especially Implementation Plan and Verification, can be filled without guessing.
1505
1504
 
1506
- > **Here's what I'm capturing for the ADR:**
1507
- >
1508
- > - **Title**: {title}
1509
- > - **Trigger**: {why now}
1510
- > - **Constraints**: {list}
1511
- > - **Options**: {option 1} vs {option 2} [vs ...]
1512
- > - **Lean**: {which option and why}
1513
- > - **Non-goals**: {what's explicitly out of scope}
1514
- > - **Related ADRs/code**: {what exists that this interacts with}
1515
- > - **Affected files/areas**: {where in the codebase this lands}
1516
- > - **Verification**: {how we'll know it's implemented correctly}
1517
- >
1518
- > **Does this capture your intent? Anything to add or correct?**
1505
+ **Intent Summary Gate**: Before moving to Phase 2, summarize the title, trigger, constraints, options, current lean, non-goals, related ADRs/code, affected files, and verification plan, then ask: **Does this capture your intent? Anything to add or correct?**
1519
1506
 
1520
1507
  Do NOT proceed to Phase 2 until the human confirms the summary.
1521
1508
 
@@ -1535,7 +1522,7 @@ Do NOT proceed to Phase 2 until the human confirms the summary.
1535
1522
 
1536
1523
  - Use \`assets/templates/adr-simple.md\` for straightforward decisions (one clear winner, minimal tradeoffs).
1537
1524
  - Use \`assets/templates/adr-madr.md\` when you need to document multiple options with structured pros/cons/drivers.
1538
- - See \`references/template-variants.md\` for guidance.
1525
+ - See \`references/template-variants.md\` if unsure.
1539
1526
 
1540
1527
  4. **Fill every section from the confirmed intent summary.** Do not leave placeholder text. Every section should contain real content or be removed (optional sections only).
1541
1528
 
@@ -1549,7 +1536,7 @@ Do NOT proceed to Phase 2 until the human confirms the summary.
1549
1536
 
1550
1537
  ### Phase 3: Review Against Checklist
1551
1538
 
1552
- After drafting, review the ADR against the agent-readiness checklist in \`references/review-checklist.md\`.
1539
+ After drafting, review the ADR against the inline quality criteria above, then use \`references/review-checklist.md\` for the full pass.
1553
1540
 
1554
1541
  **Present the review as a summary**, not a raw checklist dump. Format:
1555
1542
 
@@ -1564,70 +1551,29 @@ After drafting, review the ADR against the agent-readiness checklist in \`refere
1564
1551
  >
1565
1552
  > **Recommendation**: {Ship it / Fix the gaps first / Needs more Phase 1 work}
1566
1553
 
1567
- Only surface failures and notable strengths do not recite every passing checkbox.
1568
-
1569
- If there are gaps, propose specific fixes. Do not just flag problems — offer solutions and ask the human to approve.
1570
-
1571
- Do not finalize until the ADR passes the checklist or the human explicitly accepts the gaps.
1554
+ Only surface failures and notable strengths. If there are gaps, propose specific fixes. Do not finalize until the ADR passes the checklist or the human explicitly accepts the gaps.
1572
1555
 
1573
1556
  ## Consulting ADRs (Read Workflow)
1574
1557
 
1575
1558
  Agents should read existing ADRs **before implementing changes** in a codebase that has them. This is not part of the create-an-ADR workflow — it's a standalone operation any agent should do.
1576
1559
 
1577
- ### When to Consult ADRs
1578
-
1579
- - Before starting work on a feature that touches architecture (auth, data layer, API design, infrastructure)
1580
- - When you encounter a pattern in the code and wonder "why is it done this way?"
1581
- - Before proposing a change that might contradict an existing decision
1582
- - When a human says "check the ADRs" or "there's a decision about this"
1583
- - When you find an ADR reference in a code comment
1584
-
1585
- ### How to Consult ADRs
1586
-
1587
- 1. **Find the ADR directory.** Check \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`decisions/\`. Also check for an index file (\`README.md\` or \`index.md\`).
1560
+ Consult ADRs:
1588
1561
 
1589
- 2. **Scan titles and statuses.** Read the index or list filenames. Focus on \`accepted\` ADRs these are active decisions.
1590
-
1591
- 3. **Read relevant ADRs fully.** Don't just read the title read context, decision, consequences, non-goals, AND the Implementation Plan. The Implementation Plan tells you what patterns to follow and what files are governed by this decision.
1592
-
1593
- 4. **Respect the decisions.** If an accepted ADR says "use PostgreSQL," don't propose switching to MongoDB without creating a new ADR that supersedes it. If you find a conflict between what the code does and what the ADR says, flag it to the human.
1594
-
1595
- 5. **Follow the Implementation Plan.** When implementing code in an area governed by an ADR, follow the patterns specified in its Implementation Plan. If the plan says "all new queries go through the data-access layer in \`src/db/\`," do that.
1596
-
1597
- 6. **Reference ADRs in your work.** Add ADR references in code comments and PR descriptions (see "Code ↔ ADR Linking" below).
1562
+ 1. **Before architecture work.** Especially auth, data layer, API design, infra, or any change that could contradict an existing decision.
1563
+ 2. **Find the ADR directory and index.** Check \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`decisions/\`, then read \`README.md\` or \`index.md\` if present.
1564
+ 3. **Focus on accepted ADRs first.** Those are the active rules unless superseded.
1565
+ 4. **Read the full ADR, not just the title.** Context, decision, consequences, non-goals, and Implementation Plan all matter.
1566
+ 5. **Follow the Implementation Plan.** If an ADR says new queries go through \`src/db/\`, do that.
1567
+ 6. **Escalate conflicts.** If code and ADR disagree, or a new change would contradict an accepted ADR, flag it or draft a superseding ADR.
1568
+ 7. **Reference the ADR in your work.** Link it in code comments or PR notes when the decision directly governs the change.
1598
1569
 
1599
1570
  ## Code ↔ ADR Linking
1600
1571
 
1601
1572
  ADRs should be bidirectionally linked to the code they govern.
1602
1573
 
1603
- ### ADR → Code (in the Implementation Plan)
1604
-
1605
- The Implementation Plan section names specific files, directories, and patterns:
1606
-
1607
- \`\`\`markdown
1608
- ## Implementation Plan
1609
-
1610
- - **Affected paths**: \`src/db/\`, \`src/config/database.ts\`, \`tests/integration/\`
1611
- - **Pattern**: all database queries go through \`src/db/client.ts\`
1612
- \`\`\`
1613
-
1614
- ### Code → ADR (in comments)
1615
-
1616
- When implementing code guided by an ADR, add a comment referencing it:
1617
-
1618
- \`\`\`typescript
1619
- // ADR: Using sql.js for test database
1620
- // See: docs/decisions/2025-06-15-use-sqlite-for-test-database.md
1621
- import initSqlJs from 'sql.js';
1622
- \`\`\`
1623
-
1624
- Keep these lightweight — one comment at the entry point, not on every line. The goal is discoverability: when a future agent reads this code, they can find the reasoning.
1625
-
1626
- ### Why This Matters
1627
-
1628
- - An agent working in \`src/db/\` can find which ADRs govern that area
1629
- - An agent reading an ADR can find the code that implements it
1630
- - When an ADR is superseded, the code references make it easy to find all code that needs updating
1574
+ - **ADR → Code**: the Implementation Plan should name affected paths, patterns, config changes, and verification commands.
1575
+ - **Code → ADR**: add one lightweight comment at the entry point that links back to the governing ADR.
1576
+ - **Why**: this makes the reasoning discoverable in both directions and makes superseded decisions easier to unwind.
1631
1577
 
1632
1578
  ## Other Operations
1633
1579
 
@@ -1650,7 +1596,7 @@ After an ADR is accepted:
1650
1596
  2. **Reference the ADR in PRs.** Link to the ADR in PR descriptions, e.g. "Implements \`contributing/decisions/2025-06-15-use-sqlite-for-test-database.md\`."
1651
1597
  3. **Add code references.** Add ADR path comments at key implementation points.
1652
1598
  4. **Check verification criteria.** Once implementation is complete, walk through the Verification checkboxes. Update the ADR with results in \`## More Information\`.
1653
- 5. **Revisit when triggers fire.** If the ADR specified revisit conditions ("if X happens, reconsider"), monitor for those conditions.
1599
+ 5. **Revisit when triggers fire.** If the ADR specified revisit conditions, monitor them.
1654
1600
 
1655
1601
  ### Index
1656
1602
 
@@ -1671,64 +1617,36 @@ node scripts/bootstrap_adr.js
1671
1617
 
1672
1618
  This creates the directory, an index file, and a filled-out first ADR ("Adopt architecture decision records") with real content explaining why the team is using ADRs. Use \`--json\` for machine-readable output. Use \`--dir\` to override the directory name.
1673
1619
 
1674
- ### Categories (Large Projects)
1675
-
1676
- For repos with many ADRs, organize by subdirectory:
1677
-
1678
- \`\`\`
1679
- docs/decisions/
1680
- backend/
1681
- 2025-06-15-use-postgres.md
1682
- frontend/
1683
- 2025-06-20-use-react.md
1684
- infrastructure/
1685
- 2025-07-01-use-terraform.md
1686
- \`\`\`
1687
-
1688
- Date prefixes are local to each category. Choose a categorization scheme early (by layer, by domain, by team) and document it in the index.
1689
-
1690
1620
  ## Resources
1691
1621
 
1692
1622
  ### scripts/
1693
1623
 
1694
1624
  - \`scripts/new_adr.js\` — create a new ADR file from a template, using repo conventions.
1695
- - \`scripts/set_adr_status.js\` — update an ADR status in-place (YAML front matter or inline). Use \`--json\` for machine output.
1696
- - \`scripts/bootstrap_adr.js\` — create ADR dir, \`README.md\`, and initial "Adopt ADRs" decision.
1625
+ - \`scripts/set_adr_status.js\` — update ADR status in-place. Use \`--json\` for machine output.
1626
+ - \`scripts/bootstrap_adr.js\` — create ADR dir, \`README.md\`, and an initial "Adopt ADRs" decision.
1697
1627
 
1698
1628
  ### references/
1699
1629
 
1700
- - \`references/review-checklist.md\` — agent-readiness checklist for Phase 3 review.
1630
+ - \`references/review-checklist.md\` — full Phase 3 checklist.
1701
1631
  - \`references/adr-conventions.md\` — directory, filename, status, and lifecycle conventions.
1702
- - \`references/template-variants.md\` — when to use simple vs MADR-style templates.
1703
- - \`references/examples.md\` — filled-out short and long ADR examples with implementation plans.
1632
+ - \`references/template-variants.md\` — when to use simple vs MADR.
1633
+ - \`references/examples.md\` — filled-out examples.
1704
1634
 
1705
1635
  ### assets/
1706
1636
 
1707
1637
  - \`assets/templates/adr-simple.md\` — lean template for straightforward decisions.
1708
- - \`assets/templates/adr-madr.md\` — MADR 4.0 template for decisions with multiple options and structured tradeoffs.
1709
- - \`assets/templates/adr-readme.md\` — default ADR index scaffold used by \`scripts/bootstrap_adr.js\`.
1638
+ - \`assets/templates/adr-madr.md\` — MADR 4.0 template for structured tradeoffs.
1639
+ - \`assets/templates/adr-readme.md\` — default ADR index scaffold.
1710
1640
 
1711
1641
  ### Script Usage
1712
1642
 
1713
- From the directory that contains this skill's \`scripts/\` folder:
1714
-
1715
1643
  \`\`\`bash
1716
- # Simple ADR
1717
1644
  node scripts/new_adr.js --title "Choose database" --status proposed
1718
1645
 
1719
- # MADR-style with options
1720
- node scripts/new_adr.js --title "Choose database" --template madr --status proposed
1721
-
1722
- # With index update
1723
1646
  node scripts/new_adr.js --title "Choose database" --status proposed --update-index
1724
-
1725
- # Bootstrap a new repo
1647
+ node scripts/new_adr.js --title "Choose database" --template madr --status proposed
1726
1648
  node scripts/bootstrap_adr.js --dir docs/decisions
1727
1649
  \`\`\`
1728
1650
 
1729
- Notes:
1730
-
1731
- - Scripts auto-detect ADR directory and filename strategy.
1732
- - Use \`--dir\` and \`--strategy\` to override.
1733
- - Use \`--json\` to emit machine-readable output.
1651
+ Scripts auto-detect ADR directory and filename strategy. Use \`--dir\`, \`--strategy\`, and \`--json\` when you need overrides or machine-readable output.
1734
1652
  `}];export{e as default};