ai-projects 1.6.1 → 1.7.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.
Files changed (3) hide show
  1. package/README.md +5 -4
  2. package/dist/index.js +60 -51
  3. package/package.json +6 -10
package/README.md CHANGED
@@ -15,7 +15,7 @@ aip help quickstart
15
15
  # 📋 Read list of verbs by all nouns
16
16
  aip --help # Same as `aip help usage`
17
17
 
18
- # 🎯 List of verbs by noun (same as `aip <noun> --help`)
18
+ # 🎯 List of verbs by noun
19
19
  aip task --help # Same as `aip help usage task`
20
20
 
21
21
  # 📋 Get all parameters and --options for a command
@@ -93,8 +93,8 @@ $AIP_HOME/projects/{project}/
93
93
  ├── main.md # Goals + body (YAML frontmatter + markdown)
94
94
  ├── log.tsv # Activity log (tab-separated: date, time, type, slug, action, text)
95
95
  ├── hooks/ # Automation: pre|post-{create,start,update,complete}
96
- ├── outputs/ # Deliverables go here
97
- ├── inputs/ # External data (API responses, downloads)
96
+ ├── inputs/ # Intermediate/raw data (e.g. API responses, downloads)
97
+ ├── outputs/ # Deliverables go here (e.g. reports, leads, etc.)
98
98
  ├── scripts/ # Automation scripts
99
99
  └── tasks/{task}/
100
100
  ├── main.md # Task definition + body
@@ -116,7 +116,8 @@ aip project path my-project # Get absolute path
116
116
  ```bash
117
117
  aip task create my-project "task-name" --description "..."
118
118
  aip task update --status in-progress
119
- aip task list
119
+ aip task list --project my-project
120
+ aip task list --search "-name"
120
121
  aip task path task-name # Get absolute path
121
122
  aip task start # Start working (and read in all context to stdout)
122
123
  ```
package/dist/index.js CHANGED
@@ -1,26 +1,26 @@
1
1
  #!/usr/bin/env node
