claude-code-session-manager 0.13.0 → 0.14.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 (43) hide show
  1. package/dist/assets/{TiptapBody-ZBlGHTXg.js → TiptapBody-CzNkD0kT.js} +1 -1
  2. package/dist/assets/{cssMode-Co9Kuq5g.js → cssMode-DUMBBgBO.js} +1 -1
  3. package/dist/assets/{freemarker2-B9Y0PjVt.js → freemarker2-BjnoO8uS.js} +1 -1
  4. package/dist/assets/{handlebars-BGL0s1rN.js → handlebars-CggfdTjZ.js} +1 -1
  5. package/dist/assets/{html-C8BuG-2M.js → html-Bcnp_pNA.js} +1 -1
  6. package/dist/assets/{htmlMode-BfCigMol.js → htmlMode-CFws8rf5.js} +1 -1
  7. package/dist/assets/{index-CkI_4Vt7.js → index-0geebEag.js} +1684 -1383
  8. package/dist/assets/{index-DewIHWBs.css → index-DOo1Vtox.css} +1 -1
  9. package/dist/assets/{javascript-sES7Ewin.js → javascript-DI4e0TwT.js} +1 -1
  10. package/dist/assets/{jsonMode-C7txz4wN.js → jsonMode-Ca0fgEAF.js} +1 -1
  11. package/dist/assets/{liquid-B4qxgigx.js → liquid-E7hk4hx9.js} +1 -1
  12. package/dist/assets/{lspLanguageFeatures-B5xgGS7m.js → lspLanguageFeatures-Wh7EFavn.js} +1 -1
  13. package/dist/assets/{mdx-De-8TTNY.js → mdx-iXIhpMBJ.js} +1 -1
  14. package/dist/assets/{python-SfayDkVl.js → python-laxy9eG1.js} +1 -1
  15. package/dist/assets/{razor-BRCdaeYo.js → razor-CatPL_eG.js} +1 -1
  16. package/dist/assets/{tsMode-DBTPpTr8.js → tsMode-D2WL55Cf.js} +1 -1
  17. package/dist/assets/{typescript-Bxbq3Vd7.js → typescript-B5DFbVoi.js} +1 -1
  18. package/dist/assets/{xml-DQBPiVNP.js → xml-BsStStQW.js} +1 -1
  19. package/dist/assets/{yaml-lyIgZXXz.js → yaml-rg2IIDpu.js} +1 -1
  20. package/dist/index.html +2 -2
  21. package/package.json +2 -1
  22. package/src/main/__tests__/runVerify.test.cjs +388 -0
  23. package/src/main/config.cjs +1 -7
  24. package/src/main/files.cjs +4 -37
  25. package/src/main/historyAggregator.cjs +37 -14
  26. package/src/main/index.cjs +53 -134
  27. package/src/main/ipcSchemas.cjs +4 -0
  28. package/src/main/lib/childWithLog.cjs +218 -0
  29. package/src/main/lib/expandHome.cjs +21 -0
  30. package/src/main/lib/insideHome.cjs +74 -21
  31. package/src/main/lib/openExternalApp.cjs +141 -0
  32. package/src/main/pluginInstall.cjs +39 -1
  33. package/src/main/pty.cjs +11 -4
  34. package/src/main/queueOps.cjs +2 -2
  35. package/src/main/runVerify.cjs +527 -0
  36. package/src/main/scheduler.cjs +335 -286
  37. package/src/main/search.cjs +1 -6
  38. package/src/main/superagent.cjs +10 -0
  39. package/src/main/supervisor.cjs +16 -8
  40. package/src/main/transcripts.cjs +6 -0
  41. package/src/main/watchers.cjs +2 -2
  42. package/src/preload/api.d.ts +16 -7
  43. package/src/preload/index.cjs +1 -0
