@agimon-ai/doompi-file-edit 0.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs +1 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs +1 -0
  5. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.cjs +1 -0
  6. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.mjs +1 -0
  7. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.cjs +1 -0
  8. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.mjs +1 -0
  9. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  10. package/dist/commandLine.cjs +2 -0
  11. package/dist/commandLine.cjs.map +1 -0
  12. package/dist/commandLine.mjs +2 -0
  13. package/dist/commandLine.mjs.map +1 -0
  14. package/dist/container/index.cjs +2 -0
  15. package/dist/container/index.cjs.map +1 -0
  16. package/dist/container/index.d.cts +7 -0
  17. package/dist/container/index.d.cts.map +1 -0
  18. package/dist/container/index.d.mts +7 -0
  19. package/dist/container/index.d.mts.map +1 -0
  20. package/dist/container/index.mjs +2 -0
  21. package/dist/container/index.mjs.map +1 -0
  22. package/dist/domain.d.cts +32 -0
  23. package/dist/domain.d.cts.map +1 -0
  24. package/dist/domain.d.mts +32 -0
  25. package/dist/domain.d.mts.map +1 -0
  26. package/dist/extension.cjs +2 -0
  27. package/dist/extension.cjs.map +1 -0
  28. package/dist/extension.d.cts +6 -0
  29. package/dist/extension.d.cts.map +1 -0
  30. package/dist/extension.d.mts +6 -0
  31. package/dist/extension.d.mts.map +1 -0
  32. package/dist/extension.mjs +2 -0
  33. package/dist/extension.mjs.map +1 -0
  34. package/dist/extensions/doom.cjs +2 -0
  35. package/dist/extensions/doom.cjs.map +1 -0
  36. package/dist/extensions/doom.d.cts +6 -0
  37. package/dist/extensions/doom.d.cts.map +1 -0
  38. package/dist/extensions/doom.d.mts +6 -0
  39. package/dist/extensions/doom.d.mts.map +1 -0
  40. package/dist/extensions/doom.mjs +2 -0
  41. package/dist/extensions/doom.mjs.map +1 -0
  42. package/dist/extensions/pi.cjs +1 -0
  43. package/dist/extensions/pi.d.cts +2 -0
  44. package/dist/extensions/pi.d.mts +2 -0
  45. package/dist/extensions/pi.mjs +1 -0
  46. package/dist/index.cjs +1 -0
  47. package/dist/index.d.cts +5 -0
  48. package/dist/index.d.mts +5 -0
  49. package/dist/index.mjs +1 -0
  50. package/dist/services/EditTracker/EditTracker.cjs +2 -0
  51. package/dist/services/EditTracker/EditTracker.cjs.map +1 -0
  52. package/dist/services/EditTracker/EditTracker.mjs +2 -0
  53. package/dist/services/EditTracker/EditTracker.mjs.map +1 -0
  54. package/dist/services/EditorConfigService/EditorConfigService.cjs +2 -0
  55. package/dist/services/EditorConfigService/EditorConfigService.cjs.map +1 -0
  56. package/dist/services/EditorConfigService/EditorConfigService.mjs +2 -0
  57. package/dist/services/EditorConfigService/EditorConfigService.mjs.map +1 -0
  58. package/dist/services/EditorLauncher/EditorLauncher.cjs +2 -0
  59. package/dist/services/EditorLauncher/EditorLauncher.cjs.map +1 -0
  60. package/dist/services/EditorLauncher/EditorLauncher.mjs +2 -0
  61. package/dist/services/EditorLauncher/EditorLauncher.mjs.map +1 -0
  62. package/dist/services/FileEditPaths/FileEditPaths.cjs +2 -0
  63. package/dist/services/FileEditPaths/FileEditPaths.cjs.map +1 -0
  64. package/dist/services/FileEditPaths/FileEditPaths.mjs +2 -0
  65. package/dist/services/FileEditPaths/FileEditPaths.mjs.map +1 -0
  66. package/dist/services/FileEditWorkflow/FileEditWorkflow.cjs +2 -0
  67. package/dist/services/FileEditWorkflow/FileEditWorkflow.cjs.map +1 -0
  68. package/dist/services/FileEditWorkflow/FileEditWorkflow.mjs +2 -0
  69. package/dist/services/FileEditWorkflow/FileEditWorkflow.mjs.map +1 -0
  70. package/dist/services/GitDiffService/GitDiffService.cjs +5 -0
  71. package/dist/services/GitDiffService/GitDiffService.cjs.map +1 -0
  72. package/dist/services/GitDiffService/GitDiffService.mjs +5 -0
  73. package/dist/services/GitDiffService/GitDiffService.mjs.map +1 -0
  74. package/dist/services/TimelineStore/TimelineStore.cjs +3 -0
  75. package/dist/services/TimelineStore/TimelineStore.cjs.map +1 -0
  76. package/dist/services/TimelineStore/TimelineStore.mjs +3 -0
  77. package/dist/services/TimelineStore/TimelineStore.mjs.map +1 -0
  78. package/dist/tui/fileEditOverlay.cjs +2 -0
  79. package/dist/tui/fileEditOverlay.cjs.map +1 -0
  80. package/dist/tui/fileEditOverlay.mjs +2 -0
  81. package/dist/tui/fileEditOverlay.mjs.map +1 -0
  82. package/dist/types.cjs +2 -0
  83. package/dist/types.cjs.map +1 -0
  84. package/dist/types.d.cts +13 -0
  85. package/dist/types.d.cts.map +1 -0
  86. package/dist/types.d.mts +13 -0
  87. package/dist/types.d.mts.map +1 -0
  88. package/dist/types.mjs +2 -0
  89. package/dist/types.mjs.map +1 -0
  90. package/package.json +83 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vuong Ngo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # @agimon-ai/doompi-file-edit
