@vpxa/aikit 0.1.51 → 0.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vpxa/aikit",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "type": "module",
5
5
  "description": "Local-first AI developer toolkit — knowledge base, code analysis, context management, and developer tools for LLM agents",
6
6
  "license": "MIT",
@@ -5,7 +5,10 @@ declare class ClaudePluginAdapter implements FlowFormatAdapter {
5
5
  readonly format: "claude-plugin";
6
6
  detect(sourceDir: string): boolean;
7
7
  parse(sourceDir: string, options?: FlowParseOptions): Promise<FlowManifest>;
8
+ private readPluginJson;
9
+ private readString;
8
10
  private discoverSteps;
11
+ private readStep;
9
12
  /**
10
13
  * Copy supporting subdirectories (references/, assets/, scripts/) from
11
14
  * skills/<step>/ to steps/<step>/ so that relative paths inside the
@@ -14,6 +17,8 @@ declare class ClaudePluginAdapter implements FlowFormatAdapter {
14
17
  * When `force` is true, existing copies are replaced (used during updates).
15
18
  */
16
19
  private syncStepAssets;
20
+ private materializeNativeSteps;
21
+ private buildStepReadme;
17
22
  private discoverAgents;
18
23
  }
19
24
  //#endregion
@@ -1 +1,3 @@
1
- import{cpSync as e,existsSync as t,readFileSync as n,readdirSync as r,rmSync as i}from"node:fs";import{basename as a,join as o}from"node:path";const s=[`spec`,`plan`,`task`,`execute`,`verify`];var c=class{format=`claude-plugin`;detect(e){return t(o(e,`.claude-plugin`,`plugin.json`))}async parse(e,t){let r=o(e,`.claude-plugin`,`plugin.json`),i=JSON.parse(n(r,`utf-8`)),s=this.discoverSteps(e),c=this.discoverAgents(e);return this.syncStepAssets(e,s,t?.forceAssetSync),{name:i.name??a(e),version:i.version??`1.0.0`,description:i.description??``,author:i.author,steps:s,agents:c,artifacts_dir:`.spec`,install:[]}}discoverSteps(e){let n=o(e,`skills`);if(!t(n))return[];let i=r(n,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).sort((e,t)=>{let n=s.indexOf(e),r=s.indexOf(t);return n!==-1&&r!==-1?n-r:n===-1?r===-1?e.localeCompare(t):1:-1});return i.map((e,t)=>({id:e,name:e.charAt(0).toUpperCase()+e.slice(1),instruction:`steps/${e}/README.md`,produces:[`${e}.md`],requires:t>0?[i[t-1]]:[],agents:[],description:`${e} step`}))}syncStepAssets(n,r,a){let s=[`references`,`assets`,`scripts`];for(let c of r)for(let r of s){let s=o(n,`skills`,c.id,r),l=o(n,`steps`,c.id,r);if(t(s)){if(t(l)){if(!a)continue;i(l,{recursive:!0,force:!0})}e(s,l,{recursive:!0})}}}discoverAgents(e){let n=o(e,`agents`);return t(n)?r(n,{withFileTypes:!0}).filter(e=>e.isFile()&&e.name.endsWith(`.md`)).map(e=>`agents/${e.name}`):[]}};export{c as ClaudePluginAdapter};
1
+ import{cpSync as e,existsSync as t,mkdirSync as n,readFileSync as r,readdirSync as i,rmSync as a,writeFileSync as o}from"node:fs";import{basename as s,join as c}from"node:path";const l=[`spec`,`plan`,`task`,`execute`,`verify`];function u(e){let t=e.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);if(!t)return{data:{},body:e};let n={};for(let e of t[1].split(/\r?\n/)){let t=e.indexOf(`:`);t>0&&(n[e.slice(0,t).trim()]=e.slice(t+1).trim().replace(/^["']|["']$/g,``))}return{data:n,body:t[2]}}function d(e){return e.split(/[-_]/g).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}function f(e){let t=l.indexOf(e);return t===-1?1/0:t}var p=class{format=`claude-plugin`;detect(e){return t(c(e,`.claude-plugin`,`plugin.json`))}async parse(e,t){let n=this.readPluginJson(e),r=this.discoverSteps(e);await this.materializeNativeSteps(e,r,t);let i=this.discoverAgents(e),a=r.map(({step:e})=>e);return{name:this.readString(n.name)??s(e),version:this.readString(n.version)??`1.0.0`,description:this.readString(n.description)??``,author:this.readString(n.author),steps:a,agents:i,artifacts_dir:`.spec`,install:[]}}readPluginJson(e){let t=c(e,`.claude-plugin`,`plugin.json`);return JSON.parse(r(t,`utf-8`))}readString(e){return typeof e==`string`?e:void 0}discoverSteps(e){let n=c(e,`skills`);if(!t(n))return[];let r=i(n,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).map(t=>this.readStep(e,t)).filter(e=>e!==null).sort((e,t)=>{let n=Number(e.metadata.order),r=Number(t.metadata.order),i=Number.isFinite(n),a=Number.isFinite(r);if(i&&a)return n-r;if(i)return-1;if(a)return 1;let o=f(e.step.id),s=f(t.step.id);return o===s?e.step.id.localeCompare(t.step.id):o-s});return r.map((e,t)=>({...e,step:{...e.step,requires:t>0?[r[t-1].step.id]:[]}}))}readStep(e,n){let i=c(e,`skills`,n,`SKILL.md`);if(!t(i))return null;let a=r(i,`utf-8`),{data:o,body:s}=u(a),l=o.name||d(n),f=o.description||`${l} step`;return{step:{id:n,name:l,instruction:`steps/${n}/README.md`,produces:[`${n}.md`],requires:[],agents:[],description:f},metadata:o,content:a,body:s}}syncStepAssets(n,r,i){let o=[`references`,`assets`,`scripts`];for(let s of r)for(let r of o){let o=c(n,`skills`,s.id,r),l=c(n,`steps`,s.id,r);if(t(o)){if(t(l)){if(!i)continue;a(l,{recursive:!0,force:!0})}e(o,l,{recursive:!0})}}}async materializeNativeSteps(e,t,r){for(let i of t){let t=c(e,`steps`,i.step.id);n(t,{recursive:!0});let a=r?.transform?await r.transform({content:i.content,sourceFormat:`claude-plugin`,stepName:i.step.id,metadata:i.metadata}):this.buildStepReadme(i.step.name,i.step.description,i.body);o(c(t,`README.md`),a,`utf-8`)}this.syncStepAssets(e,t.map(({step:e})=>e),r?.forceAssetSync)}buildStepReadme(e,t,n){return[`# ${e}`,``,t,``,`---`,``,n.trim()].join(`
2
+ `).trimEnd().concat(`
3
+ `)}discoverAgents(e){let n=c(e,`agents`);return t(n)?i(n,{withFileTypes:!0}).filter(e=>e.isFile()&&e.name.endsWith(`.md`)).map(e=>`agents/${e.name}`):[]}};export{p as ClaudePluginAdapter};
@@ -1,10 +1,12 @@
1
- import { FlowFormatAdapter, FlowManifest } from "../types.js";
1
+ import { FlowFormatAdapter, FlowManifest, FlowParseOptions } from "../types.js";
2
2
 
3
3
  //#region packages/flows/src/adapters/copilot.d.ts
4
4
  declare class CopilotAdapter implements FlowFormatAdapter {
5
5
  readonly format: "copilot";
6
6
  detect(sourceDir: string): boolean;
7
- parse(sourceDir: string): Promise<FlowManifest>;
7
+ parse(sourceDir: string, options?: FlowParseOptions): Promise<FlowManifest>;
8
+ private getStepId;
9
+ private materializeSteps;
8
10
  }
9
11
  //#endregion
10
12
  export { CopilotAdapter };
@@ -1 +1 @@
1
- import{existsSync as e,readdirSync as t}from"node:fs";import{basename as n,join as r}from"node:path";var i=class{format=`copilot`;detect(t){return e(r(t,`.github`,`agents`))}async parse(e){let i=t(r(e,`.github`,`agents`),{withFileTypes:!0}).filter(e=>e.isFile()&&e.name.endsWith(`.md`)).map(e=>`.github/agents/${e.name}`),a=i.map((e,t)=>{let r=n(e,`.md`).toLowerCase().replace(/\.agent$/,``);return{id:r,name:r.charAt(0).toUpperCase()+r.slice(1),instruction:e,produces:[`${r}.md`],requires:t>0?[i[t-1].replace(/.*\//,``).replace(/\.md$/,``).toLowerCase()]:[],agents:[e],description:`${r} agent step`}});return{name:n(e),version:`1.0.0`,description:`Copilot agents flow from ${n(e)}`,steps:a,agents:i,artifacts_dir:`.spec`,install:[]}}};export{i as CopilotAdapter};
1
+ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,writeFileSync as i}from"node:fs";import{basename as a,join as o}from"node:path";var s=class{format=`copilot`;detect(t){return e(o(t,`.github`,`agents`))}async parse(e,t){let n=r(o(e,`.github`,`agents`),{withFileTypes:!0}).filter(e=>e.isFile()&&e.name.endsWith(`.md`)).map(e=>`.github/agents/${e.name}`),i=n.map(e=>this.getStepId(e)),s=n.map((e,t)=>{let n=i[t];return{id:n,name:n.charAt(0).toUpperCase()+n.slice(1),instruction:`steps/${n}/README.md`,produces:[`${n}.md`],requires:t>0?[i[t-1]]:[],agents:[e],description:`${n} agent step`}});return await this.materializeSteps(e,s,t),{name:a(e),version:`1.0.0`,description:`Copilot agents flow from ${a(e)}`,steps:s,agents:n,artifacts_dir:`.spec`,install:[]}}getStepId(e){return a(e,`.md`).toLowerCase().replace(/\.agent$/,``)}async materializeSteps(e,r,a){for(let s of r){let r=s.agents[0];if(!r)continue;let c=n(o(e,r),`utf-8`),l=o(e,`steps`,s.id),u=o(l,`README.md`);t(l,{recursive:!0}),i(u,a?.transform?await a.transform({content:c,sourceFormat:`copilot`,stepName:s.id,metadata:{sourcePath:r,displayName:s.name}}):`# ${s.name}\n\n${c}`,`utf-8`)}}};export{s as CopilotAdapter};
@@ -1,25 +1,40 @@
1
1
  import { FlowFormatAdapter, FlowManifest, FlowParseOptions } from "../types.js";
2
2
 
3
3
  //#region packages/flows/src/adapters/openspec.d.ts
4
+ /**
5
+ * OpenSpec adapter — detects repos that follow the OpenSpec workflow
6
+ * (https://github.com/Fission-AI/OpenSpec) and converts them to FlowManifest.
7
+ *
8
+ * Detection marker: `openspec/config.yaml` in the source directory.
9
+ *
10
+ * Flow steps are derived from the active OpenSpec schema.yaml.
11
+ */
4
12
  declare class OpenSpecAdapter implements FlowFormatAdapter {
5
13
  readonly format: "openspec";
6
14
  detect(sourceDir: string): boolean;
7
- parse(sourceDir: string, _options?: FlowParseOptions): Promise<FlowManifest>;
15
+ parse(sourceDir: string, options?: FlowParseOptions): Promise<FlowManifest>;
8
16
  /**
9
17
  * Read metadata from package.json if available, otherwise use defaults
10
18
  * derived from the openspec config.
11
19
  */
12
20
  private readMeta;
13
- /**
14
- * Discover phases from the openspec/specs/ directory.
15
- * If the directory contains subdirectories matching known phases, use those.
16
- * Otherwise, fall back to the full standard phase list.
17
- */
18
- private discoverPhases;
19
- /**
20
- * Convert phases to FlowSteps with linear dependency chain.
21
- */
22
- private buildSteps;
21
+ private readConfigSchema;
22
+ private resolveSchemaDir;
23
+ private parseSchemaArtifacts;
24
+ private loadSchema;
25
+ private discoverSchemaFromDirectory;
26
+ private collectMarkdownFiles;
27
+ private hasFileRecursive;
28
+ private findSchemaYaml;
29
+ private toStepIdFromMarkdown;
30
+ private buildStepsFromSchema;
31
+ private materializeSteps;
32
+ private resolveInstructionContent;
33
+ private normalizeArtifact;
34
+ private normalizeApply;
35
+ private readString;
36
+ private readStringArray;
37
+ private humanizeStepName;
23
38
  }
24
39
  //#endregion
25
40
  export { OpenSpecAdapter };
@@ -1 +1 @@
1
- import{existsSync as e,readFileSync as t,readdirSync as n,statSync as r}from"node:fs";import{basename as i,join as a}from"node:path";const o=[{id:`propose`,name:`Propose`,description:`Create a change proposal outlining the problem, motivation, and high-level approach`,produces:[`proposal.md`]},{id:`specs`,name:`Specs`,description:`Write detailed specifications with acceptance criteria and constraints`,produces:[`specs/`]},{id:`design`,name:`Design`,description:`Produce a technical design document mapping specs to implementation approach`,produces:[`design.md`]},{id:`tasks`,name:`Tasks`,description:`Break the design into ordered, actionable implementation tasks`,produces:[`tasks.md`]},{id:`apply`,name:`Apply`,description:`Implement the tasks, writing code and tests according to the design`,produces:[]},{id:`archive`,name:`Archive`,description:`Archive the completed change and update project documentation`,produces:[]}];var s=class{format=`openspec`;detect(t){return e(a(t,`openspec`,`config.yaml`))}async parse(e,t){let n=this.readMeta(e),r=a(e,`openspec`,`specs`),i=this.discoverPhases(r),o=this.buildSteps(i);return{name:n.name,version:n.version,description:n.description,author:n.author,steps:o,agents:[],artifacts_dir:`openspec`,install:[]}}readMeta(n){let r=a(n,`package.json`);if(e(r))try{let e=JSON.parse(t(r,`utf-8`));return{name:e.name??i(n),version:e.version??`0.1.0`,description:e.description??`OpenSpec flow`,author:e.author}}catch{}return{name:i(n),version:`0.1.0`,description:`OpenSpec flow`}}discoverPhases(t){if(!e(t))return o;try{let e=n(t).filter(e=>r(a(t,e)).isDirectory());if(e.length===0)return o;let i=new Set(o.map(e=>e.id)),s=e.filter(e=>i.has(e));if(s.length>0)return o.filter(e=>s.includes(e.id))}catch{}return o}buildSteps(e){return e.map((t,n)=>({id:t.id,name:t.name,instruction:`steps/${t.id}/README.md`,produces:t.produces,requires:n>0?[e[n-1].id]:[],agents:[],description:t.description}))}};export{s as OpenSpecAdapter};
1
+ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,writeFileSync as i}from"node:fs";import{basename as a,join as o,relative as s}from"node:path";import{execSync as c}from"node:child_process";import{parse as l}from"yaml";var u=class{format=`openspec`;detect(t){return!!(e(o(t,`openspec`,`config.yaml`))||e(o(t,`schemas`))&&this.findSchemaYaml(t))}async parse(e,t){let n=this.readMeta(e),r=this.loadSchema(e),i=this.buildStepsFromSchema(r);return await this.materializeSteps(e,i,r,t),{name:n.name,version:n.version,description:r.description??n.description,author:n.author,steps:i,agents:[],artifacts_dir:`openspec`,install:[]}}readMeta(t){let r=o(t,`package.json`);if(e(r))try{let e=JSON.parse(n(r,`utf-8`));return{name:e.name??a(t),version:e.version??`0.1.0`,description:e.description??`OpenSpec flow`,author:e.author}}catch{}return{name:a(t),version:`0.1.0`,description:`OpenSpec flow`}}readConfigSchema(t){let r=o(t,`openspec`,`config.yaml`);if(!e(r))return`spec-driven`;try{let e=l(n(r,`utf-8`));return typeof e?.schema==`string`&&e.schema.trim().length>0?e.schema:`spec-driven`}catch{return`spec-driven`}}resolveSchemaDir(t){let n=this.readConfigSchema(t),r=o(t,`openspec`,`schemas`,n);if(e(o(r,`schema.yaml`)))return r;let i=o(t,`schemas`,n);if(e(o(i,`schema.yaml`)))return i;try{let t=o(c(`npm root -g`,{encoding:`utf-8`,timeout:5e3,stdio:[`ignore`,`pipe`,`ignore`]}).trim(),`@fission-ai`,`openspec`,`schemas`,n);if(e(o(t,`schema.yaml`)))return t}catch{}return null}parseSchemaArtifacts(e){try{let t=l(e);if(!t||!Array.isArray(t.artifacts))return null;let n=t.artifacts.map(e=>this.normalizeArtifact(e)).filter(e=>e!==null);return n.length===0?null:{name:typeof t.name==`string`&&t.name.trim().length>0?t.name:this.readString(t.schema)??`spec-driven`,version:typeof t.version==`number`?t.version:1,description:this.readString(t.description),artifacts:n,apply:this.normalizeApply(t.apply)}}catch{return null}}loadSchema(e){let t=this.resolveSchemaDir(e);if(t){let e=o(t,`schema.yaml`);try{let t=n(e,`utf-8`),r=this.parseSchemaArtifacts(t);if(r)return r}catch{}}return this.discoverSchemaFromDirectory(e)}discoverSchemaFromDirectory(t){let i=[o(t,`openspec`,`schemas`),o(t,`schemas`)];for(let t of i)if(e(t))try{let i=r(t,{withFileTypes:!0}).filter(e=>e.isDirectory());for(let r of i){let i=o(t,r.name,`schema.yaml`);if(e(i))try{let e=this.parseSchemaArtifacts(n(i,`utf-8`));if(e)return e}catch{}}}catch{}let c=[],l=o(t,`openspec`),u=[],d=this.collectMarkdownFiles(l);for(let e of d){let t=s(l,e).replace(/\\/g,`/`),n=this.toStepIdFromMarkdown(t);!n||u.includes(n)||(c.push({id:n,generates:t,description:`${this.humanizeStepName(n)} phase`,template:a(e),requires:u.length>0?[u[u.length-1]]:[]}),u.push(n))}return{name:this.readConfigSchema(t),version:1,artifacts:c}}collectMarkdownFiles(t){if(!e(t))return[];let n=[];try{let e=r(t,{withFileTypes:!0}).sort((e,t)=>e.name.localeCompare(t.name));for(let r of e){let e=o(t,r.name);if(r.isFile()&&r.name.toLowerCase().endsWith(`.md`)){n.push(e);continue}r.isDirectory()&&n.push(...this.collectMarkdownFiles(e))}}catch{}return n}hasFileRecursive(t,n){if(!e(t))return!1;try{let e=r(t,{withFileTypes:!0});for(let r of e)if(r.isFile()&&r.name===n||r.isDirectory()&&this.hasFileRecursive(o(t,r.name),n))return!0}catch{}return!1}findSchemaYaml(t){let n=o(t,`schemas`);if(!e(n))return!1;try{return r(n,{withFileTypes:!0}).filter(e=>e.isDirectory()).some(t=>e(o(n,t.name,`schema.yaml`)))}catch{return!1}}toStepIdFromMarkdown(e){let t=e.replace(/\\/g,`/`).split(`/`).filter(Boolean);if(t.length===0)return null;let n=(t[t.length-1]??``).replace(/\.md$/i,``);return(n.toLowerCase()===`readme`&&t.length>1?t[t.length-2]:n).toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||null}buildStepsFromSchema(e){let t=e.artifacts.map(e=>({id:e.id,name:this.humanizeStepName(e.id),instruction:`steps/${e.id}/README.md`,produces:e.generates?[e.generates]:[],requires:e.requires,agents:[],description:e.description}));return e.apply&&t.push({id:`apply`,name:`Apply`,instruction:`steps/apply/README.md`,produces:[],requires:e.apply.requires,agents:[],description:`Implement the tasks, writing code and tests according to the design`}),t}async materializeSteps(n,r,a,s){let c=this.resolveSchemaDir(n),l=new Map(a.artifacts.map(e=>[e.id,e]));for(let u of r){let r=o(n,`steps`,u.id),d=o(r,`README.md`);t(r,{recursive:!0});let f=this.resolveInstructionContent(c,u.id,l.get(u.id),a.apply);if(!f){e(d)||i(d,`# ${u.name}\n\n${u.description}\n`,`utf-8`);continue}i(d,s?.transform?await s.transform({content:f,sourceFormat:`openspec`,stepName:u.id,metadata:{schemaName:a.name,displayName:u.name,description:u.description,produces:u.produces,requires:u.requires}}):`# ${u.name}\n\n${f}`,`utf-8`)}}resolveInstructionContent(t,r,i,a){if(r===`apply`&&a?.instruction)return a.instruction;if(!i)return null;if(i.instruction)return i.instruction;if(t&&i.template){let r=o(t,`templates`,i.template);if(e(r))try{return n(r,`utf-8`)}catch{}}return i.description||null}normalizeArtifact(e){if(!e||typeof e!=`object`)return null;let t=e,n=this.readString(t.id),r=this.readString(t.generates);return!n||!r?null:{id:n,generates:r,description:this.readString(t.description)??`${this.humanizeStepName(n)} phase`,template:this.readString(t.template)??``,instruction:this.readString(t.instruction),requires:this.readStringArray(t.requires)}}normalizeApply(e){if(!e||typeof e!=`object`)return;let t=e;return{requires:this.readStringArray(t.requires),tracks:this.readString(t.tracks)??null,instruction:this.readString(t.instruction)}}readString(e){return typeof e==`string`&&e.trim().length>0?e:void 0}readStringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`&&e.length>0):[]}humanizeStepName(e){return e.split(/[-_]/g).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}};export{u as OpenSpecAdapter};
@@ -10,7 +10,7 @@ declare class GitInstaller {
10
10
  * credential manager can open a browser / device-code flow.
11
11
  * Returns the local install path.
12
12
  */
13
- clone(repoUrl: string): FlowResult<string>;
13
+ clone(repoUrl: string, token?: string): FlowResult<string>;
14
14
  /**
15
15
  * Update an existing cloned flow repo.
16
16
  */
@@ -1,2 +1,2 @@
1
- import{cpSync as e,existsSync as t,mkdirSync as n,rmSync as r}from"node:fs";import{basename as i,join as a}from"node:path";import{execSync as o,spawnSync as s}from"node:child_process";function c(e){if(!e||typeof e!=`object`||!(`stderr`in e))return``;let t=e.stderr;return Buffer.isBuffer(t)?t.toString().trim():typeof t==`string`?t.trim():``}function l(e){try{return new URL(e).hostname}catch{}return e.match(/^[^@]+@([^:]+):/)?.[1]??`<host>`}function u(e,t,n){let r=[`Git operation failed for: ${e}`],i=l(e),a=t.includes(`ETIMEDOUT`)||t.includes(`SIGTERM`)||t.toLowerCase().includes(`timed out`),o=n.includes(`Authentication failed`)||n.includes(`could not read Username`)||n.includes(`terminal prompts disabled`)||n.includes(`401`)||n.includes(`403`)||n.includes(`Permission denied`),s=n.includes(`SSL certificate`)||n.includes(`unable to access`)&&n.includes(`SSL`),c=n.includes(`Could not resolve host`)||n.includes(`Name or service not known`),u=n.includes(`SAML`)||n.includes(`single sign-on`);return o||u?(r.push(``),r.push(`Cause: Authentication required or credentials not configured.`),r.push(``),r.push(`To fix this, ensure git can access this repository:`),r.push(``),r.push(` Option 1 - SSH key:`),r.push(` 1. Generate an SSH key: ssh-keygen -t ed25519`),r.push(` 2. Add the public key to your Git hosting account`),r.push(` 3. Use the SSH URL instead: git@<host>:<org>/<repo>.git`),r.push(``),r.push(` Option 2 - Personal Access Token (PAT):`),r.push(` 1. Create a PAT in your Git hosting Settings > Developer settings > Tokens`),r.push(` 2. For GitHub Enterprise with SAML SSO, authorize the token for your org`),r.push(` 3. Configure git credentials:`),r.push(` git config --global credential.helper store`),r.push(` git clone ${e} (enter PAT as password)`),r.push(``),r.push(` Option 3 - Git Credential Manager:`),r.push(` 1. Install: https://github.com/git-ecosystem/git-credential-manager`),r.push(` 2. Run: git clone ${e}`),r.push(` 3. Follow the browser-based auth prompt`),r.push(``),r.push(`After configuring credentials, retry: aikit flow add ${e}`)):a?(r.push(``),r.push(`Cause: Connection timed out - the server did not respond within 60 seconds.`),r.push(``),r.push(`Possible reasons:`),r.push(` - The repository requires VPN access. Ensure your VPN is connected`),r.push(` - The host is behind a firewall or corporate proxy`),r.push(` - The URL may be incorrect`),r.push(``),r.push(`Diagnostics:`),r.push(` 1. Verify the URL is correct: ${e}`),r.push(` 2. Test connectivity: git ls-remote ${e}`),r.push(` 3. If behind a proxy, configure git:`),r.push(` git config --global http.proxy http://proxy:port`),r.push(``),r.push(`If this is a corporate/internal host, you may need to:`),r.push(` - Connect to the corporate VPN`),r.push(` - Add the host to your SSH config or git config`),r.push(` - Ask your IT team to allowlist your machine`),r.push(``),r.push(`After resolving, retry: aikit flow add ${e}`)):s?(r.push(``),r.push(`Cause: SSL/TLS certificate verification failed.`),r.push(``),r.push(`This often happens with corporate proxies or self-signed certificates.`),r.push(``),r.push(`To fix:`),r.push(` 1. If your company uses a custom CA, add it:`),r.push(` git config --global http.sslCAInfo /path/to/ca-bundle.crt`),r.push(` 2. As a last resort (not recommended for production):`),r.push(` git config --global http.sslVerify false`),r.push(``),r.push(`After resolving, retry: aikit flow add ${e}`)):c?(r.push(``),r.push(`Cause: Cannot resolve hostname.`),r.push(``),r.push(`Check:`),r.push(` 1. Is the URL correct? ${e}`),r.push(` 2. Are you connected to the internet/VPN?`),r.push(` 3. Can you resolve the host? ping ${i}`),r.push(``),r.push(`After resolving, retry: aikit flow add ${e}`)):(r.push(``),r.push(`Error: ${t}`),n&&r.push(`Details: ${n}`),r.push(``),r.push(`Troubleshooting:`),r.push(` 1. Verify the URL is a valid git repository: git ls-remote ${e}`),r.push(` 2. Check your git credentials and network connectivity`),r.push(` 3. If the repo requires auth, configure credentials first (PAT or SSH key)`),r.push(``),r.push(`After resolving, retry: aikit flow add ${e}`)),r.join(`
2
- `)}function d(){try{return s(`git`,[`credential-manager`,`--version`],{stdio:`pipe`,timeout:5e3}).status===0}catch{return!1}}function f(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`authentication failed`)||n.includes(`could not read username`)||n.includes(`saml sso`)||n.includes(`terminal prompts disabled`)||n.includes(`host key verification failed`)||n.includes(`permission denied`)||n.includes(`403`)||n.includes(`401`)}var p=class{constructor(e){this.flowsDir=e}clone(e){let n=this.repoNameFromUrl(e),i=a(this.flowsDir,n);if(t(i))if(!t(a(i,`.git`)))r(i,{recursive:!0,force:!0});else return{success:!1,error:`Flow "${n}" already installed at ${i}. Use update instead.`};try{return this.ensureFlowsDir(),o(`git clone --depth 1 ${e} ${i}`,{stdio:`pipe`,timeout:6e4,env:{...process.env,GIT_TERMINAL_PROMPT:`0`,GIT_ASKPASS:``}}),{success:!0,data:i}}catch(n){t(i)&&r(i,{recursive:!0,force:!0});let a=c(n),o=n instanceof Error?n.message:String(n);if(f(o,a)&&d()){let n=l(e)||e;console.log(`\nAuthentication required for ${n}.\nGit Credential Manager detected - opening browser for login...\n(If a browser window does not open, check your terminal for a device code.)\n`);try{if(this.ensureFlowsDir(),s(`git`,[`clone`,`--depth`,`1`,e,i],{stdio:`inherit`,timeout:12e4,env:{...process.env,GIT_TERMINAL_PROMPT:`1`}}).status===0)return{success:!0,data:i};t(i)&&r(i,{recursive:!0,force:!0})}catch{t(i)&&r(i,{recursive:!0,force:!0})}}return{success:!1,error:u(e,o,a)}}}update(e){if(!t(e))return{success:!1,error:`Install path not found: ${e}`};try{return o(`git pull --ff-only`,{cwd:e,stdio:`pipe`,timeout:6e4,env:{...process.env,GIT_TERMINAL_PROMPT:`0`,GIT_ASKPASS:``}}),{success:!0}}catch(t){let n=e;try{n=o(`git remote get-url origin`,{cwd:e,stdio:`pipe`,timeout:1e4}).toString().trim()}catch{}let r=c(t),i=t instanceof Error?t.message:String(t);if(f(i,r)&&d()){let t=l(n)||n;console.log(`\nAuthentication required for ${t}.\nGit Credential Manager detected - opening browser for login...\n`);try{if(s(`git`,[`pull`,`--ff-only`],{cwd:e,stdio:`inherit`,timeout:12e4,env:{...process.env,GIT_TERMINAL_PROMPT:`1`}}).status===0)return{success:!0}}catch{}}return{success:!1,error:u(n,i,r)}}}copyLocal(n,r){let i=a(this.flowsDir,r);if(t(i))return{success:!1,error:`Flow "${r}" already installed at ${i}`};try{return this.ensureFlowsDir(),e(n,i,{recursive:!0}),{success:!0,data:i}}catch(e){return{success:!1,error:`Copy failed: ${e instanceof Error?e.message:String(e)}`}}}remove(e){if(!t(e))return{success:!0};try{return r(e,{recursive:!0,force:!0}),{success:!0}}catch(e){return{success:!1,error:`Remove failed: ${e instanceof Error?e.message:String(e)}`}}}runInstallDeps(e){for(let t of e)try{if(t.startsWith(`npm:`)){o(`npx skills add ${t.slice(4)} -g`,{stdio:`pipe`,timeout:12e4});continue}if(t.endsWith(`.git`)||t.includes(`github.com`)){o(`npx skills add ${t} -g`,{stdio:`pipe`,timeout:12e4});continue}return{success:!1,error:`Unknown install entry format: ${t}`}}catch(e){return{success:!1,error:`Install dependency failed for "${t}": ${e instanceof Error?e.message:String(e)}`}}return{success:!0}}getLocalCommit(e){try{return o(`git rev-parse HEAD`,{cwd:e,stdio:`pipe`,timeout:1e4,env:{...process.env,GIT_TERMINAL_PROMPT:`0`}}).toString().trim()||null}catch{return null}}getRemoteCommit(e){try{return o(`git ls-remote origin HEAD`,{cwd:e,stdio:`pipe`,timeout:3e4,env:{...process.env,GIT_TERMINAL_PROMPT:`0`}}).toString().trim().split(/\s+/)[0]||null}catch{return null}}hasUpdates(e){let t=this.getLocalCommit(e);if(!t)return{success:!1,error:`Could not determine local commit (not a git repo?)`};let n=this.getRemoteCommit(e);return n?{success:!0,data:{localCommit:t,remoteCommit:n,hasUpdates:t!==n}}:{success:!1,error:`Could not reach remote to check for updates`}}repoNameFromUrl(e){return i(e).replace(/\.git$/,``)}ensureFlowsDir(){t(this.flowsDir)||n(this.flowsDir,{recursive:!0})}};export{p as GitInstaller};
1
+ import{cpSync as e,existsSync as t,mkdirSync as n,rmSync as r,unlinkSync as i,writeFileSync as a}from"node:fs";import{basename as o,join as s}from"node:path";import{execSync as c,spawnSync as l}from"node:child_process";import{tmpdir as u}from"node:os";function d(e){if(!e||typeof e!=`object`||!(`stderr`in e))return``;let t=e.stderr;return Buffer.isBuffer(t)?t.toString().trim():typeof t==`string`?t.trim():``}function f(e){try{return new URL(e).hostname}catch{}return e.match(/^[^@]+@([^:]+):/)?.[1]??`<host>`}function p(e,t,n){let r=[`Git operation failed for: ${e}`],i=f(e),a=t.includes(`ETIMEDOUT`)||t.includes(`SIGTERM`)||t.toLowerCase().includes(`timed out`),o=n.includes(`Authentication failed`)||n.includes(`could not read Username`)||n.includes(`terminal prompts disabled`)||n.includes(`401`)||n.includes(`403`)||n.includes(`Permission denied`),s=n.includes(`SSL certificate`)||n.includes(`unable to access`)&&n.includes(`SSL`),c=n.includes(`Could not resolve host`)||n.includes(`Name or service not known`),l=n.includes(`SAML`)||n.includes(`single sign-on`);return o||l?(r.push(``),r.push(`Cause: Authentication required or credentials not configured.`),r.push(``),r.push(`To fix this, ensure git can access this repository:`),r.push(``),r.push(` Option 1 - SSH key:`),r.push(` 1. Generate an SSH key: ssh-keygen -t ed25519`),r.push(` 2. Add the public key to your Git hosting account`),r.push(` 3. Use the SSH URL instead: git@<host>:<org>/<repo>.git`),r.push(``),r.push(` Option 2 - Personal Access Token (PAT):`),r.push(` 1. Create a PAT in your Git hosting Settings > Developer settings > Tokens`),r.push(` 2. For GitHub Enterprise with SAML SSO, authorize the token for your org`),r.push(` 3. Configure git credentials:`),r.push(` git config --global credential.helper store`),r.push(` git clone ${e} (enter PAT as password)`),r.push(``),r.push(` Option 3 - Git Credential Manager:`),r.push(` 1. Install: https://github.com/git-ecosystem/git-credential-manager`),r.push(` 2. Run: git clone ${e}`),r.push(` 3. Follow the browser-based auth prompt`),r.push(``),r.push(`After configuring credentials, retry: aikit flow add ${e}`)):a?(r.push(``),r.push(`Cause: Connection timed out - the server did not respond within 60 seconds.`),r.push(``),r.push(`Possible reasons:`),r.push(` - The repository requires VPN access. Ensure your VPN is connected`),r.push(` - The host is behind a firewall or corporate proxy`),r.push(` - The URL may be incorrect`),r.push(``),r.push(`Diagnostics:`),r.push(` 1. Verify the URL is correct: ${e}`),r.push(` 2. Test connectivity: git ls-remote ${e}`),r.push(` 3. If behind a proxy, configure git:`),r.push(` git config --global http.proxy http://proxy:port`),r.push(``),r.push(`If this is a corporate/internal host, you may need to:`),r.push(` - Connect to the corporate VPN`),r.push(` - Add the host to your SSH config or git config`),r.push(` - Ask your IT team to allowlist your machine`),r.push(``),r.push(`After resolving, retry: aikit flow add ${e}`)):s?(r.push(``),r.push(`Cause: SSL/TLS certificate verification failed.`),r.push(``),r.push(`This often happens with corporate proxies or self-signed certificates.`),r.push(``),r.push(`To fix:`),r.push(` 1. If your company uses a custom CA, add it:`),r.push(` git config --global http.sslCAInfo /path/to/ca-bundle.crt`),r.push(` 2. As a last resort (not recommended for production):`),r.push(` git config --global http.sslVerify false`),r.push(``),r.push(`After resolving, retry: aikit flow add ${e}`)):c?(r.push(``),r.push(`Cause: Cannot resolve hostname.`),r.push(``),r.push(`Check:`),r.push(` 1. Is the URL correct? ${e}`),r.push(` 2. Are you connected to the internet/VPN?`),r.push(` 3. Can you resolve the host? ping ${i}`),r.push(``),r.push(`After resolving, retry: aikit flow add ${e}`)):(r.push(``),r.push(`Error: ${t}`),n&&r.push(`Details: ${n}`),r.push(``),r.push(`Troubleshooting:`),r.push(` 1. Verify the URL is a valid git repository: git ls-remote ${e}`),r.push(` 2. Check your git credentials and network connectivity`),r.push(` 3. If the repo requires auth, configure credentials first (PAT or SSH key)`),r.push(``),r.push(`After resolving, retry: aikit flow add ${e}`)),r.join(`
2
+ `)}function m(){try{return l(`git`,[`credential-manager`,`--version`],{stdio:`pipe`,timeout:5e3}).status===0}catch{return!1}}function h(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`authentication failed`)||n.includes(`could not read username`)||n.includes(`saml sso`)||n.includes(`terminal prompts disabled`)||n.includes(`host key verification failed`)||n.includes(`permission denied`)||n.includes(`403`)||n.includes(`401`)}var g=class{constructor(e){this.flowsDir=e}clone(e,n){let o=this.repoNameFromUrl(e),c=s(this.flowsDir,o);if(t(c))if(!t(s(c,`.git`)))r(c,{recursive:!0,force:!0});else return{success:!1,error:`Flow "${o}" already installed at ${c}. Use update instead.`};try{if(this.ensureFlowsDir(),n){let t=process.platform===`win32`?`bat`:`sh`,r=s(u(),`git-askpass-${Date.now()}.${t}`);process.platform===`win32`?a(r,`@echo ${n}`,{mode:448}):a(r,`#!/bin/sh\necho "${n}"`,{mode:448});try{let t=l(`git`,[`clone`,`--depth`,`1`,e,c],{stdio:`pipe`,timeout:6e4,env:{...process.env,GIT_TERMINAL_PROMPT:`0`,GIT_ASKPASS:r}});if(t.status!==0){let e=t.stderr?.toString().trim()??``;throw Error(e||t.error?.message||`git clone failed`)}}finally{try{i(r)}catch{}}}else{let t=l(`git`,[`clone`,`--depth`,`1`,e,c],{stdio:`pipe`,timeout:6e4,env:{...process.env,GIT_TERMINAL_PROMPT:`0`,GIT_ASKPASS:``}});if(t.status!==0){let e=t.stderr?.toString().trim()??``;throw Error(e||t.error?.message||`git clone failed`)}}return{success:!0,data:c}}catch(i){t(c)&&r(c,{recursive:!0,force:!0});let a=d(i),o=i instanceof Error?i.message:String(i);if(!n&&h(o,a)&&m()){let n=f(e)||e;console.log(`\nAuthentication required for ${n}.\nGit Credential Manager detected - opening browser for login...\n(If a browser window does not open, check your terminal for a device code.)\n`);try{if(this.ensureFlowsDir(),l(`git`,[`clone`,`--depth`,`1`,e,c],{stdio:`inherit`,timeout:12e4,env:{...process.env,GIT_TERMINAL_PROMPT:`1`}}).status===0)return{success:!0,data:c};t(c)&&r(c,{recursive:!0,force:!0})}catch{t(c)&&r(c,{recursive:!0,force:!0})}}return{success:!1,error:p(e,o,a)}}}update(e){if(!t(e))return{success:!1,error:`Install path not found: ${e}`};try{return c(`git pull --ff-only`,{cwd:e,stdio:`pipe`,timeout:6e4,env:{...process.env,GIT_TERMINAL_PROMPT:`0`,GIT_ASKPASS:``}}),{success:!0}}catch(t){let n=e;try{n=c(`git remote get-url origin`,{cwd:e,stdio:`pipe`,timeout:1e4}).toString().trim()}catch{}let r=d(t),i=t instanceof Error?t.message:String(t);if(h(i,r)&&m()){let t=f(n)||n;console.log(`\nAuthentication required for ${t}.\nGit Credential Manager detected - opening browser for login...\n`);try{if(l(`git`,[`pull`,`--ff-only`],{cwd:e,stdio:`inherit`,timeout:12e4,env:{...process.env,GIT_TERMINAL_PROMPT:`1`}}).status===0)return{success:!0}}catch{}}return{success:!1,error:p(n,i,r)}}}copyLocal(n,r){let i=s(this.flowsDir,r);if(t(i))return{success:!1,error:`Flow "${r}" already installed at ${i}`};try{return this.ensureFlowsDir(),e(n,i,{recursive:!0}),{success:!0,data:i}}catch(e){return{success:!1,error:`Copy failed: ${e instanceof Error?e.message:String(e)}`}}}remove(e){if(!t(e))return{success:!0};try{return r(e,{recursive:!0,force:!0}),{success:!0}}catch(e){return{success:!1,error:`Remove failed: ${e instanceof Error?e.message:String(e)}`}}}runInstallDeps(e){for(let t of e)try{if(t.startsWith(`npm:`)){c(`npx skills add ${t.slice(4)} -g`,{stdio:`pipe`,timeout:12e4});continue}if(t.endsWith(`.git`)||t.includes(`github.com`)){c(`npx skills add ${t} -g`,{stdio:`pipe`,timeout:12e4});continue}return{success:!1,error:`Unknown install entry format: ${t}`}}catch(e){return{success:!1,error:`Install dependency failed for "${t}": ${e instanceof Error?e.message:String(e)}`}}return{success:!0}}getLocalCommit(e){try{return c(`git rev-parse HEAD`,{cwd:e,stdio:`pipe`,timeout:1e4,env:{...process.env,GIT_TERMINAL_PROMPT:`0`}}).toString().trim()||null}catch{return null}}getRemoteCommit(e){try{return c(`git ls-remote origin HEAD`,{cwd:e,stdio:`pipe`,timeout:3e4,env:{...process.env,GIT_TERMINAL_PROMPT:`0`}}).toString().trim().split(/\s+/)[0]||null}catch{return null}}hasUpdates(e){let t=this.getLocalCommit(e);if(!t)return{success:!1,error:`Could not determine local commit (not a git repo?)`};let n=this.getRemoteCommit(e);return n?{success:!0,data:{localCommit:t,remoteCommit:n,hasUpdates:t!==n}}:{success:!1,error:`Could not reach remote to check for updates`}}repoNameFromUrl(e){return o(e).replace(/\.git$/,``)}ensureFlowsDir(){t(this.flowsDir)||n(this.flowsDir,{recursive:!0})}};export{g as GitInstaller};
@@ -1,7 +1,8 @@
1
- import { FlowFormat, FlowFormatAdapter, FlowManifest, FlowParseOptions, FlowRegistry, FlowRegistryEntry, FlowResult, FlowSourceType, FlowState, FlowStatus, FlowStep, StepAction } from "./types.js";
1
+ import { ContentTransformFn, FlowFormat, FlowFormatAdapter, FlowManifest, FlowParseOptions, FlowRegistry, FlowRegistryEntry, FlowResult, FlowSourceType, FlowState, FlowStatus, FlowStep, StepAction } from "./types.js";
2
2
  import { ClaudePluginAdapter } from "./adapters/claude-plugin.js";
3
3
  import { CopilotAdapter } from "./adapters/copilot.js";
4
4
  import { NativeAdapter } from "./adapters/native.js";
5
+ import { OpenSpecAdapter } from "./adapters/openspec.js";
5
6
  import { BuiltinFlow, getBuiltinFlows } from "./builtins.js";
6
7
  import { FoundationIntegration } from "./foundation.js";
7
8
  import { GitInstaller } from "./git.js";
@@ -9,4 +10,4 @@ import { FlowLoader } from "./loader.js";
9
10
  import { FlowRegistryManager } from "./registry.js";
10
11
  import { FlowStateMachine } from "./state-machine.js";
11
12
  import { SymlinkManager } from "./symlinks.js";
12
- export { type BuiltinFlow, ClaudePluginAdapter, CopilotAdapter, type FlowFormat, type FlowFormatAdapter, FlowLoader, type FlowManifest, type FlowParseOptions, type FlowRegistry, type FlowRegistryEntry, FlowRegistryManager, type FlowResult, type FlowSourceType, type FlowState, FlowStateMachine, type FlowStatus, type FlowStep, FoundationIntegration, GitInstaller, NativeAdapter, type StepAction, SymlinkManager, getBuiltinFlows };
13
+ export { type BuiltinFlow, ClaudePluginAdapter, type ContentTransformFn, CopilotAdapter, type FlowFormat, type FlowFormatAdapter, FlowLoader, type FlowManifest, type FlowParseOptions, type FlowRegistry, type FlowRegistryEntry, FlowRegistryManager, type FlowResult, type FlowSourceType, type FlowState, FlowStateMachine, type FlowStatus, type FlowStep, FoundationIntegration, GitInstaller, NativeAdapter, OpenSpecAdapter, type StepAction, SymlinkManager, getBuiltinFlows };
@@ -1 +1 @@
1
- import{getBuiltinFlows as e}from"./builtins.js";import{FoundationIntegration as t}from"./foundation.js";import{GitInstaller as n}from"./git.js";import{ClaudePluginAdapter as r}from"./adapters/claude-plugin.js";import{CopilotAdapter as i}from"./adapters/copilot.js";import{NativeAdapter as a}from"./adapters/native.js";import{FlowLoader as o}from"./loader.js";import{FlowRegistryManager as s}from"./registry.js";import{FlowStateMachine as c}from"./state-machine.js";import{SymlinkManager as l}from"./symlinks.js";export{r as ClaudePluginAdapter,i as CopilotAdapter,o as FlowLoader,s as FlowRegistryManager,c as FlowStateMachine,t as FoundationIntegration,n as GitInstaller,a as NativeAdapter,l as SymlinkManager,e as getBuiltinFlows};
1
+ import{getBuiltinFlows as e}from"./builtins.js";import{FoundationIntegration as t}from"./foundation.js";import{GitInstaller as n}from"./git.js";import{ClaudePluginAdapter as r}from"./adapters/claude-plugin.js";import{CopilotAdapter as i}from"./adapters/copilot.js";import{NativeAdapter as a}from"./adapters/native.js";import{OpenSpecAdapter as o}from"./adapters/openspec.js";import{FlowLoader as s}from"./loader.js";import{FlowRegistryManager as c}from"./registry.js";import{FlowStateMachine as l}from"./state-machine.js";import{SymlinkManager as u}from"./symlinks.js";export{r as ClaudePluginAdapter,i as CopilotAdapter,s as FlowLoader,c as FlowRegistryManager,l as FlowStateMachine,t as FoundationIntegration,n as GitInstaller,a as NativeAdapter,o as OpenSpecAdapter,u as SymlinkManager,e as getBuiltinFlows};
@@ -39,9 +39,15 @@ interface FlowManifest {
39
39
  install: string[];
40
40
  }
41
41
  /** Source type for flow installation */
42
- type FlowSourceType = 'builtin' | 'git' | 'local';
42
+ type FlowSourceType = 'builtin' | 'git' | 'local' | 'npm-global' | 'claude-plugin';
43
43
  /** Detected format of the flow source */
44
44
  type FlowFormat = 'native' | 'claude-plugin' | 'copilot' | 'openspec';
45
+ type ContentTransformFn = (request: {
46
+ content: string;
47
+ sourceFormat: FlowFormat;
48
+ stepName: string;
49
+ metadata?: Record<string, unknown>;
50
+ }) => Promise<string>;
45
51
  /** Registry entry for an installed flow */
46
52
  interface FlowRegistryEntry {
47
53
  /** Flow name */
@@ -99,11 +105,8 @@ type StepAction = 'next' | 'skip' | 'redo';
99
105
  interface FlowParseOptions {
100
106
  /** When true, force-refresh supporting assets (e.g. on update) */
101
107
  forceAssetSync?: boolean;
102
- }
103
- /** Options passed to format adapter parse */
104
- interface FlowParseOptions {
105
- /** When true, force-refresh supporting assets (e.g. on update) */
106
- forceAssetSync?: boolean;
108
+ /** Optional content transformer for converting foreign step content to native README.md */
109
+ transform?: ContentTransformFn;
107
110
  }
108
111
  /** Format adapter interface — converts external formats to FlowManifest */
109
112
  interface FlowFormatAdapter {
@@ -121,4 +124,4 @@ interface FlowResult<T = void> {
121
124
  error?: string;
122
125
  }
123
126
  //#endregion
124
- export { FlowFormat, FlowFormatAdapter, FlowManifest, FlowParseOptions, FlowRegistry, FlowRegistryEntry, FlowResult, FlowSourceType, FlowState, FlowStatus, FlowStep, StepAction };
127
+ export { ContentTransformFn, FlowFormat, FlowFormatAdapter, FlowManifest, FlowParseOptions, FlowRegistry, FlowRegistryEntry, FlowResult, FlowSourceType, FlowState, FlowStatus, FlowStep, StepAction };
@@ -1 +1,16 @@
1
- import{getToolMeta as e}from"../tool-metadata.js";import{existsSync as t}from"node:fs";import{basename as n,join as r,resolve as i}from"node:path";import{homedir as a}from"node:os";import{z as o}from"zod";import{readFile as s}from"node:fs/promises";import{createLogger as c,getGlobalDataDir as l,serializeError as u}from"../../../core/dist/index.js";const d=c(`flow-tools`);function f(e){return{content:[{type:`text`,text:e}]}}function p(e){return e instanceof Error?e.message:String(e)}function m(c,m){function h(){return m.sources?.[0]?.path??process.cwd()}function g(){return r(l(),`flows`,`registry.json`)}function _(){return r(m.stateDir??r(h(),`.aikit-state`),`flows`,`state.json`)}function v(e,r){let o=r?.installPath?n(r.installPath):e.replaceAll(`:`,`-`),s=i(a(),`.copilot`,`flows`,o).replaceAll(`\\`,`/`);if(t(s))return s;let c=i(a(),`.claude`,`flows`,o).replaceAll(`\\`,`/`);if(t(c))return c;let l=i(h(),`.github`,`flows`,o).replaceAll(`\\`,`/`);return t(l)?l:r?.installPath&&t(r.installPath)?r.installPath.replaceAll(`\\`,`/`):null}function y(e,t){let n=v(e.name,e);return n?i(n,t).replaceAll(`\\`,`/`):t}async function b(){let{FlowRegistryManager:e,FlowStateMachine:t,FlowLoader:n,GitInstaller:i}=await import(`../../../flows/dist/index.js`),a=r(m.stateDir??r(h(),`.aikit-state`),`flows`,`installed`);return{registry:new e(g()),stateMachine:new t(_()),loader:new n,installer:new i(a)}}let x=e(`flow_list`);c.registerTool(`flow_list`,{title:x.title,description:`List all installed flows and their steps`,annotations:x.annotations,inputSchema:{}},async()=>{try{let{registry:e,stateMachine:t,installer:n}=await b(),r=e.list(),i=t.getStatus(),a={flows:r.map(e=>{let t={name:e.name,version:e.version,source:e.source,sourceType:e.sourceType,format:e.format,steps:e.manifest.steps.map(e=>e.id)};if(e.sourceType===`git`&&e.commitSha){let r=n.hasUpdates(e.installPath);return{...t,commitSha:e.commitSha,updateAvailable:r.success&&r.data?r.data.hasUpdates:void 0}}return t}),activeFlow:i.success&&i.data?{flow:i.data.flow,status:i.data.status,currentStep:i.data.currentStep}:null};return f(JSON.stringify(a,null,2))}catch(e){return d.error(`flow_list failed`,u(e)),f(`Error: ${p(e)}`)}});let S=e(`flow_info`);c.registerTool(`flow_info`,{title:S.title,description:`Show detailed information about a specific flow`,annotations:S.annotations,inputSchema:{name:o.string().describe(`Flow name to get info for`)}},async({name:e})=>{try{let{registry:t,installer:n}=await b(),r=t.get(e);if(!r)return f(`Flow "${e}" not found. Use flow_list to see available flows.`);let i=r.commitSha??null,a;if(r.sourceType===`git`&&r.installPath){let e=n.hasUpdates(r.installPath);a=e.success&&e.data?e.data.hasUpdates:void 0}let o={name:r.name,version:r.version,description:r.manifest.description,source:r.source,sourceType:r.sourceType,format:r.format,commitSha:i,updateAvailable:a,installPath:v(r.name,r),registeredAt:r.registeredAt,updatedAt:r.updatedAt,steps:r.manifest.steps.map(e=>({id:e.id,name:e.name,instruction:y(r,e.instruction),produces:e.produces,requires:e.requires,description:e.description})),agents:r.manifest.agents,artifactsDir:r.manifest.artifacts_dir,install:r.manifest.install};return f(JSON.stringify(o,null,2))}catch(e){return d.error(`flow_info failed`,u(e)),f(`Error: ${p(e)}`)}});let C=e(`flow_start`);c.registerTool(`flow_start`,{title:C.title,description:`Start a flow. Sets the active flow and positions at the first step.`,annotations:C.annotations,inputSchema:{flow:o.string().describe(`Flow name to start (use flow_list to see options)`)}},async({flow:e})=>{try{let{registry:t,stateMachine:n}=await b(),r=t.get(e);if(!r)return f(`Flow "${e}" not found. Use flow_list to see available flows.`);let i=n.start(r.name,r.manifest);if(!i.success||!i.data)return f(`Cannot start: ${i.error}`);let a=i.data,o=r.manifest.steps.find(e=>e.id===a.currentStep),s={started:!0,flow:a.flow,currentStep:a.currentStep,currentStepInstruction:r&&o?y(r,o.instruction):null,currentStepDescription:o?.description??null,totalSteps:r.manifest.steps.length,stepSequence:r.manifest.steps.map(e=>e.id),artifactsDir:r.manifest.artifacts_dir};return f(JSON.stringify(s,null,2))}catch(e){return d.error(`flow_start failed`,u(e)),f(`Error: ${p(e)}`)}});let w=e(`flow_step`);c.registerTool(`flow_step`,{title:w.title,description:`Advance the active flow: complete current step and move to next, skip current step, or redo current step.`,annotations:w.annotations,inputSchema:{action:o.enum([`next`,`skip`,`redo`]).describe(`next: mark current step done and advance. skip: skip current step. redo: repeat current step.`)}},async({action:e})=>{try{let{registry:t,stateMachine:n}=await b(),r=n.load();if(!r)return f(`No active flow. Use flow_start first.`);let i=t.get(r.flow);if(!i)return f(`Flow "${r.flow}" not found in registry.`);let a=n.step(e,i.manifest);if(!a.success||!a.data)return f(`Cannot ${e}: ${a.error}`);let o=a.data,s=o.currentStep?i.manifest.steps.find(e=>e.id===o.currentStep):null,c={flow:o.flow,status:o.status,action:e,currentStep:o.currentStep,currentStepInstruction:i&&s?y(i,s.instruction):null,currentStepDescription:s?.description??null,completedSteps:o.completedSteps,skippedSteps:o.skippedSteps,totalSteps:i.manifest.steps.length,remaining:i.manifest.steps.filter(e=>!o.completedSteps.includes(e.id)&&!o.skippedSteps.includes(e.id)&&e.id!==o.currentStep).map(e=>e.id)};return f(JSON.stringify(c,null,2))}catch(e){return d.error(`flow_step failed`,u(e)),f(`Error: ${p(e)}`)}});let T=e(`flow_status`);c.registerTool(`flow_status`,{title:T.title,description:`Show the current flow execution state — which flow is active, current step, completed steps, and artifacts.`,annotations:T.annotations,inputSchema:{}},async()=>{try{let{registry:e,stateMachine:t}=await b(),n=t.getStatus();if(!n.success||!n.data)return f(`No active flow. Use flow_start to begin one, or flow_list to see available flows.`);let r=n.data,i=e.get(r.flow),a=i?.manifest.steps.find(e=>e.id===r.currentStep),o=i&&a?y(i,a.instruction):null,s={flow:r.flow,status:r.status,currentStep:r.currentStep,currentStepInstruction:o,instructionPath:o,currentStepDescription:a?.description??null,completedSteps:r.completedSteps,skippedSteps:r.skippedSteps,artifacts:r.artifacts,startedAt:r.startedAt,updatedAt:r.updatedAt,totalSteps:i?.manifest.steps.length??0,progress:i?`${r.completedSteps.length+r.skippedSteps.length}/${i.manifest.steps.length}`:`unknown`};return f(JSON.stringify(s,null,2))}catch(e){return d.error(`flow_status failed`,u(e)),f(`Error: ${p(e)}`)}});let E=e(`flow_read_instruction`);c.registerTool(`flow_read_instruction`,{title:E.title===`flow_read_instruction`?`Flow Read Instruction`:E.title,description:`Read the instruction content for a flow step. If step is omitted, reads the current step.`,annotations:E.title===`flow_read_instruction`?{readOnlyHint:!0,idempotentHint:!0}:E.annotations,inputSchema:{step:o.string().optional().describe(`Step id or name to read. Defaults to the current step.`)}},async({step:e})=>{try{let{registry:t,stateMachine:n}=await b(),r=n.getStatus();if(!r.success||!r.data)return f(`No active flow. Use flow_start to begin one, or flow_list to see available flows.`);let i=r.data,a=t.get(i.flow);if(!a)return f(`Flow "${i.flow}" not found in registry.`);let o=e??i.currentStep;if(!o)return f(`No current step is available for the active flow.`);let c=a.manifest.steps.find(e=>e.id===o||e.name===o);return f(c?await s(y(a,c.instruction),`utf-8`):`Step "${o}" not found in flow "${i.flow}".`)}catch(e){return d.error(`flow_read_instruction failed`,u(e)),e instanceof Error&&`code`in e&&e.code===`ENOENT`?f(`Could not read instruction file: ${e.message}`):f(`Error: ${p(e)}`)}});let D=e(`flow_reset`);c.registerTool(`flow_reset`,{title:D.title,description:`Reset the active flow, clearing all state. Use to start over or switch to a different flow.`,annotations:D.annotations,inputSchema:{}},async()=>{try{let{stateMachine:e}=await b(),t=e.reset();return t.success?f(`Flow state reset. Use flow_start to begin a new flow.`):f(`Reset failed: ${t.error}`)}catch(e){return d.error(`flow_reset failed`,u(e)),f(`Error: ${p(e)}`)}});let O=e(`flow_add`);c.registerTool(`flow_add`,{title:O.title,description:`Install a new development flow from a git repository URL or local directory path. Use when the user wants to add, install, import, or onboard a new workflow — for example: "use this as a flow", "add this flow", "add this flow URL", "install a flow", or "onboard a flow". Accepts git URLs (https://..., git@...) and local filesystem paths.`,annotations:O.annotations,inputSchema:{source:o.string().describe(`Git repository URL (https://... or git@...) or absolute/local directory path containing a flow definition.`),name:o.string().optional().describe(`Optional override for the installed flow name. If omitted, the name comes from the flow manifest.`)}},async({source:e,name:t})=>{try{let{registry:r,loader:a,installer:o}=await b(),s=/^https?:\/\/|^git@/.test(e),c;if(s){let t=o.clone(e);if(!t.success||!t.data)return f(`Failed to clone flow: ${t.error}`);c=t.data}else{let r=i(e),a=t||n(r)||`custom-flow`,s=o.copyLocal(r,a);if(!s.success||!s.data)return f(`Failed to copy flow: ${s.error}`);c=s.data}let l=await a.load(c);if(!l.success||!l.data)return o.remove(c),f(`Failed to load flow manifest: ${l.error}`);let{manifest:u,format:d}=l.data,p=t||u.name;if(r.has(p))return o.remove(c),f(`Flow "${p}" is already installed. Use flow_update to update it, or flow_remove then flow_add to replace.`);if(u.install.length>0){let e=o.runInstallDeps(u.install);if(!e.success)return o.remove(c),f(`Dependency install failed: ${e.error}`)}let m=new Date().toISOString(),h=s?o.getLocalCommit(c):void 0,g=r.register({name:p,version:u.version,source:e,sourceType:s?`git`:`local`,installPath:c,format:d,registeredAt:m,updatedAt:m,manifest:u,...h?{commitSha:h}:{}});if(!g.success)return o.remove(c),f(`Failed to register flow: ${g.error}`);let _=u.steps.length;return f(`Flow "${p}" installed successfully (${_} steps). Use flow_start({ flow: "${p}" }) to begin.`)}catch(e){return d.error(`flow_add failed`,u(e)),f(`Error: ${p(e)}`)}});let k=e(`flow_remove`);c.registerTool(`flow_remove`,{title:k.title,description:`Remove an installed flow by name. Unregisters it and deletes its files when applicable. Builtin flows cannot be removed.`,annotations:k.annotations,inputSchema:{name:o.string().describe(`Name of the flow to remove.`)}},async({name:e})=>{try{let{registry:t,installer:n}=await b();if(!t.has(e))return f(`Flow "${e}" is not installed.`);let r=t.get(e);if(!r)return f(`Flow "${e}" is not installed.`);if(r.sourceType===`builtin`)return f(`Cannot remove builtin flow "${e}".`);let i=n.remove(r.installPath);if(!i.success)return f(`Failed to remove flow files: ${i.error}`);let a=t.unregister(e);return a.success?f(`Flow "${e}" removed successfully.`):f(`Failed to unregister flow: ${a.error}`)}catch(e){return d.error(`flow_remove failed`,u(e)),f(`Error: ${p(e)}`)}});let A=e(`flow_update`);c.registerTool(`flow_update`,{title:A.title,description:`Update an installed git-based flow to its latest version by pulling changes. Only works for flows installed from git repositories.`,annotations:A.annotations,inputSchema:{name:o.string().describe(`Name of the flow to update.`)}},async({name:e})=>{try{let{registry:t,loader:n,installer:r}=await b();if(!t.has(e))return f(`Flow "${e}" is not installed.`);let i=t.get(e);if(!i||i.sourceType!==`git`)return f(`Flow "${e}" was not installed from git — cannot update. Remove and re-add instead.`);let a=r.update(i.installPath);if(!a.success)return f(`Update failed: ${a.error}`);let o=await n.load(i.installPath,{forceAssetSync:!0}),s=o.success&&o.data?o.data.manifest:null,c=o.success&&o.data?o.data.format:i.format;if(s){let e=r.getLocalCommit(i.installPath),n=t.register({...i,version:s.version,format:c,manifest:s,updatedAt:new Date().toISOString(),...e?{commitSha:e}:{}});if(!n.success)return f(`Failed to refresh flow registry entry: ${n.error}`)}let l=s?.install??i.manifest.install;if(l.length>0){let e=r.runInstallDeps(l);if(!e.success)return f(`Dependency install failed: ${e.error}`)}return f(`Flow "${e}" updated successfully.`)}catch(e){return d.error(`flow_update failed`,u(e)),f(`Error: ${p(e)}`)}})}export{m as registerFlowTools};
1
+ import{createSamplingClient as e}from"../sampling.js";import{getToolMeta as t}from"../tool-metadata.js";import{existsSync as n,readFileSync as r,readdirSync as i,renameSync as a,rmSync as o}from"node:fs";import{basename as s,join as c,resolve as l}from"node:path";import{homedir as u}from"node:os";import{z as d}from"zod";import{readFile as f}from"node:fs/promises";import{createLogger as p,getGlobalDataDir as m,serializeError as h}from"../../../core/dist/index.js";import{execSync as g}from"node:child_process";const _=p(`flow-tools`);function v(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`&&e.trim().length>0):[]}function y(t){let n=e(t);return async({content:e,sourceFormat:t,stepName:r,metadata:i})=>{let a=r.charAt(0).toUpperCase()+r.slice(1),o=i?JSON.stringify(i,null,2):void 0,s=`You are converting a flow step definition from "${t}" format to aikit native flow step README.md format.
2
+
3
+ Convert the provided content into a well-structured README.md for a flow step named "${r}".
4
+
5
+ The output must:
6
+ 1. Start with a heading: # ${a}
7
+ 2. Have a "## Purpose" section explaining what this step does
8
+ 3. Have a "## Instructions" section with ALL technical instructions preserved from the source
9
+ 4. Have a "## Artifacts" section listing what this step produces
10
+ 5. Have a "## Prerequisites" section if there are dependencies on previous steps
11
+ 6. Preserve ALL tool restrictions, constraints, and requirements from the source
12
+ 7. Be formatted as clean Markdown
13
+
14
+ Output ONLY the README.md content, nothing else.`;if(n.available)try{let i=(await n.createMessage({prompt:e,systemPrompt:s,context:o?`Step name: ${r}\nSource format: ${t}\nMetadata:\n${o}`:`Step name: ${r}\nSource format: ${t}`,maxTokens:4096,modelPreferences:{intelligencePriority:.8,speedPriority:.4,costPriority:.2}})).text.trim();if(i.length>0)return i}catch(e){_.debug(`Flow transform sampling failed; using fallback formatting`,{error:x(e),sourceFormat:t,stepName:r})}let c=typeof i?.description==`string`&&i.description.trim().length>0?i.description.trim():null,l=v(i?.produces),u=v(i?.requires);return[`# ${a}`,``,`## Purpose`,``,c??`${a} step.`,``,`## Instructions`,``,e.trim(),``,`## Artifacts`,``,...l.length>0?l.map(e=>`- ${e}`):[`- ${r}.md`],...u.length>0?[``,`## Prerequisites`,``,...u.map(e=>`- ${e}`)]:[],``].join(`
15
+ `).trimEnd().concat(`
16
+ `)}}function b(e){return{content:[{type:`text`,text:e}]}}function x(e){return e instanceof Error?e.message:String(e)}function S(e,p){function v(){return p.sources?.[0]?.path??process.cwd()}function S(){return c(m(),`flows`,`registry.json`)}function C(){return c(p.stateDir??c(v(),`.aikit-state`),`flows`,`state.json`)}function w(e,t){let r=t?.installPath?s(t.installPath):e.replaceAll(`:`,`-`),i=l(u(),`.copilot`,`flows`,r).replaceAll(`\\`,`/`);if(n(i))return i;let a=l(u(),`.claude`,`flows`,r).replaceAll(`\\`,`/`);if(n(a))return a;let o=l(v(),`.github`,`flows`,r).replaceAll(`\\`,`/`);return n(o)?o:t?.installPath&&n(t.installPath)?t.installPath.replaceAll(`\\`,`/`):null}function T(e,t){let n=w(e.name,e);return n?l(n,t).replaceAll(`\\`,`/`):t}function E(){try{let e=c(g(`npm root -g`,{encoding:`utf-8`}).trim(),`@fission-ai`,`openspec`);if(n(e))return{path:e,sourceType:`npm-global`,isGit:!1}}catch{}return{path:`https://github.com/Fission-AI/OpenSpec.git`,sourceType:`git`,isGit:!0}}function D(e){let t=l(e);if(n(c(t,`.claude-plugin`,`plugin.json`)))return{path:t,sourceType:`claude-plugin`};let a=u(),o=c(a,`.claude`,`plugins`,e);if(n(c(o,`.claude-plugin`,`plugin.json`)))return{path:o,sourceType:`claude-plugin`};let s=c(a,`.claude`,`plugins`);if(!n(s))return null;for(let t of i(s,{withFileTypes:!0})){if(!t.isDirectory())continue;let i=c(s,t.name,`.claude-plugin`,`plugin.json`);if(n(i))try{if(JSON.parse(r(i,`utf-8`)).name===e)return{path:c(s,t.name),sourceType:`claude-plugin`}}catch{}}return null}async function O(){let{FlowRegistryManager:e,FlowStateMachine:t,FlowLoader:n,GitInstaller:r}=await import(`../../../flows/dist/index.js`),i=c(p.stateDir??c(v(),`.aikit-state`),`flows`,`installed`);return{registry:new e(S()),stateMachine:new t(C()),loader:new n,installer:new r(i)}}let k=t(`flow_list`);e.registerTool(`flow_list`,{title:k.title,description:`List all installed flows and their steps`,annotations:k.annotations,inputSchema:{}},async()=>{try{let{registry:e,stateMachine:t,installer:n}=await O(),r=e.list(),i=t.getStatus(),a={flows:r.map(e=>{let t={name:e.name,version:e.version,source:e.source,sourceType:e.sourceType,format:e.format,steps:e.manifest.steps.map(e=>e.id)};if(e.sourceType===`git`&&e.commitSha){let r=n.hasUpdates(e.installPath);return{...t,commitSha:e.commitSha,updateAvailable:r.success&&r.data?r.data.hasUpdates:void 0}}return t}),activeFlow:i.success&&i.data?{flow:i.data.flow,status:i.data.status,currentStep:i.data.currentStep}:null};return b(JSON.stringify(a,null,2))}catch(e){return _.error(`flow_list failed`,h(e)),b(`Error: ${x(e)}`)}});let A=t(`flow_info`);e.registerTool(`flow_info`,{title:A.title,description:`Show detailed information about a specific flow`,annotations:A.annotations,inputSchema:{name:d.string().describe(`Flow name to get info for`)}},async({name:e})=>{try{let{registry:t,installer:n}=await O(),r=t.get(e);if(!r)return b(`Flow "${e}" not found. Use flow_list to see available flows.`);let i=r.commitSha??null,a;if(r.sourceType===`git`&&r.installPath){let e=n.hasUpdates(r.installPath);a=e.success&&e.data?e.data.hasUpdates:void 0}let o={name:r.name,version:r.version,description:r.manifest.description,source:r.source,sourceType:r.sourceType,format:r.format,commitSha:i,updateAvailable:a,installPath:w(r.name,r),registeredAt:r.registeredAt,updatedAt:r.updatedAt,steps:r.manifest.steps.map(e=>({id:e.id,name:e.name,instruction:T(r,e.instruction),produces:e.produces,requires:e.requires,description:e.description})),agents:r.manifest.agents,artifactsDir:r.manifest.artifacts_dir,install:r.manifest.install};return b(JSON.stringify(o,null,2))}catch(e){return _.error(`flow_info failed`,h(e)),b(`Error: ${x(e)}`)}});let j=t(`flow_start`);e.registerTool(`flow_start`,{title:j.title,description:`Start a flow. Sets the active flow and positions at the first step.`,annotations:j.annotations,inputSchema:{flow:d.string().describe(`Flow name to start (use flow_list to see options)`)}},async({flow:e})=>{try{let{registry:t,stateMachine:n}=await O(),r=t.get(e);if(!r)return b(`Flow "${e}" not found. Use flow_list to see available flows.`);let i=n.start(r.name,r.manifest);if(!i.success||!i.data)return b(`Cannot start: ${i.error}`);let a=i.data,o=r.manifest.steps.find(e=>e.id===a.currentStep),s={started:!0,flow:a.flow,currentStep:a.currentStep,currentStepInstruction:r&&o?T(r,o.instruction):null,currentStepDescription:o?.description??null,totalSteps:r.manifest.steps.length,stepSequence:r.manifest.steps.map(e=>e.id),artifactsDir:r.manifest.artifacts_dir};return b(JSON.stringify(s,null,2))}catch(e){return _.error(`flow_start failed`,h(e)),b(`Error: ${x(e)}`)}});let M=t(`flow_step`);e.registerTool(`flow_step`,{title:M.title,description:`Advance the active flow: complete current step and move to next, skip current step, or redo current step.`,annotations:M.annotations,inputSchema:{action:d.enum([`next`,`skip`,`redo`]).describe(`next: mark current step done and advance. skip: skip current step. redo: repeat current step.`)}},async({action:e})=>{try{let{registry:t,stateMachine:n}=await O(),r=n.load();if(!r)return b(`No active flow. Use flow_start first.`);let i=t.get(r.flow);if(!i)return b(`Flow "${r.flow}" not found in registry.`);let a=n.step(e,i.manifest);if(!a.success||!a.data)return b(`Cannot ${e}: ${a.error}`);let o=a.data,s=o.currentStep?i.manifest.steps.find(e=>e.id===o.currentStep):null,c={flow:o.flow,status:o.status,action:e,currentStep:o.currentStep,currentStepInstruction:i&&s?T(i,s.instruction):null,currentStepDescription:s?.description??null,completedSteps:o.completedSteps,skippedSteps:o.skippedSteps,totalSteps:i.manifest.steps.length,remaining:i.manifest.steps.filter(e=>!o.completedSteps.includes(e.id)&&!o.skippedSteps.includes(e.id)&&e.id!==o.currentStep).map(e=>e.id)};return b(JSON.stringify(c,null,2))}catch(e){return _.error(`flow_step failed`,h(e)),b(`Error: ${x(e)}`)}});let N=t(`flow_status`);e.registerTool(`flow_status`,{title:N.title,description:`Show the current flow execution state — which flow is active, current step, completed steps, and artifacts.`,annotations:N.annotations,inputSchema:{}},async()=>{try{let{registry:e,stateMachine:t}=await O(),n=t.getStatus();if(!n.success||!n.data)return b(`No active flow. Use flow_start to begin one, or flow_list to see available flows.`);let r=n.data,i=e.get(r.flow),a=i?.manifest.steps.find(e=>e.id===r.currentStep),o=i&&a?T(i,a.instruction):null,s={flow:r.flow,status:r.status,currentStep:r.currentStep,currentStepInstruction:o,instructionPath:o,currentStepDescription:a?.description??null,completedSteps:r.completedSteps,skippedSteps:r.skippedSteps,artifacts:r.artifacts,startedAt:r.startedAt,updatedAt:r.updatedAt,totalSteps:i?.manifest.steps.length??0,progress:i?`${r.completedSteps.length+r.skippedSteps.length}/${i.manifest.steps.length}`:`unknown`};return b(JSON.stringify(s,null,2))}catch(e){return _.error(`flow_status failed`,h(e)),b(`Error: ${x(e)}`)}});let P=t(`flow_read_instruction`);e.registerTool(`flow_read_instruction`,{title:P.title===`flow_read_instruction`?`Flow Read Instruction`:P.title,description:`Read the instruction content for a flow step. If step is omitted, reads the current step.`,annotations:P.title===`flow_read_instruction`?{readOnlyHint:!0,idempotentHint:!0}:P.annotations,inputSchema:{step:d.string().optional().describe(`Step id or name to read. Defaults to the current step.`)}},async({step:e})=>{try{let{registry:t,stateMachine:n}=await O(),r=n.getStatus();if(!r.success||!r.data)return b(`No active flow. Use flow_start to begin one, or flow_list to see available flows.`);let i=r.data,a=t.get(i.flow);if(!a)return b(`Flow "${i.flow}" not found in registry.`);let o=e??i.currentStep;if(!o)return b(`No current step is available for the active flow.`);let s=a.manifest.steps.find(e=>e.id===o||e.name===o);return b(s?await f(T(a,s.instruction),`utf-8`):`Step "${o}" not found in flow "${i.flow}".`)}catch(e){return _.error(`flow_read_instruction failed`,h(e)),e instanceof Error&&`code`in e&&e.code===`ENOENT`?b(`Could not read instruction file: ${e.message}`):b(`Error: ${x(e)}`)}});let F=t(`flow_reset`);e.registerTool(`flow_reset`,{title:F.title,description:`Reset the active flow, clearing all state. Use to start over or switch to a different flow.`,annotations:F.annotations,inputSchema:{}},async()=>{try{let{stateMachine:e}=await O(),t=e.reset();return t.success?b(`Flow state reset. Use flow_start to begin a new flow.`):b(`Reset failed: ${t.error}`)}catch(e){return _.error(`flow_reset failed`,h(e)),b(`Error: ${x(e)}`)}});let I=t(`flow_add`);e.registerTool(`flow_add`,{title:I.title,description:`Install a new development flow from a git repository URL or local directory path. Use when the user wants to add, install, import, or onboard a new workflow — for example: "use this as a flow", "add this flow", "add this flow URL", "install a flow", or "onboard a flow". Accepts git URLs (https://..., git@...) and local filesystem paths. Also accepts the shorthand "openspec" to install the OpenSpec flow (auto-detects local npm global install or clones from GitHub).`,annotations:I.annotations,inputSchema:{source:d.string().describe(`Git repository URL (https://... or git@...) or absolute/local directory path containing a flow definition. Use "openspec" as a shorthand to auto-resolve the OpenSpec flow.`),name:d.string().optional().describe(`Optional override for the installed flow name. If omitted, the name comes from the flow manifest.`),token:d.string().optional().describe(`Authentication token for private/GHE repositories`)}},async({source:t,name:n,token:r})=>{try{let{registry:i,loader:a,installer:o}=await O(),c=y(e),u=t===`openspec`||t.startsWith(`openspec:`),d,f=t,p;if(u){let e=E();f=e.path,d=e.sourceType}let m=u?null:D(f);m&&(f=m.path,d=m.sourceType);let h=/^https?:\/\/|^git@/.test(f),g;if(h){let e=o.clone(f,r);if(!e.success||!e.data)return b(`Failed to clone flow: ${e.error}`);g=e.data}else{let e=l(f);p=e;let t=n||s(e)||`custom-flow`,r=o.copyLocal(e,t);if(!r.success||!r.data)return b(`Failed to copy flow: ${r.error}`);g=r.data}let _=await a.load(g,{forceAssetSync:!0,transform:c});if(!_.success||!_.data)return o.remove(g),b(`Failed to load flow manifest: ${_.error}`);let{manifest:v,format:x}=_.data,S=n||v.name,C=d??(h?`git`:`local`);if(i.has(S))return o.remove(g),b(`Flow "${S}" is already installed. Use flow_update to update it, or flow_remove then flow_add to replace.`);if(v.install.length>0){let e=o.runInstallDeps(v.install);if(!e.success)return o.remove(g),b(`Dependency install failed: ${e.error}`)}let w=new Date().toISOString(),T=h?o.getLocalCommit(g):void 0,k=i.register({name:S,version:v.version,source:C===`local`||C===`claude-plugin`?p??t:t,sourceType:C,installPath:g,format:x,registeredAt:w,updatedAt:w,manifest:v,...T?{commitSha:T}:{}});if(!k.success)return o.remove(g),b(`Failed to register flow: ${k.error}`);let A=v.steps.length;return b(`Flow "${S}" installed successfully (${A} steps). Use flow_start({ flow: "${S}" }) to begin.`)}catch(e){return _.error(`flow_add failed`,h(e)),b(`Error: ${x(e)}`)}});let L=t(`flow_remove`);e.registerTool(`flow_remove`,{title:L.title,description:`Remove an installed flow by name. Unregisters it and deletes its files when applicable. Builtin flows cannot be removed.`,annotations:L.annotations,inputSchema:{name:d.string().describe(`Name of the flow to remove.`)}},async({name:e})=>{try{let{registry:t,installer:n}=await O();if(!t.has(e))return b(`Flow "${e}" is not installed.`);let r=t.get(e);if(!r)return b(`Flow "${e}" is not installed.`);if(r.sourceType===`builtin`)return b(`Cannot remove builtin flow "${e}".`);let i=n.remove(r.installPath);if(!i.success)return b(`Failed to remove flow files: ${i.error}`);let a=t.unregister(e);return a.success?b(`Flow "${e}" removed successfully.`):b(`Failed to unregister flow: ${a.error}`)}catch(e){return _.error(`flow_remove failed`,h(e)),b(`Error: ${x(e)}`)}});let R=t(`flow_update`);e.registerTool(`flow_update`,{title:R.title,description:`Update an installed flow to its latest version. For git-based flows, pulls the latest changes. For npm-global flows (e.g. OpenSpec), runs npm update.`,annotations:R.annotations,inputSchema:{name:d.string().describe(`Name of the flow to update.`)}},async({name:t})=>{try{let{registry:r,loader:i,installer:c}=await O(),u=y(e);if(!r.has(t))return b(`Flow "${t}" is not installed.`);let d=r.get(t);if(!d||d.sourceType!==`git`&&d.sourceType!==`npm-global`&&d.sourceType!==`local`&&d.sourceType!==`claude-plugin`)return b(`Flow "${t}" was not installed from git, npm, or a local source — cannot update. Remove and re-add instead.`);if(d.sourceType===`local`||d.sourceType===`claude-plugin`){let e=l(d.source);if(!n(e))return b(`Source path no longer exists: ${e}`);let t=`${d.installPath}.updating`,f=`${d.installPath}.backup`;n(t)&&o(t,{recursive:!0,force:!0}),n(f)&&o(f,{recursive:!0,force:!0});let p=c.copyLocal(e,s(t));if(!p.success||!p.data)return b(`Failed to copy flow: ${p.error}`);let m=await i.load(t,{forceAssetSync:!0,transform:u});if(!m.success||!m.data)return c.remove(t),b(`Failed to load flow manifest: ${m.error}`);let h=!1;try{a(d.installPath,f),a(t,d.installPath);let e=await i.load(d.installPath,{forceAssetSync:!0,transform:u});if(!e.success||!e.data)throw Error(`Failed to load flow manifest: ${e.error}`);let n=e.data.manifest,s=e.data.format,l=r.register({...d,version:n.version,format:s,installPath:d.installPath,manifest:n,updatedAt:new Date().toISOString()});if(!l.success)throw Error(`Failed to refresh flow registry entry: ${l.error}`);if(h=!0,n.install.length>0){let e=c.runInstallDeps(n.install);if(!e.success)throw Error(`Dependency install failed: ${e.error}`)}o(f,{recursive:!0,force:!0})}catch(e){return n(t)&&o(t,{recursive:!0,force:!0}),n(f)&&(n(d.installPath)&&o(d.installPath,{recursive:!0,force:!0}),a(f,d.installPath)),h&&r.register(d),b(x(e))}return b(`Flow "${d.name}" updated from ${d.sourceType} source successfully.`)}if(d.sourceType===`npm-global`){try{g(`npm update -g @fission-ai/openspec`,{encoding:`utf-8`,stdio:`pipe`})}catch(e){return b(`npm update failed: ${x(e)}`)}let e=E(),n=await i.load(e.path,{forceAssetSync:!0,transform:u}),a=n.success&&n.data?n.data.manifest:null,o=n.success&&n.data?n.data.format:d.format;if(a){let t=r.register({...d,version:a.version,format:o,installPath:e.path,manifest:a,updatedAt:new Date().toISOString()});if(!t.success)return b(`Failed to refresh flow registry entry: ${t.error}`)}return b(`Flow "${t}" updated via npm successfully.`)}let f=c.update(d.installPath);if(!f.success)return b(`Update failed: ${f.error}`);let p=await i.load(d.installPath,{forceAssetSync:!0,transform:u}),m=p.success&&p.data?p.data.manifest:null,h=p.success&&p.data?p.data.format:d.format;if(m){let e=c.getLocalCommit(d.installPath),t=r.register({...d,version:m.version,format:h,manifest:m,updatedAt:new Date().toISOString(),...e?{commitSha:e}:{}});if(!t.success)return b(`Failed to refresh flow registry entry: ${t.error}`)}let _=m?.install??d.manifest.install;if(_.length>0){let e=c.runInstallDeps(_);if(!e.success)return b(`Dependency install failed: ${e.error}`)}return b(`Flow "${t}" updated successfully.`)}catch(e){return _.error(`flow_update failed`,h(e)),b(`Error: ${x(e)}`)}})}export{S as registerFlowTools};