ai-projects 1.3.0 → 1.4.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/dist/index.d.ts +13 -4
- package/dist/index.js +74 -71
- package/package.json +7 -5
package/dist/index.d.ts
CHANGED
|
@@ -127,14 +127,17 @@ declare const commands: {
|
|
|
127
127
|
description: zod.ZodString;
|
|
128
128
|
status: zod.ZodDefault<zod.ZodString>;
|
|
129
129
|
assignee: zod.ZodOptional<zod.ZodString>;
|
|
130
|
+
summary: zod.ZodOptional<zod.ZodString>;
|
|
130
131
|
}, "strip", zod.ZodTypeAny, {
|
|
131
132
|
status: string;
|
|
132
133
|
description: string;
|
|
133
134
|
assignee?: string | undefined;
|
|
135
|
+
summary?: string | undefined;
|
|
134
136
|
}, {
|
|
135
137
|
description: string;
|
|
136
138
|
status?: string | undefined;
|
|
137
139
|
assignee?: string | undefined;
|
|
140
|
+
summary?: string | undefined;
|
|
138
141
|
}>, zod.ZodObject<{
|
|
139
142
|
name: zod.ZodString;
|
|
140
143
|
}, "strip", zod.ZodTypeAny, {
|
|
@@ -205,23 +208,26 @@ declare const commands: {
|
|
|
205
208
|
description: zod.ZodOptional<zod.ZodString>;
|
|
206
209
|
assignee: zod.ZodOptional<zod.ZodString>;
|
|
207
210
|
status: zod.ZodDefault<zod.ZodString>;
|
|
211
|
+
summary: zod.ZodOptional<zod.ZodString>;
|
|
208
212
|
}, "strip", zod.ZodTypeAny, {
|
|
209
213
|
status: string;
|
|
210
214
|
description?: string | undefined;
|
|
211
215
|
assignee?: string | undefined;
|
|
216
|
+
summary?: string | undefined;
|
|
212
217
|
}, {
|
|
213
218
|
status?: string | undefined;
|
|
214
219
|
description?: string | undefined;
|
|
215
220
|
assignee?: string | undefined;
|
|
221
|
+
summary?: string | undefined;
|
|
216
222
|
}>, zod.ZodObject<{
|
|
217
223
|
project: zod.ZodString;
|
|
218
224
|
name: zod.ZodString;
|
|
219
225
|
}, "strip", zod.ZodTypeAny, {
|
|
220
|
-
name: string;
|
|
221
226
|
project: string;
|
|
222
|
-
}, {
|
|
223
227
|
name: string;
|
|
228
|
+
}, {
|
|
224
229
|
project: string;
|
|
230
|
+
name: string;
|
|
225
231
|
}>>;
|
|
226
232
|
readonly current: CommandDef<zod.ZodObject<{}, "strip", zod.ZodTypeAny, {}, {}>, zod.ZodObject<any, zod.UnknownKeysParam, zod.ZodTypeAny, {
|
|
227
233
|
[x: string]: any;
|
|
@@ -246,16 +252,19 @@ declare const commands: {
|
|
|
246
252
|
statuses: zod.ZodDefault<zod.ZodArray<zod.ZodString, "many">>;
|
|
247
253
|
assignee: zod.ZodOptional<zod.ZodString>;
|
|
248
254
|
all: zod.ZodDefault<zod.ZodBoolean>;
|
|
255
|
+
search: zod.ZodOptional<zod.ZodString>;
|
|
249
256
|
}, "strip", zod.ZodTypeAny, {
|
|
250
257
|
statuses: string[];
|
|
251
258
|
all: boolean;
|
|
252
259
|
project?: string | undefined;
|
|
253
260
|
assignee?: string | undefined;
|
|
261
|
+
search?: string | undefined;
|
|
254
262
|
}, {
|
|
255
263
|
project?: string | undefined;
|
|
256
264
|
assignee?: string | undefined;
|
|
257
265
|
statuses?: string[] | undefined;
|
|
258
266
|
all?: boolean | undefined;
|
|
267
|
+
search?: string | undefined;
|
|
259
268
|
}>, zod.ZodObject<any, zod.UnknownKeysParam, zod.ZodTypeAny, {
|
|
260
269
|
[x: string]: any;
|
|
261
270
|
}, {
|
|
@@ -298,17 +307,17 @@ declare const commands: {
|
|
|
298
307
|
project: zod.ZodOptional<zod.ZodString>;
|
|
299
308
|
summary: zod.ZodOptional<zod.ZodString>;
|
|
300
309
|
}, "strip", zod.ZodTypeAny, {
|
|
310
|
+
project?: string | undefined;
|
|
301
311
|
status?: string | undefined;
|
|
302
312
|
description?: string | undefined;
|
|
303
313
|
name?: string | undefined;
|
|
304
|
-
project?: string | undefined;
|
|
305
314
|
assignee?: string | undefined;
|
|
306
315
|
summary?: string | undefined;
|
|
307
316
|
}, {
|
|
317
|
+
project?: string | undefined;
|
|
308
318
|
status?: string | undefined;
|
|
309
319
|
description?: string | undefined;
|
|
310
320
|
name?: string | undefined;
|
|
311
|
-
project?: string | undefined;
|
|
312
321
|
assignee?: string | undefined;
|
|
313
322
|
summary?: string | undefined;
|
|
314
323
|
}>, zod.ZodObject<{
|
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 {spawn,exec}from'child_process';import
|
|
2
|
+
import {z as z$1}from'zod';import {parser}from'zod-opts';import {spawn,exec}from'child_process';import J from'fs/promises';import T from'lodash';import B from'moment';import M from'path';import {fileURLToPath}from'url';import {inspect}from'util';import Ze from'dotenv';import Ve from'fs';import St from'yaml';var Fe=Object.defineProperty;var f=(t,e)=>()=>(t&&(e=t(t=0)),e);var He=(t,e)=>{for(var o in e)Fe(t,o,{get:e[o],enumerable:true});};var S,W=f(()=>{S={name:"ai-projects",version:"1.4.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: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"},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,a={};for(let[u,l]of Object.entries(i))a[u]={type:l};let d=parser().options(a);if(r){let u=r.shape,l=Object.entries(u).map(([g,P])=>({name:g,type:P}));l.length>0&&(d=d.args(l));}return e&&(d=d.description(e)),{description:e,options:o,args:r,parser:d,handler:n,cli:u=>n(d.parse(u))}}var k=f(()=>{});var w,s,b=f(()=>{w={REPO:fileURLToPath(import.meta.url.replace(/\/(dist|src)\/.*/,"/")),join:(...t)=>M.join(...t),onRepo:(...t)=>w.join(w.REPO,...t),async ensureDir(t){await J.mkdir(t,{recursive:true});},async write(t,e){await w.ensureDir(M.dirname(t)),await J.writeFile(t,e,"utf8");},async read(t){return J.readFile(t,"utf8")},async readRepo(...t){return w.read(w.onRepo(...t))},async readMany(...t){return (await Promise.all(t.map(async o=>{if(await w.fileExists(o)){let r=await w.read(o);return `# ${o}
|
|
3
3
|
|
|
4
|
-
${r}`}return ""}))).filter(Boolean)},async logFiles(...t){let e=await
|
|
4
|
+
${r}`}return ""}))).filter(Boolean)},async logFiles(...t){let e=await w.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 w.entriesOf(o))r.push(`${n} {${Object.keys(i).join("|")}}`);return r},async fileExists(t){try{return await J.access(t),!0}catch{return false}},async listDir(t){try{return await J.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 a=r?.code;o({stdout:n??"",stderr:i??"",code:r?typeof a=="number"?a:1:0});});})},noop:()=>{},omitByDeep:(t,e)=>T.isArray(t)?t.map(o=>w.omitByDeep(o,e)):T.isPlainObject(t)?T.mapValues(T.omitBy(t,e),o=>w.omitByDeep(o,e)):t,omitNilsDeep:t=>w.omitByDeep(t,T.isNil),omitUndefinedsDeep:t=>w.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)?w.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,w.memoizeKey),toMS:t=>B.duration(t).asMilliseconds(),toSecs:t=>B.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=>w.oneLine(w.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())=>
|
|
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())=>B.duration(B(e).diff(t||0)),iso:t=>B.utc(t).toISOString(),isoDate:t=>w.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=w;});var c,$=f(()=>{c={dirs:{PROJECTS:"projects",SKILLS:"skills",AGENTS:"agents",TASKS:"tasks",INPUTS:"inputs",OUTPUTS:"outputs",HOOKS:"hooks",SCRIPTS:"scripts"},files:{MAIN:"main.md",STATUS:"status.md",LOG:"audit.log",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 Ye,bt,U,Qe,y,I=f(()=>{$();Ye=()=>{let t=fileURLToPath(import.meta.url),e=M.dirname(t),o=10;for(let r=0;r<o;r++)try{if(Ve.existsSync(M.join(e,"package.json")))return e;let n=M.dirname(e);if(n===e)break;e=n;}catch{break}return process.cwd()},bt=Ye(),U=process.env.AIP_HOME;U||(Ze.config({path:M.join(bt,".env"),quiet:true}),U=process.env.AIP_HOME);if(!U){U=process.cwd();let t=U.split(M.sep);for(let e of [c.dirs.PROJECTS,c.dirs.SKILLS,c.dirs.AGENTS]){let o=t.indexOf(e);if(o!==-1){U=t.slice(0,o).join(M.sep);break}}}Qe={ROOT:bt,AIP_HOME:U},y=Qe;});var xt,Z,dt,V,Et=f(()=>{b();xt=t=>{let e=t.trim();if(!e.startsWith("---"))return null;let o=e.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);return o?{frontmatter:St.parse(o[1]),content:o[2].trim()}:null},Z=async(t,e,o="")=>{let n=`---
|
|
13
|
+
${St.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
|
|
19
|
-
Expected at: ${o}`);let i=[r,o];await s.logFiles(...i);}});});var
|
|
20
|
-
`,r.length>0||n.length>0){let
|
|
17
|
+
`);},dt=async(t,e)=>{let o=await s.read(t),r=xt(o);if(!r)throw new Error(`No frontmatter found in ${t}`);let n={...r.frontmatter,...e};return await Z(t,n,r.content),n},V=async t=>{let e=await s.read(t),o=xt(e);if(!o)throw new Error(`No frontmatter found in ${t}`);return o.frontmatter};});var K,X,At,$t,Xe,to,m,x=f(()=>{$();I();Et();b();K=(a=>(a.BACKLOG="backlog",a.IN_PROGRESS="in-progress",a.ONGOING="ongoing",a.DONE="done",a.BLOCKED="blocked",a.TO_DO="to-do",a))(K||{}),X=Object.values(K),At=["backlog","in-progress","ongoing"],$t=[c.dirs.HOOKS,c.dirs.INPUTS,c.dirs.OUTPUTS,c.dirs.SCRIPTS],Xe=[c.dirs.TASKS,...$t],to={getProjectDir(t){return s.join(y.AIP_HOME,c.dirs.PROJECTS,t)},getTaskDir(t,e){return s.join(this.getProjectDir(t),c.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(y.AIP_HOME,c.dirs.AGENTS,t)},async listProjects(){let t=s.join(y.AIP_HOME,c.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!==c.dirs.AGENTS?r:null})).filter(r=>r!==null).sort()},async listTasks(t){let e=s.join(this.getProjectDir(t),c.dirs.TASKS);return await s.fileExists(e)?await s.listDir(e):[]},async listAgents(){let t=s.join(y.AIP_HOME,c.dirs.AGENTS);return await s.fileExists(t)?await s.listDir(t):[]},async createProject(t,e){let o=this.getProjectDir(t);return await s.ensureDir(o),await Promise.all(Xe.map(r=>s.ensureDir(s.join(o,r)))),await Z(s.join(o,c.files.MAIN),e),await s.write(s.join(o,c.files.STATUS),""),o},async createTask(t,e,o){let r=this.getTaskDir(t,e);return await s.ensureDir(r),await Promise.all($t.map(n=>s.ensureDir(s.join(r,n)))),await Z(s.join(r,c.files.MAIN),o),await s.write(s.join(r,c.files.STATUS),""),r},async createAgent(t,e){let o=this.getAgentDir(t);return await s.ensureDir(o),await Z(s.join(o,c.files.MAIN),e),o},async getProject(t){let e=s.join(this.getProjectDir(t),c.files.MAIN);return await s.fileExists(e)?await V(e):null},async getTask(t,e){let o=s.join(this.getTaskDir(t,e),c.files.MAIN);return await s.fileExists(o)?await V(o):null},async getAgent(t){let e=s.join(this.getAgentDir(t),c.files.MAIN);return await s.fileExists(e)?await V(e):null},async updateProject(t,e){let o=s.join(this.getProjectDir(t),c.files.MAIN);return await dt(o,e)},async updateTask(t,e,o){let r=s.join(this.getTaskDir(t,e),c.files.MAIN);if((await V(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,c.files.MAIN),s.join(o,c.files.MAIN),s.join(o,c.files.STATUS)];await s.logFiles(...n);},async ingestProject(t){let e=this.getProjectDir(t),o=await this.listTasks(t),r=[s.join(e,c.files.MAIN),s.join(e,c.files.STATUS),...o.map(n=>s.join(this.getTaskDir(t,n),c.files.MAIN))];await s.logFiles(...r);},async ingestFiles(t){await s.logFiles(...t);}},m=to;});var vt,Ot=f(()=>{k();b();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 m.createAgent(r,{name:t,description:e,status:o,created:new Date().toISOString()}),console.log(`Agent created: ${r}`),console.log(` Path: ${m.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 It,Nt=f(()=>{k();x();It=p({options:z$1.object({}),handler:async()=>{let t=await m.listAgents(),e=[];for(let o of t){let r=await m.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 Rt,Ut=f(()=>{$();k();I();b();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(y.AIP_HOME,c.dirs.AGENTS,t),o=s.join(e,c.files.MAIN);if(!await s.fileExists(o))throw new Error(`Agent not found: ${t}
|
|
18
|
+
Expected at: ${o}`);console.log(e);}});});var _t,Lt=f(()=>{$();k();I();b();_t=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(y.AIP_HOME,c.dirs.AGENTS,t),o=s.join(e,c.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 Ft,Ht,so,Mt,Kt=f(()=>{k();Ft=t=>{let e=t.shape,o=[];for(let[r,n]of Object.entries(e)){let i=n._def,a="unknown",d,u;i.typeName==="ZodString"?a="string":i.typeName==="ZodNumber"?a="number":i.typeName==="ZodBoolean"?a="boolean":i.typeName==="ZodEnum"?a=`enum: ${i.values.join(" | ")}`:i.typeName==="ZodOptional"?a=Ht(i.innerType):i.typeName==="ZodDefault"&&(a=Ht(i.innerType),u=i.defaultValue()),i.description&&(d=i.description);let l=i.typeName!=="ZodOptional"&&i.typeName!=="ZodDefault";o.push({name:r,type:a,required:l,description:d,defaultValue:u});}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"},so=(t,e,o)=>{let r=Ft(o.options),n=o.args?Ft(o.args):[],i=o.description,a=`\`${t} ${e}\``;if(i&&(a+=`: ${i}`),a+=`
|
|
20
|
+
`,r.length>0||n.length>0){let u=[];for(let l of n){let g=l.required?`<${l.name}>`:`[${l.name}]`,P=l.description||"";l.defaultValue!==void 0&&(P+=P?` (default: ${l.defaultValue})`:`(default: ${l.defaultValue})`),u.push(`\xB7 ${g}${P?": "+P:""}`);}for(let l of r){let g=l.description||"";l.defaultValue!==void 0&&(g+=g?` (default: ${l.defaultValue})`:`(default: ${l.defaultValue})`),u.push(`\xB7 --${l.name}${g?": "+g:""}`);}a+=u.join(`
|
|
21
21
|
`)+`
|
|
22
|
-
`;}return
|
|
23
|
-
`,
|
|
22
|
+
`;}return a+=`
|
|
23
|
+
`,a},Mt=p({description:"Print generated CLI reference from command schemas",options:z$1.object({}),handler:async()=>{let{default:t}=await Promise.resolve().then(()=>(tt(),gt)),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 a of i){let d=n[a];e+=so(r,a,d);}}console.log(e);}});});var io,Gt,zt=f(()=>{k();io=`# AIP Hooks
|
|
24
24
|
|
|
25
25
|
Automated scripts that run before/after project/task actions.
|
|
26
26
|
|
|
@@ -200,26 +200,26 @@ 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
|
+
`,Gt=p({description:"Hook types, env vars, and patterns",options:z$1.object({}),handler:async()=>{console.log(io);}});});var D,co,Wt,Jt=f(()=>{W();k();D=Object.keys(S.bin)[0],co=`# Quick Start
|
|
204
204
|
|
|
205
205
|
## New Task/Project
|
|
206
206
|
|
|
207
207
|
\`\`\`bash
|
|
208
208
|
# 1. Create project (if needed)
|
|
209
|
-
${
|
|
209
|
+
${D} project create "my-project" --description "What I'm building"
|
|
210
210
|
|
|
211
211
|
# 2. Create task
|
|
212
|
-
${
|
|
212
|
+
${D} task create my-project "first-task" --description "Start here"
|
|
213
213
|
|
|
214
214
|
# 3. Navigate to task
|
|
215
|
-
cd $(${
|
|
215
|
+
cd $(${D} task path first-task)
|
|
216
216
|
|
|
217
217
|
# 4. Start working (implicit from PWD)
|
|
218
|
-
${
|
|
218
|
+
${D} task start
|
|
219
219
|
# \u2192 sets status to in-progress
|
|
220
220
|
|
|
221
221
|
# 5. Work session
|
|
222
|
-
# Log:
|
|
222
|
+
# Log progress: ${D} task update --summary "API integration complete"
|
|
223
223
|
# Save: outputs/ to deliver, inputs/ for data
|
|
224
224
|
\`\`\`
|
|
225
225
|
|
|
@@ -227,20 +227,20 @@ ${C} task start
|
|
|
227
227
|
|
|
228
228
|
\`\`\`bash
|
|
229
229
|
# Find your tasks (searches all projects)
|
|
230
|
-
${
|
|
230
|
+
${D} task list
|
|
231
231
|
|
|
232
232
|
# Navigate to task
|
|
233
|
-
cd $(${
|
|
233
|
+
cd $(${D} task path task-slug)
|
|
234
234
|
|
|
235
235
|
# All commands work implicitly from PWD:
|
|
236
|
-
${
|
|
236
|
+
${D} task start # Start working (sets status to in-progress)
|
|
237
237
|
\`\`\`
|
|
238
238
|
|
|
239
239
|
## Completing
|
|
240
240
|
|
|
241
241
|
\`\`\`bash
|
|
242
242
|
# Mark done (implicit from PWD)
|
|
243
|
-
${
|
|
243
|
+
${D} task update --status done --summary "What I built"
|
|
244
244
|
\`\`\`
|
|
245
245
|
|
|
246
246
|
**Note:** All task commands accept an optional task slug, but use PWD if not provided. Navigate with \`cd\` first for smoother workflow.
|
|
@@ -258,7 +258,7 @@ $AIP_HOME/projects/slug/
|
|
|
258
258
|
|
|
259
259
|
**Status format:** \`[YYYY-MM-DD HH:MM] message\`
|
|
260
260
|
**Example:** \`[2026-03-30 14:30] API integration complete\`
|
|
261
|
-
`,
|
|
261
|
+
`,Wt=p({description:"Quick start guide for new users",options:z$1.object({}),handler:async()=>{console.log(co);}});});var po,lo,Zt,Vt,qt,Yt,Qt,et,ot,kt,Xt,te=f(()=>{W();$();k();x();po=Object.values(K).join("|"),{PROJECTS:lo,TASKS:Zt,HOOKS:Vt,INPUTS:qt,OUTPUTS:Yt,SCRIPTS:Qt}=c.dirs,{MAIN:et,STATUS:ot}=c.files,kt=`# AIP - Project Management Skill
|
|
262
262
|
|
|
263
263
|
Automated project and task creation with proper structure, front-matter, and file organization.
|
|
264
264
|
|
|
@@ -271,21 +271,21 @@ Streamline creation of new projects and tasks with the correct directory structu
|
|
|
271
271
|
Every project follows this structure:
|
|
272
272
|
|
|
273
273
|
\`\`\`
|
|
274
|
-
$AIP_HOME/${
|
|
275
|
-
\u251C\u2500\u2500 ${
|
|
276
|
-
\u251C\u2500\u2500 ${
|
|
277
|
-
\u251C\u2500\u2500 ${
|
|
278
|
-
\u251C\u2500\u2500 ${
|
|
279
|
-
\u251C\u2500\u2500 ${
|
|
280
|
-
\u251C\u2500\u2500 ${
|
|
281
|
-
\u2514\u2500\u2500 ${
|
|
274
|
+
$AIP_HOME/${lo}/{project-slug}/
|
|
275
|
+
\u251C\u2500\u2500 ${et} # Primary context: goals, objectives, scope, data
|
|
276
|
+
\u251C\u2500\u2500 ${ot} # Chronological log of status updates
|
|
277
|
+
\u251C\u2500\u2500 ${Vt}/ # pre|post-{create,start,update,complete}.*
|
|
278
|
+
\u251C\u2500\u2500 ${Yt}/ # Deliverables (reports, code, final files)
|
|
279
|
+
\u251C\u2500\u2500 ${qt}/ # External data (API responses, downloads)
|
|
280
|
+
\u251C\u2500\u2500 ${Qt}/ # Automation (bash/python/js)
|
|
281
|
+
\u2514\u2500\u2500 ${Zt}/
|
|
282
282
|
\u2514\u2500\u2500 {task-slug}/
|
|
283
|
-
\u251C\u2500\u2500 ${
|
|
284
|
-
\u251C\u2500\u2500 ${
|
|
285
|
-
\u251C\u2500\u2500 ${
|
|
286
|
-
\u251C\u2500\u2500 ${
|
|
287
|
-
\u251C\u2500\u2500 ${
|
|
288
|
-
\u2514\u2500\u2500 ${
|
|
283
|
+
\u251C\u2500\u2500 ${et} # Task definition with front-matter
|
|
284
|
+
\u251C\u2500\u2500 ${ot} # Single-line updates per work session
|
|
285
|
+
\u251C\u2500\u2500 ${Vt}/ # Task-level hooks
|
|
286
|
+
\u251C\u2500\u2500 ${Yt}/ # Task-specific deliverables
|
|
287
|
+
\u251C\u2500\u2500 ${qt}/ # Task-specific data files
|
|
288
|
+
\u2514\u2500\u2500 ${Qt}/ # Task-specific automation
|
|
289
289
|
\`\`\`
|
|
290
290
|
|
|
291
291
|
## File Purposes
|
|
@@ -304,7 +304,7 @@ $AIP_HOME/${co}/{project-slug}/
|
|
|
304
304
|
name: Clear Title
|
|
305
305
|
description: One-line summary for search
|
|
306
306
|
assignee: agent-name
|
|
307
|
-
status: ${
|
|
307
|
+
status: ${po}
|
|
308
308
|
created: YYYY-MM-DD
|
|
309
309
|
---
|
|
310
310
|
\`\`\`
|
|
@@ -395,12 +395,12 @@ aip task update --summary "Completed implementation"
|
|
|
395
395
|
|
|
396
396
|
\`\`\`
|
|
397
397
|
$AIP_HOME/projects/my-project/
|
|
398
|
-
\u251C\u2500\u2500 ${
|
|
399
|
-
\u251C\u2500\u2500 ${
|
|
400
|
-
\u2514\u2500\u2500 ${
|
|
398
|
+
\u251C\u2500\u2500 ${et} # Goals, context
|
|
399
|
+
\u251C\u2500\u2500 ${ot} # Activity log
|
|
400
|
+
\u2514\u2500\u2500 ${Zt}/
|
|
401
401
|
\u2514\u2500\u2500 task-1/
|
|
402
|
-
\u251C\u2500\u2500 ${
|
|
403
|
-
\u2514\u2500\u2500 ${
|
|
402
|
+
\u251C\u2500\u2500 ${et} # Task definition
|
|
403
|
+
\u2514\u2500\u2500 ${ot} # Updates
|
|
404
404
|
\`\`\`
|
|
405
405
|
|
|
406
406
|
**Status log:**
|
|
@@ -409,23 +409,26 @@ $AIP_HOME/projects/my-project/
|
|
|
409
409
|
[2026-03-17 10:30] Updated: status=in-progress
|
|
410
410
|
[2026-03-17 12:00] Tests passing
|
|
411
411
|
\`\`\`
|
|
412
|
-
`,
|
|
412
|
+
`,Xt=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(`---
|
|
413
413
|
name: ${e}
|
|
414
|
-
description: ${
|
|
415
|
-
---`),console.log(),console.log(
|
|
414
|
+
description: ${S.description}
|
|
415
|
+
---`),console.log(),console.log(kt);return}if(t.mode==="hermes"){console.log(`---
|
|
416
416
|
name: ${e}
|
|
417
|
-
description: ${
|
|
418
|
-
version: ${
|
|
419
|
-
author: ${
|
|
420
|
-
license: ${
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
417
|
+
description: ${S.description}
|
|
418
|
+
version: ${S.version}
|
|
419
|
+
author: ${S.author}
|
|
420
|
+
license: ${S.license}
|
|
421
|
+
metadata:
|
|
422
|
+
hermes:
|
|
423
|
+
tags: [${S.keywords.join(", ")}]
|
|
424
|
+
---`),console.log(),console.log(kt);return}}});});var ee,oe=f(()=>{W();k();b();ee=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(()=>(tt(),gt)),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(`
|
|
425
|
+
`)),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,mo,j,v=f(()=>{$();I();b();yt=s.join(y.AIP_HOME,c.dirs.PROJECTS),mo={getProjectFromPwd(t=process.cwd()){let e=M.relative(yt,t);if(!e||e.startsWith(".."))return null;let o=e.split(M.sep).filter(Boolean);return o.length===0?null:o[0]},getTaskFromPwd(t=process.cwd()){let e=M.relative(yt,t);if(!e||e.startsWith(".."))return null;let o=e.split(M.sep).filter(Boolean);return o.length<3||o[1]!==c.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(`
|
|
426
|
+
`)},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.join(y.AIP_HOME,c.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.join(y.AIP_HOME,c.dirs.PROJECTS,o.project);return {targetDir:s.join(r,c.dirs.TASKS,o.task),projectDir:r,entityType:"task"}}if(o.task&&o.project){let r=s.join(y.AIP_HOME,c.dirs.PROJECTS,o.project);return {targetDir:s.join(r,c.dirs.TASKS,o.task),projectDir:r,entityType:"task"}}if(o.project){let r=s.join(y.AIP_HOME,c.dirs.PROJECTS,o.project);return {targetDir:r,projectDir:r,entityType:"project"}}throw new Error("Not in a project or task directory. Specify --target flag.")}},j=mo;});var re,se=f(()=>{k();b();v();$();re=p({options:z$1.object({lang:z$1.enum(c.languages).default("ts").describe("Language"),target:z$1.enum(c.targets).default("project").describe("Target level")}),args:z$1.object({type:z$1.enum(c.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:a}=j.getTargetDir(o),d=s.join(i,c.dirs.HOOKS);await s.ensureDir(d);let u=s.join(d,`${t}${n}`);if(await s.fileExists(u))throw new Error(`Hook already exists: ${u}`);let g;if(r==="ts")g=`#!/usr/bin/env tsx
|
|
427
|
+
/** ${t} hook for ${a} */
|
|
425
428
|
|
|
426
429
|
import { env } from 'node:process'
|
|
427
430
|
|
|
428
|
-
console.log('Running ${t} hook for ${
|
|
431
|
+
console.log('Running ${t} hook for ${a}')
|
|
429
432
|
console.log('PROJECT_SLUG:', env.PROJECT_SLUG)
|
|
430
433
|
if (env.TASK_SLUG) {
|
|
431
434
|
console.log('TASK_SLUG:', env.TASK_SLUG)
|
|
@@ -433,10 +436,10 @@ if (env.TASK_SLUG) {
|
|
|
433
436
|
|
|
434
437
|
// Exit with non-zero to prevent action (for pre-hooks)
|
|
435
438
|
// process.exit(1)
|
|
436
|
-
`;else if(r==="js")
|
|
437
|
-
/** ${t} hook for ${
|
|
439
|
+
`;else if(r==="js")g=`#!/usr/bin/env node
|
|
440
|
+
/** ${t} hook for ${a} */
|
|
438
441
|
|
|
439
|
-
console.log('Running ${t} hook for ${
|
|
442
|
+
console.log('Running ${t} hook for ${a}')
|
|
440
443
|
console.log('PROJECT_SLUG:', process.env.PROJECT_SLUG)
|
|
441
444
|
if (process.env.TASK_SLUG) {
|
|
442
445
|
console.log('TASK_SLUG:', process.env.TASK_SLUG)
|
|
@@ -444,10 +447,10 @@ if (process.env.TASK_SLUG) {
|
|
|
444
447
|
|
|
445
448
|
// Exit with non-zero to prevent action (for pre-hooks)
|
|
446
449
|
// process.exit(1)
|
|
447
|
-
`;else if(r==="sh")
|
|
448
|
-
# ${t} hook for ${
|
|
450
|
+
`;else if(r==="sh")g=`#!/bin/bash
|
|
451
|
+
# ${t} hook for ${a}
|
|
449
452
|
|
|
450
|
-
echo "Running ${t} hook for ${
|
|
453
|
+
echo "Running ${t} hook for ${a}"
|
|
451
454
|
echo "PROJECT_SLUG: $PROJECT_SLUG"
|
|
452
455
|
if [ -n "$TASK_SLUG" ]; then
|
|
453
456
|
echo "TASK_SLUG: $TASK_SLUG"
|
|
@@ -455,25 +458,25 @@ fi
|
|
|
455
458
|
|
|
456
459
|
# Exit with non-zero to prevent action (for pre-hooks)
|
|
457
460
|
# exit 1
|
|
458
|
-
`;else if(r==="py")
|
|
459
|
-
"""${t} hook for ${
|
|
461
|
+
`;else if(r==="py")g=`#!/usr/bin/env python3
|
|
462
|
+
"""${t} hook for ${a}"""
|
|
460
463
|
|
|
461
464
|
import os
|
|
462
465
|
|
|
463
|
-
print(f"Running ${t} hook for ${
|
|
466
|
+
print(f"Running ${t} hook for ${a}")
|
|
464
467
|
print(f"PROJECT_SLUG: {os.environ.get('PROJECT_SLUG')}")
|
|
465
468
|
if os.environ.get('TASK_SLUG'):
|
|
466
469
|
print(f"TASK_SLUG: {os.environ.get('TASK_SLUG')}")
|
|
467
470
|
|
|
468
471
|
# Exit with non-zero to prevent action (for pre-hooks)
|
|
469
472
|
# sys.exit(1)
|
|
470
|
-
`;else throw new Error(`Unsupported language: ${r}`);await s.write(
|
|
471
|
-
`},async appendStatus(t,e,o){let r=s.join(t,
|
|
472
|
-
Expected at: ${o}`);await s.logFiles(o);}});});var
|
|
473
|
-
${
|
|
474
|
-
`).filter(
|
|
475
|
-
=== Post-Mortem Analysis ===`),console.log(`Project: ${t}`),console.log(`Task: ${e}`),console.log(`Name: ${
|
|
476
|
-
Suggestions:`),
|
|
473
|
+
`;else throw new Error(`Unsupported language: ${r}`);await s.write(u,g);let{chmod:P}=await import('fs/promises');await P(u,493),console.log(`Hook created: ${u}`);}});});var fo,E,G=f(()=>{$();I();b();fo={async findHooks(t,e){let o=s.join(t,c.dirs.HOOKS);if(!await s.fileExists(o))return [];let n=await s.listDir(o),i=`${e}.`;return n.filter(a=>a.startsWith(i)).map(a=>s.join(o,a))},async executeHook(t,e,o){if(!await s.fileExists(t))return true;let n=e.project?s.join(y.AIP_HOME,c.dirs.PROJECTS,e.project):o,i=e.task?s.join(n,c.dirs.TASKS,e.task):"",a={...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 u=spawn(t,[],{stdio:"inherit",env:a,cwd:M.dirname(t),shell:false});u.on("close",(l,g)=>{l===0?d(true):(console.error(`Hook ${M.basename(t)} failed (code=${l??g})`),d(false));}),u.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))}},E=fo;});var ie,ae=f(()=>{k();G();v();$();ie=p({options:z$1.object({target:z$1.enum(c.targets).optional().describe("Target level")}),args:z$1.object({type:z$1.enum(c.hookTypes).describe("Hook type to run")}),handler:async({type:t,target:e})=>{let{targetDir:o,entityType:r}=j.getTargetDir(e),n={action:t,entityType:r,project:j.getProjectFromPwd()||void 0,task:j.getTaskFromPwd()||void 0};await E.runHooks(o,t,n)||(console.error(`Hook ${t} failed`),process.exit(1)),console.log(`Hook ${t} completed successfully`);}});});var go,A,z=f(()=>{$();v();I();b();go={formatLogEntry(t,e){let o=new Date().toISOString().replace("T"," ").replace(/\.\d{3}Z$/,""),r=e?` | ${e}`:"";return `[${o}${r}] ${t}
|
|
474
|
+
`},async appendStatus(t,e,o){let r=s.join(t,c.files.STATUS),n=this.formatLogEntry(e,o);await s.write(r,n);},async appendStatusIfExists(t,e,o){let r=s.join(t,c.files.STATUS);if(!await s.fileExists(r))return false;let i=this.formatLogEntry(e,o);return await s.write(r,i),true},async readStatus(t){let e=s.join(t,c.files.STATUS);return await s.fileExists(e)?await s.read(e):""},getCurrentAgent(){return process.env.CURRENT_AGENT},async logTask(t,e){let o=j.getCurrentContext();if(!o.project||!o.task)throw new Error("Not in a task directory");let r=s.join(y.AIP_HOME,c.dirs.PROJECTS,o.project,c.dirs.TASKS,o.task);await this.appendStatus(r,t,e||this.getCurrentAgent());},async logProject(t,e){let o=j.getProjectFromPwd();if(!o)throw new Error("Not in a project directory");let r=s.join(y.AIP_HOME,c.dirs.PROJECTS,o);await this.appendStatus(r,t,e||this.getCurrentAgent());},async logStatusChange(t,e,o,r){let n=e;if(o&&Object.keys(o).length>0){let i=Object.entries(o).map(([a,d])=>`${a}=${d}`).join(", ");n=`${e}: ${i}`;}await this.appendStatus(t,n,r);}},A=go;});var ce,pe=f(()=>{k();G();b();x();z();ce=p({description:"Create a new project with name, description, optional status and assignee",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"),summary:z$1.string().optional().describe("Override the default summary line to append to status.md")}),args:z$1.object({name:z$1.string().describe("Project name")}),handler:async({name:t,description:e,status:o,assignee:r,summary:n})=>{let i=s.slugify(t),a=m.getProjectDir(i);if(!await E.runHooks(a,"pre-create",{action:"pre-create",entityType:"project"}))throw new Error("Pre-create hook failed, aborting project creation");await m.createProject(i,{name:t,description:e,status:o,assignee:r,created:new Date().toISOString()}),await A.appendStatus(a,n||`Project created: ${t} > status is ${o}`),await E.runHooks(a,"post-create",{action:"post-create",entityType:"project"}),console.log(`Project created: ${i}`),console.log(` Path: ${a}`);}});});var le,me=f(()=>{k();v();le=p({options:z$1.object({}),handler:async()=>{let t=j.getProjectFromPwd();t||(console.error("Not in a project directory"),process.exit(1)),console.log(t);}});});var ue,fe=f(()=>{k();x();ue=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 m.listProjects(),o=[];for(let r of e){let n=await m.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 ge,ke=f(()=>{k();x();ge=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=m.getProjectDir(t);console.log(e);}});});var he,ye=f(()=>{k();x();G();z();v();he=p({description:"Update project properties: name, description, status, assignee, or append summary",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"),summary:z$1.string().optional().describe("Optional summary to append to status.md")}),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,summary:i})=>{let a=t||j.getProjectFromPwd();if(!a)throw new Error("No project specified and not in a project directory");let d=m.getProjectDir(a);if(!await E.runHooks(d,"pre-update",{action:"pre-update",entityType:"project"}))throw new Error("Pre-update hook failed, aborting update");let l={};if(e&&(l.name=e),o&&(l.description=o),r&&(l.status=r),n&&(l.assignee=n),Object.keys(l).length===0){console.log("No updates provided");return}await m.updateProject(a,l);let g=Object.entries(l).map(([P,O])=>`${P}=${O}`).join(", ");await A.appendStatus(d,`Updated: ${g}`),i&&await A.appendStatus(d,i),await E.runHooks(d,"post-update",{action:"post-update",entityType:"project"}),console.log(`Project ${a} updated`);}});});var je,Te=f(()=>{$();k();I();b();je=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(y.AIP_HOME,c.dirs.SKILLS,t),o=s.join(e,c.files.SKILL);if(!await s.fileExists(o))throw new Error(`Skill not found: ${t}
|
|
475
|
+
Expected at: ${o}`);await s.logFiles(o);}});});var we,Pe=f(()=>{k();G();b();x();z();we=p({description:"Create a new task with assignee and initial status",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"),summary:z$1.string().optional().describe("Override the default summary line to append to status.md")}),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,summary:i})=>{let a=s.slugify(e),d=m.getProjectDir(t),u=m.getTaskDir(t,a);if(!await E.runHooksForContext(d,u,"pre-create",{action:"pre-create",entityType:"task",project:t}))throw new Error("Pre-create hook failed, aborting task creation");await m.createTask(t,a,{name:e,description:o,assignee:r,status:n,created:new Date().toISOString()}),await A.appendStatus(u,i||`Created: ${e}`),await A.appendStatus(d,`Task created: ${a} > status is ${n}`),await E.runHooksForContext(d,u,"post-create",{action:"post-create",entityType:"task",project:t}),console.log(`Task created: ${a}`),console.log(` Project: ${t}`),console.log(` Path: ${u}`);}});});var be,Se=f(()=>{k();v();be=p({description:"Get the current task slug from PWD",options:z$1.object({}),handler:async()=>{let t=j.getTaskFromPwd();t||(console.error("Not in a task directory"),process.exit(1)),console.log(t);}});});var xe,Ee=f(()=>{v();k();x();xe=p({description:"Output full task context (main.md, status.md) 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=j.getCurrentContext(),r=e??o.task;if(!r)throw new Error("Need task slug (or cd into task dir)");let{project:n}=await m.findTask(r,t);await m.ingestTask(n,r);}});});var Ae,$e=f(()=>{k();x();Ae=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(!X.includes(l))throw new Error(`Invalid status: ${l}. Valid values: ${X.join(", ")}`)}let i;e&&e.length>0?i=e:r?i=X:i=At;let a=t?[t]:await m.listProjects(),d={};for(let l of a){let g=await m.listTasks(l),P=[];for(let O of g){let h=await m.getTask(l,O);if(h&&!(i.length>0&&!i.includes(h.status||""))&&!(o&&h.assignee!==o)){if(n){let H=n.toLowerCase(),lt=O.toLowerCase().includes(H),mt=(h.name||"").toLowerCase().includes(H);if(!lt&&!mt)continue}P.push({slug:O,name:h.name||O,status:h.status,assignee:h.assignee});}}P.length>0&&(d[l]=P.sort((O,h)=>O.slug.localeCompare(h.slug)));}let u=Object.keys(d).sort();if(u.length===0){console.log("No tasks found");return}for(let l of u){console.log(`
|
|
476
|
+
${l}:`),console.log("---");for(let g of d[l])console.log(`${g.slug.padEnd(20)} ${g.name?.padEnd(30)||""} ${g.status||""} ${g.assignee||""}`);}}});});var ve,Oe=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 m.findTask(e,t),n=m.getTaskDir(o,r);console.log(n);}});});var at,wt=f(()=>{v();k();G();x();z();at=p({description:"Update task properties: name, description, status, assignee, or append summary",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)"),summary:z$1.string().optional().describe("Override the default summary line to append to status.md")}),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,summary:a})=>{let d=j.getCurrentContext(),u=e??d.task;if(!u)throw new Error("No task specified (use --task or cd into task dir)");let{project:l}=await m.findTask(u,t),g=m.getTaskDir(l,u),P=m.getProjectDir(l);if(!await E.runHooksForContext(P,g,"pre-update",{action:"pre-update",entityType:"task",project:l,task:u}))throw new Error("Pre-update hook failed, aborting update");let h={};if(o&&(h.name=o),r&&(h.description=r),n&&(h.status=n),i&&(h.assignee=i),Object.keys(h).length===0&&!a){console.log("No updates provided");return}if(Object.keys(h).length>0){await m.updateTask(l,u,h);let lt=Object.entries(h).map(([mt,Le])=>`${mt}=${Le}`).join(", ");await A.appendStatus(g,`Updated: ${lt}`);}let H=a;!a&&(h.status||h.assignee)&&(H=`Updated: ${h.status?` status to ${h.status}`:""}${h.assignee?` assignee to ${h.assignee}`:""}`),H&&await A.appendStatus(g,H),h.status&&await A.appendStatus(P,`Task updated: ${u} > status to ${h.status}`),await E.runHooksForContext(m.getProjectDir(l),g,"post-update",{action:"post-update",entityType:"task",project:l,task:u}),console.log(`Task ${u} updated`);}});});var De,Ce=f(()=>{v();k();x();wt();De=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=j.getCurrentContext(),n=e??r.task;if(!n)throw new Error("No task specified (use --task or cd into task dir)");let{project:i}=await m.findTask(n,t);(await m.getTask(i,n))?.status!=="in-progress"&&await at.handler({project:i,task:n,status:"in-progress"}),o&&await m.ingestTask(i,n);}});});var Ie,Ne=f(()=>{k();x();z();Ie=p({description:"Post-mortem analysis for a completed task",options:z$1.object({}),args:z$1.object({project:z$1.string().describe("Project slug"),task:z$1.string().describe("Task slug")}),handler:async({project:t,task:e})=>{let o=m.getTaskDir(t,e),r=await m.getTask(t,e),i=(await A.readStatus(o)).split(`
|
|
477
|
+
`).filter(u=>u.startsWith("[")).length,a=[],d=r?.name||e;i>5&&a.push("High status update count - consider breaking into smaller tasks"),d.toLowerCase().includes("implement")&&a.push("Implementation task - check if similar patterns exist to reuse"),d.toLowerCase().includes("create")&&d.toLowerCase().includes("command")&&a.push("Command creation - check existing command patterns in src/commands/"),console.log(`
|
|
478
|
+
=== Post-Mortem Analysis ===`),console.log(`Project: ${t}`),console.log(`Task: ${e}`),console.log(`Name: ${d}`),console.log(`Status: ${r?.status||"unknown"}`),console.log(`Status updates: ${i}`),a.length>0?(console.log(`
|
|
479
|
+
Suggestions:`),a.forEach(u=>console.log(` - ${u}`))):console.log(`
|
|
477
480
|
No specific suggestions - task looks well-scoped`),console.log(`
|
|
478
481
|
=== End Post-Mortem ===
|
|
479
|
-
`);}});});var
|
|
482
|
+
`);}});});var Re,Ue=f(()=>{k();b();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 gt={};He(gt,{default:()=>F});var yo,F,tt=f(()=>{Ot();Ct();Nt();Ut();Lt();Kt();zt();Jt();te();oe();se();ae();pe();me();fe();ke();ye();Te();Pe();Se();Ee();$e();Oe();Ce();wt();Ne();Ue();yo={agent:{create:vt,current:Dt,list:It,path:Rt,start:_t},help:{api:Mt,hooks:Gt,quickstart:Wt,skill:Xt,usage:ee},hook:{create:re,run:ie},project:{create:ce,current:le,list:ue,path:ge,update:he},skill:{read:je},task:{create:we,current:be,ingest:xe,list:Ae,path:ve,start:De,update:at},util:{postmortem:Ie,read:Re}},F=yo;});W();tt();b();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 _e(t){let[e,o,...r]=t;(e==="--version"||e==="-v")&&(console.log(S.version),process.exit(0));try{(!e||!s.isKeyOf(e,F))&&(await F.help.usage.handler({}),process.exit(1));let n=F[e];(!o||!s.isKeyOf(o,n))&&(await F.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 ui=_e,fi=F;s.isMain()&&_e(process.argv.slice(2));export{fi as commands,ui as default};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-projects",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|
|
@@ -50,11 +50,13 @@
|
|
|
50
50
|
},
|
|
51
51
|
"keywords": [
|
|
52
52
|
"ai",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
53
|
+
"aip",
|
|
54
|
+
"project",
|
|
55
|
+
"task",
|
|
56
|
+
"kanban",
|
|
55
57
|
"cli",
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
+
"agent",
|
|
59
|
+
"automation"
|
|
58
60
|
],
|
|
59
61
|
"author": "Ariel Flesler <aflesler@gmail.com>",
|
|
60
62
|
"license": "MIT",
|