claude-code-session-manager 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/TiptapBody-CAJSNRPs.js +189 -0
- package/dist/assets/{cssMode-DyodRfD-.js → cssMode-o7rZCrm4.js} +1 -1
- package/dist/assets/{freemarker2-D1H1ixRK.js → freemarker2-CgmCS5Wh.js} +1 -1
- package/dist/assets/{handlebars-wnlxpTlt.js → handlebars-BcPLqhPv.js} +1 -1
- package/dist/assets/{html-Dv_oA_OQ.js → html-CC9xWnC3.js} +1 -1
- package/dist/assets/{htmlMode-DGXsu2-V.js → htmlMode-DEgCqH7k.js} +1 -1
- package/dist/assets/{index-oiSqLrkZ.js → index-C7ljEoqc.js} +1223 -1192
- package/dist/assets/{index-CcRP2nIC.css → index-CH3K1pkS.css} +1 -1
- package/dist/assets/{javascript-CxejmYhM.js → javascript-CjwqkQrn.js} +1 -1
- package/dist/assets/{jsonMode-ztPfF7kI.js → jsonMode-BYTLu76d.js} +4 -4
- package/dist/assets/{liquid-DvtfrYeo.js → liquid-wbQUuJwT.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-mIBTKOZq.js → lspLanguageFeatures-BJGMI7Xu.js} +1 -1
- package/dist/assets/{mdx-DTebMWEJ.js → mdx-DcDstgPF.js} +1 -1
- package/dist/assets/{python-zea5QgfT.js → python-B96yyM_5.js} +1 -1
- package/dist/assets/{razor-DODk3om_.js → razor-C7aRIxIE.js} +1 -1
- package/dist/assets/{tsMode-BQGo_Gc8.js → tsMode-B3UYlGaL.js} +1 -1
- package/dist/assets/{typescript-Cfo1NBg6.js → typescript-CV587TvC.js} +1 -1
- package/dist/assets/{xml-D1RKIHcE.js → xml-PWUJecBf.js} +1 -1
- package/dist/assets/{yaml-B8MoJlND.js → yaml-D8bBNHE4.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +5 -1
- package/src/main/agentMemory.cjs +267 -0
- package/src/main/docEditor.cjs +92 -0
- package/src/main/files.cjs +346 -0
- package/src/main/git.cjs +333 -0
- package/src/main/historyAggregator.cjs +70 -0
- package/src/main/index.cjs +66 -0
- package/src/main/ipcSchemas.cjs +75 -0
- package/src/main/projectSkills.cjs +124 -0
- package/src/main/scheduler.cjs +155 -11
- package/src/main/superagent.cjs +202 -0
- package/src/main/transcripts.cjs +8 -1
- package/src/preload/api.d.ts +215 -0
- package/src/preload/index.cjs +54 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{d as M,f as w,U as F,a as P,M as v,l as f,t as K,R as O}from"./index-C7ljEoqc.js";class E{constructor(e,t){this._modeId=e,this._defaults=t,this._worker=null,this._client=null,this._configChangeListener=this._defaults.onDidChange(()=>this._stopWorker()),this._updateExtraLibsToken=0,this._extraLibsChangeListener=this._defaults.onDidExtraLibsChange(()=>this._updateExtraLibs())}dispose(){this._configChangeListener.dispose(),this._extraLibsChangeListener.dispose(),this._stopWorker()}_stopWorker(){this._worker&&(this._worker.dispose(),this._worker=null),this._client=null}async _updateExtraLibs(){if(!this._worker)return;const e=++this._updateExtraLibsToken,t=await this._worker.getProxy();this._updateExtraLibsToken===e&&t.updateExtraLibs(this._defaults.getExtraLibs())}_getClient(){return this._client||(this._client=(async()=>(this._worker=M({moduleId:"vs/language/typescript/tsWorker",createWorker:()=>new Worker(new URL(""+new URL("ts.worker-59MjiAqk.js",import.meta.url).href,import.meta.url),{type:"module"}),label:this._modeId,keepIdleModels:!0,createData:{compilerOptions:this._defaults.getCompilerOptions(),extraLibs:this._defaults.getExtraLibs(),customWorkerPath:this._defaults.workerOptions.customWorkerPath,inlayHintsOptions:this._defaults.inlayHintsOptions}}),this._defaults.getEagerModelSync()?await this._worker.withSyncedResources(w.getModels().filter(e=>e.getLanguageId()===this._modeId).map(e=>e.uri)):await this._worker.getProxy()))()),this._client}async getLanguageServiceWorker(...e){const t=await this._getClient();return this._worker&&await this._worker.withSyncedResources(e),t}}const s={};s["lib.d.ts"]=!0;s["lib.decorators.d.ts"]=!0;s["lib.decorators.legacy.d.ts"]=!0;s["lib.dom.asynciterable.d.ts"]=!0;s["lib.dom.d.ts"]=!0;s["lib.dom.iterable.d.ts"]=!0;s["lib.es2015.collection.d.ts"]=!0;s["lib.es2015.core.d.ts"]=!0;s["lib.es2015.d.ts"]=!0;s["lib.es2015.generator.d.ts"]=!0;s["lib.es2015.iterable.d.ts"]=!0;s["lib.es2015.promise.d.ts"]=!0;s["lib.es2015.proxy.d.ts"]=!0;s["lib.es2015.reflect.d.ts"]=!0;s["lib.es2015.symbol.d.ts"]=!0;s["lib.es2015.symbol.wellknown.d.ts"]=!0;s["lib.es2016.array.include.d.ts"]=!0;s["lib.es2016.d.ts"]=!0;s["lib.es2016.full.d.ts"]=!0;s["lib.es2016.intl.d.ts"]=!0;s["lib.es2017.arraybuffer.d.ts"]=!0;s["lib.es2017.d.ts"]=!0;s["lib.es2017.date.d.ts"]=!0;s["lib.es2017.full.d.ts"]=!0;s["lib.es2017.intl.d.ts"]=!0;s["lib.es2017.object.d.ts"]=!0;s["lib.es2017.sharedmemory.d.ts"]=!0;s["lib.es2017.string.d.ts"]=!0;s["lib.es2017.typedarrays.d.ts"]=!0;s["lib.es2018.asyncgenerator.d.ts"]=!0;s["lib.es2018.asynciterable.d.ts"]=!0;s["lib.es2018.d.ts"]=!0;s["lib.es2018.full.d.ts"]=!0;s["lib.es2018.intl.d.ts"]=!0;s["lib.es2018.promise.d.ts"]=!0;s["lib.es2018.regexp.d.ts"]=!0;s["lib.es2019.array.d.ts"]=!0;s["lib.es2019.d.ts"]=!0;s["lib.es2019.full.d.ts"]=!0;s["lib.es2019.intl.d.ts"]=!0;s["lib.es2019.object.d.ts"]=!0;s["lib.es2019.string.d.ts"]=!0;s["lib.es2019.symbol.d.ts"]=!0;s["lib.es2020.bigint.d.ts"]=!0;s["lib.es2020.d.ts"]=!0;s["lib.es2020.date.d.ts"]=!0;s["lib.es2020.full.d.ts"]=!0;s["lib.es2020.intl.d.ts"]=!0;s["lib.es2020.number.d.ts"]=!0;s["lib.es2020.promise.d.ts"]=!0;s["lib.es2020.sharedmemory.d.ts"]=!0;s["lib.es2020.string.d.ts"]=!0;s["lib.es2020.symbol.wellknown.d.ts"]=!0;s["lib.es2021.d.ts"]=!0;s["lib.es2021.full.d.ts"]=!0;s["lib.es2021.intl.d.ts"]=!0;s["lib.es2021.promise.d.ts"]=!0;s["lib.es2021.string.d.ts"]=!0;s["lib.es2021.weakref.d.ts"]=!0;s["lib.es2022.array.d.ts"]=!0;s["lib.es2022.d.ts"]=!0;s["lib.es2022.error.d.ts"]=!0;s["lib.es2022.full.d.ts"]=!0;s["lib.es2022.intl.d.ts"]=!0;s["lib.es2022.object.d.ts"]=!0;s["lib.es2022.regexp.d.ts"]=!0;s["lib.es2022.string.d.ts"]=!0;s["lib.es2023.array.d.ts"]=!0;s["lib.es2023.collection.d.ts"]=!0;s["lib.es2023.d.ts"]=!0;s["lib.es2023.full.d.ts"]=!0;s["lib.es2023.intl.d.ts"]=!0;s["lib.es2024.arraybuffer.d.ts"]=!0;s["lib.es2024.collection.d.ts"]=!0;s["lib.es2024.d.ts"]=!0;s["lib.es2024.full.d.ts"]=!0;s["lib.es2024.object.d.ts"]=!0;s["lib.es2024.promise.d.ts"]=!0;s["lib.es2024.regexp.d.ts"]=!0;s["lib.es2024.sharedmemory.d.ts"]=!0;s["lib.es2024.string.d.ts"]=!0;s["lib.es5.d.ts"]=!0;s["lib.es6.d.ts"]=!0;s["lib.esnext.array.d.ts"]=!0;s["lib.esnext.collection.d.ts"]=!0;s["lib.esnext.d.ts"]=!0;s["lib.esnext.decorators.d.ts"]=!0;s["lib.esnext.disposable.d.ts"]=!0;s["lib.esnext.error.d.ts"]=!0;s["lib.esnext.float16.d.ts"]=!0;s["lib.esnext.full.d.ts"]=!0;s["lib.esnext.intl.d.ts"]=!0;s["lib.esnext.iterator.d.ts"]=!0;s["lib.esnext.promise.d.ts"]=!0;s["lib.esnext.sharedmemory.d.ts"]=!0;s["lib.scripthost.d.ts"]=!0;s["lib.webworker.asynciterable.d.ts"]=!0;s["lib.webworker.d.ts"]=!0;s["lib.webworker.importscripts.d.ts"]=!0;s["lib.webworker.iterable.d.ts"]=!0;function A(c,e,t=0){if(typeof c=="string")return c;if(c===void 0)return"";let o="";if(t){o+=e;for(let i=0;i<t;i++)o+=" "}if(o+=c.messageText,t++,c.next)for(const i of c.next)o+=A(i,e,t);return o}function _(c){return c?c.map(e=>e.text).join(""):""}class y{constructor(e){this._worker=e}_textSpanToRange(e,t){let o=e.getPositionAt(t.start),i=e.getPositionAt(t.start+t.length),{lineNumber:l,column:p}=o,{lineNumber:n,column:r}=i;return{startLineNumber:l,startColumn:p,endLineNumber:n,endColumn:r}}}class H{constructor(e){this._worker=e,this._libFiles={},this._hasFetchedLibFiles=!1,this._fetchLibFilesPromise=null}isLibFile(e){return e&&e.path.indexOf("/lib.")===0?!!s[e.path.slice(1)]:!1}getOrCreateModel(e){const t=F.parse(e),o=w.getModel(t);if(o)return o;if(this.isLibFile(t)&&this._hasFetchedLibFiles)return w.createModel(this._libFiles[t.path.slice(1)],"typescript",t);const i=K.getExtraLibs()[e];return i?w.createModel(i.content,"typescript",t):null}_containsLibFile(e){for(let t of e)if(this.isLibFile(t))return!0;return!1}async fetchLibFilesIfNecessary(e){this._containsLibFile(e)&&await this._fetchLibFiles()}_fetchLibFiles(){return this._fetchLibFilesPromise||(this._fetchLibFilesPromise=this._worker().then(e=>e.getLibFiles()).then(e=>{this._hasFetchedLibFiles=!0,this._libFiles=e})),this._fetchLibFilesPromise}}class W extends y{constructor(e,t,o,i){super(i),this._libFiles=e,this._defaults=t,this._selector=o,this._disposables=[],this._listener=Object.create(null);const l=r=>{if(r.getLanguageId()!==o)return;const u=()=>{const{onlyVisible:h}=this._defaults.getDiagnosticsOptions();h?r.isAttachedToEditor()&&this._doValidate(r):this._doValidate(r)};let d;const g=r.onDidChangeContent(()=>{clearTimeout(d),d=window.setTimeout(u,500)}),b=r.onDidChangeAttached(()=>{const{onlyVisible:h}=this._defaults.getDiagnosticsOptions();h&&(r.isAttachedToEditor()?u():w.setModelMarkers(r,this._selector,[]))});this._listener[r.uri.toString()]={dispose(){g.dispose(),b.dispose(),clearTimeout(d)}},u()},p=r=>{w.setModelMarkers(r,this._selector,[]);const u=r.uri.toString();this._listener[u]&&(this._listener[u].dispose(),delete this._listener[u])};this._disposables.push(w.onDidCreateModel(r=>l(r))),this._disposables.push(w.onWillDisposeModel(p)),this._disposables.push(w.onDidChangeModelLanguage(r=>{p(r.model),l(r.model)})),this._disposables.push({dispose(){for(const r of w.getModels())p(r)}});const n=()=>{for(const r of w.getModels())p(r),l(r)};this._disposables.push(this._defaults.onDidChange(n)),this._disposables.push(this._defaults.onDidExtraLibsChange(n)),w.getModels().forEach(r=>l(r))}dispose(){this._disposables.forEach(e=>e&&e.dispose()),this._disposables=[]}async _doValidate(e){const t=await this._worker(e.uri);if(e.isDisposed())return;const o=[],{noSyntaxValidation:i,noSemanticValidation:l,noSuggestionDiagnostics:p}=this._defaults.getDiagnosticsOptions();i||o.push(t.getSyntacticDiagnostics(e.uri.toString())),l||o.push(t.getSemanticDiagnostics(e.uri.toString())),p||o.push(t.getSuggestionDiagnostics(e.uri.toString()));const n=await Promise.all(o);if(!n||e.isDisposed())return;const r=n.reduce((d,g)=>g.concat(d),[]).filter(d=>(this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore||[]).indexOf(d.code)===-1),u=r.map(d=>d.relatedInformation||[]).reduce((d,g)=>g.concat(d),[]).map(d=>d.file?F.parse(d.file.fileName):null);await this._libFiles.fetchLibFilesIfNecessary(u),!e.isDisposed()&&w.setModelMarkers(e,this._selector,r.map(d=>this._convertDiagnostics(e,d)))}_convertDiagnostics(e,t){const o=t.start||0,i=t.length||1,{lineNumber:l,column:p}=e.getPositionAt(o),{lineNumber:n,column:r}=e.getPositionAt(o+i),u=[];return t.reportsUnnecessary&&u.push(P.Unnecessary),t.reportsDeprecated&&u.push(P.Deprecated),{severity:this._tsDiagnosticCategoryToMarkerSeverity(t.category),startLineNumber:l,startColumn:p,endLineNumber:n,endColumn:r,message:A(t.messageText,`
|
|
2
2
|
`),code:t.code.toString(),tags:u,relatedInformation:this._convertRelatedInformation(e,t.relatedInformation)}}_convertRelatedInformation(e,t){if(!t)return[];const o=[];return t.forEach(i=>{let l=e;if(i.file&&(l=this._libFiles.getOrCreateModel(i.file.fileName)),!l)return;const p=i.start||0,n=i.length||1,{lineNumber:r,column:u}=l.getPositionAt(p),{lineNumber:d,column:g}=l.getPositionAt(p+n);o.push({resource:l.uri,startLineNumber:r,startColumn:u,endLineNumber:d,endColumn:g,message:A(i.messageText,`
|
|
3
3
|
`)})}),o}_tsDiagnosticCategoryToMarkerSeverity(e){switch(e){case 1:return v.Error;case 3:return v.Info;case 0:return v.Warning;case 2:return v.Hint}return v.Info}}class C extends y{get triggerCharacters(){return["."]}async provideCompletionItems(e,t,o,i){const l=e.getWordUntilPosition(t),p=new O(t.lineNumber,l.startColumn,t.lineNumber,l.endColumn),n=e.uri,r=e.getOffsetAt(t),u=await this._worker(n);if(e.isDisposed())return;const d=await u.getCompletionsAtPosition(n.toString(),r);return!d||e.isDisposed()?void 0:{suggestions:d.entries.map(b=>{let h=p;if(b.replacementSpan){const x=e.getPositionAt(b.replacementSpan.start),D=e.getPositionAt(b.replacementSpan.start+b.replacementSpan.length);h=new O(x.lineNumber,x.column,D.lineNumber,D.column)}const k=[];return b.kindModifiers!==void 0&&b.kindModifiers.indexOf("deprecated")!==-1&&k.push(f.CompletionItemTag.Deprecated),{uri:n,position:t,offset:r,range:h,label:b.name,insertText:b.name,sortText:b.sortText,kind:C.convertKind(b.kind),tags:k}})}}async resolveCompletionItem(e,t){const o=e,i=o.uri,l=o.position,p=o.offset,r=await(await this._worker(i)).getCompletionEntryDetails(i.toString(),p,o.label);return r?{uri:i,position:l,label:r.name,kind:C.convertKind(r.kind),detail:_(r.displayParts),documentation:{value:C.createDocumentationString(r)}}:o}static convertKind(e){switch(e){case a.primitiveType:case a.keyword:return f.CompletionItemKind.Keyword;case a.variable:case a.localVariable:return f.CompletionItemKind.Variable;case a.memberVariable:case a.memberGetAccessor:case a.memberSetAccessor:return f.CompletionItemKind.Field;case a.function:case a.memberFunction:case a.constructSignature:case a.callSignature:case a.indexSignature:return f.CompletionItemKind.Function;case a.enum:return f.CompletionItemKind.Enum;case a.module:return f.CompletionItemKind.Module;case a.class:return f.CompletionItemKind.Class;case a.interface:return f.CompletionItemKind.Interface;case a.warning:return f.CompletionItemKind.File}return f.CompletionItemKind.Property}static createDocumentationString(e){let t=_(e.documentation);if(e.tags)for(const o of e.tags)t+=`
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./index-
|
|
1
|
+
import{l as e}from"./index-C7ljEoqc.js";const n={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],onEnterRules:[{beforeText:/^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,afterText:/^\s*\*\/$/,action:{indentAction:e.IndentAction.IndentOutdent,appendText:" * "}},{beforeText:/^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,action:{indentAction:e.IndentAction.None,appendText:" * "}},{beforeText:/^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,action:{indentAction:e.IndentAction.None,appendText:"* "}},{beforeText:/^(\t|(\ \ ))*\ \*\/\s*$/,action:{indentAction:e.IndentAction.None,removeText:1}}],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"',notIn:["string"]},{open:"'",close:"'",notIn:["string","comment"]},{open:"`",close:"`",notIn:["string","comment"]},{open:"/**",close:" */",notIn:["string"]}],folding:{markers:{start:new RegExp("^\\s*//\\s*#?region\\b"),end:new RegExp("^\\s*//\\s*#?endregion\\b")}}},o={defaultToken:"invalid",tokenPostfix:".ts",keywords:["abstract","any","as","asserts","bigint","boolean","break","case","catch","class","continue","const","constructor","debugger","declare","default","delete","do","else","enum","export","extends","false","finally","for","from","function","get","if","implements","import","in","infer","instanceof","interface","is","keyof","let","module","namespace","never","new","null","number","object","out","package","private","protected","public","override","readonly","require","global","return","satisfies","set","static","string","super","switch","symbol","this","throw","true","try","type","typeof","undefined","unique","unknown","var","void","while","with","yield","async","await","of"],operators:["<=",">=","==","!=","===","!==","=>","+","-","**","*","/","%","++","--","<<","</",">>",">>>","&","|","^","!","~","&&","||","??","?",":","=","+=","-=","*=","**=","/=","%=","<<=",">>=",">>>=","&=","|=","^=","@"],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,digits:/\d+(_+\d+)*/,octaldigits:/[0-7]+(_+[0-7]+)*/,binarydigits:/[0-1]+(_+[0-1]+)*/,hexdigits:/[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,regexpctl:/[(){}\[\]\$\^|\-*+?\.]/,regexpesc:/\\(?:[bBdDfnrstvwWn0\\\/]|@regexpctl|c[A-Z]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/,tokenizer:{root:[[/[{}]/,"delimiter.bracket"],{include:"common"}],common:[[/#?[a-z_$][\w$]*/,{cases:{"@keywords":"keyword","@default":"identifier"}}],[/[A-Z][\w\$]*/,"type.identifier"],{include:"@whitespace"},[/\/(?=([^\\\/]|\\.)+\/([dgimsuy]*)(\s*)(\.|;|,|\)|\]|\}|$))/,{token:"regexp",bracket:"@open",next:"@regexp"}],[/[()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/!(?=([^=]|$))/,"delimiter"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/(@digits)[eE]([\-+]?(@digits))?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?/,"number.float"],[/0[xX](@hexdigits)n?/,"number.hex"],[/0[oO]?(@octaldigits)n?/,"number.octal"],[/0[bB](@binarydigits)n?/,"number.binary"],[/(@digits)n?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string_double"],[/'/,"string","@string_single"],[/`/,"string","@string_backtick"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@jsdoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],jsdoc:[[/[^\/*]+/,"comment.doc"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],regexp:[[/(\{)(\d+(?:,\d*)?)(\})/,["regexp.escape.control","regexp.escape.control","regexp.escape.control"]],[/(\[)(\^?)(?=(?:[^\]\\\/]|\\.)+)/,["regexp.escape.control",{token:"regexp.escape.control",next:"@regexrange"}]],[/(\()(\?:|\?=|\?!)/,["regexp.escape.control","regexp.escape.control"]],[/[()]/,"regexp.escape.control"],[/@regexpctl/,"regexp.escape.control"],[/[^\\\/]/,"regexp"],[/@regexpesc/,"regexp.escape"],[/\\\./,"regexp.invalid"],[/(\/)([dgimsuy]*)/,[{token:"regexp",bracket:"@close",next:"@pop"},"keyword.other"]]],regexrange:[[/-/,"regexp.escape.control"],[/\^/,"regexp.invalid"],[/@regexpesc/,"regexp.escape"],[/[^\]]/,"regexp"],[/\]/,{token:"regexp.escape.control",next:"@pop",bracket:"@close"}]],string_double:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],string_single:[[/[^\\']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/'/,"string","@pop"]],string_backtick:[[/\$\{/,{token:"delimiter.bracket",next:"@bracketCounting"}],[/[^\\`$]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/`/,"string","@pop"]],bracketCounting:[[/\{/,"delimiter.bracket","@bracketCounting"],[/\}/,"delimiter.bracket","@pop"],{include:"common"}]}};export{n as conf,o as language};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./index-
|
|
1
|
+
import{l as e}from"./index-C7ljEoqc.js";const n={comments:{blockComment:["<!--","-->"]},brackets:[["<",">"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],onEnterRules:[{beforeText:new RegExp("<([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:e.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:e.IndentAction.Indent}}]},o={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[/<!--/,{token:"comment",next:"@comment"}]],comment:[[/[^<\-]+/,"comment.content"],[/-->/,{token:"comment",next:"@pop"}],[/<!--/,"comment.content.invalid"],[/[<\-]/,"comment.content"]]}};export{n as conf,o as language};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./index-
|
|
1
|
+
import{l as e}from"./index-C7ljEoqc.js";const t={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:e.IndentAction.Indent}}]},o={tokenPostfix:".yaml",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.square",open:"[",close:"]"}],keywords:["true","True","TRUE","false","False","FALSE","null","Null","Null","~"],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:"@whitespace"},{include:"@comment"},[/%[^ ]+.*$/,"meta.directive"],[/---/,"operators.directivesEnd"],[/\.{3}/,"operators.documentEnd"],[/[-?:](?= )/,"operators"],{include:"@anchor"},{include:"@tagHandle"},{include:"@flowCollections"},{include:"@blockStyle"},[/@numberInteger(?![ \t]*\S+)/,"number"],[/@numberFloat(?![ \t]*\S+)/,"number.float"],[/@numberOctal(?![ \t]*\S+)/,"number.octal"],[/@numberHex(?![ \t]*\S+)/,"number.hex"],[/@numberInfinity(?![ \t]*\S+)/,"number.infinity"],[/@numberNaN(?![ \t]*\S+)/,"number.nan"],[/@numberDate(?![ \t]*\S+)/,"number.date"],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,["type","white","operators","white"]],{include:"@flowScalars"},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":"keyword","@default":"string"}}]],object:[{include:"@whitespace"},{include:"@comment"},[/\}/,"@brackets","@pop"],[/,/,"delimiter.comma"],[/:(?= )/,"operators"],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,"type"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\},]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],array:[{include:"@whitespace"},{include:"@comment"},[/\]/,"@brackets","@pop"],[/,/,"delimiter.comma"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\],]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],multiString:[[/^( +).+$/,"string","@multiStringContinued.$1"]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":"string","@default":{token:"@rematch",next:"@popall"}}}]],whitespace:[[/[ \t\r\n]+/,"white"]],comment:[[/#.*$/,"comment"]],flowCollections:[[/\[/,"@brackets","@array"],[/\{/,"@brackets","@object"]],flowScalars:[[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/'[^']*'/,"string"],[/"/,"string","@doubleQuotedString"]],doubleQuotedString:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],blockStyle:[[/[>|][0-9]*[+-]?$/,"operators","@multiString"]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,"number"],[/@numberFloat(?=[ \t]*[,\]\}])/,"number.float"],[/@numberOctal(?=[ \t]*[,\]\}])/,"number.octal"],[/@numberHex(?=[ \t]*[,\]\}])/,"number.hex"],[/@numberInfinity(?=[ \t]*[,\]\}])/,"number.infinity"],[/@numberNaN(?=[ \t]*[,\]\}])/,"number.nan"],[/@numberDate(?=[ \t]*[,\]\}])/,"number.date"]],tagHandle:[[/\![^ ]*/,"tag"]],anchor:[[/[&*][^ ]+/,"namespace"]]}};export{t as conf,o as language};
|
package/dist/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Claude Session Manager</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-C7ljEoqc.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CH3K1pkS.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body class="bg-bg text-fg font-mono antialiased">
|
|
11
11
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-session-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Local cockpit for the Claude Code CLI — multi-tab terminal, full config surface, scheduler, voice dictation, and live observability.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main/index.cjs",
|
|
@@ -82,6 +82,9 @@
|
|
|
82
82
|
"@opentelemetry/sdk-trace-node": "^1.30.0",
|
|
83
83
|
"@opentelemetry/semantic-conventions": "^1.28.0",
|
|
84
84
|
"@ricky0123/vad-web": "^0.0.30",
|
|
85
|
+
"@tiptap/extension-link": "^3.23.6",
|
|
86
|
+
"@tiptap/react": "^3.23.6",
|
|
87
|
+
"@tiptap/starter-kit": "^3.23.6",
|
|
85
88
|
"@xterm/addon-fit": "^0.10.0",
|
|
86
89
|
"@xterm/addon-web-links": "^0.11.0",
|
|
87
90
|
"@xterm/xterm": "^5.5.0",
|
|
@@ -92,6 +95,7 @@
|
|
|
92
95
|
"node-pty": "^1.2.0-beta.12",
|
|
93
96
|
"onnxruntime-web": "^1.24.3",
|
|
94
97
|
"recharts": "^2.15.4",
|
|
98
|
+
"tiptap-markdown": "^0.9.0",
|
|
95
99
|
"zod": "^3.23.8",
|
|
96
100
|
"zustand": "^5.0.0"
|
|
97
101
|
},
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentMemory.cjs — per-subagent memory backend.
|
|
3
|
+
*
|
|
4
|
+
* Distinct from the workspace-scoped Memory tool (memoryTool.cjs): this stores
|
|
5
|
+
* memory entries keyed by **agentId** (the subagent name, e.g. "code-reviewer"),
|
|
6
|
+
* not by workspace cwd. Conceptually mirrors Anthropic's "give each subagent its
|
|
7
|
+
* own scratchpad" pattern.
|
|
8
|
+
*
|
|
9
|
+
* Storage: `~/.claude/session-manager/agent-memory/<agentId>.json`
|
|
10
|
+
*
|
|
11
|
+
* {
|
|
12
|
+
* "agentId": "code-reviewer",
|
|
13
|
+
* "entries": [
|
|
14
|
+
* { "id": "mem_<ts>_<rand>", "body": "<markdown>",
|
|
15
|
+
* "category": "command|preference|pattern|failure|workflow" (optional),
|
|
16
|
+
* "createdAt": <epoch ms>, "updatedAt": <epoch ms> }
|
|
17
|
+
* ],
|
|
18
|
+
* "lastUpdated": <epoch ms>
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* Deviation from Unleashed (which keys by projectPath hash and writes to
|
|
22
|
+
* `~/.claudecodeui/agent-memory/<hash>.json` with raw fs.writeFile): we key by
|
|
23
|
+
* agentId per the user's spec, write under `~/.claude/session-manager/` (the
|
|
24
|
+
* conventional spot for this app's own state), and route every mutation through
|
|
25
|
+
* config.cjs's writeJson / validatePath helpers.
|
|
26
|
+
*
|
|
27
|
+
* Hard caps:
|
|
28
|
+
* - agentId must match /^[A-Za-z0-9._-]{1,128}$/
|
|
29
|
+
* - entryId must match /^[A-Za-z0-9._-]{1,128}$/
|
|
30
|
+
* - body up to 1 MiB
|
|
31
|
+
* - up to 200 entries per agent (oldest dropped when over cap on set())
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
'use strict';
|
|
35
|
+
|
|
36
|
+
const { ipcMain } = require('electron');
|
|
37
|
+
const fs = require('node:fs');
|
|
38
|
+
const fsp = require('node:fs/promises');
|
|
39
|
+
const path = require('node:path');
|
|
40
|
+
const os = require('node:os');
|
|
41
|
+
const config = require('./config.cjs');
|
|
42
|
+
|
|
43
|
+
const MAX_BODY_BYTES = 1024 * 1024; // 1 MiB
|
|
44
|
+
const MAX_ENTRIES = 200;
|
|
45
|
+
const AGENT_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
46
|
+
const ENTRY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
47
|
+
|
|
48
|
+
function rootDir() {
|
|
49
|
+
return path.join(os.homedir(), '.claude', 'session-manager', 'agent-memory');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function recordPath(agentId) {
|
|
53
|
+
if (!AGENT_ID_RE.test(agentId)) {
|
|
54
|
+
throw new Error(`invalid agentId (must match ${AGENT_ID_RE.source})`);
|
|
55
|
+
}
|
|
56
|
+
return path.join(rootDir(), `${agentId}.json`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function ensureRoot() {
|
|
60
|
+
await fsp.mkdir(rootDir(), { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function emptyRecord(agentId) {
|
|
64
|
+
return { agentId, entries: [], lastUpdated: 0 };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function loadRecord(agentId) {
|
|
68
|
+
const abs = recordPath(agentId);
|
|
69
|
+
const r = await config.readJson(abs).catch(() => ({ exists: false, data: null }));
|
|
70
|
+
if (!r || !r.exists || !r.data) return emptyRecord(agentId);
|
|
71
|
+
const data = r.data;
|
|
72
|
+
if (!data || typeof data !== 'object' || !Array.isArray(data.entries)) {
|
|
73
|
+
return emptyRecord(agentId);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
agentId,
|
|
77
|
+
entries: data.entries.filter(
|
|
78
|
+
(e) => e && typeof e === 'object' && typeof e.id === 'string' && typeof e.body === 'string'
|
|
79
|
+
),
|
|
80
|
+
lastUpdated: typeof data.lastUpdated === 'number' ? data.lastUpdated : 0,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function persist(record) {
|
|
85
|
+
await ensureRoot();
|
|
86
|
+
// Cap entry count: drop oldest by updatedAt (or createdAt) ascending.
|
|
87
|
+
if (record.entries.length > MAX_ENTRIES) {
|
|
88
|
+
record.entries = record.entries
|
|
89
|
+
.slice()
|
|
90
|
+
.sort((a, b) => (b.updatedAt ?? b.createdAt ?? 0) - (a.updatedAt ?? a.createdAt ?? 0))
|
|
91
|
+
.slice(0, MAX_ENTRIES);
|
|
92
|
+
}
|
|
93
|
+
const abs = recordPath(record.agentId);
|
|
94
|
+
const out = { ...record, lastUpdated: Date.now() };
|
|
95
|
+
await config.writeJson(abs, out);
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ────────────────────────────────────────────────── handlers
|
|
100
|
+
|
|
101
|
+
async function list({ agentId }) {
|
|
102
|
+
if (!AGENT_ID_RE.test(agentId || '')) {
|
|
103
|
+
return { entries: [], agentId: agentId || '', error: 'invalid agentId' };
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const record = await loadRecord(agentId);
|
|
107
|
+
const entries = record.entries
|
|
108
|
+
.slice()
|
|
109
|
+
.sort((a, b) => (b.updatedAt ?? b.createdAt ?? 0) - (a.updatedAt ?? a.createdAt ?? 0))
|
|
110
|
+
.map((e) => ({
|
|
111
|
+
id: e.id,
|
|
112
|
+
body: e.body,
|
|
113
|
+
category: e.category ?? null,
|
|
114
|
+
createdAt: e.createdAt ?? 0,
|
|
115
|
+
updatedAt: e.updatedAt ?? e.createdAt ?? 0,
|
|
116
|
+
bytes: Buffer.byteLength(e.body, 'utf8'),
|
|
117
|
+
}));
|
|
118
|
+
return { entries, agentId, error: null };
|
|
119
|
+
} catch (e) {
|
|
120
|
+
return { entries: [], agentId, error: e.message };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function get({ agentId, entryId }) {
|
|
125
|
+
if (!AGENT_ID_RE.test(agentId || '') || !ENTRY_ID_RE.test(entryId || '')) {
|
|
126
|
+
return { entry: null, error: 'invalid id' };
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const record = await loadRecord(agentId);
|
|
130
|
+
const entry = record.entries.find((e) => e.id === entryId) ?? null;
|
|
131
|
+
if (!entry) return { entry: null, error: null };
|
|
132
|
+
return {
|
|
133
|
+
entry: {
|
|
134
|
+
id: entry.id,
|
|
135
|
+
body: entry.body,
|
|
136
|
+
category: entry.category ?? null,
|
|
137
|
+
createdAt: entry.createdAt ?? 0,
|
|
138
|
+
updatedAt: entry.updatedAt ?? entry.createdAt ?? 0,
|
|
139
|
+
bytes: Buffer.byteLength(entry.body, 'utf8'),
|
|
140
|
+
},
|
|
141
|
+
error: null,
|
|
142
|
+
};
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return { entry: null, error: e.message };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function set({ agentId, entryId, body, category }) {
|
|
149
|
+
if (!AGENT_ID_RE.test(agentId || '')) {
|
|
150
|
+
return { ok: false, error: 'invalid agentId' };
|
|
151
|
+
}
|
|
152
|
+
if (!ENTRY_ID_RE.test(entryId || '')) {
|
|
153
|
+
return { ok: false, error: 'invalid entryId' };
|
|
154
|
+
}
|
|
155
|
+
if (typeof body !== 'string') {
|
|
156
|
+
return { ok: false, error: 'body must be a string' };
|
|
157
|
+
}
|
|
158
|
+
const bytes = Buffer.byteLength(body, 'utf8');
|
|
159
|
+
if (bytes > MAX_BODY_BYTES) {
|
|
160
|
+
return { ok: false, error: `body exceeds 1 MiB cap (${bytes} bytes)` };
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const record = await loadRecord(agentId);
|
|
164
|
+
const now = Date.now();
|
|
165
|
+
const idx = record.entries.findIndex((e) => e.id === entryId);
|
|
166
|
+
if (idx >= 0) {
|
|
167
|
+
// Update — preserve createdAt
|
|
168
|
+
record.entries[idx] = {
|
|
169
|
+
...record.entries[idx],
|
|
170
|
+
id: entryId,
|
|
171
|
+
body,
|
|
172
|
+
category: category ?? record.entries[idx].category ?? null,
|
|
173
|
+
updatedAt: now,
|
|
174
|
+
};
|
|
175
|
+
} else {
|
|
176
|
+
// Enforce cap on create
|
|
177
|
+
if (record.entries.length >= MAX_ENTRIES) {
|
|
178
|
+
return { ok: false, error: `agent at ${MAX_ENTRIES}-entry cap` };
|
|
179
|
+
}
|
|
180
|
+
record.entries.push({
|
|
181
|
+
id: entryId,
|
|
182
|
+
body,
|
|
183
|
+
category: category ?? null,
|
|
184
|
+
createdAt: now,
|
|
185
|
+
updatedAt: now,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
await persist(record);
|
|
189
|
+
return { ok: true, error: null };
|
|
190
|
+
} catch (e) {
|
|
191
|
+
return { ok: false, error: e.message };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function deleteEntry({ agentId, entryId }) {
|
|
196
|
+
if (!AGENT_ID_RE.test(agentId || '') || !ENTRY_ID_RE.test(entryId || '')) {
|
|
197
|
+
return { ok: false, error: 'invalid id' };
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const record = await loadRecord(agentId);
|
|
201
|
+
const before = record.entries.length;
|
|
202
|
+
record.entries = record.entries.filter((e) => e.id !== entryId);
|
|
203
|
+
if (record.entries.length === before) {
|
|
204
|
+
// No-op delete (entry didn't exist) — treat as success, same as fs unlink ENOENT.
|
|
205
|
+
return { ok: true, error: null };
|
|
206
|
+
}
|
|
207
|
+
// If we just emptied the record, remove the file outright so listProjects()
|
|
208
|
+
// doesn't report a stale ghost agent.
|
|
209
|
+
if (record.entries.length === 0) {
|
|
210
|
+
const abs = recordPath(agentId);
|
|
211
|
+
let real;
|
|
212
|
+
try {
|
|
213
|
+
real = config.validatePath(abs);
|
|
214
|
+
config.validateWrite(real);
|
|
215
|
+
await fsp.unlink(real);
|
|
216
|
+
} catch (e) {
|
|
217
|
+
if (e.code !== 'ENOENT') {
|
|
218
|
+
// Fall back to persisting an empty record; better than throwing.
|
|
219
|
+
await persist(record);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return { ok: true, error: null };
|
|
223
|
+
}
|
|
224
|
+
await persist(record);
|
|
225
|
+
return { ok: true, error: null };
|
|
226
|
+
} catch (e) {
|
|
227
|
+
return { ok: false, error: e.message };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function listAgents() {
|
|
232
|
+
try {
|
|
233
|
+
await ensureRoot();
|
|
234
|
+
const dir = rootDir();
|
|
235
|
+
const r = await config.listDir(dir, { filesOnly: true });
|
|
236
|
+
if (!r.ok) return { agents: [], error: r.error };
|
|
237
|
+
const agents = r.entries
|
|
238
|
+
.filter((e) => e.name.endsWith('.json'))
|
|
239
|
+
.map((e) => ({
|
|
240
|
+
agentId: e.name.replace(/\.json$/, ''),
|
|
241
|
+
bytes: e.size,
|
|
242
|
+
mtimeMs: e.mtimeMs,
|
|
243
|
+
}))
|
|
244
|
+
.sort((a, b) => a.agentId.localeCompare(b.agentId));
|
|
245
|
+
return { agents, error: null };
|
|
246
|
+
} catch (e) {
|
|
247
|
+
return { agents: [], error: e.message };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function registerAgentMemoryHandlers() {
|
|
252
|
+
const { schemas: s, validated: v } = require('./ipcSchemas.cjs');
|
|
253
|
+
ipcMain.handle('agent-memory:list', v(s.agentMemoryList, list));
|
|
254
|
+
ipcMain.handle('agent-memory:get', v(s.agentMemoryGet, get));
|
|
255
|
+
ipcMain.handle('agent-memory:set', v(s.agentMemorySet, set));
|
|
256
|
+
ipcMain.handle('agent-memory:delete', v(s.agentMemoryDelete, deleteEntry));
|
|
257
|
+
ipcMain.handle('agent-memory:list-agents', () => listAgents());
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
module.exports = {
|
|
261
|
+
registerAgentMemoryHandlers,
|
|
262
|
+
// exported for tests
|
|
263
|
+
rootDir,
|
|
264
|
+
recordPath,
|
|
265
|
+
AGENT_ID_RE,
|
|
266
|
+
ENTRY_ID_RE,
|
|
267
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* docEditor.cjs — Doc Editor IPC (PRD 04).
|
|
3
|
+
*
|
|
4
|
+
* Three handlers:
|
|
5
|
+
* doc-editor:pick-file open native file picker, return validated path
|
|
6
|
+
* doc-editor:read-file read a text file, return { ok, text, mtimeMs }
|
|
7
|
+
* doc-editor:write-file atomic write, return { ok, mtimeMs }
|
|
8
|
+
*
|
|
9
|
+
* Write policy: uses validatePath (home-dir boundary) but NOT validateWrite
|
|
10
|
+
* (which would restrict to .claude/ / .config/ directories). The doc editor
|
|
11
|
+
* writes to user-chosen files anywhere in the home tree.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { ipcMain, dialog, BrowserWindow } = require('electron');
|
|
15
|
+
const fsp = require('node:fs/promises');
|
|
16
|
+
const path = require('node:path');
|
|
17
|
+
const os = require('node:os');
|
|
18
|
+
const { validatePath } = require('./config.cjs');
|
|
19
|
+
|
|
20
|
+
async function writeDocAtomic(abs, text) {
|
|
21
|
+
const real = validatePath(abs);
|
|
22
|
+
const dir = path.dirname(real);
|
|
23
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
24
|
+
const tmp = `${real}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
25
|
+
try {
|
|
26
|
+
await fsp.writeFile(tmp, text, 'utf8');
|
|
27
|
+
await fsp.rename(tmp, real);
|
|
28
|
+
} catch (e) {
|
|
29
|
+
try { await fsp.unlink(tmp); } catch { /* */ }
|
|
30
|
+
throw e;
|
|
31
|
+
}
|
|
32
|
+
const stat = await fsp.stat(real);
|
|
33
|
+
return { ok: true, mtimeMs: stat.mtimeMs };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function registerDocEditorHandlers() {
|
|
37
|
+
ipcMain.handle('doc-editor:pick-file', async (event, payload) => {
|
|
38
|
+
const lastDir = (typeof payload === 'object' && payload?.lastDir) ? payload.lastDir : os.homedir();
|
|
39
|
+
// Resolve the window from the sender so registration order doesn't matter.
|
|
40
|
+
const win = BrowserWindow.fromWebContents(event.sender) ?? BrowserWindow.getFocusedWindow();
|
|
41
|
+
const result = await dialog.showOpenDialog(win ?? undefined, {
|
|
42
|
+
defaultPath: lastDir,
|
|
43
|
+
properties: ['openFile'],
|
|
44
|
+
filters: [
|
|
45
|
+
{ name: 'Text / Markdown', extensions: ['md', 'txt', 'json', 'yaml', 'yml', 'toml', 'js', 'ts', 'tsx', 'jsx', 'css', 'html', 'sh'] },
|
|
46
|
+
{ name: 'All files', extensions: ['*'] },
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
if (result.canceled || !result.filePaths.length) return { path: null };
|
|
50
|
+
let real;
|
|
51
|
+
try {
|
|
52
|
+
real = validatePath(result.filePaths[0]);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
return { path: null, error: e.message };
|
|
55
|
+
}
|
|
56
|
+
return { path: real };
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
ipcMain.handle('doc-editor:read-file', async (_e, payload) => {
|
|
60
|
+
const p = payload && typeof payload.path === 'string' ? payload.path : null;
|
|
61
|
+
if (!p) return { ok: false, error: 'path required' };
|
|
62
|
+
let real;
|
|
63
|
+
try {
|
|
64
|
+
real = validatePath(p);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return { ok: false, error: e.message };
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const [text, stat] = await Promise.all([
|
|
70
|
+
fsp.readFile(real, 'utf8'),
|
|
71
|
+
fsp.stat(real),
|
|
72
|
+
]);
|
|
73
|
+
return { ok: true, text, mtimeMs: stat.mtimeMs };
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return { ok: false, error: e.message };
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
ipcMain.handle('doc-editor:write-file', async (_e, payload) => {
|
|
80
|
+
const p = payload && typeof payload.path === 'string' ? payload.path : null;
|
|
81
|
+
const text = payload && typeof payload.text === 'string' ? payload.text : null;
|
|
82
|
+
if (!p) return { ok: false, error: 'path required' };
|
|
83
|
+
if (text === null) return { ok: false, error: 'text required' };
|
|
84
|
+
try {
|
|
85
|
+
return await writeDocAtomic(p, text);
|
|
86
|
+
} catch (e) {
|
|
87
|
+
return { ok: false, error: e.message };
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { registerDocEditorHandlers };
|