buddy-agent-harness 1.2.0 → 1.3.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +6 -0
- package/dist/skill-scripts/doctor.mjs +1 -1
- package/dist/skill-scripts/init.mjs +1 -1
- package/dist/skill-scripts/mcp.mjs +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/doctor-buddy-agent-harness/README.md +1 -1
- package/skills/doctor-buddy-agent-harness/SKILL.md +1 -1
- package/skills/doctor-buddy-agent-harness/scripts/doctor.mjs +1 -1
- package/skills/enhance/SKILL.md +11 -10
- package/skills/enhance/references/delegation.md +1 -1
- package/skills/enhance/references/list-identifiers.md +2 -2
- package/skills/enhance/references/references-as-links.md +2 -2
- package/skills/enhance/references/session-scope.md +2 -2
- package/skills/enhance/scripts/doctor.mjs +1 -1
- package/skills/init-buddy-agent-harness/SKILL.md +1 -1
- package/skills/init-buddy-agent-harness/scripts/doctor.mjs +1 -1
- package/skills/init-buddy-agent-harness/scripts/init.mjs +1 -1
- package/skills/repair/SKILL.md +1 -1
- package/skills/repair/scripts/doctor.mjs +1 -1
- package/skills/repair/scripts/mcp.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# buddy-agent-harness
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 840d2dc: `enhance` now asks, for each addition, whether to write it to your global `~/.agents/AGENTS.md` or to the repository's `AGENTS.md`. It leads with the destination the addition recommends. If you pick the global file, it writes the section at the end of `~/.agents/AGENTS.md`, creating the file if needed, instead of handing you the text. A retired wording in `~/.agents/AGENTS.md` is offered the current text in its place, and replaced on your yes. Steps that bridge a harness's own user-scope file, and a global copy kept in such a file, are still handed over.
|
|
8
|
+
|
|
3
9
|
## 1.2.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -471,4 +471,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
471
471
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
472
472
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
473
473
|
`+` `.repeat(t));var Ei;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Ei||={});function parseTree$1(e,t=[],n=Ei.DEFAULT){let r={type:`array`,offset:-1,length:-1,children:[],parent:void 0};function ensurePropertyComplete(e){r.type===`property`&&(r.length=e-r.offset,r=r.parent)}function onValue(e){return r.children.push(e),e}visit(e,{onObjectBegin:e=>{r=onValue({type:`object`,offset:e,length:-1,parent:r,children:[]})},onObjectProperty:(e,t,n)=>{r=onValue({type:`property`,offset:t,length:-1,parent:r,children:[]}),r.children.push({type:`string`,value:e,offset:t,length:n,parent:r})},onObjectEnd:(e,t)=>{ensurePropertyComplete(e+t),r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onArrayBegin:(e,t)=>{r=onValue({type:`array`,offset:e,length:-1,parent:r,children:[]})},onArrayEnd:(e,t)=>{r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onLiteralValue:(e,t,n)=>{onValue({type:getNodeType(e),offset:t,length:n,parent:r,value:e}),ensurePropertyComplete(t+n)},onSeparator:(e,t,n)=>{r.type===`property`&&(e===`:`?r.colonOffset=t:e===`,`&&ensurePropertyComplete(t))},onError:(e,n,r)=>{t.push({error:e,offset:n,length:r})}},n);let i=r.children[0];return i&&delete i.parent,i}__name(parseTree$1,`parseTree`);function findNodeAtLocation$1(e,t){if(!e)return;let n=e;for(let e of t)if(typeof e==`string`){if(n.type!==`object`||!Array.isArray(n.children))return;let t=!1;for(let r of n.children)if(Array.isArray(r.children)&&r.children[0].value===e&&r.children.length===2){n=r.children[1],t=!0;break}if(!t)return}else{let t=e;if(n.type!==`array`||t<0||!Array.isArray(n.children)||t>=n.children.length)return;n=n.children[t]}return n}__name(findNodeAtLocation$1,`findNodeAtLocation`);function visit(e,t,n=Ei.DEFAULT){let r=createScanner(e,!1),i=[],a=0;function toNoArgVisit(e){return e?()=>a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisit(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisitWithPath(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice()):()=>!0}function toBeginVisit(e){return e?()=>{a>0?a++:e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice())===!1&&(a=1)}:()=>!0}function toEndVisit(e){return e?()=>{a>0&&a--,a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter())}:()=>!0}let o=toBeginVisit(t.onObjectBegin),s=toOneArgVisitWithPath(t.onObjectProperty),c=toEndVisit(t.onObjectEnd),l=toBeginVisit(t.onArrayBegin),u=toEndVisit(t.onArrayEnd),d=toOneArgVisitWithPath(t.onLiteralValue),f=toOneArgVisit(t.onSeparator),p=toNoArgVisit(t.onComment),m=toOneArgVisit(t.onError),h=n&&n.disallowComments,g=n&&n.allowTrailingComma;function scanNext(){for(;;){let e=r.scan();switch(r.getTokenError()){case 4:handleError(14);break;case 5:handleError(15);break;case 3:handleError(13);break;case 1:h||handleError(11);break;case 2:handleError(12);break;case 6:handleError(16)}switch(e){case 12:case 13:h?handleError(10):p();break;case 16:handleError(1);break;case 15:case 14:break;default:return e}}}function handleError(e,t=[],n=[]){if(m(e),t.length+n.length>0){let e=r.getToken();for(;e!==17;){if(t.indexOf(e)!==-1){scanNext();break}if(n.indexOf(e)!==-1)break;e=scanNext()}}}function parseString(e){let t=r.getTokenValue();return e?d(t):(s(t),i.push(t)),scanNext(),!0}function parseLiteral(){switch(r.getToken()){case 11:let e=r.getTokenValue(),t=Number(e);isNaN(t)&&(handleError(2),t=0),d(t);break;case 7:d(null);break;case 8:d(!0);break;case 9:d(!1);break;default:return!1}return scanNext(),!0}function parseProperty(){return r.getToken()===10?(parseString(!1),r.getToken()===6?(f(`:`),scanNext(),parseValue()||handleError(4,[],[2,5])):handleError(5,[],[2,5]),i.pop(),!0):(handleError(3,[],[2,5]),!1)}function parseObject(){o(),scanNext();let e=!1;for(;r.getToken()!==2&&r.getToken()!==17;){if(r.getToken()===5){if(e||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===2&&g)break}else e&&handleError(6,[],[]);parseProperty()||handleError(4,[],[2,5]),e=!0}return c(),r.getToken()===2?scanNext():handleError(7,[2],[]),!0}function parseArray(){l(),scanNext();let e=!0,t=!1;for(;r.getToken()!==4&&r.getToken()!==17;){if(r.getToken()===5){if(t||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===4&&g)break}else t&&handleError(6,[],[]);e?(i.push(0),e=!1):i[i.length-1]++,parseValue()||handleError(4,[],[4,5]),t=!0}return u(),e||i.pop(),r.getToken()===4?scanNext():handleError(8,[4],[]),!0}function parseValue(){switch(r.getToken()){case 3:return parseArray();case 1:return parseObject();case 10:return parseString(!0);default:return parseLiteral()}}return scanNext(),r.getToken()===17?n.allowEmptyContent?!0:(handleError(4,[],[]),!1):parseValue()?(r.getToken()!==17&&handleError(9,[],[]),!0):(handleError(4,[],[]),!1)}function getNodeType(e){switch(typeof e){case`boolean`:return`boolean`;case`number`:return`number`;case`string`:return`string`;case`object`:return e?Array.isArray(e)?`array`:`object`:`null`;default:return`null`}}var Di;(function(e){e[e.None=0]=`None`,e[e.UnexpectedEndOfComment=1]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=2]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=3]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=4]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=5]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=6]=`InvalidCharacter`})(Di||={});var Oi;(function(e){e[e.OpenBraceToken=1]=`OpenBraceToken`,e[e.CloseBraceToken=2]=`CloseBraceToken`,e[e.OpenBracketToken=3]=`OpenBracketToken`,e[e.CloseBracketToken=4]=`CloseBracketToken`,e[e.CommaToken=5]=`CommaToken`,e[e.ColonToken=6]=`ColonToken`,e[e.NullKeyword=7]=`NullKeyword`,e[e.TrueKeyword=8]=`TrueKeyword`,e[e.FalseKeyword=9]=`FalseKeyword`,e[e.StringLiteral=10]=`StringLiteral`,e[e.NumericLiteral=11]=`NumericLiteral`,e[e.LineCommentTrivia=12]=`LineCommentTrivia`,e[e.BlockCommentTrivia=13]=`BlockCommentTrivia`,e[e.LineBreakTrivia=14]=`LineBreakTrivia`,e[e.Trivia=15]=`Trivia`,e[e.Unknown=16]=`Unknown`,e[e.EOF=17]=`EOF`})(Oi||={});const ki=parseTree$1,Ai=findNodeAtLocation$1;var ji;(function(e){e[e.InvalidSymbol=1]=`InvalidSymbol`,e[e.InvalidNumberFormat=2]=`InvalidNumberFormat`,e[e.PropertyNameExpected=3]=`PropertyNameExpected`,e[e.ValueExpected=4]=`ValueExpected`,e[e.ColonExpected=5]=`ColonExpected`,e[e.CommaExpected=6]=`CommaExpected`,e[e.CloseBraceExpected=7]=`CloseBraceExpected`,e[e.CloseBracketExpected=8]=`CloseBracketExpected`,e[e.EndOfFileExpected=9]=`EndOfFileExpected`,e[e.InvalidCommentToken=10]=`InvalidCommentToken`,e[e.UnexpectedEndOfComment=11]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=12]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=13]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=14]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=15]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=16]=`InvalidCharacter`})(ji||={});function indentUnit(e){let t=/\n([ \t]+)\S/.exec(e);return t?t[1]:` `}function jsonAt(e,t,n){return JSON.stringify(e,null,t).replaceAll(`
|
|
474
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.
|
|
474
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.3.0`,description:Ni.description,format:e}).default({description:"Report whether this repository's harness bridges resolve (the same as `doctor`).",options:Qr.options,async run(e){let{bin:t,...n}=await diagnoseReport({root:e.root,harness:e.harness});return result({...homeHeader({description:Ni.description}),...n})}}).command(t))}const Pi={reference:[`load`,`create`,`delete`],governance:[`show`]};function defaultFormat(e){let t=e.slice(2);if(t.some(e=>e===`--format`||e.startsWith(`--format=`)))return`toon`;let[n,r]=t;return n!==void 0&&r!==void 0&&Pi[n]?.includes(r)?`text`:`toon`}async function run(e){try{let t=await app(defaultFormat(e)).parse(e);return typeof t==`number`?t:j.success}catch(e){return process.stderr.write(`error: ${e instanceof Error?e.message:`Invalid command.`}\n`),j.usage}}process.exitCode=await run([...process.argv.slice(0,2),`doctor`,...process.argv.slice(2)]);export{};
|
|
@@ -471,4 +471,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
471
471
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
472
472
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
473
473
|
`+` `.repeat(t));var Ei;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Ei||={});function parseTree$1(e,t=[],n=Ei.DEFAULT){let r={type:`array`,offset:-1,length:-1,children:[],parent:void 0};function ensurePropertyComplete(e){r.type===`property`&&(r.length=e-r.offset,r=r.parent)}function onValue(e){return r.children.push(e),e}visit(e,{onObjectBegin:e=>{r=onValue({type:`object`,offset:e,length:-1,parent:r,children:[]})},onObjectProperty:(e,t,n)=>{r=onValue({type:`property`,offset:t,length:-1,parent:r,children:[]}),r.children.push({type:`string`,value:e,offset:t,length:n,parent:r})},onObjectEnd:(e,t)=>{ensurePropertyComplete(e+t),r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onArrayBegin:(e,t)=>{r=onValue({type:`array`,offset:e,length:-1,parent:r,children:[]})},onArrayEnd:(e,t)=>{r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onLiteralValue:(e,t,n)=>{onValue({type:getNodeType(e),offset:t,length:n,parent:r,value:e}),ensurePropertyComplete(t+n)},onSeparator:(e,t,n)=>{r.type===`property`&&(e===`:`?r.colonOffset=t:e===`,`&&ensurePropertyComplete(t))},onError:(e,n,r)=>{t.push({error:e,offset:n,length:r})}},n);let i=r.children[0];return i&&delete i.parent,i}__name(parseTree$1,`parseTree`);function findNodeAtLocation$1(e,t){if(!e)return;let n=e;for(let e of t)if(typeof e==`string`){if(n.type!==`object`||!Array.isArray(n.children))return;let t=!1;for(let r of n.children)if(Array.isArray(r.children)&&r.children[0].value===e&&r.children.length===2){n=r.children[1],t=!0;break}if(!t)return}else{let t=e;if(n.type!==`array`||t<0||!Array.isArray(n.children)||t>=n.children.length)return;n=n.children[t]}return n}__name(findNodeAtLocation$1,`findNodeAtLocation`);function visit(e,t,n=Ei.DEFAULT){let r=createScanner(e,!1),i=[],a=0;function toNoArgVisit(e){return e?()=>a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisit(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisitWithPath(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice()):()=>!0}function toBeginVisit(e){return e?()=>{a>0?a++:e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice())===!1&&(a=1)}:()=>!0}function toEndVisit(e){return e?()=>{a>0&&a--,a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter())}:()=>!0}let o=toBeginVisit(t.onObjectBegin),s=toOneArgVisitWithPath(t.onObjectProperty),c=toEndVisit(t.onObjectEnd),l=toBeginVisit(t.onArrayBegin),u=toEndVisit(t.onArrayEnd),d=toOneArgVisitWithPath(t.onLiteralValue),f=toOneArgVisit(t.onSeparator),p=toNoArgVisit(t.onComment),m=toOneArgVisit(t.onError),h=n&&n.disallowComments,g=n&&n.allowTrailingComma;function scanNext(){for(;;){let e=r.scan();switch(r.getTokenError()){case 4:handleError(14);break;case 5:handleError(15);break;case 3:handleError(13);break;case 1:h||handleError(11);break;case 2:handleError(12);break;case 6:handleError(16)}switch(e){case 12:case 13:h?handleError(10):p();break;case 16:handleError(1);break;case 15:case 14:break;default:return e}}}function handleError(e,t=[],n=[]){if(m(e),t.length+n.length>0){let e=r.getToken();for(;e!==17;){if(t.indexOf(e)!==-1){scanNext();break}if(n.indexOf(e)!==-1)break;e=scanNext()}}}function parseString(e){let t=r.getTokenValue();return e?d(t):(s(t),i.push(t)),scanNext(),!0}function parseLiteral(){switch(r.getToken()){case 11:let e=r.getTokenValue(),t=Number(e);isNaN(t)&&(handleError(2),t=0),d(t);break;case 7:d(null);break;case 8:d(!0);break;case 9:d(!1);break;default:return!1}return scanNext(),!0}function parseProperty(){return r.getToken()===10?(parseString(!1),r.getToken()===6?(f(`:`),scanNext(),parseValue()||handleError(4,[],[2,5])):handleError(5,[],[2,5]),i.pop(),!0):(handleError(3,[],[2,5]),!1)}function parseObject(){o(),scanNext();let e=!1;for(;r.getToken()!==2&&r.getToken()!==17;){if(r.getToken()===5){if(e||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===2&&g)break}else e&&handleError(6,[],[]);parseProperty()||handleError(4,[],[2,5]),e=!0}return c(),r.getToken()===2?scanNext():handleError(7,[2],[]),!0}function parseArray(){l(),scanNext();let e=!0,t=!1;for(;r.getToken()!==4&&r.getToken()!==17;){if(r.getToken()===5){if(t||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===4&&g)break}else t&&handleError(6,[],[]);e?(i.push(0),e=!1):i[i.length-1]++,parseValue()||handleError(4,[],[4,5]),t=!0}return u(),e||i.pop(),r.getToken()===4?scanNext():handleError(8,[4],[]),!0}function parseValue(){switch(r.getToken()){case 3:return parseArray();case 1:return parseObject();case 10:return parseString(!0);default:return parseLiteral()}}return scanNext(),r.getToken()===17?n.allowEmptyContent?!0:(handleError(4,[],[]),!1):parseValue()?(r.getToken()!==17&&handleError(9,[],[]),!0):(handleError(4,[],[]),!1)}function getNodeType(e){switch(typeof e){case`boolean`:return`boolean`;case`number`:return`number`;case`string`:return`string`;case`object`:return e?Array.isArray(e)?`array`:`object`:`null`;default:return`null`}}var Di;(function(e){e[e.None=0]=`None`,e[e.UnexpectedEndOfComment=1]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=2]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=3]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=4]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=5]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=6]=`InvalidCharacter`})(Di||={});var Oi;(function(e){e[e.OpenBraceToken=1]=`OpenBraceToken`,e[e.CloseBraceToken=2]=`CloseBraceToken`,e[e.OpenBracketToken=3]=`OpenBracketToken`,e[e.CloseBracketToken=4]=`CloseBracketToken`,e[e.CommaToken=5]=`CommaToken`,e[e.ColonToken=6]=`ColonToken`,e[e.NullKeyword=7]=`NullKeyword`,e[e.TrueKeyword=8]=`TrueKeyword`,e[e.FalseKeyword=9]=`FalseKeyword`,e[e.StringLiteral=10]=`StringLiteral`,e[e.NumericLiteral=11]=`NumericLiteral`,e[e.LineCommentTrivia=12]=`LineCommentTrivia`,e[e.BlockCommentTrivia=13]=`BlockCommentTrivia`,e[e.LineBreakTrivia=14]=`LineBreakTrivia`,e[e.Trivia=15]=`Trivia`,e[e.Unknown=16]=`Unknown`,e[e.EOF=17]=`EOF`})(Oi||={});const ki=parseTree$1,Ai=findNodeAtLocation$1;var ji;(function(e){e[e.InvalidSymbol=1]=`InvalidSymbol`,e[e.InvalidNumberFormat=2]=`InvalidNumberFormat`,e[e.PropertyNameExpected=3]=`PropertyNameExpected`,e[e.ValueExpected=4]=`ValueExpected`,e[e.ColonExpected=5]=`ColonExpected`,e[e.CommaExpected=6]=`CommaExpected`,e[e.CloseBraceExpected=7]=`CloseBraceExpected`,e[e.CloseBracketExpected=8]=`CloseBracketExpected`,e[e.EndOfFileExpected=9]=`EndOfFileExpected`,e[e.InvalidCommentToken=10]=`InvalidCommentToken`,e[e.UnexpectedEndOfComment=11]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=12]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=13]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=14]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=15]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=16]=`InvalidCharacter`})(ji||={});function indentUnit(e){let t=/\n([ \t]+)\S/.exec(e);return t?t[1]:` `}function jsonAt(e,t,n){return JSON.stringify(e,null,t).replaceAll(`
|
|
474
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.
|
|
474
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.3.0`,description:Ni.description,format:e}).default({description:"Report whether this repository's harness bridges resolve (the same as `doctor`).",options:Qr.options,async run(e){let{bin:t,...n}=await diagnoseReport({root:e.root,harness:e.harness});return result({...homeHeader({description:Ni.description}),...n})}}).command(t))}const Pi={reference:[`load`,`create`,`delete`],governance:[`show`]};function defaultFormat(e){let t=e.slice(2);if(t.some(e=>e===`--format`||e.startsWith(`--format=`)))return`toon`;let[n,r]=t;return n!==void 0&&r!==void 0&&Pi[n]?.includes(r)?`text`:`toon`}async function run(e){try{let t=await app(defaultFormat(e)).parse(e);return typeof t==`number`?t:j.success}catch(e){return process.stderr.write(`error: ${e instanceof Error?e.message:`Invalid command.`}\n`),j.usage}}process.exitCode=await run([...process.argv.slice(0,2),`init`,...process.argv.slice(2)]);export{};
|
|
@@ -471,4 +471,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
471
471
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
472
472
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
473
473
|
`+` `.repeat(t));var Ei;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Ei||={});function parseTree$1(e,t=[],n=Ei.DEFAULT){let r={type:`array`,offset:-1,length:-1,children:[],parent:void 0};function ensurePropertyComplete(e){r.type===`property`&&(r.length=e-r.offset,r=r.parent)}function onValue(e){return r.children.push(e),e}visit(e,{onObjectBegin:e=>{r=onValue({type:`object`,offset:e,length:-1,parent:r,children:[]})},onObjectProperty:(e,t,n)=>{r=onValue({type:`property`,offset:t,length:-1,parent:r,children:[]}),r.children.push({type:`string`,value:e,offset:t,length:n,parent:r})},onObjectEnd:(e,t)=>{ensurePropertyComplete(e+t),r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onArrayBegin:(e,t)=>{r=onValue({type:`array`,offset:e,length:-1,parent:r,children:[]})},onArrayEnd:(e,t)=>{r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onLiteralValue:(e,t,n)=>{onValue({type:getNodeType(e),offset:t,length:n,parent:r,value:e}),ensurePropertyComplete(t+n)},onSeparator:(e,t,n)=>{r.type===`property`&&(e===`:`?r.colonOffset=t:e===`,`&&ensurePropertyComplete(t))},onError:(e,n,r)=>{t.push({error:e,offset:n,length:r})}},n);let i=r.children[0];return i&&delete i.parent,i}__name(parseTree$1,`parseTree`);function findNodeAtLocation$1(e,t){if(!e)return;let n=e;for(let e of t)if(typeof e==`string`){if(n.type!==`object`||!Array.isArray(n.children))return;let t=!1;for(let r of n.children)if(Array.isArray(r.children)&&r.children[0].value===e&&r.children.length===2){n=r.children[1],t=!0;break}if(!t)return}else{let t=e;if(n.type!==`array`||t<0||!Array.isArray(n.children)||t>=n.children.length)return;n=n.children[t]}return n}__name(findNodeAtLocation$1,`findNodeAtLocation`);function visit(e,t,n=Ei.DEFAULT){let r=createScanner(e,!1),i=[],a=0;function toNoArgVisit(e){return e?()=>a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisit(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisitWithPath(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice()):()=>!0}function toBeginVisit(e){return e?()=>{a>0?a++:e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice())===!1&&(a=1)}:()=>!0}function toEndVisit(e){return e?()=>{a>0&&a--,a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter())}:()=>!0}let o=toBeginVisit(t.onObjectBegin),s=toOneArgVisitWithPath(t.onObjectProperty),c=toEndVisit(t.onObjectEnd),l=toBeginVisit(t.onArrayBegin),u=toEndVisit(t.onArrayEnd),d=toOneArgVisitWithPath(t.onLiteralValue),f=toOneArgVisit(t.onSeparator),p=toNoArgVisit(t.onComment),m=toOneArgVisit(t.onError),h=n&&n.disallowComments,g=n&&n.allowTrailingComma;function scanNext(){for(;;){let e=r.scan();switch(r.getTokenError()){case 4:handleError(14);break;case 5:handleError(15);break;case 3:handleError(13);break;case 1:h||handleError(11);break;case 2:handleError(12);break;case 6:handleError(16)}switch(e){case 12:case 13:h?handleError(10):p();break;case 16:handleError(1);break;case 15:case 14:break;default:return e}}}function handleError(e,t=[],n=[]){if(m(e),t.length+n.length>0){let e=r.getToken();for(;e!==17;){if(t.indexOf(e)!==-1){scanNext();break}if(n.indexOf(e)!==-1)break;e=scanNext()}}}function parseString(e){let t=r.getTokenValue();return e?d(t):(s(t),i.push(t)),scanNext(),!0}function parseLiteral(){switch(r.getToken()){case 11:let e=r.getTokenValue(),t=Number(e);isNaN(t)&&(handleError(2),t=0),d(t);break;case 7:d(null);break;case 8:d(!0);break;case 9:d(!1);break;default:return!1}return scanNext(),!0}function parseProperty(){return r.getToken()===10?(parseString(!1),r.getToken()===6?(f(`:`),scanNext(),parseValue()||handleError(4,[],[2,5])):handleError(5,[],[2,5]),i.pop(),!0):(handleError(3,[],[2,5]),!1)}function parseObject(){o(),scanNext();let e=!1;for(;r.getToken()!==2&&r.getToken()!==17;){if(r.getToken()===5){if(e||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===2&&g)break}else e&&handleError(6,[],[]);parseProperty()||handleError(4,[],[2,5]),e=!0}return c(),r.getToken()===2?scanNext():handleError(7,[2],[]),!0}function parseArray(){l(),scanNext();let e=!0,t=!1;for(;r.getToken()!==4&&r.getToken()!==17;){if(r.getToken()===5){if(t||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===4&&g)break}else t&&handleError(6,[],[]);e?(i.push(0),e=!1):i[i.length-1]++,parseValue()||handleError(4,[],[4,5]),t=!0}return u(),e||i.pop(),r.getToken()===4?scanNext():handleError(8,[4],[]),!0}function parseValue(){switch(r.getToken()){case 3:return parseArray();case 1:return parseObject();case 10:return parseString(!0);default:return parseLiteral()}}return scanNext(),r.getToken()===17?n.allowEmptyContent?!0:(handleError(4,[],[]),!1):parseValue()?(r.getToken()!==17&&handleError(9,[],[]),!0):(handleError(4,[],[]),!1)}function getNodeType(e){switch(typeof e){case`boolean`:return`boolean`;case`number`:return`number`;case`string`:return`string`;case`object`:return e?Array.isArray(e)?`array`:`object`:`null`;default:return`null`}}var Di;(function(e){e[e.None=0]=`None`,e[e.UnexpectedEndOfComment=1]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=2]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=3]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=4]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=5]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=6]=`InvalidCharacter`})(Di||={});var Oi;(function(e){e[e.OpenBraceToken=1]=`OpenBraceToken`,e[e.CloseBraceToken=2]=`CloseBraceToken`,e[e.OpenBracketToken=3]=`OpenBracketToken`,e[e.CloseBracketToken=4]=`CloseBracketToken`,e[e.CommaToken=5]=`CommaToken`,e[e.ColonToken=6]=`ColonToken`,e[e.NullKeyword=7]=`NullKeyword`,e[e.TrueKeyword=8]=`TrueKeyword`,e[e.FalseKeyword=9]=`FalseKeyword`,e[e.StringLiteral=10]=`StringLiteral`,e[e.NumericLiteral=11]=`NumericLiteral`,e[e.LineCommentTrivia=12]=`LineCommentTrivia`,e[e.BlockCommentTrivia=13]=`BlockCommentTrivia`,e[e.LineBreakTrivia=14]=`LineBreakTrivia`,e[e.Trivia=15]=`Trivia`,e[e.Unknown=16]=`Unknown`,e[e.EOF=17]=`EOF`})(Oi||={});const ki=parseTree$1,Ai=findNodeAtLocation$1;var ji;(function(e){e[e.InvalidSymbol=1]=`InvalidSymbol`,e[e.InvalidNumberFormat=2]=`InvalidNumberFormat`,e[e.PropertyNameExpected=3]=`PropertyNameExpected`,e[e.ValueExpected=4]=`ValueExpected`,e[e.ColonExpected=5]=`ColonExpected`,e[e.CommaExpected=6]=`CommaExpected`,e[e.CloseBraceExpected=7]=`CloseBraceExpected`,e[e.CloseBracketExpected=8]=`CloseBracketExpected`,e[e.EndOfFileExpected=9]=`EndOfFileExpected`,e[e.InvalidCommentToken=10]=`InvalidCommentToken`,e[e.UnexpectedEndOfComment=11]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=12]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=13]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=14]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=15]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=16]=`InvalidCharacter`})(ji||={});function indentUnit(e){let t=/\n([ \t]+)\S/.exec(e);return t?t[1]:` `}function jsonAt(e,t,n){return JSON.stringify(e,null,t).replaceAll(`
|
|
474
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.
|
|
474
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.3.0`,description:Ni.description,format:e}).default({description:"Report whether this repository's harness bridges resolve (the same as `doctor`).",options:Qr.options,async run(e){let{bin:t,...n}=await diagnoseReport({root:e.root,harness:e.harness});return result({...homeHeader({description:Ni.description}),...n})}}).command(t))}const Pi={reference:[`load`,`create`,`delete`],governance:[`show`]};function defaultFormat(e){let t=e.slice(2);if(t.some(e=>e===`--format`||e.startsWith(`--format=`)))return`toon`;let[n,r]=t;return n!==void 0&&r!==void 0&&Pi[n]?.includes(r)?`text`:`toon`}async function run(e){try{let t=await app(defaultFormat(e)).parse(e);return typeof t==`number`?t:j.success}catch(e){return process.stderr.write(`error: ${e instanceof Error?e.message:`Invalid command.`}\n`),j.usage}}process.exitCode=await run([...process.argv.slice(0,2),`mcp`,...process.argv.slice(2)]);export{};
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "buddy-agent-harness",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0",
|
|
5
5
|
"description": "Initialize standards-based agent configuration across agent harnesses",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "repobuddy"
|
|
@@ -7,7 +7,7 @@ Report whether the harness skill bridges in a repository still resolve into `.ag
|
|
|
7
7
|
`init` points every harness that cannot read `.agents/skills` at that directory, normally with a directory-level symlink. `doctor` re-checks each of those bridges after the fact and names the repair for whatever it finds. The command names an `init` invocation for a person at a shell; the skill hands the same repair to the `init-buddy-agent-harness` skill, because rebuilding a bridge can move skills a user wrote.
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
npx -y buddy-agent-harness@1.
|
|
10
|
+
npx -y buddy-agent-harness@1.3.0 doctor
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Why it exists
|
|
@@ -17,7 +17,7 @@ Diagnose it:
|
|
|
17
17
|
node scripts/doctor.mjs
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
That path is relative to this skill's own directory: `scripts/doctor.mjs` is a self-contained bundle built from this package's source and shipped inside the skill folder through the npm package, so it runs against the current working directory with nothing downloaded and no `node_modules` needed. Fall back to `npx -y buddy-agent-harness@1.
|
|
20
|
+
That path is relative to this skill's own directory: `scripts/doctor.mjs` is a self-contained bundle built from this package's source and shipped inside the skill folder through the npm package, so it runs against the current working directory with nothing downloaded and no `node_modules` needed. Fall back to `npx -y buddy-agent-harness@1.3.0 doctor` when `scripts/doctor.mjs` is missing or cannot be run — the case for a skill installed from git rather than from the npm package, which does not carry the bundle.
|
|
21
21
|
|
|
22
22
|
The command is read-only. It never repairs anything, so it is safe to run at any point, including from a session-start hook.
|
|
23
23
|
|
|
@@ -471,4 +471,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
471
471
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
472
472
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
473
473
|
`+` `.repeat(t));var Ei;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Ei||={});function parseTree$1(e,t=[],n=Ei.DEFAULT){let r={type:`array`,offset:-1,length:-1,children:[],parent:void 0};function ensurePropertyComplete(e){r.type===`property`&&(r.length=e-r.offset,r=r.parent)}function onValue(e){return r.children.push(e),e}visit(e,{onObjectBegin:e=>{r=onValue({type:`object`,offset:e,length:-1,parent:r,children:[]})},onObjectProperty:(e,t,n)=>{r=onValue({type:`property`,offset:t,length:-1,parent:r,children:[]}),r.children.push({type:`string`,value:e,offset:t,length:n,parent:r})},onObjectEnd:(e,t)=>{ensurePropertyComplete(e+t),r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onArrayBegin:(e,t)=>{r=onValue({type:`array`,offset:e,length:-1,parent:r,children:[]})},onArrayEnd:(e,t)=>{r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onLiteralValue:(e,t,n)=>{onValue({type:getNodeType(e),offset:t,length:n,parent:r,value:e}),ensurePropertyComplete(t+n)},onSeparator:(e,t,n)=>{r.type===`property`&&(e===`:`?r.colonOffset=t:e===`,`&&ensurePropertyComplete(t))},onError:(e,n,r)=>{t.push({error:e,offset:n,length:r})}},n);let i=r.children[0];return i&&delete i.parent,i}__name(parseTree$1,`parseTree`);function findNodeAtLocation$1(e,t){if(!e)return;let n=e;for(let e of t)if(typeof e==`string`){if(n.type!==`object`||!Array.isArray(n.children))return;let t=!1;for(let r of n.children)if(Array.isArray(r.children)&&r.children[0].value===e&&r.children.length===2){n=r.children[1],t=!0;break}if(!t)return}else{let t=e;if(n.type!==`array`||t<0||!Array.isArray(n.children)||t>=n.children.length)return;n=n.children[t]}return n}__name(findNodeAtLocation$1,`findNodeAtLocation`);function visit(e,t,n=Ei.DEFAULT){let r=createScanner(e,!1),i=[],a=0;function toNoArgVisit(e){return e?()=>a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisit(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisitWithPath(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice()):()=>!0}function toBeginVisit(e){return e?()=>{a>0?a++:e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice())===!1&&(a=1)}:()=>!0}function toEndVisit(e){return e?()=>{a>0&&a--,a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter())}:()=>!0}let o=toBeginVisit(t.onObjectBegin),s=toOneArgVisitWithPath(t.onObjectProperty),c=toEndVisit(t.onObjectEnd),l=toBeginVisit(t.onArrayBegin),u=toEndVisit(t.onArrayEnd),d=toOneArgVisitWithPath(t.onLiteralValue),f=toOneArgVisit(t.onSeparator),p=toNoArgVisit(t.onComment),m=toOneArgVisit(t.onError),h=n&&n.disallowComments,g=n&&n.allowTrailingComma;function scanNext(){for(;;){let e=r.scan();switch(r.getTokenError()){case 4:handleError(14);break;case 5:handleError(15);break;case 3:handleError(13);break;case 1:h||handleError(11);break;case 2:handleError(12);break;case 6:handleError(16)}switch(e){case 12:case 13:h?handleError(10):p();break;case 16:handleError(1);break;case 15:case 14:break;default:return e}}}function handleError(e,t=[],n=[]){if(m(e),t.length+n.length>0){let e=r.getToken();for(;e!==17;){if(t.indexOf(e)!==-1){scanNext();break}if(n.indexOf(e)!==-1)break;e=scanNext()}}}function parseString(e){let t=r.getTokenValue();return e?d(t):(s(t),i.push(t)),scanNext(),!0}function parseLiteral(){switch(r.getToken()){case 11:let e=r.getTokenValue(),t=Number(e);isNaN(t)&&(handleError(2),t=0),d(t);break;case 7:d(null);break;case 8:d(!0);break;case 9:d(!1);break;default:return!1}return scanNext(),!0}function parseProperty(){return r.getToken()===10?(parseString(!1),r.getToken()===6?(f(`:`),scanNext(),parseValue()||handleError(4,[],[2,5])):handleError(5,[],[2,5]),i.pop(),!0):(handleError(3,[],[2,5]),!1)}function parseObject(){o(),scanNext();let e=!1;for(;r.getToken()!==2&&r.getToken()!==17;){if(r.getToken()===5){if(e||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===2&&g)break}else e&&handleError(6,[],[]);parseProperty()||handleError(4,[],[2,5]),e=!0}return c(),r.getToken()===2?scanNext():handleError(7,[2],[]),!0}function parseArray(){l(),scanNext();let e=!0,t=!1;for(;r.getToken()!==4&&r.getToken()!==17;){if(r.getToken()===5){if(t||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===4&&g)break}else t&&handleError(6,[],[]);e?(i.push(0),e=!1):i[i.length-1]++,parseValue()||handleError(4,[],[4,5]),t=!0}return u(),e||i.pop(),r.getToken()===4?scanNext():handleError(8,[4],[]),!0}function parseValue(){switch(r.getToken()){case 3:return parseArray();case 1:return parseObject();case 10:return parseString(!0);default:return parseLiteral()}}return scanNext(),r.getToken()===17?n.allowEmptyContent?!0:(handleError(4,[],[]),!1):parseValue()?(r.getToken()!==17&&handleError(9,[],[]),!0):(handleError(4,[],[]),!1)}function getNodeType(e){switch(typeof e){case`boolean`:return`boolean`;case`number`:return`number`;case`string`:return`string`;case`object`:return e?Array.isArray(e)?`array`:`object`:`null`;default:return`null`}}var Di;(function(e){e[e.None=0]=`None`,e[e.UnexpectedEndOfComment=1]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=2]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=3]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=4]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=5]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=6]=`InvalidCharacter`})(Di||={});var Oi;(function(e){e[e.OpenBraceToken=1]=`OpenBraceToken`,e[e.CloseBraceToken=2]=`CloseBraceToken`,e[e.OpenBracketToken=3]=`OpenBracketToken`,e[e.CloseBracketToken=4]=`CloseBracketToken`,e[e.CommaToken=5]=`CommaToken`,e[e.ColonToken=6]=`ColonToken`,e[e.NullKeyword=7]=`NullKeyword`,e[e.TrueKeyword=8]=`TrueKeyword`,e[e.FalseKeyword=9]=`FalseKeyword`,e[e.StringLiteral=10]=`StringLiteral`,e[e.NumericLiteral=11]=`NumericLiteral`,e[e.LineCommentTrivia=12]=`LineCommentTrivia`,e[e.BlockCommentTrivia=13]=`BlockCommentTrivia`,e[e.LineBreakTrivia=14]=`LineBreakTrivia`,e[e.Trivia=15]=`Trivia`,e[e.Unknown=16]=`Unknown`,e[e.EOF=17]=`EOF`})(Oi||={});const ki=parseTree$1,Ai=findNodeAtLocation$1;var ji;(function(e){e[e.InvalidSymbol=1]=`InvalidSymbol`,e[e.InvalidNumberFormat=2]=`InvalidNumberFormat`,e[e.PropertyNameExpected=3]=`PropertyNameExpected`,e[e.ValueExpected=4]=`ValueExpected`,e[e.ColonExpected=5]=`ColonExpected`,e[e.CommaExpected=6]=`CommaExpected`,e[e.CloseBraceExpected=7]=`CloseBraceExpected`,e[e.CloseBracketExpected=8]=`CloseBracketExpected`,e[e.EndOfFileExpected=9]=`EndOfFileExpected`,e[e.InvalidCommentToken=10]=`InvalidCommentToken`,e[e.UnexpectedEndOfComment=11]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=12]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=13]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=14]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=15]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=16]=`InvalidCharacter`})(ji||={});function indentUnit(e){let t=/\n([ \t]+)\S/.exec(e);return t?t[1]:` `}function jsonAt(e,t,n){return JSON.stringify(e,null,t).replaceAll(`
|
|
474
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.
|
|
474
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.3.0`,description:Ni.description,format:e}).default({description:"Report whether this repository's harness bridges resolve (the same as `doctor`).",options:Qr.options,async run(e){let{bin:t,...n}=await diagnoseReport({root:e.root,harness:e.harness});return result({...homeHeader({description:Ni.description}),...n})}}).command(t))}const Pi={reference:[`load`,`create`,`delete`],governance:[`show`]};function defaultFormat(e){let t=e.slice(2);if(t.some(e=>e===`--format`||e.startsWith(`--format=`)))return`toon`;let[n,r]=t;return n!==void 0&&r!==void 0&&Pi[n]?.includes(r)?`text`:`toon`}async function run(e){try{let t=await app(defaultFormat(e)).parse(e);return typeof t==`number`?t:j.success}catch(e){return process.stderr.write(`error: ${e instanceof Error?e.message:`Invalid command.`}\n`),j.usage}}process.exitCode=await run([...process.argv.slice(0,2),`doctor`,...process.argv.slice(2)]);export{};
|
package/skills/enhance/SKILL.md
CHANGED
|
@@ -14,11 +14,11 @@ Every addition is **offered, never written on sight**. An addition asserts somet
|
|
|
14
14
|
|
|
15
15
|
Four additions ship today: `references/delegation.md`, `references/list-identifiers.md`, `references/session-scope.md`, and `references/references-as-links.md`. Classify, offer, and report each one on its own; the owner's answer to one decides nothing about the others. Each addition's reference file carries the text to offer, a `## Covered when` criterion for the subject already being present, a `## Stale when` section naming the history file its stale check reads and what else that check needs for this addition, and a `## Where it belongs` recommendation naming which instruction file the text should go in. The retired wordings themselves are kept beside it — `references/<addition>.history.md` — and are what a present section is compared against.
|
|
16
16
|
|
|
17
|
-
Beside the additions, it offers the two **agent-worktree settings** in `references/agent-worktrees.md`: lines in `.gitignore`, a tool config, and `.claude/settings.json` rather than prose in `AGENTS.md`. Step 6 is theirs,
|
|
17
|
+
Beside the additions, it offers the two **agent-worktree settings** in `references/agent-worktrees.md`: lines in `.gitignore`, a tool config, and `.claude/settings.json` rather than prose in `AGENTS.md`. Step 6 is theirs. Beside the owner's global file, `~/.agents/AGENTS.md`, written only where the owner picks it (step 5), they are the only writes this skill makes outside the root `AGENTS.md`.
|
|
18
18
|
|
|
19
19
|
## 1. Find the instruction file
|
|
20
20
|
|
|
21
|
-
Where the invocation names `--root <dir>`, that directory is the repository: read and write inside it only. Otherwise locate the Git repository root. The target is the root `AGENTS.md`.
|
|
21
|
+
Where the invocation names `--root <dir>`, that directory is the repository: read and write inside it only, apart from `~/.agents/AGENTS.md` as step 5 allows. Otherwise locate the Git repository root. The target is the root `AGENTS.md`.
|
|
22
22
|
|
|
23
23
|
If there is no root `AGENTS.md`, stop and say so. This skill adds to an existing file; creating one is `init`'s job, so point there and write nothing. Still report in full as step 7 says — what you read, that nothing was written — with **not judged** as each addition's verdict.
|
|
24
24
|
|
|
@@ -91,7 +91,7 @@ Once both answers are yes, offer the replacement. Do **not** then read the secti
|
|
|
91
91
|
|
|
92
92
|
**Where your instructions from outside the repository cover the subject in another form, classify that copy.** An uncovered addition whose subject they cover — judged by meaning, as above — is not absent. Ask the second question of that **global copy**, against the same texts and in the same way as of a repository section, and offer nothing for the repository whatever it answers. Its verdict is the one a repository section would get, placed **in your instructions from outside the repository**: from a retired wording, the owner's own, or you cannot tell. Say, as for already global, that a copy in the root `AGENTS.md` would add the team and nothing else, that the owner would then read the subject twice, and that they can ask for it.
|
|
93
93
|
|
|
94
|
-
Classify a global copy the same way whatever the repository holds — a covered or already-current section included — and
|
|
94
|
+
Classify a global copy the same way whatever the repository holds — a covered or already-current section included — and offer the current text in place of one from a retired wording (step 4).
|
|
95
95
|
|
|
96
96
|
Each addition ends this step in one of six states:
|
|
97
97
|
|
|
@@ -118,7 +118,7 @@ The global file is `~/.agents/AGENTS.md`, the user-scope counterpart of the root
|
|
|
118
118
|
node scripts/doctor.mjs
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
That path is relative to this skill's own directory; fall back to `npx -y buddy-agent-harness@1.
|
|
121
|
+
That path is relative to this skill's own directory; fall back to `npx -y buddy-agent-harness@1.3.0 doctor` when it is missing or cannot be run. Read the `globalInstructions` row for the harness you are running in:
|
|
122
122
|
|
|
123
123
|
- **`ok`** — say that this harness loads `~/.agents/AGENTS.md`, through the file the row names.
|
|
124
124
|
- **`missing` or `unbridged`** — say that it does not yet, so the text would reach no session of this harness until it does. Name the file the row gives, and hand over the step that bridges it beside the text: `references/global-instructions.md` names it per harness, and where `doctor` reported a finding for that file, its `help` row carries the same step. Where the row is `unbridged` and the step is a symlink, say that what the file holds moves into `~/.agents/AGENTS.md` first.
|
|
@@ -128,7 +128,7 @@ That path is relative to this skill's own directory; fall back to `npx -y buddy-
|
|
|
128
128
|
|
|
129
129
|
Name both destinations and what each buys — the global file reaches every repository the owner opens and nobody else; the project file reaches everyone who clones it, at a copy per repository.
|
|
130
130
|
|
|
131
|
-
**
|
|
131
|
+
**Ask which destination the owner wants, and write the one they pick.** Put the recommended destination first and the other as the alternative, and ask for one or neither. Either is written on that yes and neither without it: the root `AGENTS.md`, or the end of `~/.agents/AGENTS.md` (step 5). The owner's choice of the other destination is an approval of that one, not a decline. A bridging step from the `doctor` row above stays handed over: it changes a harness's own user-scope file, which this skill does not write.
|
|
132
132
|
|
|
133
133
|
Where an addition came from a **retired wording**, show the section as it stands in `AGENTS.md`, then the current text **verbatim**, say that it would replace that section and nothing else, and ask.
|
|
134
134
|
|
|
@@ -136,7 +136,7 @@ Where an addition came from a **retired wording**, show the section as it stands
|
|
|
136
136
|
|
|
137
137
|
**Where your instructions from outside the repository carry the current text, put removal beside the replacement.** Offer the replacement as above, and say that it would have the owner read the text twice, once from each place. Then offer removing the section as the other answer: name every paragraph of the owner's it would take, and say the team's agents would no longer read it. This holds however the section was placed — by the comparison, by the owner's answer, or by measurement.
|
|
138
138
|
|
|
139
|
-
Where a **global copy** came from a retired wording — whatever the repository holds, a current section included — show it as it stands, then the current text **verbatim**, name the file that holds it where you can see it, and say the current text would replace that section there. Name the owner's paragraphs it would take, as for a replacement.
|
|
139
|
+
Where a **global copy** came from a retired wording — whatever the repository holds, a current section included — show it as it stands, then the current text **verbatim**, name the file that holds it where you can see it, and say the current text would replace that section there. Name the owner's paragraphs it would take, as for a replacement. Where that file is `~/.agents/AGENTS.md`, offer the replacement and ask, as for one in the root `AGENTS.md`; it is written only on a yes (step 5). Where it is any other file — a harness's own user-scope file — or one you cannot see, hand the current text over for it instead and say so: this skill writes no other file outside the repository. Where a global copy cannot be placed, put it to the owner exactly as below; an answer that it came from here, or a measurement that puts the current text ahead, offers the current text as that replacement.
|
|
140
140
|
|
|
141
141
|
Where you **cannot tell**, say that first, in those words. Then show three texts — the section as it stands, the retired wording it partly tracks, and the current text — and put three answers to the owner:
|
|
142
142
|
|
|
@@ -161,9 +161,9 @@ Do not argue for any of it past one sentence. The user is reading the actual tex
|
|
|
161
161
|
|
|
162
162
|
On approval of an **addition** for the repository, or a request for the team copy of an **already global** one, write the section into the root `AGENTS.md` at the end of the owner's prose, outside the managed region, preserving the surrounding file exactly.
|
|
163
163
|
|
|
164
|
-
On approval of
|
|
164
|
+
On approval of the **global destination**, write the section at the end of `~/.agents/AGENTS.md`, creating the file where it does not exist, and leave every other byte of it as it was. The repository's `AGENTS.md` is left as it stands. Where the harness in use does not load that file yet, hand over the step that bridges it; that step is never written here.
|
|
165
165
|
|
|
166
|
-
On approval of a **replacement
|
|
166
|
+
On approval of a **replacement** — in the root `AGENTS.md`, or of a global copy in `~/.agents/AGENTS.md` — replace that one section in place — from its heading through to the next heading of the same or higher level — and leave every other byte of the file as it was. Do not relocate it, do not reformat around it, and do not touch the managed region.
|
|
167
167
|
|
|
168
168
|
Strip the fence when you write. The ``` markers around the addition in its reference file are there so you can see where the text begins and ends; the section goes into `AGENTS.md` as prose, not as a code block.
|
|
169
169
|
|
|
@@ -179,7 +179,7 @@ These are settings, not additions. They have no history file, no global destinat
|
|
|
179
179
|
|
|
180
180
|
## 7. Report
|
|
181
181
|
|
|
182
|
-
Report every run, whichever way it went: what you read, the verdict for each addition and why — already global, already current, absent, the owner's own, from a retired wording, undecidable, or not judged — what you offered, which destination you recommended, and what was written. For an already-current addition your instructions from outside the repository also carry, say the owner reads it twice. Wherever those instructions cover an addition's subject, name that copy and its verdict. A hand-
|
|
182
|
+
Report every run, whichever way it went: what you read, the verdict for each addition and why — already global, already current, absent, the owner's own, from a retired wording, undecidable, or not judged — what you offered, which destination you recommended, and what was written, naming the file each write went to. For an already-current addition your instructions from outside the repository also carry, say the owner reads it twice. Wherever those instructions cover an addition's subject, name that copy and its verdict. A hand-over — a bridging step, or the current text for a global copy in a file this skill does not write — is an outcome, not a decline; say so. Report each agent-worktree offer as written, declined, or already covered, naming the file. A run that offers nothing still reports — that is the only way the user can tell "already covered" from "did not look".
|
|
183
183
|
|
|
184
184
|
## Rules
|
|
185
185
|
|
|
@@ -190,5 +190,6 @@ Report every run, whichever way it went: what you read, the verdict for each add
|
|
|
190
190
|
- **Never run an evaluation unasked.** It costs the owner many model runs. Offer it; wait.
|
|
191
191
|
- **Never edit an addition to fit a repository.** The wording is fixed. Offer it as written or not at all.
|
|
192
192
|
- **Name `~/.agents/AGENTS.md` as the global file.** Name a harness's own user-scope file only where that harness documents it.
|
|
193
|
-
- **Never touch the managed region**, a nested `AGENTS.md`, or any file other than the root `AGENTS.md` — except the agent-worktree lines in step 6, as approved.
|
|
193
|
+
- **Never touch the managed region**, a nested `AGENTS.md`, or any file other than the root `AGENTS.md` — except `~/.agents/AGENTS.md` in step 5 and the agent-worktree lines in step 6, each as approved.
|
|
194
|
+
- **Never write a harness's own user-scope file**, such as `~/.claude/CLAUDE.md`. A step for it is handed over.
|
|
194
195
|
- Local agent configuration only. Do not change workflows, repository settings, or unrelated project files; the agent-worktree lines are the one exception.
|
|
@@ -51,7 +51,7 @@ Offer this repository's `AGENTS.md` as the alternative, and name what each one b
|
|
|
51
51
|
|
|
52
52
|
The project file is the right pick for one reason and it is worth stating: a team's agents read the repository, not the maintainer's home directory. Where the owner wants the guidance to reach contributors, `AGENTS.md` is the only lever, and the duplicate copy is what that costs.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
Ask which of the two the owner wants, leading with the global file. Where they pick it, write the text at the end of `~/.agents/AGENTS.md` on that yes, as step 5 of the skill says; never without it.
|
|
55
55
|
|
|
56
56
|
## Do not edit it
|
|
57
57
|
|
|
@@ -49,8 +49,8 @@ Offer this repository's `AGENTS.md` as the alternative, and name what each one b
|
|
|
49
49
|
| the owner's global file | every repository they open, themselves only | one copy, and nothing to keep in step |
|
|
50
50
|
| this repository's `AGENTS.md` | everyone who clones it, and any agent CI runs | a copy per repository, and a second copy in context for anyone who already has it globally |
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
of that
|
|
52
|
+
Ask which of the two the owner wants, leading with the global file. Where they pick it, write the
|
|
53
|
+
text at the end of `~/.agents/AGENTS.md` on that yes, as step 5 of the skill says; never without it.
|
|
54
54
|
|
|
55
55
|
## Do not edit it
|
|
56
56
|
|
|
@@ -59,8 +59,8 @@ Offer this repository's `AGENTS.md` as the alternative, and name what each one b
|
|
|
59
59
|
| the owner's global file | every repository they open, themselves only | one copy, and nothing to keep in step |
|
|
60
60
|
| this repository's `AGENTS.md` | everyone who clones it, and any agent CI runs | a copy per repository, and a second copy in context for anyone who already has it globally |
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
of that
|
|
62
|
+
Ask which of the two the owner wants, leading with the global file. Where they pick it, write the
|
|
63
|
+
text at the end of `~/.agents/AGENTS.md` on that yes, as step 5 of the skill says; never without it.
|
|
64
64
|
|
|
65
65
|
## Do not edit it
|
|
66
66
|
|
|
@@ -58,8 +58,8 @@ Offer this repository's `AGENTS.md` as the alternative, and name what each one b
|
|
|
58
58
|
| the owner's global file | every repository they open, themselves only | one copy, and nothing to keep in step |
|
|
59
59
|
| this repository's `AGENTS.md` | everyone who clones it, and any agent CI runs | a copy per repository, and a second copy in context for anyone who already has it globally |
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
of that
|
|
61
|
+
Ask which of the two the owner wants, leading with the global file. Where they pick it, write the
|
|
62
|
+
text at the end of `~/.agents/AGENTS.md` on that yes, as step 5 of the skill says; never without it.
|
|
63
63
|
|
|
64
64
|
## Do not edit it
|
|
65
65
|
|
|
@@ -471,4 +471,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
471
471
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
472
472
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
473
473
|
`+` `.repeat(t));var Ei;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Ei||={});function parseTree$1(e,t=[],n=Ei.DEFAULT){let r={type:`array`,offset:-1,length:-1,children:[],parent:void 0};function ensurePropertyComplete(e){r.type===`property`&&(r.length=e-r.offset,r=r.parent)}function onValue(e){return r.children.push(e),e}visit(e,{onObjectBegin:e=>{r=onValue({type:`object`,offset:e,length:-1,parent:r,children:[]})},onObjectProperty:(e,t,n)=>{r=onValue({type:`property`,offset:t,length:-1,parent:r,children:[]}),r.children.push({type:`string`,value:e,offset:t,length:n,parent:r})},onObjectEnd:(e,t)=>{ensurePropertyComplete(e+t),r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onArrayBegin:(e,t)=>{r=onValue({type:`array`,offset:e,length:-1,parent:r,children:[]})},onArrayEnd:(e,t)=>{r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onLiteralValue:(e,t,n)=>{onValue({type:getNodeType(e),offset:t,length:n,parent:r,value:e}),ensurePropertyComplete(t+n)},onSeparator:(e,t,n)=>{r.type===`property`&&(e===`:`?r.colonOffset=t:e===`,`&&ensurePropertyComplete(t))},onError:(e,n,r)=>{t.push({error:e,offset:n,length:r})}},n);let i=r.children[0];return i&&delete i.parent,i}__name(parseTree$1,`parseTree`);function findNodeAtLocation$1(e,t){if(!e)return;let n=e;for(let e of t)if(typeof e==`string`){if(n.type!==`object`||!Array.isArray(n.children))return;let t=!1;for(let r of n.children)if(Array.isArray(r.children)&&r.children[0].value===e&&r.children.length===2){n=r.children[1],t=!0;break}if(!t)return}else{let t=e;if(n.type!==`array`||t<0||!Array.isArray(n.children)||t>=n.children.length)return;n=n.children[t]}return n}__name(findNodeAtLocation$1,`findNodeAtLocation`);function visit(e,t,n=Ei.DEFAULT){let r=createScanner(e,!1),i=[],a=0;function toNoArgVisit(e){return e?()=>a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisit(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisitWithPath(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice()):()=>!0}function toBeginVisit(e){return e?()=>{a>0?a++:e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice())===!1&&(a=1)}:()=>!0}function toEndVisit(e){return e?()=>{a>0&&a--,a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter())}:()=>!0}let o=toBeginVisit(t.onObjectBegin),s=toOneArgVisitWithPath(t.onObjectProperty),c=toEndVisit(t.onObjectEnd),l=toBeginVisit(t.onArrayBegin),u=toEndVisit(t.onArrayEnd),d=toOneArgVisitWithPath(t.onLiteralValue),f=toOneArgVisit(t.onSeparator),p=toNoArgVisit(t.onComment),m=toOneArgVisit(t.onError),h=n&&n.disallowComments,g=n&&n.allowTrailingComma;function scanNext(){for(;;){let e=r.scan();switch(r.getTokenError()){case 4:handleError(14);break;case 5:handleError(15);break;case 3:handleError(13);break;case 1:h||handleError(11);break;case 2:handleError(12);break;case 6:handleError(16)}switch(e){case 12:case 13:h?handleError(10):p();break;case 16:handleError(1);break;case 15:case 14:break;default:return e}}}function handleError(e,t=[],n=[]){if(m(e),t.length+n.length>0){let e=r.getToken();for(;e!==17;){if(t.indexOf(e)!==-1){scanNext();break}if(n.indexOf(e)!==-1)break;e=scanNext()}}}function parseString(e){let t=r.getTokenValue();return e?d(t):(s(t),i.push(t)),scanNext(),!0}function parseLiteral(){switch(r.getToken()){case 11:let e=r.getTokenValue(),t=Number(e);isNaN(t)&&(handleError(2),t=0),d(t);break;case 7:d(null);break;case 8:d(!0);break;case 9:d(!1);break;default:return!1}return scanNext(),!0}function parseProperty(){return r.getToken()===10?(parseString(!1),r.getToken()===6?(f(`:`),scanNext(),parseValue()||handleError(4,[],[2,5])):handleError(5,[],[2,5]),i.pop(),!0):(handleError(3,[],[2,5]),!1)}function parseObject(){o(),scanNext();let e=!1;for(;r.getToken()!==2&&r.getToken()!==17;){if(r.getToken()===5){if(e||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===2&&g)break}else e&&handleError(6,[],[]);parseProperty()||handleError(4,[],[2,5]),e=!0}return c(),r.getToken()===2?scanNext():handleError(7,[2],[]),!0}function parseArray(){l(),scanNext();let e=!0,t=!1;for(;r.getToken()!==4&&r.getToken()!==17;){if(r.getToken()===5){if(t||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===4&&g)break}else t&&handleError(6,[],[]);e?(i.push(0),e=!1):i[i.length-1]++,parseValue()||handleError(4,[],[4,5]),t=!0}return u(),e||i.pop(),r.getToken()===4?scanNext():handleError(8,[4],[]),!0}function parseValue(){switch(r.getToken()){case 3:return parseArray();case 1:return parseObject();case 10:return parseString(!0);default:return parseLiteral()}}return scanNext(),r.getToken()===17?n.allowEmptyContent?!0:(handleError(4,[],[]),!1):parseValue()?(r.getToken()!==17&&handleError(9,[],[]),!0):(handleError(4,[],[]),!1)}function getNodeType(e){switch(typeof e){case`boolean`:return`boolean`;case`number`:return`number`;case`string`:return`string`;case`object`:return e?Array.isArray(e)?`array`:`object`:`null`;default:return`null`}}var Di;(function(e){e[e.None=0]=`None`,e[e.UnexpectedEndOfComment=1]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=2]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=3]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=4]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=5]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=6]=`InvalidCharacter`})(Di||={});var Oi;(function(e){e[e.OpenBraceToken=1]=`OpenBraceToken`,e[e.CloseBraceToken=2]=`CloseBraceToken`,e[e.OpenBracketToken=3]=`OpenBracketToken`,e[e.CloseBracketToken=4]=`CloseBracketToken`,e[e.CommaToken=5]=`CommaToken`,e[e.ColonToken=6]=`ColonToken`,e[e.NullKeyword=7]=`NullKeyword`,e[e.TrueKeyword=8]=`TrueKeyword`,e[e.FalseKeyword=9]=`FalseKeyword`,e[e.StringLiteral=10]=`StringLiteral`,e[e.NumericLiteral=11]=`NumericLiteral`,e[e.LineCommentTrivia=12]=`LineCommentTrivia`,e[e.BlockCommentTrivia=13]=`BlockCommentTrivia`,e[e.LineBreakTrivia=14]=`LineBreakTrivia`,e[e.Trivia=15]=`Trivia`,e[e.Unknown=16]=`Unknown`,e[e.EOF=17]=`EOF`})(Oi||={});const ki=parseTree$1,Ai=findNodeAtLocation$1;var ji;(function(e){e[e.InvalidSymbol=1]=`InvalidSymbol`,e[e.InvalidNumberFormat=2]=`InvalidNumberFormat`,e[e.PropertyNameExpected=3]=`PropertyNameExpected`,e[e.ValueExpected=4]=`ValueExpected`,e[e.ColonExpected=5]=`ColonExpected`,e[e.CommaExpected=6]=`CommaExpected`,e[e.CloseBraceExpected=7]=`CloseBraceExpected`,e[e.CloseBracketExpected=8]=`CloseBracketExpected`,e[e.EndOfFileExpected=9]=`EndOfFileExpected`,e[e.InvalidCommentToken=10]=`InvalidCommentToken`,e[e.UnexpectedEndOfComment=11]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=12]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=13]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=14]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=15]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=16]=`InvalidCharacter`})(ji||={});function indentUnit(e){let t=/\n([ \t]+)\S/.exec(e);return t?t[1]:` `}function jsonAt(e,t,n){return JSON.stringify(e,null,t).replaceAll(`
|
|
474
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.
|
|
474
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.3.0`,description:Ni.description,format:e}).default({description:"Report whether this repository's harness bridges resolve (the same as `doctor`).",options:Qr.options,async run(e){let{bin:t,...n}=await diagnoseReport({root:e.root,harness:e.harness});return result({...homeHeader({description:Ni.description}),...n})}}).command(t))}const Pi={reference:[`load`,`create`,`delete`],governance:[`show`]};function defaultFormat(e){let t=e.slice(2);if(t.some(e=>e===`--format`||e.startsWith(`--format=`)))return`toon`;let[n,r]=t;return n!==void 0&&r!==void 0&&Pi[n]?.includes(r)?`text`:`toon`}async function run(e){try{let t=await app(defaultFormat(e)).parse(e);return typeof t==`number`?t:j.success}catch(e){return process.stderr.write(`error: ${e instanceof Error?e.message:`Invalid command.`}\n`),j.usage}}process.exitCode=await run([...process.argv.slice(0,2),`doctor`,...process.argv.slice(2)]);export{};
|
|
@@ -86,7 +86,7 @@ A `CLAUDE.local.md` is its own case. It is personal, usually gitignored, and it
|
|
|
86
86
|
2. Move approved skills and commands to `.agents/skills/<name>/SKILL.md`, preferring `git mv` so history follows. Fix frontmatter per `references/frontmatter.md` — this is where cross-harness portability is won or lost.
|
|
87
87
|
3. Merge approved instruction content into `AGENTS.md`, preserving the author's wording. Append; do not restructure. Replace a harness file with a pointer only where approved. Content only some tasks need belongs in a skill, not in `AGENTS.md`.
|
|
88
88
|
4. Apply the conversions that were approved, one at a time, and nothing else from the list. A rule approved as a skill moves to `.agents/skills/<name>/SKILL.md` with frontmatter per `references/frontmatter.md`; instruction content approved for consolidation is appended as in the step above and its harness file left as a generated copy — except a `CLAUDE.md`, which is removed or left holding the import, because a copy under that name is read instead of `AGENTS.md`. Everything declined, and everything with no candidate, stays exactly where it is.
|
|
89
|
-
5. Run `node scripts/init.mjs` at the repository root, resolving that path against this skill's own directory: `scripts/init.mjs` is a self-contained bundle shipped inside this skill folder through the npm package, so it runs with nothing downloaded and no `node_modules` needed. Fall back to `npx -y buddy-agent-harness@1.
|
|
89
|
+
5. Run `node scripts/init.mjs` at the repository root, resolving that path against this skill's own directory: `scripts/init.mjs` is a self-contained bundle shipped inside this skill folder through the npm package, so it runs with nothing downloaded and no `node_modules` needed. Fall back to `npx -y buddy-agent-harness@1.3.0 init` when `scripts/init.mjs` is missing or cannot be run — the case for a skill installed from git rather than from the npm package, which does not carry the bundle. It links `.agents/skills` into the harnesses that need it and skips those that read it natively. Add harnesses the user names with `--harness codex,gemini-cli`.
|
|
90
90
|
6. If the command reports a conflict, resolve the named target and retry. Pass the target to `--force` — `--force .claude/skills` replaces that projection and reports any other conflict untouched, where a bare `--force` replaces every conflicting target at once. Reach for the bare form only when replacing all of them is what you came for. Use `--copy` only where links are unavailable — a copy is a snapshot, not a live projection, so say so when you fall back.
|
|
91
91
|
7. Apply the instruction work `init` does not do. Gemini CLI needs `AGENTS.md` added to `context.fileName` in `.gemini/settings.json` — the only bridge left, detailed in `references/harnesses/gemini-cli.md`, and only if you enabled it. Then remove each superseded `CLAUDE.md` the user approved, in every directory it was found in, and no others. Write no `CLAUDE.md` anywhere, at the root or beside a nested `AGENTS.md`: Claude Code reads both by itself, and a stub written beside one is the very file that would suppress it if anyone later put content in it.
|
|
92
92
|
8. Apply the agent-worktree lines the owner approved, per `references/agent-worktrees.md`, and nothing they declined.
|
|
@@ -471,4 +471,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
471
471
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
472
472
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
473
473
|
`+` `.repeat(t));var Ei;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Ei||={});function parseTree$1(e,t=[],n=Ei.DEFAULT){let r={type:`array`,offset:-1,length:-1,children:[],parent:void 0};function ensurePropertyComplete(e){r.type===`property`&&(r.length=e-r.offset,r=r.parent)}function onValue(e){return r.children.push(e),e}visit(e,{onObjectBegin:e=>{r=onValue({type:`object`,offset:e,length:-1,parent:r,children:[]})},onObjectProperty:(e,t,n)=>{r=onValue({type:`property`,offset:t,length:-1,parent:r,children:[]}),r.children.push({type:`string`,value:e,offset:t,length:n,parent:r})},onObjectEnd:(e,t)=>{ensurePropertyComplete(e+t),r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onArrayBegin:(e,t)=>{r=onValue({type:`array`,offset:e,length:-1,parent:r,children:[]})},onArrayEnd:(e,t)=>{r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onLiteralValue:(e,t,n)=>{onValue({type:getNodeType(e),offset:t,length:n,parent:r,value:e}),ensurePropertyComplete(t+n)},onSeparator:(e,t,n)=>{r.type===`property`&&(e===`:`?r.colonOffset=t:e===`,`&&ensurePropertyComplete(t))},onError:(e,n,r)=>{t.push({error:e,offset:n,length:r})}},n);let i=r.children[0];return i&&delete i.parent,i}__name(parseTree$1,`parseTree`);function findNodeAtLocation$1(e,t){if(!e)return;let n=e;for(let e of t)if(typeof e==`string`){if(n.type!==`object`||!Array.isArray(n.children))return;let t=!1;for(let r of n.children)if(Array.isArray(r.children)&&r.children[0].value===e&&r.children.length===2){n=r.children[1],t=!0;break}if(!t)return}else{let t=e;if(n.type!==`array`||t<0||!Array.isArray(n.children)||t>=n.children.length)return;n=n.children[t]}return n}__name(findNodeAtLocation$1,`findNodeAtLocation`);function visit(e,t,n=Ei.DEFAULT){let r=createScanner(e,!1),i=[],a=0;function toNoArgVisit(e){return e?()=>a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisit(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisitWithPath(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice()):()=>!0}function toBeginVisit(e){return e?()=>{a>0?a++:e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice())===!1&&(a=1)}:()=>!0}function toEndVisit(e){return e?()=>{a>0&&a--,a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter())}:()=>!0}let o=toBeginVisit(t.onObjectBegin),s=toOneArgVisitWithPath(t.onObjectProperty),c=toEndVisit(t.onObjectEnd),l=toBeginVisit(t.onArrayBegin),u=toEndVisit(t.onArrayEnd),d=toOneArgVisitWithPath(t.onLiteralValue),f=toOneArgVisit(t.onSeparator),p=toNoArgVisit(t.onComment),m=toOneArgVisit(t.onError),h=n&&n.disallowComments,g=n&&n.allowTrailingComma;function scanNext(){for(;;){let e=r.scan();switch(r.getTokenError()){case 4:handleError(14);break;case 5:handleError(15);break;case 3:handleError(13);break;case 1:h||handleError(11);break;case 2:handleError(12);break;case 6:handleError(16)}switch(e){case 12:case 13:h?handleError(10):p();break;case 16:handleError(1);break;case 15:case 14:break;default:return e}}}function handleError(e,t=[],n=[]){if(m(e),t.length+n.length>0){let e=r.getToken();for(;e!==17;){if(t.indexOf(e)!==-1){scanNext();break}if(n.indexOf(e)!==-1)break;e=scanNext()}}}function parseString(e){let t=r.getTokenValue();return e?d(t):(s(t),i.push(t)),scanNext(),!0}function parseLiteral(){switch(r.getToken()){case 11:let e=r.getTokenValue(),t=Number(e);isNaN(t)&&(handleError(2),t=0),d(t);break;case 7:d(null);break;case 8:d(!0);break;case 9:d(!1);break;default:return!1}return scanNext(),!0}function parseProperty(){return r.getToken()===10?(parseString(!1),r.getToken()===6?(f(`:`),scanNext(),parseValue()||handleError(4,[],[2,5])):handleError(5,[],[2,5]),i.pop(),!0):(handleError(3,[],[2,5]),!1)}function parseObject(){o(),scanNext();let e=!1;for(;r.getToken()!==2&&r.getToken()!==17;){if(r.getToken()===5){if(e||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===2&&g)break}else e&&handleError(6,[],[]);parseProperty()||handleError(4,[],[2,5]),e=!0}return c(),r.getToken()===2?scanNext():handleError(7,[2],[]),!0}function parseArray(){l(),scanNext();let e=!0,t=!1;for(;r.getToken()!==4&&r.getToken()!==17;){if(r.getToken()===5){if(t||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===4&&g)break}else t&&handleError(6,[],[]);e?(i.push(0),e=!1):i[i.length-1]++,parseValue()||handleError(4,[],[4,5]),t=!0}return u(),e||i.pop(),r.getToken()===4?scanNext():handleError(8,[4],[]),!0}function parseValue(){switch(r.getToken()){case 3:return parseArray();case 1:return parseObject();case 10:return parseString(!0);default:return parseLiteral()}}return scanNext(),r.getToken()===17?n.allowEmptyContent?!0:(handleError(4,[],[]),!1):parseValue()?(r.getToken()!==17&&handleError(9,[],[]),!0):(handleError(4,[],[]),!1)}function getNodeType(e){switch(typeof e){case`boolean`:return`boolean`;case`number`:return`number`;case`string`:return`string`;case`object`:return e?Array.isArray(e)?`array`:`object`:`null`;default:return`null`}}var Di;(function(e){e[e.None=0]=`None`,e[e.UnexpectedEndOfComment=1]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=2]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=3]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=4]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=5]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=6]=`InvalidCharacter`})(Di||={});var Oi;(function(e){e[e.OpenBraceToken=1]=`OpenBraceToken`,e[e.CloseBraceToken=2]=`CloseBraceToken`,e[e.OpenBracketToken=3]=`OpenBracketToken`,e[e.CloseBracketToken=4]=`CloseBracketToken`,e[e.CommaToken=5]=`CommaToken`,e[e.ColonToken=6]=`ColonToken`,e[e.NullKeyword=7]=`NullKeyword`,e[e.TrueKeyword=8]=`TrueKeyword`,e[e.FalseKeyword=9]=`FalseKeyword`,e[e.StringLiteral=10]=`StringLiteral`,e[e.NumericLiteral=11]=`NumericLiteral`,e[e.LineCommentTrivia=12]=`LineCommentTrivia`,e[e.BlockCommentTrivia=13]=`BlockCommentTrivia`,e[e.LineBreakTrivia=14]=`LineBreakTrivia`,e[e.Trivia=15]=`Trivia`,e[e.Unknown=16]=`Unknown`,e[e.EOF=17]=`EOF`})(Oi||={});const ki=parseTree$1,Ai=findNodeAtLocation$1;var ji;(function(e){e[e.InvalidSymbol=1]=`InvalidSymbol`,e[e.InvalidNumberFormat=2]=`InvalidNumberFormat`,e[e.PropertyNameExpected=3]=`PropertyNameExpected`,e[e.ValueExpected=4]=`ValueExpected`,e[e.ColonExpected=5]=`ColonExpected`,e[e.CommaExpected=6]=`CommaExpected`,e[e.CloseBraceExpected=7]=`CloseBraceExpected`,e[e.CloseBracketExpected=8]=`CloseBracketExpected`,e[e.EndOfFileExpected=9]=`EndOfFileExpected`,e[e.InvalidCommentToken=10]=`InvalidCommentToken`,e[e.UnexpectedEndOfComment=11]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=12]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=13]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=14]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=15]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=16]=`InvalidCharacter`})(ji||={});function indentUnit(e){let t=/\n([ \t]+)\S/.exec(e);return t?t[1]:` `}function jsonAt(e,t,n){return JSON.stringify(e,null,t).replaceAll(`
|
|
474
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.
|
|
474
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.3.0`,description:Ni.description,format:e}).default({description:"Report whether this repository's harness bridges resolve (the same as `doctor`).",options:Qr.options,async run(e){let{bin:t,...n}=await diagnoseReport({root:e.root,harness:e.harness});return result({...homeHeader({description:Ni.description}),...n})}}).command(t))}const Pi={reference:[`load`,`create`,`delete`],governance:[`show`]};function defaultFormat(e){let t=e.slice(2);if(t.some(e=>e===`--format`||e.startsWith(`--format=`)))return`toon`;let[n,r]=t;return n!==void 0&&r!==void 0&&Pi[n]?.includes(r)?`text`:`toon`}async function run(e){try{let t=await app(defaultFormat(e)).parse(e);return typeof t==`number`?t:j.success}catch(e){return process.stderr.write(`error: ${e instanceof Error?e.message:`Invalid command.`}\n`),j.usage}}process.exitCode=await run([...process.argv.slice(0,2),`doctor`,...process.argv.slice(2)]);export{};
|
|
@@ -471,4 +471,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
471
471
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
472
472
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
473
473
|
`+` `.repeat(t));var Ei;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Ei||={});function parseTree$1(e,t=[],n=Ei.DEFAULT){let r={type:`array`,offset:-1,length:-1,children:[],parent:void 0};function ensurePropertyComplete(e){r.type===`property`&&(r.length=e-r.offset,r=r.parent)}function onValue(e){return r.children.push(e),e}visit(e,{onObjectBegin:e=>{r=onValue({type:`object`,offset:e,length:-1,parent:r,children:[]})},onObjectProperty:(e,t,n)=>{r=onValue({type:`property`,offset:t,length:-1,parent:r,children:[]}),r.children.push({type:`string`,value:e,offset:t,length:n,parent:r})},onObjectEnd:(e,t)=>{ensurePropertyComplete(e+t),r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onArrayBegin:(e,t)=>{r=onValue({type:`array`,offset:e,length:-1,parent:r,children:[]})},onArrayEnd:(e,t)=>{r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onLiteralValue:(e,t,n)=>{onValue({type:getNodeType(e),offset:t,length:n,parent:r,value:e}),ensurePropertyComplete(t+n)},onSeparator:(e,t,n)=>{r.type===`property`&&(e===`:`?r.colonOffset=t:e===`,`&&ensurePropertyComplete(t))},onError:(e,n,r)=>{t.push({error:e,offset:n,length:r})}},n);let i=r.children[0];return i&&delete i.parent,i}__name(parseTree$1,`parseTree`);function findNodeAtLocation$1(e,t){if(!e)return;let n=e;for(let e of t)if(typeof e==`string`){if(n.type!==`object`||!Array.isArray(n.children))return;let t=!1;for(let r of n.children)if(Array.isArray(r.children)&&r.children[0].value===e&&r.children.length===2){n=r.children[1],t=!0;break}if(!t)return}else{let t=e;if(n.type!==`array`||t<0||!Array.isArray(n.children)||t>=n.children.length)return;n=n.children[t]}return n}__name(findNodeAtLocation$1,`findNodeAtLocation`);function visit(e,t,n=Ei.DEFAULT){let r=createScanner(e,!1),i=[],a=0;function toNoArgVisit(e){return e?()=>a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisit(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisitWithPath(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice()):()=>!0}function toBeginVisit(e){return e?()=>{a>0?a++:e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice())===!1&&(a=1)}:()=>!0}function toEndVisit(e){return e?()=>{a>0&&a--,a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter())}:()=>!0}let o=toBeginVisit(t.onObjectBegin),s=toOneArgVisitWithPath(t.onObjectProperty),c=toEndVisit(t.onObjectEnd),l=toBeginVisit(t.onArrayBegin),u=toEndVisit(t.onArrayEnd),d=toOneArgVisitWithPath(t.onLiteralValue),f=toOneArgVisit(t.onSeparator),p=toNoArgVisit(t.onComment),m=toOneArgVisit(t.onError),h=n&&n.disallowComments,g=n&&n.allowTrailingComma;function scanNext(){for(;;){let e=r.scan();switch(r.getTokenError()){case 4:handleError(14);break;case 5:handleError(15);break;case 3:handleError(13);break;case 1:h||handleError(11);break;case 2:handleError(12);break;case 6:handleError(16)}switch(e){case 12:case 13:h?handleError(10):p();break;case 16:handleError(1);break;case 15:case 14:break;default:return e}}}function handleError(e,t=[],n=[]){if(m(e),t.length+n.length>0){let e=r.getToken();for(;e!==17;){if(t.indexOf(e)!==-1){scanNext();break}if(n.indexOf(e)!==-1)break;e=scanNext()}}}function parseString(e){let t=r.getTokenValue();return e?d(t):(s(t),i.push(t)),scanNext(),!0}function parseLiteral(){switch(r.getToken()){case 11:let e=r.getTokenValue(),t=Number(e);isNaN(t)&&(handleError(2),t=0),d(t);break;case 7:d(null);break;case 8:d(!0);break;case 9:d(!1);break;default:return!1}return scanNext(),!0}function parseProperty(){return r.getToken()===10?(parseString(!1),r.getToken()===6?(f(`:`),scanNext(),parseValue()||handleError(4,[],[2,5])):handleError(5,[],[2,5]),i.pop(),!0):(handleError(3,[],[2,5]),!1)}function parseObject(){o(),scanNext();let e=!1;for(;r.getToken()!==2&&r.getToken()!==17;){if(r.getToken()===5){if(e||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===2&&g)break}else e&&handleError(6,[],[]);parseProperty()||handleError(4,[],[2,5]),e=!0}return c(),r.getToken()===2?scanNext():handleError(7,[2],[]),!0}function parseArray(){l(),scanNext();let e=!0,t=!1;for(;r.getToken()!==4&&r.getToken()!==17;){if(r.getToken()===5){if(t||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===4&&g)break}else t&&handleError(6,[],[]);e?(i.push(0),e=!1):i[i.length-1]++,parseValue()||handleError(4,[],[4,5]),t=!0}return u(),e||i.pop(),r.getToken()===4?scanNext():handleError(8,[4],[]),!0}function parseValue(){switch(r.getToken()){case 3:return parseArray();case 1:return parseObject();case 10:return parseString(!0);default:return parseLiteral()}}return scanNext(),r.getToken()===17?n.allowEmptyContent?!0:(handleError(4,[],[]),!1):parseValue()?(r.getToken()!==17&&handleError(9,[],[]),!0):(handleError(4,[],[]),!1)}function getNodeType(e){switch(typeof e){case`boolean`:return`boolean`;case`number`:return`number`;case`string`:return`string`;case`object`:return e?Array.isArray(e)?`array`:`object`:`null`;default:return`null`}}var Di;(function(e){e[e.None=0]=`None`,e[e.UnexpectedEndOfComment=1]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=2]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=3]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=4]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=5]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=6]=`InvalidCharacter`})(Di||={});var Oi;(function(e){e[e.OpenBraceToken=1]=`OpenBraceToken`,e[e.CloseBraceToken=2]=`CloseBraceToken`,e[e.OpenBracketToken=3]=`OpenBracketToken`,e[e.CloseBracketToken=4]=`CloseBracketToken`,e[e.CommaToken=5]=`CommaToken`,e[e.ColonToken=6]=`ColonToken`,e[e.NullKeyword=7]=`NullKeyword`,e[e.TrueKeyword=8]=`TrueKeyword`,e[e.FalseKeyword=9]=`FalseKeyword`,e[e.StringLiteral=10]=`StringLiteral`,e[e.NumericLiteral=11]=`NumericLiteral`,e[e.LineCommentTrivia=12]=`LineCommentTrivia`,e[e.BlockCommentTrivia=13]=`BlockCommentTrivia`,e[e.LineBreakTrivia=14]=`LineBreakTrivia`,e[e.Trivia=15]=`Trivia`,e[e.Unknown=16]=`Unknown`,e[e.EOF=17]=`EOF`})(Oi||={});const ki=parseTree$1,Ai=findNodeAtLocation$1;var ji;(function(e){e[e.InvalidSymbol=1]=`InvalidSymbol`,e[e.InvalidNumberFormat=2]=`InvalidNumberFormat`,e[e.PropertyNameExpected=3]=`PropertyNameExpected`,e[e.ValueExpected=4]=`ValueExpected`,e[e.ColonExpected=5]=`ColonExpected`,e[e.CommaExpected=6]=`CommaExpected`,e[e.CloseBraceExpected=7]=`CloseBraceExpected`,e[e.CloseBracketExpected=8]=`CloseBracketExpected`,e[e.EndOfFileExpected=9]=`EndOfFileExpected`,e[e.InvalidCommentToken=10]=`InvalidCommentToken`,e[e.UnexpectedEndOfComment=11]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=12]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=13]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=14]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=15]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=16]=`InvalidCharacter`})(ji||={});function indentUnit(e){let t=/\n([ \t]+)\S/.exec(e);return t?t[1]:` `}function jsonAt(e,t,n){return JSON.stringify(e,null,t).replaceAll(`
|
|
474
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.
|
|
474
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.3.0`,description:Ni.description,format:e}).default({description:"Report whether this repository's harness bridges resolve (the same as `doctor`).",options:Qr.options,async run(e){let{bin:t,...n}=await diagnoseReport({root:e.root,harness:e.harness});return result({...homeHeader({description:Ni.description}),...n})}}).command(t))}const Pi={reference:[`load`,`create`,`delete`],governance:[`show`]};function defaultFormat(e){let t=e.slice(2);if(t.some(e=>e===`--format`||e.startsWith(`--format=`)))return`toon`;let[n,r]=t;return n!==void 0&&r!==void 0&&Pi[n]?.includes(r)?`text`:`toon`}async function run(e){try{let t=await app(defaultFormat(e)).parse(e);return typeof t==`number`?t:j.success}catch(e){return process.stderr.write(`error: ${e instanceof Error?e.message:`Invalid command.`}\n`),j.usage}}process.exitCode=await run([...process.argv.slice(0,2),`init`,...process.argv.slice(2)]);export{};
|
package/skills/repair/SKILL.md
CHANGED
|
@@ -17,7 +17,7 @@ Each of the other three refuses this work deliberately. `init` has to run anywhe
|
|
|
17
17
|
node scripts/doctor.mjs
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
That path is relative to this skill's own directory: `scripts/doctor.mjs` is a self-contained bundle shipped inside this skill folder through the npm package, so it runs against the current working directory with nothing downloaded and no `node_modules` needed. Fall back to `npx -y buddy-agent-harness@1.
|
|
20
|
+
That path is relative to this skill's own directory: `scripts/doctor.mjs` is a self-contained bundle shipped inside this skill folder through the npm package, so it runs against the current working directory with nothing downloaded and no `node_modules` needed. Fall back to `npx -y buddy-agent-harness@1.3.0 doctor` when `scripts/doctor.mjs` is missing or cannot be run — the case for a skill installed from git rather than from the npm package, which does not carry the bundle.
|
|
21
21
|
|
|
22
22
|
The default output is TOON, which is what you parse. Each entry in `findings` carries three fields: `problem` (the finding's name), `path` (what it is about), and `detail` (what is wrong, in prose). The repairs are not on the row — they are lifted into `help`, which follows `findings` in order and collapses two entries only where the repair is word-for-word identical. Read a finding's repair off the matching position and confirm it by the `path` it names; where two findings sit at one path, what each repair says to do is what tells them apart.
|
|
23
23
|
|
|
@@ -471,4 +471,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
471
471
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
472
472
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
473
473
|
`+` `.repeat(t));var Ei;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Ei||={});function parseTree$1(e,t=[],n=Ei.DEFAULT){let r={type:`array`,offset:-1,length:-1,children:[],parent:void 0};function ensurePropertyComplete(e){r.type===`property`&&(r.length=e-r.offset,r=r.parent)}function onValue(e){return r.children.push(e),e}visit(e,{onObjectBegin:e=>{r=onValue({type:`object`,offset:e,length:-1,parent:r,children:[]})},onObjectProperty:(e,t,n)=>{r=onValue({type:`property`,offset:t,length:-1,parent:r,children:[]}),r.children.push({type:`string`,value:e,offset:t,length:n,parent:r})},onObjectEnd:(e,t)=>{ensurePropertyComplete(e+t),r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onArrayBegin:(e,t)=>{r=onValue({type:`array`,offset:e,length:-1,parent:r,children:[]})},onArrayEnd:(e,t)=>{r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onLiteralValue:(e,t,n)=>{onValue({type:getNodeType(e),offset:t,length:n,parent:r,value:e}),ensurePropertyComplete(t+n)},onSeparator:(e,t,n)=>{r.type===`property`&&(e===`:`?r.colonOffset=t:e===`,`&&ensurePropertyComplete(t))},onError:(e,n,r)=>{t.push({error:e,offset:n,length:r})}},n);let i=r.children[0];return i&&delete i.parent,i}__name(parseTree$1,`parseTree`);function findNodeAtLocation$1(e,t){if(!e)return;let n=e;for(let e of t)if(typeof e==`string`){if(n.type!==`object`||!Array.isArray(n.children))return;let t=!1;for(let r of n.children)if(Array.isArray(r.children)&&r.children[0].value===e&&r.children.length===2){n=r.children[1],t=!0;break}if(!t)return}else{let t=e;if(n.type!==`array`||t<0||!Array.isArray(n.children)||t>=n.children.length)return;n=n.children[t]}return n}__name(findNodeAtLocation$1,`findNodeAtLocation`);function visit(e,t,n=Ei.DEFAULT){let r=createScanner(e,!1),i=[],a=0;function toNoArgVisit(e){return e?()=>a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisit(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisitWithPath(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice()):()=>!0}function toBeginVisit(e){return e?()=>{a>0?a++:e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice())===!1&&(a=1)}:()=>!0}function toEndVisit(e){return e?()=>{a>0&&a--,a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter())}:()=>!0}let o=toBeginVisit(t.onObjectBegin),s=toOneArgVisitWithPath(t.onObjectProperty),c=toEndVisit(t.onObjectEnd),l=toBeginVisit(t.onArrayBegin),u=toEndVisit(t.onArrayEnd),d=toOneArgVisitWithPath(t.onLiteralValue),f=toOneArgVisit(t.onSeparator),p=toNoArgVisit(t.onComment),m=toOneArgVisit(t.onError),h=n&&n.disallowComments,g=n&&n.allowTrailingComma;function scanNext(){for(;;){let e=r.scan();switch(r.getTokenError()){case 4:handleError(14);break;case 5:handleError(15);break;case 3:handleError(13);break;case 1:h||handleError(11);break;case 2:handleError(12);break;case 6:handleError(16)}switch(e){case 12:case 13:h?handleError(10):p();break;case 16:handleError(1);break;case 15:case 14:break;default:return e}}}function handleError(e,t=[],n=[]){if(m(e),t.length+n.length>0){let e=r.getToken();for(;e!==17;){if(t.indexOf(e)!==-1){scanNext();break}if(n.indexOf(e)!==-1)break;e=scanNext()}}}function parseString(e){let t=r.getTokenValue();return e?d(t):(s(t),i.push(t)),scanNext(),!0}function parseLiteral(){switch(r.getToken()){case 11:let e=r.getTokenValue(),t=Number(e);isNaN(t)&&(handleError(2),t=0),d(t);break;case 7:d(null);break;case 8:d(!0);break;case 9:d(!1);break;default:return!1}return scanNext(),!0}function parseProperty(){return r.getToken()===10?(parseString(!1),r.getToken()===6?(f(`:`),scanNext(),parseValue()||handleError(4,[],[2,5])):handleError(5,[],[2,5]),i.pop(),!0):(handleError(3,[],[2,5]),!1)}function parseObject(){o(),scanNext();let e=!1;for(;r.getToken()!==2&&r.getToken()!==17;){if(r.getToken()===5){if(e||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===2&&g)break}else e&&handleError(6,[],[]);parseProperty()||handleError(4,[],[2,5]),e=!0}return c(),r.getToken()===2?scanNext():handleError(7,[2],[]),!0}function parseArray(){l(),scanNext();let e=!0,t=!1;for(;r.getToken()!==4&&r.getToken()!==17;){if(r.getToken()===5){if(t||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===4&&g)break}else t&&handleError(6,[],[]);e?(i.push(0),e=!1):i[i.length-1]++,parseValue()||handleError(4,[],[4,5]),t=!0}return u(),e||i.pop(),r.getToken()===4?scanNext():handleError(8,[4],[]),!0}function parseValue(){switch(r.getToken()){case 3:return parseArray();case 1:return parseObject();case 10:return parseString(!0);default:return parseLiteral()}}return scanNext(),r.getToken()===17?n.allowEmptyContent?!0:(handleError(4,[],[]),!1):parseValue()?(r.getToken()!==17&&handleError(9,[],[]),!0):(handleError(4,[],[]),!1)}function getNodeType(e){switch(typeof e){case`boolean`:return`boolean`;case`number`:return`number`;case`string`:return`string`;case`object`:return e?Array.isArray(e)?`array`:`object`:`null`;default:return`null`}}var Di;(function(e){e[e.None=0]=`None`,e[e.UnexpectedEndOfComment=1]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=2]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=3]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=4]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=5]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=6]=`InvalidCharacter`})(Di||={});var Oi;(function(e){e[e.OpenBraceToken=1]=`OpenBraceToken`,e[e.CloseBraceToken=2]=`CloseBraceToken`,e[e.OpenBracketToken=3]=`OpenBracketToken`,e[e.CloseBracketToken=4]=`CloseBracketToken`,e[e.CommaToken=5]=`CommaToken`,e[e.ColonToken=6]=`ColonToken`,e[e.NullKeyword=7]=`NullKeyword`,e[e.TrueKeyword=8]=`TrueKeyword`,e[e.FalseKeyword=9]=`FalseKeyword`,e[e.StringLiteral=10]=`StringLiteral`,e[e.NumericLiteral=11]=`NumericLiteral`,e[e.LineCommentTrivia=12]=`LineCommentTrivia`,e[e.BlockCommentTrivia=13]=`BlockCommentTrivia`,e[e.LineBreakTrivia=14]=`LineBreakTrivia`,e[e.Trivia=15]=`Trivia`,e[e.Unknown=16]=`Unknown`,e[e.EOF=17]=`EOF`})(Oi||={});const ki=parseTree$1,Ai=findNodeAtLocation$1;var ji;(function(e){e[e.InvalidSymbol=1]=`InvalidSymbol`,e[e.InvalidNumberFormat=2]=`InvalidNumberFormat`,e[e.PropertyNameExpected=3]=`PropertyNameExpected`,e[e.ValueExpected=4]=`ValueExpected`,e[e.ColonExpected=5]=`ColonExpected`,e[e.CommaExpected=6]=`CommaExpected`,e[e.CloseBraceExpected=7]=`CloseBraceExpected`,e[e.CloseBracketExpected=8]=`CloseBracketExpected`,e[e.EndOfFileExpected=9]=`EndOfFileExpected`,e[e.InvalidCommentToken=10]=`InvalidCommentToken`,e[e.UnexpectedEndOfComment=11]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=12]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=13]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=14]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=15]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=16]=`InvalidCharacter`})(ji||={});function indentUnit(e){let t=/\n([ \t]+)\S/.exec(e);return t?t[1]:` `}function jsonAt(e,t,n){return JSON.stringify(e,null,t).replaceAll(`
|
|
474
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.
|
|
474
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.3.0`,description:Ni.description,format:e}).default({description:"Report whether this repository's harness bridges resolve (the same as `doctor`).",options:Qr.options,async run(e){let{bin:t,...n}=await diagnoseReport({root:e.root,harness:e.harness});return result({...homeHeader({description:Ni.description}),...n})}}).command(t))}const Pi={reference:[`load`,`create`,`delete`],governance:[`show`]};function defaultFormat(e){let t=e.slice(2);if(t.some(e=>e===`--format`||e.startsWith(`--format=`)))return`toon`;let[n,r]=t;return n!==void 0&&r!==void 0&&Pi[n]?.includes(r)?`text`:`toon`}async function run(e){try{let t=await app(defaultFormat(e)).parse(e);return typeof t==`number`?t:j.success}catch(e){return process.stderr.write(`error: ${e instanceof Error?e.message:`Invalid command.`}\n`),j.usage}}process.exitCode=await run([...process.argv.slice(0,2),`doctor`,...process.argv.slice(2)]);export{};
|
|
@@ -471,4 +471,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
471
471
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
472
472
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
473
473
|
`+` `.repeat(t));var Ei;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Ei||={});function parseTree$1(e,t=[],n=Ei.DEFAULT){let r={type:`array`,offset:-1,length:-1,children:[],parent:void 0};function ensurePropertyComplete(e){r.type===`property`&&(r.length=e-r.offset,r=r.parent)}function onValue(e){return r.children.push(e),e}visit(e,{onObjectBegin:e=>{r=onValue({type:`object`,offset:e,length:-1,parent:r,children:[]})},onObjectProperty:(e,t,n)=>{r=onValue({type:`property`,offset:t,length:-1,parent:r,children:[]}),r.children.push({type:`string`,value:e,offset:t,length:n,parent:r})},onObjectEnd:(e,t)=>{ensurePropertyComplete(e+t),r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onArrayBegin:(e,t)=>{r=onValue({type:`array`,offset:e,length:-1,parent:r,children:[]})},onArrayEnd:(e,t)=>{r.length=e+t-r.offset,r=r.parent,ensurePropertyComplete(e+t)},onLiteralValue:(e,t,n)=>{onValue({type:getNodeType(e),offset:t,length:n,parent:r,value:e}),ensurePropertyComplete(t+n)},onSeparator:(e,t,n)=>{r.type===`property`&&(e===`:`?r.colonOffset=t:e===`,`&&ensurePropertyComplete(t))},onError:(e,n,r)=>{t.push({error:e,offset:n,length:r})}},n);let i=r.children[0];return i&&delete i.parent,i}__name(parseTree$1,`parseTree`);function findNodeAtLocation$1(e,t){if(!e)return;let n=e;for(let e of t)if(typeof e==`string`){if(n.type!==`object`||!Array.isArray(n.children))return;let t=!1;for(let r of n.children)if(Array.isArray(r.children)&&r.children[0].value===e&&r.children.length===2){n=r.children[1],t=!0;break}if(!t)return}else{let t=e;if(n.type!==`array`||t<0||!Array.isArray(n.children)||t>=n.children.length)return;n=n.children[t]}return n}__name(findNodeAtLocation$1,`findNodeAtLocation`);function visit(e,t,n=Ei.DEFAULT){let r=createScanner(e,!1),i=[],a=0;function toNoArgVisit(e){return e?()=>a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisit(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter()):()=>!0}function toOneArgVisitWithPath(e){return e?t=>a===0&&e(t,r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice()):()=>!0}function toBeginVisit(e){return e?()=>{a>0?a++:e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter(),()=>i.slice())===!1&&(a=1)}:()=>!0}function toEndVisit(e){return e?()=>{a>0&&a--,a===0&&e(r.getTokenOffset(),r.getTokenLength(),r.getTokenStartLine(),r.getTokenStartCharacter())}:()=>!0}let o=toBeginVisit(t.onObjectBegin),s=toOneArgVisitWithPath(t.onObjectProperty),c=toEndVisit(t.onObjectEnd),l=toBeginVisit(t.onArrayBegin),u=toEndVisit(t.onArrayEnd),d=toOneArgVisitWithPath(t.onLiteralValue),f=toOneArgVisit(t.onSeparator),p=toNoArgVisit(t.onComment),m=toOneArgVisit(t.onError),h=n&&n.disallowComments,g=n&&n.allowTrailingComma;function scanNext(){for(;;){let e=r.scan();switch(r.getTokenError()){case 4:handleError(14);break;case 5:handleError(15);break;case 3:handleError(13);break;case 1:h||handleError(11);break;case 2:handleError(12);break;case 6:handleError(16)}switch(e){case 12:case 13:h?handleError(10):p();break;case 16:handleError(1);break;case 15:case 14:break;default:return e}}}function handleError(e,t=[],n=[]){if(m(e),t.length+n.length>0){let e=r.getToken();for(;e!==17;){if(t.indexOf(e)!==-1){scanNext();break}if(n.indexOf(e)!==-1)break;e=scanNext()}}}function parseString(e){let t=r.getTokenValue();return e?d(t):(s(t),i.push(t)),scanNext(),!0}function parseLiteral(){switch(r.getToken()){case 11:let e=r.getTokenValue(),t=Number(e);isNaN(t)&&(handleError(2),t=0),d(t);break;case 7:d(null);break;case 8:d(!0);break;case 9:d(!1);break;default:return!1}return scanNext(),!0}function parseProperty(){return r.getToken()===10?(parseString(!1),r.getToken()===6?(f(`:`),scanNext(),parseValue()||handleError(4,[],[2,5])):handleError(5,[],[2,5]),i.pop(),!0):(handleError(3,[],[2,5]),!1)}function parseObject(){o(),scanNext();let e=!1;for(;r.getToken()!==2&&r.getToken()!==17;){if(r.getToken()===5){if(e||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===2&&g)break}else e&&handleError(6,[],[]);parseProperty()||handleError(4,[],[2,5]),e=!0}return c(),r.getToken()===2?scanNext():handleError(7,[2],[]),!0}function parseArray(){l(),scanNext();let e=!0,t=!1;for(;r.getToken()!==4&&r.getToken()!==17;){if(r.getToken()===5){if(t||handleError(4,[],[]),f(`,`),scanNext(),r.getToken()===4&&g)break}else t&&handleError(6,[],[]);e?(i.push(0),e=!1):i[i.length-1]++,parseValue()||handleError(4,[],[4,5]),t=!0}return u(),e||i.pop(),r.getToken()===4?scanNext():handleError(8,[4],[]),!0}function parseValue(){switch(r.getToken()){case 3:return parseArray();case 1:return parseObject();case 10:return parseString(!0);default:return parseLiteral()}}return scanNext(),r.getToken()===17?n.allowEmptyContent?!0:(handleError(4,[],[]),!1):parseValue()?(r.getToken()!==17&&handleError(9,[],[]),!0):(handleError(4,[],[]),!1)}function getNodeType(e){switch(typeof e){case`boolean`:return`boolean`;case`number`:return`number`;case`string`:return`string`;case`object`:return e?Array.isArray(e)?`array`:`object`:`null`;default:return`null`}}var Di;(function(e){e[e.None=0]=`None`,e[e.UnexpectedEndOfComment=1]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=2]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=3]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=4]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=5]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=6]=`InvalidCharacter`})(Di||={});var Oi;(function(e){e[e.OpenBraceToken=1]=`OpenBraceToken`,e[e.CloseBraceToken=2]=`CloseBraceToken`,e[e.OpenBracketToken=3]=`OpenBracketToken`,e[e.CloseBracketToken=4]=`CloseBracketToken`,e[e.CommaToken=5]=`CommaToken`,e[e.ColonToken=6]=`ColonToken`,e[e.NullKeyword=7]=`NullKeyword`,e[e.TrueKeyword=8]=`TrueKeyword`,e[e.FalseKeyword=9]=`FalseKeyword`,e[e.StringLiteral=10]=`StringLiteral`,e[e.NumericLiteral=11]=`NumericLiteral`,e[e.LineCommentTrivia=12]=`LineCommentTrivia`,e[e.BlockCommentTrivia=13]=`BlockCommentTrivia`,e[e.LineBreakTrivia=14]=`LineBreakTrivia`,e[e.Trivia=15]=`Trivia`,e[e.Unknown=16]=`Unknown`,e[e.EOF=17]=`EOF`})(Oi||={});const ki=parseTree$1,Ai=findNodeAtLocation$1;var ji;(function(e){e[e.InvalidSymbol=1]=`InvalidSymbol`,e[e.InvalidNumberFormat=2]=`InvalidNumberFormat`,e[e.PropertyNameExpected=3]=`PropertyNameExpected`,e[e.ValueExpected=4]=`ValueExpected`,e[e.ColonExpected=5]=`ColonExpected`,e[e.CommaExpected=6]=`CommaExpected`,e[e.CloseBraceExpected=7]=`CloseBraceExpected`,e[e.CloseBracketExpected=8]=`CloseBracketExpected`,e[e.EndOfFileExpected=9]=`EndOfFileExpected`,e[e.InvalidCommentToken=10]=`InvalidCommentToken`,e[e.UnexpectedEndOfComment=11]=`UnexpectedEndOfComment`,e[e.UnexpectedEndOfString=12]=`UnexpectedEndOfString`,e[e.UnexpectedEndOfNumber=13]=`UnexpectedEndOfNumber`,e[e.InvalidUnicode=14]=`InvalidUnicode`,e[e.InvalidEscapeCharacter=15]=`InvalidEscapeCharacter`,e[e.InvalidCharacter=16]=`InvalidCharacter`})(ji||={});function indentUnit(e){let t=/\n([ \t]+)\S/.exec(e);return t?t[1]:` `}function jsonAt(e,t,n){return JSON.stringify(e,null,t).replaceAll(`
|
|
474
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.
|
|
474
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?Ai(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(ki(e),t);if(!i||Ai(i,[n]))return;let a=t.length+1,o=indentUnit(e),s=`${o.repeat(a)}${JSON.stringify(n)}: ${jsonAt(r,o,a)}`,c=i.children?.at(-1);if(c){let t=c.offset+c.length;return`${e.slice(0,t)},\n${s}${e.slice(t)}`}let l=i.offset+1,u=i.offset+i.length-1;return`${e.slice(0,l)}\n${s}\n${o.repeat(a-1)}${e.slice(u)}`}function replaceJsonValue(e,t,n){let r=ki(e),i=r&&Ai(r,t);if(!i)return{kind:`refused`,reason:`absent`};let a=i.offset+i.length,o=e.slice(i.offset,a);return stripJsonComments(o)===o?{kind:`edited`,text:`${e.slice(0,i.offset)}${jsonAt(n,indentUnit(e),t.length)}${e.slice(a)}`}:{kind:`refused`,reason:`comment`}}function createFile(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):`${JSON.stringify({[e.key]:{[t]:n}},null,2)}\n`}function appendServer(e,t,n,r){return e.format===`toml`?appendTomlTable(t,[e.key,n],r):insertJsonProperty(t,[e.key],n,r)??insertJsonProperty(t,[],e.key,{[n]:r})}function replaceServer(e,t,n,r){let i=e.format===`toml`?replaceTomlTable(t,[e.key,n],r):replaceJsonValue(t,[e.key,n],r);return i.kind===`edited`?i.text:void 0}function renderEntry(e,t,n){return e.format===`toml`?renderTomlTable([e.key,t],n):JSON.stringify({[t]:n},null,2)}function read(e,t){let r=C(e,t);return n(r)?a(r,`utf8`):void 0}const Mi={target:`the target changed since the last projection — reconcile it into the golden set first`,both:`both sides changed since the last projection — a three-way conflict for a person to settle`,unknown:`no baseline says which side changed — compare the two by hand`};function heldBack(e){return[`both`,`unknown`,`target`].find(t=>e.includes(t))}function projectMcp({root:e,git:t,write:n}){let r=parseGoldenSet(read(e,Y));if(r.kind!==`servers`)return r;let i=parseProjectionRecord(read(e,jr)),a=new McpBaseline({git:t,record:i}),o=[],s=[],c=[];for(let t of mcpTargets(e)){let n=read(e,t.path),i=parseTarget(t,n);if(i.kind===`unreadable`){o.push({target:t.path,server:``,action:`refuse`,detail:`the target does not parse`});continue}let l={config:t,source:n,created:n===void 0,servers:i.kind===`servers`?i.servers:new Map,agreed:new Map};c.push(l);let u=Er[t.dialect];for(let[e,n]of r.servers){let row=(n,r)=>o.push({target:t.path,server:e,action:n,detail:r}),r=credentialFields(n)[0];if(r){row(`refuse`,`the golden set holds a literal credential at ${r} — reference it as \${VAR} first`);continue}let i=u.write(n);if(i.kind===`refused`){row(`refuse`,`${i.field}: ${i.reason}`);continue}let c=l.servers.get(e);if(!c){let r=l.source===void 0?createFile(t,e,i.entry):appendServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,r,l.created?`creates the file`:`appends the server`,row,s);continue}let d=divergingFields(n,c,u.fields);if(!d.length){l.agreed.set(e,n);continue}let f=heldBack(d.map(r=>a.directionOf(t,e,r,n,c)));if(f){row(`skip`,Mi[f]);continue}let p=replaceServer(t,l.source,e,i.entry);apply(l,e,n,i.entry,p,`replaces ${d.join(`, `)}`,row,s)}}if(n){for(let t of c)t.source!==void 0&&t.source!==read(e,t.config.path)&&writeTarget(e,t);writeRecord(e,i,c)}return{kind:`planned`,rows:o,entries:s,written:n}}function apply(e,t,n,r,i,a,o,s){let c=!e.servers.has(t),l=i!==void 0&&readsBack(e,t,n,i),u=l?c?`add`:`update`:`edit`;if(s.push({target:e.config.path,server:t,action:u,entry:renderEntry(e.config,t,r)}),!l){o(`edit`,c?`the file has no safe place to add it — apply the entry by hand`:`an in-place change here is applied by hand`);return}e.source=i,e.servers=parseTarget(e.config,i).servers,e.agreed.set(t,n),o(u,a)}function readsBack(e,t,n,r){let i=parseTarget(e.config,r);if(i.kind!==`servers`)return!1;let a=i.servers.get(t),o=Er[e.config.dialect].fields;return!a||divergingFields(n,a,o).length?!1:[...e.servers].every(([e,n])=>{if(e===t)return!0;let r=i.servers.get(e);return r!==void 0&&JSON.stringify(r)===JSON.stringify(n)})}function writeTarget(e,t){let n=C(e,t.config.path);i(b(n),{recursive:!0}),m(n,t.source)}function writeRecord(e,t,n){for(let e of n){let n=t.get(e.config.path)??new Map;for(let[t,r]of e.agreed)n.set(t,r);n.size&&t.set(e.config.path,n)}let r=C(e,jr);i(b(r),{recursive:!0}),m(r,formatProjectionRecord(t))}const Ni={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[ri,Qr,command({name:`mcp`,description:`Work with the golden MCP server set at .agents/buddy-agent-harness/mcp.toml.`,commands:[command({name:`project`,description:`Project the golden MCP server set into each enabled harness. A dry run unless --write; never overwrites a change made on the harness side.`,options:{root:{description:`Repository or package directory. Defaults to the current directory.`,type:N.string().optional()},write:{description:`Apply the plan and record what was projected. Without it nothing on disk changes.`,type:N.boolean().optional()}},examples:[{command:`buddy-agent-harness mcp project`,description:`Preview what would be projected`},{command:`buddy-agent-harness mcp project --write`,description:`Project the golden set into each harness`}],run(e){try{let t=e.root??process.cwd(),n=e.write===!0,r=projectMcp({root:t,git:new GitBridgeState(t),write:n});if(r.kind===`unreadable`){let e=r.position?` at line ${r.position.line}, column ${r.position.column}`:``;throw new M(`${Y} does not parse${e}.`,{code:`golden-set-unreadable`,help:`Fix the TOML syntax in ${Y}, then re-run.`})}return r.kind===`absent`?empty(`0 servers — no golden set at ${Y}`,{help:[`Create ${Y} with the MCP servers to project.`,ref(`mcp project`,{},`preview the projection`)]}):result({golden:Y,mode:n?`written`:`dry run — nothing written; re-run with --write to apply`,actions:r.rows.length?r.rows:`0 changes — every enabled harness already carries the golden set`,...r.entries.length?{entries:r.entries}:{},record:n?jr:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),wi]}),$n,qr,ni]};function app(e){let[t,...n]=Ni.commands;return n.reduce((e,t)=>e.command(t),cli({name:Ni.name,version:`1.3.0`,description:Ni.description,format:e}).default({description:"Report whether this repository's harness bridges resolve (the same as `doctor`).",options:Qr.options,async run(e){let{bin:t,...n}=await diagnoseReport({root:e.root,harness:e.harness});return result({...homeHeader({description:Ni.description}),...n})}}).command(t))}const Pi={reference:[`load`,`create`,`delete`],governance:[`show`]};function defaultFormat(e){let t=e.slice(2);if(t.some(e=>e===`--format`||e.startsWith(`--format=`)))return`toon`;let[n,r]=t;return n!==void 0&&r!==void 0&&Pi[n]?.includes(r)?`text`:`toon`}async function run(e){try{let t=await app(defaultFormat(e)).parse(e);return typeof t==`number`?t:j.success}catch(e){return process.stderr.write(`error: ${e instanceof Error?e.message:`Invalid command.`}\n`),j.usage}}process.exitCode=await run([...process.argv.slice(0,2),`mcp`,...process.argv.slice(2)]);export{};
|