buddy-agent-harness 1.0.0 → 1.0.1
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 +1 -1
- package/skills/enhance/scripts/doctor.mjs +1 -1
- package/skills/init-buddy-agent-harness/SKILL.md +2 -2
- package/skills/init-buddy-agent-harness/references/standard.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.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9a418f5: The `init-buddy-agent-harness` skill now says what it scaffolds, `AGENTS.md` and `.agents/skills/`, and treats other `.agents/` folders such as `.agents/agents/` as the project's choice, instead of forbidding them. `init` still creates none of them and still does not call them standard.
|
|
8
|
+
|
|
3
9
|
## 1.0.0
|
|
4
10
|
|
|
5
11
|
### Major Changes
|
|
@@ -464,4 +464,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
464
464
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
465
465
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
466
466
|
`+` `.repeat(t));var fi;(function(e){e.DEFAULT={allowTrailingComma:!1}})(fi||={});function parseTree$1(e,t=[],n=fi.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=fi.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 pi;(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`})(pi||={});var mi;(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`})(mi||={});const hi=parseTree$1,gi=findNodeAtLocation$1;var _i;(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`})(_i||={});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(`
|
|
467
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.
|
|
467
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.1`,description:yi.description,format:e}).command(t))}const bi={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&&bi[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{};
|
|
@@ -464,4 +464,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
464
464
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
465
465
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
466
466
|
`+` `.repeat(t));var fi;(function(e){e.DEFAULT={allowTrailingComma:!1}})(fi||={});function parseTree$1(e,t=[],n=fi.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=fi.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 pi;(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`})(pi||={});var mi;(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`})(mi||={});const hi=parseTree$1,gi=findNodeAtLocation$1;var _i;(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`})(_i||={});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(`
|
|
467
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.
|
|
467
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.1`,description:yi.description,format:e}).command(t))}const bi={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&&bi[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{};
|
|
@@ -464,4 +464,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
464
464
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
465
465
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
466
466
|
`+` `.repeat(t));var fi;(function(e){e.DEFAULT={allowTrailingComma:!1}})(fi||={});function parseTree$1(e,t=[],n=fi.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=fi.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 pi;(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`})(pi||={});var mi;(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`})(mi||={});const hi=parseTree$1,gi=findNodeAtLocation$1;var _i;(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`})(_i||={});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(`
|
|
467
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.
|
|
467
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.1`,description:yi.description,format:e}).command(t))}const bi={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&&bi[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.0.
|
|
4
|
+
"version": "1.0.1",
|
|
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.0.
|
|
10
|
+
npx -y buddy-agent-harness@1.0.1 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.0.
|
|
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.0.1 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
|
|
|
@@ -464,4 +464,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
464
464
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
465
465
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
466
466
|
`+` `.repeat(t));var fi;(function(e){e.DEFAULT={allowTrailingComma:!1}})(fi||={});function parseTree$1(e,t=[],n=fi.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=fi.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 pi;(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`})(pi||={});var mi;(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`})(mi||={});const hi=parseTree$1,gi=findNodeAtLocation$1;var _i;(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`})(_i||={});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(`
|
|
467
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.
|
|
467
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.1`,description:yi.description,format:e}).command(t))}const bi={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&&bi[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
|
@@ -116,7 +116,7 @@ The global file is `~/.agents/AGENTS.md`, the user-scope counterpart of the root
|
|
|
116
116
|
node scripts/doctor.mjs
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
That path is relative to this skill's own directory; fall back to `npx -y buddy-agent-harness@1.0.
|
|
119
|
+
That path is relative to this skill's own directory; fall back to `npx -y buddy-agent-harness@1.0.1 doctor` when it is missing or cannot be run. Read the `globalInstructions` row for the harness you are running in:
|
|
120
120
|
|
|
121
121
|
- **`ok`** — say that this harness loads `~/.agents/AGENTS.md`, through the file the row names.
|
|
122
122
|
- **`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.
|
|
@@ -464,4 +464,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
464
464
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
465
465
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
466
466
|
`+` `.repeat(t));var fi;(function(e){e.DEFAULT={allowTrailingComma:!1}})(fi||={});function parseTree$1(e,t=[],n=fi.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=fi.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 pi;(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`})(pi||={});var mi;(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`})(mi||={});const hi=parseTree$1,gi=findNodeAtLocation$1;var _i;(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`})(_i||={});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(`
|
|
467
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.
|
|
467
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.1`,description:yi.description,format:e}).command(t))}const bi={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&&bi[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{};
|
|
@@ -84,7 +84,7 @@ A `CLAUDE.local.md` is its own case. It is personal, usually gitignored, and it
|
|
|
84
84
|
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.
|
|
85
85
|
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`.
|
|
86
86
|
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.
|
|
87
|
-
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.0.
|
|
87
|
+
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.0.1 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`.
|
|
88
88
|
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.
|
|
89
89
|
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.
|
|
90
90
|
8. If a skills bridge now exists, write or restore the non-material region in `AGENTS.md` per `references/agents-md.md`, unless the markers are present and empty. Without it, an agent asked to change a skill edits `.claude/skills/<name>/SKILL.md` — which a link resolves back to canonical, but a copy silently forks. Skip this when every enabled harness reads `.agents/skills/` natively; with no bridge there is nothing to warn about.
|
|
@@ -107,7 +107,7 @@ Then offer to continue with the `enhance` skill, which proposes guidance the rep
|
|
|
107
107
|
- Never convert an artifact only one harness reads without asking, and never in a way that leaves fewer readers than before. The content is the user's, the conversion is a judgment about what their guidance is *for*, and a consolidation with no bridge behind it silently narrows who sees it.
|
|
108
108
|
- Edit a user-authored settings file in place: change the one key or array element you came for and leave the rest byte-identical, including key order, indentation, and comments. Harnesses disagree about whether a JSON settings file may hold comments, so reading one with `JSON.parse` and writing the object back either deletes what the author wrote or produces a file the harness rejects. The matching `references/harnesses/<harness>.md` says which case a file is.
|
|
109
109
|
- Symlinks belong in version control. Leave them tracked; do not add them to `.gitignore`.
|
|
110
|
-
-
|
|
110
|
+
- `init` writes `AGENTS.md` and `.agents/skills/`, and treats only those as standard: `.agents/skills/` is the one `.agents/` folder harnesses read. Other folders, such as `.agents/rules/`, `.agents/commands/`, or `.agents/agents/`, are the project's choice and outside what `init` scaffolds. Leave any that exist as they are, and do not describe them as standard.
|
|
111
111
|
- Never merge a nested `AGENTS.md` into the root. Merging changes which files it governs.
|
|
112
112
|
- Do not create `AGENTS.local.md`. The standard has no local-override file — two filenames and two meanings are still under discussion upstream. Personal instructions go in `CLAUDE.local.md`, gitignored, read by Claude Code alone — and that file suppresses `AGENTS.md` unless it opens with an `@AGENTS.md` import. Say so whenever you point anyone at it.
|
|
113
113
|
- Local agent configuration only. Do not change workflows, GitHub Actions, repository settings, security scanning, branch rules, or unrelated project files.
|
|
@@ -14,7 +14,7 @@ Optional, only when the repository already has content for them: `.agents/AGENTS
|
|
|
14
14
|
|
|
15
15
|
## Constraints
|
|
16
16
|
|
|
17
|
-
- **
|
|
17
|
+
- **Scaffold only the paths above.** `.agents/skills/` is the one `.agents/` folder harnesses read. Other folders, such as `.agents/rules/`, `.agents/commands/`, or `.agents/agents/`, are the project's choice: `init` creates none of them, leaves any that exist, and never calls them standard.
|
|
18
18
|
- **Do not invent policy for `AGENTS.md`.** Write only content the user authored or approved. Deriving facts from the repository is not invention; asserting rules nobody agreed to is. See `agents-md.md`.
|
|
19
19
|
- Call `.agents/` a convention, not a standard, in any user-facing output.
|
|
20
20
|
- `AGENTS.md` has no schema and no required sections. Do not impose one.
|
|
@@ -464,4 +464,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
464
464
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
465
465
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
466
466
|
`+` `.repeat(t));var fi;(function(e){e.DEFAULT={allowTrailingComma:!1}})(fi||={});function parseTree$1(e,t=[],n=fi.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=fi.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 pi;(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`})(pi||={});var mi;(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`})(mi||={});const hi=parseTree$1,gi=findNodeAtLocation$1;var _i;(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`})(_i||={});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(`
|
|
467
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.
|
|
467
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.1`,description:yi.description,format:e}).command(t))}const bi={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&&bi[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{};
|
|
@@ -464,4 +464,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
464
464
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
465
465
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
466
466
|
`+` `.repeat(t));var fi;(function(e){e.DEFAULT={allowTrailingComma:!1}})(fi||={});function parseTree$1(e,t=[],n=fi.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=fi.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 pi;(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`})(pi||={});var mi;(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`})(mi||={});const hi=parseTree$1,gi=findNodeAtLocation$1;var _i;(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`})(_i||={});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(`
|
|
467
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.
|
|
467
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.1`,description:yi.description,format:e}).command(t))}const bi={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&&bi[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.0.
|
|
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.0.1 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
|
|
|
@@ -464,4 +464,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
464
464
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
465
465
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
466
466
|
`+` `.repeat(t));var fi;(function(e){e.DEFAULT={allowTrailingComma:!1}})(fi||={});function parseTree$1(e,t=[],n=fi.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=fi.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 pi;(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`})(pi||={});var mi;(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`})(mi||={});const hi=parseTree$1,gi=findNodeAtLocation$1;var _i;(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`})(_i||={});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(`
|
|
467
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.
|
|
467
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.1`,description:yi.description,format:e}).command(t))}const bi={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&&bi[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{};
|
|
@@ -464,4 +464,4 @@ const Y=`.agents/buddy-agent-harness/mcp.toml`;function mcpTargets(e){let t=new
|
|
|
464
464
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`
|
|
465
465
|
`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r`+` `.repeat(t)),Array(200).fill(0).map((e,t)=>`\r
|
|
466
466
|
`+` `.repeat(t));var fi;(function(e){e.DEFAULT={allowTrailingComma:!1}})(fi||={});function parseTree$1(e,t=[],n=fi.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=fi.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 pi;(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`})(pi||={});var mi;(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`})(mi||={});const hi=parseTree$1,gi=findNodeAtLocation$1;var _i;(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`})(_i||={});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(`
|
|
467
|
-
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.
|
|
467
|
+
`,`\n${t.repeat(n)}`)}function objectAt(e,t){let n=e&&(t.length?gi(e,t):e);return n?.type===`object`?n:void 0}function insertJsonProperty(e,t,n,r){let i=objectAt(hi(e),t);if(!i||gi(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=hi(e),i=r&&gi(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=S(e,t);return n(r)?a(r,`utf8`):void 0}const vi={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,X)),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=q[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`,vi[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=q[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=S(e,t.config.path);i(y(n),{recursive:!0}),p(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=S(e,X);i(y(r),{recursive:!0}),p(r,formatProjectionRecord(t))}const yi={name:`buddy-agent-harness`,description:`Initialize agent harness skill compatibility in consumer repositories.`,commands:[Ur,Rr,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()}},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 process.stderr.write(`error: ${Y} does not parse${e}.\n`),M.reported(j.error)}return r.kind===`absent`?result({golden:`0 servers — no golden set at ${Y}`,mode:`nothing to project`,actions:`0 changes`,record:`not written`}):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?X:`not written — dry run`})}catch(e){return fail(e,`MCP projection failed.`)}}}),ui]}),Wn,Pr,Hr]};function app(e){let[t,...n]=yi.commands;return n.reduce((e,t)=>e.command(t),cli({name:yi.name,version:`1.0.1`,description:yi.description,format:e}).command(t))}const bi={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&&bi[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{};
|