@kitelev/exocortex-cli 15.148.9 → 15.149.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.js +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// @kitelev/exocortex-cli v15.
|
|
2
|
+
// @kitelev/exocortex-cli v15.149.0
|
|
3
3
|
// CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more
|
|
4
4
|
// License: MIT
|
|
5
5
|
|
|
@@ -620,7 +620,7 @@ Available prefixes: ${u}`,this.wellKnownPrefixes[r]&&c.push(`Add: PREFIX ${r}: <
|
|
|
620
620
|
- ${r}`)),this.dryRun){if(console.log("\u{1F50D} DRY RUN: Preview of changes (not applied)"),console.log(` File: ${e}`),console.log(" Changes:"),console.log(` \u2022 exo__Asset_label: "${r}"`),l.includes(r))console.log(" \u2022 aliases: unchanged (label already present)");else{let u=l.length===0?[r]:[...l,r];console.log(` \u2022 aliases: [${u.map(f=>`"${f}"`).join(", ")}]`)}console.log(`
|
|
621
621
|
\u{1F4A1} Run without --dry-run to apply changes`),process.exit(0)}await this.fsAdapter.updateFile(i,a),console.log("\u2705 Updated label"),console.log(` File: ${e}`),console.log(` New label: "${r}"`),console.log(" Aliases synchronized"),process.exit(0)}catch(r){D.handle(r)}}async executeSchedule(e,t){try{await this.updatePlannedTimestamp(e,t,"ems__Effort_plannedStartTimestamp"),process.exit(0)}catch(r){D.handle(r)}}async executeSetDeadline(e,t){try{await this.updatePlannedTimestamp(e,t,"ems__Effort_plannedEndTimestamp"),process.exit(0)}catch(r){D.handle(r)}}async updatePlannedTimestamp(e,t,r){let{relativePath:i}=this.resolveAndValidate(e);if(!await this.fsAdapter.fileExists(i))throw new Error(`File not found: ${e}`);if(!/^\d{4}-\d{2}-\d{2}$/.test(t))throw new Error(`Invalid date format: ${t}. Expected YYYY-MM-DD (e.g., 2025-11-25)`);let a=TF.DateFormatter.toTimestampAtStartOfDay(t),c=await this.fsAdapter.readFile(i),l=this.frontmatterService.updateProperty(c,r,a);await this.fsAdapter.writeFile(i,l),console.log(`\u2705 ${r==="ems__Effort_plannedStartTimestamp"?"Scheduled":"Set deadline for"}: ${e}`),console.log(` Date: ${t}`),console.log(` Timestamp: ${a}`)}};var Za=Y(require("path"));var hd=class extends Hi{static{o(this,"FolderRepairExecutor")}constructor(e){super(e)}async executeRepairFolder(e){try{let{relativePath:t}=this.resolveAndValidate(e),i=(await this.fsAdapter.getFileMetadata(t))?.exo__Asset_isDefinedBy;if(!i)throw new Error("Cannot determine expected folder: missing exo__Asset_isDefinedBy");let s=this.extractReference(i);if(!s)throw new Error("Cannot determine expected folder: invalid exo__Asset_isDefinedBy format");let a=await this.findReferencedFile(s,t);if(!a)throw new Error(`Cannot determine expected folder: referenced asset not found: ${s}`);let c=Za.default.dirname(a),l=c==="."?"":c,u=Za.default.dirname(t),f=u==="."?"":u;if(this.normalizePath(f)===this.normalizePath(l))return console.log("\u2705 Already in correct folder"),console.log(` File: ${e}`),console.log(` Folder: ${l||"(root)"}`),process.exit(0),{moved:!1,oldPath:t,newPath:t,expectedFolder:l};if(this.dryRun){let _=Za.default.basename(t),v=l?`${l}/${_}`:_;return console.log("\u{1F50D} DRY RUN: Preview of changes (not applied)"),console.log(` File: ${e}`),console.log(` Current folder: ${f||"(root)"}`),console.log(` Expected folder: ${l||"(root)"}`),console.log(` Would move to: ${v}`),console.log(`
|
|
622
622
|
\u{1F4A1} Run without --dry-run to apply changes`),process.exit(0),{moved:!1,oldPath:t,newPath:v,expectedFolder:l}}let d=Za.default.basename(t),h=l?`${l}/${d}`:d;if(await this.fsAdapter.fileExists(h))throw new Error(`Cannot move file: ${h} already exists`);return l&&(await this.fsAdapter.directoryExists(l)||await this.fsAdapter.createDirectory(l)),await this.fsAdapter.renameFile(t,h),console.log("\u2705 Moved to correct folder"),console.log(` Old path: ${t}`),console.log(` New path: ${h}`),console.log(` Expected folder: ${l||"(root)"}`),process.exit(0),{moved:!0,oldPath:t,newPath:h,expectedFolder:l}}catch(t){D.handle(t)}}extractReference(e){if(typeof e!="string")return null;let t=e.trim().replace(/^["']|["']$/g,"");return t=t.replace(/^\[\[|\]\]$/g,""),t||null}async findReferencedFile(e,t){let r=e.endsWith(".md")?e:`${e}.md`;if(e.includes("/")&&await this.fsAdapter.fileExists(r))return r;let i=Za.default.dirname(t),s=i?`${i}/${r}`:r;if(await this.fsAdapter.fileExists(s))return s;let c=await this.fsAdapter.findFileByUID(e);return c||(await this.fsAdapter.getMarkdownFiles()).find(f=>{let d=Za.default.basename(f,".md"),h=Za.default.basename(r,".md");return d===h})||null}normalizePath(e){return e.replace(/\\/g,"/").replace(/^\.\//,"").replace(/\/$/,"")}};var Hg=class{static{o(this,"CommandExecutor")}constructor(e,t=!1){this.context={pathResolver:new tu(e),fsAdapter:new Rt(e),frontmatterService:new AF.FrontmatterService,dryRun:t},this.statusExecutor=new ld(this.context),this.creationExecutor=new fd(this.context),this.propertyExecutor=new dd(this.context),this.folderRepairExecutor=new hd(this.context)}async execute(e,t,r){try{let i=this.context.pathResolver.resolve(t);this.context.pathResolver.validate(i),await this.context.fsAdapter.readFile(i.replace(this.context.pathResolver.getVaultRoot()+"/","")),console.log("\u2705 Command infrastructure verified"),console.log(` Command: ${e}`),console.log(` File: ${i}`),console.log(` Vault: ${this.context.pathResolver.getVaultRoot()}`),Object.keys(r).length>0&&console.log(` Options: ${JSON.stringify(r,null,2)}`),console.log(`
|
|
623
|
-
\u{1F4DD} Note: Actual command execution will be implemented in follow-up issues.`),console.log(" This PR establishes the infrastructure foundation."),process.exit(0)}catch(i){D.handle(i)}}getVaultRoot(){return this.context.pathResolver.getVaultRoot()}async executeStart(e){return this.statusExecutor.executeStart(e)}async executeComplete(e){return this.statusExecutor.executeComplete(e)}async executeTrash(e){return this.statusExecutor.executeTrash(e)}async executeArchive(e){return this.statusExecutor.executeArchive(e)}async executeMoveToBacklog(e){return this.statusExecutor.executeMoveToBacklog(e)}async executeMoveToAnalysis(e){return this.statusExecutor.executeMoveToAnalysis(e)}async executeMoveToToDo(e){return this.statusExecutor.executeMoveToToDo(e)}async executeCreateTask(e,t,r={}){return this.creationExecutor.executeCreateTask(e,t,r)}async executeCreateMeeting(e,t,r={}){return this.creationExecutor.executeCreateMeeting(e,t,r)}async executeCreateProject(e,t,r={}){return this.creationExecutor.executeCreateProject(e,t,r)}async executeCreateArea(e,t,r={}){return this.creationExecutor.executeCreateArea(e,t,r)}async executeRenameToUid(e){return this.propertyExecutor.executeRenameToUid(e)}async executeUpdateLabel(e,t){return this.propertyExecutor.executeUpdateLabel(e,t)}async executeSchedule(e,t){return this.propertyExecutor.executeSchedule(e,t)}async executeSetDeadline(e,t){return this.propertyExecutor.executeSetDeadline(e,t)}async executeRepairFolder(e){return this.folderRepairExecutor.executeRepairFolder(e)}};function Vr(n,e,t,r,i){if(n==="json"){let s={command:e,filepath:t,action:r,...i&&{changes:i}},a=J.success(s);console.log(JSON.stringify(a,null,2))}else console.log(`\u2705 ${r}`)}o(Vr,"outputResult");function rc(n,e,t,r){let i=new Qe(`--${e} option is required for ${t} command`,r,{command:t,missingOption:e});D.handle(i)}o(rc,"handleMissingOption");function CF(){return new xe("command").description("Execute plugin command on single asset").argument("<command-name>","Command to execute (rename-to-uid, start, complete, schedule, set-deadline, etc.)").argument("<filepath>","Path to asset file (relative to vault root or absolute)").option("--vault <path>","Path to Obsidian vault",process.cwd()).option("--label <value>","Asset label (required for update-label and creation commands)").option("--prototype <uid>","Prototype UID for inheritance (creation commands)").option("--area <uid>","Area UID for effort linkage (creation commands)").option("--parent <uid>","Parent UID for effort linkage (creation commands)").option("--date <value>","Date in YYYY-MM-DD format (required for schedule and set-deadline commands)").option("--dry-run","Preview changes without modifying files").option("--format <type>","Output format: text|json (default: text)","text").action(async(n,e,t)=>{let r=t.format||"text";D.setFormat(r);try{let i=(0,xF.resolve)(t.vault),s=new Hg(i,t.dryRun);switch(n){case"rename-to-uid":await s.executeRenameToUid(e),Vr(r,n,e,"Renamed file to UID-based name");break;case"update-label":t.label||rc(r,"label","update-label",'exocortex command update-label <filepath> --label "<value>"'),await s.executeUpdateLabel(e,t.label),Vr(r,n,e,`Updated label to "${t.label}"`,{label:t.label});break;case"start":await s.executeStart(e),Vr(r,n,e,"Started task");break;case"complete":await s.executeComplete(e),Vr(r,n,e,"Completed task");break;case"trash":await s.executeTrash(e),Vr(r,n,e,"Moved task to trash");break;case"archive":await s.executeArchive(e),Vr(r,n,e,"Archived task");break;case"move-to-backlog":await s.executeMoveToBacklog(e),Vr(r,n,e,"Moved task to backlog");break;case"move-to-analysis":await s.executeMoveToAnalysis(e),Vr(r,n,e,"Moved task to analysis");break;case"move-to-todo":await s.executeMoveToToDo(e),Vr(r,n,e,"Moved task to todo");break;case"create-task":t.label||rc(r,"label","create-task",'exocortex command create-task <filepath> --label "<value>"'),await s.executeCreateTask(e,t.label,{prototype:t.prototype,area:t.area,parent:t.parent}),Vr(r,n,e,`Created task "${t.label}"`,{label:t.label,prototype:t.prototype,area:t.area,parent:t.parent});break;case"create-meeting":t.label||rc(r,"label","create-meeting",'exocortex command create-meeting <filepath> --label "<value>"'),await s.executeCreateMeeting(e,t.label,{prototype:t.prototype,area:t.area,parent:t.parent}),Vr(r,n,e,`Created meeting "${t.label}"`,{label:t.label,prototype:t.prototype,area:t.area,parent:t.parent});break;case"create-project":t.label||rc(r,"label","create-project",'exocortex command create-project <filepath> --label "<value>"'),await s.executeCreateProject(e,t.label,{prototype:t.prototype,area:t.area,parent:t.parent}),Vr(r,n,e,`Created project "${t.label}"`,{label:t.label,prototype:t.prototype,area:t.area,parent:t.parent});break;case"create-area":t.label||rc(r,"label","create-area",'exocortex command create-area <filepath> --label "<value>"'),await s.executeCreateArea(e,t.label,{prototype:t.prototype,area:t.area,parent:t.parent}),Vr(r,n,e,`Created area "${t.label}"`,{label:t.label,prototype:t.prototype,area:t.area,parent:t.parent});break;case"schedule":t.date||rc(r,"date","schedule",'exocortex command schedule <filepath> --date "YYYY-MM-DD"'),await s.executeSchedule(e,t.date),Vr(r,n,e,`Scheduled task for ${t.date}`,{date:t.date});break;case"set-deadline":t.date||rc(r,"date","set-deadline",'exocortex command set-deadline <filepath> --date "YYYY-MM-DD"'),await s.executeSetDeadline(e,t.date),Vr(r,n,e,`Set deadline to ${t.date}`,{date:t.date});break;case"repair-folder":{let a=await s.executeRepairFolder(e);Vr(r,n,e,a.moved?`Moved to correct folder: ${a.newPath}`:"Already in correct folder",{moved:a.moved,oldPath:a.oldPath,newPath:a.newPath,expectedFolder:a.expectedFolder});break}default:await s.execute(n,e,t),Vr(r,n,e,`Executed ${n}`);break}}catch(i){D.handle(i)}})}o(CF,"commandCommand");var RF=require("path");var fa=Y(require("fs")),cE=Y(require("path")),IF=require("events");var SY={watch:fa.default.watch.bind(fa.default),existsSync:fa.default.existsSync.bind(fa.default),statSync:fa.default.statSync.bind(fa.default),readFileSync:fa.default.readFileSync.bind(fa.default)},Qg=class extends IF.EventEmitter{constructor(t,r={}){super();this.watcher=null;this.debounceTimers=new Map;this.isRunning=!1;this.vaultPath=cE.default.resolve(t),this.fsAdapter=r.fsAdapter??SY,this.options={pattern:r.pattern??"**/*.md",assetType:r.assetType??"",debounceMs:r.debounceMs??100,recursive:r.recursive??!0}}static{o(this,"FileSystemWatcher")}start(){this.isRunning||(this.watcher=this.fsAdapter.watch(this.vaultPath,{recursive:this.options.recursive},(t,r)=>{r&&this.handleFileEvent(t,r.toString())}),this.watcher.on("error",t=>{this.emit("error",t)}),this.isRunning=!0,this.emit("started"))}stop(){if(this.isRunning){for(let t of this.debounceTimers.values())clearTimeout(t);this.debounceTimers.clear(),this.watcher&&(this.watcher.close(),this.watcher=null),this.isRunning=!1,this.emit("stopped")}}isWatching(){return this.isRunning}handleFileEvent(t,r){let i=cE.default.join(this.vaultPath,r),s=r;if(!this.matchesPattern(s))return;let a=this.debounceTimers.get(i);a&&clearTimeout(a);let c=setTimeout(()=>{this.debounceTimers.delete(i),this.processFileEvent(i,s)},this.options.debounceMs);this.debounceTimers.set(i,c)}processFileEvent(t,r){let i=this.fsAdapter.existsSync(t),s;if(!i)s="delete";else{let l=this.fsAdapter.statSync(t);s=Date.now()-l.birthtimeMs<1e3?"create":"modify"}let a;if(i&&t.endsWith(".md")&&(a=this.extractAssetType(t)),this.options.assetType&&a!==this.options.assetType)return;let c={type:s,path:t,relativePath:r,timestamp:new Date().toISOString(),assetType:a};this.emit("change",c)}matchesPattern(t){return this.options.pattern?$r(t,this.options.pattern):!0}extractAssetType(t){try{let i=this.fsAdapter.readFileSync(t,"utf-8").match(/^---\n([\s\S]*?)\n---/);if(!i)return;let a=i[1].match(/exo__Instance_class:\s*(?:\[?"?\[\[([^\]]+)\]\]"?\]?|"?\[\[([^\]]+)\]\]"?)/);return a?a[1]||a[2]:void 0}catch{return}}};var lE=Y(require("fs"));function PF(){return new xe("watch").description("Watch vault for file changes and emit NDJSON events").option("--vault <path>","Path to Obsidian vault",process.cwd()).option("--pattern <glob>","Glob pattern to filter files (e.g., '*.md', 'tasks/**')").option("--asset-type <type>","Filter by asset type (e.g., 'ems__Task', 'ems__Project')").option("--debounce <ms>","Debounce interval in milliseconds","100").action(async n=>{D.setFormat("json");try{let e=(0,RF.resolve)(n.vault);if(!lE.default.existsSync(e))throw new Ee(e);if(!lE.default.statSync(e).isDirectory())throw new Qe(`Vault path is not a directory: ${e}`,"Provide a path to a directory, not a file",{vaultPath:e});let t=parseInt(n.debounce?.toString()??"100",10);if(isNaN(t)||t<0)throw new Qe("--debounce must be a non-negative integer","Use a positive integer value for debounce",{debounce:n.debounce});let r=new Qg(e,{pattern:n.pattern,assetType:n.assetType,debounceMs:t});r.on("change",s=>{console.log(JSON.stringify(s))}),r.on("error",s=>{let a=J.error("INTERNAL_OPERATION_FAILED",s.message,5,{recovery:{message:"Check file permissions and vault access",suggestion:"Ensure the vault directory is accessible"}});console.log(JSON.stringify(a))});let i=o(()=>{r.stop(),process.exit(0)},"shutdown");process.on("SIGINT",i),process.on("SIGTERM",i),console.error(`Watching vault: ${e}`),n.pattern&&console.error(` Pattern filter: ${n.pattern}`),n.assetType&&console.error(` Asset type filter: ${n.assetType}`),console.error(` Debounce: ${t}ms`),console.error("Press Ctrl+C to stop"),r.start()}catch(e){D.handle(e)}})}o(PF,"watchCommand");var fE=require("path"),dE=Y(Ga());var nc=Y(require("path"));var nu=Y(Mt());var Qi=Y(Ga()),uE=Y(require("crypto"));var Yg=class{constructor(){this.backups=new Map;this.fileHashes=new Map}static{o(this,"TransactionManager")}async begin(e){if(!Qi.default.existsSync(e))throw new Error(`Cannot backup non-existent file: ${e}`);let t=await Qi.default.readFile(e,"utf-8"),r=uE.default.createHash("sha256").update(t).digest("hex");this.fileHashes.set(e,r);let i=`${e}.backup.${Date.now()}`;await Qi.default.writeFile(i,t,"utf-8"),this.backups.set(e,i)}async verify(e){let t=this.fileHashes.get(e);if(!t)return!0;if(!Qi.default.existsSync(e))return!1;let r=await Qi.default.readFile(e,"utf-8");return uE.default.createHash("sha256").update(r).digest("hex")===t}async commit(){for(let e of this.backups.values())Qi.default.existsSync(e)&&await Qi.default.remove(e);this.backups.clear(),this.fileHashes.clear()}async rollback(){for(let[e,t]of this.backups.entries())Qi.default.existsSync(t)&&(await Qi.default.copy(t,e,{overwrite:!0}),await Qi.default.remove(t));this.backups.clear(),this.fileHashes.clear()}getTrackedFiles(){return Array.from(this.backups.keys())}};var ic=class{static{o(this,"BatchExecutor")}constructor(e,t=!1){this.pathResolver=new tu(e),this.fsAdapter=new Rt(e),this.frontmatterService=new nu.FrontmatterService,this.transactionManager=new Yg,this.dryRun=t}async executeBatch(e,t=!1){let r=Date.now(),i=[],s=0,a=0;if(e.length===0)return{success:!0,total:0,succeeded:0,failed:0,results:[],durationMs:Date.now()-r,atomic:t};if(t&&!this.dryRun)try{await this.backupAllFiles(e)}catch(l){return{success:!1,total:e.length,succeeded:0,failed:e.length,results:e.map(u=>({success:!1,command:u.command,filepath:u.filepath,error:`Backup failed: ${l.message}`})),durationMs:Date.now()-r,atomic:t}}for(let l of e){let u=await this.executeSingleOperation(l);if(i.push(u),u.success)s++;else if(a++,t&&!this.dryRun){await this.transactionManager.rollback();let f=i.length;for(let d=f;d<e.length;d++)i.push({success:!1,command:e[d].command,filepath:e[d].filepath,error:"Not executed due to atomic rollback"}),a++;return{success:!1,total:e.length,succeeded:0,failed:e.length,results:i,durationMs:Date.now()-r,atomic:t,rolledBack:!0}}}return t&&!this.dryRun&&await this.transactionManager.commit(),{success:a===0,total:e.length,succeeded:s,failed:a,results:i,durationMs:Date.now()-r,atomic:t,...t&&{rolledBack:!1}}}static parseInput(e){try{let t=JSON.parse(e);if(!Array.isArray(t))throw new Qe("Batch input must be a JSON array of operations",`exo batch --input '[{"command":"start","filepath":"task.md"}]'`);for(let r of t){if(!r.command||typeof r.command!="string")throw new Qe('Each operation must have a "command" string property','{"command":"start","filepath":"task.md"}');if(!r.filepath||typeof r.filepath!="string")throw new Qe('Each operation must have a "filepath" string property','{"command":"start","filepath":"task.md"}')}return t}catch(t){throw t instanceof Qe?t:new Qe(`Failed to parse batch input: ${t.message}`,`exo batch --input '[{"command":"start","filepath":"task.md"}]'`)}}async backupAllFiles(e){let t=new Set;for(let r of e){let i=this.pathResolver.resolve(r.filepath);t.add(i)}for(let r of t)await this.transactionManager.begin(r)}async executeSingleOperation(e){try{let t=this.pathResolver.resolve(e.filepath);this.pathResolver.validate(t);let r=t.replace(this.pathResolver.getVaultRoot()+"/","");if(await this.fsAdapter.readFile(r),this.dryRun)return{success:!0,command:e.command,filepath:e.filepath,action:`Would execute ${e.command} (dry-run)`};switch(e.command){case"start":return await this.executeStart(r,e);case"complete":return await this.executeComplete(r,e);case"trash":return await this.executeTrash(r,e);case"archive":return await this.executeArchive(r,e);case"move-to-backlog":return await this.executeStatusUpdate(r,e,"ems__EffortStatusBacklog","Moved to backlog");case"move-to-analysis":return await this.executeStatusUpdate(r,e,"ems__EffortStatusAnalysis","Moved to analysis");case"move-to-todo":return await this.executeStatusUpdate(r,e,"ems__EffortStatusToDo","Moved to todo");case"update-label":return await this.executeUpdateLabel(r,e);case"schedule":return await this.executeSchedule(r,e);case"set-deadline":return await this.executeSetDeadline(r,e);case"repair-folder":return await this.executeRepairFolder(r,e);default:return{success:!1,command:e.command,filepath:e.filepath,error:`Unknown command: ${e.command}`}}}catch(t){return{success:!1,command:e.command,filepath:e.filepath,error:t.message}}}getCurrentTimestamp(){return nu.DateFormatter.toLocalTimestamp(new Date)}async executeStart(e,t){let r=await this.fsAdapter.readFile(e),i=this.getCurrentTimestamp(),s=this.frontmatterService.updateProperty(r,"ems__Effort_status",'"[[ems__EffortStatusDoing]]"');return s=this.frontmatterService.updateProperty(s,"ems__Effort_startTimestamp",i),await this.fsAdapter.updateFile(e,s),{success:!0,command:t.command,filepath:t.filepath,action:"Started task",changes:{status:"Doing",startTimestamp:i}}}async executeComplete(e,t){let r=await this.fsAdapter.readFile(e),i=this.getCurrentTimestamp(),s=this.frontmatterService.updateProperty(r,"ems__Effort_status",'"[[ems__EffortStatusDone]]"');return s=this.frontmatterService.updateProperty(s,"ems__Effort_endTimestamp",i),s=this.frontmatterService.updateProperty(s,"ems__Effort_resolutionTimestamp",i),await this.fsAdapter.updateFile(e,s),{success:!0,command:t.command,filepath:t.filepath,action:"Completed task",changes:{status:"Done",endTimestamp:i,resolutionTimestamp:i}}}async executeTrash(e,t){let r=await this.fsAdapter.readFile(e),i=this.getCurrentTimestamp(),s=this.frontmatterService.updateProperty(r,"ems__Effort_status",'"[[ems__EffortStatusTrashed]]"');return s=this.frontmatterService.updateProperty(s,"ems__Effort_resolutionTimestamp",i),await this.fsAdapter.updateFile(e,s),{success:!0,command:t.command,filepath:t.filepath,action:"Trashed task",changes:{status:"Trashed",resolutionTimestamp:i}}}async executeArchive(e,t){let r=await this.fsAdapter.readFile(e),i=this.frontmatterService.updateProperty(r,"archived","true");return i=this.frontmatterService.removeProperty(i,"aliases"),await this.fsAdapter.updateFile(e,i),{success:!0,command:t.command,filepath:t.filepath,action:"Archived task",changes:{archived:!0,aliasesRemoved:!0}}}async executeStatusUpdate(e,t,r,i){let s=await this.fsAdapter.readFile(e),a=this.frontmatterService.updateProperty(s,"ems__Effort_status",`"[[${r}]]"`);return await this.fsAdapter.updateFile(e,a),{success:!0,command:t.command,filepath:t.filepath,action:i,changes:{status:r.replace("ems__EffortStatus","")}}}async executeUpdateLabel(e,t){let r=t.options?.label;if(!r||r.trim()==="")return{success:!1,command:t.command,filepath:t.filepath,error:'Missing required option: "label"'};let i=r.trim(),s=await this.fsAdapter.readFile(e),a=this.frontmatterService.updateProperty(s,"exo__Asset_label",`"${i}"`);return await this.fsAdapter.updateFile(e,a),{success:!0,command:t.command,filepath:t.filepath,action:`Updated label to "${i}"`,changes:{label:i}}}async executeSchedule(e,t){let r=t.options?.date;if(!r)return{success:!1,command:t.command,filepath:t.filepath,error:'Missing required option: "date"'};if(!/^\d{4}-\d{2}-\d{2}$/.test(r))return{success:!1,command:t.command,filepath:t.filepath,error:`Invalid date format: ${r}. Expected YYYY-MM-DD`};let i=await this.fsAdapter.readFile(e),s=nu.DateFormatter.toTimestampAtStartOfDay(r),a=this.frontmatterService.updateProperty(i,"ems__Effort_scheduledTimestamp",s);return await this.fsAdapter.updateFile(e,a),{success:!0,command:t.command,filepath:t.filepath,action:`Scheduled for ${r}`,changes:{scheduledTimestamp:s}}}async executeSetDeadline(e,t){let r=t.options?.date;if(!r)return{success:!1,command:t.command,filepath:t.filepath,error:'Missing required option: "date"'};if(!/^\d{4}-\d{2}-\d{2}$/.test(r))return{success:!1,command:t.command,filepath:t.filepath,error:`Invalid date format: ${r}. Expected YYYY-MM-DD`};let i=await this.fsAdapter.readFile(e),s=nu.DateFormatter.toTimestampAtStartOfDay(r),a=this.frontmatterService.updateProperty(i,"ems__Effort_deadlineTimestamp",s);return await this.fsAdapter.updateFile(e,a),{success:!0,command:t.command,filepath:t.filepath,action:`Set deadline to ${r}`,changes:{deadlineTimestamp:s}}}async executeRepairFolder(e,t){let i=(await this.fsAdapter.getFileMetadata(e))?.exo__Asset_isDefinedBy;if(!i)return{success:!1,command:t.command,filepath:t.filepath,error:"Cannot determine expected folder: missing exo__Asset_isDefinedBy"};let s=this.extractReference(i);if(!s)return{success:!1,command:t.command,filepath:t.filepath,error:"Cannot determine expected folder: invalid exo__Asset_isDefinedBy format"};let a=await this.findReferencedFile(s,e);if(!a)return{success:!1,command:t.command,filepath:t.filepath,error:`Cannot determine expected folder: referenced asset not found: ${s}`};let c=nc.default.dirname(a),l=c==="."?"":c,u=nc.default.dirname(e),f=u==="."?"":u;if(this.normalizePath(f)===this.normalizePath(l))return{success:!0,command:t.command,filepath:t.filepath,action:"Already in correct folder",changes:{moved:!1,folder:l||"(root)"}};let d=nc.default.basename(e),h=l?`${l}/${d}`:d;return await this.fsAdapter.fileExists(h)?{success:!1,command:t.command,filepath:t.filepath,error:`Cannot move file: ${h} already exists`}:(await this.fsAdapter.renameFile(e,h),{success:!0,command:t.command,filepath:t.filepath,action:`Moved to ${l||"(root)"}`,changes:{moved:!0,oldPath:e,newPath:h,expectedFolder:l||"(root)"}})}extractReference(e){if(typeof e!="string")return null;let t=e.trim().replace(/^["']|["']$/g,"");return t=t.replace(/^\[\[|\]\]$/g,""),t||null}async findReferencedFile(e,t){let r=e.endsWith(".md")?e:`${e}.md`;if(e.includes("/")&&await this.fsAdapter.fileExists(r))return r;let i=nc.default.dirname(t),s=i!=="."?`${i}/${r}`:r;if(await this.fsAdapter.fileExists(s))return s;let c=await this.fsAdapter.findFileByUID(e);return c||(await this.fsAdapter.getMarkdownFiles()).find(f=>{let d=nc.default.basename(f,".md"),h=nc.default.basename(r,".md");return d===h})||null}normalizePath(e){return e.replace(/\\/g,"/").replace(/^\.\//,"").replace(/\/$/,"")}};function vY(n,e){if(n==="json"){let t=J.success(e,{durationMs:e.durationMs,itemCount:e.total});console.log(JSON.stringify(t,null,2))}else{if(console.log(`
|
|
623
|
+
\u{1F4DD} Note: Actual command execution will be implemented in follow-up issues.`),console.log(" This PR establishes the infrastructure foundation."),process.exit(0)}catch(i){D.handle(i)}}getVaultRoot(){return this.context.pathResolver.getVaultRoot()}async executeStart(e){return this.statusExecutor.executeStart(e)}async executeComplete(e){return this.statusExecutor.executeComplete(e)}async executeTrash(e){return this.statusExecutor.executeTrash(e)}async executeArchive(e){return this.statusExecutor.executeArchive(e)}async executeMoveToBacklog(e){return this.statusExecutor.executeMoveToBacklog(e)}async executeMoveToAnalysis(e){return this.statusExecutor.executeMoveToAnalysis(e)}async executeMoveToToDo(e){return this.statusExecutor.executeMoveToToDo(e)}async executeCreateTask(e,t,r={}){return this.creationExecutor.executeCreateTask(e,t,r)}async executeCreateMeeting(e,t,r={}){return this.creationExecutor.executeCreateMeeting(e,t,r)}async executeCreateProject(e,t,r={}){return this.creationExecutor.executeCreateProject(e,t,r)}async executeCreateArea(e,t,r={}){return this.creationExecutor.executeCreateArea(e,t,r)}async executeRenameToUid(e){return this.propertyExecutor.executeRenameToUid(e)}async executeUpdateLabel(e,t){return this.propertyExecutor.executeUpdateLabel(e,t)}async executeSchedule(e,t){return this.propertyExecutor.executeSchedule(e,t)}async executeSetDeadline(e,t){return this.propertyExecutor.executeSetDeadline(e,t)}async executeRepairFolder(e){return this.folderRepairExecutor.executeRepairFolder(e)}};function Vr(n,e,t,r,i){if(n==="json"){let s={command:e,filepath:t,action:r,...i&&{changes:i}},a=J.success(s);console.log(JSON.stringify(a,null,2))}else console.log(`\u2705 ${r}`)}o(Vr,"outputResult");function rc(n,e,t,r){let i=new Qe(`--${e} option is required for ${t} command`,r,{command:t,missingOption:e});D.handle(i)}o(rc,"handleMissingOption");function CF(){return new xe("command").description("Execute plugin command on single asset").argument("<command-name>","Command to execute (rename-to-uid, start, complete, schedule, set-deadline, etc.)").argument("<filepath>","Path to asset file (relative to vault root or absolute)").option("--vault <path>","Path to Obsidian vault",process.cwd()).option("--label <value>","Asset label (required for update-label and creation commands)").option("--prototype <uid>","Prototype UID for inheritance (creation commands)").option("--area <uid>","Area UID for effort linkage (creation commands)").option("--parent <uid>","Parent UID for effort linkage (creation commands)").option("--date <value>","Date in YYYY-MM-DD format (required for schedule and set-deadline commands)").option("--dry-run","Preview changes without modifying files").option("--format <type>","Output format: text|json (default: text)","text").action(async(n,e,t)=>{let r=t.format||"text";D.setFormat(r),n==="start"&&console.error("\u26A0\uFE0F DEPRECATED: `command start <path>` is deprecated. Use `dyncommand exec <uid>` instead. `command start <path>` will be removed in 2 minor releases (T7.3).");try{let i=(0,xF.resolve)(t.vault),s=new Hg(i,t.dryRun);switch(n){case"rename-to-uid":await s.executeRenameToUid(e),Vr(r,n,e,"Renamed file to UID-based name");break;case"update-label":t.label||rc(r,"label","update-label",'exocortex command update-label <filepath> --label "<value>"'),await s.executeUpdateLabel(e,t.label),Vr(r,n,e,`Updated label to "${t.label}"`,{label:t.label});break;case"start":await s.executeStart(e),Vr(r,n,e,"Started task");break;case"complete":await s.executeComplete(e),Vr(r,n,e,"Completed task");break;case"trash":await s.executeTrash(e),Vr(r,n,e,"Moved task to trash");break;case"archive":await s.executeArchive(e),Vr(r,n,e,"Archived task");break;case"move-to-backlog":await s.executeMoveToBacklog(e),Vr(r,n,e,"Moved task to backlog");break;case"move-to-analysis":await s.executeMoveToAnalysis(e),Vr(r,n,e,"Moved task to analysis");break;case"move-to-todo":await s.executeMoveToToDo(e),Vr(r,n,e,"Moved task to todo");break;case"create-task":t.label||rc(r,"label","create-task",'exocortex command create-task <filepath> --label "<value>"'),await s.executeCreateTask(e,t.label,{prototype:t.prototype,area:t.area,parent:t.parent}),Vr(r,n,e,`Created task "${t.label}"`,{label:t.label,prototype:t.prototype,area:t.area,parent:t.parent});break;case"create-meeting":t.label||rc(r,"label","create-meeting",'exocortex command create-meeting <filepath> --label "<value>"'),await s.executeCreateMeeting(e,t.label,{prototype:t.prototype,area:t.area,parent:t.parent}),Vr(r,n,e,`Created meeting "${t.label}"`,{label:t.label,prototype:t.prototype,area:t.area,parent:t.parent});break;case"create-project":t.label||rc(r,"label","create-project",'exocortex command create-project <filepath> --label "<value>"'),await s.executeCreateProject(e,t.label,{prototype:t.prototype,area:t.area,parent:t.parent}),Vr(r,n,e,`Created project "${t.label}"`,{label:t.label,prototype:t.prototype,area:t.area,parent:t.parent});break;case"create-area":t.label||rc(r,"label","create-area",'exocortex command create-area <filepath> --label "<value>"'),await s.executeCreateArea(e,t.label,{prototype:t.prototype,area:t.area,parent:t.parent}),Vr(r,n,e,`Created area "${t.label}"`,{label:t.label,prototype:t.prototype,area:t.area,parent:t.parent});break;case"schedule":t.date||rc(r,"date","schedule",'exocortex command schedule <filepath> --date "YYYY-MM-DD"'),await s.executeSchedule(e,t.date),Vr(r,n,e,`Scheduled task for ${t.date}`,{date:t.date});break;case"set-deadline":t.date||rc(r,"date","set-deadline",'exocortex command set-deadline <filepath> --date "YYYY-MM-DD"'),await s.executeSetDeadline(e,t.date),Vr(r,n,e,`Set deadline to ${t.date}`,{date:t.date});break;case"repair-folder":{let a=await s.executeRepairFolder(e);Vr(r,n,e,a.moved?`Moved to correct folder: ${a.newPath}`:"Already in correct folder",{moved:a.moved,oldPath:a.oldPath,newPath:a.newPath,expectedFolder:a.expectedFolder});break}default:await s.execute(n,e,t),Vr(r,n,e,`Executed ${n}`);break}}catch(i){D.handle(i)}})}o(CF,"commandCommand");var RF=require("path");var fa=Y(require("fs")),cE=Y(require("path")),IF=require("events");var SY={watch:fa.default.watch.bind(fa.default),existsSync:fa.default.existsSync.bind(fa.default),statSync:fa.default.statSync.bind(fa.default),readFileSync:fa.default.readFileSync.bind(fa.default)},Qg=class extends IF.EventEmitter{constructor(t,r={}){super();this.watcher=null;this.debounceTimers=new Map;this.isRunning=!1;this.vaultPath=cE.default.resolve(t),this.fsAdapter=r.fsAdapter??SY,this.options={pattern:r.pattern??"**/*.md",assetType:r.assetType??"",debounceMs:r.debounceMs??100,recursive:r.recursive??!0}}static{o(this,"FileSystemWatcher")}start(){this.isRunning||(this.watcher=this.fsAdapter.watch(this.vaultPath,{recursive:this.options.recursive},(t,r)=>{r&&this.handleFileEvent(t,r.toString())}),this.watcher.on("error",t=>{this.emit("error",t)}),this.isRunning=!0,this.emit("started"))}stop(){if(this.isRunning){for(let t of this.debounceTimers.values())clearTimeout(t);this.debounceTimers.clear(),this.watcher&&(this.watcher.close(),this.watcher=null),this.isRunning=!1,this.emit("stopped")}}isWatching(){return this.isRunning}handleFileEvent(t,r){let i=cE.default.join(this.vaultPath,r),s=r;if(!this.matchesPattern(s))return;let a=this.debounceTimers.get(i);a&&clearTimeout(a);let c=setTimeout(()=>{this.debounceTimers.delete(i),this.processFileEvent(i,s)},this.options.debounceMs);this.debounceTimers.set(i,c)}processFileEvent(t,r){let i=this.fsAdapter.existsSync(t),s;if(!i)s="delete";else{let l=this.fsAdapter.statSync(t);s=Date.now()-l.birthtimeMs<1e3?"create":"modify"}let a;if(i&&t.endsWith(".md")&&(a=this.extractAssetType(t)),this.options.assetType&&a!==this.options.assetType)return;let c={type:s,path:t,relativePath:r,timestamp:new Date().toISOString(),assetType:a};this.emit("change",c)}matchesPattern(t){return this.options.pattern?$r(t,this.options.pattern):!0}extractAssetType(t){try{let i=this.fsAdapter.readFileSync(t,"utf-8").match(/^---\n([\s\S]*?)\n---/);if(!i)return;let a=i[1].match(/exo__Instance_class:\s*(?:\[?"?\[\[([^\]]+)\]\]"?\]?|"?\[\[([^\]]+)\]\]"?)/);return a?a[1]||a[2]:void 0}catch{return}}};var lE=Y(require("fs"));function PF(){return new xe("watch").description("Watch vault for file changes and emit NDJSON events").option("--vault <path>","Path to Obsidian vault",process.cwd()).option("--pattern <glob>","Glob pattern to filter files (e.g., '*.md', 'tasks/**')").option("--asset-type <type>","Filter by asset type (e.g., 'ems__Task', 'ems__Project')").option("--debounce <ms>","Debounce interval in milliseconds","100").action(async n=>{D.setFormat("json");try{let e=(0,RF.resolve)(n.vault);if(!lE.default.existsSync(e))throw new Ee(e);if(!lE.default.statSync(e).isDirectory())throw new Qe(`Vault path is not a directory: ${e}`,"Provide a path to a directory, not a file",{vaultPath:e});let t=parseInt(n.debounce?.toString()??"100",10);if(isNaN(t)||t<0)throw new Qe("--debounce must be a non-negative integer","Use a positive integer value for debounce",{debounce:n.debounce});let r=new Qg(e,{pattern:n.pattern,assetType:n.assetType,debounceMs:t});r.on("change",s=>{console.log(JSON.stringify(s))}),r.on("error",s=>{let a=J.error("INTERNAL_OPERATION_FAILED",s.message,5,{recovery:{message:"Check file permissions and vault access",suggestion:"Ensure the vault directory is accessible"}});console.log(JSON.stringify(a))});let i=o(()=>{r.stop(),process.exit(0)},"shutdown");process.on("SIGINT",i),process.on("SIGTERM",i),console.error(`Watching vault: ${e}`),n.pattern&&console.error(` Pattern filter: ${n.pattern}`),n.assetType&&console.error(` Asset type filter: ${n.assetType}`),console.error(` Debounce: ${t}ms`),console.error("Press Ctrl+C to stop"),r.start()}catch(e){D.handle(e)}})}o(PF,"watchCommand");var fE=require("path"),dE=Y(Ga());var nc=Y(require("path"));var nu=Y(Mt());var Qi=Y(Ga()),uE=Y(require("crypto"));var Yg=class{constructor(){this.backups=new Map;this.fileHashes=new Map}static{o(this,"TransactionManager")}async begin(e){if(!Qi.default.existsSync(e))throw new Error(`Cannot backup non-existent file: ${e}`);let t=await Qi.default.readFile(e,"utf-8"),r=uE.default.createHash("sha256").update(t).digest("hex");this.fileHashes.set(e,r);let i=`${e}.backup.${Date.now()}`;await Qi.default.writeFile(i,t,"utf-8"),this.backups.set(e,i)}async verify(e){let t=this.fileHashes.get(e);if(!t)return!0;if(!Qi.default.existsSync(e))return!1;let r=await Qi.default.readFile(e,"utf-8");return uE.default.createHash("sha256").update(r).digest("hex")===t}async commit(){for(let e of this.backups.values())Qi.default.existsSync(e)&&await Qi.default.remove(e);this.backups.clear(),this.fileHashes.clear()}async rollback(){for(let[e,t]of this.backups.entries())Qi.default.existsSync(t)&&(await Qi.default.copy(t,e,{overwrite:!0}),await Qi.default.remove(t));this.backups.clear(),this.fileHashes.clear()}getTrackedFiles(){return Array.from(this.backups.keys())}};var ic=class{static{o(this,"BatchExecutor")}constructor(e,t=!1){this.pathResolver=new tu(e),this.fsAdapter=new Rt(e),this.frontmatterService=new nu.FrontmatterService,this.transactionManager=new Yg,this.dryRun=t}async executeBatch(e,t=!1){let r=Date.now(),i=[],s=0,a=0;if(e.length===0)return{success:!0,total:0,succeeded:0,failed:0,results:[],durationMs:Date.now()-r,atomic:t};if(t&&!this.dryRun)try{await this.backupAllFiles(e)}catch(l){return{success:!1,total:e.length,succeeded:0,failed:e.length,results:e.map(u=>({success:!1,command:u.command,filepath:u.filepath,error:`Backup failed: ${l.message}`})),durationMs:Date.now()-r,atomic:t}}for(let l of e){let u=await this.executeSingleOperation(l);if(i.push(u),u.success)s++;else if(a++,t&&!this.dryRun){await this.transactionManager.rollback();let f=i.length;for(let d=f;d<e.length;d++)i.push({success:!1,command:e[d].command,filepath:e[d].filepath,error:"Not executed due to atomic rollback"}),a++;return{success:!1,total:e.length,succeeded:0,failed:e.length,results:i,durationMs:Date.now()-r,atomic:t,rolledBack:!0}}}return t&&!this.dryRun&&await this.transactionManager.commit(),{success:a===0,total:e.length,succeeded:s,failed:a,results:i,durationMs:Date.now()-r,atomic:t,...t&&{rolledBack:!1}}}static parseInput(e){try{let t=JSON.parse(e);if(!Array.isArray(t))throw new Qe("Batch input must be a JSON array of operations",`exo batch --input '[{"command":"start","filepath":"task.md"}]'`);for(let r of t){if(!r.command||typeof r.command!="string")throw new Qe('Each operation must have a "command" string property','{"command":"start","filepath":"task.md"}');if(!r.filepath||typeof r.filepath!="string")throw new Qe('Each operation must have a "filepath" string property','{"command":"start","filepath":"task.md"}')}return t}catch(t){throw t instanceof Qe?t:new Qe(`Failed to parse batch input: ${t.message}`,`exo batch --input '[{"command":"start","filepath":"task.md"}]'`)}}async backupAllFiles(e){let t=new Set;for(let r of e){let i=this.pathResolver.resolve(r.filepath);t.add(i)}for(let r of t)await this.transactionManager.begin(r)}async executeSingleOperation(e){try{let t=this.pathResolver.resolve(e.filepath);this.pathResolver.validate(t);let r=t.replace(this.pathResolver.getVaultRoot()+"/","");if(await this.fsAdapter.readFile(r),this.dryRun)return{success:!0,command:e.command,filepath:e.filepath,action:`Would execute ${e.command} (dry-run)`};switch(e.command){case"start":return await this.executeStart(r,e);case"complete":return await this.executeComplete(r,e);case"trash":return await this.executeTrash(r,e);case"archive":return await this.executeArchive(r,e);case"move-to-backlog":return await this.executeStatusUpdate(r,e,"ems__EffortStatusBacklog","Moved to backlog");case"move-to-analysis":return await this.executeStatusUpdate(r,e,"ems__EffortStatusAnalysis","Moved to analysis");case"move-to-todo":return await this.executeStatusUpdate(r,e,"ems__EffortStatusToDo","Moved to todo");case"update-label":return await this.executeUpdateLabel(r,e);case"schedule":return await this.executeSchedule(r,e);case"set-deadline":return await this.executeSetDeadline(r,e);case"repair-folder":return await this.executeRepairFolder(r,e);default:return{success:!1,command:e.command,filepath:e.filepath,error:`Unknown command: ${e.command}`}}}catch(t){return{success:!1,command:e.command,filepath:e.filepath,error:t.message}}}getCurrentTimestamp(){return nu.DateFormatter.toLocalTimestamp(new Date)}async executeStart(e,t){let r=await this.fsAdapter.readFile(e),i=this.getCurrentTimestamp(),s=this.frontmatterService.updateProperty(r,"ems__Effort_status",'"[[ems__EffortStatusDoing]]"');return s=this.frontmatterService.updateProperty(s,"ems__Effort_startTimestamp",i),await this.fsAdapter.updateFile(e,s),{success:!0,command:t.command,filepath:t.filepath,action:"Started task",changes:{status:"Doing",startTimestamp:i}}}async executeComplete(e,t){let r=await this.fsAdapter.readFile(e),i=this.getCurrentTimestamp(),s=this.frontmatterService.updateProperty(r,"ems__Effort_status",'"[[ems__EffortStatusDone]]"');return s=this.frontmatterService.updateProperty(s,"ems__Effort_endTimestamp",i),s=this.frontmatterService.updateProperty(s,"ems__Effort_resolutionTimestamp",i),await this.fsAdapter.updateFile(e,s),{success:!0,command:t.command,filepath:t.filepath,action:"Completed task",changes:{status:"Done",endTimestamp:i,resolutionTimestamp:i}}}async executeTrash(e,t){let r=await this.fsAdapter.readFile(e),i=this.getCurrentTimestamp(),s=this.frontmatterService.updateProperty(r,"ems__Effort_status",'"[[ems__EffortStatusTrashed]]"');return s=this.frontmatterService.updateProperty(s,"ems__Effort_resolutionTimestamp",i),await this.fsAdapter.updateFile(e,s),{success:!0,command:t.command,filepath:t.filepath,action:"Trashed task",changes:{status:"Trashed",resolutionTimestamp:i}}}async executeArchive(e,t){let r=await this.fsAdapter.readFile(e),i=this.frontmatterService.updateProperty(r,"archived","true");return i=this.frontmatterService.removeProperty(i,"aliases"),await this.fsAdapter.updateFile(e,i),{success:!0,command:t.command,filepath:t.filepath,action:"Archived task",changes:{archived:!0,aliasesRemoved:!0}}}async executeStatusUpdate(e,t,r,i){let s=await this.fsAdapter.readFile(e),a=this.frontmatterService.updateProperty(s,"ems__Effort_status",`"[[${r}]]"`);return await this.fsAdapter.updateFile(e,a),{success:!0,command:t.command,filepath:t.filepath,action:i,changes:{status:r.replace("ems__EffortStatus","")}}}async executeUpdateLabel(e,t){let r=t.options?.label;if(!r||r.trim()==="")return{success:!1,command:t.command,filepath:t.filepath,error:'Missing required option: "label"'};let i=r.trim(),s=await this.fsAdapter.readFile(e),a=this.frontmatterService.updateProperty(s,"exo__Asset_label",`"${i}"`);return await this.fsAdapter.updateFile(e,a),{success:!0,command:t.command,filepath:t.filepath,action:`Updated label to "${i}"`,changes:{label:i}}}async executeSchedule(e,t){let r=t.options?.date;if(!r)return{success:!1,command:t.command,filepath:t.filepath,error:'Missing required option: "date"'};if(!/^\d{4}-\d{2}-\d{2}$/.test(r))return{success:!1,command:t.command,filepath:t.filepath,error:`Invalid date format: ${r}. Expected YYYY-MM-DD`};let i=await this.fsAdapter.readFile(e),s=nu.DateFormatter.toTimestampAtStartOfDay(r),a=this.frontmatterService.updateProperty(i,"ems__Effort_scheduledTimestamp",s);return await this.fsAdapter.updateFile(e,a),{success:!0,command:t.command,filepath:t.filepath,action:`Scheduled for ${r}`,changes:{scheduledTimestamp:s}}}async executeSetDeadline(e,t){let r=t.options?.date;if(!r)return{success:!1,command:t.command,filepath:t.filepath,error:'Missing required option: "date"'};if(!/^\d{4}-\d{2}-\d{2}$/.test(r))return{success:!1,command:t.command,filepath:t.filepath,error:`Invalid date format: ${r}. Expected YYYY-MM-DD`};let i=await this.fsAdapter.readFile(e),s=nu.DateFormatter.toTimestampAtStartOfDay(r),a=this.frontmatterService.updateProperty(i,"ems__Effort_deadlineTimestamp",s);return await this.fsAdapter.updateFile(e,a),{success:!0,command:t.command,filepath:t.filepath,action:`Set deadline to ${r}`,changes:{deadlineTimestamp:s}}}async executeRepairFolder(e,t){let i=(await this.fsAdapter.getFileMetadata(e))?.exo__Asset_isDefinedBy;if(!i)return{success:!1,command:t.command,filepath:t.filepath,error:"Cannot determine expected folder: missing exo__Asset_isDefinedBy"};let s=this.extractReference(i);if(!s)return{success:!1,command:t.command,filepath:t.filepath,error:"Cannot determine expected folder: invalid exo__Asset_isDefinedBy format"};let a=await this.findReferencedFile(s,e);if(!a)return{success:!1,command:t.command,filepath:t.filepath,error:`Cannot determine expected folder: referenced asset not found: ${s}`};let c=nc.default.dirname(a),l=c==="."?"":c,u=nc.default.dirname(e),f=u==="."?"":u;if(this.normalizePath(f)===this.normalizePath(l))return{success:!0,command:t.command,filepath:t.filepath,action:"Already in correct folder",changes:{moved:!1,folder:l||"(root)"}};let d=nc.default.basename(e),h=l?`${l}/${d}`:d;return await this.fsAdapter.fileExists(h)?{success:!1,command:t.command,filepath:t.filepath,error:`Cannot move file: ${h} already exists`}:(await this.fsAdapter.renameFile(e,h),{success:!0,command:t.command,filepath:t.filepath,action:`Moved to ${l||"(root)"}`,changes:{moved:!0,oldPath:e,newPath:h,expectedFolder:l||"(root)"}})}extractReference(e){if(typeof e!="string")return null;let t=e.trim().replace(/^["']|["']$/g,"");return t=t.replace(/^\[\[|\]\]$/g,""),t||null}async findReferencedFile(e,t){let r=e.endsWith(".md")?e:`${e}.md`;if(e.includes("/")&&await this.fsAdapter.fileExists(r))return r;let i=nc.default.dirname(t),s=i!=="."?`${i}/${r}`:r;if(await this.fsAdapter.fileExists(s))return s;let c=await this.fsAdapter.findFileByUID(e);return c||(await this.fsAdapter.getMarkdownFiles()).find(f=>{let d=nc.default.basename(f,".md"),h=nc.default.basename(r,".md");return d===h})||null}normalizePath(e){return e.replace(/\\/g,"/").replace(/^\.\//,"").replace(/\/$/,"")}};function vY(n,e){if(n==="json"){let t=J.success(e,{durationMs:e.durationMs,itemCount:e.total});console.log(JSON.stringify(t,null,2))}else{if(console.log(`
|
|
624
624
|
\u{1F4E6} Batch Execution ${e.success?"Complete":"Failed"}`),console.log(` Total: ${e.total} operations`),console.log(` \u2705 Succeeded: ${e.succeeded}`),console.log(` \u274C Failed: ${e.failed}`),console.log(` \u23F1\uFE0F Duration: ${e.durationMs}ms`),e.atomic&&console.log(` \u{1F512} Atomic mode: ${e.rolledBack?"Rolled back":"Committed"}`),e.failed>0){console.log(`
|
|
625
625
|
\u{1F4CB} Failed Operations:`);for(let t of e.results.filter(r=>!r.success))console.log(` \u274C ${t.command} ${t.filepath}: ${t.error}`)}if(e.succeeded>0&&n==="text"){console.log(`
|
|
626
626
|
\u{1F4CB} Successful Operations:`);for(let t of e.results.filter(r=>r.success))console.log(` \u2705 ${t.command} ${t.filepath}: ${t.action}`)}}}o(vY,"outputResult");function OF(){return new xe("batch").description("Execute multiple operations in a single CLI invocation").option("--vault <path>","Path to Obsidian vault",process.cwd()).option("--input <json>","JSON array of operations to execute").option("--file <path>","Path to JSON file containing operations").option("--atomic","All-or-nothing execution (rollback on any failure)").option("--dry-run","Preview changes without modifying files").option("--format <type>","Output format: text|json (default: text)","text").action(async n=>{let e=n.format||"text";D.setFormat(e);try{let t=(0,fE.resolve)(n.vault),r;if(n.input)r=n.input;else if(n.file){let c=(0,fE.resolve)(n.file);if(!dE.default.existsSync(c))throw new Qe(`Batch file not found: ${c}`,"exocortex batch --file operations.json");r=await dE.default.readFile(c,"utf-8")}else throw new Qe("Either --input or --file option is required",`exocortex batch --input '[{"command":"start","filepath":"task.md"}]' or exocortex batch --file operations.json`);let i=ic.parseInput(r);if(i.length===0)throw new Qe("Batch input contains no operations",`exocortex batch --input '[{"command":"start","filepath":"task.md"}]'`);let a=await new ic(t,n.dryRun).executeBatch(i,n.atomic);vY(e,a),process.exit(a.success?0:5)}catch(t){D.handle(t)}})}o(OF,"batchCommand");var FF=require("path");function bY(n,e){if(n==="json"){let t=J.success(e,{durationMs:e.durationMs,itemCount:e.total});console.log(JSON.stringify(t,null,2))}else{if(console.log(`
|
|
@@ -794,7 +794,7 @@ exo__BacklinksTableBlock_columns:${f==="[]"?" []":f}
|
|
|
794
794
|
--- END PREVIEW ---
|
|
795
795
|
`)}o(xK,"printDryRunReport");function CK(n,e){let r=[`--- MIGRATION ${e.apply?"APPLY":"DRY RUN"} ---`,`Migrated pairs: ${n.pairs.length}`,`Skipped configs: ${n.skipped.length}`];e.apply&&(r.push(`Files written to: ${e.outDir}/ (relative to vault root)`),r.push(`Total files written: ${n.pairs.length*2} (${n.pairs.length} Layout + ${n.pairs.length} Block)`));for(let i of n.skipped)r.push(` SKIPPED: ${i.sourcePath} \u2014 ${i.reason}`);return r.push(`--- END SUMMARY ---
|
|
796
796
|
`),r.join(`
|
|
797
|
-
`)}o(CK,"formatSummary");function IK(n,e){return{mode:e.apply?"apply":"dry-run",outDir:e.outDir,pairsCount:n.pairs.length,skippedCount:n.skipped.length,pairs:n.pairs.map(t=>({sourceUid:t.sourceUid,sourcePath:t.sourcePath,layoutUid:t.layout.uid,blockUid:t.block.uid,warnings:t.warnings})),skipped:n.skipped}}o(IK,"summariseResult");function $D(n){n.addCommand(pO()),n.addCommand(_O()),n.addCommand(SO())}o($D,"addQuerySubcommands");function BD(n){let e=new xe;e.name("exocortex").description("CLI tool for Exocortex knowledge management system").version(n??"15.
|
|
797
|
+
`)}o(CK,"formatSummary");function IK(n,e){return{mode:e.apply?"apply":"dry-run",outDir:e.outDir,pairsCount:n.pairs.length,skippedCount:n.skipped.length,pairs:n.pairs.map(t=>({sourceUid:t.sourceUid,sourcePath:t.sourcePath,layoutUid:t.layout.uid,blockUid:t.block.uid,warnings:t.warnings})),skipped:n.skipped}}o(IK,"summariseResult");function $D(n){n.addCommand(pO()),n.addCommand(_O()),n.addCommand(SO())}o($D,"addQuerySubcommands");function BD(n){let e=new xe;e.name("exocortex").description("CLI tool for Exocortex knowledge management system").version(n??"15.149.0");let t=e.command("exoql").description("ExoQL query execution and cache management");$D(t);let r=e.command("sparql").description("(deprecated) Use 'exoql' instead");return $D(r),r.hook("preAction",()=>{console.error('\u26A0\uFE0F "sparql" is deprecated. Use "exoql" instead.')}),e.addCommand(CF()),e.addCommand(PF()),e.addCommand(OF()),e.addCommand(DF()),e.addCommand(NF()),e.addCommand($F()),e.addCommand(qF()),e.addCommand(rD()),e.addCommand(sD()),e.addCommand(uD()),e.addCommand(hD()),e.addCommand(pD()),e.addCommand(gD()),e.addCommand(CD()),e.addCommand(OD()),e.addCommand(jD()),e}o(BD,"createProgram");BD().parse();0&&(module.exports={createProgram});
|
|
798
798
|
/*! Bundled license information:
|
|
799
799
|
|
|
800
800
|
reflect-metadata/Reflect.js:
|
package/package.json
CHANGED