inscope 0.8.0-canary.8 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/bin/index.mjs +24 -24
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +12 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -140,6 +140,10 @@ inscope doctor Verify tokens, identities, and the hook resolve correctly
|
|
|
140
140
|
|
|
141
141
|
Run any command with `-h` for its options.
|
|
142
142
|
|
|
143
|
+
<p align="center">
|
|
144
|
+
<img src="https://raw.githubusercontent.com/nrjdalal/demo-kit/main/inscope/demo-diff.gif" alt="inscope diff: preview drift as a colored diff, then --adopt back-syncs an on-disk setting into the config" width="900" />
|
|
145
|
+
</p>
|
|
146
|
+
|
|
143
147
|
<p align="center">
|
|
144
148
|
<img src="https://raw.githubusercontent.com/nrjdalal/demo-kit/main/inscope/demo-manage.gif" alt="inscope edit and rm with type-to-confirm" width="900" />
|
|
145
149
|
</p>
|
package/dist/bin/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import{parseArgs as e}from"node:util";import t from"node:fs";import n from"node:path";import r from"node:os";import{spawnSync as i}from"node:child_process";import a from"node:readline";const o=()=>r.homedir(),s=()=>process.env.XDG_CONFIG_HOME?.trim()||n.join(o(),`.config`),c=e=>e===`~`?o():e.startsWith(`~/`)?n.join(o(),e.slice(2)):e,l=e=>{let t=c(e),r=o();return t===r?`~`:t.startsWith(r+n.sep)?`~/`+t.slice(r.length+1):t},u=e=>n.resolve(c(e)),d=()=>n.join(s(),`inscope`),f=()=>n.join(d(),`inscope.json`),p=()=>n.join(d(),`inscope.zsh`),m=()=>n.join(d(),`git`),h=()=>n.join(o(),`.gitconfig`),g=()=>n.join(o(),`.zshrc`),_=()=>({version:1,workspaces:[]}),v=()=>t.existsSync(f()),y=()=>{let e=f(),n=t.readFileSync(e,`utf8`),r=JSON.parse(n);try{ne(r)}catch(t){let n=t instanceof Error?t.message:String(t);throw Error(`${n}\nFix it in ${l(e)}, then re-run.`)}return r},b=e=>{let r=f();t.mkdirSync(n.dirname(r),{recursive:!0}),t.writeFileSync(r,JSON.stringify(e,null,2)+`
|
|
3
|
-
`)},x=/^[A-Za-z0-9._-]+$/,S=e=>e?x.test(e)?null:`use only letters, digits, dot (.), dash (-), or underscore (_)`:`must not be empty`,C=/["`$\n]/,w=e=>C.test(e)?'must not contain a quote ("), backtick (`), $, or newline':null,ee=e=>e?w(e):`must not be empty`,te=e=>e.toLowerCase().replace(/[^a-z0-9._-]+/g,`-`).replace(/^[-.]+|[-.]+$/g,``),ne=e=>{if(!e||typeof e!=`object`)throw Error(`config is not an object`);if(!Array.isArray(e.workspaces))throw Error(`config.workspaces must be an array`);let t=new Set;for(let n of e.workspaces){if(!n.name)throw Error(`a workspace is missing a name`);let e=S(n.name);if(e)throw Error(`workspace name "${n.name}" is invalid: ${e}`);if(!n.path)throw Error(`workspace "${n.name}" is missing a path`);let r=ee(n.path);if(r)throw Error(`workspace "${n.name}" path "${n.path}" is invalid: ${r}`);if(n.gh){let e=w(n.gh);if(e)throw Error(`workspace "${n.name}" gh account "${n.gh}" is invalid: ${e}`)}let i=n.servers?.slack;if(i&&i.keychain){let e=w(i.keychain);if(e)throw Error(`workspace "${n.name}" Slack keychain "${i.keychain}" is invalid: ${e}`)}if(t.has(n.name))throw Error(`duplicate workspace name "${n.name}"`);t.add(n.name)}},re=e=>te(n.basename(u(e)))||`workspace`,T=(e,t)=>{let n=e.workspaces.find(e=>e.name===t);if(n)return n;let r=u(t);return e.workspaces.find(e=>u(e.path)===r)},ie=(e,t)=>{let n=e.workspaces.filter(e=>e.name!==t.name);return n.push({...t,path:l(t.path)}),n.sort((e,t)=>e.name.localeCompare(t.name)),{...e,workspaces:n}},ae=(e,t)=>{let n=T(e,t);return n?{cfg:{...e,workspaces:e.workspaces.filter(e=>e.name!==n.name)},removed:n}:{cfg:e}},
|
|
4
|
-
`)},fe=e=>{let n=
|
|
5
|
-
`)},A=(e,t,n)=>{let r=i(e,t,{encoding:`utf8`,input:n?.input});return{status:r.status??(r.error?127:1),stdout:r.stdout??``,stderr:r.stderr??``}},pe=()=>process.platform===`darwin`,j=()=>process.env.USER||``,me=(e,t=A)=>{let n=t(`gh`,[`auth`,`token`,`-u`,e]),r=n.stdout.trim();return n.status===0&&r?r:null},he=(e=A)=>{let t=e(`gh`,[`auth`,`status`]);return(t.stdout+t.stderr).trim()},
|
|
3
|
+
`)},x=/^[A-Za-z0-9._-]+$/,S=e=>e?x.test(e)?null:`use only letters, digits, dot (.), dash (-), or underscore (_)`:`must not be empty`,C=/["`$\n]/,w=e=>C.test(e)?'must not contain a quote ("), backtick (`), $, or newline':null,ee=e=>e?w(e):`must not be empty`,te=e=>e.toLowerCase().replace(/[^a-z0-9._-]+/g,`-`).replace(/^[-.]+|[-.]+$/g,``),ne=e=>{if(!e||typeof e!=`object`)throw Error(`config is not an object`);if(!Array.isArray(e.workspaces))throw Error(`config.workspaces must be an array`);let t=new Set;for(let n of e.workspaces){if(!n.name)throw Error(`a workspace is missing a name`);let e=S(n.name);if(e)throw Error(`workspace name "${n.name}" is invalid: ${e}`);if(!n.path)throw Error(`workspace "${n.name}" is missing a path`);let r=ee(n.path);if(r)throw Error(`workspace "${n.name}" path "${n.path}" is invalid: ${r}`);if(n.gh){let e=w(n.gh);if(e)throw Error(`workspace "${n.name}" gh account "${n.gh}" is invalid: ${e}`)}let i=n.servers?.slack;if(i&&i.keychain){let e=w(i.keychain);if(e)throw Error(`workspace "${n.name}" Slack keychain "${i.keychain}" is invalid: ${e}`)}if(t.has(n.name))throw Error(`duplicate workspace name "${n.name}"`);t.add(n.name)}},re=e=>te(n.basename(u(e)))||`workspace`,T=(e,t)=>{let n=e.workspaces.find(e=>e.name===t);if(n)return n;let r=u(t);return e.workspaces.find(e=>u(e.path)===r)},ie=(e,t)=>{let n=e.workspaces.filter(e=>e.name!==t.name);return n.push({...t,path:l(t.path)}),n.sort((e,t)=>e.name.localeCompare(t.name)),{...e,workspaces:n}},ae=(e,t)=>{let n=T(e,t);return n?{cfg:{...e,workspaces:e.workspaces.filter(e=>e.name!==n.name)},removed:n}:{cfg:e}},E={atlassian:`https://mcp.atlassian.com/v1/mcp`,canva:`https://mcp.canva.com/mcp`,clickup:`https://mcp.clickup.com/mcp`,hubspot:`https://mcp.hubspot.com`,intercom:`https://mcp.intercom.com/mcp`,linear:`https://mcp.linear.app/mcp`,monday:`https://mcp.monday.com/mcp`,notion:`https://mcp.notion.com/mcp`,plane:`https://mcp.plane.so/http/mcp`,sentry:`https://mcp.sentry.dev/mcp`,stripe:`https://mcp.stripe.com`,vercel:`https://mcp.vercel.com`,webflow:`https://mcp.webflow.com/`},D=[`github`,`atlassian`,`canva`,`clickup`,`hubspot`,`intercom`,`linear`,`monday`,`notion`,`plane`,`sentry`,`slack`,`stripe`,`vercel`,`webflow`],O=e=>D.map(t=>`${t}-${e}`),k=e=>n.join(u(e.path),`.mcp.json`),oe=(e,t)=>e&&typeof e==`object`&&e.url?e.url:t,se=e=>{let t=e.servers,n={};for(let r of D){let i=t[r];if(!i)continue;let a=`${r}-${e.name}`;if(r===`github`)n[a]={type:`http`,url:`https://api.githubcopilot.com/mcp/`,headers:{Authorization:"Bearer ${GITHUB_TOKEN}"}};else if(r===`slack`){let e=i,t={SLACK_MCP_XOXP_TOKEN:"${SLACK_MCP_XOXP_TOKEN}"};e.addMessageTool&&(t.SLACK_MCP_ADD_MESSAGE_TOOL=`true`),n[a]={type:`stdio`,command:`npx`,args:[`-y`,`slack-mcp-server@1.3.0`,`--transport`,`stdio`],env:t}}else n[a]={type:`http`,url:oe(i,E[r])}}return n},ce=e=>{if(!t.existsSync(e))return{};try{return JSON.parse(t.readFileSync(e,`utf8`))}catch{return{}}},le=e=>{if(!t.existsSync(e))return{};try{return JSON.parse(t.readFileSync(e,`utf8`))}catch{throw Error(`${e} is not valid JSON; fix or remove it, then re-run inscope (left it untouched)`)}},ue=e=>{let n=k(e);return t.existsSync(n)?ce(n):null},de=e=>{let r=k(e);t.mkdirSync(n.dirname(r),{recursive:!0});let i=le(r),a=i.mcpServers&&typeof i.mcpServers==`object`?{...i.mcpServers}:{};for(let t of O(e.name))delete a[t];Object.assign(a,se(e)),i.mcpServers=a,t.writeFileSync(r,JSON.stringify(i,null,2)+`
|
|
4
|
+
`)},fe=e=>{let n=k(e);if(!t.existsSync(n))return;let r=le(n);if(r.mcpServers&&typeof r.mcpServers==`object`)for(let t of O(e.name))delete r.mcpServers[t];t.writeFileSync(n,JSON.stringify(r,null,2)+`
|
|
5
|
+
`)},A=(e,t,n)=>{let r=i(e,t,{encoding:`utf8`,input:n?.input});return{status:r.status??(r.error?127:1),stdout:r.stdout??``,stderr:r.stderr??``}},pe=()=>process.platform===`darwin`,j=()=>process.env.USER||``,me=(e,t=A)=>{let n=t(`gh`,[`auth`,`token`,`-u`,e]),r=n.stdout.trim();return n.status===0&&r?r:null},he=(e=A)=>{let t=e(`gh`,[`auth`,`status`]);return(t.stdout+t.stderr).trim()},ge=(e=A)=>{let t=[];for(let n of he(e).matchAll(/account (\S+) \(/g))t.includes(n[1])||t.push(n[1]);return t},_e=(e,t=A)=>{let n=t(`git`,[`config`,`--global`,e]),r=n.stdout.trim();return n.status===0&&r?r:null},M=(e,t=A)=>{let n=t(`security`,[`find-generic-password`,`-a`,j(),`-s`,e,`-w`]);return n.status===0&&n.stdout.trim().length>0},ve=(e,t,n=A)=>{let r=n(`security`,[`add-generic-password`,`-U`,`-a`,j(),`-s`,e,`-w`,t]);if(r.status!==0)throw Error(`security add-generic-password failed: ${r.stderr.trim()||`unknown error`}`)},ye=e=>`security add-generic-password -U -a "${j()||`$USER`}" -s ${e} -w 'xoxp-...'`,be=(e,t=A)=>{let n=t(`git`,[`config`,`--file`,e,`user.email`]);return n.status===0?n.stdout.trim():null},N=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),xe=(e,t=e)=>process.stdout.isTTY?`\x1b]8;;${e}\x07${t}\x1b]8;;\x07`:e,P=e=>t=>process.stdout.isTTY?`\x1b[${e}m${t}\x1b[0m`:t,F=P(`38;5;208`),Se=P(`38;2;63;185;80`),Ce=P(`38;2;210;153;34`),I=P(`38;2;248;81;73`),L=e=>{let t=process.stdin;t.isTTY&&typeof t.setRawMode==`function`&&t.setRawMode(e)};let R=``;const we=e=>new Promise(t=>{process.stdout.write(e);let n=()=>{let e=R.indexOf(`
|
|
6
6
|
`);if(e<0)return!1;let n=R.slice(0,e).replace(/\r$/,``);return R=R.slice(e+1),t(n),!0};if(n())return;let r=e=>{R+=e.toString(`utf8`),R.includes(`
|
|
7
|
-
`)&&(process.stdin.off(`data`,r),process.stdin.off(`end`,i),process.stdin.pause(),n())},i=()=>{process.stdin.off(`data`,r),process.stdin.off(`end`,i);let e=R.replace(/\r$/,``);R=``,t(e)};process.stdin.on(`data`,r),process.stdin.on(`end`,i),process.stdin.resume()}),z=async(e,t=``)=>(await we(`${e}${t?` [${t}]`:``}: `)).trim()||t,B=async(e,t=!1)=>{if(!
|
|
8
|
-
`),n.close(),t(e.trim())})}),Ee=`\x1B[36m`,De=`\x1B[0m`,V=(e,t,n=0)=>new Promise(r=>{if(!
|
|
7
|
+
`)&&(process.stdin.off(`data`,r),process.stdin.off(`end`,i),process.stdin.pause(),n())},i=()=>{process.stdin.off(`data`,r),process.stdin.off(`end`,i);let e=R.replace(/\r$/,``);R=``,t(e)};process.stdin.on(`data`,r),process.stdin.on(`end`,i),process.stdin.resume()}),z=async(e,t=``)=>(await we(`${e}${t?` [${t}]`:``}: `)).trim()||t,B=async(e,t=!1)=>{if(!N()){let n=(await we(`${e} [${t?`Y/n`:`y/N`}]: `)).trim().toLowerCase();return n?n===`y`||n===`yes`:t}return V(e,[{label:`Yes`,value:!0},{label:`No`,value:!1}],t?0:1)},Te=e=>new Promise(t=>{let n=a.createInterface({input:process.stdin,output:process.stdout}),r=n.output,i=!1;n._writeToOutput=t=>{if(!i){r.write(t),t.includes(e)&&(i=!0);return}},n.question(e,e=>{r.write(`
|
|
8
|
+
`),n.close(),t(e.trim())})}),Ee=`\x1B[36m`,De=`\x1B[0m`,V=(e,t,n=0)=>new Promise(r=>{if(!N()||t.length===0){r(t[Math.min(n,t.length-1)]?.value);return}let i=Math.max(0,Math.min(n,t.length-1)),o=process.stdout;o.write(e+`
|
|
9
9
|
`);let s=e=>{e||o.write(`\x1b[${t.length}A`);for(let e=0;e<t.length;e++){let n=e===i,r=`${n?`❯`:` `} ${t[e].label}`;o.write(`\x1b[2K ${n?Ee+r+De:r}\n`)}};s(!0),a.emitKeypressEvents(process.stdin),L(!0),process.stdin.resume();let c=()=>{process.stdin.off(`keypress`,l),L(!1),process.stdin.pause()},l=(e,n)=>{n.name===`up`||n.name===`k`?(i=(i-1+t.length)%t.length,s(!1)):n.name===`down`||n.name===`j`?(i=(i+1)%t.length,s(!1)):n.name===`return`||n.name===`enter`?(c(),r(t[i].value)):n.ctrl&&n.name===`c`&&(c(),o.write(`
|
|
10
|
-
`),process.exit(130))};process.stdin.on(`keypress`,l)}),Oe=(e,t)=>new Promise(n=>{let r=t.map(e=>!!e.checked),i=()=>t.filter((e,t)=>r[t]).map(e=>e.value);if(!
|
|
10
|
+
`),process.exit(130))};process.stdin.on(`keypress`,l)}),Oe=(e,t)=>new Promise(n=>{let r=t.map(e=>!!e.checked),i=()=>t.filter((e,t)=>r[t]).map(e=>e.value);if(!N()||t.length===0){n(i());return}let o=0,s=process.stdout;s.write(e+`
|
|
11
11
|
`);let c=e=>{e||s.write(`\x1b[${t.length}A`);for(let e=0;e<t.length;e++){let n=e===o,i=`${n?`❯`:` `} ${r[e]?`◉`:`◯`} ${t[e].label}`;s.write(`\x1b[2K ${n?Ee+i+De:i}\n`)}};c(!0),a.emitKeypressEvents(process.stdin),L(!0),process.stdin.resume();let l=()=>{process.stdin.off(`keypress`,u),L(!1),process.stdin.pause()},u=(e,a)=>{a.name===`up`||a.name===`k`?(o=(o-1+t.length)%t.length,c(!1)):a.name===`down`||a.name===`j`?(o=(o+1)%t.length,c(!1)):a.name===`space`||e===` `?(r[o]=!r[o],c(!1)):a.name===`return`||a.name===`enter`?(l(),n(i())):a.ctrl&&a.name===`c`&&(l(),s.write(`
|
|
12
12
|
`),process.exit(130))};process.stdin.on(`keypress`,u)}),H=e=>`# >>> inscope:${e} >>>`,U=e=>`# <<< inscope:${e} <<<`,W=e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),ke=e=>RegExp(`${W(H(e))}\\n[\\s\\S]*?\\n${W(U(e))}\\n?`),Ae=(e,t)=>{let n=t.replace(/\n+$/,``);return`${H(e)}\n${n}\n${U(e)}\n`},G=e=>{try{return t.readFileSync(e,`utf8`)}catch{return``}},je=(e,r,i)=>{t.mkdirSync(n.dirname(e),{recursive:!0});let a=G(e),o=Ae(r,i),s=ke(r),c;if(s.test(a))c=a.replace(s,o);else{let e=a.replace(/\n*$/,``);c=e.length?`${e}\n\n${o}`:o}t.writeFileSync(e,c)},Me=(e,n)=>{let r=G(e);if(!r)return;let i=r.replace(ke(n),``).replace(/\n{3,}/g,`
|
|
13
13
|
|
|
@@ -61,8 +61,8 @@ autoload -Uz add-zsh-hook
|
|
|
61
61
|
add-zsh-hook chpwd __inscope_resolve_identity
|
|
62
62
|
__inscope_ws="__init__" # force the first resolve, clearing any inherited token
|
|
63
63
|
__inscope_resolve_identity
|
|
64
|
-
`},Ve=e=>{let t=o();return e===t?`$HOME`:e.startsWith(t+n.sep)?`$HOME/${e.slice(t.length+1)}`:e},He=()=>{let e=Ve(p());return`[ -r "${e}" ] && source "${e}"`},Ue=e=>{let t=He();if(e.includes(t))return e;let n=e.replace(/\n*$/,``),r=`# inscope: load each workspace's tokens (GitHub, Slack) from \$PWD on every cd\n${t}`;return n.length?`${n}\n\n${r}\n`:`${r}\n`},We=()=>{let e=g(),n=``;try{n=t.readFileSync(e,`utf8`)}catch{}let r=Ue(n);r!==n&&t.writeFileSync(e,r)},Ge=()=>{try{return t.readFileSync(g(),`utf8`).includes(He())}catch{return!1}},X=e=>{let r=p();t.mkdirSync(n.dirname(r),{recursive:!0}),t.writeFileSync(r,Y(e)),Le(e),We();let i=[];for(let t of e.workspaces)de(t),i.push(
|
|
65
|
-
No token entered; skipped keychain write.`)}else
|
|
64
|
+
`},Ve=e=>{let t=o();return e===t?`$HOME`:e.startsWith(t+n.sep)?`$HOME/${e.slice(t.length+1)}`:e},He=()=>{let e=Ve(p());return`[ -r "${e}" ] && source "${e}"`},Ue=e=>{let t=He();if(e.includes(t))return e;let n=e.replace(/\n*$/,``),r=`# inscope: load each workspace's tokens (GitHub, Slack) from \$PWD on every cd\n${t}`;return n.length?`${n}\n\n${r}\n`:`${r}\n`},We=()=>{let e=g(),n=``;try{n=t.readFileSync(e,`utf8`)}catch{}let r=Ue(n);r!==n&&t.writeFileSync(e,r)},Ge=()=>{try{return t.readFileSync(g(),`utf8`).includes(He())}catch{return!1}},X=e=>{let r=p();t.mkdirSync(n.dirname(r),{recursive:!0}),t.writeFileSync(r,Y(e)),Le(e),We();let i=[];for(let t of e.workspaces)de(t),i.push(k(t));return{hook:r,gitconfig:e.workspaces.some(e=>e.git?.email||e.git?.name),mcp:i}},Ke=D,qe=e=>`SLACK_MCP_XOXP_TOKEN_${e.toUpperCase().replace(/[^A-Z0-9]+/g,`_`)}`,Je=e=>D.filter(t=>!!e[t]),Ye=(e,t)=>{let n={};for(let r of D)n[r]=r===`slack`?t?{keychain:t.keychain,addMessageTool:t.addMessageTool}:!1:e.includes(r);return n},Xe=e=>{let t=ie(v()?y():_(),e);b(t),X(t)},Ze=async(e,t)=>{if(!e.servers.slack)return;let n=e.servers.slack.keychain;if(t){let e=await Te(`Paste the Slack xoxp token for ${n}: `);e?(ve(n,e),console.log(`\n✓ stored ${n} in the macOS keychain`)):console.error(`
|
|
65
|
+
No token entered; skipped keychain write.`)}else M(n)||console.log(`\nSlack token not in the keychain yet. Store it once with:\n${F(ye(n))}\n\nSetup guide: ${F(xe(`https://github.com/korotovsky/slack-mcp-server/blob/HEAD/docs/01-authentication-setup.md#option-2-using-slack_mcp_xoxp_token-user-oauth`))}`)};var Z=`inscope`,Qe=`0.8.0`,Q={name:`Neeraj Dalal`,email:`admin@nrjdalal.com`,url:`https://nrjdalal.com`};const $e=`Map a directory to a GitHub account, git email, and MCP servers.
|
|
66
66
|
Runs interactively in a terminal; pass flags or -y to skip the prompts. Re-running
|
|
67
67
|
with the same path or label updates that workspace.
|
|
68
68
|
|
|
@@ -83,8 +83,8 @@ Options:
|
|
|
83
83
|
--slack-message allow the Slack MCP server to post messages
|
|
84
84
|
--seed-slack prompt for the Slack token and store it in the keychain
|
|
85
85
|
-y, --yes accept defaults, skip all prompts (non-interactive)
|
|
86
|
-
-h, --help Display help message`,et=
|
|
87
|
-
GitHub account for this workspace`,[...
|
|
86
|
+
-h, --help Display help message`,et=D.map(e=>({label:e,value:e,checked:e===`github`})),tt=async t=>{let{positionals:n,values:r}=e({allowPositionals:!0,options:{help:{type:`boolean`,short:`h`},yes:{type:`boolean`,short:`y`},gh:{type:`string`},email:{type:`string`},"git-name":{type:`string`},label:{type:`string`},servers:{type:`string`},"slack-keychain":{type:`string`},"slack-message":{type:`boolean`},"seed-slack":{type:`boolean`}},args:t});r.help&&(console.log($e),process.exit(0));let i=N()&&!r.yes;i&&console.log();let a=n[0];if(!a)if(i)a=await z(`Workspace directory`,process.cwd());else throw Error($e);let o=ee(a);o&&(console.error(`\nInvalid workspace path "${a}": ${o}`),process.exit(1));let s=r.label||re(a);i&&!r.label&&(s=await z(`Label`,s));let c=S(s);c&&(console.error(`\nInvalid label "${s}": ${c}`),process.exit(1));let u=r.gh;u===void 0&&i&&(u=await V(`
|
|
87
|
+
GitHub account for this workspace`,[...ge().map(e=>({label:e,value:e})),{label:`(none)`,value:``}])||void 0);let d=r.email,f=r[`git-name`];if(i){if(d===void 0){let e=_e(`user.email`);d=await z(`Git email${e?` [${e} · global]`:``} (enter to inherit global)`)||void 0}if(f===void 0){let e=_e(`user.name`);f=await z(`Git name${e?` [${e} · global]`:``} (enter to inherit global)`)||void 0}}let p;p=r.servers===void 0?i?await Oe(`MCP servers (space toggles, enter confirms)`,et):[`github`]:r.servers.split(`,`).map(e=>e.trim()).filter(Boolean);let m=p.includes(`slack`)||!!r[`slack-keychain`]||!!r[`seed-slack`],h=r[`slack-keychain`]||qe(s),g=!!r[`slack-message`],_=!!r[`seed-slack`];m&&i&&(console.log(`
|
|
88
88
|
Slack uses a user OAuth (xoxp) token.`),r[`slack-keychain`]||(h=await z(`Slack keychain service`,h)),r[`slack-message`]||(g=await B(`Allow Slack to post messages?`,!0)),r[`seed-slack`]||(_=await B(`Store the Slack token now?`,!0)));let v=u?w(u):null;if(v&&(console.error(`\nInvalid gh account "${u}": ${v}`),process.exit(1)),m){let e=w(h);e&&(console.error(`\nInvalid Slack keychain service "${h}": ${e}`),process.exit(1))}let y={name:s,path:l(a),gh:u,git:d||f?{email:d,name:f}:void 0,servers:Ye(p,m?{keychain:h,addMessageTool:g}:null)};Xe(y),console.log(`\n✓ workspace "${s}" -> ${y.path}`),console.log(`✓ regenerated the hook, git includes, and ${y.path}/.mcp.json`),await Ze(y,_),console.log(`\nLaunch \`claude\` from ${y.path} (or relaunch) to pick up the new identity.`),process.exit(0)},nt=`Regenerate the chpwd hook, git includes, and every .mcp.json
|
|
89
89
|
from your config. Idempotent: run it any time the config changes.
|
|
90
90
|
|
|
@@ -92,13 +92,13 @@ Usage:
|
|
|
92
92
|
$ ${Z} apply
|
|
93
93
|
|
|
94
94
|
Options:
|
|
95
|
-
-h, --help Display help message`,rt=t=>{let{values:n}=e({allowPositionals:!0,options:{help:{type:`boolean`,short:`h`}},args:t});n.help&&(console.log(nt),process.exit(0)),v()||(console.error(`No config found. Run \`${Z} init\` first.`),process.exit(1));let r=y(),i=X(r);console.log(`\n✓ hook ${i.hook}`),i.gitconfig&&console.log(`✓ gitconfig ~/.gitconfig (includeIf block)`);for(let e of i.mcp)console.log(`✓ mcp ${e}`);console.log(`\nApplied ${r.workspaces.length} workspace(s).`),process.exit(0)},$=e=>{try{return t.readFileSync(e,`utf8`)}catch{return``}},it=e=>{let t=$(e);if(!t)return null;try{return JSON.parse(t)}catch{return null}},at=e=>{let t=it(
|
|
96
|
-
`},ot=e=>{let t=[],n=p();t.push({label:`hook`,path:n,current:$(n),next:Y(e)}),t.push({label:`gitconfig`,path:h(),current:Ne(h(),K)??``,next:Fe(e)});for(let n of e.workspaces){if(!q(n))continue;let e=J(n.name);t.push({label:`gitconfig:${n.name}`,path:e,current:$(e),next:Ie(n)})}for(let n of e.workspaces){let e=
|
|
95
|
+
-h, --help Display help message`,rt=t=>{let{values:n}=e({allowPositionals:!0,options:{help:{type:`boolean`,short:`h`}},args:t});n.help&&(console.log(nt),process.exit(0)),v()||(console.error(`No config found. Run \`${Z} init\` first.`),process.exit(1));let r=y(),i=X(r);console.log(`\n✓ hook ${i.hook}`),i.gitconfig&&console.log(`✓ gitconfig ~/.gitconfig (includeIf block)`);for(let e of i.mcp)console.log(`✓ mcp ${e}`);console.log(`\nApplied ${r.workspaces.length} workspace(s).`),process.exit(0)},$=e=>{try{return t.readFileSync(e,`utf8`)}catch{return``}},it=e=>{let t=$(e);if(!t)return null;try{return JSON.parse(t)}catch{return null}},at=e=>{let t=it(k(e))??{},n=t.mcpServers&&typeof t.mcpServers==`object`?{...t.mcpServers}:{};for(let t of O(e.name))delete n[t];return Object.assign(n,se(e)),t.mcpServers=n,JSON.stringify(t,null,2)+`
|
|
96
|
+
`},ot=e=>{let n=k(e);if(!t.existsSync(n))return null;try{return JSON.parse(t.readFileSync(n,`utf8`)),null}catch{return"invalid JSON; `apply` will not touch it until you fix it"}},st=e=>{let t=[],n=p();t.push({label:`hook`,path:n,current:$(n),next:Y(e)}),t.push({label:`gitconfig`,path:h(),current:Ne(h(),K)??``,next:Fe(e)});for(let n of e.workspaces){if(!q(n))continue;let e=J(n.name);t.push({label:`gitconfig:${n.name}`,path:e,current:$(e),next:Ie(n)})}for(let n of e.workspaces){let e=k(n),r=ot(n);if(r){t.push({label:`mcp:${n.name}`,path:e,current:``,next:``,error:r});continue}t.push({label:`mcp:${n.name}`,path:e,current:$(e),next:at(n)})}return t.filter(e=>e.error!=null||e.current!==e.next)},ct=(e,t)=>{let n=e.length?e.split(`
|
|
97
97
|
`):[],r=t.length?t.split(`
|
|
98
98
|
`):[],i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array.from({length:a+1},()=>0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)o[e][t]=n[e]===r[t]?o[e+1][t+1]+1:Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push(` ${n[c]}`),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push(`- ${n[c]}`),c++):(s.push(`+ ${r[l]}`),l++);for(;c<i;)s.push(`- ${n[c++]}`);for(;l<a;)s.push(`+ ${r[l++]}`);return s.join(`
|
|
99
|
-
`)},
|
|
99
|
+
`)},lt=e=>{let t=[],n=e.workspaces.map(e=>{let n=it(k(e))?.mcpServers;if(!n||typeof n!=`object`)return e;let r=e.servers,i=e.servers.slack;i&&!i.addMessageTool&&n[`slack-${e.name}`]?.env?.SLACK_MCP_ADD_MESSAGE_TOOL===`true`&&(r={...r,slack:{...i,addMessageTool:!0}},t.push(`${e.name}: slack.addMessageTool = true`));for(let i of D){if(i===`github`||i===`slack`)continue;let a=n[`${i}-${e.name}`]?.url;if(typeof a!=`string`)continue;let o=e.servers[i];if(!o){r={...r,[i]:a===E[i]?!0:{url:a}},t.push(`${e.name}: ${i} = ${a===E[i]?`enabled`:a}`);continue}a!==((typeof o==`object`?o.url:void 0)??E[i])&&(r={...r,[i]:{url:a}},t.push(`${e.name}: ${i}.url = ${a}`))}return r===e.servers?e:{...e,servers:r}});return{cfg:{...e,workspaces:n},changes:t}},ut=e=>{if(!process.stdout.isTTY)return e;let t=process.stdout.columns||80;return e.split(`
|
|
100
100
|
`).map(e=>{if(!e.startsWith(`- `)&&!e.startsWith(`+ `))return e;let n=` `.repeat(Math.max(0,t-e.length));return`\x1b[${e.startsWith(`- `)?`48;2;48;27;31;38;2;248;81;73`:`48;2;18;38;30;38;2;63;185;80`}m${e}${n}\x1b[0m`}).join(`
|
|
101
|
-
`)},
|
|
101
|
+
`)},dt=`Show what \`${Z} apply\` would change: a diff of each managed
|
|
102
102
|
artifact (the zsh hook, git includes, and .mcp.json files) against what your
|
|
103
103
|
config would generate. Read-only.
|
|
104
104
|
|
|
@@ -111,7 +111,7 @@ Usage:
|
|
|
111
111
|
|
|
112
112
|
Options:
|
|
113
113
|
--adopt Write config-expressible on-disk settings back into the config
|
|
114
|
-
-h, --help Display help message`,
|
|
114
|
+
-h, --help Display help message`,ft=t=>{let{values:n}=e({allowPositionals:!0,options:{help:{type:`boolean`,short:`h`},adopt:{type:`boolean`}},args:t});n.help&&(console.log(dt),process.exit(0)),v()||(console.error(`No config found. Run \`${Z} init\` first.`),process.exit(1));let r=y();if(n.adopt){let{cfg:e,changes:t}=lt(r);t.length||(console.log(`Nothing to adopt: the config already covers your .mcp.json settings.`),process.exit(0)),ne(e),b(e),console.log(`Adopted into config:`);for(let e of t)console.log(` ${e}`);console.log(`\nRun ${F(`${Z} apply`)} to regenerate from the updated config.`),process.exit(0)}let i=st(r);i.length||(console.log("In sync. `apply` would change nothing."),process.exit(0));for(let e of i)console.log(`\n${F(`${l(e.path)} (${e.label})`)}`),e.error?console.log(I(` ${e.error}`)):console.log(ut(ct(e.current,e.next)));let{changes:a}=lt(r);if(a.length){console.log("\nThese .mcp.json settings aren't in your config, so `apply` would drop them:");for(let e of a)console.log(` ${e}`);console.log(`\nRun ${F(`${Z} diff --adopt`)} to keep them by writing them into the config.`)}process.exit(0)},pt=e=>{try{return t.readFileSync(e,`utf8`)}catch{return null}},mt=e=>{let t=[],n=e?.mcpServers;if(!n||typeof n!=`object`)return t;for(let[e,r]of Object.entries(n)){let n=Array.isArray(r?.args)?r.args:[];if(n.some(e=>typeof e==`string`&&e.endsWith(`@latest`)))t.push(e);else if(r?.command===`npx`){let r=n.find(e=>typeof e==`string`&&!e.startsWith(`-`));r&&!r.includes(`@`)&&t.push(e)}}return t},ht=(e,t=process.cwd())=>{let r=n.resolve(t);return e.workspaces.find(e=>{let t=u(e.path);return r===t||r.startsWith(t+n.sep)})},gt=(e=A)=>{let t=e(`gh`,[`api`,`user`,`--jq`,`.login`]),n=e(`git`,[`config`,`user.email`]);return{pwd:process.cwd(),gh:t.status===0&&t.stdout.trim()?t.stdout.trim():`none`,gitEmail:n.status===0?n.stdout.trim():`none`,tokenSet:!!process.env.GITHUB_TOKEN}},_t=(e,n=A)=>{let r=[];pe()||r.push({status:`warn`,label:`platform`,detail:`inscope's secret resolution targets macOS (gh keyring + Keychain)`});let i=p(),a=pt(i);a===null?r.push({status:`fail`,label:`hook`,detail:`missing ${i}; run \`inscope init\``}):a===Y(e)?r.push({status:`ok`,label:`hook`,detail:i}):r.push({status:`warn`,label:`hook`,detail:"out of date; run `inscope apply`"}),r.push(Ge()?{status:`ok`,label:`zshrc`,detail:`sources the hook`}:{status:`warn`,label:`zshrc`,detail:"does not source the hook; run `inscope init`"}),e.workspaces.some(q)&&r.push(Ne(h(),K)===null?{status:`fail`,label:`gitconfig`,detail:"missing includeIf block; run `inscope apply`"}:{status:`ok`,label:`gitconfig`,detail:`includeIf block present`});for(let i of e.workspaces){let e=`[${i.name}]`;if(i.gh&&r.push(me(i.gh,n)?{status:`ok`,label:`${e} gh`,detail:`token for ${i.gh}`}:{status:`fail`,label:`${e} gh`,detail:`no token for ${i.gh}; run \`gh auth login\``}),i.servers.slack){let t=i.servers.slack.keychain;r.push(M(t,n)?{status:`ok`,label:`${e} slack`,detail:t}:{status:`fail`,label:`${e} slack`,detail:`${t} not in keychain; run \`${ye(t)}\``})}if(q(i)){let a=J(i.name);if(!t.existsSync(a))r.push({status:`fail`,label:`${e} git`,detail:`missing ${a}; run \`inscope apply\``});else if(i.git?.email){let t=be(a,n);r.push(t===i.git.email?{status:`ok`,label:`${e} git`,detail:i.git.email}:{status:`fail`,label:`${e} git`,detail:`email is ${t??`unset`}, expected ${i.git.email}`})}}let a=ot(i);if(a)r.push({status:`fail`,label:`${e} mcp`,detail:a});else{let t=ue(i);if(t===null)r.push({status:`warn`,label:`${e} mcp`,detail:"no .mcp.json; run `inscope apply`"});else{let n=O(i.name).filter(e=>t.mcpServers?.[e]);r.push({status:`ok`,label:`${e} mcp`,detail:`${n.length} server(s)`}),pt(k(i))!==at(i)&&r.push({status:`warn`,label:`${e} mcp`,detail:"out of date; run `inscope diff`"});let a=mt(t);a.length&&r.push({status:`warn`,label:`${e} mcp`,detail:`unpinned: ${a.join(`, `)}`})}}}return r},vt=`Verify the setup: gh tokens resolve, keychain entries exist,
|
|
115
115
|
git emails match per path, the hook is current, and no MCP server is unpinned.
|
|
116
116
|
Exits non-zero if any check fails.
|
|
117
117
|
|
|
@@ -119,8 +119,8 @@ Usage:
|
|
|
119
119
|
$ ${Z} doctor
|
|
120
120
|
|
|
121
121
|
Options:
|
|
122
|
-
-h, --help Display help message`,
|
|
123
|
-
`);console.log(`\n${a}`);let o=
|
|
122
|
+
-h, --help Display help message`,yt={ok:`✓`,warn:`!`,fail:`✗`},bt={ok:Se,warn:Ce,fail:I},xt=t=>{let{values:n}=e({allowPositionals:!0,options:{help:{type:`boolean`,short:`h`}},args:t});n.help&&(console.log(vt),process.exit(0)),v()||(console.error(`No config found. Run \`${Z} init\` first.`),process.exit(1));let r=y(),i=_t(r),a=i.map(e=>`${bt[e.status](yt[e.status])} ${e.label}${e.detail?` ${e.detail}`:``}`).join(`
|
|
123
|
+
`);console.log(`\n${a}`);let o=ht(r);if(o){let e=gt();console.log(`\nThis shell (${o.name}):`),console.log(` pwd ${e.pwd}`),console.log(` gh ${e.gh}`),console.log(` git ${e.gitEmail}`),console.log(` token ${e.tokenSet?`set`:`unset`}`)}let s=i.filter(e=>e.status===`fail`).length;s&&(console.log(`\n${I(`${s} check(s) failed.`)}`),process.exit(1)),console.log(`\n${Se(`All checks passed.`)}`),process.exit(0)},St=`Edit a configured workspace interactively, then re-apply.
|
|
124
124
|
Pick a workspace (or pass its path/label), step through the prompts pre-filled
|
|
125
125
|
with its current values, and inscope regenerates everything on save.
|
|
126
126
|
|
|
@@ -128,25 +128,25 @@ Usage:
|
|
|
128
128
|
$ ${Z} edit [path|label]
|
|
129
129
|
|
|
130
130
|
Options:
|
|
131
|
-
-h, --help Display help message`,
|
|
132
|
-
Slack uses a user OAuth (xoxp) token.`),b=await z(`Slack keychain service`,b),x=await B(`Allow Slack to post messages?`,x),
|
|
131
|
+
-h, --help Display help message`,Ct=async t=>{let{positionals:n,values:r}=e({allowPositionals:!0,options:{help:{type:`boolean`,short:`h`}},args:t});r.help&&(console.log(St),process.exit(0)),v()||(console.error(`No config found. Run \`${Z} init\` first.`),process.exit(1));let i=y();i.workspaces.length||(console.error(`No workspaces yet. Add one with \`${Z} add <path>\`.`),process.exit(1));let a=n[0],o=await(async()=>{if(a){let e=T(i,a);return e||(console.error(`No workspace matching "${a}".`),process.exit(1)),e}if(i.workspaces.length===1)return i.workspaces[0];if(N())return V(`Edit which workspace?`,i.workspaces.map(e=>({label:`${e.name} (${e.path})`,value:e})));console.error(`Specify a workspace, e.g. \`${Z} edit <label>\`.`),process.exit(1)})();console.log(`\nEditing "${o.name}" (${o.path})\n`);let s=[...ge().map(e=>({label:e,value:e})),{label:`(none)`,value:``}],c=await V(`GitHub account`,s,Math.max(0,s.findIndex(e=>e.value===(o.gh??``))))||void 0,l=o.git?.email,u=await z(l?`Git email (enter keeps ${l}, "-" to inherit global)`:`Git email (enter to inherit global)`,l??``),d=u===`-`?void 0:u||void 0,f=o.git?.name,p=await z(f?`Git name (enter keeps ${f}, "-" to inherit global)`:`Git name (enter to inherit global)`,f??``),m=p===`-`?void 0:p||void 0,h=Je(o.servers),g=await Oe(`MCP servers (space toggles, enter confirms)`,Ke.map(e=>({label:e,value:e,checked:h.includes(e)}))),_=g.includes(`slack`),b=o.servers.slack?o.servers.slack.keychain:qe(o.name),x=o.servers.slack?!!o.servers.slack.addMessageTool:!1,S=!1;if(_&&(console.log(`
|
|
132
|
+
Slack uses a user OAuth (xoxp) token.`),b=await z(`Slack keychain service`,b),x=await B(`Allow Slack to post messages?`,x),M(b)||(S=await B(`Store the Slack token now?`,!0))),_){let e=w(b);e&&(console.error(`\nInvalid Slack keychain service "${b}": ${e}`),process.exit(1))}let C={name:o.name,path:o.path,gh:c,git:d||m?{email:d,name:m}:void 0,servers:Ye(g,_?{keychain:b,addMessageTool:x}:null)};Xe(C),console.log(`\n✓ updated "${C.name}" -> ${C.path}`),await Ze(C,S),console.log(`\nRelaunch \`claude\` from ${C.path} to pick up the changes.`),process.exit(0)},wt=`Set up inscope: create the config, generate the chpwd hook, and
|
|
133
133
|
source it from ~/.zshrc. Safe to run again; it never overwrites your config.
|
|
134
134
|
|
|
135
135
|
Usage:
|
|
136
136
|
$ ${Z} init
|
|
137
137
|
|
|
138
138
|
Options:
|
|
139
|
-
-h, --help Display help message`,
|
|
139
|
+
-h, --help Display help message`,Tt=t=>{let{values:n}=e({allowPositionals:!0,options:{help:{type:`boolean`,short:`h`}},args:t});n.help&&(console.log(wt),process.exit(0));let r;v()?(r=y(),console.log(`\nUsing existing config at ${f()}`)):(r=_(),b(r),console.log(`\nCreated ${f()}`)),X(r),console.log(`Generated the chpwd hook and added a source line to ~/.zshrc.`),console.log(`
|
|
140
140
|
Next steps:
|
|
141
141
|
1. Reload your shell: source ~/.zshrc (or open a new terminal)
|
|
142
|
-
2. Map a workspace: ${Z} add ~/acme`),process.exit(0)},
|
|
142
|
+
2. Map a workspace: ${Z} add ~/acme`),process.exit(0)},Et=`List the configured workspaces. Run \`${Z} doctor\` to verify
|
|
143
143
|
that their tokens and identities actually resolve.
|
|
144
144
|
|
|
145
145
|
Usage:
|
|
146
146
|
$ ${Z} list
|
|
147
147
|
|
|
148
148
|
Options:
|
|
149
|
-
-h, --help Display help message`,
|
|
149
|
+
-h, --help Display help message`,Dt=e=>D.filter(t=>!!e[t]).join(`, `)||`none`,Ot=t=>{let{values:n}=e({allowPositionals:!0,options:{help:{type:`boolean`,short:`h`}},args:t});n.help&&(console.log(Et),process.exit(0)),v()||(console.error(`No config found. Run \`${Z} init\` first.`),process.exit(1));let r=y();r.workspaces.length||(console.log(`No workspaces yet. Add one with \`${Z} add <path> --gh <account>\`.`),process.exit(0));for(let e of r.workspaces)console.log(`\n${e.name}`),console.log(` path ${e.path}`),console.log(` gh ${e.gh??`(none)`}`),console.log(` git ${e.git?.email??`(default)`}`),console.log(` servers ${Dt(e.servers)}`),e.servers.slack&&console.log(` slack keychain: ${e.servers.slack.keychain}`);process.exit(0)},kt=`Remove a workspace mapping. Drops its git include and the MCP
|
|
150
150
|
servers inscope manages; leaves your keychain and gh accounts untouched. Pick a
|
|
151
151
|
workspace, or pass its path/label.
|
|
152
152
|
|
|
@@ -155,7 +155,7 @@ Usage:
|
|
|
155
155
|
|
|
156
156
|
Options:
|
|
157
157
|
-y, --yes Skip the type-the-label confirmation
|
|
158
|
-
-h, --help Display help message`,
|
|
158
|
+
-h, --help Display help message`,At=async t=>{let{positionals:n,values:r}=e({allowPositionals:!0,options:{help:{type:`boolean`,short:`h`},yes:{type:`boolean`,short:`y`}},args:t});r.help&&(console.log(kt),process.exit(0)),v()||(console.error(`No config found. Run \`${Z} init\` first.`),process.exit(1));let i=y();i.workspaces.length||(console.error(`No workspaces to remove.`),process.exit(1));let a=n[0],o;if(a){let e=T(i,a);e||(console.error(`No workspace matching "${a}".`),process.exit(1)),o=e}else N()?o=await V(`Remove which workspace?`,i.workspaces.map(e=>({label:`${e.name} (${e.path})`,value:e}))):(console.error(`Specify a workspace, e.g. \`${Z} rm <label>\`.`),process.exit(1));if(!r.yes){console.log(`\n⚠ Removing "${o.name}" (${o.path}) unmaps it from inscope.`);let e=await z(`Type "${o.name}" to confirm`);e!==o.name&&(console.error(`Aborted: "${e}" does not match "${o.name}".`),process.exit(1))}let{cfg:s}=ae(i,o.name);fe(o),Re(o.name),b(s),X(s),console.log(`\n✓ removed workspace "${o.name}"`),o.servers.slack&&console.log(`\nNote: the keychain entry ${o.servers.slack.keychain} was left in place.\nDelete it with: ${F(`security delete-generic-password -s ${o.servers.slack.keychain}`)}`),process.exit(0)},jt=`Version:
|
|
159
159
|
${Z}@${Qe}
|
|
160
160
|
|
|
161
161
|
Per-workspace identity for Claude Code: scope MCP servers, GitHub auth, and git
|
|
@@ -179,4 +179,4 @@ Options:
|
|
|
179
179
|
-h, --help Display help
|
|
180
180
|
|
|
181
181
|
Author:
|
|
182
|
-
${Q.name} <${Q.email}> (${Q.url})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return
|
|
182
|
+
${Q.name} <${Q.email}> (${Q.url})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return Tt(n);case`add`:return await tt(n);case`edit`:return Ct(n);case`rm`:case`remove`:return await At(n);case`ls`:case`list`:return Ot(n);case`diff`:return ft(n);case`apply`:case`sync`:return rt(n);case`doctor`:return xt(n)}(t===`-v`||t===`--version`)&&(console.log(`${Z}@${Qe}`),process.exit(0)),(!t||t===`-h`||t===`--help`)&&(console.log(jt),process.exit(0)),console.error(`unknown command: ${e.join(` `)}\n`),console.error(jt),process.exit(1)}catch(e){console.error(e.message),process.exit(1)}})();export{};
|
package/dist/index.d.mts
CHANGED
|
@@ -105,11 +105,13 @@ declare const runDoctor: (cfg: Config, run?: Runner) => Check[];
|
|
|
105
105
|
//#endregion
|
|
106
106
|
//#region src/drift.d.ts
|
|
107
107
|
declare const mcpTarget: (ws: Workspace) => string;
|
|
108
|
+
declare const mcpError: (ws: Workspace) => string | null;
|
|
108
109
|
type Drift = {
|
|
109
110
|
label: string;
|
|
110
111
|
path: string;
|
|
111
112
|
current: string;
|
|
112
113
|
next: string;
|
|
114
|
+
error?: string;
|
|
113
115
|
};
|
|
114
116
|
declare const computeDrift: (cfg: Config) => Drift[];
|
|
115
117
|
declare const diffLines: (a: string, b: string) => string;
|
|
@@ -138,4 +140,4 @@ declare const readMcp: (ws: Workspace) => Record<string, any> | null;
|
|
|
138
140
|
declare const applyMcp: (ws: Workspace) => void;
|
|
139
141
|
declare const removeMcp: (ws: Workspace) => void;
|
|
140
142
|
//#endregion
|
|
141
|
-
export { ApplyResult, CONFIG_VERSION, Check, CheckStatus, Config, Drift, HttpServer, RunResult, Runner, SLACK_MCP_VERSION, Servers, SlackServer, WORKSPACE_NAME_RE, Workspace, adoptable, applyAll, applyGitconfig, applyMcp, computeDrift, configExists, currentWorkspace, defaultConfig, defaultRunner, diffLines, ensureZshrcSource, findWorkspace, ghAccounts, ghStatus, ghToken, gitEmailForFile, gitGlobal, hookValueError, isMacOS, keychainHas, keychainSet, keychainSetCommand, labelFromPath, liveSnapshot, loadConfig, managedKeys, mcpFilePath, mcpTarget, readMcp, removeMcp, removeWorkspace, renderGitInclude, renderHook, renderMcp, renderPerWorkspaceGitconfig, renderServers, renderZshrcSource, runDoctor, saveConfig, slugify, upsertWorkspace, validateConfig, workspaceNameError, workspacePathError, zshrcSourcesHook };
|
|
143
|
+
export { ApplyResult, CONFIG_VERSION, Check, CheckStatus, Config, Drift, HttpServer, RunResult, Runner, SLACK_MCP_VERSION, Servers, SlackServer, WORKSPACE_NAME_RE, Workspace, adoptable, applyAll, applyGitconfig, applyMcp, computeDrift, configExists, currentWorkspace, defaultConfig, defaultRunner, diffLines, ensureZshrcSource, findWorkspace, ghAccounts, ghStatus, ghToken, gitEmailForFile, gitGlobal, hookValueError, isMacOS, keychainHas, keychainSet, keychainSetCommand, labelFromPath, liveSnapshot, loadConfig, managedKeys, mcpError, mcpFilePath, mcpTarget, readMcp, removeMcp, removeWorkspace, renderGitInclude, renderHook, renderMcp, renderPerWorkspaceGitconfig, renderServers, renderZshrcSource, runDoctor, saveConfig, slugify, upsertWorkspace, validateConfig, workspaceNameError, workspacePathError, zshrcSourcesHook };
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import e from"node:fs";import t from"node:path";import n from"node:os";import{spawnSync as r}from"node:child_process";const i=()=>n.homedir(),a=()=>process.env.XDG_CONFIG_HOME?.trim()||t.join(i(),`.config`),o=e=>e===`~`?i():e.startsWith(`~/`)?t.join(i(),e.slice(2)):e,s=e=>{let n=o(e),r=i();return n===r?`~`:n.startsWith(r+t.sep)?`~/`+n.slice(r.length+1):n},c=e=>t.resolve(o(e)),l=()=>t.join(a(),`inscope`),u=()=>t.join(l(),`inscope.json`),d=()=>t.join(l(),`inscope.zsh`),f=()=>t.join(l(),`git`),p=()=>t.join(i(),`.gitconfig`),m=()=>t.join(i(),`.zshrc`),ee=1,te=()=>({version:1,workspaces:[]}),ne=()=>e.existsSync(u()),re=()=>{let t=u(),n=e.readFileSync(t,`utf8`),r=JSON.parse(n);try{
|
|
2
|
-
`)},h=/^[A-Za-z0-9._-]+$/,g=e=>e?h.test(e)?null:`use only letters, digits, dot (.), dash (-), or underscore (_)`:`must not be empty`,ae=/["`$\n]/,_=e=>ae.test(e)?'must not contain a quote ("), backtick (`), $, or newline':null,v=e=>e?_(e):`must not be empty`,y=e=>e.toLowerCase().replace(/[^a-z0-9._-]+/g,`-`).replace(/^[-.]+|[-.]+$/g,``),
|
|
1
|
+
import e from"node:fs";import t from"node:path";import n from"node:os";import{spawnSync as r}from"node:child_process";const i=()=>n.homedir(),a=()=>process.env.XDG_CONFIG_HOME?.trim()||t.join(i(),`.config`),o=e=>e===`~`?i():e.startsWith(`~/`)?t.join(i(),e.slice(2)):e,s=e=>{let n=o(e),r=i();return n===r?`~`:n.startsWith(r+t.sep)?`~/`+n.slice(r.length+1):n},c=e=>t.resolve(o(e)),l=()=>t.join(a(),`inscope`),u=()=>t.join(l(),`inscope.json`),d=()=>t.join(l(),`inscope.zsh`),f=()=>t.join(l(),`git`),p=()=>t.join(i(),`.gitconfig`),m=()=>t.join(i(),`.zshrc`),ee=1,te=()=>({version:1,workspaces:[]}),ne=()=>e.existsSync(u()),re=()=>{let t=u(),n=e.readFileSync(t,`utf8`),r=JSON.parse(n);try{oe(r)}catch(e){let n=e instanceof Error?e.message:String(e);throw Error(`${n}\nFix it in ${s(t)}, then re-run.`)}return r},ie=n=>{let r=u();e.mkdirSync(t.dirname(r),{recursive:!0}),e.writeFileSync(r,JSON.stringify(n,null,2)+`
|
|
2
|
+
`)},h=/^[A-Za-z0-9._-]+$/,g=e=>e?h.test(e)?null:`use only letters, digits, dot (.), dash (-), or underscore (_)`:`must not be empty`,ae=/["`$\n]/,_=e=>ae.test(e)?'must not contain a quote ("), backtick (`), $, or newline':null,v=e=>e?_(e):`must not be empty`,y=e=>e.toLowerCase().replace(/[^a-z0-9._-]+/g,`-`).replace(/^[-.]+|[-.]+$/g,``),oe=e=>{if(!e||typeof e!=`object`)throw Error(`config is not an object`);if(!Array.isArray(e.workspaces))throw Error(`config.workspaces must be an array`);let t=new Set;for(let n of e.workspaces){if(!n.name)throw Error(`a workspace is missing a name`);let e=g(n.name);if(e)throw Error(`workspace name "${n.name}" is invalid: ${e}`);if(!n.path)throw Error(`workspace "${n.name}" is missing a path`);let r=v(n.path);if(r)throw Error(`workspace "${n.name}" path "${n.path}" is invalid: ${r}`);if(n.gh){let e=_(n.gh);if(e)throw Error(`workspace "${n.name}" gh account "${n.gh}" is invalid: ${e}`)}let i=n.servers?.slack;if(i&&i.keychain){let e=_(i.keychain);if(e)throw Error(`workspace "${n.name}" Slack keychain "${i.keychain}" is invalid: ${e}`)}if(t.has(n.name))throw Error(`duplicate workspace name "${n.name}"`);t.add(n.name)}},se=e=>y(t.basename(c(e)))||`workspace`,b=(e,t)=>{let n=e.workspaces.find(e=>e.name===t);if(n)return n;let r=c(t);return e.workspaces.find(e=>c(e.path)===r)},ce=(e,t)=>{let n=e.workspaces.filter(e=>e.name!==t.name);return n.push({...t,path:s(t.path)}),n.sort((e,t)=>e.name.localeCompare(t.name)),{...e,workspaces:n}},le=(e,t)=>{let n=b(e,t);return n?{cfg:{...e,workspaces:e.workspaces.filter(e=>e.name!==n.name)},removed:n}:{cfg:e}},x=e=>`# >>> inscope:${e} >>>`,S=e=>`# <<< inscope:${e} <<<`,C=e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),w=e=>RegExp(`${C(x(e))}\\n[\\s\\S]*?\\n${C(S(e))}\\n?`),ue=(e,t)=>{let n=t.replace(/\n+$/,``);return`${x(e)}\n${n}\n${S(e)}\n`},T=t=>{try{return e.readFileSync(t,`utf8`)}catch{return``}},de=(n,r,i)=>{e.mkdirSync(t.dirname(n),{recursive:!0});let a=T(n),o=ue(r,i),s=w(r),c;if(s.test(a))c=a.replace(s,o);else{let e=a.replace(/\n*$/,``);c=e.length?`${e}\n\n${o}`:o}e.writeFileSync(n,c)},fe=(t,n)=>{let r=T(t);if(!r)return;let i=r.replace(w(n),``).replace(/\n{3,}/g,`
|
|
3
3
|
|
|
4
|
-
`).replace(/^\n+/,``);e.writeFileSync(t,i)},
|
|
5
|
-
`),
|
|
4
|
+
`).replace(/^\n+/,``);e.writeFileSync(t,i)},E=(e,t)=>{let n=T(e).match(RegExp(`${C(x(t))}\\n([\\s\\S]*?)\\n${C(S(t))}`));return n?n[1]:null},D=`gitconfig`,O=e=>!!(e.git&&(e.git.email||e.git.name)),k=e=>t.join(f(),`${e}.gitconfig`),pe=e=>s(e).replace(/\/+$/,``)+`/`,A=e=>e.workspaces.filter(O).map(e=>`[includeIf "gitdir:${pe(e.path)}"]\n\tpath = ${s(k(e.name))}`).join(`
|
|
5
|
+
`),j=e=>{let t=[`# Managed by inscope. Do not edit by hand.`,`[user]`];return e.git?.email&&t.push(`\temail = ${e.git.email}`),e.git?.name&&t.push(`\tname = ${e.git.name}`),t.join(`
|
|
6
6
|
`)+`
|
|
7
|
-
`},
|
|
7
|
+
`},M=t=>{e.mkdirSync(f(),{recursive:!0});for(let n of t.workspaces)O(n)&&e.writeFileSync(k(n.name),j(n));let n=A(t);n?de(p(),D,n):fe(p(),D)},me=e=>{let t=s(e);return t===`~`?`"$HOME/"*`:t.startsWith(`~/`)?`"$HOME/${t.slice(2)}/"*`:`"${t}/"*`},he=e=>e.servers.slack?e.servers.slack.keychain:``,N=e=>{let t=[...e.workspaces].sort((e,t)=>e.name.localeCompare(t.name));return`# Managed by inscope. Do not edit by hand.
|
|
8
8
|
# Source of truth: ~/.config/inscope/inscope.json
|
|
9
9
|
# Edit there, then run \`inscope apply\` to regenerate this file.
|
|
10
10
|
#
|
|
@@ -15,7 +15,7 @@ import e from"node:fs";import t from"node:path";import n from"node:os";import{sp
|
|
|
15
15
|
__inscope_resolve_identity() {
|
|
16
16
|
local ws
|
|
17
17
|
case "\${PWD}/" in
|
|
18
|
-
${t.map(e=>` ${
|
|
18
|
+
${t.map(e=>` ${me(e.path)}) ws="${e.name}" ;;`).join(`
|
|
19
19
|
`)||` # no workspaces configured`}
|
|
20
20
|
*) ws="" ;;
|
|
21
21
|
esac
|
|
@@ -25,7 +25,7 @@ ${t.map(e=>` ${pe(e.path)}) ws="${e.name}" ;;`).join(`
|
|
|
25
25
|
|
|
26
26
|
local gh_user="" slack_svc=""
|
|
27
27
|
case "$ws" in
|
|
28
|
-
${t.map(e=>{let t=[];e.gh&&t.push(`gh_user="${e.gh}"`);let n=
|
|
28
|
+
${t.map(e=>{let t=[];e.gh&&t.push(`gh_user="${e.gh}"`);let n=he(e);return n&&t.push(`slack_svc="${n}"`),` ${e.name}) ${t.length?t.join(`; `):`:`} ;;`}).join(`
|
|
29
29
|
`)||` # no workspaces configured`}
|
|
30
30
|
*) return ;; # outside a mapped workspace: nothing set
|
|
31
31
|
esac
|
|
@@ -51,10 +51,10 @@ autoload -Uz add-zsh-hook
|
|
|
51
51
|
add-zsh-hook chpwd __inscope_resolve_identity
|
|
52
52
|
__inscope_ws="__init__" # force the first resolve, clearing any inherited token
|
|
53
53
|
__inscope_resolve_identity
|
|
54
|
-
`},
|
|
55
|
-
`)},ye=t=>{let n=
|
|
56
|
-
`)},be=e=>{let n=i();return e===n?`$HOME`:e.startsWith(n+t.sep)?`$HOME/${e.slice(n.length+1)}`:e},U=()=>{let e=be(d());return`[ -r "${e}" ] && source "${e}"`},W=e=>{let t=U();if(e.includes(t))return e;let n=e.replace(/\n*$/,``),r=`# inscope: load each workspace's tokens (GitHub, Slack) from \$PWD on every cd\n${t}`;return n.length?`${n}\n\n${r}\n`:`${r}\n`},G=()=>{let t=m(),n=``;try{n=e.readFileSync(t,`utf8`)}catch{}let r=W(n);r!==n&&e.writeFileSync(t,r)},K=()=>{try{return e.readFileSync(m(),`utf8`).includes(U())}catch{return!1}},xe=n=>{let r=d();e.mkdirSync(t.dirname(r),{recursive:!0}),e.writeFileSync(r,
|
|
57
|
-
`},Se=e=>{let t=[],n=d();t.push({label:`hook`,path:n,current:q(n),next:
|
|
54
|
+
`},P=`1.3.0`,F={atlassian:`https://mcp.atlassian.com/v1/mcp`,canva:`https://mcp.canva.com/mcp`,clickup:`https://mcp.clickup.com/mcp`,hubspot:`https://mcp.hubspot.com`,intercom:`https://mcp.intercom.com/mcp`,linear:`https://mcp.linear.app/mcp`,monday:`https://mcp.monday.com/mcp`,notion:`https://mcp.notion.com/mcp`,plane:`https://mcp.plane.so/http/mcp`,sentry:`https://mcp.sentry.dev/mcp`,stripe:`https://mcp.stripe.com`,vercel:`https://mcp.vercel.com`,webflow:`https://mcp.webflow.com/`},I=[`github`,`atlassian`,`canva`,`clickup`,`hubspot`,`intercom`,`linear`,`monday`,`notion`,`plane`,`sentry`,`slack`,`stripe`,`vercel`,`webflow`],L=e=>I.map(t=>`${t}-${e}`),R=e=>t.join(c(e.path),`.mcp.json`),ge=(e,t)=>e&&typeof e==`object`&&e.url?e.url:t,z=e=>{let t=e.servers,n={};for(let r of I){let i=t[r];if(!i)continue;let a=`${r}-${e.name}`;if(r===`github`)n[a]={type:`http`,url:`https://api.githubcopilot.com/mcp/`,headers:{Authorization:"Bearer ${GITHUB_TOKEN}"}};else if(r===`slack`){let e=i,t={SLACK_MCP_XOXP_TOKEN:"${SLACK_MCP_XOXP_TOKEN}"};e.addMessageTool&&(t.SLACK_MCP_ADD_MESSAGE_TOOL=`true`),n[a]={type:`stdio`,command:`npx`,args:[`-y`,`slack-mcp-server@${P}`,`--transport`,`stdio`],env:t}}else n[a]={type:`http`,url:ge(i,F[r])}}return n},_e=e=>({mcpServers:z(e)}),ve=t=>{if(!e.existsSync(t))return{};try{return JSON.parse(e.readFileSync(t,`utf8`))}catch{return{}}},B=t=>{if(!e.existsSync(t))return{};try{return JSON.parse(e.readFileSync(t,`utf8`))}catch{throw Error(`${t} is not valid JSON; fix or remove it, then re-run inscope (left it untouched)`)}},V=t=>{let n=R(t);return e.existsSync(n)?ve(n):null},H=n=>{let r=R(n);e.mkdirSync(t.dirname(r),{recursive:!0});let i=B(r),a=i.mcpServers&&typeof i.mcpServers==`object`?{...i.mcpServers}:{};for(let e of L(n.name))delete a[e];Object.assign(a,z(n)),i.mcpServers=a,e.writeFileSync(r,JSON.stringify(i,null,2)+`
|
|
55
|
+
`)},ye=t=>{let n=R(t);if(!e.existsSync(n))return;let r=B(n);if(r.mcpServers&&typeof r.mcpServers==`object`)for(let e of L(t.name))delete r.mcpServers[e];e.writeFileSync(n,JSON.stringify(r,null,2)+`
|
|
56
|
+
`)},be=e=>{let n=i();return e===n?`$HOME`:e.startsWith(n+t.sep)?`$HOME/${e.slice(n.length+1)}`:e},U=()=>{let e=be(d());return`[ -r "${e}" ] && source "${e}"`},W=e=>{let t=U();if(e.includes(t))return e;let n=e.replace(/\n*$/,``),r=`# inscope: load each workspace's tokens (GitHub, Slack) from \$PWD on every cd\n${t}`;return n.length?`${n}\n\n${r}\n`:`${r}\n`},G=()=>{let t=m(),n=``;try{n=e.readFileSync(t,`utf8`)}catch{}let r=W(n);r!==n&&e.writeFileSync(t,r)},K=()=>{try{return e.readFileSync(m(),`utf8`).includes(U())}catch{return!1}},xe=n=>{let r=d();e.mkdirSync(t.dirname(r),{recursive:!0}),e.writeFileSync(r,N(n)),M(n),G();let i=[];for(let e of n.workspaces)H(e),i.push(R(e));return{hook:r,gitconfig:n.workspaces.some(e=>e.git?.email||e.git?.name),mcp:i}},q=t=>{try{return e.readFileSync(t,`utf8`)}catch{return``}},J=e=>{let t=q(e);if(!t)return null;try{return JSON.parse(t)}catch{return null}},Y=e=>{let t=J(R(e))??{},n=t.mcpServers&&typeof t.mcpServers==`object`?{...t.mcpServers}:{};for(let t of L(e.name))delete n[t];return Object.assign(n,z(e)),t.mcpServers=n,JSON.stringify(t,null,2)+`
|
|
57
|
+
`},X=t=>{let n=R(t);if(!e.existsSync(n))return null;try{return JSON.parse(e.readFileSync(n,`utf8`)),null}catch{return"invalid JSON; `apply` will not touch it until you fix it"}},Se=e=>{let t=[],n=d();t.push({label:`hook`,path:n,current:q(n),next:N(e)}),t.push({label:`gitconfig`,path:p(),current:E(p(),D)??``,next:A(e)});for(let n of e.workspaces){if(!O(n))continue;let e=k(n.name);t.push({label:`gitconfig:${n.name}`,path:e,current:q(e),next:j(n)})}for(let n of e.workspaces){let e=R(n),r=X(n);if(r){t.push({label:`mcp:${n.name}`,path:e,current:``,next:``,error:r});continue}t.push({label:`mcp:${n.name}`,path:e,current:q(e),next:Y(n)})}return t.filter(e=>e.error!=null||e.current!==e.next)},Ce=(e,t)=>{let n=e.length?e.split(`
|
|
58
58
|
`):[],r=t.length?t.split(`
|
|
59
59
|
`):[],i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array.from({length:a+1},()=>0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)o[e][t]=n[e]===r[t]?o[e+1][t+1]+1:Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push(` ${n[c]}`),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push(`- ${n[c]}`),c++):(s.push(`+ ${r[l]}`),l++);for(;c<i;)s.push(`- ${n[c++]}`);for(;l<a;)s.push(`+ ${r[l++]}`);return s.join(`
|
|
60
|
-
`)},we=e=>{let t=[],n=e.workspaces.map(e=>{let n=J(
|
|
60
|
+
`)},we=e=>{let t=[],n=e.workspaces.map(e=>{let n=J(R(e))?.mcpServers;if(!n||typeof n!=`object`)return e;let r=e.servers,i=e.servers.slack;i&&!i.addMessageTool&&n[`slack-${e.name}`]?.env?.SLACK_MCP_ADD_MESSAGE_TOOL===`true`&&(r={...r,slack:{...i,addMessageTool:!0}},t.push(`${e.name}: slack.addMessageTool = true`));for(let i of I){if(i===`github`||i===`slack`)continue;let a=n[`${i}-${e.name}`]?.url;if(typeof a!=`string`)continue;let o=e.servers[i];if(!o){r={...r,[i]:a===F[i]?!0:{url:a}},t.push(`${e.name}: ${i} = ${a===F[i]?`enabled`:a}`);continue}a!==((typeof o==`object`?o.url:void 0)??F[i])&&(r={...r,[i]:{url:a}},t.push(`${e.name}: ${i}.url = ${a}`))}return r===e.servers?e:{...e,servers:r}});return{cfg:{...e,workspaces:n},changes:t}},Z=(e,t,n)=>{let i=r(e,t,{encoding:`utf8`,input:n?.input});return{status:i.status??(i.error?127:1),stdout:i.stdout??``,stderr:i.stderr??``}},Te=()=>process.platform===`darwin`,Q=()=>process.env.USER||``,Ee=(e,t=Z)=>{let n=t(`gh`,[`auth`,`token`,`-u`,e]),r=n.stdout.trim();return n.status===0&&r?r:null},De=(e=Z)=>{let t=e(`gh`,[`auth`,`status`]);return(t.stdout+t.stderr).trim()},Oe=(e=Z)=>{let t=[];for(let n of De(e).matchAll(/account (\S+) \(/g))t.includes(n[1])||t.push(n[1]);return t},ke=(e,t=Z)=>{let n=t(`git`,[`config`,`--global`,e]),r=n.stdout.trim();return n.status===0&&r?r:null},Ae=(e,t=Z)=>{let n=t(`security`,[`find-generic-password`,`-a`,Q(),`-s`,e,`-w`]);return n.status===0&&n.stdout.trim().length>0},je=(e,t,n=Z)=>{let r=n(`security`,[`add-generic-password`,`-U`,`-a`,Q(),`-s`,e,`-w`,t]);if(r.status!==0)throw Error(`security add-generic-password failed: ${r.stderr.trim()||`unknown error`}`)},Me=e=>`security add-generic-password -U -a "${Q()||`$USER`}" -s ${e} -w 'xoxp-...'`,Ne=(e,t=Z)=>{let n=t(`git`,[`config`,`--file`,e,`user.email`]);return n.status===0?n.stdout.trim():null},$=t=>{try{return e.readFileSync(t,`utf8`)}catch{return null}},Pe=e=>{let t=[],n=e?.mcpServers;if(!n||typeof n!=`object`)return t;for(let[e,r]of Object.entries(n)){let n=Array.isArray(r?.args)?r.args:[];if(n.some(e=>typeof e==`string`&&e.endsWith(`@latest`)))t.push(e);else if(r?.command===`npx`){let r=n.find(e=>typeof e==`string`&&!e.startsWith(`-`));r&&!r.includes(`@`)&&t.push(e)}}return t},Fe=(e,n=process.cwd())=>{let r=t.resolve(n);return e.workspaces.find(e=>{let n=c(e.path);return r===n||r.startsWith(n+t.sep)})},Ie=(e=Z)=>{let t=e(`gh`,[`api`,`user`,`--jq`,`.login`]),n=e(`git`,[`config`,`user.email`]);return{pwd:process.cwd(),gh:t.status===0&&t.stdout.trim()?t.stdout.trim():`none`,gitEmail:n.status===0?n.stdout.trim():`none`,tokenSet:!!process.env.GITHUB_TOKEN}},Le=(t,n=Z)=>{let r=[];Te()||r.push({status:`warn`,label:`platform`,detail:`inscope's secret resolution targets macOS (gh keyring + Keychain)`});let i=d(),a=$(i);a===null?r.push({status:`fail`,label:`hook`,detail:`missing ${i}; run \`inscope init\``}):a===N(t)?r.push({status:`ok`,label:`hook`,detail:i}):r.push({status:`warn`,label:`hook`,detail:"out of date; run `inscope apply`"}),r.push(K()?{status:`ok`,label:`zshrc`,detail:`sources the hook`}:{status:`warn`,label:`zshrc`,detail:"does not source the hook; run `inscope init`"}),t.workspaces.some(O)&&r.push(E(p(),D)===null?{status:`fail`,label:`gitconfig`,detail:"missing includeIf block; run `inscope apply`"}:{status:`ok`,label:`gitconfig`,detail:`includeIf block present`});for(let i of t.workspaces){let t=`[${i.name}]`;if(i.gh&&r.push(Ee(i.gh,n)?{status:`ok`,label:`${t} gh`,detail:`token for ${i.gh}`}:{status:`fail`,label:`${t} gh`,detail:`no token for ${i.gh}; run \`gh auth login\``}),i.servers.slack){let e=i.servers.slack.keychain;r.push(Ae(e,n)?{status:`ok`,label:`${t} slack`,detail:e}:{status:`fail`,label:`${t} slack`,detail:`${e} not in keychain; run \`${Me(e)}\``})}if(O(i)){let a=k(i.name);if(!e.existsSync(a))r.push({status:`fail`,label:`${t} git`,detail:`missing ${a}; run \`inscope apply\``});else if(i.git?.email){let e=Ne(a,n);r.push(e===i.git.email?{status:`ok`,label:`${t} git`,detail:i.git.email}:{status:`fail`,label:`${t} git`,detail:`email is ${e??`unset`}, expected ${i.git.email}`})}}let a=X(i);if(a)r.push({status:`fail`,label:`${t} mcp`,detail:a});else{let e=V(i);if(e===null)r.push({status:`warn`,label:`${t} mcp`,detail:"no .mcp.json; run `inscope apply`"});else{let n=L(i.name).filter(t=>e.mcpServers?.[t]);r.push({status:`ok`,label:`${t} mcp`,detail:`${n.length} server(s)`}),$(R(i))!==Y(i)&&r.push({status:`warn`,label:`${t} mcp`,detail:"out of date; run `inscope diff`"});let a=Pe(e);a.length&&r.push({status:`warn`,label:`${t} mcp`,detail:`unpinned: ${a.join(`, `)}`})}}}return r};export{ee as CONFIG_VERSION,P as SLACK_MCP_VERSION,h as WORKSPACE_NAME_RE,we as adoptable,xe as applyAll,M as applyGitconfig,H as applyMcp,Se as computeDrift,ne as configExists,Fe as currentWorkspace,te as defaultConfig,Z as defaultRunner,Ce as diffLines,G as ensureZshrcSource,b as findWorkspace,Oe as ghAccounts,De as ghStatus,Ee as ghToken,Ne as gitEmailForFile,ke as gitGlobal,_ as hookValueError,Te as isMacOS,Ae as keychainHas,je as keychainSet,Me as keychainSetCommand,se as labelFromPath,Ie as liveSnapshot,re as loadConfig,L as managedKeys,X as mcpError,R as mcpFilePath,Y as mcpTarget,V as readMcp,ye as removeMcp,le as removeWorkspace,A as renderGitInclude,N as renderHook,_e as renderMcp,j as renderPerWorkspaceGitconfig,z as renderServers,W as renderZshrcSource,Le as runDoctor,ie as saveConfig,y as slugify,ce as upsertWorkspace,oe as validateConfig,g as workspaceNameError,v as workspacePathError,K as zshrcSourcesHook};
|
package/package.json
CHANGED