2
+
3
+ Pi session file edit timeline and external editor overlay.
4
+
5
+ A timeline of every file the session edited, plus an external editor handoff.
6
+
7
+ Part of [Doompi](https://www.npmjs.com/package/@agimon-ai/doompi), a coding agent that loads
8
+ only the skills and tools you name. This package is composed by Doompi and is not usually
9
+ installed on its own.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @agimon-ai/doompi-file-edit
15
+ ```
16
+
17
+ ## How it loads
18
+
19
+ Doompi loads this unconditionally. It is core, not a layer, so it does not appear in
20
+ `.doom/modes.yaml`: making it optional would only create broken configurations.
21
+
22
+ Extension entry: `@agimon-ai/doompi-file-edit/extensions/doom`
23
+
24
+ ## Leader
25
+
26
+ `SPC e f` opens session edits, once the extension is loaded.
27
+
28
+ ## Entry points
29
+
30
+ | Import |
31
+ | --------------------------------------------- |
32
+ | `@agimon-ai/doompi-file-edit` |
33
+ | `@agimon-ai/doompi-file-edit/extensions/pi` |
34
+ | `@agimon-ai/doompi-file-edit/extensions/doom` |
35
+
36
+ ## License
37
+
38
+ MIT
@@ -0,0 +1 @@
1
+ function e(e,t,n,r){var i=arguments.length,a=i<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,o;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)a=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}exports.default=e;
@@ -0,0 +1 @@
1
+ function e(e,t,n,r){var i=arguments.length,a=i<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,o;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)a=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}export{e as default};
@@ -0,0 +1 @@
1
+ function e(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}exports.default=e;
@@ -0,0 +1 @@
1
+ function e(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}export{e as default};
@@ -0,0 +1 @@
1
+ function e(e,t){return function(n,r){t(n,r,e)}}exports.default=e;
@@ -0,0 +1 @@
1
+ function e(e,t){return function(n,r){t(n,r,e)}}export{e as default};
@@ -0,0 +1 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));exports.__toESM=s;
@@ -0,0 +1,2 @@
1
+ function e(e){let t=[],n=``,r,i=!1;for(let a of e.trim())i?(n+=a,i=!1):a===`\\`&&r!==`single`?i=!0:a===`'`&&r!==`double`?r=r===`single`?void 0:`single`:a===`"`&&r!==`single`?r=r===`double`?void 0:`double`:/\s/u.test(a)&&r===void 0?(n&&t.push(n),n=``):n+=a;if(i&&(n+=`\\`),r!==void 0)throw Error(`Editor command has an unterminated quote`);if(n&&t.push(n),t.length===0)throw Error(`Editor command must not be empty`);return t}function t(t){for(let e of t.matchAll(/\{([^}]+)\}/gu))if(e[1]!==`file`&&e[1]!==`line`)throw Error(`Unsupported editor placeholder: {${e[1]}}`);e(t)}function n(n,r,i){t(n);let a=n.includes(`{file}`),o=e(n).map(e=>e.replaceAll(`{file}`,r).replaceAll(`{line}`,String(i)));return a||o.push(r),o}exports.expandEditorTemplate=n,exports.splitCommandLine=e,exports.validateEditorTemplate=t;
2
+ //# sourceMappingURL=commandLine.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandLine.cjs","names":[],"sources":["../src/commandLine.ts"],"sourcesContent":["export function splitCommandLine(template: string): string[] {\n const result: string[] = [];\n let current = '';\n let quote: 'single' | 'double' | undefined;\n let escaped = false;\n for (const character of template.trim()) {\n if (escaped) {\n current += character;\n escaped = false;\n } else if (character === '\\\\' && quote !== 'single') {\n escaped = true;\n } else if (character === \"'\" && quote !== 'double') {\n quote = quote === 'single' ? undefined : 'single';\n } else if (character === '\"' && quote !== 'single') {\n quote = quote === 'double' ? undefined : 'double';\n } else if (/\\s/u.test(character) && quote === undefined) {\n if (current) result.push(current);\n current = '';\n } else {\n current += character;\n }\n }\n if (escaped) current += '\\\\';\n if (quote !== undefined) throw new Error('Editor command has an unterminated quote');\n if (current) result.push(current);\n if (result.length === 0) throw new Error('Editor command must not be empty');\n return result;\n}\n\nexport function validateEditorTemplate(template: string): void {\n for (const match of template.matchAll(/\\{([^}]+)\\}/gu)) {\n if (match[1] !== 'file' && match[1] !== 'line') {\n throw new Error(`Unsupported editor placeholder: {${match[1]}}`);\n }\n }\n splitCommandLine(template);\n}\n\nexport function expandEditorTemplate(template: string, file: string, line: number): string[] {\n validateEditorTemplate(template);\n const hasFile = template.includes('{file}');\n const values = splitCommandLine(template).map((part) =>\n part.replaceAll('{file}', file).replaceAll('{line}', String(line)),\n );\n if (!hasFile) values.push(file);\n return values;\n}\n"],"mappings":"AAAA,SAAgB,EAAiB,EAA4B,CAC3D,IAAM,EAAmB,CAAC,EACtB,EAAU,GACV,EACA,EAAU,GACd,IAAK,IAAM,KAAa,EAAS,KAAK,EAChC,GACF,GAAW,EACX,EAAU,IACD,IAAc,MAAQ,IAAU,SACzC,EAAU,GACD,IAAc,KAAO,IAAU,SACxC,EAAQ,IAAU,SAAW,IAAA,GAAY,SAChC,IAAc,KAAO,IAAU,SACxC,EAAQ,IAAU,SAAW,IAAA,GAAY,SAChC,MAAM,KAAK,CAAS,GAAK,IAAU,IAAA,IACxC,GAAS,EAAO,KAAK,CAAO,EAChC,EAAU,IAEV,GAAW,EAIf,GADI,IAAS,GAAW,MACpB,IAAU,IAAA,GAAW,MAAU,MAAM,0CAA0C,EAEnF,GADI,GAAS,EAAO,KAAK,CAAO,EAC5B,EAAO,SAAW,EAAG,MAAU,MAAM,kCAAkC,EAC3E,OAAO,CACT,CAEA,SAAgB,EAAuB,EAAwB,CAC7D,IAAK,IAAM,KAAS,EAAS,SAAS,eAAe,EACnD,GAAI,EAAM,KAAO,QAAU,EAAM,KAAO,OACtC,MAAU,MAAM,oCAAoC,EAAM,GAAG,EAAE,EAGnE,EAAiB,CAAQ,CAC3B,CAEA,SAAgB,EAAqB,EAAkB,EAAc,EAAwB,CAC3F,EAAuB,CAAQ,EAC/B,IAAM,EAAU,EAAS,SAAS,QAAQ,EACpC,EAAS,EAAiB,CAAQ,CAAC,CAAC,IAAK,GAC7C,EAAK,WAAW,SAAU,CAAI,CAAC,CAAC,WAAW,SAAU,OAAO,CAAI,CAAC,CACnE,EAEA,OADK,GAAS,EAAO,KAAK,CAAI,EACvB,CACT"}
@@ -0,0 +1,2 @@
1
+ function e(e){let t=[],n=``,r,i=!1;for(let a of e.trim())i?(n+=a,i=!1):a===`\\`&&r!==`single`?i=!0:a===`'`&&r!==`double`?r=r===`single`?void 0:`single`:a===`"`&&r!==`single`?r=r===`double`?void 0:`double`:/\s/u.test(a)&&r===void 0?(n&&t.push(n),n=``):n+=a;if(i&&(n+=`\\`),r!==void 0)throw Error(`Editor command has an unterminated quote`);if(n&&t.push(n),t.length===0)throw Error(`Editor command must not be empty`);return t}function t(t){for(let e of t.matchAll(/\{([^}]+)\}/gu))if(e[1]!==`file`&&e[1]!==`line`)throw Error(`Unsupported editor placeholder: {${e[1]}}`);e(t)}function n(n,r,i){t(n);let a=n.includes(`{file}`),o=e(n).map(e=>e.replaceAll(`{file}`,r).replaceAll(`{line}`,String(i)));return a||o.push(r),o}export{n as expandEditorTemplate,e as splitCommandLine,t as validateEditorTemplate};
2
+ //# sourceMappingURL=commandLine.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandLine.mjs","names":[],"sources":["../src/commandLine.ts"],"sourcesContent":["export function splitCommandLine(template: string): string[] {\n const result: string[] = [];\n let current = '';\n let quote: 'single' | 'double' | undefined;\n let escaped = false;\n for (const character of template.trim()) {\n if (escaped) {\n current += character;\n escaped = false;\n } else if (character === '\\\\' && quote !== 'single') {\n escaped = true;\n } else if (character === \"'\" && quote !== 'double') {\n quote = quote === 'single' ? undefined : 'single';\n } else if (character === '\"' && quote !== 'single') {\n quote = quote === 'double' ? undefined : 'double';\n } else if (/\\s/u.test(character) && quote === undefined) {\n if (current) result.push(current);\n current = '';\n } else {\n current += character;\n }\n }\n if (escaped) current += '\\\\';\n if (quote !== undefined) throw new Error('Editor command has an unterminated quote');\n if (current) result.push(current);\n if (result.length === 0) throw new Error('Editor command must not be empty');\n return result;\n}\n\nexport function validateEditorTemplate(template: string): void {\n for (const match of template.matchAll(/\\{([^}]+)\\}/gu)) {\n if (match[1] !== 'file' && match[1] !== 'line') {\n throw new Error(`Unsupported editor placeholder: {${match[1]}}`);\n }\n }\n splitCommandLine(template);\n}\n\nexport function expandEditorTemplate(template: string, file: string, line: number): string[] {\n validateEditorTemplate(template);\n const hasFile = template.includes('{file}');\n const values = splitCommandLine(template).map((part) =>\n part.replaceAll('{file}', file).replaceAll('{line}', String(line)),\n );\n if (!hasFile) values.push(file);\n return values;\n}\n"],"mappings":"AAAA,SAAgB,EAAiB,EAA4B,CAC3D,IAAM,EAAmB,CAAC,EACtB,EAAU,GACV,EACA,EAAU,GACd,IAAK,IAAM,KAAa,EAAS,KAAK,EAChC,GACF,GAAW,EACX,EAAU,IACD,IAAc,MAAQ,IAAU,SACzC,EAAU,GACD,IAAc,KAAO,IAAU,SACxC,EAAQ,IAAU,SAAW,IAAA,GAAY,SAChC,IAAc,KAAO,IAAU,SACxC,EAAQ,IAAU,SAAW,IAAA,GAAY,SAChC,MAAM,KAAK,CAAS,GAAK,IAAU,IAAA,IACxC,GAAS,EAAO,KAAK,CAAO,EAChC,EAAU,IAEV,GAAW,EAIf,GADI,IAAS,GAAW,MACpB,IAAU,IAAA,GAAW,MAAU,MAAM,0CAA0C,EAEnF,GADI,GAAS,EAAO,KAAK,CAAO,EAC5B,EAAO,SAAW,EAAG,MAAU,MAAM,kCAAkC,EAC3E,OAAO,CACT,CAEA,SAAgB,EAAuB,EAAwB,CAC7D,IAAK,IAAM,KAAS,EAAS,SAAS,eAAe,EACnD,GAAI,EAAM,KAAO,QAAU,EAAM,KAAO,OACtC,MAAU,MAAM,oCAAoC,EAAM,GAAG,EAAE,EAGnE,EAAiB,CAAQ,CAC3B,CAEA,SAAgB,EAAqB,EAAkB,EAAc,EAAwB,CAC3F,EAAuB,CAAQ,EAC/B,IAAM,EAAU,EAAS,SAAS,QAAQ,EACpC,EAAS,EAAiB,CAAQ,CAAC,CAAC,IAAK,GAC7C,EAAK,WAAW,SAAU,CAAI,CAAC,CAAC,WAAW,SAAU,OAAO,CAAI,CAAC,CACnE,EAEA,OADK,GAAS,EAAO,KAAK,CAAI,EACvB,CACT"}
@@ -0,0 +1,2 @@
1
+ const e=require("../types.cjs"),t=require("../services/EditTracker/EditTracker.cjs"),n=require("../services/EditorConfigService/EditorConfigService.cjs"),r=require("../services/EditorLauncher/EditorLauncher.cjs"),i=require("../services/FileEditPaths/FileEditPaths.cjs"),a=require("../services/FileEditWorkflow/FileEditWorkflow.cjs"),o=require("../services/GitDiffService/GitDiffService.cjs"),s=require("../services/TimelineStore/TimelineStore.cjs");let c=require("inversify");require("reflect-metadata/lite");function l(){let l=new c.Container;return l.bind(e.FILE_EDIT_TYPES.Paths).to(i.FileEditPaths).inSingletonScope(),l.bind(e.FILE_EDIT_TYPES.TimelineStore).to(s.TimelineStore).inSingletonScope(),l.bind(e.FILE_EDIT_TYPES.GitDiffService).to(o.GitDiffService).inSingletonScope(),l.bind(e.FILE_EDIT_TYPES.EditTracker).to(t.EditTracker).inSingletonScope(),l.bind(e.FILE_EDIT_TYPES.EditorConfigService).to(n.EditorConfigService).inSingletonScope(),l.bind(e.FILE_EDIT_TYPES.EditorLauncher).to(r.EditorLauncher).inSingletonScope(),l.bind(e.FILE_EDIT_TYPES.Workflow).to(a.FileEditWorkflow).inSingletonScope(),l}exports.createFileEditContainer=l;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["Container","FILE_EDIT_TYPES","FileEditPaths","TimelineStore","GitDiffService","EditTracker","EditorConfigService","EditorLauncher","FileEditWorkflow"],"sources":["../../src/container/index.ts"],"sourcesContent":["import { Container } from 'inversify';\nimport 'reflect-metadata/lite';\nimport { EditTracker } from '../services/EditTracker/EditTracker.ts';\nimport type { IEditTracker } from '../services/EditTracker/types.ts';\nimport { EditorConfigService } from '../services/EditorConfigService/EditorConfigService.ts';\nimport type { IEditorConfigService } from '../services/EditorConfigService/types.ts';\nimport { EditorLauncher } from '../services/EditorLauncher/EditorLauncher.ts';\nimport type { IEditorLauncher } from '../services/EditorLauncher/types.ts';\nimport { FileEditPaths } from '../services/FileEditPaths/FileEditPaths.ts';\nimport type { IFileEditPaths } from '../services/FileEditPaths/types.ts';\nimport { FileEditWorkflow } from '../services/FileEditWorkflow/FileEditWorkflow.ts';\nimport type { IFileEditWorkflow } from '../services/FileEditWorkflow/types.ts';\nimport { GitDiffService } from '../services/GitDiffService/GitDiffService.ts';\nimport type { IGitDiffService } from '../services/GitDiffService/types.ts';\nimport { TimelineStore } from '../services/TimelineStore/TimelineStore.ts';\nimport type { ITimelineStore } from '../services/TimelineStore/types.ts';\nimport { FILE_EDIT_TYPES } from '../types.ts';\n\nexport function createFileEditContainer(): Container {\n const container = new Container();\n container.bind<IFileEditPaths>(FILE_EDIT_TYPES.Paths).to(FileEditPaths).inSingletonScope();\n container.bind<ITimelineStore>(FILE_EDIT_TYPES.TimelineStore).to(TimelineStore).inSingletonScope();\n container.bind<IGitDiffService>(FILE_EDIT_TYPES.GitDiffService).to(GitDiffService).inSingletonScope();\n container.bind<IEditTracker>(FILE_EDIT_TYPES.EditTracker).to(EditTracker).inSingletonScope();\n container.bind<IEditorConfigService>(FILE_EDIT_TYPES.EditorConfigService).to(EditorConfigService).inSingletonScope();\n container.bind<IEditorLauncher>(FILE_EDIT_TYPES.EditorLauncher).to(EditorLauncher).inSingletonScope();\n container.bind<IFileEditWorkflow>(FILE_EDIT_TYPES.Workflow).to(FileEditWorkflow).inSingletonScope();\n return container;\n}\n"],"mappings":"6fAkBA,SAAgB,GAAqC,CACnD,IAAM,EAAY,IAAIA,EAAAA,UAQtB,OAPA,EAAU,KAAqBC,EAAAA,gBAAgB,KAAK,CAAC,CAAC,GAAGC,EAAAA,aAAa,CAAC,CAAC,iBAAiB,EACzF,EAAU,KAAqBD,EAAAA,gBAAgB,aAAa,CAAC,CAAC,GAAGE,EAAAA,aAAa,CAAC,CAAC,iBAAiB,EACjG,EAAU,KAAsBF,EAAAA,gBAAgB,cAAc,CAAC,CAAC,GAAGG,EAAAA,cAAc,CAAC,CAAC,iBAAiB,EACpG,EAAU,KAAmBH,EAAAA,gBAAgB,WAAW,CAAC,CAAC,GAAGI,EAAAA,WAAW,CAAC,CAAC,iBAAiB,EAC3F,EAAU,KAA2BJ,EAAAA,gBAAgB,mBAAmB,CAAC,CAAC,GAAGK,EAAAA,mBAAmB,CAAC,CAAC,iBAAiB,EACnH,EAAU,KAAsBL,EAAAA,gBAAgB,cAAc,CAAC,CAAC,GAAGM,EAAAA,cAAc,CAAC,CAAC,iBAAiB,EACpG,EAAU,KAAwBN,EAAAA,gBAAgB,QAAQ,CAAC,CAAC,GAAGO,EAAAA,gBAAgB,CAAC,CAAC,iBAAiB,EAC3F,CACT"}
@@ -0,0 +1,7 @@
1
+ import { Container } from "inversify";
2
+ import "reflect-metadata/lite";
3
+ //#region src/container/index.d.ts
4
+ declare function createFileEditContainer(): Container;
5
+ //#endregion
6
+ export { createFileEditContainer };
7
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/container/index.ts"],"mappings":";;;iBAkBgB,2BAA2B"}
@@ -0,0 +1,7 @@
1
+ import { Container } from "inversify";
2
+ import "reflect-metadata/lite";
3
+ //#region src/container/index.d.ts
4
+ declare function createFileEditContainer(): Container;
5
+ //#endregion
6
+ export { createFileEditContainer };
7
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/container/index.ts"],"mappings":";;;iBAkBgB,2BAA2B"}
@@ -0,0 +1,2 @@
1
+ import{FILE_EDIT_TYPES as e}from"../types.mjs";import{EditTracker as t}from"../services/EditTracker/EditTracker.mjs";import{EditorConfigService as n}from"../services/EditorConfigService/EditorConfigService.mjs";import{EditorLauncher as r}from"../services/EditorLauncher/EditorLauncher.mjs";import{FileEditPaths as i}from"../services/FileEditPaths/FileEditPaths.mjs";import{FileEditWorkflow as a}from"../services/FileEditWorkflow/FileEditWorkflow.mjs";import{GitDiffService as o}from"../services/GitDiffService/GitDiffService.mjs";import{TimelineStore as s}from"../services/TimelineStore/TimelineStore.mjs";import{Container as c}from"inversify";import"reflect-metadata/lite";function l(){let l=new c;return l.bind(e.Paths).to(i).inSingletonScope(),l.bind(e.TimelineStore).to(s).inSingletonScope(),l.bind(e.GitDiffService).to(o).inSingletonScope(),l.bind(e.EditTracker).to(t).inSingletonScope(),l.bind(e.EditorConfigService).to(n).inSingletonScope(),l.bind(e.EditorLauncher).to(r).inSingletonScope(),l.bind(e.Workflow).to(a).inSingletonScope(),l}export{l as createFileEditContainer};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/container/index.ts"],"sourcesContent":["import { Container } from 'inversify';\nimport 'reflect-metadata/lite';\nimport { EditTracker } from '../services/EditTracker/EditTracker.ts';\nimport type { IEditTracker } from '../services/EditTracker/types.ts';\nimport { EditorConfigService } from '../services/EditorConfigService/EditorConfigService.ts';\nimport type { IEditorConfigService } from '../services/EditorConfigService/types.ts';\nimport { EditorLauncher } from '../services/EditorLauncher/EditorLauncher.ts';\nimport type { IEditorLauncher } from '../services/EditorLauncher/types.ts';\nimport { FileEditPaths } from '../services/FileEditPaths/FileEditPaths.ts';\nimport type { IFileEditPaths } from '../services/FileEditPaths/types.ts';\nimport { FileEditWorkflow } from '../services/FileEditWorkflow/FileEditWorkflow.ts';\nimport type { IFileEditWorkflow } from '../services/FileEditWorkflow/types.ts';\nimport { GitDiffService } from '../services/GitDiffService/GitDiffService.ts';\nimport type { IGitDiffService } from '../services/GitDiffService/types.ts';\nimport { TimelineStore } from '../services/TimelineStore/TimelineStore.ts';\nimport type { ITimelineStore } from '../services/TimelineStore/types.ts';\nimport { FILE_EDIT_TYPES } from '../types.ts';\n\nexport function createFileEditContainer(): Container {\n const container = new Container();\n container.bind<IFileEditPaths>(FILE_EDIT_TYPES.Paths).to(FileEditPaths).inSingletonScope();\n container.bind<ITimelineStore>(FILE_EDIT_TYPES.TimelineStore).to(TimelineStore).inSingletonScope();\n container.bind<IGitDiffService>(FILE_EDIT_TYPES.GitDiffService).to(GitDiffService).inSingletonScope();\n container.bind<IEditTracker>(FILE_EDIT_TYPES.EditTracker).to(EditTracker).inSingletonScope();\n container.bind<IEditorConfigService>(FILE_EDIT_TYPES.EditorConfigService).to(EditorConfigService).inSingletonScope();\n container.bind<IEditorLauncher>(FILE_EDIT_TYPES.EditorLauncher).to(EditorLauncher).inSingletonScope();\n container.bind<IFileEditWorkflow>(FILE_EDIT_TYPES.Workflow).to(FileEditWorkflow).inSingletonScope();\n return container;\n}\n"],"mappings":"kqBAkBA,SAAgB,GAAqC,CACnD,IAAM,EAAY,IAAI,EAQtB,OAPA,EAAU,KAAqB,EAAgB,KAAK,CAAC,CAAC,GAAG,CAAa,CAAC,CAAC,iBAAiB,EACzF,EAAU,KAAqB,EAAgB,aAAa,CAAC,CAAC,GAAG,CAAa,CAAC,CAAC,iBAAiB,EACjG,EAAU,KAAsB,EAAgB,cAAc,CAAC,CAAC,GAAG,CAAc,CAAC,CAAC,iBAAiB,EACpG,EAAU,KAAmB,EAAgB,WAAW,CAAC,CAAC,GAAG,CAAW,CAAC,CAAC,iBAAiB,EAC3F,EAAU,KAA2B,EAAgB,mBAAmB,CAAC,CAAC,GAAG,CAAmB,CAAC,CAAC,iBAAiB,EACnH,EAAU,KAAsB,EAAgB,cAAc,CAAC,CAAC,GAAG,CAAc,CAAC,CAAC,iBAAiB,EACpG,EAAU,KAAwB,EAAgB,QAAQ,CAAC,CAAC,GAAG,CAAgB,CAAC,CAAC,iBAAiB,EAC3F,CACT"}
@@ -0,0 +1,32 @@
1
+ //#region src/domain.d.ts
2
+ type FileEditTool = 'edit' | 'write' | 'bash';
3
+ type FileEditState = 'modified' | 'added' | 'deleted' | 'unchanged' | 'binary' | 'external';
4
+ interface TimelineEvent {
5
+ version: 1;
6
+ path: string;
7
+ tool: FileEditTool;
8
+ at: number;
9
+ }
10
+ interface FileEditEntry {
11
+ path: string;
12
+ tool: FileEditTool;
13
+ at: number;
14
+ count: number;
15
+ }
16
+ interface FileDiff {
17
+ path: string;
18
+ state: FileEditState;
19
+ lines: string[];
20
+ additions: number;
21
+ removals: number;
22
+ tracked: boolean;
23
+ truncated: boolean;
24
+ suggestedLine: number;
25
+ }
26
+ interface ResolvedEditor {
27
+ template: string;
28
+ source: 'configured' | 'VISUAL' | 'EDITOR' | 'fallback';
29
+ }
30
+ //#endregion
31
+ export { FileDiff, FileEditEntry, FileEditState, FileEditTool, ResolvedEditor, TimelineEvent };
32
+ //# sourceMappingURL=domain.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.d.cts","names":[],"sources":["../src/domain.ts"],"mappings":";KAAY;KACA;UAEK;EACf;EACA;EACA,MAAM;EACN;;UAGe;EACf;EACA,MAAM;EACN;EACA;;UAGe;EACf;EACA,OAAO;EACP;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA"}
@@ -0,0 +1,32 @@
1
+ //#region src/domain.d.ts
2
+ type FileEditTool = 'edit' | 'write' | 'bash';
3
+ type FileEditState = 'modified' | 'added' | 'deleted' | 'unchanged' | 'binary' | 'external';
4
+ interface TimelineEvent {
5
+ version: 1;
6
+ path: string;
7
+ tool: FileEditTool;
8
+ at: number;
9
+ }
10
+ interface FileEditEntry {
11
+ path: string;
12
+ tool: FileEditTool;
13
+ at: number;
14
+ count: number;
15
+ }
16
+ interface FileDiff {
17
+ path: string;
18
+ state: FileEditState;
19
+ lines: string[];
20
+ additions: number;
21
+ removals: number;
22
+ tracked: boolean;
23
+ truncated: boolean;
24
+ suggestedLine: number;
25
+ }
26
+ interface ResolvedEditor {
27
+ template: string;
28
+ source: 'configured' | 'VISUAL' | 'EDITOR' | 'fallback';
29
+ }
30
+ //#endregion
31
+ export { FileDiff, FileEditEntry, FileEditState, FileEditTool, ResolvedEditor, TimelineEvent };
32
+ //# sourceMappingURL=domain.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.d.mts","names":[],"sources":["../src/domain.ts"],"mappings":";KAAY;KACA;UAEK;EACf;EACA;EACA,MAAM;EACN;;UAGe;EACf;EACA,MAAM;EACN;EACA;;UAGe;EACf;EACA,OAAO;EACP;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA"}
@@ -0,0 +1,2 @@
1
+ const e=require("./types.cjs"),t=require("./container/index.cjs");let n=require("@agimon-ai/doompi-extension-contracts/child-process"),r=require("@agimon-ai/doompi-telemetry");const i=`file-edits`,a={source:`@agimon-ai/doompi-file-edit`,bindings:[{id:`editor.files`,path:[{key:`e`,label:`extension`,order:50},{key:`f`,label:`files`,detail:`session edits`}],command:{name:i}}]};function o(a){let o=t.createFileEditContainer(),s=o.get(e.FILE_EDIT_TYPES.Paths),c=o.get(e.FILE_EDIT_TYPES.TimelineStore),l=o.get(e.FILE_EDIT_TYPES.EditTracker),u=o.get(e.FILE_EDIT_TYPES.Workflow),d=(0,r.createDoomTelemetry)({serviceName:`doom-file-edit`,packageName:`@agimon-ai/doompi-file-edit`,env:process.env,enableLogs:!0,enableTraces:!0}),f=new Map;a.registerCommand(i,{description:`Review files edited in this session`,handler:async(e,t)=>{if(!t.hasUI){t.ui.notify(`/file-edits requires interactive mode`,`error`);return}await u.open(t)}}),a.on(`session_start`,async(e,t)=>{let n=s.sessionKey(t.sessionManager.getSessionId());c.initialize(s.timelinePath(t.cwd,n)),d.recordEvent(`doom_file_edit.timeline_started`,{outcome:`initialized`})}),a.on(`tool_execution_start`,async(e,t)=>{f.set(e.toolCallId,Date.now()),await l.start(e.toolCallId,e.toolName,e.args,t.cwd),d.recordEvent(`doom_file_edit.edit_started`,{"tool.name":e.toolName})}),a.on(`tool_execution_end`,async e=>{let t=f.get(e.toolCallId);f.delete(e.toolCallId),await l.end(e.toolCallId,e.isError),d.recordEvent(`doom_file_edit.edit_finished`,{"tool.name":e.toolName,"tool.result.error":e.isError,...t===void 0?{}:{duration_ms:Date.now()-t},outcome:e.isError?`failed`:`completed`})}),a.on(`session_shutdown`,async()=>{process.env[n.SUBAGENT_CHILD_ENV]||await c.clear(),await d.recordEvent(`doom_file_edit.timeline_finished`,{outcome:`closed`}),await d.shutdown()})}exports.FILE_EDIT_LEADER_CONTRIBUTION=a,exports.fileEditExtension=o;
2
+ //# sourceMappingURL=extension.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.cjs","names":["createFileEditContainer","FILE_EDIT_TYPES","createDoomTelemetry","SUBAGENT_CHILD_ENV"],"sources":["../src/extension.ts"],"sourcesContent":["import { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport { SUBAGENT_CHILD_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { createFileEditContainer } from './container/index.ts';\nimport type { IEditTracker } from './services/EditTracker/types.ts';\nimport type { IFileEditPaths } from './services/FileEditPaths/types.ts';\nimport type { IFileEditWorkflow } from './services/FileEditWorkflow/types.ts';\nimport type { ITimelineStore } from './services/TimelineStore/types.ts';\nimport { FILE_EDIT_TYPES } from './types.ts';\n\nconst FILES_COMMAND = 'file-edits';\n\nexport const FILE_EDIT_LEADER_CONTRIBUTION = {\n source: '@agimon-ai/doompi-file-edit',\n bindings: [\n {\n id: 'editor.files',\n path: [\n { key: 'e', label: 'extension', order: 50 },\n { key: 'f', label: 'files', detail: 'session edits' },\n ],\n command: { name: FILES_COMMAND },\n },\n ],\n} as const;\n\nexport function fileEditExtension(pi: ExtensionAPI): void {\n const container = createFileEditContainer();\n const paths = container.get<IFileEditPaths>(FILE_EDIT_TYPES.Paths);\n const timeline = container.get<ITimelineStore>(FILE_EDIT_TYPES.TimelineStore);\n const tracker = container.get<IEditTracker>(FILE_EDIT_TYPES.EditTracker);\n const workflow = container.get<IFileEditWorkflow>(FILE_EDIT_TYPES.Workflow);\n const telemetry: DoomTelemetry = createDoomTelemetry({\n serviceName: 'doom-file-edit',\n packageName: '@agimon-ai/doompi-file-edit',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const toolStartedAt = new Map<string, number>();\n\n pi.registerCommand(FILES_COMMAND, {\n description: 'Review files edited in this session',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI) {\n ctx.ui.notify('/file-edits requires interactive mode', 'error');\n return;\n }\n await workflow.open(ctx);\n },\n });\n\n pi.on('session_start', async (_event, ctx) => {\n const sessionKey = paths.sessionKey(ctx.sessionManager.getSessionId());\n timeline.initialize(paths.timelinePath(ctx.cwd, sessionKey));\n void telemetry.recordEvent('doom_file_edit.timeline_started', { outcome: 'initialized' });\n });\n pi.on('tool_execution_start', async (event, ctx) => {\n toolStartedAt.set(event.toolCallId, Date.now());\n await tracker.start(event.toolCallId, event.toolName, event.args, ctx.cwd);\n void telemetry.recordEvent('doom_file_edit.edit_started', { 'tool.name': event.toolName });\n });\n pi.on('tool_execution_end', async (event) => {\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n await tracker.end(event.toolCallId, event.isError);\n void telemetry.recordEvent('doom_file_edit.edit_finished', {\n 'tool.name': event.toolName,\n 'tool.result.error': event.isError,\n ...(startedAt === undefined ? {} : { duration_ms: Date.now() - startedAt }),\n outcome: event.isError ? 'failed' : 'completed',\n });\n });\n pi.on('session_shutdown', async () => {\n if (!process.env[SUBAGENT_CHILD_ENV]) await timeline.clear();\n await telemetry.recordEvent('doom_file_edit.timeline_finished', { outcome: 'closed' });\n await telemetry.shutdown();\n });\n}\n"],"mappings":"gLAUA,MAAM,EAAgB,aAET,EAAgC,CAC3C,OAAQ,8BACR,SAAU,CACR,CACE,GAAI,eACJ,KAAM,CACJ,CAAE,IAAK,IAAK,MAAO,YAAa,MAAO,EAAG,EAC1C,CAAE,IAAK,IAAK,MAAO,QAAS,OAAQ,eAAgB,CACtD,EACA,QAAS,CAAE,KAAM,CAAc,CACjC,CACF,CACF,EAEA,SAAgB,EAAkB,EAAwB,CACxD,IAAM,EAAYA,EAAAA,wBAAwB,EACpC,EAAQ,EAAU,IAAoBC,EAAAA,gBAAgB,KAAK,EAC3D,EAAW,EAAU,IAAoBA,EAAAA,gBAAgB,aAAa,EACtE,EAAU,EAAU,IAAkBA,EAAAA,gBAAgB,WAAW,EACjE,EAAW,EAAU,IAAuBA,EAAAA,gBAAgB,QAAQ,EACpE,GAAA,EAA2BC,EAAAA,oBAAAA,CAAoB,CACnD,YAAa,iBACb,YAAa,8BACb,IAAK,QAAQ,IACb,WAAY,GACZ,aAAc,EAChB,CAAC,EACK,EAAgB,IAAI,IAE1B,EAAG,gBAAgB,EAAe,CAChC,YAAa,sCACb,QAAS,MAAO,EAAO,IAAQ,CAC7B,GAAI,CAAC,EAAI,MAAO,CACd,EAAI,GAAG,OAAO,wCAAyC,OAAO,EAC9D,MACF,CACA,MAAM,EAAS,KAAK,CAAG,CACzB,CACF,CAAC,EAED,EAAG,GAAG,gBAAiB,MAAO,EAAQ,IAAQ,CAC5C,IAAM,EAAa,EAAM,WAAW,EAAI,eAAe,aAAa,CAAC,EACrE,EAAS,WAAW,EAAM,aAAa,EAAI,IAAK,CAAU,CAAC,EAC3D,EAAe,YAAY,kCAAmC,CAAE,QAAS,aAAc,CAAC,CAC1F,CAAC,EACD,EAAG,GAAG,uBAAwB,MAAO,EAAO,IAAQ,CAClD,EAAc,IAAI,EAAM,WAAY,KAAK,IAAI,CAAC,EAC9C,MAAM,EAAQ,MAAM,EAAM,WAAY,EAAM,SAAU,EAAM,KAAM,EAAI,GAAG,EACzE,EAAe,YAAY,8BAA+B,CAAE,YAAa,EAAM,QAAS,CAAC,CAC3F,CAAC,EACD,EAAG,GAAG,qBAAsB,KAAO,IAAU,CAC3C,IAAM,EAAY,EAAc,IAAI,EAAM,UAAU,EACpD,EAAc,OAAO,EAAM,UAAU,EACrC,MAAM,EAAQ,IAAI,EAAM,WAAY,EAAM,OAAO,EACjD,EAAe,YAAY,+BAAgC,CACzD,YAAa,EAAM,SACnB,oBAAqB,EAAM,QAC3B,GAAI,IAAc,IAAA,GAAY,CAAC,EAAI,CAAE,YAAa,KAAK,IAAI,EAAI,CAAU,EACzE,QAAS,EAAM,QAAU,SAAW,WACtC,CAAC,CACH,CAAC,EACD,EAAG,GAAG,mBAAoB,SAAY,CAC/B,QAAQ,IAAIC,EAAAA,qBAAqB,MAAM,EAAS,MAAM,EAC3D,MAAM,EAAU,YAAY,mCAAoC,CAAE,QAAS,QAAS,CAAC,EACrF,MAAM,EAAU,SAAS,CAC3B,CAAC,CACH"}
@@ -0,0 +1,6 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ //#region src/extension.d.ts
3
+ declare function fileEditExtension(pi: ExtensionAPI): void;
4
+ //#endregion
5
+ export { fileEditExtension };
6
+ //# sourceMappingURL=extension.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.d.cts","names":[],"sources":["../src/extension.ts"],"mappings":";;iBA0BgB,kBAAkB,IAAI"}
@@ -0,0 +1,6 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ //#region src/extension.d.ts
3
+ declare function fileEditExtension(pi: ExtensionAPI): void;
4
+ //#endregion
5
+ export { fileEditExtension };
6
+ //# sourceMappingURL=extension.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.d.mts","names":[],"sources":["../src/extension.ts"],"mappings":";;iBA0BgB,kBAAkB,IAAI"}
@@ -0,0 +1,2 @@
1
+ import{FILE_EDIT_TYPES as e}from"./types.mjs";import{createFileEditContainer as t}from"./container/index.mjs";import{SUBAGENT_CHILD_ENV as n}from"@agimon-ai/doompi-extension-contracts/child-process";import{createDoomTelemetry as r}from"@agimon-ai/doompi-telemetry";const i=`file-edits`,a={source:`@agimon-ai/doompi-file-edit`,bindings:[{id:`editor.files`,path:[{key:`e`,label:`extension`,order:50},{key:`f`,label:`files`,detail:`session edits`}],command:{name:i}}]};function o(a){let o=t(),s=o.get(e.Paths),c=o.get(e.TimelineStore),l=o.get(e.EditTracker),u=o.get(e.Workflow),d=r({serviceName:`doom-file-edit`,packageName:`@agimon-ai/doompi-file-edit`,env:process.env,enableLogs:!0,enableTraces:!0}),f=new Map;a.registerCommand(i,{description:`Review files edited in this session`,handler:async(e,t)=>{if(!t.hasUI){t.ui.notify(`/file-edits requires interactive mode`,`error`);return}await u.open(t)}}),a.on(`session_start`,async(e,t)=>{let n=s.sessionKey(t.sessionManager.getSessionId());c.initialize(s.timelinePath(t.cwd,n)),d.recordEvent(`doom_file_edit.timeline_started`,{outcome:`initialized`})}),a.on(`tool_execution_start`,async(e,t)=>{f.set(e.toolCallId,Date.now()),await l.start(e.toolCallId,e.toolName,e.args,t.cwd),d.recordEvent(`doom_file_edit.edit_started`,{"tool.name":e.toolName})}),a.on(`tool_execution_end`,async e=>{let t=f.get(e.toolCallId);f.delete(e.toolCallId),await l.end(e.toolCallId,e.isError),d.recordEvent(`doom_file_edit.edit_finished`,{"tool.name":e.toolName,"tool.result.error":e.isError,...t===void 0?{}:{duration_ms:Date.now()-t},outcome:e.isError?`failed`:`completed`})}),a.on(`session_shutdown`,async()=>{process.env[n]||await c.clear(),await d.recordEvent(`doom_file_edit.timeline_finished`,{outcome:`closed`}),await d.shutdown()})}export{a as FILE_EDIT_LEADER_CONTRIBUTION,o as fileEditExtension};
2
+ //# sourceMappingURL=extension.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.mjs","names":[],"sources":["../src/extension.ts"],"sourcesContent":["import { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport { SUBAGENT_CHILD_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { createFileEditContainer } from './container/index.ts';\nimport type { IEditTracker } from './services/EditTracker/types.ts';\nimport type { IFileEditPaths } from './services/FileEditPaths/types.ts';\nimport type { IFileEditWorkflow } from './services/FileEditWorkflow/types.ts';\nimport type { ITimelineStore } from './services/TimelineStore/types.ts';\nimport { FILE_EDIT_TYPES } from './types.ts';\n\nconst FILES_COMMAND = 'file-edits';\n\nexport const FILE_EDIT_LEADER_CONTRIBUTION = {\n source: '@agimon-ai/doompi-file-edit',\n bindings: [\n {\n id: 'editor.files',\n path: [\n { key: 'e', label: 'extension', order: 50 },\n { key: 'f', label: 'files', detail: 'session edits' },\n ],\n command: { name: FILES_COMMAND },\n },\n ],\n} as const;\n\nexport function fileEditExtension(pi: ExtensionAPI): void {\n const container = createFileEditContainer();\n const paths = container.get<IFileEditPaths>(FILE_EDIT_TYPES.Paths);\n const timeline = container.get<ITimelineStore>(FILE_EDIT_TYPES.TimelineStore);\n const tracker = container.get<IEditTracker>(FILE_EDIT_TYPES.EditTracker);\n const workflow = container.get<IFileEditWorkflow>(FILE_EDIT_TYPES.Workflow);\n const telemetry: DoomTelemetry = createDoomTelemetry({\n serviceName: 'doom-file-edit',\n packageName: '@agimon-ai/doompi-file-edit',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const toolStartedAt = new Map<string, number>();\n\n pi.registerCommand(FILES_COMMAND, {\n description: 'Review files edited in this session',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI) {\n ctx.ui.notify('/file-edits requires interactive mode', 'error');\n return;\n }\n await workflow.open(ctx);\n },\n });\n\n pi.on('session_start', async (_event, ctx) => {\n const sessionKey = paths.sessionKey(ctx.sessionManager.getSessionId());\n timeline.initialize(paths.timelinePath(ctx.cwd, sessionKey));\n void telemetry.recordEvent('doom_file_edit.timeline_started', { outcome: 'initialized' });\n });\n pi.on('tool_execution_start', async (event, ctx) => {\n toolStartedAt.set(event.toolCallId, Date.now());\n await tracker.start(event.toolCallId, event.toolName, event.args, ctx.cwd);\n void telemetry.recordEvent('doom_file_edit.edit_started', { 'tool.name': event.toolName });\n });\n pi.on('tool_execution_end', async (event) => {\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n await tracker.end(event.toolCallId, event.isError);\n void telemetry.recordEvent('doom_file_edit.edit_finished', {\n 'tool.name': event.toolName,\n 'tool.result.error': event.isError,\n ...(startedAt === undefined ? {} : { duration_ms: Date.now() - startedAt }),\n outcome: event.isError ? 'failed' : 'completed',\n });\n });\n pi.on('session_shutdown', async () => {\n if (!process.env[SUBAGENT_CHILD_ENV]) await timeline.clear();\n await telemetry.recordEvent('doom_file_edit.timeline_finished', { outcome: 'closed' });\n await telemetry.shutdown();\n });\n}\n"],"mappings":"yQAUA,MAAM,EAAgB,aAET,EAAgC,CAC3C,OAAQ,8BACR,SAAU,CACR,CACE,GAAI,eACJ,KAAM,CACJ,CAAE,IAAK,IAAK,MAAO,YAAa,MAAO,EAAG,EAC1C,CAAE,IAAK,IAAK,MAAO,QAAS,OAAQ,eAAgB,CACtD,EACA,QAAS,CAAE,KAAM,CAAc,CACjC,CACF,CACF,EAEA,SAAgB,EAAkB,EAAwB,CACxD,IAAM,EAAY,EAAwB,EACpC,EAAQ,EAAU,IAAoB,EAAgB,KAAK,EAC3D,EAAW,EAAU,IAAoB,EAAgB,aAAa,EACtE,EAAU,EAAU,IAAkB,EAAgB,WAAW,EACjE,EAAW,EAAU,IAAuB,EAAgB,QAAQ,EACpE,EAA2B,EAAoB,CACnD,YAAa,iBACb,YAAa,8BACb,IAAK,QAAQ,IACb,WAAY,GACZ,aAAc,EAChB,CAAC,EACK,EAAgB,IAAI,IAE1B,EAAG,gBAAgB,EAAe,CAChC,YAAa,sCACb,QAAS,MAAO,EAAO,IAAQ,CAC7B,GAAI,CAAC,EAAI,MAAO,CACd,EAAI,GAAG,OAAO,wCAAyC,OAAO,EAC9D,MACF,CACA,MAAM,EAAS,KAAK,CAAG,CACzB,CACF,CAAC,EAED,EAAG,GAAG,gBAAiB,MAAO,EAAQ,IAAQ,CAC5C,IAAM,EAAa,EAAM,WAAW,EAAI,eAAe,aAAa,CAAC,EACrE,EAAS,WAAW,EAAM,aAAa,EAAI,IAAK,CAAU,CAAC,EAC3D,EAAe,YAAY,kCAAmC,CAAE,QAAS,aAAc,CAAC,CAC1F,CAAC,EACD,EAAG,GAAG,uBAAwB,MAAO,EAAO,IAAQ,CAClD,EAAc,IAAI,EAAM,WAAY,KAAK,IAAI,CAAC,EAC9C,MAAM,EAAQ,MAAM,EAAM,WAAY,EAAM,SAAU,EAAM,KAAM,EAAI,GAAG,EACzE,EAAe,YAAY,8BAA+B,CAAE,YAAa,EAAM,QAAS,CAAC,CAC3F,CAAC,EACD,EAAG,GAAG,qBAAsB,KAAO,IAAU,CAC3C,IAAM,EAAY,EAAc,IAAI,EAAM,UAAU,EACpD,EAAc,OAAO,EAAM,UAAU,EACrC,MAAM,EAAQ,IAAI,EAAM,WAAY,EAAM,OAAO,EACjD,EAAe,YAAY,+BAAgC,CACzD,YAAa,EAAM,SACnB,oBAAqB,EAAM,QAC3B,GAAI,IAAc,IAAA,GAAY,CAAC,EAAI,CAAE,YAAa,KAAK,IAAI,EAAI,CAAU,EACzE,QAAS,EAAM,QAAU,SAAW,WACtC,CAAC,CACH,CAAC,EACD,EAAG,GAAG,mBAAoB,SAAY,CAC/B,QAAQ,IAAI,IAAqB,MAAM,EAAS,MAAM,EAC3D,MAAM,EAAU,YAAY,mCAAoC,CAAE,QAAS,QAAS,CAAC,EACrF,MAAM,EAAU,SAAS,CAC3B,CAAC,CACH"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../extension.cjs");let t=require("@agimon-ai/doompi-ui/leader");function n(n){e.fileEditExtension(n),(0,t.registerDoomLeaderContribution)(n,e.FILE_EDIT_LEADER_CONTRIBUTION)}exports.default=n,exports.doomFileEditExtension=n;
2
+ //# sourceMappingURL=doom.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doom.cjs","names":["FILE_EDIT_LEADER_CONTRIBUTION"],"sources":["../../src/extensions/doom.ts"],"sourcesContent":["import { registerDoomLeaderContribution, type DoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { fileEditExtension, FILE_EDIT_LEADER_CONTRIBUTION } from '../extension.ts';\n\nexport function doomFileEditExtension(pi: ExtensionAPI): void {\n fileEditExtension(pi);\n registerDoomLeaderContribution(pi, FILE_EDIT_LEADER_CONTRIBUTION satisfies DoomLeaderContribution);\n}\n\nexport { doomFileEditExtension as default };\n"],"mappings":"gLAIA,SAAgB,EAAsB,EAAwB,CAC5D,EAAA,kBAAkB,CAAE,GACpB,EAAA,EAAA,+BAAA,CAA+B,EAAIA,EAAAA,6BAA8D,CACnG"}
@@ -0,0 +1,6 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ //#region src/extensions/doom.d.ts
3
+ declare function doomFileEditExtension(pi: ExtensionAPI): void;
4
+ //#endregion
5
+ export { doomFileEditExtension as default, doomFileEditExtension };
6
+ //# sourceMappingURL=doom.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doom.d.cts","names":[],"sources":["../../src/extensions/doom.ts"],"mappings":";;iBAIgB,sBAAsB,IAAI"}
@@ -0,0 +1,6 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ //#region src/extensions/doom.d.ts
3
+ declare function doomFileEditExtension(pi: ExtensionAPI): void;
4
+ //#endregion
5
+ export { doomFileEditExtension as default, doomFileEditExtension };
6
+ //# sourceMappingURL=doom.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doom.d.mts","names":[],"sources":["../../src/extensions/doom.ts"],"mappings":";;iBAIgB,sBAAsB,IAAI"}
@@ -0,0 +1,2 @@
1
+ import{FILE_EDIT_LEADER_CONTRIBUTION as e,fileEditExtension as t}from"../extension.mjs";import{registerDoomLeaderContribution as n}from"@agimon-ai/doompi-ui/leader";function r(r){t(r),n(r,e)}export{r as default,r as doomFileEditExtension};
2
+ //# sourceMappingURL=doom.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doom.mjs","names":[],"sources":["../../src/extensions/doom.ts"],"sourcesContent":["import { registerDoomLeaderContribution, type DoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { fileEditExtension, FILE_EDIT_LEADER_CONTRIBUTION } from '../extension.ts';\n\nexport function doomFileEditExtension(pi: ExtensionAPI): void {\n fileEditExtension(pi);\n registerDoomLeaderContribution(pi, FILE_EDIT_LEADER_CONTRIBUTION satisfies DoomLeaderContribution);\n}\n\nexport { doomFileEditExtension as default };\n"],"mappings":"qKAIA,SAAgB,EAAsB,EAAwB,CAC5D,EAAkB,CAAE,EACpB,EAA+B,EAAI,CAA8D,CACnG"}
@@ -0,0 +1 @@
1
+ const e=require("../extension.cjs");module.exports=e.fileEditExtension;
@@ -0,0 +1,2 @@
1
+ import { fileEditExtension } from "../extension.cjs";
2
+ export = fileEditExtension;
@@ -0,0 +1,2 @@
1
+ import { fileEditExtension } from "../extension.mjs";
2
+ export { fileEditExtension as default };
@@ -0,0 +1 @@
1
+ import{fileEditExtension as e}from"../extension.mjs";export{e as default};
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./types.cjs"),t=require("./container/index.cjs"),n=require("./extension.cjs");exports.FILE_EDIT_TYPES=e.FILE_EDIT_TYPES,exports.createFileEditContainer=t.createFileEditContainer,exports.fileEditExtension=n.fileEditExtension;
@@ -0,0 +1,5 @@
1
+ import { fileEditExtension } from "./extension.cjs";
2
+ import { createFileEditContainer } from "./container/index.cjs";
3
+ import { FileDiff, FileEditEntry, FileEditState, FileEditTool, ResolvedEditor, TimelineEvent } from "./domain.cjs";
4
+ import { FILE_EDIT_TYPES } from "./types.cjs";
5
+ export { FILE_EDIT_TYPES, FileDiff, FileEditEntry, FileEditState, FileEditTool, ResolvedEditor, TimelineEvent, createFileEditContainer, fileEditExtension };
@@ -0,0 +1,5 @@
1
+ import { fileEditExtension } from "./extension.mjs";
2
+ import { createFileEditContainer } from "./container/index.mjs";
3
+ import { FileDiff, FileEditEntry, FileEditState, FileEditTool, ResolvedEditor, TimelineEvent } from "./domain.mjs";
4
+ import { FILE_EDIT_TYPES } from "./types.mjs";
5
+ export { FILE_EDIT_TYPES, FileDiff, FileEditEntry, FileEditState, FileEditTool, ResolvedEditor, TimelineEvent, createFileEditContainer, fileEditExtension };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{FILE_EDIT_TYPES as e}from"./types.mjs";import{createFileEditContainer as t}from"./container/index.mjs";import{fileEditExtension as n}from"./extension.mjs";export{e as FILE_EDIT_TYPES,t as createFileEditContainer,n as fileEditExtension};
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../types.cjs"),n=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.cjs"),r=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.cjs"),i=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs");let a=require("inversify"),o=require("node:fs/promises");o=e.__toESM(o,1);let s=require("node:path");s=e.__toESM(s,1);const c=`edit`,l=`write`,u=`bash`,d=new Set([`ENOENT`,`ENAMETOOLONG`]);function f(e,t){if(!e||typeof e!=`object`)return;let n=e[t];return typeof n==`string`?n:void 0}let p=class{timeline;pending=new Map;constructor(e){this.timeline=e}async start(e,t,n,r){if(t===c||t===l){let i=f(n,`path`);if(!i)return;let a=s.default.resolve(r,i);this.pending.set(e,{tool:t,paths:new Map([[a,await this.fingerprint(a)]])});return}if(t!==u)return;let i=f(n,`command`);if(!i)return;let a=new Map;for(let e of this.literalPaths(i,r))a.set(e,await this.fingerprint(e));this.pending.set(e,{tool:u,paths:a})}async end(e,t){let n=this.pending.get(e);if(this.pending.delete(e),!(!n||t))for(let[e,t]of n.paths){let r=await this.fingerprint(e);(n.tool===c||n.tool===l||JSON.stringify(t)!==JSON.stringify(r))&&await this.timeline.append({version:1,path:e,tool:n.tool,at:Date.now()})}}literalPaths(e,t){let n=(e.match(/"[^"]+"|'[^']+'|[^\s|;&<>]+/gu)??[]).map(e=>e.replace(/^['"]|['"]$/gu,``)).filter(e=>!e.startsWith(`-`)&&(e.includes(`/`)||/\.[a-z0-9_-]+$/iu.test(e))).map(e=>s.default.resolve(t,e));return[...new Set(n)]}async fingerprint(e){try{let t=await o.default.stat(e);return{exists:t.isFile(),size:t.size,modified:t.mtimeMs}}catch(e){if(e instanceof Error&&`code`in e&&typeof e.code==`string`&&d.has(e.code))return{exists:!1,size:0,modified:0};throw e}}};p=i.default([(0,a.injectable)(),r.default(0,(0,a.inject)(t.FILE_EDIT_TYPES.TimelineStore)),n.default(`design:paramtypes`,[Object])],p),Object.defineProperty(exports,"EditTracker",{enumerable:!0,get:function(){return p}});
2
+ //# sourceMappingURL=EditTracker.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditTracker.cjs","names":["path","fs","injectable","inject","FILE_EDIT_TYPES"],"sources":["../../../src/services/EditTracker/EditTracker.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { inject, injectable } from 'inversify';\nimport type { FileEditTool } from '../../domain.ts';\nimport { FILE_EDIT_TYPES } from '../../types.ts';\nimport type { ITimelineStore } from '../TimelineStore/types.ts';\nimport type { IEditTracker } from './types.ts';\n\nconst EDIT_TOOL = 'edit';\nconst WRITE_TOOL = 'write';\nconst BASH_TOOL = 'bash';\nconst MISSING_PATH_ERROR_CODES = new Set(['ENOENT', 'ENAMETOOLONG']);\n\ninterface Fingerprint {\n exists: boolean;\n size: number;\n modified: number;\n}\n\ninterface PendingEdit {\n tool: FileEditTool;\n paths: Map<string, Fingerprint>;\n}\n\nfunction objectValue(value: unknown, key: string): string | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const found = (value as Record<string, unknown>)[key];\n return typeof found === 'string' ? found : undefined;\n}\n\n@injectable()\nexport class EditTracker implements IEditTracker {\n private readonly pending = new Map<string, PendingEdit>();\n\n constructor(@inject(FILE_EDIT_TYPES.TimelineStore) private readonly timeline: ITimelineStore) {}\n\n async start(id: string, tool: string, args: unknown, cwd: string): Promise<void> {\n if (tool === EDIT_TOOL || tool === WRITE_TOOL) {\n const supplied = objectValue(args, 'path');\n if (!supplied) return;\n const absolute = path.resolve(cwd, supplied);\n this.pending.set(id, { tool, paths: new Map([[absolute, await this.fingerprint(absolute)]]) });\n return;\n }\n if (tool !== BASH_TOOL) return;\n const command = objectValue(args, 'command');\n if (!command) return;\n const paths = new Map<string, Fingerprint>();\n for (const candidate of this.literalPaths(command, cwd)) paths.set(candidate, await this.fingerprint(candidate));\n this.pending.set(id, { tool: BASH_TOOL, paths });\n }\n\n async end(id: string, isError: boolean): Promise<void> {\n const pending = this.pending.get(id);\n this.pending.delete(id);\n if (!pending || isError) return;\n for (const [filePath, before] of pending.paths) {\n const after = await this.fingerprint(filePath);\n const exactTool = pending.tool === EDIT_TOOL || pending.tool === WRITE_TOOL;\n if (exactTool || JSON.stringify(before) !== JSON.stringify(after)) {\n await this.timeline.append({ version: 1, path: filePath, tool: pending.tool, at: Date.now() });\n }\n }\n }\n\n private literalPaths(command: string, cwd: string): string[] {\n const tokens = command.match(/\"[^\"]+\"|'[^']+'|[^\\s|;&<>]+/gu) ?? [];\n const candidates = tokens\n .map((token) => token.replace(/^['\"]|['\"]$/gu, ''))\n .filter((token) => !token.startsWith('-') && (token.includes('/') || /\\.[a-z0-9_-]+$/iu.test(token)))\n .map((token) => path.resolve(cwd, token));\n return [...new Set(candidates)];\n }\n\n private async fingerprint(filePath: string): Promise<Fingerprint> {\n try {\n const stat = await fs.stat(filePath);\n return { exists: stat.isFile(), size: stat.size, modified: stat.mtimeMs };\n } catch (error) {\n if (\n error instanceof Error &&\n 'code' in error &&\n typeof error.code === 'string' &&\n MISSING_PATH_ERROR_CODES.has(error.code)\n ) {\n return { exists: false, size: 0, modified: 0 };\n }\n throw error;\n }\n }\n}\n"],"mappings":"idAQA,MAAM,EAAY,OACZ,EAAa,QACb,EAAY,OACZ,EAA2B,IAAI,IAAI,CAAC,SAAU,cAAc,CAAC,EAanE,SAAS,EAAY,EAAgB,EAAiC,CACpE,GAAI,CAAC,GAAS,OAAO,GAAU,SAAU,OACzC,IAAM,EAAS,EAAkC,GACjD,OAAO,OAAO,GAAU,SAAW,EAAQ,IAAA,EAC7C,CAGO,IAAM,EAAN,KAA0C,CAGqB,SAFpE,QAA2B,IAAI,IAE/B,YAAY,EAAkF,CAA1B,KAAA,SAAA,CAA2B,CAE/F,MAAM,MAAM,EAAY,EAAc,EAAe,EAA4B,CAC/E,GAAI,IAAS,GAAa,IAAS,EAAY,CAC7C,IAAM,EAAW,EAAY,EAAM,MAAM,EACzC,GAAI,CAAC,EAAU,OACf,IAAM,EAAWA,EAAAA,QAAK,QAAQ,EAAK,CAAQ,EAC3C,KAAK,QAAQ,IAAI,EAAI,CAAE,OAAM,MAAO,IAAI,IAAI,CAAC,CAAC,EAAU,MAAM,KAAK,YAAY,CAAQ,CAAC,CAAC,CAAC,CAAE,CAAC,EAC7F,MACF,CACA,GAAI,IAAS,EAAW,OACxB,IAAM,EAAU,EAAY,EAAM,SAAS,EAC3C,GAAI,CAAC,EAAS,OACd,IAAM,EAAQ,IAAI,IAClB,IAAK,IAAM,KAAa,KAAK,aAAa,EAAS,CAAG,EAAG,EAAM,IAAI,EAAW,MAAM,KAAK,YAAY,CAAS,CAAC,EAC/G,KAAK,QAAQ,IAAI,EAAI,CAAE,KAAM,EAAW,OAAM,CAAC,CACjD,CAEA,MAAM,IAAI,EAAY,EAAiC,CACrD,IAAM,EAAU,KAAK,QAAQ,IAAI,CAAE,EACnC,QAAK,QAAQ,OAAO,CAAE,EAClB,GAAC,GAAW,GAChB,IAAK,GAAM,CAAC,EAAU,KAAW,EAAQ,MAAO,CAC9C,IAAM,EAAQ,MAAM,KAAK,YAAY,CAAQ,GAC3B,EAAQ,OAAS,GAAa,EAAQ,OAAS,GAChD,KAAK,UAAU,CAAM,IAAM,KAAK,UAAU,CAAK,IAC9D,MAAM,KAAK,SAAS,OAAO,CAAE,QAAS,EAAG,KAAM,EAAU,KAAM,EAAQ,KAAM,GAAI,KAAK,IAAI,CAAE,CAAC,CAEjG,CACF,CAEA,aAAqB,EAAiB,EAAuB,CAE3D,IAAM,GADS,EAAQ,MAAM,+BAA+B,GAAK,CAAC,EAAA,CAE/D,IAAK,GAAU,EAAM,QAAQ,gBAAiB,EAAE,CAAC,CAAC,CAClD,OAAQ,GAAU,CAAC,EAAM,WAAW,GAAG,IAAM,EAAM,SAAS,GAAG,GAAK,mBAAmB,KAAK,CAAK,EAAE,CAAC,CACpG,IAAK,GAAUA,EAAAA,QAAK,QAAQ,EAAK,CAAK,CAAC,EAC1C,MAAO,CAAC,GAAG,IAAI,IAAI,CAAU,CAAC,CAChC,CAEA,MAAc,YAAY,EAAwC,CAChE,GAAI,CACF,IAAM,EAAO,MAAMC,EAAAA,QAAG,KAAK,CAAQ,EACnC,MAAO,CAAE,OAAQ,EAAK,OAAO,EAAG,KAAM,EAAK,KAAM,SAAU,EAAK,OAAQ,CAC1E,OAAS,EAAO,CACd,GACE,aAAiB,OACjB,SAAU,GACV,OAAO,EAAM,MAAS,UACtB,EAAyB,IAAI,EAAM,IAAI,EAEvC,MAAO,CAAE,OAAQ,GAAO,KAAM,EAAG,SAAU,CAAE,EAE/C,MAAM,CACR,CACF,CACF,gBA5DCC,EAAAA,EAAAA,WAAAA,CAAW,EAIGC,EAAAA,QAAAA,GAAAA,EAAAA,EAAAA,OAAAA,CAAOC,EAAAA,gBAAgB,aAAa,CAAA"}
@@ -0,0 +1,2 @@
1
+ import{FILE_EDIT_TYPES as e}from"../../types.mjs";import t from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.mjs";import n from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.mjs";import r from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs";import{inject as i,injectable as a}from"inversify";import o from"node:fs/promises";import s from"node:path";const c=`edit`,l=`write`,u=`bash`,d=new Set([`ENOENT`,`ENAMETOOLONG`]);function f(e,t){if(!e||typeof e!=`object`)return;let n=e[t];return typeof n==`string`?n:void 0}let p=class{timeline;pending=new Map;constructor(e){this.timeline=e}async start(e,t,n,r){if(t===c||t===l){let i=f(n,`path`);if(!i)return;let a=s.resolve(r,i);this.pending.set(e,{tool:t,paths:new Map([[a,await this.fingerprint(a)]])});return}if(t!==u)return;let i=f(n,`command`);if(!i)return;let a=new Map;for(let e of this.literalPaths(i,r))a.set(e,await this.fingerprint(e));this.pending.set(e,{tool:u,paths:a})}async end(e,t){let n=this.pending.get(e);if(this.pending.delete(e),!(!n||t))for(let[e,t]of n.paths){let r=await this.fingerprint(e);(n.tool===c||n.tool===l||JSON.stringify(t)!==JSON.stringify(r))&&await this.timeline.append({version:1,path:e,tool:n.tool,at:Date.now()})}}literalPaths(e,t){let n=(e.match(/"[^"]+"|'[^']+'|[^\s|;&<>]+/gu)??[]).map(e=>e.replace(/^['"]|['"]$/gu,``)).filter(e=>!e.startsWith(`-`)&&(e.includes(`/`)||/\.[a-z0-9_-]+$/iu.test(e))).map(e=>s.resolve(t,e));return[...new Set(n)]}async fingerprint(e){try{let t=await o.stat(e);return{exists:t.isFile(),size:t.size,modified:t.mtimeMs}}catch(e){if(e instanceof Error&&`code`in e&&typeof e.code==`string`&&d.has(e.code))return{exists:!1,size:0,modified:0};throw e}}};p=r([a(),n(0,i(e.TimelineStore)),t(`design:paramtypes`,[Object])],p);export{p as EditTracker};
2
+ //# sourceMappingURL=EditTracker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditTracker.mjs","names":[],"sources":["../../../src/services/EditTracker/EditTracker.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { inject, injectable } from 'inversify';\nimport type { FileEditTool } from '../../domain.ts';\nimport { FILE_EDIT_TYPES } from '../../types.ts';\nimport type { ITimelineStore } from '../TimelineStore/types.ts';\nimport type { IEditTracker } from './types.ts';\n\nconst EDIT_TOOL = 'edit';\nconst WRITE_TOOL = 'write';\nconst BASH_TOOL = 'bash';\nconst MISSING_PATH_ERROR_CODES = new Set(['ENOENT', 'ENAMETOOLONG']);\n\ninterface Fingerprint {\n exists: boolean;\n size: number;\n modified: number;\n}\n\ninterface PendingEdit {\n tool: FileEditTool;\n paths: Map<string, Fingerprint>;\n}\n\nfunction objectValue(value: unknown, key: string): string | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const found = (value as Record<string, unknown>)[key];\n return typeof found === 'string' ? found : undefined;\n}\n\n@injectable()\nexport class EditTracker implements IEditTracker {\n private readonly pending = new Map<string, PendingEdit>();\n\n constructor(@inject(FILE_EDIT_TYPES.TimelineStore) private readonly timeline: ITimelineStore) {}\n\n async start(id: string, tool: string, args: unknown, cwd: string): Promise<void> {\n if (tool === EDIT_TOOL || tool === WRITE_TOOL) {\n const supplied = objectValue(args, 'path');\n if (!supplied) return;\n const absolute = path.resolve(cwd, supplied);\n this.pending.set(id, { tool, paths: new Map([[absolute, await this.fingerprint(absolute)]]) });\n return;\n }\n if (tool !== BASH_TOOL) return;\n const command = objectValue(args, 'command');\n if (!command) return;\n const paths = new Map<string, Fingerprint>();\n for (const candidate of this.literalPaths(command, cwd)) paths.set(candidate, await this.fingerprint(candidate));\n this.pending.set(id, { tool: BASH_TOOL, paths });\n }\n\n async end(id: string, isError: boolean): Promise<void> {\n const pending = this.pending.get(id);\n this.pending.delete(id);\n if (!pending || isError) return;\n for (const [filePath, before] of pending.paths) {\n const after = await this.fingerprint(filePath);\n const exactTool = pending.tool === EDIT_TOOL || pending.tool === WRITE_TOOL;\n if (exactTool || JSON.stringify(before) !== JSON.stringify(after)) {\n await this.timeline.append({ version: 1, path: filePath, tool: pending.tool, at: Date.now() });\n }\n }\n }\n\n private literalPaths(command: string, cwd: string): string[] {\n const tokens = command.match(/\"[^\"]+\"|'[^']+'|[^\\s|;&<>]+/gu) ?? [];\n const candidates = tokens\n .map((token) => token.replace(/^['\"]|['\"]$/gu, ''))\n .filter((token) => !token.startsWith('-') && (token.includes('/') || /\\.[a-z0-9_-]+$/iu.test(token)))\n .map((token) => path.resolve(cwd, token));\n return [...new Set(candidates)];\n }\n\n private async fingerprint(filePath: string): Promise<Fingerprint> {\n try {\n const stat = await fs.stat(filePath);\n return { exists: stat.isFile(), size: stat.size, modified: stat.mtimeMs };\n } catch (error) {\n if (\n error instanceof Error &&\n 'code' in error &&\n typeof error.code === 'string' &&\n MISSING_PATH_ERROR_CODES.has(error.code)\n ) {\n return { exists: false, size: 0, modified: 0 };\n }\n throw error;\n }\n }\n}\n"],"mappings":"0aAQA,MAAM,EAAY,OACZ,EAAa,QACb,EAAY,OACZ,EAA2B,IAAI,IAAI,CAAC,SAAU,cAAc,CAAC,EAanE,SAAS,EAAY,EAAgB,EAAiC,CACpE,GAAI,CAAC,GAAS,OAAO,GAAU,SAAU,OACzC,IAAM,EAAS,EAAkC,GACjD,OAAO,OAAO,GAAU,SAAW,EAAQ,IAAA,EAC7C,CAGO,IAAM,EAAN,KAA0C,CAGqB,SAFpE,QAA2B,IAAI,IAE/B,YAAY,EAAkF,CAA1B,KAAA,SAAA,CAA2B,CAE/F,MAAM,MAAM,EAAY,EAAc,EAAe,EAA4B,CAC/E,GAAI,IAAS,GAAa,IAAS,EAAY,CAC7C,IAAM,EAAW,EAAY,EAAM,MAAM,EACzC,GAAI,CAAC,EAAU,OACf,IAAM,EAAW,EAAK,QAAQ,EAAK,CAAQ,EAC3C,KAAK,QAAQ,IAAI,EAAI,CAAE,OAAM,MAAO,IAAI,IAAI,CAAC,CAAC,EAAU,MAAM,KAAK,YAAY,CAAQ,CAAC,CAAC,CAAC,CAAE,CAAC,EAC7F,MACF,CACA,GAAI,IAAS,EAAW,OACxB,IAAM,EAAU,EAAY,EAAM,SAAS,EAC3C,GAAI,CAAC,EAAS,OACd,IAAM,EAAQ,IAAI,IAClB,IAAK,IAAM,KAAa,KAAK,aAAa,EAAS,CAAG,EAAG,EAAM,IAAI,EAAW,MAAM,KAAK,YAAY,CAAS,CAAC,EAC/G,KAAK,QAAQ,IAAI,EAAI,CAAE,KAAM,EAAW,OAAM,CAAC,CACjD,CAEA,MAAM,IAAI,EAAY,EAAiC,CACrD,IAAM,EAAU,KAAK,QAAQ,IAAI,CAAE,EACnC,QAAK,QAAQ,OAAO,CAAE,EAClB,GAAC,GAAW,GAChB,IAAK,GAAM,CAAC,EAAU,KAAW,EAAQ,MAAO,CAC9C,IAAM,EAAQ,MAAM,KAAK,YAAY,CAAQ,GAC3B,EAAQ,OAAS,GAAa,EAAQ,OAAS,GAChD,KAAK,UAAU,CAAM,IAAM,KAAK,UAAU,CAAK,IAC9D,MAAM,KAAK,SAAS,OAAO,CAAE,QAAS,EAAG,KAAM,EAAU,KAAM,EAAQ,KAAM,GAAI,KAAK,IAAI,CAAE,CAAC,CAEjG,CACF,CAEA,aAAqB,EAAiB,EAAuB,CAE3D,IAAM,GADS,EAAQ,MAAM,+BAA+B,GAAK,CAAC,EAAA,CAE/D,IAAK,GAAU,EAAM,QAAQ,gBAAiB,EAAE,CAAC,CAAC,CAClD,OAAQ,GAAU,CAAC,EAAM,WAAW,GAAG,IAAM,EAAM,SAAS,GAAG,GAAK,mBAAmB,KAAK,CAAK,EAAE,CAAC,CACpG,IAAK,GAAU,EAAK,QAAQ,EAAK,CAAK,CAAC,EAC1C,MAAO,CAAC,GAAG,IAAI,IAAI,CAAU,CAAC,CAChC,CAEA,MAAc,YAAY,EAAwC,CAChE,GAAI,CACF,IAAM,EAAO,MAAM,EAAG,KAAK,CAAQ,EACnC,MAAO,CAAE,OAAQ,EAAK,OAAO,EAAG,KAAM,EAAK,KAAM,SAAU,EAAK,OAAQ,CAC1E,OAAS,EAAO,CACd,GACE,aAAiB,OACjB,SAAU,GACV,OAAO,EAAM,MAAS,UACtB,EAAyB,IAAI,EAAM,IAAI,EAEvC,MAAO,CAAE,OAAQ,GAAO,KAAM,EAAG,SAAU,CAAE,EAE/C,MAAM,CACR,CACF,CACF,OA5DC,EAAW,EAIG,EAAA,EAAA,EAAO,EAAgB,aAAa,CAAA"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs");let n=require("inversify"),r=require("node:fs/promises");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("@agimon-ai/doompi-config"),o=require("node:os");o=e.__toESM(o,1);const s=/^\s*command:\s*(.+?)\s*$/mu;function c(){return process.env.PI_CODING_AGENT_DIR||i.default.join(o.default.homedir(),`.pi`,`agent`)}let l=class{path(){return(0,a.globalDoomConfigPath)()}packagePath(){return i.default.join(c(),`doom-file-edit`,`config.yaml`)}async command(){return await this.read(this.path())??await this.read(this.packagePath())}async read(e){try{let t=await r.default.readFile(e,`utf8`),n=/^editor:\s*\n((?:[ \t]+.*(?:\n|$))*)/mu.exec(t)?.[1];if(!n)return;let i=s.exec(n)?.[1]?.trim();if(!i)return;if(/^(?:[-+]?\d+(?:\.\d+)?|true|false|null)$/u.test(i))throw Error(`${e}: editor.command must be a string`);return i.replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/u,`$1$2`)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}}};l=t.default([(0,n.injectable)()],l),Object.defineProperty(exports,"EditorConfigService",{enumerable:!0,get:function(){return l}});
2
+ //# sourceMappingURL=EditorConfigService.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorConfigService.cjs","names":["path","os","globalDoomConfigPath","fs","injectable"],"sources":["../../../src/services/EditorConfigService/EditorConfigService.ts"],"sourcesContent":["import { globalDoomConfigPath } from '@agimon-ai/doompi-config';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { injectable } from 'inversify';\nimport type { IEditorConfigService } from './types.ts';\n\nconst commandPattern = /^\\s*command:\\s*(.+?)\\s*$/mu;\n\nfunction agentDirectory(): string {\n return process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), '.pi', 'agent');\n}\n\n@injectable()\nexport class EditorConfigService implements IEditorConfigService {\n path(): string {\n return globalDoomConfigPath();\n }\n\n packagePath(): string {\n return path.join(agentDirectory(), 'doom-file-edit', 'config.yaml');\n }\n\n async command(): Promise<string | undefined> {\n // Shared Doom config owns `editor:`; the package-scoped file is the\n // fallback for standalone installs that have no Doom config.\n return (await this.read(this.path())) ?? (await this.read(this.packagePath()));\n }\n\n private async read(filePath: string): Promise<string | undefined> {\n try {\n const content = await fs.readFile(filePath, 'utf8');\n const editorSection = /^editor:\\s*\\n((?:[ \\t]+.*(?:\\n|$))*)/mu.exec(content)?.[1];\n if (!editorSection) return undefined;\n const value = commandPattern.exec(editorSection)?.[1]?.trim();\n if (!value) return undefined;\n if (/^(?:[-+]?\\d+(?:\\.\\d+)?|true|false|null)$/u.test(value)) {\n throw new Error(`${filePath}: editor.command must be a string`);\n }\n return value.replace(/^(?:\"([\\s\\S]*)\"|'([\\s\\S]*)')$/u, '$1$2');\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return undefined;\n throw error;\n }\n }\n}\n"],"mappings":"iVAOA,MAAM,EAAiB,6BAEvB,SAAS,GAAyB,CAChC,OAAO,QAAQ,IAAI,qBAAuBA,EAAAA,QAAK,KAAKC,EAAAA,QAAG,QAAQ,EAAG,MAAO,OAAO,CAClF,CAGO,IAAM,EAAN,KAA0D,CAC/D,MAAe,CACb,OAAA,EAAOC,EAAAA,qBAAAA,CAAqB,CAC9B,CAEA,aAAsB,CACpB,OAAOF,EAAAA,QAAK,KAAK,EAAe,EAAG,iBAAkB,aAAa,CACpE,CAEA,MAAM,SAAuC,CAG3C,OAAQ,MAAM,KAAK,KAAK,KAAK,KAAK,CAAC,GAAO,MAAM,KAAK,KAAK,KAAK,YAAY,CAAC,CAC9E,CAEA,MAAc,KAAK,EAA+C,CAChE,GAAI,CACF,IAAM,EAAU,MAAMG,EAAAA,QAAG,SAAS,EAAU,MAAM,EAC5C,EAAgB,yCAAyC,KAAK,CAAO,CAAC,GAAG,GAC/E,GAAI,CAAC,EAAe,OACpB,IAAM,EAAQ,EAAe,KAAK,CAAa,CAAC,GAAG,EAAE,EAAE,KAAK,EAC5D,GAAI,CAAC,EAAO,OACZ,GAAI,4CAA4C,KAAK,CAAK,EACxD,MAAU,MAAM,GAAG,EAAS,kCAAkC,EAEhE,OAAO,EAAM,QAAQ,iCAAkC,MAAM,CAC/D,OAAS,EAAO,CACd,GAAI,aAAiB,OAAS,SAAU,GAAS,EAAM,OAAS,SAAU,OAC1E,MAAM,CACR,CACF,CACF,EAhCCC,EAAAA,EAAAA,QAAAA,EAAAA,EAAAA,EAAAA,WAAAA,CAAW,CAAA,EAAA,CAAA"}
@@ -0,0 +1,2 @@
1
+ import e from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs";import{injectable as t}from"inversify";import n from"node:fs/promises";import r from"node:path";import{globalDoomConfigPath as i}from"@agimon-ai/doompi-config";import a from"node:os";const o=/^\s*command:\s*(.+?)\s*$/mu;function s(){return process.env.PI_CODING_AGENT_DIR||r.join(a.homedir(),`.pi`,`agent`)}let c=class{path(){return i()}packagePath(){return r.join(s(),`doom-file-edit`,`config.yaml`)}async command(){return await this.read(this.path())??await this.read(this.packagePath())}async read(e){try{let t=await n.readFile(e,`utf8`),r=/^editor:\s*\n((?:[ \t]+.*(?:\n|$))*)/mu.exec(t)?.[1];if(!r)return;let i=o.exec(r)?.[1]?.trim();if(!i)return;if(/^(?:[-+]?\d+(?:\.\d+)?|true|false|null)$/u.test(i))throw Error(`${e}: editor.command must be a string`);return i.replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/u,`$1$2`)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}}};c=e([t()],c);export{c as EditorConfigService};
2
+ //# sourceMappingURL=EditorConfigService.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorConfigService.mjs","names":[],"sources":["../../../src/services/EditorConfigService/EditorConfigService.ts"],"sourcesContent":["import { globalDoomConfigPath } from '@agimon-ai/doompi-config';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { injectable } from 'inversify';\nimport type { IEditorConfigService } from './types.ts';\n\nconst commandPattern = /^\\s*command:\\s*(.+?)\\s*$/mu;\n\nfunction agentDirectory(): string {\n return process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), '.pi', 'agent');\n}\n\n@injectable()\nexport class EditorConfigService implements IEditorConfigService {\n path(): string {\n return globalDoomConfigPath();\n }\n\n packagePath(): string {\n return path.join(agentDirectory(), 'doom-file-edit', 'config.yaml');\n }\n\n async command(): Promise<string | undefined> {\n // Shared Doom config owns `editor:`; the package-scoped file is the\n // fallback for standalone installs that have no Doom config.\n return (await this.read(this.path())) ?? (await this.read(this.packagePath()));\n }\n\n private async read(filePath: string): Promise<string | undefined> {\n try {\n const content = await fs.readFile(filePath, 'utf8');\n const editorSection = /^editor:\\s*\\n((?:[ \\t]+.*(?:\\n|$))*)/mu.exec(content)?.[1];\n if (!editorSection) return undefined;\n const value = commandPattern.exec(editorSection)?.[1]?.trim();\n if (!value) return undefined;\n if (/^(?:[-+]?\\d+(?:\\.\\d+)?|true|false|null)$/u.test(value)) {\n throw new Error(`${filePath}: editor.command must be a string`);\n }\n return value.replace(/^(?:\"([\\s\\S]*)\"|'([\\s\\S]*)')$/u, '$1$2');\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return undefined;\n throw error;\n }\n }\n}\n"],"mappings":"4QAOA,MAAM,EAAiB,6BAEvB,SAAS,GAAyB,CAChC,OAAO,QAAQ,IAAI,qBAAuB,EAAK,KAAK,EAAG,QAAQ,EAAG,MAAO,OAAO,CAClF,CAGO,IAAM,EAAN,KAA0D,CAC/D,MAAe,CACb,OAAO,EAAqB,CAC9B,CAEA,aAAsB,CACpB,OAAO,EAAK,KAAK,EAAe,EAAG,iBAAkB,aAAa,CACpE,CAEA,MAAM,SAAuC,CAG3C,OAAQ,MAAM,KAAK,KAAK,KAAK,KAAK,CAAC,GAAO,MAAM,KAAK,KAAK,KAAK,YAAY,CAAC,CAC9E,CAEA,MAAc,KAAK,EAA+C,CAChE,GAAI,CACF,IAAM,EAAU,MAAM,EAAG,SAAS,EAAU,MAAM,EAC5C,EAAgB,yCAAyC,KAAK,CAAO,CAAC,GAAG,GAC/E,GAAI,CAAC,EAAe,OACpB,IAAM,EAAQ,EAAe,KAAK,CAAa,CAAC,GAAG,EAAE,EAAE,KAAK,EAC5D,GAAI,CAAC,EAAO,OACZ,GAAI,4CAA4C,KAAK,CAAK,EACxD,MAAU,MAAM,GAAG,EAAS,kCAAkC,EAEhE,OAAO,EAAM,QAAQ,iCAAkC,MAAM,CAC/D,OAAS,EAAO,CACd,GAAI,aAAiB,OAAS,SAAU,GAAS,EAAM,OAAS,SAAU,OAC1E,MAAM,CACR,CACF,CACF,EAhCC,EAAA,EAAA,CAAA,EAAW,CAAA,EAAA,CAAA"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../types.cjs"),n=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.cjs"),r=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.cjs"),i=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs"),a=require("../../commandLine.cjs");let o=require("inversify"),s=require("node:fs/promises");s=e.__toESM(s,1);let c=require("node:path");c=e.__toESM(c,1);let l=require("cross-spawn");l=e.__toESM(l,1);let u=class{config;constructor(e){this.config=e}async resolve(e=process.env,t=process.platform){let n=await this.config.command(),r=[...n?[{template:n,source:`configured`}]:[],...e.VISUAL?[{template:e.VISUAL,source:`VISUAL`}]:[],...e.EDITOR?[{template:e.EDITOR,source:`EDITOR`}]:[],{template:t===`win32`?`notepad {file}`:`nano {file}`,source:`fallback`}];for(let n of r){let r=a.splitCommandLine(n.template)[0];if(r&&await this.isExecutable(r,e,t))return n}}async launch(e,t,n){try{let r=await this.resolve();if(!r)return{success:!1,error:`No editor command could be resolved`};let[i,...o]=a.expandEditorTemplate(r.template,e,t);if(!i)return{success:!1,error:`Editor command must not be empty`};n.stop();try{await new Promise((e,t)=>{let n=(0,l.default)(i,o,{stdio:`inherit`,shell:!1});n.once(`error`,t),n.once(`exit`,()=>e())})}finally{n.start(),n.requestRender(!0)}return{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}async isExecutable(e,t,n){let r=n===`win32`?(t.PATHEXT??`.EXE;.CMD;.BAT`).split(`;`):[``],i=c.default.isAbsolute(e)||e.includes(c.default.sep)?[``]:(t.PATH??``).split(c.default.delimiter);for(let t of i)for(let i of r){let r=t?c.default.join(t,`${e}${i}`):`${e}${i}`;try{return await s.default.access(r,n===`win32`?s.default.constants.F_OK:s.default.constants.X_OK),!0}catch(e){if(!(e instanceof Error&&`code`in e&&(e.code===`ENOENT`||e.code===`EACCES`)))throw e}}return!1}};u=i.default([(0,o.injectable)(),r.default(0,(0,o.inject)(t.FILE_EDIT_TYPES.EditorConfigService)),n.default(`design:paramtypes`,[Object])],u),Object.defineProperty(exports,"EditorLauncher",{enumerable:!0,get:function(){return u}});
2
+ //# sourceMappingURL=EditorLauncher.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorLauncher.cjs","names":["splitCommandLine","expandEditorTemplate","spawn","path","fs","injectable","inject","FILE_EDIT_TYPES"],"sources":["../../../src/services/EditorLauncher/EditorLauncher.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { inject, injectable } from 'inversify';\nimport { expandEditorTemplate, splitCommandLine } from '../../commandLine.ts';\nimport type { ResolvedEditor } from '../../domain.ts';\nimport { FILE_EDIT_TYPES } from '../../types.ts';\nimport type { IEditorConfigService } from '../EditorConfigService/types.ts';\nimport type { EditorLaunchResult, EditorTui, IEditorLauncher } from './types.ts';\n\n@injectable()\nexport class EditorLauncher implements IEditorLauncher {\n constructor(@inject(FILE_EDIT_TYPES.EditorConfigService) private readonly config: IEditorConfigService) {}\n\n async resolve(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n ): Promise<ResolvedEditor | undefined> {\n const configured = await this.config.command();\n const candidates: ResolvedEditor[] = [\n ...(configured ? [{ template: configured, source: 'configured' as const }] : []),\n ...(env.VISUAL ? [{ template: env.VISUAL, source: 'VISUAL' as const }] : []),\n ...(env.EDITOR ? [{ template: env.EDITOR, source: 'EDITOR' as const }] : []),\n { template: platform === 'win32' ? 'notepad {file}' : 'nano {file}', source: 'fallback' },\n ];\n for (const candidate of candidates) {\n const executable = splitCommandLine(candidate.template)[0];\n if (executable && (await this.isExecutable(executable, env, platform))) return candidate;\n }\n return undefined;\n }\n\n async launch(filePath: string, line: number, tui: EditorTui): Promise<EditorLaunchResult> {\n try {\n const editor = await this.resolve();\n if (!editor) return { success: false, error: 'No editor command could be resolved' };\n const [command, ...args] = expandEditorTemplate(editor.template, filePath, line);\n if (!command) return { success: false, error: 'Editor command must not be empty' };\n tui.stop();\n try {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command, args, { stdio: 'inherit', shell: false });\n child.once('error', reject);\n child.once('exit', () => resolve());\n });\n } finally {\n tui.start();\n tui.requestRender(true);\n }\n return { success: true };\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n }\n\n private async isExecutable(command: string, env: NodeJS.ProcessEnv, platform: NodeJS.Platform): Promise<boolean> {\n const extensions = platform === 'win32' ? (env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';') : [''];\n const locations =\n path.isAbsolute(command) || command.includes(path.sep) ? [''] : (env.PATH ?? '').split(path.delimiter);\n for (const location of locations) {\n for (const extension of extensions) {\n const candidate = location ? path.join(location, `${command}${extension}`) : `${command}${extension}`;\n try {\n await fs.access(candidate, platform === 'win32' ? fs.constants.F_OK : fs.constants.X_OK);\n return true;\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && (error.code === 'ENOENT' || error.code === 'EACCES')))\n throw error;\n }\n }\n }\n return false;\n }\n}\n"],"mappings":"kiBAWO,IAAM,EAAN,KAAgD,CACqB,OAA1E,YAAY,EAA4F,CAA9B,KAAA,OAAA,CAA+B,CAEzG,MAAM,QACJ,EAAyB,QAAQ,IACjC,EAA4B,QAAQ,SACC,CACrC,IAAM,EAAa,MAAM,KAAK,OAAO,QAAQ,EACvC,EAA+B,CACnC,GAAI,EAAa,CAAC,CAAE,SAAU,EAAY,OAAQ,YAAsB,CAAC,EAAI,CAAC,EAC9E,GAAI,EAAI,OAAS,CAAC,CAAE,SAAU,EAAI,OAAQ,OAAQ,QAAkB,CAAC,EAAI,CAAC,EAC1E,GAAI,EAAI,OAAS,CAAC,CAAE,SAAU,EAAI,OAAQ,OAAQ,QAAkB,CAAC,EAAI,CAAC,EAC1E,CAAE,SAAU,IAAa,QAAU,iBAAmB,cAAe,OAAQ,UAAW,CAC1F,EACA,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAaA,EAAAA,iBAAiB,EAAU,QAAQ,CAAC,CAAC,GACxD,GAAI,GAAe,MAAM,KAAK,aAAa,EAAY,EAAK,CAAQ,EAAI,OAAO,CACjF,CAEF,CAEA,MAAM,OAAO,EAAkB,EAAc,EAA6C,CACxF,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,QAAQ,EAClC,GAAI,CAAC,EAAQ,MAAO,CAAE,QAAS,GAAO,MAAO,qCAAsC,EACnF,GAAM,CAAC,EAAS,GAAG,GAAQC,EAAAA,qBAAqB,EAAO,SAAU,EAAU,CAAI,EAC/E,GAAI,CAAC,EAAS,MAAO,CAAE,QAAS,GAAO,MAAO,kCAAmC,EACjF,EAAI,KAAK,EACT,GAAI,CACF,MAAM,IAAI,SAAe,EAAS,IAAW,CAC3C,IAAM,GAAA,EAAQC,EAAAA,QAAAA,CAAM,EAAS,EAAM,CAAE,MAAO,UAAW,MAAO,EAAM,CAAC,EACrE,EAAM,KAAK,QAAS,CAAM,EAC1B,EAAM,KAAK,WAAc,EAAQ,CAAC,CACpC,CAAC,CACH,QAAU,CACR,EAAI,MAAM,EACV,EAAI,cAAc,EAAI,CACxB,CACA,MAAO,CAAE,QAAS,EAAK,CACzB,OAAS,EAAO,CACd,MAAO,CAAE,QAAS,GAAO,MAAO,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAAE,CACzF,CACF,CAEA,MAAc,aAAa,EAAiB,EAAwB,EAA6C,CAC/G,IAAM,EAAa,IAAa,SAAW,EAAI,SAAW,iBAAA,CAAkB,MAAM,GAAG,EAAI,CAAC,EAAE,EACtF,EACJC,EAAAA,QAAK,WAAW,CAAO,GAAK,EAAQ,SAASA,EAAAA,QAAK,GAAG,EAAI,CAAC,EAAE,GAAK,EAAI,MAAQ,GAAA,CAAI,MAAMA,EAAAA,QAAK,SAAS,EACvG,IAAK,IAAM,KAAY,EACrB,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAY,EAAWA,EAAAA,QAAK,KAAK,EAAU,GAAG,IAAU,GAAW,EAAI,GAAG,IAAU,IAC1F,GAAI,CAEF,OADA,MAAMC,EAAAA,QAAG,OAAO,EAAW,IAAa,QAAUA,EAAAA,QAAG,UAAU,KAAOA,EAAAA,QAAG,UAAU,IAAI,EAChF,EACT,OAAS,EAAO,CACd,GAAI,EAAE,aAAiB,OAAS,SAAU,IAAU,EAAM,OAAS,UAAY,EAAM,OAAS,WAC5F,MAAM,CACV,CACF,CAEF,MAAO,EACT,CACF,gBA/DCC,EAAAA,EAAAA,WAAAA,CAAW,EAEGC,EAAAA,QAAAA,GAAAA,EAAAA,EAAAA,OAAAA,CAAOC,EAAAA,gBAAgB,mBAAmB,CAAA"}
@@ -0,0 +1,2 @@
1
+ import{FILE_EDIT_TYPES as e}from"../../types.mjs";import t from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.mjs";import n from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.mjs";import r from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs";import{expandEditorTemplate as i,splitCommandLine as a}from"../../commandLine.mjs";import{inject as o,injectable as s}from"inversify";import c from"node:fs/promises";import l from"node:path";import u from"cross-spawn";let d=class{config;constructor(e){this.config=e}async resolve(e=process.env,t=process.platform){let n=await this.config.command(),r=[...n?[{template:n,source:`configured`}]:[],...e.VISUAL?[{template:e.VISUAL,source:`VISUAL`}]:[],...e.EDITOR?[{template:e.EDITOR,source:`EDITOR`}]:[],{template:t===`win32`?`notepad {file}`:`nano {file}`,source:`fallback`}];for(let n of r){let r=a(n.template)[0];if(r&&await this.isExecutable(r,e,t))return n}}async launch(e,t,n){try{let r=await this.resolve();if(!r)return{success:!1,error:`No editor command could be resolved`};let[a,...o]=i(r.template,e,t);if(!a)return{success:!1,error:`Editor command must not be empty`};n.stop();try{await new Promise((e,t)=>{let n=u(a,o,{stdio:`inherit`,shell:!1});n.once(`error`,t),n.once(`exit`,()=>e())})}finally{n.start(),n.requestRender(!0)}return{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}async isExecutable(e,t,n){let r=n===`win32`?(t.PATHEXT??`.EXE;.CMD;.BAT`).split(`;`):[``],i=l.isAbsolute(e)||e.includes(l.sep)?[``]:(t.PATH??``).split(l.delimiter);for(let t of i)for(let i of r){let r=t?l.join(t,`${e}${i}`):`${e}${i}`;try{return await c.access(r,n===`win32`?c.constants.F_OK:c.constants.X_OK),!0}catch(e){if(!(e instanceof Error&&`code`in e&&(e.code===`ENOENT`||e.code===`EACCES`)))throw e}}return!1}};d=r([s(),n(0,o(e.EditorConfigService)),t(`design:paramtypes`,[Object])],d);export{d as EditorLauncher};
2
+ //# sourceMappingURL=EditorLauncher.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorLauncher.mjs","names":[],"sources":["../../../src/services/EditorLauncher/EditorLauncher.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { inject, injectable } from 'inversify';\nimport { expandEditorTemplate, splitCommandLine } from '../../commandLine.ts';\nimport type { ResolvedEditor } from '../../domain.ts';\nimport { FILE_EDIT_TYPES } from '../../types.ts';\nimport type { IEditorConfigService } from '../EditorConfigService/types.ts';\nimport type { EditorLaunchResult, EditorTui, IEditorLauncher } from './types.ts';\n\n@injectable()\nexport class EditorLauncher implements IEditorLauncher {\n constructor(@inject(FILE_EDIT_TYPES.EditorConfigService) private readonly config: IEditorConfigService) {}\n\n async resolve(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n ): Promise<ResolvedEditor | undefined> {\n const configured = await this.config.command();\n const candidates: ResolvedEditor[] = [\n ...(configured ? [{ template: configured, source: 'configured' as const }] : []),\n ...(env.VISUAL ? [{ template: env.VISUAL, source: 'VISUAL' as const }] : []),\n ...(env.EDITOR ? [{ template: env.EDITOR, source: 'EDITOR' as const }] : []),\n { template: platform === 'win32' ? 'notepad {file}' : 'nano {file}', source: 'fallback' },\n ];\n for (const candidate of candidates) {\n const executable = splitCommandLine(candidate.template)[0];\n if (executable && (await this.isExecutable(executable, env, platform))) return candidate;\n }\n return undefined;\n }\n\n async launch(filePath: string, line: number, tui: EditorTui): Promise<EditorLaunchResult> {\n try {\n const editor = await this.resolve();\n if (!editor) return { success: false, error: 'No editor command could be resolved' };\n const [command, ...args] = expandEditorTemplate(editor.template, filePath, line);\n if (!command) return { success: false, error: 'Editor command must not be empty' };\n tui.stop();\n try {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command, args, { stdio: 'inherit', shell: false });\n child.once('error', reject);\n child.once('exit', () => resolve());\n });\n } finally {\n tui.start();\n tui.requestRender(true);\n }\n return { success: true };\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n }\n\n private async isExecutable(command: string, env: NodeJS.ProcessEnv, platform: NodeJS.Platform): Promise<boolean> {\n const extensions = platform === 'win32' ? (env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';') : [''];\n const locations =\n path.isAbsolute(command) || command.includes(path.sep) ? [''] : (env.PATH ?? '').split(path.delimiter);\n for (const location of locations) {\n for (const extension of extensions) {\n const candidate = location ? path.join(location, `${command}${extension}`) : `${command}${extension}`;\n try {\n await fs.access(candidate, platform === 'win32' ? fs.constants.F_OK : fs.constants.X_OK);\n return true;\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && (error.code === 'ENOENT' || error.code === 'EACCES')))\n throw error;\n }\n }\n }\n return false;\n }\n}\n"],"mappings":"whBAWO,IAAM,EAAN,KAAgD,CACqB,OAA1E,YAAY,EAA4F,CAA9B,KAAA,OAAA,CAA+B,CAEzG,MAAM,QACJ,EAAyB,QAAQ,IACjC,EAA4B,QAAQ,SACC,CACrC,IAAM,EAAa,MAAM,KAAK,OAAO,QAAQ,EACvC,EAA+B,CACnC,GAAI,EAAa,CAAC,CAAE,SAAU,EAAY,OAAQ,YAAsB,CAAC,EAAI,CAAC,EAC9E,GAAI,EAAI,OAAS,CAAC,CAAE,SAAU,EAAI,OAAQ,OAAQ,QAAkB,CAAC,EAAI,CAAC,EAC1E,GAAI,EAAI,OAAS,CAAC,CAAE,SAAU,EAAI,OAAQ,OAAQ,QAAkB,CAAC,EAAI,CAAC,EAC1E,CAAE,SAAU,IAAa,QAAU,iBAAmB,cAAe,OAAQ,UAAW,CAC1F,EACA,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAa,EAAiB,EAAU,QAAQ,CAAC,CAAC,GACxD,GAAI,GAAe,MAAM,KAAK,aAAa,EAAY,EAAK,CAAQ,EAAI,OAAO,CACjF,CAEF,CAEA,MAAM,OAAO,EAAkB,EAAc,EAA6C,CACxF,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,QAAQ,EAClC,GAAI,CAAC,EAAQ,MAAO,CAAE,QAAS,GAAO,MAAO,qCAAsC,EACnF,GAAM,CAAC,EAAS,GAAG,GAAQ,EAAqB,EAAO,SAAU,EAAU,CAAI,EAC/E,GAAI,CAAC,EAAS,MAAO,CAAE,QAAS,GAAO,MAAO,kCAAmC,EACjF,EAAI,KAAK,EACT,GAAI,CACF,MAAM,IAAI,SAAe,EAAS,IAAW,CAC3C,IAAM,EAAQ,EAAM,EAAS,EAAM,CAAE,MAAO,UAAW,MAAO,EAAM,CAAC,EACrE,EAAM,KAAK,QAAS,CAAM,EAC1B,EAAM,KAAK,WAAc,EAAQ,CAAC,CACpC,CAAC,CACH,QAAU,CACR,EAAI,MAAM,EACV,EAAI,cAAc,EAAI,CACxB,CACA,MAAO,CAAE,QAAS,EAAK,CACzB,OAAS,EAAO,CACd,MAAO,CAAE,QAAS,GAAO,MAAO,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAAE,CACzF,CACF,CAEA,MAAc,aAAa,EAAiB,EAAwB,EAA6C,CAC/G,IAAM,EAAa,IAAa,SAAW,EAAI,SAAW,iBAAA,CAAkB,MAAM,GAAG,EAAI,CAAC,EAAE,EACtF,EACJ,EAAK,WAAW,CAAO,GAAK,EAAQ,SAAS,EAAK,GAAG,EAAI,CAAC,EAAE,GAAK,EAAI,MAAQ,GAAA,CAAI,MAAM,EAAK,SAAS,EACvG,IAAK,IAAM,KAAY,EACrB,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAY,EAAW,EAAK,KAAK,EAAU,GAAG,IAAU,GAAW,EAAI,GAAG,IAAU,IAC1F,GAAI,CAEF,OADA,MAAM,EAAG,OAAO,EAAW,IAAa,QAAU,EAAG,UAAU,KAAO,EAAG,UAAU,IAAI,EAChF,EACT,OAAS,EAAO,CACd,GAAI,EAAE,aAAiB,OAAS,SAAU,IAAU,EAAM,OAAS,UAAY,EAAM,OAAS,WAC5F,MAAM,CACV,CACF,CAEF,MAAO,EACT,CACF,OA/DC,EAAW,EAEG,EAAA,EAAA,EAAO,EAAgB,mBAAmB,CAAA"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs");let n=require("inversify"),r=require("node:path");r=e.__toESM(r,1);let i=require("node:os");i=e.__toESM(i,1);let a=require("node:child_process"),o=require("node:crypto"),s=require("node:fs");s=e.__toESM(s,1);let c=require("@agimon-ai/doompi-extension-contracts/child-process");function l(e){return(0,o.createHash)(`sha256`).update(e).digest(`hex`).slice(0,16)}let u=class{sessionKey(e,t=process.env){if(t[c.SUBAGENT_CHILD_ENV]){let e=t[c.SUBAGENT_PARENT_SESSION_ENV];if(e)return e}return e}timelinePath(e,t){let n=this.gitCommonDirectory(e),a=l(s.default.realpathSync(e)),o=process.env.PI_CODING_AGENT_DIR||r.default.join(i.default.homedir(),`.pi`,`agent`),c=r.default.join(n??o,`doom-file-edit`);return s.default.mkdirSync(c,{recursive:!0}),r.default.join(c,`${a}-${l(t)}.jsonl`)}gitCommonDirectory(e){try{return(0,a.execFileSync)(`git`,[`rev-parse`,`--path-format=absolute`,`--git-common-dir`],{cwd:e,encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()||void 0}catch(e){if(e instanceof Error)return;throw e}}};u=t.default([(0,n.injectable)()],u),Object.defineProperty(exports,"FileEditPaths",{enumerable:!0,get:function(){return u}});
2
+ //# sourceMappingURL=FileEditPaths.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileEditPaths.cjs","names":["createHash","SUBAGENT_CHILD_ENV","SUBAGENT_PARENT_SESSION_ENV","fs","path","os","execFileSync","injectable"],"sources":["../../../src/services/FileEditPaths/FileEditPaths.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { injectable } from 'inversify';\nimport type { IFileEditPaths } from './types.ts';\n\nfunction hash(value: string): string {\n return createHash('sha256').update(value).digest('hex').slice(0, 16);\n}\n\n@injectable()\nexport class FileEditPaths implements IFileEditPaths {\n sessionKey(sessionId: string, env: NodeJS.ProcessEnv = process.env): string {\n if (env[SUBAGENT_CHILD_ENV]) {\n const parent = env[SUBAGENT_PARENT_SESSION_ENV];\n if (parent) return parent;\n }\n return sessionId;\n }\n\n timelinePath(cwd: string, sessionKey: string): string {\n const commonDirectory = this.gitCommonDirectory(cwd);\n const worktree = hash(fs.realpathSync(cwd));\n const agentDirectory = process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), '.pi', 'agent');\n const directory = path.join(commonDirectory ?? agentDirectory, 'doom-file-edit');\n fs.mkdirSync(directory, { recursive: true });\n return path.join(directory, `${worktree}-${hash(sessionKey)}.jsonl`);\n }\n\n private gitCommonDirectory(cwd: string): string | undefined {\n try {\n const result = execFileSync('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], {\n cwd,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return result || undefined;\n } catch (error) {\n if (error instanceof Error) return undefined;\n throw error;\n }\n }\n}\n"],"mappings":"gaASA,SAAS,EAAK,EAAuB,CACnC,OAAA,EAAOA,EAAAA,WAAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,CAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,EAAG,EAAE,CACrE,CAGO,IAAM,EAAN,KAA8C,CACnD,WAAW,EAAmB,EAAyB,QAAQ,IAAa,CAC1E,GAAI,EAAIC,EAAAA,oBAAqB,CAC3B,IAAM,EAAS,EAAIC,EAAAA,6BACnB,GAAI,EAAQ,OAAO,CACrB,CACA,OAAO,CACT,CAEA,aAAa,EAAa,EAA4B,CACpD,IAAM,EAAkB,KAAK,mBAAmB,CAAG,EAC7C,EAAW,EAAKC,EAAAA,QAAG,aAAa,CAAG,CAAC,EACpC,EAAiB,QAAQ,IAAI,qBAAuBC,EAAAA,QAAK,KAAKC,EAAAA,QAAG,QAAQ,EAAG,MAAO,OAAO,EAC1F,EAAYD,EAAAA,QAAK,KAAK,GAAmB,EAAgB,gBAAgB,EAE/E,OADA,EAAA,QAAG,UAAU,EAAW,CAAE,UAAW,EAAK,CAAC,EACpCA,EAAAA,QAAK,KAAK,EAAW,GAAG,EAAS,GAAG,EAAK,CAAU,EAAE,OAAO,CACrE,CAEA,mBAA2B,EAAiC,CAC1D,GAAI,CAMF,OAAA,EALeE,EAAAA,aAAAA,CAAa,MAAO,CAAC,YAAa,yBAA0B,kBAAkB,EAAG,CAC9F,MACA,SAAU,OACV,MAAO,CAAC,SAAU,OAAQ,QAAQ,CACpC,CAAC,CAAC,CAAC,KACS,GAAK,IAAA,EACnB,OAAS,EAAO,CACd,GAAI,aAAiB,MAAO,OAC5B,MAAM,CACR,CACF,CACF,EAhCCC,EAAAA,EAAAA,QAAAA,EAAAA,EAAAA,EAAAA,WAAAA,CAAW,CAAA,EAAA,CAAA"}
@@ -0,0 +1,2 @@
1
+ import e from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs";import{injectable as t}from"inversify";import n from"node:path";import r from"node:os";import{execFileSync as i}from"node:child_process";import{createHash as a}from"node:crypto";import o from"node:fs";import{SUBAGENT_CHILD_ENV as s,SUBAGENT_PARENT_SESSION_ENV as c}from"@agimon-ai/doompi-extension-contracts/child-process";function l(e){return a(`sha256`).update(e).digest(`hex`).slice(0,16)}let u=class{sessionKey(e,t=process.env){if(t[s]){let e=t[c];if(e)return e}return e}timelinePath(e,t){let i=this.gitCommonDirectory(e),a=l(o.realpathSync(e)),s=process.env.PI_CODING_AGENT_DIR||n.join(r.homedir(),`.pi`,`agent`),c=n.join(i??s,`doom-file-edit`);return o.mkdirSync(c,{recursive:!0}),n.join(c,`${a}-${l(t)}.jsonl`)}gitCommonDirectory(e){try{return i(`git`,[`rev-parse`,`--path-format=absolute`,`--git-common-dir`],{cwd:e,encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()||void 0}catch(e){if(e instanceof Error)return;throw e}}};u=e([t()],u);export{u as FileEditPaths};
2
+ //# sourceMappingURL=FileEditPaths.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileEditPaths.mjs","names":[],"sources":["../../../src/services/FileEditPaths/FileEditPaths.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { injectable } from 'inversify';\nimport type { IFileEditPaths } from './types.ts';\n\nfunction hash(value: string): string {\n return createHash('sha256').update(value).digest('hex').slice(0, 16);\n}\n\n@injectable()\nexport class FileEditPaths implements IFileEditPaths {\n sessionKey(sessionId: string, env: NodeJS.ProcessEnv = process.env): string {\n if (env[SUBAGENT_CHILD_ENV]) {\n const parent = env[SUBAGENT_PARENT_SESSION_ENV];\n if (parent) return parent;\n }\n return sessionId;\n }\n\n timelinePath(cwd: string, sessionKey: string): string {\n const commonDirectory = this.gitCommonDirectory(cwd);\n const worktree = hash(fs.realpathSync(cwd));\n const agentDirectory = process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), '.pi', 'agent');\n const directory = path.join(commonDirectory ?? agentDirectory, 'doom-file-edit');\n fs.mkdirSync(directory, { recursive: true });\n return path.join(directory, `${worktree}-${hash(sessionKey)}.jsonl`);\n }\n\n private gitCommonDirectory(cwd: string): string | undefined {\n try {\n const result = execFileSync('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], {\n cwd,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return result || undefined;\n } catch (error) {\n if (error instanceof Error) return undefined;\n throw error;\n }\n }\n}\n"],"mappings":"wZASA,SAAS,EAAK,EAAuB,CACnC,OAAO,EAAW,QAAQ,CAAC,CAAC,OAAO,CAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,EAAG,EAAE,CACrE,CAGO,IAAM,EAAN,KAA8C,CACnD,WAAW,EAAmB,EAAyB,QAAQ,IAAa,CAC1E,GAAI,EAAI,GAAqB,CAC3B,IAAM,EAAS,EAAI,GACnB,GAAI,EAAQ,OAAO,CACrB,CACA,OAAO,CACT,CAEA,aAAa,EAAa,EAA4B,CACpD,IAAM,EAAkB,KAAK,mBAAmB,CAAG,EAC7C,EAAW,EAAK,EAAG,aAAa,CAAG,CAAC,EACpC,EAAiB,QAAQ,IAAI,qBAAuB,EAAK,KAAK,EAAG,QAAQ,EAAG,MAAO,OAAO,EAC1F,EAAY,EAAK,KAAK,GAAmB,EAAgB,gBAAgB,EAE/E,OADA,EAAG,UAAU,EAAW,CAAE,UAAW,EAAK,CAAC,EACpC,EAAK,KAAK,EAAW,GAAG,EAAS,GAAG,EAAK,CAAU,EAAE,OAAO,CACrE,CAEA,mBAA2B,EAAiC,CAC1D,GAAI,CAMF,OALe,EAAa,MAAO,CAAC,YAAa,yBAA0B,kBAAkB,EAAG,CAC9F,MACA,SAAU,OACV,MAAO,CAAC,SAAU,OAAQ,QAAQ,CACpC,CAAC,CAAC,CAAC,KACS,GAAK,IAAA,EACnB,OAAS,EAAO,CACd,GAAI,aAAiB,MAAO,OAC5B,MAAM,CACR,CACF,CACF,EAhCC,EAAA,EAAA,CAAA,EAAW,CAAA,EAAA,CAAA"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../types.cjs"),t=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.cjs"),n=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.cjs"),r=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs"),i=require("../../tui/fileEditOverlay.cjs");let a=require("inversify"),o=require("@earendil-works/pi-coding-agent");const s={overlay:!0,overlayOptions:{anchor:`top-left`,width:`100%`,maxHeight:`100%`,margin:0}},c=`error`;let l=class{timeline;diffs;config;launcher;constructor(e,t,n,r){this.timeline=e,this.diffs=t,this.config=n,this.launcher=r}async open(e){let t=!0;for(;t;){let n=await this.timeline.list(),r=await Promise.all(n.map(t=>this.diffs.diff(e.cwd,t.path))),a=await this.launcher.resolve(),l,u=await e.ui.custom((t,o,s,c)=>(l=t,new i.FileEditOverlayComponent(t,o,{cwd:e.cwd,entries:n,diffs:r,editor:a,configPath:this.config.path()},c)),s);if(u.action===`close`)return;let d=n[u.index],f=r[u.index];if(!(!d||!f)){if(u.action===`copy`)await(0,o.copyToClipboard)(d.path),e.ui.notify(`Copied ${d.path}`,`info`);else if(u.action===`open`){if(!l){e.ui.notify(`Terminal editor is unavailable outside the interactive overlay`,c);continue}let t=await this.launcher.launch(d.path,f.suggestedLine,l);t.success||e.ui.notify(`Could not open editor: ${t.error}`,c)}else u.action===`refresh`&&(t=!0)}}}};l=r.default([(0,a.injectable)(),n.default(0,(0,a.inject)(e.FILE_EDIT_TYPES.TimelineStore)),n.default(1,(0,a.inject)(e.FILE_EDIT_TYPES.GitDiffService)),n.default(2,(0,a.inject)(e.FILE_EDIT_TYPES.EditorConfigService)),n.default(3,(0,a.inject)(e.FILE_EDIT_TYPES.EditorLauncher)),t.default(`design:paramtypes`,[Object,Object,Object,Object])],l),Object.defineProperty(exports,"FileEditWorkflow",{enumerable:!0,get:function(){return l}});
2
+ //# sourceMappingURL=FileEditWorkflow.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileEditWorkflow.cjs","names":["FileEditOverlayComponent","copyToClipboard","injectable","inject","FILE_EDIT_TYPES"],"sources":["../../../src/services/FileEditWorkflow/FileEditWorkflow.ts"],"sourcesContent":["import { copyToClipboard, type ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { inject, injectable } from 'inversify';\nimport { FileEditOverlayComponent, type FileEditOverlayResult } from '../../tui/fileEditOverlay.ts';\nimport { FILE_EDIT_TYPES } from '../../types.ts';\nimport type { IEditorConfigService } from '../EditorConfigService/types.ts';\nimport type { EditorTui, IEditorLauncher } from '../EditorLauncher/types.ts';\nimport type { IGitDiffService } from '../GitDiffService/types.ts';\nimport type { ITimelineStore } from '../TimelineStore/types.ts';\nimport type { IFileEditWorkflow } from './types.ts';\n\nconst FULLSCREEN_UI_OPTIONS = {\n overlay: true,\n overlayOptions: { anchor: 'top-left' as const, width: '100%' as const, maxHeight: '100%' as const, margin: 0 },\n};\nconst ACTION_CLOSE = 'close';\nconst ACTION_COPY = 'copy';\nconst ACTION_OPEN = 'open';\nconst ACTION_REFRESH = 'refresh';\nconst LEVEL_INFO = 'info';\nconst LEVEL_ERROR = 'error';\n\n@injectable()\nexport class FileEditWorkflow implements IFileEditWorkflow {\n constructor(\n @inject(FILE_EDIT_TYPES.TimelineStore) private readonly timeline: ITimelineStore,\n @inject(FILE_EDIT_TYPES.GitDiffService) private readonly diffs: IGitDiffService,\n @inject(FILE_EDIT_TYPES.EditorConfigService) private readonly config: IEditorConfigService,\n @inject(FILE_EDIT_TYPES.EditorLauncher) private readonly launcher: IEditorLauncher,\n ) {}\n\n async open(ctx: ExtensionContext): Promise<void> {\n let keepOpen = true;\n while (keepOpen) {\n const entries = await this.timeline.list();\n const diffs = await Promise.all(entries.map((entry) => this.diffs.diff(ctx.cwd, entry.path)));\n const editor = await this.launcher.resolve();\n let terminal: EditorTui | undefined;\n const result = await ctx.ui.custom<FileEditOverlayResult>((tui, theme, _keybindings, done) => {\n terminal = tui;\n return new FileEditOverlayComponent(\n tui,\n theme,\n {\n cwd: ctx.cwd,\n entries,\n diffs,\n editor,\n configPath: this.config.path(),\n },\n done,\n );\n }, FULLSCREEN_UI_OPTIONS);\n if (result.action === ACTION_CLOSE) return;\n const selected = entries[result.index];\n const diff = diffs[result.index];\n if (!selected || !diff) continue;\n if (result.action === ACTION_COPY) {\n await copyToClipboard(selected.path);\n ctx.ui.notify(`Copied ${selected.path}`, LEVEL_INFO);\n } else if (result.action === ACTION_OPEN) {\n if (!terminal) {\n ctx.ui.notify('Terminal editor is unavailable outside the interactive overlay', LEVEL_ERROR);\n continue;\n }\n const launched = await this.launcher.launch(selected.path, diff.suggestedLine, terminal);\n if (!launched.success) ctx.ui.notify(`Could not open editor: ${launched.error}`, LEVEL_ERROR);\n } else if (result.action === ACTION_REFRESH) {\n keepOpen = true;\n }\n }\n }\n}\n"],"mappings":"4ZAUA,MAAM,EAAwB,CAC5B,QAAS,GACT,eAAgB,CAAE,OAAQ,WAAqB,MAAO,OAAiB,UAAW,OAAiB,OAAQ,CAAE,CAC/G,EAMM,EAAc,QAGb,IAAM,EAAN,KAAoD,CAEC,SACC,MACK,OACL,SAJ3D,YACE,EACA,EACA,EACA,EACA,CAJwD,KAAA,SAAA,EACC,KAAA,MAAA,EACK,KAAA,OAAA,EACL,KAAA,SAAA,CACxD,CAEH,MAAM,KAAK,EAAsC,CAC/C,IAAI,EAAW,GACf,KAAO,GAAU,CACf,IAAM,EAAU,MAAM,KAAK,SAAS,KAAK,EACnC,EAAQ,MAAM,QAAQ,IAAI,EAAQ,IAAK,GAAU,KAAK,MAAM,KAAK,EAAI,IAAK,EAAM,IAAI,CAAC,CAAC,EACtF,EAAS,MAAM,KAAK,SAAS,QAAQ,EACvC,EACE,EAAS,MAAM,EAAI,GAAG,QAA+B,EAAK,EAAO,EAAc,KACnF,EAAW,EACJ,IAAIA,EAAAA,yBACT,EACA,EACA,CACE,IAAK,EAAI,IACT,UACA,QACA,SACA,WAAY,KAAK,OAAO,KAAK,CAC/B,EACA,CACF,GACC,CAAqB,EACxB,GAAI,EAAO,SAAW,QAAc,OACpC,IAAM,EAAW,EAAQ,EAAO,OAC1B,EAAO,EAAM,EAAO,OACtB,MAAC,GAAY,CAAC,GAClB,IAAI,EAAO,SAAW,OACpB,MAAA,EAAMC,EAAAA,gBAAAA,CAAgB,EAAS,IAAI,EACnC,EAAI,GAAG,OAAO,UAAU,EAAS,OAAQ,MAAU,OAC9C,GAAI,EAAO,SAAW,OAAa,CACxC,GAAI,CAAC,EAAU,CACb,EAAI,GAAG,OAAO,iEAAkE,CAAW,EAC3F,QACF,CACA,IAAM,EAAW,MAAM,KAAK,SAAS,OAAO,EAAS,KAAM,EAAK,cAAe,CAAQ,EAClF,EAAS,SAAS,EAAI,GAAG,OAAO,0BAA0B,EAAS,QAAS,CAAW,CAC9F,MAAW,EAAO,SAAW,YAC3B,EAAW,GAAA,CAEf,CACF,CACF,gBAlDCC,EAAAA,EAAAA,WAAAA,CAAW,EAGPC,EAAAA,QAAAA,GAAAA,EAAAA,EAAAA,OAAAA,CAAOC,EAAAA,gBAAgB,aAAa,CAAA,EACpCD,EAAAA,QAAAA,GAAAA,EAAAA,EAAAA,OAAAA,CAAOC,EAAAA,gBAAgB,cAAc,CAAA,EACrCD,EAAAA,QAAAA,GAAAA,EAAAA,EAAAA,OAAAA,CAAOC,EAAAA,gBAAgB,mBAAmB,CAAA,EAC1CD,EAAAA,QAAAA,GAAAA,EAAAA,EAAAA,OAAAA,CAAOC,EAAAA,gBAAgB,cAAc,CAAA"}
@@ -0,0 +1,2 @@
1
+ import{FILE_EDIT_TYPES as e}from"../../types.mjs";import t from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.mjs";import n from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.mjs";import r from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs";import{FileEditOverlayComponent as i}from"../../tui/fileEditOverlay.mjs";import{inject as a,injectable as o}from"inversify";import{copyToClipboard as s}from"@earendil-works/pi-coding-agent";const c={overlay:!0,overlayOptions:{anchor:`top-left`,width:`100%`,maxHeight:`100%`,margin:0}},l=`error`;let u=class{timeline;diffs;config;launcher;constructor(e,t,n,r){this.timeline=e,this.diffs=t,this.config=n,this.launcher=r}async open(e){let t=!0;for(;t;){let n=await this.timeline.list(),r=await Promise.all(n.map(t=>this.diffs.diff(e.cwd,t.path))),a=await this.launcher.resolve(),o,u=await e.ui.custom((t,s,c,l)=>(o=t,new i(t,s,{cwd:e.cwd,entries:n,diffs:r,editor:a,configPath:this.config.path()},l)),c);if(u.action===`close`)return;let d=n[u.index],f=r[u.index];if(!(!d||!f)){if(u.action===`copy`)await s(d.path),e.ui.notify(`Copied ${d.path}`,`info`);else if(u.action===`open`){if(!o){e.ui.notify(`Terminal editor is unavailable outside the interactive overlay`,l);continue}let t=await this.launcher.launch(d.path,f.suggestedLine,o);t.success||e.ui.notify(`Could not open editor: ${t.error}`,l)}else u.action===`refresh`&&(t=!0)}}}};u=r([o(),n(0,a(e.TimelineStore)),n(1,a(e.GitDiffService)),n(2,a(e.EditorConfigService)),n(3,a(e.EditorLauncher)),t(`design:paramtypes`,[Object,Object,Object,Object])],u);export{u as FileEditWorkflow};
2
+ //# sourceMappingURL=FileEditWorkflow.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileEditWorkflow.mjs","names":[],"sources":["../../../src/services/FileEditWorkflow/FileEditWorkflow.ts"],"sourcesContent":["import { copyToClipboard, type ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { inject, injectable } from 'inversify';\nimport { FileEditOverlayComponent, type FileEditOverlayResult } from '../../tui/fileEditOverlay.ts';\nimport { FILE_EDIT_TYPES } from '../../types.ts';\nimport type { IEditorConfigService } from '../EditorConfigService/types.ts';\nimport type { EditorTui, IEditorLauncher } from '../EditorLauncher/types.ts';\nimport type { IGitDiffService } from '../GitDiffService/types.ts';\nimport type { ITimelineStore } from '../TimelineStore/types.ts';\nimport type { IFileEditWorkflow } from './types.ts';\n\nconst FULLSCREEN_UI_OPTIONS = {\n overlay: true,\n overlayOptions: { anchor: 'top-left' as const, width: '100%' as const, maxHeight: '100%' as const, margin: 0 },\n};\nconst ACTION_CLOSE = 'close';\nconst ACTION_COPY = 'copy';\nconst ACTION_OPEN = 'open';\nconst ACTION_REFRESH = 'refresh';\nconst LEVEL_INFO = 'info';\nconst LEVEL_ERROR = 'error';\n\n@injectable()\nexport class FileEditWorkflow implements IFileEditWorkflow {\n constructor(\n @inject(FILE_EDIT_TYPES.TimelineStore) private readonly timeline: ITimelineStore,\n @inject(FILE_EDIT_TYPES.GitDiffService) private readonly diffs: IGitDiffService,\n @inject(FILE_EDIT_TYPES.EditorConfigService) private readonly config: IEditorConfigService,\n @inject(FILE_EDIT_TYPES.EditorLauncher) private readonly launcher: IEditorLauncher,\n ) {}\n\n async open(ctx: ExtensionContext): Promise<void> {\n let keepOpen = true;\n while (keepOpen) {\n const entries = await this.timeline.list();\n const diffs = await Promise.all(entries.map((entry) => this.diffs.diff(ctx.cwd, entry.path)));\n const editor = await this.launcher.resolve();\n let terminal: EditorTui | undefined;\n const result = await ctx.ui.custom<FileEditOverlayResult>((tui, theme, _keybindings, done) => {\n terminal = tui;\n return new FileEditOverlayComponent(\n tui,\n theme,\n {\n cwd: ctx.cwd,\n entries,\n diffs,\n editor,\n configPath: this.config.path(),\n },\n done,\n );\n }, FULLSCREEN_UI_OPTIONS);\n if (result.action === ACTION_CLOSE) return;\n const selected = entries[result.index];\n const diff = diffs[result.index];\n if (!selected || !diff) continue;\n if (result.action === ACTION_COPY) {\n await copyToClipboard(selected.path);\n ctx.ui.notify(`Copied ${selected.path}`, LEVEL_INFO);\n } else if (result.action === ACTION_OPEN) {\n if (!terminal) {\n ctx.ui.notify('Terminal editor is unavailable outside the interactive overlay', LEVEL_ERROR);\n continue;\n }\n const launched = await this.launcher.launch(selected.path, diff.suggestedLine, terminal);\n if (!launched.success) ctx.ui.notify(`Could not open editor: ${launched.error}`, LEVEL_ERROR);\n } else if (result.action === ACTION_REFRESH) {\n keepOpen = true;\n }\n }\n }\n}\n"],"mappings":"4fAUA,MAAM,EAAwB,CAC5B,QAAS,GACT,eAAgB,CAAE,OAAQ,WAAqB,MAAO,OAAiB,UAAW,OAAiB,OAAQ,CAAE,CAC/G,EAMM,EAAc,QAGb,IAAM,EAAN,KAAoD,CAEC,SACC,MACK,OACL,SAJ3D,YACE,EACA,EACA,EACA,EACA,CAJwD,KAAA,SAAA,EACC,KAAA,MAAA,EACK,KAAA,OAAA,EACL,KAAA,SAAA,CACxD,CAEH,MAAM,KAAK,EAAsC,CAC/C,IAAI,EAAW,GACf,KAAO,GAAU,CACf,IAAM,EAAU,MAAM,KAAK,SAAS,KAAK,EACnC,EAAQ,MAAM,QAAQ,IAAI,EAAQ,IAAK,GAAU,KAAK,MAAM,KAAK,EAAI,IAAK,EAAM,IAAI,CAAC,CAAC,EACtF,EAAS,MAAM,KAAK,SAAS,QAAQ,EACvC,EACE,EAAS,MAAM,EAAI,GAAG,QAA+B,EAAK,EAAO,EAAc,KACnF,EAAW,EACJ,IAAI,EACT,EACA,EACA,CACE,IAAK,EAAI,IACT,UACA,QACA,SACA,WAAY,KAAK,OAAO,KAAK,CAC/B,EACA,CACF,GACC,CAAqB,EACxB,GAAI,EAAO,SAAW,QAAc,OACpC,IAAM,EAAW,EAAQ,EAAO,OAC1B,EAAO,EAAM,EAAO,OACtB,MAAC,GAAY,CAAC,GAClB,IAAI,EAAO,SAAW,OACpB,MAAM,EAAgB,EAAS,IAAI,EACnC,EAAI,GAAG,OAAO,UAAU,EAAS,OAAQ,MAAU,OAC9C,GAAI,EAAO,SAAW,OAAa,CACxC,GAAI,CAAC,EAAU,CACb,EAAI,GAAG,OAAO,iEAAkE,CAAW,EAC3F,QACF,CACA,IAAM,EAAW,MAAM,KAAK,SAAS,OAAO,EAAS,KAAM,EAAK,cAAe,CAAQ,EAClF,EAAS,SAAS,EAAI,GAAG,OAAO,0BAA0B,EAAS,QAAS,CAAW,CAC9F,MAAW,EAAO,SAAW,YAC3B,EAAW,GAAA,CAEf,CACF,CACF,OAlDC,EAAW,EAGP,EAAA,EAAA,EAAO,EAAgB,aAAa,CAAA,EACpC,EAAA,EAAA,EAAO,EAAgB,cAAc,CAAA,EACrC,EAAA,EAAA,EAAO,EAAgB,mBAAmB,CAAA,EAC1C,EAAA,EAAA,EAAO,EAAgB,cAAc,CAAA"}
@@ -0,0 +1,5 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs");let n=require("inversify"),r=require("node:fs/promises");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:child_process");const o=262144,s=8192;function c(e,t){let n=t||e.length>200,r=e.slice(0,200);return n&&r.push(`... diff truncated at 200 lines / ${o} bytes ...`),{lines:r,truncated:n}}function l(e,t,n,r){let i=r.byteLength>=o;if(r.subarray(0,s).includes(0))return{path:e,state:`binary`,lines:[`Binary file changed (${r.byteLength} bytes shown by git)`],additions:0,removals:0,tracked:n,truncated:i,suggestedLine:1};let a=c(r.subarray(0,o).toString(`utf8`).split(`
2
+ `),i),l=a.lines.filter(e=>e.startsWith(`+`)&&!e.startsWith(`+++`)).length,u=a.lines.filter(e=>e.startsWith(`-`)&&!e.startsWith(`---`)).length,d=a.lines.find(e=>e.startsWith(`@@`))?.match(/\+(\d+)/u);return{path:e,state:t,lines:a.lines,additions:l,removals:u,tracked:n,truncated:a.truncated,suggestedLine:Number(d?.[1]??1)}}let u=class{async diff(e,t){let n=i.default.resolve(e,t),o=i.default.relative(e,n),s=this.isTracked(e,o),c=!0;try{await r.default.access(n)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)c=!1;else throw e}if(!s&&!c)return l(n,`deleted`,!1,Buffer.from(`File no longer exists`));if(!s)return this.fullAdd(n);let u=(0,a.spawnSync)(`git`,[`diff`,`--no-ext-diff`,`--no-color`,`--unified=3`,`HEAD`,`--`,o],{cwd:e,encoding:`buffer`,maxBuffer:262144}).stdout??Buffer.alloc(0);return c&&/^(Binary files .* differ|GIT binary patch)$/mu.test(u.toString(`utf8`))?{path:n,state:`binary`,lines:[`Binary tracked file changed`],additions:0,removals:0,tracked:!0,truncated:!1,suggestedLine:1}:l(n,c?u.length>0?`modified`:`unchanged`:`deleted`,!0,u)}isTracked(e,t){return(0,a.spawnSync)(`git`,[`ls-files`,`--error-unmatch`,`--`,t],{cwd:e,stdio:`ignore`}).status===0}async fullAdd(e){let t=await r.default.open(e,`r`);try{let n=Buffer.alloc(o),{bytesRead:r}=await t.read(n,0,n.length,0),a=n.subarray(0,r);if(a.subarray(0,s).includes(0))return l(e,`binary`,!1,Buffer.from(`Binary untracked file (${r}+ bytes)`));let c=a.toString(`utf8`).split(`
3
+ `).map(e=>`+${e}`).join(`
4
+ `),u=`--- /dev/null\n+++ b/${i.default.basename(e)}\n@@ -0,0 +1 @@\n`;return l(e,`added`,!1,Buffer.from(u+c))}finally{await t.close()}}};u=t.default([(0,n.injectable)()],u),Object.defineProperty(exports,"GitDiffService",{enumerable:!0,get:function(){return u}}),exports.MAX_DIFF_BYTES=o;
5
+ //# sourceMappingURL=GitDiffService.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitDiffService.cjs","names":["path","fs","spawnSync","injectable"],"sources":["../../../src/services/GitDiffService/GitDiffService.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { injectable } from 'inversify';\nimport type { FileDiff, FileEditState } from '../../domain.ts';\nimport type { IGitDiffService } from './types.ts';\n\nexport const MAX_DIFF_LINES = 200;\nexport const MAX_DIFF_BYTES = 256 * 1024;\nconst BINARY_SAMPLE_BYTES = 8192;\n\nfunction capLines(lines: string[], byteTruncated: boolean): { lines: string[]; truncated: boolean } {\n const truncated = byteTruncated || lines.length > MAX_DIFF_LINES;\n const visible = lines.slice(0, MAX_DIFF_LINES);\n if (truncated) visible.push(`... diff truncated at ${MAX_DIFF_LINES} lines / ${MAX_DIFF_BYTES} bytes ...`);\n return { lines: visible, truncated };\n}\n\nfunction summarize(pathValue: string, state: FileEditState, tracked: boolean, raw: Buffer): FileDiff {\n const byteTruncated = raw.byteLength >= MAX_DIFF_BYTES;\n if (raw.subarray(0, BINARY_SAMPLE_BYTES).includes(0)) {\n return {\n path: pathValue,\n state: 'binary',\n lines: [`Binary file changed (${raw.byteLength} bytes shown by git)`],\n additions: 0,\n removals: 0,\n tracked,\n truncated: byteTruncated,\n suggestedLine: 1,\n };\n }\n const capped = capLines(raw.subarray(0, MAX_DIFF_BYTES).toString('utf8').split('\\n'), byteTruncated);\n const additions = capped.lines.filter((line) => line.startsWith('+') && !line.startsWith('+++')).length;\n const removals = capped.lines.filter((line) => line.startsWith('-') && !line.startsWith('---')).length;\n const hunk = capped.lines.find((line) => line.startsWith('@@'))?.match(/\\+(\\d+)/u);\n return {\n path: pathValue,\n state,\n lines: capped.lines,\n additions,\n removals,\n tracked,\n truncated: capped.truncated,\n suggestedLine: Number(hunk?.[1] ?? 1),\n };\n}\n\n@injectable()\nexport class GitDiffService implements IGitDiffService {\n async diff(cwd: string, filePath: string): Promise<FileDiff> {\n const absolute = path.resolve(cwd, filePath);\n const relative = path.relative(cwd, absolute);\n const tracked = this.isTracked(cwd, relative);\n let exists = true;\n try {\n await fs.access(absolute);\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') exists = false;\n else throw error;\n }\n if (!tracked && !exists) return summarize(absolute, 'deleted', false, Buffer.from('File no longer exists'));\n if (!tracked) return this.fullAdd(absolute);\n const result = spawnSync('git', ['diff', '--no-ext-diff', '--no-color', '--unified=3', 'HEAD', '--', relative], {\n cwd,\n encoding: 'buffer',\n maxBuffer: MAX_DIFF_BYTES,\n });\n const output = result.stdout ?? Buffer.alloc(0);\n if (exists && /^(Binary files .* differ|GIT binary patch)$/mu.test(output.toString('utf8'))) {\n return {\n path: absolute,\n state: 'binary',\n lines: ['Binary tracked file changed'],\n additions: 0,\n removals: 0,\n tracked: true,\n truncated: false,\n suggestedLine: 1,\n };\n }\n const state: FileEditState = exists ? (output.length > 0 ? 'modified' : 'unchanged') : 'deleted';\n return summarize(absolute, state, true, output);\n }\n\n private isTracked(cwd: string, relative: string): boolean {\n return spawnSync('git', ['ls-files', '--error-unmatch', '--', relative], { cwd, stdio: 'ignore' }).status === 0;\n }\n\n private async fullAdd(filePath: string): Promise<FileDiff> {\n const handle = await fs.open(filePath, 'r');\n try {\n const buffer = Buffer.alloc(MAX_DIFF_BYTES);\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);\n const sample = buffer.subarray(0, bytesRead);\n if (sample.subarray(0, BINARY_SAMPLE_BYTES).includes(0)) {\n return summarize(filePath, 'binary', false, Buffer.from(`Binary untracked file (${bytesRead}+ bytes)`));\n }\n const body = sample\n .toString('utf8')\n .split('\\n')\n .map((line) => `+${line}`)\n .join('\\n');\n const header = `--- /dev/null\\n+++ b/${path.basename(filePath)}\\n@@ -0,0 +1 @@\\n`;\n return summarize(filePath, 'added', false, Buffer.from(header + body));\n } finally {\n await handle.close();\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import e from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs";import{injectable as t}from"inversify";import n from"node:fs/promises";import r from"node:path";import{spawnSync as i}from"node:child_process";const a=262144,o=8192;function s(e,t){let n=t||e.length>200,r=e.slice(0,200);return n&&r.push(`... diff truncated at 200 lines / ${a} bytes ...`),{lines:r,truncated:n}}function c(e,t,n,r){let i=r.byteLength>=a;if(r.subarray(0,o).includes(0))return{path:e,state:`binary`,lines:[`Binary file changed (${r.byteLength} bytes shown by git)`],additions:0,removals:0,tracked:n,truncated:i,suggestedLine:1};let c=s(r.subarray(0,a).toString(`utf8`).split(`
2
+ `),i),l=c.lines.filter(e=>e.startsWith(`+`)&&!e.startsWith(`+++`)).length,u=c.lines.filter(e=>e.startsWith(`-`)&&!e.startsWith(`---`)).length,d=c.lines.find(e=>e.startsWith(`@@`))?.match(/\+(\d+)/u);return{path:e,state:t,lines:c.lines,additions:l,removals:u,tracked:n,truncated:c.truncated,suggestedLine:Number(d?.[1]??1)}}let l=class{async diff(e,t){let a=r.resolve(e,t),o=r.relative(e,a),s=this.isTracked(e,o),l=!0;try{await n.access(a)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)l=!1;else throw e}if(!s&&!l)return c(a,`deleted`,!1,Buffer.from(`File no longer exists`));if(!s)return this.fullAdd(a);let u=i(`git`,[`diff`,`--no-ext-diff`,`--no-color`,`--unified=3`,`HEAD`,`--`,o],{cwd:e,encoding:`buffer`,maxBuffer:262144}).stdout??Buffer.alloc(0);return l&&/^(Binary files .* differ|GIT binary patch)$/mu.test(u.toString(`utf8`))?{path:a,state:`binary`,lines:[`Binary tracked file changed`],additions:0,removals:0,tracked:!0,truncated:!1,suggestedLine:1}:c(a,l?u.length>0?`modified`:`unchanged`:`deleted`,!0,u)}isTracked(e,t){return i(`git`,[`ls-files`,`--error-unmatch`,`--`,t],{cwd:e,stdio:`ignore`}).status===0}async fullAdd(e){let t=await n.open(e,`r`);try{let n=Buffer.alloc(a),{bytesRead:i}=await t.read(n,0,n.length,0),s=n.subarray(0,i);if(s.subarray(0,o).includes(0))return c(e,`binary`,!1,Buffer.from(`Binary untracked file (${i}+ bytes)`));let l=s.toString(`utf8`).split(`
3
+ `).map(e=>`+${e}`).join(`
4
+ `),u=`--- /dev/null\n+++ b/${r.basename(e)}\n@@ -0,0 +1 @@\n`;return c(e,`added`,!1,Buffer.from(u+l))}finally{await t.close()}}};l=e([t()],l);export{l as GitDiffService,a as MAX_DIFF_BYTES};
5
+ //# sourceMappingURL=GitDiffService.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitDiffService.mjs","names":[],"sources":["../../../src/services/GitDiffService/GitDiffService.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { injectable } from 'inversify';\nimport type { FileDiff, FileEditState } from '../../domain.ts';\nimport type { IGitDiffService } from './types.ts';\n\nexport const MAX_DIFF_LINES = 200;\nexport const MAX_DIFF_BYTES = 256 * 1024;\nconst BINARY_SAMPLE_BYTES = 8192;\n\nfunction capLines(lines: string[], byteTruncated: boolean): { lines: string[]; truncated: boolean } {\n const truncated = byteTruncated || lines.length > MAX_DIFF_LINES;\n const visible = lines.slice(0, MAX_DIFF_LINES);\n if (truncated) visible.push(`... diff truncated at ${MAX_DIFF_LINES} lines / ${MAX_DIFF_BYTES} bytes ...`);\n return { lines: visible, truncated };\n}\n\nfunction summarize(pathValue: string, state: FileEditState, tracked: boolean, raw: Buffer): FileDiff {\n const byteTruncated = raw.byteLength >= MAX_DIFF_BYTES;\n if (raw.subarray(0, BINARY_SAMPLE_BYTES).includes(0)) {\n return {\n path: pathValue,\n state: 'binary',\n lines: [`Binary file changed (${raw.byteLength} bytes shown by git)`],\n additions: 0,\n removals: 0,\n tracked,\n truncated: byteTruncated,\n suggestedLine: 1,\n };\n }\n const capped = capLines(raw.subarray(0, MAX_DIFF_BYTES).toString('utf8').split('\\n'), byteTruncated);\n const additions = capped.lines.filter((line) => line.startsWith('+') && !line.startsWith('+++')).length;\n const removals = capped.lines.filter((line) => line.startsWith('-') && !line.startsWith('---')).length;\n const hunk = capped.lines.find((line) => line.startsWith('@@'))?.match(/\\+(\\d+)/u);\n return {\n path: pathValue,\n state,\n lines: capped.lines,\n additions,\n removals,\n tracked,\n truncated: capped.truncated,\n suggestedLine: Number(hunk?.[1] ?? 1),\n };\n}\n\n@injectable()\nexport class GitDiffService implements IGitDiffService {\n async diff(cwd: string, filePath: string): Promise<FileDiff> {\n const absolute = path.resolve(cwd, filePath);\n const relative = path.relative(cwd, absolute);\n const tracked = this.isTracked(cwd, relative);\n let exists = true;\n try {\n await fs.access(absolute);\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') exists = false;\n else throw error;\n }\n if (!tracked && !exists) return summarize(absolute, 'deleted', false, Buffer.from('File no longer exists'));\n if (!tracked) return this.fullAdd(absolute);\n const result = spawnSync('git', ['diff', '--no-ext-diff', '--no-color', '--unified=3', 'HEAD', '--', relative], {\n cwd,\n encoding: 'buffer',\n maxBuffer: MAX_DIFF_BYTES,\n });\n const output = result.stdout ?? Buffer.alloc(0);\n if (exists && /^(Binary files .* differ|GIT binary patch)$/mu.test(output.toString('utf8'))) {\n return {\n path: absolute,\n state: 'binary',\n lines: ['Binary tracked file changed'],\n additions: 0,\n removals: 0,\n tracked: true,\n truncated: false,\n suggestedLine: 1,\n };\n }\n const state: FileEditState = exists ? (output.length > 0 ? 'modified' : 'unchanged') : 'deleted';\n return summarize(absolute, state, true, output);\n }\n\n private isTracked(cwd: string, relative: string): boolean {\n return spawnSync('git', ['ls-files', '--error-unmatch', '--', relative], { cwd, stdio: 'ignore' }).status === 0;\n }\n\n private async fullAdd(filePath: string): Promise<FileDiff> {\n const handle = await fs.open(filePath, 'r');\n try {\n const buffer = Buffer.alloc(MAX_DIFF_BYTES);\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);\n const sample = buffer.subarray(0, bytesRead);\n if (sample.subarray(0, BINARY_SAMPLE_BYTES).includes(0)) {\n return summarize(filePath, 'binary', false, Buffer.from(`Binary untracked file (${bytesRead}+ bytes)`));\n }\n const body = sample\n .toString('utf8')\n .split('\\n')\n .map((line) => `+${line}`)\n .join('\\n');\n const header = `--- /dev/null\\n+++ b/${path.basename(filePath)}\\n@@ -0,0 +1 @@\\n`;\n return summarize(filePath, 'added', false, Buffer.from(header + body));\n } finally {\n await handle.close();\n }\n }\n}\n"],"mappings":"oOAQA,MAAa,EAAiB,OACxB,EAAsB,KAE5B,SAAS,EAAS,EAAiB,EAAiE,CAClG,IAAM,EAAY,GAAiB,EAAM,OAAA,IACnC,EAAU,EAAM,MAAM,EAAA,GAAiB,EAE7C,OADI,GAAW,EAAQ,KAAK,qCAAmD,EAAe,WAAW,EAClG,CAAE,MAAO,EAAS,WAAU,CACrC,CAEA,SAAS,EAAU,EAAmB,EAAsB,EAAkB,EAAuB,CACnG,IAAM,EAAgB,EAAI,YAAc,EACxC,GAAI,EAAI,SAAS,EAAG,CAAmB,CAAC,CAAC,SAAS,CAAC,EACjD,MAAO,CACL,KAAM,EACN,MAAO,SACP,MAAO,CAAC,wBAAwB,EAAI,WAAW,qBAAqB,EACpE,UAAW,EACX,SAAU,EACV,UACA,UAAW,EACX,cAAe,CACjB,EAEF,IAAM,EAAS,EAAS,EAAI,SAAS,EAAG,CAAc,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,MAAM;CAAI,EAAG,CAAa,EAC7F,EAAY,EAAO,MAAM,OAAQ,GAAS,EAAK,WAAW,GAAG,GAAK,CAAC,EAAK,WAAW,KAAK,CAAC,CAAC,CAAC,OAC3F,EAAW,EAAO,MAAM,OAAQ,GAAS,EAAK,WAAW,GAAG,GAAK,CAAC,EAAK,WAAW,KAAK,CAAC,CAAC,CAAC,OAC1F,EAAO,EAAO,MAAM,KAAM,GAAS,EAAK,WAAW,IAAI,CAAC,CAAC,EAAE,MAAM,UAAU,EACjF,MAAO,CACL,KAAM,EACN,QACA,MAAO,EAAO,MACd,YACA,WACA,UACA,UAAW,EAAO,UAClB,cAAe,OAAO,IAAO,IAAM,CAAC,CACtC,CACF,CAGO,IAAM,EAAN,KAAgD,CACrD,MAAM,KAAK,EAAa,EAAqC,CAC3D,IAAM,EAAW,EAAK,QAAQ,EAAK,CAAQ,EACrC,EAAW,EAAK,SAAS,EAAK,CAAQ,EACtC,EAAU,KAAK,UAAU,EAAK,CAAQ,EACxC,EAAS,GACb,GAAI,CACF,MAAM,EAAG,OAAO,CAAQ,CAC1B,OAAS,EAAO,CACd,GAAI,aAAiB,OAAS,SAAU,GAAS,EAAM,OAAS,SAAU,EAAS,QAC9E,MAAM,CACb,CACA,GAAI,CAAC,GAAW,CAAC,EAAQ,OAAO,EAAU,EAAU,UAAW,GAAO,OAAO,KAAK,uBAAuB,CAAC,EAC1G,GAAI,CAAC,EAAS,OAAO,KAAK,QAAQ,CAAQ,EAM1C,IAAM,EALS,EAAU,MAAO,CAAC,OAAQ,gBAAiB,aAAc,cAAe,OAAQ,KAAM,CAAQ,EAAG,CAC9G,MACA,SAAU,SACV,UAAA,MACF,CACoB,CAAC,CAAC,QAAU,OAAO,MAAM,CAAC,EAc9C,OAbI,GAAU,gDAAgD,KAAK,EAAO,SAAS,MAAM,CAAC,EACjF,CACL,KAAM,EACN,MAAO,SACP,MAAO,CAAC,6BAA6B,EACrC,UAAW,EACX,SAAU,EACV,QAAS,GACT,UAAW,GACX,cAAe,CACjB,EAGK,EAAU,EADY,EAAU,EAAO,OAAS,EAAI,WAAa,YAAe,UACrD,GAAM,CAAM,CAChD,CAEA,UAAkB,EAAa,EAA2B,CACxD,OAAO,EAAU,MAAO,CAAC,WAAY,kBAAmB,KAAM,CAAQ,EAAG,CAAE,MAAK,MAAO,QAAS,CAAC,CAAC,CAAC,SAAW,CAChH,CAEA,MAAc,QAAQ,EAAqC,CACzD,IAAM,EAAS,MAAM,EAAG,KAAK,EAAU,GAAG,EAC1C,GAAI,CACF,IAAM,EAAS,OAAO,MAAM,CAAc,EACpC,CAAE,aAAc,MAAM,EAAO,KAAK,EAAQ,EAAG,EAAO,OAAQ,CAAC,EAC7D,EAAS,EAAO,SAAS,EAAG,CAAS,EAC3C,GAAI,EAAO,SAAS,EAAG,CAAmB,CAAC,CAAC,SAAS,CAAC,EACpD,OAAO,EAAU,EAAU,SAAU,GAAO,OAAO,KAAK,0BAA0B,EAAU,SAAS,CAAC,EAExG,IAAM,EAAO,EACV,SAAS,MAAM,CAAC,CAChB,MAAM;CAAI,CAAC,CACX,IAAK,GAAS,IAAI,GAAM,CAAC,CACzB,KAAK;CAAI,EACN,EAAS,wBAAwB,EAAK,SAAS,CAAQ,EAAE,mBAC/D,OAAO,EAAU,EAAU,QAAS,GAAO,OAAO,KAAK,EAAS,CAAI,CAAC,CACvE,QAAU,CACR,MAAM,EAAO,MAAM,CACrB,CACF,CACF,EA7DC,EAAA,EAAA,CAAA,EAAW,CAAA,EAAA,CAAA"}
@@ -0,0 +1,3 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs");let n=require("inversify"),r=require("node:fs/promises");r=e.__toESM(r,1);function i(e,t){return e instanceof Error&&`code`in e&&e.code===t}function a(e){if(!e||typeof e!=`object`)return!1;let t=e;return t.version===1&&typeof t.path==`string`&&typeof t.at==`number`&&(t.tool===`edit`||t.tool===`write`||t.tool===`bash`)}let o=class{filePath;initialize(e){this.filePath=e}async append(e){await this.withLock(async()=>r.default.appendFile(this.requirePath(),`${JSON.stringify(e)}\n`,`utf8`))}async list(){let e;try{e=await r.default.readFile(this.requirePath(),`utf8`)}catch(e){if(i(e,`ENOENT`))return[];throw e}let t=new Map;for(let n of e.split(`
2
+ `)){if(!n)continue;let e;try{e=JSON.parse(n)}catch(e){console.warn(`Ignoring malformed file edit timeline line: ${e instanceof Error?e.message:String(e)}`);continue}if(!a(e))continue;let r=t.get(e.path);t.set(e.path,{path:e.path,tool:e.at>=(r?.at??0)?e.tool:r?.tool??e.tool,at:Math.max(e.at,r?.at??0),count:(r?.count??0)+1})}return[...t.values()].sort((e,t)=>t.at-e.at)}async clear(){try{await r.default.unlink(this.requirePath())}catch(e){if(!i(e,`ENOENT`))throw e}}requirePath(){if(!this.filePath)throw Error(`Timeline store is not initialized`);return this.filePath}async withLock(e){let t=`${this.requirePath()}.lock`;for(let n=0;n<100;n+=1)try{let n=await r.default.open(t,`wx`);try{return await e()}finally{await n.close();try{await r.default.unlink(t)}catch(e){i(e,`ENOENT`)||console.warn(`Could not remove timeline lock: ${String(e)}`)}}}catch(e){if(!i(e,`EEXIST`))throw e;await new Promise(e=>setTimeout(e,10))}throw Error(`Timed out acquiring timeline lock ${t}`)}};o=t.default([(0,n.injectable)()],o),Object.defineProperty(exports,"TimelineStore",{enumerable:!0,get:function(){return o}});
3
+ //# sourceMappingURL=TimelineStore.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimelineStore.cjs","names":["fs","injectable"],"sources":["../../../src/services/TimelineStore/TimelineStore.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport { injectable } from 'inversify';\nimport type { FileEditEntry, TimelineEvent } from '../../domain.ts';\nimport type { ITimelineStore } from './types.ts';\n\nconst LOCK_RETRY_MS = 10;\nconst LOCK_RETRIES = 100;\n\nfunction hasCode(error: unknown, code: string): boolean {\n return error instanceof Error && 'code' in error && error.code === code;\n}\n\nfunction isTimelineEvent(value: unknown): value is TimelineEvent {\n if (!value || typeof value !== 'object') return false;\n const event = value as Partial<TimelineEvent>;\n return (\n event.version === 1 &&\n typeof event.path === 'string' &&\n typeof event.at === 'number' &&\n (event.tool === 'edit' || event.tool === 'write' || event.tool === 'bash')\n );\n}\n\n@injectable()\nexport class TimelineStore implements ITimelineStore {\n private filePath: string | undefined;\n\n initialize(filePath: string): void {\n this.filePath = filePath;\n }\n\n async append(event: TimelineEvent): Promise<void> {\n await this.withLock(async () => fs.appendFile(this.requirePath(), `${JSON.stringify(event)}\\n`, 'utf8'));\n }\n\n async list(): Promise<FileEditEntry[]> {\n let content: string;\n try {\n content = await fs.readFile(this.requirePath(), 'utf8');\n } catch (error) {\n if (hasCode(error, 'ENOENT')) return [];\n throw error;\n }\n const folded = new Map<string, FileEditEntry>();\n for (const line of content.split('\\n')) {\n if (!line) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch (error) {\n console.warn(\n `Ignoring malformed file edit timeline line: ${error instanceof Error ? error.message : String(error)}`,\n );\n continue;\n }\n if (!isTimelineEvent(parsed)) continue;\n const current = folded.get(parsed.path);\n folded.set(parsed.path, {\n path: parsed.path,\n tool: parsed.at >= (current?.at ?? 0) ? parsed.tool : (current?.tool ?? parsed.tool),\n at: Math.max(parsed.at, current?.at ?? 0),\n count: (current?.count ?? 0) + 1,\n });\n }\n return [...folded.values()].sort((left, right) => right.at - left.at);\n }\n\n async clear(): Promise<void> {\n try {\n await fs.unlink(this.requirePath());\n } catch (error) {\n if (!hasCode(error, 'ENOENT')) throw error;\n }\n }\n\n private requirePath(): string {\n if (!this.filePath) throw new Error('Timeline store is not initialized');\n return this.filePath;\n }\n\n private async withLock<T>(operation: () => Promise<T>): Promise<T> {\n const lockPath = `${this.requirePath()}.lock`;\n for (let attempt = 0; attempt < LOCK_RETRIES; attempt += 1) {\n try {\n const lock = await fs.open(lockPath, 'wx');\n try {\n return await operation();\n } finally {\n await lock.close();\n try {\n await fs.unlink(lockPath);\n } catch (error) {\n if (!hasCode(error, 'ENOENT')) console.warn(`Could not remove timeline lock: ${String(error)}`);\n }\n }\n } catch (error) {\n if (!hasCode(error, 'EEXIST')) throw error;\n await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));\n }\n }\n throw new Error(`Timed out acquiring timeline lock ${lockPath}`);\n }\n}\n"],"mappings":"qNAQA,SAAS,EAAQ,EAAgB,EAAuB,CACtD,OAAO,aAAiB,OAAS,SAAU,GAAS,EAAM,OAAS,CACrE,CAEA,SAAS,EAAgB,EAAwC,CAC/D,GAAI,CAAC,GAAS,OAAO,GAAU,SAAU,MAAO,GAChD,IAAM,EAAQ,EACd,OACE,EAAM,UAAY,GAClB,OAAO,EAAM,MAAS,UACtB,OAAO,EAAM,IAAO,WACnB,EAAM,OAAS,QAAU,EAAM,OAAS,SAAW,EAAM,OAAS,OAEvE,CAGO,IAAM,EAAN,KAA8C,CACnD,SAEA,WAAW,EAAwB,CACjC,KAAK,SAAW,CAClB,CAEA,MAAM,OAAO,EAAqC,CAChD,MAAM,KAAK,SAAS,SAAYA,EAAAA,QAAG,WAAW,KAAK,YAAY,EAAG,GAAG,KAAK,UAAU,CAAK,EAAE,IAAK,MAAM,CAAC,CACzG,CAEA,MAAM,MAAiC,CACrC,IAAI,EACJ,GAAI,CACF,EAAU,MAAMA,EAAAA,QAAG,SAAS,KAAK,YAAY,EAAG,MAAM,CACxD,OAAS,EAAO,CACd,GAAI,EAAQ,EAAO,QAAQ,EAAG,MAAO,CAAC,EACtC,MAAM,CACR,CACA,IAAM,EAAS,IAAI,IACnB,IAAK,IAAM,KAAQ,EAAQ,MAAM;CAAI,EAAG,CACtC,GAAI,CAAC,EAAM,SACX,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,CAAI,CAC1B,OAAS,EAAO,CACd,QAAQ,KACN,+CAA+C,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GACtG,EACA,QACF,CACA,GAAI,CAAC,EAAgB,CAAM,EAAG,SAC9B,IAAM,EAAU,EAAO,IAAI,EAAO,IAAI,EACtC,EAAO,IAAI,EAAO,KAAM,CACtB,KAAM,EAAO,KACb,KAAM,EAAO,KAAO,GAAS,IAAM,GAAK,EAAO,KAAQ,GAAS,MAAQ,EAAO,KAC/E,GAAI,KAAK,IAAI,EAAO,GAAI,GAAS,IAAM,CAAC,EACxC,OAAQ,GAAS,OAAS,GAAK,CACjC,CAAC,CACH,CACA,MAAO,CAAC,GAAG,EAAO,OAAO,CAAC,CAAC,CAAC,MAAM,EAAM,IAAU,EAAM,GAAK,EAAK,EAAE,CACtE,CAEA,MAAM,OAAuB,CAC3B,GAAI,CACF,MAAMA,EAAAA,QAAG,OAAO,KAAK,YAAY,CAAC,CACpC,OAAS,EAAO,CACd,GAAI,CAAC,EAAQ,EAAO,QAAQ,EAAG,MAAM,CACvC,CACF,CAEA,aAA8B,CAC5B,GAAI,CAAC,KAAK,SAAU,MAAU,MAAM,mCAAmC,EACvE,OAAO,KAAK,QACd,CAEA,MAAc,SAAY,EAAyC,CACjE,IAAM,EAAW,GAAG,KAAK,YAAY,EAAE,OACvC,IAAK,IAAI,EAAU,EAAG,EAAU,IAAc,GAAW,EACvD,GAAI,CACF,IAAM,EAAO,MAAMA,EAAAA,QAAG,KAAK,EAAU,IAAI,EACzC,GAAI,CACF,OAAO,MAAM,EAAU,CACzB,QAAU,CACR,MAAM,EAAK,MAAM,EACjB,GAAI,CACF,MAAMA,EAAAA,QAAG,OAAO,CAAQ,CAC1B,OAAS,EAAO,CACT,EAAQ,EAAO,QAAQ,GAAG,QAAQ,KAAK,mCAAmC,OAAO,CAAK,GAAG,CAChG,CACF,CACF,OAAS,EAAO,CACd,GAAI,CAAC,EAAQ,EAAO,QAAQ,EAAG,MAAM,EACrC,MAAM,IAAI,QAAS,GAAY,WAAW,EAAS,EAAa,CAAC,CACnE,CAEF,MAAU,MAAM,qCAAqC,GAAU,CACjE,CACF,EA/ECC,EAAAA,EAAAA,QAAAA,EAAAA,EAAAA,EAAAA,WAAAA,CAAW,CAAA,EAAA,CAAA"}
@@ -0,0 +1,3 @@
1
+ import e from"../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs";import{injectable as t}from"inversify";import n from"node:fs/promises";function r(e,t){return e instanceof Error&&`code`in e&&e.code===t}function i(e){if(!e||typeof e!=`object`)return!1;let t=e;return t.version===1&&typeof t.path==`string`&&typeof t.at==`number`&&(t.tool===`edit`||t.tool===`write`||t.tool===`bash`)}let a=class{filePath;initialize(e){this.filePath=e}async append(e){await this.withLock(async()=>n.appendFile(this.requirePath(),`${JSON.stringify(e)}\n`,`utf8`))}async list(){let e;try{e=await n.readFile(this.requirePath(),`utf8`)}catch(e){if(r(e,`ENOENT`))return[];throw e}let t=new Map;for(let n of e.split(`
2
+ `)){if(!n)continue;let e;try{e=JSON.parse(n)}catch(e){console.warn(`Ignoring malformed file edit timeline line: ${e instanceof Error?e.message:String(e)}`);continue}if(!i(e))continue;let r=t.get(e.path);t.set(e.path,{path:e.path,tool:e.at>=(r?.at??0)?e.tool:r?.tool??e.tool,at:Math.max(e.at,r?.at??0),count:(r?.count??0)+1})}return[...t.values()].sort((e,t)=>t.at-e.at)}async clear(){try{await n.unlink(this.requirePath())}catch(e){if(!r(e,`ENOENT`))throw e}}requirePath(){if(!this.filePath)throw Error(`Timeline store is not initialized`);return this.filePath}async withLock(e){let t=`${this.requirePath()}.lock`;for(let i=0;i<100;i+=1)try{let i=await n.open(t,`wx`);try{return await e()}finally{await i.close();try{await n.unlink(t)}catch(e){r(e,`ENOENT`)||console.warn(`Could not remove timeline lock: ${String(e)}`)}}}catch(e){if(!r(e,`EEXIST`))throw e;await new Promise(e=>setTimeout(e,10))}throw Error(`Timed out acquiring timeline lock ${t}`)}};a=e([t()],a);export{a as TimelineStore};
3
+ //# sourceMappingURL=TimelineStore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimelineStore.mjs","names":[],"sources":["../../../src/services/TimelineStore/TimelineStore.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport { injectable } from 'inversify';\nimport type { FileEditEntry, TimelineEvent } from '../../domain.ts';\nimport type { ITimelineStore } from './types.ts';\n\nconst LOCK_RETRY_MS = 10;\nconst LOCK_RETRIES = 100;\n\nfunction hasCode(error: unknown, code: string): boolean {\n return error instanceof Error && 'code' in error && error.code === code;\n}\n\nfunction isTimelineEvent(value: unknown): value is TimelineEvent {\n if (!value || typeof value !== 'object') return false;\n const event = value as Partial<TimelineEvent>;\n return (\n event.version === 1 &&\n typeof event.path === 'string' &&\n typeof event.at === 'number' &&\n (event.tool === 'edit' || event.tool === 'write' || event.tool === 'bash')\n );\n}\n\n@injectable()\nexport class TimelineStore implements ITimelineStore {\n private filePath: string | undefined;\n\n initialize(filePath: string): void {\n this.filePath = filePath;\n }\n\n async append(event: TimelineEvent): Promise<void> {\n await this.withLock(async () => fs.appendFile(this.requirePath(), `${JSON.stringify(event)}\\n`, 'utf8'));\n }\n\n async list(): Promise<FileEditEntry[]> {\n let content: string;\n try {\n content = await fs.readFile(this.requirePath(), 'utf8');\n } catch (error) {\n if (hasCode(error, 'ENOENT')) return [];\n throw error;\n }\n const folded = new Map<string, FileEditEntry>();\n for (const line of content.split('\\n')) {\n if (!line) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch (error) {\n console.warn(\n `Ignoring malformed file edit timeline line: ${error instanceof Error ? error.message : String(error)}`,\n );\n continue;\n }\n if (!isTimelineEvent(parsed)) continue;\n const current = folded.get(parsed.path);\n folded.set(parsed.path, {\n path: parsed.path,\n tool: parsed.at >= (current?.at ?? 0) ? parsed.tool : (current?.tool ?? parsed.tool),\n at: Math.max(parsed.at, current?.at ?? 0),\n count: (current?.count ?? 0) + 1,\n });\n }\n return [...folded.values()].sort((left, right) => right.at - left.at);\n }\n\n async clear(): Promise<void> {\n try {\n await fs.unlink(this.requirePath());\n } catch (error) {\n if (!hasCode(error, 'ENOENT')) throw error;\n }\n }\n\n private requirePath(): string {\n if (!this.filePath) throw new Error('Timeline store is not initialized');\n return this.filePath;\n }\n\n private async withLock<T>(operation: () => Promise<T>): Promise<T> {\n const lockPath = `${this.requirePath()}.lock`;\n for (let attempt = 0; attempt < LOCK_RETRIES; attempt += 1) {\n try {\n const lock = await fs.open(lockPath, 'wx');\n try {\n return await operation();\n } finally {\n await lock.close();\n try {\n await fs.unlink(lockPath);\n } catch (error) {\n if (!hasCode(error, 'ENOENT')) console.warn(`Could not remove timeline lock: ${String(error)}`);\n }\n }\n } catch (error) {\n if (!hasCode(error, 'EEXIST')) throw error;\n await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));\n }\n }\n throw new Error(`Timed out acquiring timeline lock ${lockPath}`);\n }\n}\n"],"mappings":"4JAQA,SAAS,EAAQ,EAAgB,EAAuB,CACtD,OAAO,aAAiB,OAAS,SAAU,GAAS,EAAM,OAAS,CACrE,CAEA,SAAS,EAAgB,EAAwC,CAC/D,GAAI,CAAC,GAAS,OAAO,GAAU,SAAU,MAAO,GAChD,IAAM,EAAQ,EACd,OACE,EAAM,UAAY,GAClB,OAAO,EAAM,MAAS,UACtB,OAAO,EAAM,IAAO,WACnB,EAAM,OAAS,QAAU,EAAM,OAAS,SAAW,EAAM,OAAS,OAEvE,CAGO,IAAM,EAAN,KAA8C,CACnD,SAEA,WAAW,EAAwB,CACjC,KAAK,SAAW,CAClB,CAEA,MAAM,OAAO,EAAqC,CAChD,MAAM,KAAK,SAAS,SAAY,EAAG,WAAW,KAAK,YAAY,EAAG,GAAG,KAAK,UAAU,CAAK,EAAE,IAAK,MAAM,CAAC,CACzG,CAEA,MAAM,MAAiC,CACrC,IAAI,EACJ,GAAI,CACF,EAAU,MAAM,EAAG,SAAS,KAAK,YAAY,EAAG,MAAM,CACxD,OAAS,EAAO,CACd,GAAI,EAAQ,EAAO,QAAQ,EAAG,MAAO,CAAC,EACtC,MAAM,CACR,CACA,IAAM,EAAS,IAAI,IACnB,IAAK,IAAM,KAAQ,EAAQ,MAAM;CAAI,EAAG,CACtC,GAAI,CAAC,EAAM,SACX,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,CAAI,CAC1B,OAAS,EAAO,CACd,QAAQ,KACN,+CAA+C,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GACtG,EACA,QACF,CACA,GAAI,CAAC,EAAgB,CAAM,EAAG,SAC9B,IAAM,EAAU,EAAO,IAAI,EAAO,IAAI,EACtC,EAAO,IAAI,EAAO,KAAM,CACtB,KAAM,EAAO,KACb,KAAM,EAAO,KAAO,GAAS,IAAM,GAAK,EAAO,KAAQ,GAAS,MAAQ,EAAO,KAC/E,GAAI,KAAK,IAAI,EAAO,GAAI,GAAS,IAAM,CAAC,EACxC,OAAQ,GAAS,OAAS,GAAK,CACjC,CAAC,CACH,CACA,MAAO,CAAC,GAAG,EAAO,OAAO,CAAC,CAAC,CAAC,MAAM,EAAM,IAAU,EAAM,GAAK,EAAK,EAAE,CACtE,CAEA,MAAM,OAAuB,CAC3B,GAAI,CACF,MAAM,EAAG,OAAO,KAAK,YAAY,CAAC,CACpC,OAAS,EAAO,CACd,GAAI,CAAC,EAAQ,EAAO,QAAQ,EAAG,MAAM,CACvC,CACF,CAEA,aAA8B,CAC5B,GAAI,CAAC,KAAK,SAAU,MAAU,MAAM,mCAAmC,EACvE,OAAO,KAAK,QACd,CAEA,MAAc,SAAY,EAAyC,CACjE,IAAM,EAAW,GAAG,KAAK,YAAY,EAAE,OACvC,IAAK,IAAI,EAAU,EAAG,EAAU,IAAc,GAAW,EACvD,GAAI,CACF,IAAM,EAAO,MAAM,EAAG,KAAK,EAAU,IAAI,EACzC,GAAI,CACF,OAAO,MAAM,EAAU,CACzB,QAAU,CACR,MAAM,EAAK,MAAM,EACjB,GAAI,CACF,MAAM,EAAG,OAAO,CAAQ,CAC1B,OAAS,EAAO,CACT,EAAQ,EAAO,QAAQ,GAAG,QAAQ,KAAK,mCAAmC,OAAO,CAAK,GAAG,CAChG,CACF,CACF,OAAS,EAAO,CACd,GAAI,CAAC,EAAQ,EAAO,QAAQ,EAAG,MAAM,EACrC,MAAM,IAAI,QAAS,GAAY,WAAW,EAAS,EAAa,CAAC,CACnE,CAEF,MAAU,MAAM,qCAAqC,GAAU,CACjE,CACF,EA/EC,EAAA,EAAA,CAAA,EAAW,CAAA,EAAA,CAAA"}
@@ -0,0 +1,2 @@
1
+ const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("@agimon-ai/doompi-ui/components/doomOverlay"),r=require("@earendil-works/pi-tui");const i=`accent`,a=`success`,o=`error`,s=`borderMuted`,c={added:`added`,deleted:`deleted`,external:`external`};function l(e,t){let n=(0,r.visibleWidth)(e);return n>t?(0,r.truncateToWidth)(e,t):e+` `.repeat(t-n)}function u(e){return new Date(e).toTimeString().slice(0,5)}var d=class extends n.DoomOverlay{view;done;selected=0;constructor(e,t,n,r){super(e,t),this.view=n,this.done=r}getChrome(){let e=this.view.entries.reduce((e,t)=>e+t.count,0);return{title:`FILE EDITS`,accent:n.DOOM_OVERLAY_ACCENT,breadcrumb:`SPC › e / editor › f / files`,headerRight:`${this.view.entries.length} files · ${e} edits · this session`,footer:`↑↓ select · enter open · y copy path · r refresh · esc close`,footerHints:[[`↑↓`,`select`],[`enter`,`open`],[`y`,`copy path`],[`r`,`refresh`],[`esc`,`close`]],footerRight:this.view.entries.length>0?`${this.selected+1}/${this.view.entries.length}`:`empty`}}handleInput(e){if((0,r.matchesKey)(e,`escape`)||(0,r.matchesKey)(e,`ctrl+c`)||e===`q`)return this.finish(`close`);if((0,r.matchesKey)(e,`up`))this.selected=Math.max(0,this.selected-1);else if((0,r.matchesKey)(e,`down`))this.selected=Math.min(this.view.entries.length-1,this.selected+1);else if((0,r.matchesKey)(e,`enter`)&&this.view.entries.length>0)return this.finish(`open`);else if(e.toLowerCase()===`y`&&this.view.entries.length>0)return this.finish(`copy`);else if(e.toLowerCase()===`r`)return this.finish(`refresh`);this.tui.requestRender()}renderBody(e,t){if(e<60){let n=Math.max(1,Math.floor((t-1)/2)),r=Math.max(0,t-n-1);return[...this.leftColumn(e,n),this.theme.fg(s,`─`.repeat(e)),...this.rightColumn(e,r)]}let n=Math.floor(e*.55),r=e-n-1,i=Math.max(1,n-1),a=Math.max(1,r-1),o=this.leftColumn(i,t),c=this.rightColumn(a,t),u=this.theme.fg(s,`│`);return Array.from({length:t},(e,t)=>`${l(o[t]??``,i)} ${u} ${l(c[t]??``,a)}`)}finish(e){this.done({action:e,index:this.selected})}leftColumn(e,n){let s=[this.theme.fg(`dim`,` EDITED THIS SESSION · most recent first`)];this.view.entries.length===0&&s.push(``,this.theme.fg(`dim`,` No files edited in this session yet.`));let l=n>=7?5:0,d=Math.max(0,n-s.length-l),f=Math.max(1,Math.floor(d/2)),p=Math.max(0,this.view.entries.length-f),m=Math.min(p,Math.max(0,this.selected-f+1)),h=this.view.entries.slice(m,m+f);for(let[e,n]of h.entries()){let r=m+e,l=this.view.diffs[r],d=r===this.selected?this.theme.fg(i,`▌`):` `,f=t.default.relative(this.view.cwd,n.path)||t.default.basename(n.path),p=l?.state??c.external;s.push(`${d}${u(n.at)} ${n.count}x ${n.tool.padEnd(5)} ${f}`),s.push(` ${this.theme.fg(p===c.deleted?o:p===c.added?a:`warning`,p)}`)}return l>0&&(s.push(``,this.theme.fg(`dim`,` EDITOR LAUNCH`)),s.push(` ${this.view.editor?.template??`no command available`}`),s.push(` ${this.theme.fg(`dim`,this.view.editor?.source??this.view.configPath)}`),s.push(this.theme.fg(`dim`,` configured → VISUAL → EDITOR → fallback; TUI suspends for terminal editors`)),s.push(this.theme.fg(`dim`,` SPC e c to change`))),s.slice(0,n).map(t=>(0,r.truncateToWidth)(t,e))}rightColumn(e,n){let s=this.view.entries[this.selected],c=this.view.diffs[this.selected];if(!s||!c)return[this.theme.fg(`dim`,` Select a file to review its diff.`)];let l=t.default.basename(s.path),u=c.tracked?`vs git HEAD`:`full add`,d=[` ${l} ${this.theme.fg(`dim`,u)}`];d.push(` ${this.theme.fg(a,`+${c.additions}`)} ${this.theme.fg(o,`-${c.removals}`)} · ${s.count} edits · ${c.state}`);for(let e of c.lines){let t=e.startsWith(`@@`)?i:e.startsWith(`+`)?a:e.startsWith(`-`)?o:`text`;d.push(this.theme.fg(t,e))}return d.slice(0,n).map(t=>(0,r.truncateToWidth)(t,e))}};exports.FileEditOverlayComponent=d;
2
+ //# sourceMappingURL=fileEditOverlay.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileEditOverlay.cjs","names":["visibleWidth","truncateToWidth","DoomOverlay","DOOM_OVERLAY_ACCENT","matchesKey","path"],"sources":["../../src/tui/fileEditOverlay.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Theme } from '@earendil-works/pi-coding-agent';\nimport {\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/components/doomOverlay';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport type { FileDiff, FileEditEntry, FileEditState, ResolvedEditor } from '../domain.ts';\n\nexport type FileEditOverlayAction = 'close' | 'copy' | 'refresh' | 'open';\n\nexport interface FileEditOverlayResult {\n action: FileEditOverlayAction;\n index: number;\n}\n\nexport interface FileEditOverlayView {\n cwd: string;\n entries: FileEditEntry[];\n diffs: FileDiff[];\n editor: ResolvedEditor | undefined;\n configPath: string;\n}\n\nconst MIN_WIDTH = 60;\nconst LEFT_RATIO = 0.55;\nconst EDITOR_ROWS = 5;\nconst ENTRY_ROWS = 2;\n\n/** The palette names are not exported, so take the union from the method. */\ntype ThemeColor = Parameters<Theme['fg']>[0];\n\nconst DIM: ThemeColor = 'dim';\nconst ACCENT: ThemeColor = 'accent';\nconst ADDED: ThemeColor = 'success';\nconst REMOVED: ThemeColor = 'error';\nconst CHANGED: ThemeColor = 'warning';\nconst BODY: ThemeColor = 'text';\nconst RULE: ThemeColor = 'borderMuted';\n\n/**\n * The states this surface branches on. `external` is the fallback for an entry\n * with no diff, so it is named here rather than repeated as a literal.\n */\nconst STATE = { added: 'added', deleted: 'deleted', external: 'external' } as const satisfies Record<\n string,\n FileEditState\n>;\n\n/** Diff-hunk prefixes, which decide a line's colour. */\nconst HUNK_PREFIX = '@@';\nconst ADDED_PREFIX = '+';\nconst REMOVED_PREFIX = '-';\n\nfunction pad(text: string, width: number): string {\n const visible = visibleWidth(text);\n return visible > width ? truncateToWidth(text, width) : text + ' '.repeat(width - visible);\n}\n\nfunction time(at: number): string {\n return new Date(at).toTimeString().slice(0, 5);\n}\n\nexport class FileEditOverlayComponent extends DoomOverlay {\n private selected = 0;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly view: FileEditOverlayView,\n private readonly done: (result: FileEditOverlayResult) => void,\n ) {\n super(tui, theme);\n }\n\n protected getChrome(): DoomOverlayChrome {\n const totalEdits = this.view.entries.reduce((sum, entry) => sum + entry.count, 0);\n return {\n title: 'FILE EDITS',\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: 'SPC › e / editor › f / files',\n headerRight: `${this.view.entries.length} files · ${totalEdits} edits · this session`,\n footer: '↑↓ select · enter open · y copy path · r refresh · esc close',\n footerHints: [\n ['↑↓', 'select'],\n ['enter', 'open'],\n ['y', 'copy path'],\n ['r', 'refresh'],\n ['esc', 'close'],\n ],\n footerRight: this.view.entries.length > 0 ? `${this.selected + 1}/${this.view.entries.length}` : 'empty',\n };\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c') || data === 'q') return this.finish('close');\n if (matchesKey(data, 'up')) this.selected = Math.max(0, this.selected - 1);\n else if (matchesKey(data, 'down')) this.selected = Math.min(this.view.entries.length - 1, this.selected + 1);\n else if (matchesKey(data, 'enter') && this.view.entries.length > 0) return this.finish('open');\n else if (data.toLowerCase() === 'y' && this.view.entries.length > 0) return this.finish('copy');\n else if (data.toLowerCase() === 'r') return this.finish('refresh');\n this.tui.requestRender();\n }\n\n protected renderBody(width: number, height: number): string[] {\n if (width < MIN_WIDTH) {\n const leftHeight = Math.max(1, Math.floor((height - 1) / 2));\n const rightHeight = Math.max(0, height - leftHeight - 1);\n return [\n ...this.leftColumn(width, leftHeight),\n this.theme.fg(RULE, '─'.repeat(width)),\n ...this.rightColumn(width, rightHeight),\n ];\n }\n\n const leftWidth = Math.floor(width * LEFT_RATIO);\n const rightWidth = width - leftWidth - 1;\n // A blank column each side of the divider: flush columns let a long path in\n // one pane run straight into the other pane's heading.\n const leftContent = Math.max(1, leftWidth - 1);\n const rightContent = Math.max(1, rightWidth - 1);\n const left = this.leftColumn(leftContent, height);\n const right = this.rightColumn(rightContent, height);\n const divider = this.theme.fg(RULE, '│');\n return Array.from(\n { length: height },\n (_, index) => `${pad(left[index] ?? '', leftContent)} ${divider} ${pad(right[index] ?? '', rightContent)}`,\n );\n }\n\n private finish(action: FileEditOverlayAction): void {\n this.done({ action, index: this.selected });\n }\n\n private leftColumn(width: number, height: number): string[] {\n const lines = [this.theme.fg(DIM, ' EDITED THIS SESSION · most recent first')];\n if (this.view.entries.length === 0) {\n lines.push('', this.theme.fg(DIM, ' No files edited in this session yet.'));\n }\n\n const editorRows = height >= EDITOR_ROWS + ENTRY_ROWS ? EDITOR_ROWS : 0;\n const availableEntryRows = Math.max(0, height - lines.length - editorRows);\n const visibleEntries = Math.max(1, Math.floor(availableEntryRows / ENTRY_ROWS));\n const maxStart = Math.max(0, this.view.entries.length - visibleEntries);\n const start = Math.min(maxStart, Math.max(0, this.selected - visibleEntries + 1));\n const entries = this.view.entries.slice(start, start + visibleEntries);\n for (const [offset, entry] of entries.entries()) {\n const index = start + offset;\n const diff = this.view.diffs[index];\n const selected = index === this.selected ? this.theme.fg(ACCENT, '▌') : ' ';\n const relative = path.relative(this.view.cwd, entry.path) || path.basename(entry.path);\n const state = diff?.state ?? STATE.external;\n lines.push(`${selected}${time(entry.at)} ${entry.count}x ${entry.tool.padEnd(5)} ${relative}`);\n lines.push(\n ` ${this.theme.fg(state === STATE.deleted ? REMOVED : state === STATE.added ? ADDED : CHANGED, state)}`,\n );\n }\n if (editorRows > 0) {\n lines.push('', this.theme.fg(DIM, ' EDITOR LAUNCH'));\n lines.push(` ${this.view.editor?.template ?? 'no command available'}`);\n lines.push(` ${this.theme.fg(DIM, this.view.editor?.source ?? this.view.configPath)}`);\n lines.push(this.theme.fg(DIM, ' configured → VISUAL → EDITOR → fallback; TUI suspends for terminal editors'));\n // Changing it lives in the config panel now, which this overlay cannot open\n // without depending on the package that depends on it.\n lines.push(this.theme.fg(DIM, ' SPC e c to change'));\n }\n return lines.slice(0, height).map((line) => truncateToWidth(line, width));\n }\n\n private rightColumn(width: number, height: number): string[] {\n const entry = this.view.entries[this.selected];\n const diff = this.view.diffs[this.selected];\n if (!entry || !diff) return [this.theme.fg(DIM, ' Select a file to review its diff.')];\n const name = path.basename(entry.path);\n const base = diff.tracked ? 'vs git HEAD' : 'full add';\n const lines = [` ${name} ${this.theme.fg(DIM, base)}`];\n lines.push(\n ` ${this.theme.fg(ADDED, `+${diff.additions}`)} ${this.theme.fg(REMOVED, `-${diff.removals}`)} · ${entry.count} edits · ${diff.state}`,\n );\n for (const line of diff.lines) {\n const color = line.startsWith(HUNK_PREFIX)\n ? ACCENT\n : line.startsWith(ADDED_PREFIX)\n ? ADDED\n : line.startsWith(REMOVED_PREFIX)\n ? REMOVED\n : BODY;\n lines.push(this.theme.fg(color, line));\n }\n return lines.slice(0, height).map((line) => truncateToWidth(line, width));\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import e from"node:path";import{DOOM_OVERLAY_ACCENT as t,DoomOverlay as n}from"@agimon-ai/doompi-ui/components/doomOverlay";import{matchesKey as r,truncateToWidth as i,visibleWidth as a}from"@earendil-works/pi-tui";const o=`accent`,s=`success`,c=`error`,l=`borderMuted`,u={added:`added`,deleted:`deleted`,external:`external`};function d(e,t){let n=a(e);return n>t?i(e,t):e+` `.repeat(t-n)}function f(e){return new Date(e).toTimeString().slice(0,5)}var p=class extends n{view;done;selected=0;constructor(e,t,n,r){super(e,t),this.view=n,this.done=r}getChrome(){let e=this.view.entries.reduce((e,t)=>e+t.count,0);return{title:`FILE EDITS`,accent:t,breadcrumb:`SPC › e / editor › f / files`,headerRight:`${this.view.entries.length} files · ${e} edits · this session`,footer:`↑↓ select · enter open · y copy path · r refresh · esc close`,footerHints:[[`↑↓`,`select`],[`enter`,`open`],[`y`,`copy path`],[`r`,`refresh`],[`esc`,`close`]],footerRight:this.view.entries.length>0?`${this.selected+1}/${this.view.entries.length}`:`empty`}}handleInput(e){if(r(e,`escape`)||r(e,`ctrl+c`)||e===`q`)return this.finish(`close`);if(r(e,`up`))this.selected=Math.max(0,this.selected-1);else if(r(e,`down`))this.selected=Math.min(this.view.entries.length-1,this.selected+1);else if(r(e,`enter`)&&this.view.entries.length>0)return this.finish(`open`);else if(e.toLowerCase()===`y`&&this.view.entries.length>0)return this.finish(`copy`);else if(e.toLowerCase()===`r`)return this.finish(`refresh`);this.tui.requestRender()}renderBody(e,t){if(e<60){let n=Math.max(1,Math.floor((t-1)/2)),r=Math.max(0,t-n-1);return[...this.leftColumn(e,n),this.theme.fg(l,`─`.repeat(e)),...this.rightColumn(e,r)]}let n=Math.floor(e*.55),r=e-n-1,i=Math.max(1,n-1),a=Math.max(1,r-1),o=this.leftColumn(i,t),s=this.rightColumn(a,t),c=this.theme.fg(l,`│`);return Array.from({length:t},(e,t)=>`${d(o[t]??``,i)} ${c} ${d(s[t]??``,a)}`)}finish(e){this.done({action:e,index:this.selected})}leftColumn(t,n){let r=[this.theme.fg(`dim`,` EDITED THIS SESSION · most recent first`)];this.view.entries.length===0&&r.push(``,this.theme.fg(`dim`,` No files edited in this session yet.`));let a=n>=7?5:0,l=Math.max(0,n-r.length-a),d=Math.max(1,Math.floor(l/2)),p=Math.max(0,this.view.entries.length-d),m=Math.min(p,Math.max(0,this.selected-d+1)),h=this.view.entries.slice(m,m+d);for(let[t,n]of h.entries()){let i=m+t,a=this.view.diffs[i],l=i===this.selected?this.theme.fg(o,`▌`):` `,d=e.relative(this.view.cwd,n.path)||e.basename(n.path),p=a?.state??u.external;r.push(`${l}${f(n.at)} ${n.count}x ${n.tool.padEnd(5)} ${d}`),r.push(` ${this.theme.fg(p===u.deleted?c:p===u.added?s:`warning`,p)}`)}return a>0&&(r.push(``,this.theme.fg(`dim`,` EDITOR LAUNCH`)),r.push(` ${this.view.editor?.template??`no command available`}`),r.push(` ${this.theme.fg(`dim`,this.view.editor?.source??this.view.configPath)}`),r.push(this.theme.fg(`dim`,` configured → VISUAL → EDITOR → fallback; TUI suspends for terminal editors`)),r.push(this.theme.fg(`dim`,` SPC e c to change`))),r.slice(0,n).map(e=>i(e,t))}rightColumn(t,n){let r=this.view.entries[this.selected],a=this.view.diffs[this.selected];if(!r||!a)return[this.theme.fg(`dim`,` Select a file to review its diff.`)];let l=e.basename(r.path),u=a.tracked?`vs git HEAD`:`full add`,d=[` ${l} ${this.theme.fg(`dim`,u)}`];d.push(` ${this.theme.fg(s,`+${a.additions}`)} ${this.theme.fg(c,`-${a.removals}`)} · ${r.count} edits · ${a.state}`);for(let e of a.lines){let t=e.startsWith(`@@`)?o:e.startsWith(`+`)?s:e.startsWith(`-`)?c:`text`;d.push(this.theme.fg(t,e))}return d.slice(0,n).map(e=>i(e,t))}};export{p as FileEditOverlayComponent};
2
+ //# sourceMappingURL=fileEditOverlay.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileEditOverlay.mjs","names":[],"sources":["../../src/tui/fileEditOverlay.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Theme } from '@earendil-works/pi-coding-agent';\nimport {\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/components/doomOverlay';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport type { FileDiff, FileEditEntry, FileEditState, ResolvedEditor } from '../domain.ts';\n\nexport type FileEditOverlayAction = 'close' | 'copy' | 'refresh' | 'open';\n\nexport interface FileEditOverlayResult {\n action: FileEditOverlayAction;\n index: number;\n}\n\nexport interface FileEditOverlayView {\n cwd: string;\n entries: FileEditEntry[];\n diffs: FileDiff[];\n editor: ResolvedEditor | undefined;\n configPath: string;\n}\n\nconst MIN_WIDTH = 60;\nconst LEFT_RATIO = 0.55;\nconst EDITOR_ROWS = 5;\nconst ENTRY_ROWS = 2;\n\n/** The palette names are not exported, so take the union from the method. */\ntype ThemeColor = Parameters<Theme['fg']>[0];\n\nconst DIM: ThemeColor = 'dim';\nconst ACCENT: ThemeColor = 'accent';\nconst ADDED: ThemeColor = 'success';\nconst REMOVED: ThemeColor = 'error';\nconst CHANGED: ThemeColor = 'warning';\nconst BODY: ThemeColor = 'text';\nconst RULE: ThemeColor = 'borderMuted';\n\n/**\n * The states this surface branches on. `external` is the fallback for an entry\n * with no diff, so it is named here rather than repeated as a literal.\n */\nconst STATE = { added: 'added', deleted: 'deleted', external: 'external' } as const satisfies Record<\n string,\n FileEditState\n>;\n\n/** Diff-hunk prefixes, which decide a line's colour. */\nconst HUNK_PREFIX = '@@';\nconst ADDED_PREFIX = '+';\nconst REMOVED_PREFIX = '-';\n\nfunction pad(text: string, width: number): string {\n const visible = visibleWidth(text);\n return visible > width ? truncateToWidth(text, width) : text + ' '.repeat(width - visible);\n}\n\nfunction time(at: number): string {\n return new Date(at).toTimeString().slice(0, 5);\n}\n\nexport class FileEditOverlayComponent extends DoomOverlay {\n private selected = 0;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly view: FileEditOverlayView,\n private readonly done: (result: FileEditOverlayResult) => void,\n ) {\n super(tui, theme);\n }\n\n protected getChrome(): DoomOverlayChrome {\n const totalEdits = this.view.entries.reduce((sum, entry) => sum + entry.count, 0);\n return {\n title: 'FILE EDITS',\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: 'SPC › e / editor › f / files',\n headerRight: `${this.view.entries.length} files · ${totalEdits} edits · this session`,\n footer: '↑↓ select · enter open · y copy path · r refresh · esc close',\n footerHints: [\n ['↑↓', 'select'],\n ['enter', 'open'],\n ['y', 'copy path'],\n ['r', 'refresh'],\n ['esc', 'close'],\n ],\n footerRight: this.view.entries.length > 0 ? `${this.selected + 1}/${this.view.entries.length}` : 'empty',\n };\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c') || data === 'q') return this.finish('close');\n if (matchesKey(data, 'up')) this.selected = Math.max(0, this.selected - 1);\n else if (matchesKey(data, 'down')) this.selected = Math.min(this.view.entries.length - 1, this.selected + 1);\n else if (matchesKey(data, 'enter') && this.view.entries.length > 0) return this.finish('open');\n else if (data.toLowerCase() === 'y' && this.view.entries.length > 0) return this.finish('copy');\n else if (data.toLowerCase() === 'r') return this.finish('refresh');\n this.tui.requestRender();\n }\n\n protected renderBody(width: number, height: number): string[] {\n if (width < MIN_WIDTH) {\n const leftHeight = Math.max(1, Math.floor((height - 1) / 2));\n const rightHeight = Math.max(0, height - leftHeight - 1);\n return [\n ...this.leftColumn(width, leftHeight),\n this.theme.fg(RULE, '─'.repeat(width)),\n ...this.rightColumn(width, rightHeight),\n ];\n }\n\n const leftWidth = Math.floor(width * LEFT_RATIO);\n const rightWidth = width - leftWidth - 1;\n // A blank column each side of the divider: flush columns let a long path in\n // one pane run straight into the other pane's heading.\n const leftContent = Math.max(1, leftWidth - 1);\n const rightContent = Math.max(1, rightWidth - 1);\n const left = this.leftColumn(leftContent, height);\n const right = this.rightColumn(rightContent, height);\n const divider = this.theme.fg(RULE, '│');\n return Array.from(\n { length: height },\n (_, index) => `${pad(left[index] ?? '', leftContent)} ${divider} ${pad(right[index] ?? '', rightContent)}`,\n );\n }\n\n private finish(action: FileEditOverlayAction): void {\n this.done({ action, index: this.selected });\n }\n\n private leftColumn(width: number, height: number): string[] {\n const lines = [this.theme.fg(DIM, ' EDITED THIS SESSION · most recent first')];\n if (this.view.entries.length === 0) {\n lines.push('', this.theme.fg(DIM, ' No files edited in this session yet.'));\n }\n\n const editorRows = height >= EDITOR_ROWS + ENTRY_ROWS ? EDITOR_ROWS : 0;\n const availableEntryRows = Math.max(0, height - lines.length - editorRows);\n const visibleEntries = Math.max(1, Math.floor(availableEntryRows / ENTRY_ROWS));\n const maxStart = Math.max(0, this.view.entries.length - visibleEntries);\n const start = Math.min(maxStart, Math.max(0, this.selected - visibleEntries + 1));\n const entries = this.view.entries.slice(start, start + visibleEntries);\n for (const [offset, entry] of entries.entries()) {\n const index = start + offset;\n const diff = this.view.diffs[index];\n const selected = index === this.selected ? this.theme.fg(ACCENT, '▌') : ' ';\n const relative = path.relative(this.view.cwd, entry.path) || path.basename(entry.path);\n const state = diff?.state ?? STATE.external;\n lines.push(`${selected}${time(entry.at)} ${entry.count}x ${entry.tool.padEnd(5)} ${relative}`);\n lines.push(\n ` ${this.theme.fg(state === STATE.deleted ? REMOVED : state === STATE.added ? ADDED : CHANGED, state)}`,\n );\n }\n if (editorRows > 0) {\n lines.push('', this.theme.fg(DIM, ' EDITOR LAUNCH'));\n lines.push(` ${this.view.editor?.template ?? 'no command available'}`);\n lines.push(` ${this.theme.fg(DIM, this.view.editor?.source ?? this.view.configPath)}`);\n lines.push(this.theme.fg(DIM, ' configured → VISUAL → EDITOR → fallback; TUI suspends for terminal editors'));\n // Changing it lives in the config panel now, which this overlay cannot open\n // without depending on the package that depends on it.\n lines.push(this.theme.fg(DIM, ' SPC e c to change'));\n }\n return lines.slice(0, height).map((line) => truncateToWidth(line, width));\n }\n\n private rightColumn(width: number, height: number): string[] {\n const entry = this.view.entries[this.selected];\n const diff = this.view.diffs[this.selected];\n if (!entry || !diff) return [this.theme.fg(DIM, ' Select a file to review its diff.')];\n const name = path.basename(entry.path);\n const base = diff.tracked ? 'vs git HEAD' : 'full add';\n const lines = [` ${name} ${this.theme.fg(DIM, base)}`];\n lines.push(\n ` ${this.theme.fg(ADDED, `+${diff.additions}`)} ${this.theme.fg(REMOVED, `-${diff.removals}`)} · ${entry.count} edits · ${diff.state}`,\n );\n for (const line of diff.lines) {\n const color = line.startsWith(HUNK_PREFIX)\n ? ACCENT\n : line.startsWith(ADDED_PREFIX)\n ? ADDED\n : line.startsWith(REMOVED_PREFIX)\n ? REMOVED\n : BODY;\n lines.push(this.theme.fg(color, line));\n }\n return lines.slice(0, height).map((line) => truncateToWidth(line, width));\n }\n}\n"],"mappings":"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"}
package/dist/types.cjs ADDED
@@ -0,0 +1,2 @@
1
+ const e={Paths:Symbol.for(`doom-file-edit:Paths`),TimelineStore:Symbol.for(`doom-file-edit:TimelineStore`),GitDiffService:Symbol.for(`doom-file-edit:GitDiffService`),EditTracker:Symbol.for(`doom-file-edit:EditTracker`),EditorConfigService:Symbol.for(`doom-file-edit:EditorConfigService`),EditorLauncher:Symbol.for(`doom-file-edit:EditorLauncher`),Workflow:Symbol.for(`doom-file-edit:Workflow`)};exports.FILE_EDIT_TYPES=e;
2
+ //# sourceMappingURL=types.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["export const FILE_EDIT_TYPES = {\n Paths: Symbol.for('doom-file-edit:Paths'),\n TimelineStore: Symbol.for('doom-file-edit:TimelineStore'),\n GitDiffService: Symbol.for('doom-file-edit:GitDiffService'),\n EditTracker: Symbol.for('doom-file-edit:EditTracker'),\n EditorConfigService: Symbol.for('doom-file-edit:EditorConfigService'),\n EditorLauncher: Symbol.for('doom-file-edit:EditorLauncher'),\n Workflow: Symbol.for('doom-file-edit:Workflow'),\n} as const;\n"],"mappings":"AAAA,MAAa,EAAkB,CAC7B,MAAO,OAAO,IAAI,sBAAsB,EACxC,cAAe,OAAO,IAAI,8BAA8B,EACxD,eAAgB,OAAO,IAAI,+BAA+B,EAC1D,YAAa,OAAO,IAAI,4BAA4B,EACpD,oBAAqB,OAAO,IAAI,oCAAoC,EACpE,eAAgB,OAAO,IAAI,+BAA+B,EAC1D,SAAU,OAAO,IAAI,yBAAyB,CAChD"}
@@ -0,0 +1,13 @@
1
+ //#region src/types.d.ts
2
+ declare const FILE_EDIT_TYPES: {
3
+ readonly Paths: symbol;
4
+ readonly TimelineStore: symbol;
5
+ readonly GitDiffService: symbol;
6
+ readonly EditTracker: symbol;
7
+ readonly EditorConfigService: symbol;
8
+ readonly EditorLauncher: symbol;
9
+ readonly Workflow: symbol;
10
+ };
11
+ //#endregion
12
+ export { FILE_EDIT_TYPES };
13
+ //# sourceMappingURL=types.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"mappings":";cAAa"}
@@ -0,0 +1,13 @@
1
+ //#region src/types.d.ts
2
+ declare const FILE_EDIT_TYPES: {
3
+ readonly Paths: symbol;
4
+ readonly TimelineStore: symbol;
5
+ readonly GitDiffService: symbol;
6
+ readonly EditTracker: symbol;
7
+ readonly EditorConfigService: symbol;
8
+ readonly EditorLauncher: symbol;
9
+ readonly Workflow: symbol;
10
+ };
11
+ //#endregion
12
+ export { FILE_EDIT_TYPES };
13
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";cAAa"}
package/dist/types.mjs ADDED
@@ -0,0 +1,2 @@
1
+ const e={Paths:Symbol.for(`doom-file-edit:Paths`),TimelineStore:Symbol.for(`doom-file-edit:TimelineStore`),GitDiffService:Symbol.for(`doom-file-edit:GitDiffService`),EditTracker:Symbol.for(`doom-file-edit:EditTracker`),EditorConfigService:Symbol.for(`doom-file-edit:EditorConfigService`),EditorLauncher:Symbol.for(`doom-file-edit:EditorLauncher`),Workflow:Symbol.for(`doom-file-edit:Workflow`)};export{e as FILE_EDIT_TYPES};
2
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.mjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["export const FILE_EDIT_TYPES = {\n Paths: Symbol.for('doom-file-edit:Paths'),\n TimelineStore: Symbol.for('doom-file-edit:TimelineStore'),\n GitDiffService: Symbol.for('doom-file-edit:GitDiffService'),\n EditTracker: Symbol.for('doom-file-edit:EditTracker'),\n EditorConfigService: Symbol.for('doom-file-edit:EditorConfigService'),\n EditorLauncher: Symbol.for('doom-file-edit:EditorLauncher'),\n Workflow: Symbol.for('doom-file-edit:Workflow'),\n} as const;\n"],"mappings":"AAAA,MAAa,EAAkB,CAC7B,MAAO,OAAO,IAAI,sBAAsB,EACxC,cAAe,OAAO,IAAI,8BAA8B,EACxD,eAAgB,OAAO,IAAI,+BAA+B,EAC1D,YAAa,OAAO,IAAI,4BAA4B,EACpD,oBAAqB,OAAO,IAAI,oCAAoC,EACpE,eAAgB,OAAO,IAAI,+BAA+B,EAC1D,SAAU,OAAO,IAAI,yBAAyB,CAChD"}
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@agimon-ai/doompi-file-edit",
3
+ "version": "0.0.1-alpha.0",
4
+ "description": "Pi session file edit timeline and external editor overlay",
5
+ "homepage": "https://agimon.ai",
6
+ "license": "MIT",
7
+ "author": "Vuong Ngo",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "type": "module",
12
+ "main": "./dist/index.cjs",
13
+ "types": "./dist/index.d.mts",
14
+ "jsnext:main": "./dist/index.mjs",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.mts",
18
+ "import": "./dist/index.mjs",
19
+ "require": "./dist/index.cjs"
20
+ },
21
+ "./extensions/pi": {
22
+ "types": "./dist/extensions/pi.d.mts",
23
+ "import": "./dist/extensions/pi.mjs",
24
+ "require": "./dist/extensions/pi.cjs"
25
+ },
26
+ "./extensions/doom": {
27
+ "types": "./dist/extensions/doom.d.mts",
28
+ "import": "./dist/extensions/doom.mjs",
29
+ "require": "./dist/extensions/doom.cjs"
30
+ },
31
+ "./package.json": "./package.json"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "registry": "https://registry.npmjs.org/"
36
+ },
37
+ "dependencies": {
38
+ "cross-spawn": "7.0.6",
39
+ "inversify": "8.2.1",
40
+ "reflect-metadata": "0.2.2",
41
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.0",
42
+ "@agimon-ai/doompi-config": "0.0.1-alpha.0",
43
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.0",
44
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.0"
45
+ },
46
+ "devDependencies": {
47
+ "@earendil-works/pi-coding-agent": "0.84.1",
48
+ "@earendil-works/pi-tui": "0.84.1",
49
+ "@types/cross-spawn": "6.0.6",
50
+ "@types/node": "26.1.1",
51
+ "@vitest/coverage-v8": "4.1.10",
52
+ "tsdown": "0.22.14",
53
+ "typescript": "6.0.3",
54
+ "vitest": "4.1.10"
55
+ },
56
+ "peerDependencies": {
57
+ "@earendil-works/pi-coding-agent": "0.84.1",
58
+ "@earendil-works/pi-tui": "0.84.1"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "@earendil-works/pi-coding-agent": {
62
+ "optional": true
63
+ },
64
+ "@earendil-works/pi-tui": {
65
+ "optional": true
66
+ }
67
+ },
68
+ "engines": {
69
+ "node": ">=22.19.0"
70
+ },
71
+ "pi": {
72
+ "extensions": [
73
+ "./dist/extensions/pi.mjs"
74
+ ]
75
+ },
76
+ "scripts": {
77
+ "build": "tsdown",
78
+ "test": "vitest --run",
79
+ "typecheck": "tsc --noEmit",
80
+ "lint": "oxlint . && oxfmt . --check",
81
+ "fixcode": "oxlint . --fix && oxfmt ."
82
+ }
83
+ }