@@ -1,4 +1,4 @@
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-CkI_4Vt7.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,`
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-0geebEag.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-CkI_4Vt7.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
+ import{l as e}from"./index-0geebEag.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-CkI_4Vt7.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
+ import{l as e}from"./index-0geebEag.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-CkI_4Vt7.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};
1
+ import{l as e}from"./index-0geebEag.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
@@ -7,8 +7,8 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
9
  <link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;0,6..72,700;1,6..72,400&family=Geist:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
10
- <script type="module" crossorigin src="./assets/index-CkI_4Vt7.js"></script>
11
- <link rel="stylesheet" crossorigin href="./assets/index-DewIHWBs.css">
10
+ <script type="module" crossorigin src="./assets/index-0geebEag.js"></script>
11
+ <link rel="stylesheet" crossorigin href="./assets/index-DOo1Vtox.css">
12
12
  </head>
13
13
  <body class="bg-bg text-fg font-sans antialiased">
14
14
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-session-manager",
3
- "version": "0.13.0",
3
+ "version": "0.14.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",
@@ -27,6 +27,7 @@
27
27
  "postinstall": "electron-rebuild -f -w node-pty",
28
28
  "prepublishOnly": "vite build",
29
29
  "test:unit": "vitest run",
30
+ "test:verify": "node src/main/__tests__/runVerify.test.cjs",
30
31
  "test:e2e": "xvfb-run -a playwright test",
31
32
  "smoke:darwin": "playwright test tests/smoke/darwin-boot.spec.ts",
32
33
  "test:e2e:mic": "xvfb-run -a playwright test e2e/mic.spec.mjs",
@@ -0,0 +1,388 @@
1
+ /**
2
+ * runVerify.test.cjs — unit tests for the post-run reconciliation verifier.
3
+ *
4
+ * Run standalone: node src/main/__tests__/runVerify.test.cjs
5
+ *
6
+ * Three fixture scenarios (matching the 2026-05-23→24 incident window):
7
+ *
8
+ * 1. Case 1 (false-PASS): M2 acceptance script prints test failures + Traceback,
9
+ * then echoes "M2 acceptance: PASS". Scheduler used to stamp 'completed'.
10
+ * Expected: transcript_errors → needs_review
11
+ *
12
+ * 2. Case 2 (HALT): M7 agent correctly halts because M1 soak is unmet.
13
+ * Expected: halt → pending, reason embeds soak floor date
14
+ *
15
+ * 3. Case 3 (deps_unmet): PRD 56 has an unsatisfied dependsOn entry.
16
+ * Expected: deps_unmet → pending
17
+ *
18
+ * 4. Truly clean: all tests pass, no error markers.
19
+ * Expected: clean → null (caller may stamp 'completed')
20
+ */
21
+
22
+ 'use strict';
23
+
24
+ const { test } = require('node:test');
25
+ const assert = require('node:assert/strict');
26
+ const os = require('node:os');
27
+ const fs = require('node:fs');
28
+ const path = require('node:path');
29
+ const { verifyRun } = require('../runVerify.cjs');
30
+
31
+ // ─── helpers ──────────────────────────────────────────────────────────────────
32
+
33
+ /** Create a temp directory that is cleaned up after the test. */
34
+ function makeTmpDir() {
35
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'run-verify-test-'));
36
+ }
37
+
38
+ function rmdir(dir) {
39
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* */ }
40
+ }
41
+
42
+ /**
43
+ * Build a minimal stream-JSON log file.
44
+ * `events` is an array of raw JSON objects to serialise one-per-line.
45
+ * A "[scheduler] starting..." prefix line is also prepended (must be skipped).
46
+ */
47
+ function writeLog(dir, slug, events) {
48
+ const lines = ['[scheduler] starting ' + slug + ' at 2026-05-24T00:00:00.000Z'];
49
+ for (const ev of events) lines.push(JSON.stringify(ev));
50
+ lines.push('[scheduler] exit code=0 (raw code=0 signal=null) duration=47s');
51
+ fs.writeFileSync(path.join(dir, `${slug}.log`), lines.join('\n') + '\n');
52
+ }
53
+
54
+ /** Write a minimal PRD .md file with frontmatter + body. */
55
+ function writePrd(dir, slug, body) {
56
+ const text = `---\ntitle: Test PRD\ncwd: /tmp\nestimateMinutes: 30\n---\n${body}\n`;
57
+ fs.writeFileSync(path.join(dir, `${slug}.md`), text);
58
+ return path.join(dir, `${slug}.md`);
59
+ }
60
+
61
+ // ─── fixture: Case 1 — false-PASS (M2 acceptance) ─────────────────────────
62
+
63
+ test('Case 1: false-PASS (Traceback + echo PASS) → transcript_errors/needs_review', async () => {
64
+ const tmp = makeTmpDir();
65
+ try {
66
+ const slug = '39-signal-builder-m2-sentiment-stack';
67
+ const logEvents = [
68
+ // Assistant turn: run acceptance script
69
+ {
70
+ type: 'assistant',
71
+ message: {
72
+ role: 'assistant',
73
+ content: [{
74
+ type: 'tool_use',
75
+ id: 'toolu_m2_001',
76
+ name: 'Bash',
77
+ input: {
78
+ command: 'bash check_m2.sh',
79
+ description: 'Run M2 acceptance checks',
80
+ },
81
+ }],
82
+ },
83
+ },
84
+ // Tool result: test failures then echo PASS (the false-positive pattern)
85
+ {
86
+ type: 'user',
87
+ message: {
88
+ role: 'user',
89
+ content: [{
90
+ type: 'tool_result',
91
+ tool_use_id: 'toolu_m2_001',
92
+ content: [
93
+ '=== Signal-builder side ===',
94
+ 'ERROR tests/test_news_signals_panel.py',
95
+ '!!!!!!!! stopping after 1 failures !!!!!!!!',
96
+ '1 error in 0.03s',
97
+ 'SB: 0',
98
+ '=== Sanity: SB sentiment_cache.db rows ===',
99
+ 'rows: 0',
100
+ '=== contract.json panels.sentiment ===',
101
+ 'Traceback (most recent call last):',
102
+ ' File "<string>", line 1, in <module>',
103
+ "KeyError: 'panels.sentiment'",
104
+ 'M2 acceptance: PASS',
105
+ ].join('\n'),
106
+ is_error: false,
107
+ }],
108
+ },
109
+ },
110
+ // Result: agent declares success
111
+ {
112
+ type: 'result',
113
+ subtype: 'success',
114
+ result: 'M2 acceptance criteria verified. All checks passed.',
115
+ },
116
+ ];
117
+
118
+ writeLog(tmp, slug, logEvents);
119
+ const prdPath = writePrd(tmp, slug, '# M2 Sentiment Stack\n\nBuild the sentiment pipeline.');
120
+
121
+ const verdict = await verifyRun({
122
+ runDir: tmp,
123
+ prdPath,
124
+ queueEntry: { slug, status: 'running' },
125
+ allJobs: [],
126
+ });
127
+
128
+ assert.equal(verdict.verdict, 'transcript_errors', `expected transcript_errors, got ${verdict.verdict}: ${verdict.reason}`);
129
+ assert.equal(verdict.downgradeTo, 'needs_review');
130
+ assert.ok(verdict.reason.length > 0, 'reason should be non-empty');
131
+
132
+ // verdicts.json sidecar should have been written
133
+ const sidecar = JSON.parse(fs.readFileSync(path.join(tmp, `${slug}.verdicts.json`), 'utf8'));
134
+ assert.equal(sidecar.schemaVersion, 1);
135
+ assert.equal(sidecar.verdict, 'transcript_errors');
136
+ } finally {
137
+ rmdir(tmp);
138
+ }
139
+ });
140
+
141
+ // ─── fixture: Case 2 — HALT (M7 soak unmet) ──────────────────────────────────
142
+
143
+ test('Case 2: HALT in result event → halt/pending with floor date in reason', async () => {
144
+ const tmp = makeTmpDir();
145
+ try {
146
+ const slug = '44-signal-builder-m7-final-cutover';
147
+ const haltMsg = [
148
+ 'HALT: M7 prerequisite — M1 not yet soaked 30 days × 6 phases',
149
+ ' M1 commit: f91ce44 2026-05-23 (age: 0 days, need ≥ 180)',
150
+ ' M7 earliest eligible: ~2026-11-19',
151
+ ].join('\n');
152
+
153
+ const logEvents = [
154
+ {
155
+ type: 'assistant',
156
+ message: {
157
+ role: 'assistant',
158
+ content: [{
159
+ type: 'tool_use',
160
+ id: 'toolu_m7_001',
161
+ name: 'Bash',
162
+ input: { command: 'git log --oneline m1-tag', description: 'Check M1 soak period' },
163
+ }],
164
+ },
165
+ },
166
+ {
167
+ type: 'user',
168
+ message: {
169
+ role: 'user',
170
+ content: [{
171
+ type: 'tool_result',
172
+ tool_use_id: 'toolu_m7_001',
173
+ content: 'f91ce44 2026-05-23 feat(m1): initial trading signals',
174
+ is_error: false,
175
+ }],
176
+ },
177
+ },
178
+ {
179
+ type: 'result',
180
+ subtype: 'success',
181
+ result: haltMsg,
182
+ },
183
+ ];
184
+
185
+ writeLog(tmp, slug, logEvents);
186
+ const prdPath = writePrd(
187
+ tmp,
188
+ slug,
189
+ '# M7 Final Cutover\n\nOnly after M1–M6 have run 30+ days each.',
190
+ );
191
+
192
+ const verdict = await verifyRun({
193
+ runDir: tmp,
194
+ prdPath,
195
+ queueEntry: { slug, status: 'running' },
196
+ allJobs: [],
197
+ });
198
+
199
+ assert.equal(verdict.verdict, 'halt', `expected halt, got ${verdict.verdict}: ${verdict.reason}`);
200
+ assert.equal(verdict.downgradeTo, 'pending');
201
+ assert.ok(verdict.reason.includes('HALT'), 'reason should mention HALT');
202
+ // Floor date from the HALT message should appear in reason
203
+ assert.ok(
204
+ verdict.reason.includes('2026-11-19') || verdict.reason.length > 20,
205
+ `reason should embed soak floor date: ${verdict.reason}`,
206
+ );
207
+ } finally {
208
+ rmdir(tmp);
209
+ }
210
+ });
211
+
212
+ // ─── fixture: Case 3 — deps_unmet (dependsOn unsatisfied) ────────────────────
213
+
214
+ test('Case 3: dependsOn pointing at pending dep → deps_unmet/pending', async () => {
215
+ const tmp = makeTmpDir();
216
+ try {
217
+ const slug = '56-burrow-mcp-retire-tier3-frozen-endpoints';
218
+ const logEvents = [
219
+ {
220
+ type: 'result',
221
+ subtype: 'success',
222
+ result: 'Completed burrow MCP tier-3 retirement.',
223
+ },
224
+ ];
225
+
226
+ writeLog(tmp, slug, logEvents);
227
+ const prdPath = writePrd(
228
+ tmp,
229
+ slug,
230
+ '# Burrow MCP Retire Tier-3\n\nRetire frozen endpoints.',
231
+ );
232
+
233
+ // M7 is a predecessor that is still pending.
234
+ const allJobs = [
235
+ { slug: '44-signal-builder-m7-final-cutover', status: 'pending', finishedAt: null },
236
+ { slug: slug, status: 'running', finishedAt: null },
237
+ ];
238
+ const queueEntry = {
239
+ slug,
240
+ status: 'running',
241
+ dependsOn: ['44-signal-builder-m7-final-cutover'],
242
+ };
243
+
244
+ const verdict = await verifyRun({ runDir: tmp, prdPath, queueEntry, allJobs });
245
+
246
+ assert.equal(verdict.verdict, 'deps_unmet', `expected deps_unmet, got ${verdict.verdict}: ${verdict.reason}`);
247
+ assert.equal(verdict.downgradeTo, 'pending');
248
+ assert.ok(verdict.reason.includes('44-signal-builder-m7-final-cutover'), 'reason should name the unmet dep');
249
+ } finally {
250
+ rmdir(tmp);
251
+ }
252
+ });
253
+
254
+ // ─── fixture: truly clean run ─────────────────────────────────────────────────
255
+
256
+ test('Truly clean run (no error markers) → clean/null', async () => {
257
+ const tmp = makeTmpDir();
258
+ try {
259
+ const slug = '20-clean-feature';
260
+ const logEvents = [
261
+ {
262
+ type: 'assistant',
263
+ message: {
264
+ role: 'assistant',
265
+ content: [{
266
+ type: 'tool_use',
267
+ id: 'toolu_clean_001',
268
+ name: 'Bash',
269
+ input: { command: 'npm test', description: 'Run test suite' },
270
+ }],
271
+ },
272
+ },
273
+ {
274
+ type: 'user',
275
+ message: {
276
+ role: 'user',
277
+ content: [{
278
+ type: 'tool_result',
279
+ tool_use_id: 'toolu_clean_001',
280
+ content: '✓ 42 tests passed (1.3s)\nAll tests passed.',
281
+ is_error: false,
282
+ }],
283
+ },
284
+ },
285
+ {
286
+ type: 'result',
287
+ subtype: 'success',
288
+ result: 'Feature complete. All acceptance criteria satisfied.',
289
+ },
290
+ ];
291
+
292
+ writeLog(tmp, slug, logEvents);
293
+ const prdPath = writePrd(tmp, slug, '# Clean Feature\n\nImplement a clean feature.');
294
+
295
+ const verdict = await verifyRun({
296
+ runDir: tmp,
297
+ prdPath,
298
+ queueEntry: { slug, status: 'running' },
299
+ allJobs: [],
300
+ });
301
+
302
+ assert.equal(verdict.verdict, 'clean', `expected clean, got ${verdict.verdict}: ${verdict.reason}`);
303
+ assert.equal(verdict.downgradeTo, null);
304
+ } finally {
305
+ rmdir(tmp);
306
+ }
307
+ });
308
+
309
+ // ─── self-recovery: FAIL followed by successful retry ──────────────────────
310
+
311
+ test('FAIL recovered within 30 events → clean', async () => {
312
+ const tmp = makeTmpDir();
313
+ try {
314
+ const slug = '21-self-recovering';
315
+ const desc = 'Run pytest';
316
+ const logEvents = [
317
+ // First attempt: fails
318
+ {
319
+ type: 'assistant',
320
+ message: {
321
+ role: 'assistant',
322
+ content: [{
323
+ type: 'tool_use',
324
+ id: 'toolu_r1',
325
+ name: 'Bash',
326
+ input: { command: 'pytest', description: desc },
327
+ }],
328
+ },
329
+ },
330
+ {
331
+ type: 'user',
332
+ message: {
333
+ role: 'user',
334
+ content: [{
335
+ type: 'tool_result',
336
+ tool_use_id: 'toolu_r1',
337
+ content: 'FAILED tests/test_foo.py::test_bar - AssertionError',
338
+ is_error: false,
339
+ }],
340
+ },
341
+ },
342
+ // Agent fixes and retries with same description
343
+ {
344
+ type: 'assistant',
345
+ message: {
346
+ role: 'assistant',
347
+ content: [{
348
+ type: 'tool_use',
349
+ id: 'toolu_r2',
350
+ name: 'Bash',
351
+ input: { command: 'pytest', description: desc },
352
+ }],
353
+ },
354
+ },
355
+ {
356
+ type: 'user',
357
+ message: {
358
+ role: 'user',
359
+ content: [{
360
+ type: 'tool_result',
361
+ tool_use_id: 'toolu_r2',
362
+ content: '5 passed in 0.42s',
363
+ is_error: false,
364
+ }],
365
+ },
366
+ },
367
+ {
368
+ type: 'result',
369
+ subtype: 'success',
370
+ result: 'Fixed the failing test and verified all pass.',
371
+ },
372
+ ];
373
+
374
+ writeLog(tmp, slug, logEvents);
375
+ const prdPath = writePrd(tmp, slug, '# Self-recovering test');
376
+
377
+ const verdict = await verifyRun({
378
+ runDir: tmp,
379
+ prdPath,
380
+ queueEntry: { slug, status: 'running' },
381
+ allJobs: [],
382
+ });
383
+
384
+ assert.equal(verdict.verdict, 'clean', `self-recovery should yield clean, got ${verdict.verdict}: ${verdict.reason}`);
385
+ } finally {
386
+ rmdir(tmp);
387
+ }
388
+ });
@@ -22,6 +22,7 @@ const os = require('node:os');
22
22
  const chokidar = require('chokidar');
23
23
  const logs = require('./logs.cjs');
24
24
  const { sendIfAlive } = require('./lib/sendToRenderer.cjs');
25
+ const { expandHome } = require('./lib/expandHome.cjs');
25
26
 
26
27
  /** Map<absPath, {watcher, refCount}> — one chokidar watcher per path. */
27
28
  const watchers = new Map();
@@ -31,13 +32,6 @@ function attachWindow(w) {
31
32
  window = w;
32
33
  }
33
34
 
34
- function expandHome(p) {
35
- if (!p) return p;
36
- if (p === '~') return os.homedir();
37
- if (p.startsWith('~/')) return path.join(os.homedir(), p.slice(2));
38
- return p;
39
- }
40
-
41
35
  /**
42
36
  * Resolve a path to its realpath, handling non-existent files by resolving
43
37
  * the parent directory instead. Prevents symlink traversal attacks.
@@ -14,42 +14,13 @@
14
14
  */
15
15
 
16
16
  const { ipcMain, shell } = require('electron');
17
- const fs = require('node:fs');
18
17
  const fsp = require('node:fs/promises');
19
18
  const path = require('node:path');
20
19
  const os = require('node:os');
21
20
 
22
21
  const { z } = require('zod');
23
-
24
- function expandHome(p) {
25
- if (!p) return p;
26
- if (p === '~') return os.homedir();
27
- if (p.startsWith('~/')) return path.join(os.homedir(), p.slice(2));
28
- return p;
29
- }
30
-
31
- /**
32
- * Resolve to realpath; on ENOENT resolve the parent then re-append basename so
33
- * we can still validate create destinations. Mirrors config.cjs.
34
- */
35
- function realResolve(abs) {
36
- const lex = path.resolve(expandHome(abs));
37
- try {
38
- return fs.realpathSync(lex);
39
- } catch (e) {
40
- if (e.code === 'ENOENT') {
41
- const parent = path.dirname(lex);
42
- try {
43
- return path.join(fs.realpathSync(parent), path.basename(lex));
44
- } catch {
45
- return lex;
46
- }
47
- }
48
- throw e;
49
- }
50
- }
51
-
52
- const HOME = os.homedir();
22
+ const { assertInsideHome } = require('./lib/insideHome.cjs');
23
+ const { expandHome } = require('./lib/expandHome.cjs');
53
24
 
54
25
  /**
55
26
  * Validates that the path is under the home directory. Returns the realpath
@@ -58,11 +29,7 @@ const HOME = os.homedir();
58
29
  * ~ — but never escapes the home tree.
59
30
  */
60
31
  function validateHomePath(abs) {
61
- const real = realResolve(abs);
62
- let realHome;
63
- try { realHome = fs.realpathSync(HOME); } catch { realHome = HOME; }
64
- if (real === realHome || real.startsWith(realHome + path.sep)) return real;
65
- throw new Error(`Path outside home directory: ${real}`);
32
+ return assertInsideHome(expandHome(abs)).realPath;
66
33
  }
67
34
 
68
35
  /** Reject .credentials.json writes regardless of where they sit. */
@@ -228,7 +195,7 @@ async function renameEntry(oldPath, newName) {
228
195
  }
229
196
  }
230
197
 
231
- const CRITICAL_PATHS = new Set([HOME, '/', '/usr', '/bin', '/etc', '/var', '/System', '/Applications']);
198
+ const CRITICAL_PATHS = new Set([os.homedir(), '/', '/usr', '/bin', '/etc', '/var', '/System', '/Applications']);
232
199
 
233
200
  async function deleteEntry(filePath) {
234
201
  let resolved;