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.
- package/README.md +5 -4
- package/dist/index.js +60 -51
- 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
|
|
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
|
-
├──
|
|
97
|
-
├──
|
|
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
|
|
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}`}
|
|
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
|
|
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=>
|
|
13
|
-
${
|
|
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
|
-
`);},
|
|
18
|
-
Expected at: ${o}`);console.log(e);}});});var Ft
|
|
19
|
-
Expected at: ${o}`);let i=[r,o];await s.logFiles(...i);}});});var
|
|
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},
|
|
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
|
-
`,
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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 $(${
|
|
219
|
+
cd $(${$} task path first-task)
|
|
220
220
|
|
|
221
221
|
# 4. Start working (implicit from PWD)
|
|
222
|
-
${
|
|
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: ${
|
|
227
|
-
# Save
|
|
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
|
-
${
|
|
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 $(${
|
|
238
|
+
cd $(${$} task path task-slug)
|
|
238
239
|
|
|
239
240
|
# All commands work implicitly from PWD:
|
|
240
|
-
${
|
|
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
|
-
${
|
|
248
|
-
${
|
|
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
|
-
${
|
|
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 ${
|
|
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 ${
|
|
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
|
-
|
|
274
|
-
|
|
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/${
|
|
293
|
-
\u251C\u2500\u2500 ${
|
|
294
|
-
\u251C\u2500\u2500 ${
|
|
295
|
-
\u251C\u2500\u2500 ${
|
|
296
|
-
\u251C\u2500\u2500 ${
|
|
297
|
-
\u251C\u2500\u2500 ${co}/ #
|
|
298
|
-
\u251C\u2500\u2500 ${
|
|
299
|
-
\u2514\u2500\u2500 ${
|
|
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 ${
|
|
302
|
-
\u251C\u2500\u2500 ${
|
|
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: ${
|
|
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 \`${
|
|
330
|
-
`,
|
|
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(
|
|
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(
|
|
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
|
|
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.")}},
|
|
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
|
|
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=
|
|
393
|
-
Expected at: ${o}`);await s.logFiles(o);}});});var
|
|
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.
|
|
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:
|
|
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
|
|
33
|
-
"test:types:bin": "tsc -p bin/tsconfig.json --incremental --tsBuildInfoFile
|
|
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:
|
|
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:
|
|
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",
|