2
- import {z as z$1}from'zod';import {parser}from'zod-opts';import rt from'path';import {spawn,exec}from'child_process';import K from'fs/promises';import T from'lodash';import V from'moment';import {fileURLToPath}from'url';import {inspect}from'util';import xt from'yaml';var _e=Object.defineProperty;var f=(t,e)=>()=>(t&&(e=t(t=0)),e);var Ue=(t,e)=>{for(var o in e)_e(t,o,{get:e[o],enumerable:true});};var S,B=f(()=>{S={name:"ai-projects",version:"1.6.1",type:"module",description:"Useful CLI for AI agents to create and manage projects, tasks, skills and agents",main:"dist/index.js",types:"dist/index.d.ts",bin:{aip:"dist/index.js"},files:["dist/**/*","README.md","LICENSE"],scripts:{build:"npm run build:map && npm run build:code","build:map":"tsx bin/map-commands.ts","build:code":"tsup --no-dts","build:all":"npm run build:map && tsup","build:check":"tsc --noEmit","build:watch":"npm run build:code -- --watch","build:clean":"rimraf dist/*","build:incr":"tsup --no-dts",start:"dist/index.js",dev:'npm run build:watch -- --onSuccess "dist/index.js"',cli:"npm run build && npm start --",ts:"tsx",vitest:"VITE_CJS_IGNORE_WARNING=true vitest",test:"npm run test:types && npm run test:unit","test:unit":"npm run vitest -- run","test:types:test":"tsc -p tsconfig.test.json --incremental --tsBuildInfoFile dist/test.tsbuildinfo","test:types:bin":"tsc -p bin/tsconfig.json --incremental --tsBuildInfoFile dist/bin.tsbuildinfo","test:types":"npm run test:types:test && npm run test:types:bin","test:full":"npm run lint:full && npm run test",eslint:"eslint --cache",lint:"npm run eslint -- '{src,bin}/**/*.ts'","lint:fix":"npm run lint -- --fix","lint:full":"npm run build:incr && npm run test:types:bin && npm run lint:fix","lint:full:silent":"npm run -s lint:full && echo LINT OK",map:"npm run build:map",link:"npm link",unlink:"npm unlink",prepare:'[ "$CI" != "true" ] && [ -d node_modules/husky ] && husky || true',prepack:"npm run test:full && npm run build:all","publish:dry":"npm pack --dry-run","version:patch":"npm version patch","version:minor":"npm version minor","version:major":"npm version major","agent:stop":"npm run -s lint:full:silent"},keywords:["ai","aip","project","task","kanban","cli","agent","automation"],author:"Ariel Flesler <aflesler@gmail.com>",license:"MIT",repository:{type:"git",url:"git+https://github.com/flesler/ai-projects.git"},homepage:"https://github.com/flesler/ai-projects#readme",bugs:{url:"https://github.com/flesler/ai-projects/issues"},engines:{node:">=20.0.0"},dependencies:{dotenv:"^17.3.1",lodash:"^4.17.21",moment:"^2.30.1",tslib:"^2.8.1",yaml:"^2.8.0",zod:"^3.25.76","zod-opts":"^1.0.0"},devDependencies:{"@eslint/js":"^10.0.1","@stylistic/eslint-plugin":"^5.7.0","@types/dotenv":"^8.2.3","@types/eslint":"^9.6.1","@types/lodash":"^4.17.23","@types/moment":"^2.13.0","@types/node":"^25.0.9","@types/source-map-support":"^0.5.10","@typescript-eslint/eslint-plugin":"^8.53.0","@typescript-eslint/parser":"^8.53.0",eslint:"^10.0.3","eslint-plugin-import-x":"^4.16.2","eslint-plugin-unused-imports":"^4.4.1","fast-glob":"^3.3.3",husky:"^9.1.7",rimraf:"^6.0.1","source-map-support":"^0.5.21",tsup:"^8.5.1",tsx:"^4.20.3","types-package-json":"^2.0.39",typescript:"^5.8.3",vitest:"^4.0.17"}};});function p(t){let{description:e,options:o=z$1.object({}),args:r,handler:n}=t,i=o.shape,c={};for(let[m,l]of Object.entries(i))c[m]={type:l};let d=parser().options(c);if(r){let m=r.shape,l=Object.entries(m).map(([g,b])=>({name:g,type:b}));l.length>0&&(d=d.args(l));}return e&&(d=d.description(e)),{description:e,options:o,args:r,parser:d,handler:n,cli:m=>n(d.parse(m))}}var k=f(()=>{});var a,P=f(()=>{a={dirs:{PROJECTS:"projects",SKILLS:"skills",AGENTS:"agents",TASKS:"tasks",INPUTS:"inputs",OUTPUTS:"outputs",HOOKS:"hooks",SCRIPTS:"scripts"},files:{MAIN:"main.md",LOG:"log.tsv",SKILL:"SKILL.md"},hookTypes:["pre-create","post-create","pre-complete","post-complete","pre-start","post-start","pre-update","post-update"],languages:["ts","js","sh","py"],targets:["project","task"]};});var J,Me,A,H=f(()=>{P();J=process.env.AIP_HOME;if(!J){J=process.cwd();let t=J.split(rt.sep);for(let e of [a.dirs.PROJECTS,a.dirs.SKILLS,a.dirs.AGENTS]){let o=t.indexOf(e);if(o!==-1){J=t.slice(0,o).join(rt.sep);break}}}Me={AIP_HOME:J},A=Me;});var h,s,j=f(()=>{H();h={REPO:fileURLToPath(import.meta.url.replace(/\/(dist|src)\/.*/,"/")),join:(...t)=>rt.join(...t),onRepo:(...t)=>h.join(h.REPO,...t),joinHome:(...t)=>h.join(A.AIP_HOME,...t),async ensureDir(t){await K.mkdir(t,{recursive:true});},async write(t,e){await h.ensureDir(rt.dirname(t)),await K.writeFile(t,e,"utf8");},async append(t,e){await h.ensureDir(rt.dirname(t)),await K.appendFile(t,e,"utf8");},async read(t){return K.readFile(t,"utf8")},async readRepo(...t){return h.read(h.onRepo(...t))},async readMany(...t){return (await Promise.all(t.map(async o=>{if(await h.fileExists(o)){let r=await h.read(o);return `# ${o}
2
+ import {z as z$1}from'zod';import {parser}from'zod-opts';import rt from'path';import {spawn,exec}from'child_process';import K from'fs/promises';import T from'lodash';import V from'moment';import {fileURLToPath}from'url';import {inspect}from'util';import Pt from'yaml';var Le=Object.defineProperty;var f=(t,e)=>()=>(t&&(e=t(t=0)),e);var _e=(t,e)=>{for(var o in e)Le(t,o,{get:e[o],enumerable:true});};var S,B=f(()=>{S={name:"ai-projects",version:"1.7.0",type:"module",description:"Useful CLI for AI agents to create and manage projects, tasks, skills and agents",main:"dist/index.js",types:"dist/index.d.ts",bin:{aip:"dist/index.js"},files:["dist/**/*","README.md","LICENSE"],scripts:{build:"npm run build:map && npm run build:code","build:map":"tsx bin/map-commands.ts","build:code":"tsup --no-dts","build:full":"npm run build:map && tsup","build:check":"tsc --noEmit","build:watch":"npm run build:code -- --watch","build:clean":"rimraf dist/*",start:"dist/index.js",dev:'npm run build:watch -- --onSuccess "dist/index.js"',cli:"npm run build && npm start --",ts:"tsx",vitest:"VITE_CJS_IGNORE_WARNING=true vitest",test:"npm run test:types && npm run test:unit","test:unit":"npm run vitest -- run","test:types:test":"tsc -p tsconfig.test.json --incremental --tsBuildInfoFile test.tsbuildinfo","test:types:bin":"tsc -p bin/tsconfig.json --incremental --tsBuildInfoFile bin.tsbuildinfo","test:types":"npm run test:types:test && npm run test:types:bin","test:full":"npm run lint:full && npm run test",eslint:"eslint --cache",lint:"npm run eslint -- '{src,bin}/**/*.ts'","lint:fix":"npm run lint -- --fix","lint:full":"npm run build:check && npm run test:types:bin && npm run lint:fix","lint:full:silent":"npm run -s lint:full && echo LINT OK",prepare:'[ "$CI" != "true" ] && [ -d node_modules/husky ] && husky || true',prepack:"npm run test:full && npm run build:full","publish:dry":"npm pack --dry-run","version:patch":"npm version patch","version:minor":"npm version minor","version:major":"npm version major","agent:stop":"npm run -s lint:full:silent"},keywords:["ai","aip","project","task","kanban","cli","agent","automation"],author:"Ariel Flesler <aflesler@gmail.com>",license:"MIT",repository:{type:"git",url:"git+https://github.com/flesler/ai-projects.git"},homepage:"https://github.com/flesler/ai-projects#readme",bugs:{url:"https://github.com/flesler/ai-projects/issues"},engines:{node:">=20.0.0"},dependencies:{dotenv:"^17.3.1",lodash:"^4.17.21",moment:"^2.30.1",tslib:"^2.8.1",yaml:"^2.8.0",zod:"^3.25.76","zod-opts":"^1.0.0"},devDependencies:{"@eslint/js":"^10.0.1","@stylistic/eslint-plugin":"^5.7.0","@types/dotenv":"^8.2.3","@types/eslint":"^9.6.1","@types/lodash":"^4.17.23","@types/moment":"^2.13.0","@types/node":"^25.0.9","@types/source-map-support":"^0.5.10","@typescript-eslint/eslint-plugin":"^8.53.0","@typescript-eslint/parser":"^8.53.0",eslint:"^10.0.3","eslint-plugin-import-x":"^4.16.2","eslint-plugin-unused-imports":"^4.4.1","fast-glob":"^3.3.3",husky:"^9.1.7",rimraf:"^6.0.1","source-map-support":"^0.5.21",tsup:"^8.5.1",tsx:"^4.20.3","types-package-json":"^2.0.39",typescript:"^5.8.3",vitest:"^4.0.17"}};});function p(t){let{description:e,options:o=z$1.object({}),args:r,handler:n}=t,i=o.shape,c={};for(let[m,l]of Object.entries(i))c[m]={type:l};let d=parser().options(c);if(r){let m=r.shape,l=Object.entries(m).map(([g,b])=>({name:g,type:b}));l.length>0&&(d=d.args(l));}return e&&(d=d.description(e)),{description:e,options:o,args:r,parser:d,handler:n,cli:m=>n(d.parse(m))}}var k=f(()=>{});var a,P=f(()=>{a={dirs:{PROJECTS:"projects",SKILLS:"skills",AGENTS:"agents",TASKS:"tasks",INPUTS:"inputs",OUTPUTS:"outputs",HOOKS:"hooks",SCRIPTS:"scripts"},files:{MAIN:"main.md",LOG:"log.tsv",SKILL:"SKILL.md"},hookTypes:["pre-create","post-create","pre-complete","post-complete","pre-start","post-start","pre-update","post-update"],languages:["ts","js","sh","py"],targets:["project","task"]};});var J,Ge,A,H=f(()=>{P();J=process.env.AIP_HOME;if(!J){J=process.cwd();let t=J.split(rt.sep);for(let e of [a.dirs.PROJECTS,a.dirs.SKILLS,a.dirs.AGENTS]){let o=t.indexOf(e);if(o!==-1){J=t.slice(0,o).join(rt.sep);break}}}Ge={AIP_HOME:J},A=Ge;});var y,s,j=f(()=>{H();y={REPO:fileURLToPath(import.meta.url.replace(/\/(dist|src)\/.*/,"/")),join:(...t)=>rt.join(...t),onRepo:(...t)=>y.join(y.REPO,...t),joinHome:(...t)=>y.join(A.AIP_HOME,...t),async ensureDir(t){await K.mkdir(t,{recursive:true});},async write(t,e){await y.ensureDir(rt.dirname(t)),await K.writeFile(t,e,"utf8");},async append(t,e){await y.ensureDir(rt.dirname(t)),await K.appendFile(t,e,"utf8");},async read(t){return K.readFile(t,"utf8")},async readRepo(...t){return y.read(y.onRepo(...t))},async readMany(...t){return (await Promise.all(t.map(async o=>{let r=await y.fileExists(o)?await y.read(o):"ERROR: File not found";return `# ${o}
3
3
 
4
- ${r}`}return ""}))).filter(Boolean)},async logFiles(...t){let e=await h.readMany(...t);console.log(e.join(`
4
+ ${r}`}))).filter(Boolean)},async logFiles(...t){let e=await y.readMany(...t);console.log(e.join(`
5
5
 
6
6
  ---
7
7
 
8
- `));},dumpCommandMapLines(t,e){let o=e?T.pick(t,e):t,r=[];for(let[n,i]of h.entriesOf(o))r.push(`${n} {${Object.keys(i).join("|")}}`);return r},async fileExists(t){try{return await K.access(t),!0}catch{return false}},async listDir(t){try{return await K.readdir(t)}catch{return []}},spawn:spawn,cd(t){process.chdir(t);},async exec(t,e){return new Promise(o=>{exec(t,{encoding:"utf8",...e},(r,n,i)=>{let c=r?.code;o({stdout:n??"",stderr:i??"",code:r?typeof c=="number"?c:1:0});});})},noop:()=>{},omitByDeep:(t,e)=>T.isArray(t)?t.map(o=>h.omitByDeep(o,e)):T.isPlainObject(t)?T.mapValues(T.omitBy(t,e),o=>h.omitByDeep(o,e)):t,omitNilsDeep:t=>h.omitByDeep(t,T.isNil),omitUndefinedsDeep:t=>h.omitByDeep(t,T.isUndefined),cloneDeep:t=>T.cloneDeep(t),int:t=>Number.parseInt(t,10),keysOf:t=>Object.keys(t),entriesOf:t=>Object.entries(t),isKeyOf:(t,e)=>!T.isNil(t)&&t in e,typeOf:t=>typeof t,never:{},undef:t=>t??void 0,toNull:t=>t??null,same:t=>t,notNil:t=>{if(T.isNil(t))throw new Error(`Received unexpected ${t} value`);return t},randomInt:(t,e)=>Math.floor(Math.random()*(e-t+1))+t,promise:()=>{let t,e=new Promise((o,r)=>{t={resolve:o,reject:r};});return T.extend(e,t)},isPromise:t=>T.isObject(t)&&"catch"in t,delay:t=>{let e=T.isObject(t)?h.toMS(t):t;return new Promise(o=>setTimeout(o,e))},promiseMap:(t,e)=>Promise.all(t.map(e)),promiseEach:(t,e)=>Promise.all(t.map(e)).then(()=>{}),memoize:t=>T.memoize(t,h.memoizeKey),toMS:t=>V.duration(t).asMilliseconds(),toSecs:t=>V.duration(t).asSeconds(),inspect:(t,e)=>inspect(t,{colors:false,showHidden:false,depth:null,compact:true,breakLength:1/0,...e}),stringify(t,e,o){let r=new WeakSet;return JSON.stringify(t,(n,i)=>{if(T.isObject(i)){if(r.has(i))return "[Circular]";r.add(i);}return T.isFunction(e)?e(n,i):i},o)},dump:t=>h.oneLine(h.stringify(t)),compactWhitespace:t=>t.replace(/\\n/g,`
8
+ `));},dumpCommandMapLines(t,e){let o=e?T.pick(t,e):t,r=[];for(let[n,i]of y.entriesOf(o))r.push(`${n} {${Object.keys(i).join("|")}}`);return r},async fileExists(t){try{return await K.access(t),!0}catch{return false}},async listDir(t){try{return await K.readdir(t)}catch{return []}},spawn:spawn,cd(t){process.chdir(t);},async exec(t,e){return new Promise(o=>{exec(t,{encoding:"utf8",...e},(r,n,i)=>{let c=r?.code;o({stdout:n??"",stderr:i??"",code:r?typeof c=="number"?c:1:0});});})},noop:()=>{},omitByDeep:(t,e)=>T.isArray(t)?t.map(o=>y.omitByDeep(o,e)):T.isPlainObject(t)?T.mapValues(T.omitBy(t,e),o=>y.omitByDeep(o,e)):t,omitNilsDeep:t=>y.omitByDeep(t,T.isNil),omitUndefinedsDeep:t=>y.omitByDeep(t,T.isUndefined),cloneDeep:t=>T.cloneDeep(t),int:t=>Number.parseInt(t,10),keysOf:t=>Object.keys(t),entriesOf:t=>Object.entries(t),isKeyOf:(t,e)=>!T.isNil(t)&&t in e,typeOf:t=>typeof t,never:{},undef:t=>t??void 0,toNull:t=>t??null,same:t=>t,notNil:t=>{if(T.isNil(t))throw new Error(`Received unexpected ${t} value`);return t},randomInt:(t,e)=>Math.floor(Math.random()*(e-t+1))+t,promise:()=>{let t,e=new Promise((o,r)=>{t={resolve:o,reject:r};});return T.extend(e,t)},isPromise:t=>T.isObject(t)&&"catch"in t,delay:t=>{let e=T.isObject(t)?y.toMS(t):t;return new Promise(o=>setTimeout(o,e))},promiseMap:(t,e)=>Promise.all(t.map(e)),promiseEach:(t,e)=>Promise.all(t.map(e)).then(()=>{}),memoize:t=>T.memoize(t,y.memoizeKey),toMS:t=>V.duration(t).asMilliseconds(),toSecs:t=>V.duration(t).asSeconds(),inspect:(t,e)=>inspect(t,{colors:false,showHidden:false,depth:null,compact:true,breakLength:1/0,...e}),stringify(t,e,o){let r=new WeakSet;return JSON.stringify(t,(n,i)=>{if(T.isObject(i)){if(r.has(i))return "[Circular]";r.add(i);}return T.isFunction(e)?e(n,i):i},o)},dump:t=>y.oneLine(y.stringify(t)),compactWhitespace:t=>t.replace(/\\n/g,`
9
9
  `).replace(/\r/g,"").replace(/[ \t]*\n[ \t]*/g,`
10
10
  `).replace(/\n{3,}/g,`
11
11
 
12
- `).replace(/ {2,}/g," ").trim(),oneLine:t=>t.replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/ +/g," ").trim(),fuzzySearch:(t,e)=>T.includes(T.toLower(t||""),T.toLower(e||"")),elapsed:(t,e=Date.now())=>V.duration(V(e).diff(t||0)),iso:t=>V.utc(t).toISOString(),isoDate:t=>h.iso(t).slice(0,10),memoizeKey:(...t)=>t.join(""),exit:(t=0)=>{process.exit(t);},set:(t,e,o)=>(t[e]=o,t),run:async t=>{try{await t(),process.exit(0);}catch(e){console.error(e),process.exit(1);}},isMain:()=>{let t=process.argv[1]||"";return t.includes("/index.")||t.endsWith("/bin/aip")},errorMessage:t=>t instanceof Error?t.message:String(t),slugify:t=>t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")},s=h;});var tt,G,ut,Z,$t=f(()=>{j();tt=t=>{let e=t.trim();if(!e.startsWith("---"))return null;let o=e.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);return o?{frontmatter:xt.parse(o[1]),content:o[2].trim()}:null},G=async(t,e,o="")=>{let n=`---
13
- ${xt.stringify(e).trim()}
12
+ `).replace(/ {2,}/g," ").trim(),oneLine:t=>t.replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/ +/g," ").trim(),fuzzySearch:(t,e)=>T.includes(T.toLower(t||""),T.toLower(e||"")),elapsed:(t,e=Date.now())=>V.duration(V(e).diff(t||0)),iso:t=>V.utc(t).toISOString(),isoDate:t=>y.iso(t).slice(0,10),memoizeKey:(...t)=>t.join(""),exit:(t=0)=>{process.exit(t);},set:(t,e,o)=>(t[e]=o,t),run:async t=>{try{await t(),process.exit(0);}catch(e){console.error(e),process.exit(1);}},isMain:()=>{let t=process.argv[1]||"";return t.includes("/index.")||t.endsWith("/bin/aip")},errorMessage:t=>t instanceof Error?t.message:String(t),slugify:t=>t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),matchesSearch:(t,e)=>{let o=e?.toLowerCase().split(/\s+/).filter(Boolean);if(!o?.length)return true;let r=t.toLowerCase();return o.every(n=>r.includes(n))}},s=y;});var tt,G,dt,Z,St=f(()=>{j();tt=t=>{let e=t.trim();if(!e.startsWith("---"))return null;let o=e.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);return o?{frontmatter:Pt.parse(o[1]),content:o[2].trim()}:null},G=async(t,e,o="")=>{let n=`---
13
+ ${Pt.stringify(e).trim()}
14
14
  ---
15
15
 
16
16
  ${o}`.trim();await s.write(t,n+`
17
- `);},ut=async(t,e)=>{let o=await s.read(t),r=tt(o);if(!r)throw new Error(`No frontmatter found in ${t}`);let n={...r.frontmatter,...e};return await G(t,n,r.content),n},Z=async t=>{let e=await s.read(t),o=tt(e);if(!o)throw new Error(`No frontmatter found in ${t}`);return o.frontmatter};});var M,et,vt,Et,Ze,We,u,x=f(()=>{P();H();$t();j();M=(c=>(c.BACKLOG="backlog",c.IN_PROGRESS="in-progress",c.ONGOING="ongoing",c.DONE="done",c.BLOCKED="blocked",c.TO_DO="to-do",c))(M||{}),et=Object.values(M),vt=["backlog","in-progress","ongoing"],Et=[a.dirs.HOOKS,a.dirs.INPUTS,a.dirs.OUTPUTS,a.dirs.SCRIPTS],Ze=[a.dirs.TASKS,...Et],We={getProjectDir(t){return s.joinHome(a.dirs.PROJECTS,t)},getTaskDir(t,e){return s.join(this.getProjectDir(t),a.dirs.TASKS,e)},async findTask(t,e){if(e)return {project:e,task:t};let o=await this.listProjects(),r=[];if(await s.promiseEach(o,async n=>{(await this.listTasks(n)).includes(t)&&r.push(n);}),r.length===0)throw new Error(`Task '${t}' not found. Use --project to specify the project.`);if(r.length>1)throw new Error(`Task '${t}' found in multiple projects: ${r.join(", ")}. Use --project to specify which one.`);return {project:r[0],task:t}},getAgentDir(t){return s.join(A.AIP_HOME,a.dirs.AGENTS,t)},async listProjects(){let t=s.join(A.AIP_HOME,a.dirs.PROJECTS),e=await s.listDir(t);return (await s.promiseMap(e,async r=>{let n=this.getProjectDir(r);return await s.fileExists(n)&&r!==a.dirs.AGENTS?r:null})).filter(r=>r!==null).sort()},async listTasks(t){let e=s.join(this.getProjectDir(t),a.dirs.TASKS);return await s.fileExists(e)?await s.listDir(e):[]},async listAgents(){let t=s.join(A.AIP_HOME,a.dirs.AGENTS);return await s.fileExists(t)?await s.listDir(t):[]},async createProject(t,e,o){let r=this.getProjectDir(t);return await s.ensureDir(r),await Promise.all(Ze.map(n=>s.ensureDir(s.join(r,n)))),await G(s.join(r,a.files.MAIN),e,o),await s.write(s.join(r,a.files.LOG),""),r},async createTask(t,e,o,r){let n=this.getTaskDir(t,e);return await s.ensureDir(n),await Promise.all(Et.map(i=>s.ensureDir(s.join(n,i)))),await G(s.join(n,a.files.MAIN),o,r),await s.write(s.join(n,a.files.LOG),""),n},async createAgent(t,e){let o=this.getAgentDir(t);return await s.ensureDir(o),await G(s.join(o,a.files.MAIN),e),o},async getProject(t){let e=s.join(this.getProjectDir(t),a.files.MAIN);return await s.fileExists(e)?await Z(e):null},async getTask(t,e){let o=s.join(this.getTaskDir(t,e),a.files.MAIN);return await s.fileExists(o)?await Z(o):null},async getAgent(t){let e=s.join(this.getAgentDir(t),a.files.MAIN);return await s.fileExists(e)?await Z(e):null},async updateProject(t,e){let o=s.join(this.getProjectDir(t),a.files.MAIN);return await ut(o,e)},async updateBody(t,e){let o=await s.read(t),r=tt(o);if(!r)throw new Error(`No frontmatter found in ${t}`);await G(t,r.frontmatter,e);},async updateTask(t,e,o){let r=s.join(this.getTaskDir(t,e),a.files.MAIN);if((await Z(r))?.status==="ongoing")throw new Error("Cannot update ongoing task. Should be done by user or update manually");return await ut(r,o)},async ingestTask(t,e){let o=this.getTaskDir(t,e),r=this.getProjectDir(t),n=[s.join(r,a.files.MAIN),s.join(o,a.files.MAIN),s.join(o,a.files.LOG)];await s.logFiles(...n);},async ingestProject(t){let e=this.getProjectDir(t),o=await this.listTasks(t),r=[s.join(e,a.files.MAIN),s.join(e,a.files.LOG),...o.map(n=>s.join(this.getTaskDir(t,n),a.files.MAIN))];await s.logFiles(...r);},async ingestFiles(t){await s.logFiles(...t);}},u=We;});var At,Ot=f(()=>{k();j();x();At=p({options:z$1.object({description:z$1.string().describe("Agent description"),status:z$1.string().default("active").describe("Initial status")}),args:z$1.object({name:z$1.string().describe("Agent name")}),handler:async({name:t,description:e,status:o})=>{let r=s.slugify(t);await u.createAgent(r,{name:t,description:e,status:o,created:new Date().toISOString()}),console.log(`Agent created: ${r}`),console.log(` Path: ${u.getAgentDir(r)}`);}});});var Dt,Ct=f(()=>{k();Dt=p({options:z$1.object({}),handler:async()=>{let t=process.env.CURRENT_AGENT;t||(console.error("CURRENT_AGENT not set"),process.exit(1)),console.log(t);}});});var Nt,Rt=f(()=>{k();x();Nt=p({options:z$1.object({}),handler:async()=>{let t=await u.listAgents(),e=[];for(let o of t){let r=await u.getAgent(o);r&&e.push({slug:o,name:r.name||o,status:r.status,description:r.description});}if(e.length===0){console.log("No agents found");return}console.log("Agents:"),console.log("---");for(let o of e)console.log(`${o.slug.padEnd(20)} ${o.name?.padEnd(30)||""} ${o.status||""} ${o.description||""}`);}});});var It,Lt=f(()=>{P();k();H();j();It=p({description:"Output agent directory path (for cd)",options:z$1.object({}),args:z$1.object({name:z$1.string().describe("Agent name (directory name)")}),handler:async({name:t})=>{let e=s.join(A.AIP_HOME,a.dirs.AGENTS,t),o=s.join(e,a.files.MAIN);if(!await s.fileExists(o))throw new Error(`Agent not found: ${t}
18
- Expected at: ${o}`);console.log(e);}});});var Ft,_t=f(()=>{P();k();H();j();Ft=p({description:"Start an agent: read SOUL.md and AGENTS.md content",options:z$1.object({}),args:z$1.object({name:z$1.string().describe("Agent name (directory name)")}),handler:async({name:t})=>{let e=s.join(A.AIP_HOME,a.dirs.AGENTS,t),o=s.join(e,a.files.MAIN),r=s.join(e,"SOUL.md");if(!await s.fileExists(o))throw new Error(`Agent not found: ${t}
19
- Expected at: ${o}`);let i=[r,o];await s.logFiles(...i);}});});var Ut,Ht,Xe,Kt,Gt=f(()=>{k();Ut=t=>{let e=t.shape,o=[];for(let[r,n]of Object.entries(e)){let i=n._def,c="unknown",d,m;i.typeName==="ZodString"?c="string":i.typeName==="ZodNumber"?c="number":i.typeName==="ZodBoolean"?c="boolean":i.typeName==="ZodEnum"?c=`enum: ${i.values.join(" | ")}`:i.typeName==="ZodOptional"?c=Ht(i.innerType):i.typeName==="ZodDefault"&&(c=Ht(i.innerType),m=i.defaultValue()),i.description&&(d=i.description);let l=i.typeName!=="ZodOptional"&&i.typeName!=="ZodDefault";o.push({name:r,type:c,required:l,description:d,defaultValue:m});}return o},Ht=t=>{let e=t._def;return e.typeName==="ZodString"?"string":e.typeName==="ZodNumber"?"number":e.typeName==="ZodBoolean"?"boolean":e.typeName==="ZodEnum"?`enum: ${e.values.join(" | ")}`:"unknown"},Xe=(t,e,o)=>{let r=Ut(o.options),n=o.args?Ut(o.args):[],i=o.description,c=`\`${t} ${e}\``;if(i&&(c+=`: ${i}`),c+=`
17
+ `);},dt=async(t,e)=>{let o=await s.read(t),r=tt(o);if(!r)throw new Error(`No frontmatter found in ${t}`);let n={...r.frontmatter,...e};return await G(t,n,r.content),n},Z=async t=>{let e=await s.read(t),o=tt(e);if(!o)throw new Error(`No frontmatter found in ${t}`);return o.frontmatter};});var M,et,xt,$t,Ve,Ze,u,x=f(()=>{P();H();St();j();M=(c=>(c.BACKLOG="backlog",c.IN_PROGRESS="in-progress",c.ONGOING="ongoing",c.DONE="done",c.BLOCKED="blocked",c.TO_DO="to-do",c))(M||{}),et=Object.values(M),xt=["backlog","in-progress","ongoing"],$t=[a.dirs.HOOKS,a.dirs.INPUTS,a.dirs.OUTPUTS,a.dirs.SCRIPTS],Ve=[a.dirs.TASKS,...$t],Ze={getProjectDir(t){return s.joinHome(a.dirs.PROJECTS,t)},getTaskDir(t,e){return s.join(this.getProjectDir(t),a.dirs.TASKS,e)},async findTask(t,e){if(e)return {project:e,task:t};let o=await this.listProjects(),r=[];if(await s.promiseEach(o,async n=>{(await this.listTasks(n)).includes(t)&&r.push(n);}),r.length===0)throw new Error(`Task '${t}' not found. Use --project to specify the project.`);if(r.length>1)throw new Error(`Task '${t}' found in multiple projects: ${r.join(", ")}. Use --project to specify which one.`);return {project:r[0],task:t}},getAgentDir(t){return s.join(A.AIP_HOME,a.dirs.AGENTS,t)},async listProjects(){let t=s.join(A.AIP_HOME,a.dirs.PROJECTS),e=await s.listDir(t);return (await s.promiseMap(e,async r=>{let n=this.getProjectDir(r);return await s.fileExists(n)&&r!==a.dirs.AGENTS?r:null})).filter(r=>r!==null).sort()},async listTasks(t){let e=s.join(this.getProjectDir(t),a.dirs.TASKS);return await s.fileExists(e)?await s.listDir(e):[]},async listAgents(){let t=s.join(A.AIP_HOME,a.dirs.AGENTS);return await s.fileExists(t)?await s.listDir(t):[]},async createProject(t,e,o){let r=this.getProjectDir(t);return await s.ensureDir(r),await Promise.all(Ve.map(n=>s.ensureDir(s.join(r,n)))),await G(s.join(r,a.files.MAIN),e,o),await s.write(s.join(r,a.files.LOG),""),r},async createTask(t,e,o,r){let n=this.getTaskDir(t,e);return await s.ensureDir(n),await Promise.all($t.map(i=>s.ensureDir(s.join(n,i)))),await G(s.join(n,a.files.MAIN),o,r),await s.write(s.join(n,a.files.LOG),""),n},async createAgent(t,e){let o=this.getAgentDir(t);return await s.ensureDir(o),await G(s.join(o,a.files.MAIN),e),o},async getProject(t){let e=s.join(this.getProjectDir(t),a.files.MAIN);return await s.fileExists(e)?await Z(e):null},async getTask(t,e){let o=s.join(this.getTaskDir(t,e),a.files.MAIN);return await s.fileExists(o)?await Z(o):null},async getAgent(t){let e=s.join(this.getAgentDir(t),a.files.MAIN);return await s.fileExists(e)?await Z(e):null},async updateProject(t,e){let o=s.join(this.getProjectDir(t),a.files.MAIN);return await dt(o,e)},async updateBody(t,e){let o=await s.read(t),r=tt(o);if(!r)throw new Error(`No frontmatter found in ${t}`);await G(t,r.frontmatter,e);},async updateTask(t,e,o){let r=s.join(this.getTaskDir(t,e),a.files.MAIN);if((await Z(r))?.status==="ongoing")throw new Error("Cannot update ongoing task. Should be done by user or update manually");return await dt(r,o)},async ingestTask(t,e){let o=this.getTaskDir(t,e),r=this.getProjectDir(t),n=[s.join(r,a.files.MAIN),s.join(o,a.files.MAIN),s.join(o,a.files.LOG)];await s.logFiles(...n);},async ingestProject(t){let e=this.getProjectDir(t),o=await this.listTasks(t),r=[s.join(e,a.files.MAIN),s.join(e,a.files.LOG),...o.map(n=>s.join(this.getTaskDir(t,n),a.files.MAIN))];await s.logFiles(...r);},async ingestFiles(t){await s.logFiles(...t);}},u=Ze;});var vt,At=f(()=>{k();j();x();vt=p({options:z$1.object({description:z$1.string().describe("Agent description"),status:z$1.string().default("active").describe("Initial status")}),args:z$1.object({name:z$1.string().describe("Agent name")}),handler:async({name:t,description:e,status:o})=>{let r=s.slugify(t);await u.createAgent(r,{name:t,description:e,status:o,created:new Date().toISOString()}),console.log(`Agent created: ${r}`),console.log(` Path: ${u.getAgentDir(r)}`);}});});var Et,Dt=f(()=>{k();Et=p({options:z$1.object({}),handler:async()=>{let t=process.env.CURRENT_AGENT;t||(console.error("CURRENT_AGENT not set"),process.exit(1)),console.log(t);}});});var Ot,Ct=f(()=>{k();x();Ot=p({options:z$1.object({}),handler:async()=>{let t=await u.listAgents(),e=[];for(let o of t){let r=await u.getAgent(o);r&&e.push({slug:o,description:r.description});}if(e.length===0)return console.log("No agents found");console.log("Agents:");for(let o of e)console.log(`- ${o.slug}: ${o.description||""}`);}});});var Rt,Nt=f(()=>{P();k();H();j();Rt=p({description:"Output agent directory path (for cd)",options:z$1.object({}),args:z$1.object({name:z$1.string().describe("Agent name (directory name)")}),handler:async({name:t})=>{let e=s.join(A.AIP_HOME,a.dirs.AGENTS,t),o=s.join(e,a.files.MAIN);if(!await s.fileExists(o))throw new Error(`Agent not found: ${t}
18
+ Expected at: ${o}`);console.log(e);}});});var It,Ft=f(()=>{P();k();H();j();It=p({description:"Start an agent: read SOUL.md and AGENTS.md content",options:z$1.object({}),args:z$1.object({name:z$1.string().describe("Agent name (directory name)")}),handler:async({name:t})=>{let e=s.join(A.AIP_HOME,a.dirs.AGENTS,t),o=s.join(e,a.files.MAIN),r=s.join(e,"SOUL.md");if(!await s.fileExists(o))throw new Error(`Agent not found: ${t}
19
+ Expected at: ${o}`);let i=[r,o];await s.logFiles(...i);}});});var Lt,_t,Qe,Ut,Ht=f(()=>{k();Lt=t=>{let e=t.shape,o=[];for(let[r,n]of Object.entries(e)){let i=n._def,c="unknown",d,m;i.typeName==="ZodString"?c="string":i.typeName==="ZodNumber"?c="number":i.typeName==="ZodBoolean"?c="boolean":i.typeName==="ZodEnum"?c=`enum: ${i.values.join(" | ")}`:i.typeName==="ZodOptional"?c=_t(i.innerType):i.typeName==="ZodDefault"&&(c=_t(i.innerType),m=i.defaultValue()),i.description&&(d=i.description);let l=i.typeName!=="ZodOptional"&&i.typeName!=="ZodDefault";o.push({name:r,type:c,required:l,description:d,defaultValue:m});}return o},_t=t=>{let e=t._def;return e.typeName==="ZodString"?"string":e.typeName==="ZodNumber"?"number":e.typeName==="ZodBoolean"?"boolean":e.typeName==="ZodEnum"?`enum: ${e.values.join(" | ")}`:"unknown"},Qe=(t,e,o)=>{let r=Lt(o.options),n=o.args?Lt(o.args):[],i=o.description,c=`\`${t} ${e}\``;if(i&&(c+=`: ${i}`),c+=`
20
20
  `,r.length>0||n.length>0){let m=[];for(let l of n){let g=l.required?`<${l.name}>`:`[${l.name}]`,b=l.description||"";l.defaultValue!==void 0&&(b+=b?` (default: ${l.defaultValue})`:`(default: ${l.defaultValue})`),m.push(`\xB7 ${g}${b?": "+b:""}`);}for(let l of r){let g=l.description||"";l.defaultValue!==void 0&&(g+=g?` (default: ${l.defaultValue})`:`(default: ${l.defaultValue})`),m.push(`\xB7 --${l.name}${g?": "+g:""}`);}c+=m.join(`
21
21
  `)+`
22
22
  `;}return c+=`
23
- `,c},Kt=p({description:"Print generated CLI reference from command schemas",options:z$1.object({}),handler:async()=>{let{default:t}=await Promise.resolve().then(()=>(ot(),kt)),e="Prepend `aip` to each command\n\n",o=Object.keys(t).sort();for(let r of o){let n=t[r],i=Object.keys(n).sort();for(let c of i){let d=n[c];e+=Xe(r,c,d);}}console.log(e);}});});var eo,Mt,zt=f(()=>{k();eo=`# AIP Hooks
23
+ `,c},Ut=p({description:"Print generated CLI reference from command schemas",options:z$1.object({}),handler:async()=>{let{default:t}=await Promise.resolve().then(()=>(ot(),ft)),e="Prepend `aip` to each command\n\n",o=Object.keys(t).sort();for(let r of o){let n=t[r],i=Object.keys(n).sort();for(let c of i){let d=n[c];e+=Qe(r,c,d);}}console.log(e);}});});var to,Kt,Gt=f(()=>{k();to=`# AIP Hooks
24
24
 
25
25
  Automated scripts that run before/after project/task actions.
26
26
 
@@ -200,78 +200,88 @@ DEBUG=1 aip task update --status done
200
200
  ---
201
201
 
202
202
  *Hooks automate workflow enforcement. Pre-hooks guard quality, post-hooks handle cleanup.*
203
- `,Mt=p({description:"Hook types, env vars, and patterns",options:z$1.object({}),handler:async()=>{console.log(eo);}});});var O,Bt,ro,Jt,Vt=f(()=>{P();k();B();O=Object.keys(S.bin)[0],{LOG:Bt}=a.files,ro=`# Quick Start
203
+ `,Kt=p({description:"Hook types, env vars, and patterns",options:z$1.object({}),handler:async()=>{console.log(to);}});});var $,Mt,oo,zt,Bt=f(()=>{B();P();k();$=Object.keys(S.bin)[0],{LOG:Mt}=a.files,oo=`# Quick Start
204
204
 
205
205
  ## New Task/Project
206
206
 
207
207
  \`\`\`bash
208
208
  # 1. Create project (if needed)
209
- ${O} project create "my-project" --description "What I'm building" --body $'# Goals
209
+ ${$} project create "my-project" --description "What I'm building" --body $'# Goals
210
210
  - Build X
211
211
  - Solve Y'
212
212
 
213
213
  # 2. Create task (heredoc works too)
214
- ${O} task create my-project "first-task" --description "Start here" --body "$(cat <<< '# Task Details
214
+ ${$} task create my-project "first-task" --description "Start here" --body "$(cat <<< '# Task Details
215
215
  - Step 1
216
216
  - Step 2')"
217
217
 
218
218
  # 3. Navigate to task
219
- cd $(${O} task path first-task)
219
+ cd $(${$} task path first-task)
220
220
 
221
221
  # 4. Start working (implicit from PWD)
222
- ${O} task start
222
+ ${$} task start
223
223
  # \u2192 sets status to in-progress, reads all context to stdout
224
224
 
225
225
  # 5. Work session
226
- # Log progress: ${O} log append "API integration complete"
227
- # Save: outputs/ to deliver, inputs/ for data
226
+ # Log progress: ${$} log append "API integration complete"
227
+ # Save deliverables to outputs/ (e.g. reports, leads, etc.)
228
+ # Save intermediate/raw (lower value) data to inputs/ (e.g. raw API responses, downloads)
228
229
  \`\`\`
229
230
 
230
231
  ## Resuming
231
232
 
232
233
  \`\`\`bash
233
- # Find your tasks (searches all projects)
234
- ${O} task list
234
+ # Find your tasks (searches all projects), can pass --project or --search to filter
235
+ ${$} task list
235
236
 
236
237
  # Navigate to task
237
- cd $(${O} task path task-slug)
238
+ cd $(${$} task path task-slug)
238
239
 
239
240
  # All commands work implicitly from PWD:
240
- ${O} task start # Start working (sets status to in-progress, reads all context to stdout)
241
+ ${$} task start # Start working (sets status to in-progress, reads all context to stdout)
241
242
  \`\`\`
242
243
 
243
244
  ## Completing
244
245
 
245
246
  \`\`\`bash
246
247
  # Mark done (implicit from PWD)
247
- ${O} task update --status done
248
- ${O} log append "if you want to log something extra"
248
+ ${$} task update --status done
249
+ ${$} log append "if you want to log something extra"
249
250
  \`\`\`
250
251
 
251
252
  ## Check current task
252
253
 
253
254
  \`\`\`bash
254
- ${O} task current
255
+ ${$} task current
255
256
  \`\`\`
256
257
 
257
- **Note:** All task commands accept an optional task slug, but use PWD if not provided. Navigate with \`cd\` first for smoother workflow.
258
+ **Note:** All task commands (except create) accept an optional task slug, but use PWD if not provided. Navigate with \`cd\` first for smoother workflow.
258
259
 
259
260
  ## Structure
260
261
 
261
- \`\`\`
262
262
  $AIP_HOME/projects/slug/
263
263
  \u251C\u2500\u2500 main.md # Definition + body
264
- \u251C\u2500\u2500 ${Bt} # Log (auto-appended, has key task updates too)
264
+ \u251C\u2500\u2500 ${Mt} # Log (auto-appended, has key task updates too)
265
265
  \u2514\u2500\u2500 tasks/task/
266
266
  \u251C\u2500\u2500 main.md # Task definition + body
267
- \u2514\u2500\u2500 ${Bt} # Updates
268
- \`\`\`
267
+ \u2514\u2500\u2500 ${Mt} # Updates
269
268
 
270
269
  **Log format:** TSV with columns: date, time, entityType, slug, action, text
271
270
  **Example:** \`2026-03-30<TAB>14:30:00<TAB>task<TAB>api-integration<TAB>log<TAB>API integration complete\`
272
271
 
273
- Run \`${O} help usage\` for all commands.
274
- `,Jt=p({description:"Quick start guide for new users",options:z$1.object({}),handler:async()=>{console.log(ro);}});});var so,no,io,ao,co,po,lo,Wt,yt,q,ht,qt,Yt=f(()=>{B();P();k();x();so=Object.values(M).join("|"),{PROJECTS:no,TASKS:io,HOOKS:ao,INPUTS:co,OUTPUTS:po,SCRIPTS:lo}=a.dirs,{MAIN:Wt,LOG:yt}=a.files,q=Object.keys(S.bin)[0],ht=`# AIP - Project Management Skill
272
+ ## Help
273
+
274
+ \`\`\`bash
275
+ # Get all nouns and verbs
276
+ ${$} --help
277
+
278
+ # All verbs for a noun
279
+ ${$} <noun> --help
280
+
281
+ # All parameters and --options for a command
282
+ ${$} <noun> <verb> --help
283
+ \`\`\`
284
+ `,zt=p({description:"Quick start guide for new users",options:z$1.object({}),handler:async()=>{console.log(oo);}});});var ro,so,no,io,ao,co,po,Vt,gt,q,kt,Zt,Wt=f(()=>{B();P();k();x();ro=Object.values(M).join("|"),{PROJECTS:so,TASKS:no,HOOKS:io,INPUTS:ao,OUTPUTS:co,SCRIPTS:po}=a.dirs,{MAIN:Vt,LOG:gt}=a.files,q=Object.keys(S.bin)[0],kt=`# AIP - Project Management Skill
275
285
 
276
286
  Automated project and task creation with proper structure, front-matter, and file organization.
277
287
 
@@ -289,17 +299,17 @@ Streamline creation of new projects and tasks with the correct directory structu
289
299
  ## File Structure Overview
290
300
 
291
301
  \`\`\`
292
- $AIP_HOME/${no}/{project-slug}/
293
- \u251C\u2500\u2500 ${Wt} # Goals + body (YAML frontmatter + markdown content)
294
- \u251C\u2500\u2500 ${yt} # Chronological log (TSV: date, time, entityType, slug, action, text)
295
- \u251C\u2500\u2500 ${ao}/ # pre|post-{create,start,update,complete}.*
296
- \u251C\u2500\u2500 ${po}/ # Deliverables
297
- \u251C\u2500\u2500 ${co}/ # External data
298
- \u251C\u2500\u2500 ${lo}/ # Automation
299
- \u2514\u2500\u2500 ${io}/
302
+ $AIP_HOME/${so}/{project-slug}/
303
+ \u251C\u2500\u2500 ${Vt} # Goals + body (YAML frontmatter + markdown content)
304
+ \u251C\u2500\u2500 ${gt} # Chronological log (TSV: date, time, entityType, slug, action, text)
305
+ \u251C\u2500\u2500 ${io}/ # pre|post-{create,start,update,complete}.*
306
+ \u251C\u2500\u2500 ${ao}/ # Intermediate/raw data (e.g. API responses, downloads)
307
+ \u251C\u2500\u2500 ${co}/ # Deliverables (e.g. reports, leads, etc.)
308
+ \u251C\u2500\u2500 ${po}/ # Automation
309
+ \u2514\u2500\u2500 ${no}/
300
310
  \u2514\u2500\u2500 {task-slug}/
301
- \u251C\u2500\u2500 ${Wt} # Task definition + body
302
- \u251C\u2500\u2500 ${yt} # Activity log
311
+ \u251C\u2500\u2500 ${Vt} # Task definition + body
312
+ \u251C\u2500\u2500 ${gt} # Activity log
303
313
  \u2514\u2500\u2500 ... (same structure)
304
314
  \`\`\`
305
315
 
@@ -310,7 +320,7 @@ $AIP_HOME/${no}/{project-slug}/
310
320
  name: Clear Title
311
321
  description: One-line summary
312
322
  assignee: agent-name
313
- status: ${so}
323
+ status: ${ro}
314
324
  created: YYYY-MM-DD
315
325
  ---
316
326
  \`\`\`
@@ -326,11 +336,11 @@ Markdown content after frontmatter. See \`aip help quickstart\` for examples of
326
336
 
327
337
  ## For Agents
328
338
 
329
- Typical workflow: read \`main.md\` for context, check \`${yt}\` for history, work, and log progress with \`aip log append\`.
330
- `,qt=p({description:"Project management skill for creating and managing projects/tasks",options:z$1.object({mode:z$1.enum(["md","claude","hermes"]).default("md").describe("Output format: md (no header), claude (name+description), hermes (full frontmatter)")}),handler:async t=>{let e=Object.keys(S.bin)[0];if(t.mode==="md"){console.log(ht);return}if(t.mode==="claude"){console.log(`---
339
+ Typical workflow: read \`main.md\` for context, check \`${gt}\` for history, work, and log progress with \`aip log append\`.
340
+ `,Zt=p({description:"Project management skill for creating and managing projects/tasks",options:z$1.object({mode:z$1.enum(["md","claude","hermes"]).default("md").describe("Output format: md (no header), claude (name+description), hermes (full frontmatter)")}),handler:async t=>{let e=Object.keys(S.bin)[0];if(t.mode==="md"){console.log(kt);return}if(t.mode==="claude"){console.log(`---
331
341
  name: ${e}
332
342
  description: ${S.description}
333
- ---`),console.log(),console.log(ht);return}if(t.mode==="hermes"){console.log(`---
343
+ ---`),console.log(),console.log(kt);return}if(t.mode==="hermes"){console.log(`---
334
344
  name: ${e}
335
345
  description: ${S.description}
336
346
  version: ${S.version}
@@ -339,9 +349,9 @@ license: ${S.license}
339
349
  metadata:
340
350
  hermes:
341
351
  tags: [${S.keywords.join(", ")}]
342
- ---`),console.log(),console.log(ht);return}}});});var Qt,Xt=f(()=>{B();k();j();Qt=p({description:"Print compact CLI usage (all nouns, or one noun if name is given)",options:z$1.object({}),args:z$1.object({name:z$1.string().optional().describe("Noun (e.g. task) to list verbs for")}),handler:async({name:t})=>{let{default:e}=await Promise.resolve().then(()=>(ot(),kt)),o=`${S.name} ${S.version} -`,r=s.dumpCommandMapLines(e,t);if(!t)console.log(o,"Usage: aip <noun> <verb> [options]"),console.log(r.map(n=>`-> ${n}`).join(`
343
- `)),console.log(),console.log("Run `aip help quickstart` for a quick start guide"),console.log("Run `aip help skill` for a broader overview");else {let n=r[0]??`${t} {?}`;console.log(o,`Usage: aip ${n} [options]`);}}});});var Tt,mo,y,D=f(()=>{P();j();Tt=s.joinHome(a.dirs.PROJECTS),mo={getProjectFromPwd(t=process.cwd()){let e=rt.relative(Tt,t);if(!e||e.startsWith(".."))return null;let o=e.split(rt.sep).filter(Boolean);return o.length===0?null:o[0]},getTaskFromPwd(t=process.cwd()){let e=rt.relative(Tt,t);if(!e||e.startsWith(".."))return null;let o=e.split(rt.sep).filter(Boolean);return o.length<3||o[1]!==a.dirs.TASKS?null:o[2]},getCurrentContext(t=process.cwd()){return {project:this.getProjectFromPwd(t),task:this.getTaskFromPwd(t)}},requireProject(t=process.cwd()){let e=this.getProjectFromPwd(t);if(!e)throw new Error(`Not in a project directory. PWD: ${t}, projects: ${Tt}`);return e},requireTask(t=process.cwd()){let e=this.getCurrentContext(t);if(!e.project||!e.task)throw new Error(`Not in a task directory. PWD: ${t}`);return {project:e.project,task:e.task}},exportContext(t=process.cwd()){let e=this.getCurrentContext(t),o=[];return e.project&&o.push(`export CURRENT_PROJECT="${e.project}"`),e.task&&o.push(`export CURRENT_TASK="${e.task}"`),process.env.CURRENT_AGENT&&o.push(`export CURRENT_AGENT="${process.env.CURRENT_AGENT}"`),o.join(`
344
- `)},getTargetDir(t,e=process.cwd()){let o=this.getCurrentContext(e);if(t==="project"){let r=this.getProjectFromPwd(e);if(!r)throw new Error("Not in a project directory. Use --project flag or cd into a project.");let n=s.joinHome(a.dirs.PROJECTS,r);return {targetDir:n,projectDir:n,entityType:"project"}}if(t==="task"){if(!o.project||!o.task)throw new Error("Not in a task directory. Use --project and --task flags or cd into a task.");let r=s.joinHome(a.dirs.PROJECTS,o.project);return {targetDir:s.join(r,a.dirs.TASKS,o.task),projectDir:r,entityType:"task"}}if(o.task&&o.project){let r=s.joinHome(a.dirs.PROJECTS,o.project);return {targetDir:s.join(r,a.dirs.TASKS,o.task),projectDir:r,entityType:"task"}}if(o.project){let r=s.joinHome(a.dirs.PROJECTS,o.project);return {targetDir:r,projectDir:r,entityType:"project"}}throw new Error("Not in a project or task directory. Specify --target flag.")}},y=mo;});var te,ee=f(()=>{k();j();D();P();te=p({options:z$1.object({lang:z$1.enum(a.languages).default("ts").describe("Language"),target:z$1.enum(a.targets).default("project").describe("Target level")}),args:z$1.object({type:z$1.enum(a.hookTypes).describe("Hook type (e.g., pre-create, post-complete)")}),handler:async({type:t,lang:e,target:o})=>{let r=e,n=`.${r}`,{targetDir:i,entityType:c}=y.getTargetDir(o),d=s.join(i,a.dirs.HOOKS);await s.ensureDir(d);let m=s.join(d,`${t}${n}`);if(await s.fileExists(m))throw new Error(`Hook already exists: ${m}`);let g;if(r==="ts")g=`#!/usr/bin/env tsx
352
+ ---`),console.log(),console.log(kt);return}}});});var qt,Yt=f(()=>{B();k();j();qt=p({description:"Print compact CLI usage (all nouns, or one noun if name is given)",options:z$1.object({}),args:z$1.object({name:z$1.string().optional().describe("Noun (e.g. task) to list verbs for")}),handler:async({name:t})=>{let{default:e}=await Promise.resolve().then(()=>(ot(),ft)),o=`${S.name} ${S.version} -`,r=s.dumpCommandMapLines(e,t);if(!t)console.log(o,"Usage: aip <noun> <verb> [options]"),console.log(r.map(n=>`-> ${n}`).join(`
353
+ `)),console.log(),console.log("Run `aip help quickstart` for a quick start guide"),console.log("Run `aip help skill` for a broader overview");else {let n=r[0]??`${t} {?}`;console.log(o,`Usage: aip ${n} [options]`);}}});});var yt,lo,h,E=f(()=>{P();j();yt=s.joinHome(a.dirs.PROJECTS),lo={getProjectFromPwd(t=process.cwd()){let e=rt.relative(yt,t);if(!e||e.startsWith(".."))return null;let o=e.split(rt.sep).filter(Boolean);return o.length===0?null:o[0]},getTaskFromPwd(t=process.cwd()){let e=rt.relative(yt,t);if(!e||e.startsWith(".."))return null;let o=e.split(rt.sep).filter(Boolean);return o.length<3||o[1]!==a.dirs.TASKS?null:o[2]},getCurrentContext(t=process.cwd()){return {project:this.getProjectFromPwd(t),task:this.getTaskFromPwd(t)}},requireProject(t=process.cwd()){let e=this.getProjectFromPwd(t);if(!e)throw new Error(`Not in a project directory. PWD: ${t}, projects: ${yt}`);return e},requireTask(t=process.cwd()){let e=this.getCurrentContext(t);if(!e.project||!e.task)throw new Error(`Not in a task directory. PWD: ${t}`);return {project:e.project,task:e.task}},exportContext(t=process.cwd()){let e=this.getCurrentContext(t),o=[];return e.project&&o.push(`export CURRENT_PROJECT="${e.project}"`),e.task&&o.push(`export CURRENT_TASK="${e.task}"`),process.env.CURRENT_AGENT&&o.push(`export CURRENT_AGENT="${process.env.CURRENT_AGENT}"`),o.join(`
354
+ `)},getTargetDir(t,e=process.cwd()){let o=this.getCurrentContext(e);if(t==="project"){let r=this.getProjectFromPwd(e);if(!r)throw new Error("Not in a project directory. Use --project flag or cd into a project.");let n=s.joinHome(a.dirs.PROJECTS,r);return {targetDir:n,projectDir:n,entityType:"project"}}if(t==="task"){if(!o.project||!o.task)throw new Error("Not in a task directory. Use --project and --task flags or cd into a task.");let r=s.joinHome(a.dirs.PROJECTS,o.project);return {targetDir:s.join(r,a.dirs.TASKS,o.task),projectDir:r,entityType:"task"}}if(o.task&&o.project){let r=s.joinHome(a.dirs.PROJECTS,o.project);return {targetDir:s.join(r,a.dirs.TASKS,o.task),projectDir:r,entityType:"task"}}if(o.project){let r=s.joinHome(a.dirs.PROJECTS,o.project);return {targetDir:r,projectDir:r,entityType:"project"}}throw new Error("Not in a project or task directory. Specify --target flag.")}},h=lo;});var Qt,Xt=f(()=>{k();j();E();P();Qt=p({options:z$1.object({lang:z$1.enum(a.languages).default("ts").describe("Language"),target:z$1.enum(a.targets).default("project").describe("Target level")}),args:z$1.object({type:z$1.enum(a.hookTypes).describe("Hook type (e.g., pre-create, post-complete)")}),handler:async({type:t,lang:e,target:o})=>{let r=e,n=`.${r}`,{targetDir:i,entityType:c}=h.getTargetDir(o),d=s.join(i,a.dirs.HOOKS);await s.ensureDir(d);let m=s.join(d,`${t}${n}`);if(await s.fileExists(m))throw new Error(`Hook already exists: ${m}`);let g;if(r==="ts")g=`#!/usr/bin/env tsx
345
355
  /** ${t} hook for ${c} */
346
356
 
347
357
  import { env } from 'node:process'
@@ -388,7 +398,6 @@ if os.environ.get('TASK_SLUG'):
388
398
 
389
399
  # Exit with non-zero to prevent action (for pre-hooks)
390
400
  # sys.exit(1)
391
- `;else throw new Error(`Unsupported language: ${r}`);await s.write(m,g);let{chmod:b}=await import('fs/promises');await b(m,493),console.log(`Hook created: ${m}`);}});});var fo,$,z=f(()=>{P();j();fo={async findHooks(t,e){let o=s.join(t,a.dirs.HOOKS);if(!await s.fileExists(o))return [];let n=await s.listDir(o),i=`${e}.`;return n.filter(c=>c.startsWith(i)).map(c=>s.join(o,c))},async executeHook(t,e,o){if(!await s.fileExists(t))return true;let n=e.project?s.joinHome(a.dirs.PROJECTS,e.project):o,i=e.task?s.join(n,a.dirs.TASKS,e.task):"",c={...process.env,HOOK_TYPE:e.action,ENTITY_TYPE:e.entityType,TARGET_DIR:o,PROJECT_DIR:n,...e.project?{PROJECT_SLUG:e.project}:{},...e.task?{TASK_SLUG:e.task,TASK_DIR:i}:{}};return new Promise(d=>{let m=spawn(t,[],{stdio:"inherit",env:c,cwd:rt.dirname(t),shell:false});m.on("close",(l,g)=>{l===0?d(true):(console.error(`Hook ${rt.basename(t)} failed (code=${l??g})`),d(false));}),m.on("error",l=>{console.error(`Failed to execute hook ${t}: ${l.message}`),d(false);});})},async runHooks(t,e,o){let r=await this.findHooks(t,e),n=e.startsWith("pre-");for(let i of r)if(!await this.executeHook(i,o,t)&&n)return false;return true},async runHooksForContext(t,e,o,r){return !(!await this.runHooks(t,o,r)||e&&!await this.runHooks(e,o,r))}},$=fo;});var re,se=f(()=>{k();z();D();P();re=p({options:z$1.object({target:z$1.enum(a.targets).optional().describe("Target level")}),args:z$1.object({type:z$1.enum(a.hookTypes).describe("Hook type to run")}),handler:async({type:t,target:e})=>{let{targetDir:o,entityType:r}=y.getTargetDir(e),n={action:t,entityType:r,project:y.getProjectFromPwd()||void 0,task:y.getTaskFromPwd()||void 0};await $.runHooks(o,t,n)||(console.error(`Hook ${t} failed`),process.exit(1)),console.log(`Hook ${t} completed successfully`);}});});var go,w,L=f(()=>{P();D();j();go={formatEntry(t,e,o,r){let n=new Date,i=n.getFullYear(),c=String(n.getMonth()+1).padStart(2,"0"),d=String(n.getDate()).padStart(2,"0"),m=n.toLocaleTimeString("en-US",{hour12:false,hour:"2-digit",minute:"2-digit",second:"2-digit"});return `${`${i}-${c}-${d}`} ${m} ${t} ${e} ${o} ${r}
392
- `},async append(t,e,o,r,n){let i=s.join(t,a.files.LOG),c=this.formatEntry(e,o,r,n);await s.append(i,c);},async read(t){let e=s.join(t,a.files.LOG);return await s.fileExists(e)?await s.read(e):""},async task(t,e,o){let r=y.getCurrentContext();if(!r.project||!r.task)throw new Error("Not in a task directory");let n=s.joinHome(a.dirs.PROJECTS,r.project,a.dirs.TASKS,r.task);await this.append(n,"task",t,"log",o?`${o}: ${e}`:e);},async project(t,e,o){let r=y.getProjectFromPwd();if(!r)throw new Error("Not in a project directory");let n=s.joinHome(a.dirs.PROJECTS,r);await this.append(n,"project",t,"log",o?`${o}: ${e}`:e);}},w=go;});var ne,ie=f(()=>{P();D();k();j();L();ne=p({description:"Append a custom entry to log.tsv",options:z$1.object({project:z$1.string().optional().describe("Project slug (uses current project from $PWD if not provided)"),task:z$1.string().optional().describe("Task slug (uses current task from $PWD if not provided)")}),args:z$1.object({text:z$1.string().describe("Text to append")}),handler:async({project:t,task:e,text:o})=>{let r=y.getCurrentContext(),n=t||r.project;if(!n)throw new Error("No project specified. Use --project or run from a project/task directory");let i=s.joinHome(a.dirs.PROJECTS,n);if(e||r.task){let c=e||r.task;if(!c)throw new Error("No task specified");let d=s.join(i,a.dirs.TASKS,c);await w.append(d,"task",c,"log",o),console.log(`Appended to task ${c} log.tsv`);}else await w.append(i,"project",n,"log",o),console.log(`Appended to project ${n} log.tsv`);}});});var ae,ce=f(()=>{P();D();k();j();L();ae=p({description:"Read log.tsv history",options:z$1.object({project:z$1.string().optional().describe("Project slug (uses current project from $PWD if not provided)"),task:z$1.string().optional().describe("Task slug (uses current task from $PWD if not provided)")}),args:z$1.object({}),handler:async({project:t,task:e})=>{let o=y.getCurrentContext(),r=t||o.project;if(!r)throw new Error("No project specified. Use --project or run from a project/task directory");let n=s.joinHome(a.dirs.PROJECTS,r);if(e||o.task){let i=e||o.task;if(!i)throw new Error("No task specified");let c=s.join(n,a.dirs.TASKS,i),d=await w.read(c);console.log(d||`No log.tsv found for task ${i}`);}else {let i=await w.read(n);console.log(i||`No log.tsv found for project ${r}`);}}});});var pe,le=f(()=>{k();z();j();x();L();pe=p({description:"Create a new project with name, description, optional status, assignee and body",options:z$1.object({description:z$1.string().describe("Project description"),status:z$1.string().default("active").describe("Initial status"),assignee:z$1.string().optional().describe("Assignee agent slug"),body:z$1.string().optional().describe("Initial body/content (markdown)")}),args:z$1.object({name:z$1.string().describe("Project name")}),handler:async({name:t,description:e,status:o,assignee:r,body:n})=>{let i=s.slugify(t),c=u.getProjectDir(i);if(!await $.runHooks(c,"pre-create",{action:"pre-create",entityType:"project"}))throw new Error("Pre-create hook failed, aborting project creation");await u.createProject(i,{name:t,description:e,status:o,assignee:r,created:new Date().toISOString()},n),await w.append(c,"project",i,"created",`${t} > status is ${o}`),await $.runHooks(c,"post-create",{action:"post-create",entityType:"project"}),console.log(`Project created: ${i}`),console.log(` Path: ${c}`);}});});var de,me=f(()=>{k();D();de=p({options:z$1.object({}),handler:async()=>{let t=y.getProjectFromPwd();t||(console.error("Not in a project directory"),process.exit(1)),console.log(t);}});});var fe,ge=f(()=>{k();x();fe=p({description:"List all projects, optionally filtered by status",options:z$1.object({status:z$1.string().optional().describe("Filter by status")}),handler:async({status:t})=>{let e=await u.listProjects(),o=[];for(let r of e){let n=await u.getProject(r);n&&(t&&n.status!==t||o.push({slug:r,name:n.name||r,status:n.status,assignee:n.assignee}));}if(o.length===0){console.log("No projects found");return}console.log("Projects:"),console.log("---");for(let r of o)console.log(`${r.slug.padEnd(20)} ${r.name?.padEnd(30)||""} ${r.status||""} ${r.assignee||""}`);}});});var ke,ye=f(()=>{k();x();ke=p({description:"Output project directory path (for cd)",options:z$1.object({}),args:z$1.object({name:z$1.string().describe("Project name (slug)")}),handler:async({name:t})=>{let e=u.getProjectDir(t);console.log(e);}});});var he,je=f(()=>{P();D();k();z();j();x();L();he=p({description:"Update project properties: name, description, status, assignee, or replace body",options:z$1.object({name:z$1.string().optional().describe("New name"),description:z$1.string().optional().describe("New description"),status:z$1.string().optional().describe("New status"),assignee:z$1.string().optional().describe("New assignee"),body:z$1.string().optional().describe("Replace entire body/content (markdown)")}),args:z$1.object({project:z$1.string().optional().describe("Project slug (defaults to current project from $PWD)")}),handler:async({project:t,name:e,description:o,status:r,assignee:n,body:i})=>{let c=t||y.getProjectFromPwd();if(!c)throw new Error("No project specified and not in a project directory");let d=u.getProjectDir(c),m=s.join(d,a.files.MAIN);if(!await $.runHooks(d,"pre-update",{action:"pre-update",entityType:"project"}))throw new Error("Pre-update hook failed, aborting update");let g={};if(e&&(g.name=e),o&&(g.description=o),r&&(g.status=r),n&&(g.assignee=n),Object.keys(g).length===0&&!i){console.log("No updates provided");return}if(Object.keys(g).length>0){await u.updateProject(c,g);let b=Object.entries(g).map(([C,E])=>`${C}=${E}`).join(", ");await w.append(d,"project",c,"updated",b);}i!==void 0&&(await u.updateBody(m,i),await w.append(d,"project",c,"updated",`body replaced (${i.length} chars)`)),await $.runHooks(d,"post-update",{action:"post-update",entityType:"project"}),console.log(`Project ${c} updated`);}});});var Te,we=f(()=>{P();k();H();j();Te=p({description:"Read a skill's SKILL.md file into the console for agent context",options:z$1.object({}),args:z$1.object({name:z$1.string().describe("Skill name (directory name)")}),handler:async({name:t})=>{let e=s.join(A.AIP_HOME,a.dirs.SKILLS,t),o=s.join(e,a.files.SKILL);if(!await s.fileExists(o))throw new Error(`Skill not found: ${t}
393
- Expected at: ${o}`);await s.logFiles(o);}});});var be,Pe=f(()=>{k();z();j();x();L();be=p({description:"Create a new task with assignee, initial status and optional body",options:z$1.object({description:z$1.string().optional().describe("Task description"),assignee:z$1.string().optional().describe("Assignee agent slug"),status:z$1.string().default("backlog").describe("Initial status"),body:z$1.string().optional().describe("Initial body/content (markdown)")}),args:z$1.object({project:z$1.string().describe("Project slug"),name:z$1.string().describe("Task name")}),handler:async({project:t,name:e,description:o,assignee:r,status:n,body:i})=>{let c=s.slugify(e),d=u.getProjectDir(t),m=u.getTaskDir(t,c);if(!await $.runHooksForContext(d,m,"pre-create",{action:"pre-create",entityType:"task",project:t}))throw new Error("Pre-create hook failed, aborting task creation");await u.createTask(t,c,{name:e,description:o,assignee:r,status:n,created:new Date().toISOString()},i),await w.append(m,"task",c,"created",e),await w.append(d,"task",c,"created",`${c} > status is ${n}`),await $.runHooksForContext(d,m,"post-create",{action:"post-create",entityType:"task",project:t}),console.log(`Task created: ${c}`),console.log(` Project: ${t}`),console.log(` Path: ${m}`);}});});var Se,xe=f(()=>{k();D();Se=p({description:"Get the current task slug from PWD",options:z$1.object({}),handler:async()=>{let t=y.getTaskFromPwd();t||(console.error("Not in a task directory"),process.exit(1)),console.log(t);}});});var $e,ve=f(()=>{D();k();x();$e=p({description:"Output full task context (main.md, log.tsv) for ingestion by agents",options:z$1.object({project:z$1.string().optional().describe("Project slug (searches all projects if not provided)")}),args:z$1.object({task:z$1.string().optional().describe("Task slug (default: from $PWD)")}),handler:async({project:t,task:e})=>{let o=y.getCurrentContext(),r=e??o.task;if(!r)throw new Error("Need task slug (or cd into task dir)");let{project:n}=await u.findTask(r,t);await u.ingestTask(n,r);}});});var Ee,Ae=f(()=>{k();x();Ee=p({description:"List tasks, optionally filtered by project, status, assignee, or search query",options:z$1.object({project:z$1.string().optional().describe("Project slug (searches all projects if not provided)"),statuses:z$1.array(z$1.string()).default([]).describe("Filter by statuses (multiple allowed)"),assignee:z$1.string().optional().describe("Filter by assignee"),all:z$1.boolean().default(false).describe("Include all tasks (including done/blocked)"),search:z$1.string().optional().describe("Search query (matches task slug or name, case-insensitive)")}),handler:async({project:t,statuses:e,assignee:o,all:r,search:n})=>{if(e&&e.length>0){for(let l of e)if(!et.includes(l))throw new Error(`Invalid status: ${l}. Valid values: ${et.join(", ")}`)}let i;e&&e.length>0?i=e:r?i=et:i=vt;let c=t?[t]:await u.listProjects(),d={};for(let l of c){let g=await u.listTasks(l),b=[];for(let C of g){let E=await u.getTask(l,C);if(E&&!(i.length>0&&!i.includes(E.status||""))&&!(o&&E.assignee!==o)){if(n){let v=n.toLowerCase(),lt=C.toLowerCase().includes(v),dt=(E.name||"").toLowerCase().includes(v);if(!lt&&!dt)continue}b.push({slug:C,name:E.name||C,status:E.status,assignee:E.assignee});}}b.length>0&&(d[l]=b.sort((C,E)=>C.slug.localeCompare(E.slug)));}let m=Object.keys(d).sort();if(m.length===0){console.log("No tasks found");return}for(let l of m){console.log(`
394
- ${l}:`),console.log("---");for(let g of d[l])console.log(`${g.slug.padEnd(20)} ${g.name?.padEnd(30)||""} ${g.status||""} ${g.assignee||""}`);}}});});var Oe,De=f(()=>{k();x();Oe=p({description:"Output task directory path (for cd)",options:z$1.object({project:z$1.string().optional().describe("Project slug (searches all projects if not provided)")}),args:z$1.object({task:z$1.string().describe("Task slug")}),handler:async({project:t,task:e})=>{let{project:o,task:r}=await u.findTask(e,t),n=u.getTaskDir(o,r);console.log(n);}});});var ct,Pt=f(()=>{P();D();k();z();j();x();L();ct=p({description:"Update task properties: name, description, status, assignee, or replace body",options:z$1.object({name:z$1.string().optional().describe("New name"),description:z$1.string().optional().describe("New description"),status:z$1.string().optional().describe("New status"),assignee:z$1.string().optional().describe("New assignee"),project:z$1.string().optional().describe("Project slug (searches all projects if not provided)"),body:z$1.string().optional().describe("Replace entire body/content (markdown)")}),args:z$1.object({task:z$1.string().optional().describe("Task slug (default: from $PWD)")}),handler:async({project:t,task:e,name:o,description:r,status:n,assignee:i,body:c})=>{let d=y.getCurrentContext(),m=e??d.task;if(!m)throw new Error("No task specified (use --task or cd into task dir)");let{project:l}=await u.findTask(m,t),g=u.getTaskDir(l,m),b=u.getProjectDir(l),C=s.join(g,a.files.MAIN);if(!await $.runHooksForContext(b,g,"pre-update",{action:"pre-update",entityType:"task",project:l,task:m}))throw new Error("Pre-update hook failed, aborting update");let v={};if(o&&(v.name=o),r&&(v.description=r),n&&(v.status=n),i&&(v.assignee=i),Object.keys(v).length===0&&!c){console.log("No updates provided");return}if(Object.keys(v).length>0){await u.updateTask(l,m,v);let lt=Object.entries(v).map(([dt,Fe])=>`${dt}=${Fe}`).join(", ");await w.append(g,"task",m,"updated",lt);}c!==void 0&&(await u.updateBody(C,c),await w.append(g,"task",m,"updated",`body replaced (${c.length} chars)`)),v.status&&await w.append(b,"task",m,"updated",`status to ${v.status}`),await $.runHooksForContext(u.getProjectDir(l),g,"post-update",{action:"post-update",entityType:"task",project:l,task:m}),console.log(`Task ${m} updated`);}});});var Ce,Ne=f(()=>{D();k();x();Pt();Ce=p({description:"Start a task: set status to in-progress, optionally print context",options:z$1.object({project:z$1.string().optional().describe("Project slug (searches all projects if not provided)"),ingest:z$1.boolean().default(false).describe("Also output context for this task")}),args:z$1.object({task:z$1.string().optional().describe("Task slug (default: from $PWD)")}),handler:async({project:t,task:e,ingest:o})=>{let r=y.getCurrentContext(),n=e??r.task;if(!n)throw new Error("No task specified (use --task or cd into task dir)");let{project:i}=await u.findTask(n,t);(await u.getTask(i,n))?.status!=="in-progress"&&await ct.handler({project:i,task:n,status:"in-progress"}),o&&await u.ingestTask(i,n);}});});var Re,Ie=f(()=>{k();j();Re=p({description:"Read one or more files into the console",options:z$1.object({}),args:z$1.object({paths:z$1.array(z$1.string()).describe("File paths to read")}),handler:async({paths:t})=>{await s.logFiles(...t);}});});var kt={};Ue(kt,{default:()=>U});var ho,U,ot=f(()=>{Ot();Ct();Rt();Lt();_t();Gt();zt();Vt();Yt();Xt();ee();se();ie();ce();le();me();ge();ye();je();we();Pe();xe();ve();Ae();De();Ne();Pt();Ie();ho={agent:{create:At,current:Dt,list:Nt,path:It,start:Ft},help:{api:Kt,hooks:Mt,quickstart:Jt,skill:qt,usage:Qt},hook:{create:te,run:re},log:{append:ne,read:ae},project:{create:pe,current:de,list:fe,path:ke,update:he},skill:{read:Te},task:{create:be,current:Se,ingest:$e,list:Ee,path:Oe,start:Ce,update:ct},util:{read:Re}},U=ho;});B();ot();j();process.on("unhandledRejection",t=>{console.error("Unhandled Rejection:",t),process.exit(1);});process.on("uncaughtException",t=>{console.error("Unhandled Exception:",t),process.exit(1);});async function Le(t){let[e,o,...r]=t;(e==="--version"||e==="-v")&&(console.log(S.version),process.exit(0));try{(!e||!s.isKeyOf(e,U))&&(await U.help.usage.handler({}),process.exit(1));let n=U[e];(!o||!s.isKeyOf(o,n))&&(await U.help.usage.handler({name:e}),process.exit(1));let i=n[o];i||(console.error(`Error: unknown command '${e} ${o}'`),console.log("Run with --help for more information"),process.exit(1)),await i.handler(i.parser.name(`${e} ${o}`).parse(r));}catch(n){console.error("Error:",s.errorMessage(n)),process.exit(1);}}var Pi=Le,Si=U;s.isMain()&&Le(process.argv.slice(2));export{Si as commands,Pi as default};
401
+ `;else throw new Error(`Unsupported language: ${r}`);await s.write(m,g);let{chmod:b}=await import('fs/promises');await b(m,493),console.log(`Hook created: ${m}`);}});});var fo,v,z=f(()=>{P();j();fo={async findHooks(t,e){let o=s.join(t,a.dirs.HOOKS);if(!await s.fileExists(o))return [];let n=await s.listDir(o),i=`${e}.`;return n.filter(c=>c.startsWith(i)).map(c=>s.join(o,c))},async executeHook(t,e,o){if(!await s.fileExists(t))return true;let n=e.project?s.joinHome(a.dirs.PROJECTS,e.project):o,i=e.task?s.join(n,a.dirs.TASKS,e.task):"",c={...process.env,HOOK_TYPE:e.action,ENTITY_TYPE:e.entityType,TARGET_DIR:o,PROJECT_DIR:n,...e.project?{PROJECT_SLUG:e.project}:{},...e.task?{TASK_SLUG:e.task,TASK_DIR:i}:{}};return new Promise(d=>{let m=spawn(t,[],{stdio:"inherit",env:c,cwd:o,shell:false});m.on("close",(l,g)=>{l===0?d(true):(console.error(`Hook ${rt.basename(t)} failed (code=${l??g})`),d(false));}),m.on("error",l=>{console.error(`Failed to execute hook ${t}: ${l.message}`),d(false);});})},async runHooks(t,e,o){let r=await this.findHooks(t,e),n=e.startsWith("pre-");for(let i of r)if(!await this.executeHook(i,o,t)&&n)return false;return true},async runHooksForContext(t,e,o,r){return !(!await this.runHooks(t,o,r)||e&&!await this.runHooks(e,o,r))}},v=fo;});var te,ee=f(()=>{k();z();E();P();te=p({options:z$1.object({target:z$1.enum(a.targets).optional().describe("Target level")}),args:z$1.object({type:z$1.enum(a.hookTypes).describe("Hook type to run")}),handler:async({type:t,target:e})=>{let{targetDir:o,entityType:r}=h.getTargetDir(e),n={action:t,entityType:r,project:h.getProjectFromPwd()||void 0,task:h.getTaskFromPwd()||void 0};await v.runHooks(o,t,n)||(console.error(`Hook ${t} failed`),process.exit(1)),console.log(`Hook ${t} completed successfully`);}});});var go,w,F=f(()=>{P();E();j();go={formatEntry(t,e,o,r){let n=new Date,i=n.getFullYear(),c=String(n.getMonth()+1).padStart(2,"0"),d=String(n.getDate()).padStart(2,"0"),m=n.toLocaleTimeString("en-US",{hour12:false,hour:"2-digit",minute:"2-digit",second:"2-digit"});return `${`${i}-${c}-${d}`} ${m} ${t} ${e} ${o} ${r}
402
+ `},async append(t,e,o,r,n){let i=s.join(t,a.files.LOG),c=this.formatEntry(e,o,r,n);await s.append(i,c);},async read(t){let e=s.join(t,a.files.LOG);return await s.fileExists(e)?await s.read(e):""},async task(t,e,o){let r=h.getCurrentContext();if(!r.project||!r.task)throw new Error("Not in a task directory");let n=s.joinHome(a.dirs.PROJECTS,r.project,a.dirs.TASKS,r.task);await this.append(n,"task",t,"log",o?`${o}: ${e}`:e);},async project(t,e,o){let r=h.getProjectFromPwd();if(!r)throw new Error("Not in a project directory");let n=s.joinHome(a.dirs.PROJECTS,r);await this.append(n,"project",t,"log",o?`${o}: ${e}`:e);}},w=go;});var oe,re=f(()=>{P();E();k();j();F();oe=p({description:"Append a custom entry to log.tsv",options:z$1.object({project:z$1.string().optional().describe("Project slug (uses current project from $PWD if not provided)"),task:z$1.string().optional().describe("Task slug (uses current task from $PWD if not provided)")}),args:z$1.object({text:z$1.string().describe("Text to append")}),handler:async({project:t,task:e,text:o})=>{let r=h.getCurrentContext(),n=t||r.project;if(!n)throw new Error("No project specified. Use --project or run from a project/task directory");let i=s.joinHome(a.dirs.PROJECTS,n);if(e||r.task){let c=e||r.task;if(!c)throw new Error("No task specified");let d=s.join(i,a.dirs.TASKS,c);await w.append(d,"task",c,"log",o),console.log(`Appended to task ${c} log.tsv`);}else await w.append(i,"project",n,"log",o),console.log(`Appended to project ${n} log.tsv`);}});});var se,ne=f(()=>{P();E();k();j();F();se=p({description:"Read log.tsv history",options:z$1.object({project:z$1.string().optional().describe("Project slug (uses current project from $PWD if not provided)"),task:z$1.string().optional().describe("Task slug (uses current task from $PWD if not provided)")}),args:z$1.object({}),handler:async({project:t,task:e})=>{let o=h.getCurrentContext(),r=t||o.project;if(!r)throw new Error("No project specified. Use --project or run from a project/task directory");let n=s.joinHome(a.dirs.PROJECTS,r);if(e||o.task){let i=e||o.task;if(!i)throw new Error("No task specified");let c=s.join(n,a.dirs.TASKS,i),d=await w.read(c);console.log(d||`No log.tsv found for task ${i}`);}else {let i=await w.read(n);console.log(i||`No log.tsv found for project ${r}`);}}});});var ie,ae=f(()=>{k();j();F();x();ie=p({description:"Create a new project with name, description, optional status, assignee and body",options:z$1.object({description:z$1.string().describe("Project description"),status:z$1.string().default("active").describe("Initial status"),assignee:z$1.string().optional().describe("Assignee agent slug"),body:z$1.string().optional().describe("Initial body/content (markdown)")}),args:z$1.object({name:z$1.string().describe("Project name")}),handler:async({name:t,description:e,status:o,assignee:r,body:n})=>{let i=s.slugify(t),c=u.getProjectDir(i);await u.createProject(i,{name:t,description:e,status:o,assignee:r,created:new Date().toISOString()},n),await w.append(c,"project",i,"created",`${t} > status is ${o}`),console.log(`Project created: ${i}. Path: ${c}`);}});});var ce,pe=f(()=>{k();E();ce=p({options:z$1.object({}),handler:async()=>{let t=h.getProjectFromPwd();t||(console.error("Not in a project directory"),process.exit(1)),console.log(t);}});});var de,me=f(()=>{k();x();de=p({description:"List all projects, optionally filtered by status",options:z$1.object({status:z$1.string().optional().describe("Filter by status")}),handler:async({status:t})=>{let e=await u.listProjects(),o=[];for(let r of e){let n=await u.getProject(r);n&&(t&&n.status!==t||o.push({slug:r,status:n.status,assignee:n.assignee}));}if(o.length===0)return console.log("No projects found");console.log("Projects:");for(let r of o)console.log(`- ${r.slug}: ${r.status||""} ${r.assignee||""}`);}});});var ue,fe=f(()=>{k();x();ue=p({description:"Output project directory path (for cd)",options:z$1.object({}),args:z$1.object({name:z$1.string().describe("Project name (slug)")}),handler:async({name:t})=>{let e=u.getProjectDir(t);console.log(e);}});});var ge,ke=f(()=>{P();E();k();z();j();x();F();ge=p({description:"Update project properties: name, description, status, assignee, or replace body",options:z$1.object({name:z$1.string().optional().describe("New name"),description:z$1.string().optional().describe("New description"),status:z$1.string().optional().describe("New status"),assignee:z$1.string().optional().describe("New assignee"),body:z$1.string().optional().describe("Replace entire body/content (markdown)")}),args:z$1.object({project:z$1.string().optional().describe("Project slug (defaults to current project from $PWD)")}),handler:async({project:t,name:e,description:o,status:r,assignee:n,body:i})=>{let c=t||h.getProjectFromPwd();if(!c)throw new Error("No project specified and not in a project directory");let d=u.getProjectDir(c),m=s.join(d,a.files.MAIN);if(!await v.runHooks(d,"pre-update",{action:"pre-update",entityType:"project"}))throw new Error("Pre-update hook failed, aborting update");let g={};if(e&&(g.name=e),o&&(g.description=o),r&&(g.status=r),n&&(g.assignee=n),Object.keys(g).length===0&&!i){console.log("No updates provided");return}if(Object.keys(g).length>0){await u.updateProject(c,g);let b=Object.entries(g).map(([O,C])=>`${O}=${C}`).join(", ");await w.append(d,"project",c,"updated",b);}i!==void 0&&(await u.updateBody(m,i),await w.append(d,"project",c,"updated",`body replaced (${i.length} chars)`)),await v.runHooks(d,"post-update",{action:"post-update",entityType:"project"}),console.log(`Project ${c} updated`);}});});var he,ye=f(()=>{P();k();H();j();he=p({description:"Read a skill's SKILL.md file into the console for agent context",options:z$1.object({}),args:z$1.object({name:z$1.string().describe("Skill name (directory name)")}),handler:async({name:t})=>{let e=s.join(A.AIP_HOME,a.dirs.SKILLS,t),o=s.join(e,a.files.SKILL);if(!await s.fileExists(o))throw new Error(`Skill not found: ${t}
403
+ Expected at: ${o}`);await s.logFiles(o);}});});var je,Te=f(()=>{k();z();j();x();F();je=p({description:"Create a new task with assignee, initial status and optional body",options:z$1.object({description:z$1.string().optional().describe("Task description"),assignee:z$1.string().optional().describe("Assignee agent slug"),status:z$1.string().default("backlog").describe("Initial status"),body:z$1.string().optional().describe("Initial body/content (markdown)")}),args:z$1.object({project:z$1.string().describe("Project slug"),name:z$1.string().describe("Task name")}),handler:async({project:t,name:e,description:o,assignee:r,status:n,body:i})=>{let c=s.slugify(e),d=u.getProjectDir(t),m=u.getTaskDir(t,c);if(!await v.runHooksForContext(d,m,"pre-create",{action:"pre-create",entityType:"task",project:t}))throw new Error("Pre-create hook failed, aborting task creation");await u.createTask(t,c,{name:e,description:o,assignee:r,status:n,created:new Date().toISOString()},i),await w.append(m,"task",c,"created",e),await w.append(d,"task",c,"created",`${c} > status is ${n}`),await v.runHooksForContext(d,m,"post-create",{action:"post-create",entityType:"task",project:t}),console.log(`Task created: ${c}`),console.log(` Project: ${t}`),console.log(` Path: ${m}`);}});});var we,be=f(()=>{k();E();we=p({description:"Get the current task slug from PWD",options:z$1.object({}),handler:async()=>{let t=h.getTaskFromPwd();t||(console.error("Not in a task directory"),process.exit(1)),console.log(t);}});});var Pe,Se=f(()=>{E();k();x();Pe=p({description:"Output full task context (main.md, log.tsv) for ingestion by agents",options:z$1.object({project:z$1.string().optional().describe("Project slug (searches all projects if not provided)")}),args:z$1.object({task:z$1.string().optional().describe("Task slug (default: from $PWD)")}),handler:async({project:t,task:e})=>{let o=h.getCurrentContext(),r=e??o.task;if(!r)throw new Error("Need task slug (or cd into task dir)");let{project:n}=await u.findTask(r,t);await u.ingestTask(n,r);}});});var xe,$e=f(()=>{k();j();x();xe=p({description:"List tasks, optionally filtered by project, status, assignee, or search query",options:z$1.object({project:z$1.string().optional().describe("Project slug (searches all projects if not provided)"),statuses:z$1.array(z$1.string()).default([]).describe("Filter by statuses (multiple allowed)"),assignee:z$1.string().optional().describe("Filter by assignee"),all:z$1.boolean().default(false).describe("Include all tasks (including done/blocked)"),search:z$1.string().optional().describe("Search query (matches task slug, case-insensitive, multi-part AND)")}),handler:async({project:t,statuses:e,assignee:o,all:r,search:n})=>{if(e&&e.length>0){for(let l of e)if(!et.includes(l))throw new Error(`Invalid status: ${l}. Valid values: ${et.join(", ")}`)}let i;e&&e.length>0?i=e:r?i=et:i=xt;let c=t?[t]:await u.listProjects(),d={};await s.promiseEach(c,async l=>{let g=await u.listTasks(l),b=[];await s.promiseEach(g,async O=>{let C=await u.getTask(l,O);C&&(i.length>0&&!i.includes(C.status||"")||o&&C.assignee!==o||s.matchesSearch(O,n)&&b.push({slug:O,description:C.description}));}),b.length>0&&(d[l]=b.sort((O,C)=>O.slug.localeCompare(C.slug)));});let m=Object.keys(d).sort();if(m.length===0)return console.log("No tasks found");for(let l of m){console.log(`${l}:`);for(let g of d[l])console.log(`- ${g.slug}: ${g.description||""}`);}}});});var ve,Ae=f(()=>{k();x();ve=p({description:"Output task directory path (for cd)",options:z$1.object({project:z$1.string().optional().describe("Project slug (searches all projects if not provided)")}),args:z$1.object({task:z$1.string().describe("Task slug")}),handler:async({project:t,task:e})=>{let{project:o,task:r}=await u.findTask(e,t),n=u.getTaskDir(o,r);console.log(n);}});});var ct,wt=f(()=>{P();E();k();z();j();x();F();ct=p({description:"Update task properties: name, description, status, assignee, or replace body",options:z$1.object({name:z$1.string().optional().describe("New name"),description:z$1.string().optional().describe("New description"),status:z$1.string().optional().describe("New status"),assignee:z$1.string().optional().describe("New assignee"),project:z$1.string().optional().describe("Project slug (searches all projects if not provided)"),body:z$1.string().optional().describe("Replace entire body/content (markdown)")}),args:z$1.object({task:z$1.string().optional().describe("Task slug (default: from $PWD)")}),handler:async({project:t,task:e,name:o,description:r,status:n,assignee:i,body:c})=>{let d=h.getCurrentContext(),m=e??d.task;if(!m)throw new Error("No task specified (use --task or cd into task dir)");let{project:l}=await u.findTask(m,t),g=u.getTaskDir(l,m),b=u.getProjectDir(l),O=s.join(g,a.files.MAIN);if(!await v.runHooksForContext(b,g,"pre-update",{action:"pre-update",entityType:"task",project:l,task:m}))throw new Error("Pre-update hook failed, aborting update");let D={};if(o&&(D.name=o),r&&(D.description=r),n&&(D.status=n),i&&(D.assignee=i),Object.keys(D).length===0&&!c){console.log("No updates provided");return}if(Object.keys(D).length>0){await u.updateTask(l,m,D);let Ne=Object.entries(D).map(([Ie,Fe])=>`${Ie}=${Fe}`).join(", ");await w.append(g,"task",m,"updated",Ne);}c!==void 0&&(await u.updateBody(O,c),await w.append(g,"task",m,"updated",`body replaced (${c.length} chars)`)),D.status&&await w.append(b,"task",m,"updated",`status to ${D.status}`),await v.runHooksForContext(u.getProjectDir(l),g,"post-update",{action:"post-update",entityType:"task",project:l,task:m}),console.log(`Task ${m} updated`);}});});var Ee,De=f(()=>{E();k();z();x();wt();Ee=p({description:"Start a task: set status to in-progress, optionally print context",options:z$1.object({project:z$1.string().optional().describe("Project slug (searches all projects if not provided)"),ingest:z$1.boolean().default(true).describe("Also output context for this task")}),args:z$1.object({task:z$1.string().optional().describe("Task slug (default: from $PWD)")}),handler:async({project:t,task:e,ingest:o})=>{let r=h.getCurrentContext(),n=e??r.task;if(!n)throw new Error("No task specified (use --task or cd into task dir)");let{project:i}=await u.findTask(n,t),c=u.getProjectDir(i),d=u.getTaskDir(i,n);if(!await v.runHooksForContext(c,d,"pre-start",{action:"pre-start",entityType:"task",project:i,task:n}))throw new Error("Pre-start hook failed, aborting task start");(await u.getTask(i,n))?.status!=="in-progress"&&await ct.handler({project:i,task:n,status:"in-progress"}),o&&await u.ingestTask(i,n),await v.runHooksForContext(c,d,"post-start",{action:"post-start",entityType:"task",project:i,task:n});}});});var Oe,Ce=f(()=>{k();j();Oe=p({description:"Read one or more files into the console",options:z$1.object({}),args:z$1.object({paths:z$1.array(z$1.string()).describe("File paths to read")}),handler:async({paths:t})=>{await s.logFiles(...t);}});});var ft={};_e(ft,{default:()=>U});var yo,U,ot=f(()=>{At();Dt();Ct();Nt();Ft();Ht();Gt();Bt();Wt();Yt();Xt();ee();re();ne();ae();pe();me();fe();ke();ye();Te();be();Se();$e();Ae();De();wt();Ce();yo={agent:{create:vt,current:Et,list:Ot,path:Rt,start:It},help:{api:Ut,hooks:Kt,quickstart:zt,skill:Zt,usage:qt},hook:{create:Qt,run:te},log:{append:oe,read:se},project:{create:ie,current:ce,list:de,path:ue,update:ge},skill:{read:he},task:{create:je,current:we,ingest:Pe,list:xe,path:ve,start:Ee,update:ct},util:{read:Oe}},U=yo;});B();ot();j();process.on("unhandledRejection",t=>{console.error("Unhandled Rejection:",t),process.exit(1);});process.on("uncaughtException",t=>{console.error("Unhandled Exception:",t),process.exit(1);});async function Re(t){let[e,o,...r]=t;(e==="--version"||e==="-v")&&(console.log(S.version),process.exit(0));try{(!e||!s.isKeyOf(e,U))&&(await U.help.usage.handler({}),process.exit(1));let n=U[e];(!o||!s.isKeyOf(o,n))&&(await U.help.usage.handler({name:e}),process.exit(1));let i=n[o];i||(console.error(`Error: unknown command '${e} ${o}'`),console.log("Run with --help for more information"),process.exit(1)),await i.handler(i.parser.name(`${e} ${o}`).parse(r));}catch(n){console.error("Error:",s.errorMessage(n)),process.exit(1);}}var Si=Re,xi=U;s.isMain()&&Re(process.argv.slice(2));export{xi as commands,Si as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-projects",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "description": "Useful CLI for AI agents to create and manage projects, tasks, skills and agents",
6
6
  "main": "dist/index.js",
@@ -17,11 +17,10 @@
17
17
  "build": "npm run build:map && npm run build:code",
18
18
  "build:map": "tsx bin/map-commands.ts",
19
19
  "build:code": "tsup --no-dts",
20
- "build:all": "npm run build:map && tsup",
20
+ "build:full": "npm run build:map && tsup",
21
21
  "build:check": "tsc --noEmit",
22
22
  "build:watch": "npm run build:code -- --watch",
23
23
  "build:clean": "rimraf dist/*",
24
- "build:incr": "tsup --no-dts",
25
24
  "start": "dist/index.js",
26
25
  "dev": "npm run build:watch -- --onSuccess \"dist/index.js\"",
27
26
  "cli": "npm run build && npm start --",
@@ -29,20 +28,17 @@
29
28
  "vitest": "VITE_CJS_IGNORE_WARNING=true vitest",
30
29
  "test": "npm run test:types && npm run test:unit",
31
30
  "test:unit": "npm run vitest -- run",
32
- "test:types:test": "tsc -p tsconfig.test.json --incremental --tsBuildInfoFile dist/test.tsbuildinfo",
33
- "test:types:bin": "tsc -p bin/tsconfig.json --incremental --tsBuildInfoFile dist/bin.tsbuildinfo",
31
+ "test:types:test": "tsc -p tsconfig.test.json --incremental --tsBuildInfoFile test.tsbuildinfo",
32
+ "test:types:bin": "tsc -p bin/tsconfig.json --incremental --tsBuildInfoFile bin.tsbuildinfo",
34
33
  "test:types": "npm run test:types:test && npm run test:types:bin",
35
34
  "test:full": "npm run lint:full && npm run test",
36
35
  "eslint": "eslint --cache",
37
36
  "lint": "npm run eslint -- '{src,bin}/**/*.ts'",
38
37
  "lint:fix": "npm run lint -- --fix",
39
- "lint:full": "npm run build:incr && npm run test:types:bin && npm run lint:fix",
38
+ "lint:full": "npm run build:check && npm run test:types:bin && npm run lint:fix",
40
39
  "lint:full:silent": "npm run -s lint:full && echo LINT OK",
41
- "map": "npm run build:map",
42
- "link": "npm link",
43
- "unlink": "npm unlink",
44
40
  "prepare": "[ \"$CI\" != \"true\" ] && [ -d node_modules/husky ] && husky || true",
45
- "prepack": "npm run test:full && npm run build:all",
41
+ "prepack": "npm run test:full && npm run build:full",
46
42
  "publish:dry": "npm pack --dry-run",
47
43
  "version:patch": "npm version patch",
48
44
  "version:minor": "npm version minor",