@loicngr/kobo 1.7.4 → 1.7.6
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/README.md +26 -9
- package/dist/mcp-server/kobo-tasks-handlers.js +41 -1
- package/dist/mcp-server/kobo-tasks-server.js +157 -8
- package/dist/server/db/migrations.js +87 -0
- package/dist/server/db/schema.js +27 -0
- package/dist/server/index.js +9 -1
- package/dist/server/routes/health.js +68 -3
- package/dist/server/routes/workspaces.js +183 -4
- package/dist/server/services/agent/engines/claude-code/engine.js +13 -6
- package/dist/server/services/agent/engines/claude-code/event-mapper.js +96 -7
- package/dist/server/services/agent/orchestrator.js +113 -71
- package/dist/server/services/auto-loop-service.js +16 -3
- package/dist/server/services/cron-service.js +279 -0
- package/dist/server/services/quota-backoff-service.js +127 -0
- package/dist/server/services/wakeup-service.js +1 -1
- package/dist/server/services/workspace-service.js +98 -0
- package/dist/server/utils/git-ops.js +8 -1
- package/package.json +2 -1
- package/src/client/dist/spa/assets/{ActivityFeed-ClJLeAXJ.js → ActivityFeed-BboSPm4b.js} +2 -2
- package/src/client/dist/spa/assets/{ActivityFeed-DVBfmJWJ.css → ActivityFeed-tE4LVYck.css} +1 -1
- package/src/client/dist/spa/assets/AutoLoopChip-w8D77bI5.js +1 -0
- package/src/client/dist/spa/assets/{CreatePage-BOkt0Psl.js → CreatePage-BDObLDJc.js} +1 -1
- package/src/client/dist/spa/assets/{DiffViewer-Dls1jFCN.js → DiffViewer-CblFgn8w.js} +3 -3
- package/src/client/dist/spa/assets/{DiffViewer-wFfQ9tcY.css → DiffViewer-DTdDcKZC.css} +1 -1
- package/src/client/dist/spa/assets/HealthPage-CBSw7e5q.js +1 -0
- package/src/client/dist/spa/assets/{MainLayout-DHNIerYJ.js → MainLayout-DhaYycak.js} +17 -17
- package/src/client/dist/spa/assets/MainLayout-drolsINz.css +1 -0
- package/src/client/dist/spa/assets/{SearchPage-BEnZ-CLq.js → SearchPage-cZTwP4Lf.js} +1 -1
- package/src/client/dist/spa/assets/{SettingsPage-DeCbWvPb.js → SettingsPage-C1efO0VM.js} +1 -1
- package/src/client/dist/spa/assets/WorkspacePage-3jcof896.js +4 -0
- package/src/client/dist/spa/assets/{WorkspacePage-eymEd4kx.css → WorkspacePage-CCtIrBiR.css} +1 -1
- package/src/client/dist/spa/assets/{cssMode-AlflsawW.js → cssMode-BFLYiiEw.js} +1 -1
- package/src/client/dist/spa/assets/{editor.api-DtvjQlUm.js → editor.api-2asmmhth.js} +1 -1
- package/src/client/dist/spa/assets/{editor.main-Ccy_gjVD.js → editor.main-ChCYZyez.js} +3 -3
- package/src/client/dist/spa/assets/{expand-template-AQsvbQ8_.js → expand-template-CXQFkQOJ.js} +1 -1
- package/src/client/dist/spa/assets/{freemarker2-DdQktlXK.js → freemarker2-BaBL9E9G.js} +1 -1
- package/src/client/dist/spa/assets/{handlebars-CE3ee2NH.js → handlebars-BxDour4L.js} +1 -1
- package/src/client/dist/spa/assets/{html-CCKX8Xv9.js → html-C6hnkfIL.js} +1 -1
- package/src/client/dist/spa/assets/{htmlMode-Dh8jDJum.js → htmlMode-9zT3-dmz.js} +1 -1
- package/src/client/dist/spa/assets/i18n-CLY0XI9-.js +1 -0
- package/src/client/dist/spa/assets/index-D6wj_wQ9.js +2 -0
- package/src/client/dist/spa/assets/{javascript-DhmZNdUp.js → javascript-C3YjvKbE.js} +1 -1
- package/src/client/dist/spa/assets/{jsonMode-B0xAtnNK.js → jsonMode-DcJDgMzf.js} +1 -1
- package/src/client/dist/spa/assets/{liquid-ByL0HpZ0.js → liquid-CsT8SjJM.js} +1 -1
- package/src/client/dist/spa/assets/{mdx-DX4pehAZ.js → mdx-CT3yVSyc.js} +1 -1
- package/src/client/dist/spa/assets/{models-ClWoqWeC.js → models-BsjWUKqM.js} +1 -1
- package/src/client/dist/spa/assets/{monaco.contribution-Fegh8Y1Y.js → monaco.contribution-DKGNz1oQ.js} +2 -2
- package/src/client/dist/spa/assets/{purify.es-BWZjBa9F.js → purify.es-CPieV82n.js} +1 -1
- package/src/client/dist/spa/assets/{python-COS2MM8n.js → python-Ca5miKgj.js} +1 -1
- package/src/client/dist/spa/assets/{razor-Cc3xCJU7.js → razor-7qzusGRc.js} +1 -1
- package/src/client/dist/spa/assets/{render-chat-markdown-DcGIpMoe.js → render-chat-markdown-Bqq2G-yI.js} +1 -1
- package/src/client/dist/spa/assets/{tsMode-eQIJjERk.js → tsMode-BdvO8jZ2.js} +1 -1
- package/src/client/dist/spa/assets/{typescript-DwIlacVU.js → typescript-BfVNzhgs.js} +1 -1
- package/src/client/dist/spa/assets/{xml-DP-09Aih.js → xml-DGNXGqXL.js} +1 -1
- package/src/client/dist/spa/assets/{yaml-BhrtimeA.js → yaml-CtAtOyt5.js} +1 -1
- package/src/client/dist/spa/index.html +1 -1
- package/src/mcp-server/kobo-tasks-handlers.ts +55 -1
- package/src/mcp-server/kobo-tasks-server.ts +165 -7
- package/src/client/dist/spa/assets/HealthPage-CMxH3SBS.js +0 -1
- package/src/client/dist/spa/assets/MainLayout-DKurmqtk.css +0 -1
- package/src/client/dist/spa/assets/WorkspacePage-DFAFT5OW.js +0 -4
- package/src/client/dist/spa/assets/i18n-BOsrrRj4.js +0 -1
- package/src/client/dist/spa/assets/index-_ZaIBxd6.js +0 -2
- /package/src/client/dist/spa/assets/{QPage-ChUKoaKe.js → QPage-DFi3K093.js} +0 -0
- /package/src/client/dist/spa/assets/{formatters-BD0_hovB.js → formatters-DCAQ6ANJ.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as e}from"./editor.api-
|
|
1
|
+
import{m as e}from"./editor.api-2asmmhth.js";import{t}from"./monaco.contribution-DKGNz1oQ.js";var n,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,s=(e,t,n,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of a(t))!o.call(e,c)&&c!==n&&r(e,c,{get:()=>t[c],enumerable:!(s=i(t,c))||s.enumerable});return e},c=(e,t,n)=>(s(e,t,`default`),n&&s(n,t,`default`)),l={};c(l,e);function u(e,t){let n=globalThis.MonacoEnvironment;if(n?.createTrustedTypesPolicy)try{return n.createTrustedTypesPolicy(e,t)}catch(e){console.error(e);return}try{return globalThis.trustedTypes?.createPolicy(e,t)}catch(e){console.error(e);return}}var d=typeof self==`object`&&self.constructor&&self.constructor.name===`DedicatedWorkerGlobalScope`&&globalThis.workerttPolicy!==void 0?globalThis.workerttPolicy:u(`defaultWorkerFactory`,{createScriptURL:e=>e});function f(e){let t=e.label,n=globalThis.MonacoEnvironment;if(n){if(typeof n.getWorker==`function`)return n.getWorker(`workerMain.js`,t);if(typeof n.getWorkerUrl==`function`){let e=n.getWorkerUrl(`workerMain.js`,t);return new Worker(d?d.createScriptURL(e):e,{name:t,type:`module`})}}throw Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`)}function p(e){let t=Promise.resolve(f({label:e.label??`monaco-editor-worker`,moduleId:e.moduleId})).then(t=>(t.postMessage(`ignore`),t.postMessage(e.createData),t));return l.editor.createWebWorker({worker:t,host:e.host,keepIdleModels:e.keepIdleModels})}var m=class{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(),null),this._client=null}async _updateExtraLibs(){if(!this._worker)return;let e=++this._updateExtraLibsToken,t=await this._worker.getProxy();this._updateExtraLibsToken===e&&t.updateExtraLibs(this._defaults.getExtraLibs())}_getClient(){return this._client||=(async()=>(this._worker=p({moduleId:`vs/language/typescript/tsWorker`,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(l.editor.getModels().filter(e=>e.getLanguageId()===this._modeId).map(e=>e.uri)):await this._worker.getProxy()))(),this._client}async getLanguageServiceWorker(...e){let t=await this._getClient();return this._worker&&await this._worker.withSyncedResources(e),t}},h={};h[`lib.d.ts`]=!0,h[`lib.decorators.d.ts`]=!0,h[`lib.decorators.legacy.d.ts`]=!0,h[`lib.dom.asynciterable.d.ts`]=!0,h[`lib.dom.d.ts`]=!0,h[`lib.dom.iterable.d.ts`]=!0,h[`lib.es2015.collection.d.ts`]=!0,h[`lib.es2015.core.d.ts`]=!0,h[`lib.es2015.d.ts`]=!0,h[`lib.es2015.generator.d.ts`]=!0,h[`lib.es2015.iterable.d.ts`]=!0,h[`lib.es2015.promise.d.ts`]=!0,h[`lib.es2015.proxy.d.ts`]=!0,h[`lib.es2015.reflect.d.ts`]=!0,h[`lib.es2015.symbol.d.ts`]=!0,h[`lib.es2015.symbol.wellknown.d.ts`]=!0,h[`lib.es2016.array.include.d.ts`]=!0,h[`lib.es2016.d.ts`]=!0,h[`lib.es2016.full.d.ts`]=!0,h[`lib.es2016.intl.d.ts`]=!0,h[`lib.es2017.d.ts`]=!0,h[`lib.es2017.date.d.ts`]=!0,h[`lib.es2017.full.d.ts`]=!0,h[`lib.es2017.intl.d.ts`]=!0,h[`lib.es2017.object.d.ts`]=!0,h[`lib.es2017.sharedmemory.d.ts`]=!0,h[`lib.es2017.string.d.ts`]=!0,h[`lib.es2017.typedarrays.d.ts`]=!0,h[`lib.es2018.asyncgenerator.d.ts`]=!0,h[`lib.es2018.asynciterable.d.ts`]=!0,h[`lib.es2018.d.ts`]=!0,h[`lib.es2018.full.d.ts`]=!0,h[`lib.es2018.intl.d.ts`]=!0,h[`lib.es2018.promise.d.ts`]=!0,h[`lib.es2018.regexp.d.ts`]=!0,h[`lib.es2019.array.d.ts`]=!0,h[`lib.es2019.d.ts`]=!0,h[`lib.es2019.full.d.ts`]=!0,h[`lib.es2019.intl.d.ts`]=!0,h[`lib.es2019.object.d.ts`]=!0,h[`lib.es2019.string.d.ts`]=!0,h[`lib.es2019.symbol.d.ts`]=!0,h[`lib.es2020.bigint.d.ts`]=!0,h[`lib.es2020.d.ts`]=!0,h[`lib.es2020.date.d.ts`]=!0,h[`lib.es2020.full.d.ts`]=!0,h[`lib.es2020.intl.d.ts`]=!0,h[`lib.es2020.number.d.ts`]=!0,h[`lib.es2020.promise.d.ts`]=!0,h[`lib.es2020.sharedmemory.d.ts`]=!0,h[`lib.es2020.string.d.ts`]=!0,h[`lib.es2020.symbol.wellknown.d.ts`]=!0,h[`lib.es2021.d.ts`]=!0,h[`lib.es2021.full.d.ts`]=!0,h[`lib.es2021.intl.d.ts`]=!0,h[`lib.es2021.promise.d.ts`]=!0,h[`lib.es2021.string.d.ts`]=!0,h[`lib.es2021.weakref.d.ts`]=!0,h[`lib.es2022.array.d.ts`]=!0,h[`lib.es2022.d.ts`]=!0,h[`lib.es2022.error.d.ts`]=!0,h[`lib.es2022.full.d.ts`]=!0,h[`lib.es2022.intl.d.ts`]=!0,h[`lib.es2022.object.d.ts`]=!0,h[`lib.es2022.regexp.d.ts`]=!0,h[`lib.es2022.sharedmemory.d.ts`]=!0,h[`lib.es2022.string.d.ts`]=!0,h[`lib.es2023.array.d.ts`]=!0,h[`lib.es2023.collection.d.ts`]=!0,h[`lib.es2023.d.ts`]=!0,h[`lib.es2023.full.d.ts`]=!0,h[`lib.es5.d.ts`]=!0,h[`lib.es6.d.ts`]=!0,h[`lib.esnext.collection.d.ts`]=!0,h[`lib.esnext.d.ts`]=!0,h[`lib.esnext.decorators.d.ts`]=!0,h[`lib.esnext.disposable.d.ts`]=!0,h[`lib.esnext.full.d.ts`]=!0,h[`lib.esnext.intl.d.ts`]=!0,h[`lib.esnext.object.d.ts`]=!0,h[`lib.esnext.promise.d.ts`]=!0,h[`lib.scripthost.d.ts`]=!0,h[`lib.webworker.asynciterable.d.ts`]=!0,h[`lib.webworker.d.ts`]=!0,h[`lib.webworker.importscripts.d.ts`]=!0,h[`lib.webworker.iterable.d.ts`]=!0;function g(e,t,n=0){if(typeof e==`string`)return e;if(e===void 0)return``;let r=``;if(n){r+=t;for(let e=0;e<n;e++)r+=` `}if(r+=e.messageText,n++,e.next)for(let i of e.next)r+=g(i,t,n);return r}function _(e){return e?e.map(e=>e.text).join(``):``}var v=class{constructor(e){this._worker=e}_textSpanToRange(e,t){let n=e.getPositionAt(t.start),r=e.getPositionAt(t.start+t.length),{lineNumber:i,column:a}=n,{lineNumber:o,column:s}=r;return{startLineNumber:i,startColumn:a,endLineNumber:o,endColumn:s}}},y=class{constructor(e){this._worker=e,this._libFiles={},this._hasFetchedLibFiles=!1,this._fetchLibFilesPromise=null}isLibFile(e){return e&&e.path.indexOf(`/lib.`)===0?!!h[e.path.slice(1)]:!1}getOrCreateModel(e){let n=l.Uri.parse(e),r=l.editor.getModel(n);if(r)return r;if(this.isLibFile(n)&&this._hasFetchedLibFiles)return l.editor.createModel(this._libFiles[n.path.slice(1)],`typescript`,n);let i=t.getExtraLibs()[e];return i?l.editor.createModel(i.content,`typescript`,n):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._worker().then(e=>e.getLibFiles()).then(e=>{this._hasFetchedLibFiles=!0,this._libFiles=e}),this._fetchLibFilesPromise}},b=class extends v{constructor(e,t,n,r){super(r),this._libFiles=e,this._defaults=t,this._selector=n,this._disposables=[],this._listener=Object.create(null);let i=e=>{if(e.getLanguageId()!==n)return;let t=()=>{let{onlyVisible:t}=this._defaults.getDiagnosticsOptions();t?e.isAttachedToEditor()&&this._doValidate(e):this._doValidate(e)},r,i=e.onDidChangeContent(()=>{clearTimeout(r),r=window.setTimeout(t,500)}),a=e.onDidChangeAttached(()=>{let{onlyVisible:n}=this._defaults.getDiagnosticsOptions();n&&(e.isAttachedToEditor()?t():l.editor.setModelMarkers(e,this._selector,[]))});this._listener[e.uri.toString()]={dispose(){i.dispose(),a.dispose(),clearTimeout(r)}},t()},a=e=>{l.editor.setModelMarkers(e,this._selector,[]);let t=e.uri.toString();this._listener[t]&&(this._listener[t].dispose(),delete this._listener[t])};this._disposables.push(l.editor.onDidCreateModel(e=>i(e))),this._disposables.push(l.editor.onWillDisposeModel(a)),this._disposables.push(l.editor.onDidChangeModelLanguage(e=>{a(e.model),i(e.model)})),this._disposables.push({dispose(){for(let e of l.editor.getModels())a(e)}});let o=()=>{for(let e of l.editor.getModels())a(e),i(e)};this._disposables.push(this._defaults.onDidChange(o)),this._disposables.push(this._defaults.onDidExtraLibsChange(o)),l.editor.getModels().forEach(e=>i(e))}dispose(){this._disposables.forEach(e=>e&&e.dispose()),this._disposables=[]}async _doValidate(e){let t=await this._worker(e.uri);if(e.isDisposed())return;let n=[],{noSyntaxValidation:r,noSemanticValidation:i,noSuggestionDiagnostics:a}=this._defaults.getDiagnosticsOptions();r||n.push(t.getSyntacticDiagnostics(e.uri.toString())),i||n.push(t.getSemanticDiagnostics(e.uri.toString())),a||n.push(t.getSuggestionDiagnostics(e.uri.toString()));let o=await Promise.all(n);if(!o||e.isDisposed())return;let s=o.reduce((e,t)=>t.concat(e),[]).filter(e=>(this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore||[]).indexOf(e.code)===-1),c=s.map(e=>e.relatedInformation||[]).reduce((e,t)=>t.concat(e),[]).map(e=>e.file?l.Uri.parse(e.file.fileName):null);await this._libFiles.fetchLibFilesIfNecessary(c),!e.isDisposed()&&l.editor.setModelMarkers(e,this._selector,s.map(t=>this._convertDiagnostics(e,t)))}_convertDiagnostics(e,t){let n=t.start||0,r=t.length||1,{lineNumber:i,column:a}=e.getPositionAt(n),{lineNumber:o,column:s}=e.getPositionAt(n+r),c=[];return t.reportsUnnecessary&&c.push(l.MarkerTag.Unnecessary),t.reportsDeprecated&&c.push(l.MarkerTag.Deprecated),{severity:this._tsDiagnosticCategoryToMarkerSeverity(t.category),startLineNumber:i,startColumn:a,endLineNumber:o,endColumn:s,message:g(t.messageText,`
|
|
2
2
|
`),code:t.code.toString(),tags:c,relatedInformation:this._convertRelatedInformation(e,t.relatedInformation)}}_convertRelatedInformation(e,t){if(!t)return[];let n=[];return t.forEach(t=>{let r=e;if(t.file&&(r=this._libFiles.getOrCreateModel(t.file.fileName)),!r)return;let i=t.start||0,a=t.length||1,{lineNumber:o,column:s}=r.getPositionAt(i),{lineNumber:c,column:l}=r.getPositionAt(i+a);n.push({resource:r.uri,startLineNumber:o,startColumn:s,endLineNumber:c,endColumn:l,message:g(t.messageText,`
|
|
3
3
|
`)})}),n}_tsDiagnosticCategoryToMarkerSeverity(e){switch(e){case 1:return l.MarkerSeverity.Error;case 3:return l.MarkerSeverity.Info;case 0:return l.MarkerSeverity.Warning;case 2:return l.MarkerSeverity.Hint}return l.MarkerSeverity.Info}},x=class e extends v{get triggerCharacters(){return[`.`]}async provideCompletionItems(t,n,r,i){let a=t.getWordUntilPosition(n),o=new l.Range(n.lineNumber,a.startColumn,n.lineNumber,a.endColumn),s=t.uri,c=t.getOffsetAt(n),u=await this._worker(s);if(t.isDisposed())return;let d=await u.getCompletionsAtPosition(s.toString(),c);if(!(!d||t.isDisposed()))return{suggestions:d.entries.map(r=>{let i=o;if(r.replacementSpan){let e=t.getPositionAt(r.replacementSpan.start),n=t.getPositionAt(r.replacementSpan.start+r.replacementSpan.length);i=new l.Range(e.lineNumber,e.column,n.lineNumber,n.column)}let a=[];return r.kindModifiers!==void 0&&r.kindModifiers.indexOf(`deprecated`)!==-1&&a.push(l.languages.CompletionItemTag.Deprecated),{uri:s,position:n,offset:c,range:i,label:r.name,insertText:r.name,sortText:r.sortText,kind:e.convertKind(r.kind),tags:a}})}}async resolveCompletionItem(t,n){let r=t,i=r.uri,a=r.position,o=r.offset,s=await(await this._worker(i)).getCompletionEntryDetails(i.toString(),o,r.label);return s?{uri:i,position:a,label:s.name,kind:e.convertKind(s.kind),detail:_(s.displayParts),documentation:{value:e.createDocumentationString(s)}}:r}static convertKind(e){switch(e){case k.primitiveType:case k.keyword:return l.languages.CompletionItemKind.Keyword;case k.variable:case k.localVariable:return l.languages.CompletionItemKind.Variable;case k.memberVariable:case k.memberGetAccessor:case k.memberSetAccessor:return l.languages.CompletionItemKind.Field;case k.function:case k.memberFunction:case k.constructSignature:case k.callSignature:case k.indexSignature:return l.languages.CompletionItemKind.Function;case k.enum:return l.languages.CompletionItemKind.Enum;case k.module:return l.languages.CompletionItemKind.Module;case k.class:return l.languages.CompletionItemKind.Class;case k.interface:return l.languages.CompletionItemKind.Interface;case k.warning:return l.languages.CompletionItemKind.File}return l.languages.CompletionItemKind.Property}static createDocumentationString(e){let t=_(e.documentation);if(e.tags)for(let n of e.tags)t+=`
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e}from"./editor.api-
|
|
1
|
+
import{m as e}from"./editor.api-2asmmhth.js";var t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a=(e,a,o,s)=>{if(a&&typeof a==`object`||typeof a==`function`)for(let c of r(a))!i.call(e,c)&&c!==o&&t(e,c,{get:()=>a[c],enumerable:!(s=n(a,c))||s.enumerable});return e},o=(e,t,n)=>(a(e,t,`default`),n&&a(n,t,`default`)),s={};o(s,e);var c={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:`//`,blockComment:[`/*`,`*/`]},brackets:[[`{`,`}`],[`[`,`]`],[`(`,`)`]],onEnterRules:[{beforeText:/^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,afterText:/^\s*\*\/$/,action:{indentAction:s.languages.IndentAction.IndentOutdent,appendText:` * `}},{beforeText:/^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,action:{indentAction:s.languages.IndentAction.None,appendText:` * `}},{beforeText:/^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,action:{indentAction:s.languages.IndentAction.None,appendText:`* `}},{beforeText:/^(\t|(\ \ ))*\ \*\/\s*$/,action:{indentAction:s.languages.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:RegExp(`^\\s*//\\s*#?region\\b`),end:RegExp(`^\\s*//\\s*#?endregion\\b`)}}},l={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`.split(`.`),operators:`<=.>=.==.!=.===.!==.=>.+.-.**.*./.%.++.--.<<.</.>>.>>>.&.|.^.!.~.&&.||.??.?.:.=.+=.-=.*=.**=./=.%=.<<=.>>=.>>>=.&=.|=.^=.@`.split(`.`),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{c as conf,l as language};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e}from"./editor.api-
|
|
1
|
+
import{m as e}from"./editor.api-2asmmhth.js";var t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a=(e,a,o,s)=>{if(a&&typeof a==`object`||typeof a==`function`)for(let c of r(a))!i.call(e,c)&&c!==o&&t(e,c,{get:()=>a[c],enumerable:!(s=n(a,c))||s.enumerable});return e},o=(e,t,n)=>(a(e,t,`default`),n&&a(n,t,`default`)),s={};o(s,e);var c={comments:{blockComment:[`<!--`,`-->`]},brackets:[[`<`,`>`]],autoClosingPairs:[{open:`<`,close:`>`},{open:`'`,close:`'`},{open:`"`,close:`"`}],surroundingPairs:[{open:`<`,close:`>`},{open:`'`,close:`'`},{open:`"`,close:`"`}],onEnterRules:[{beforeText:RegExp(`<([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`,`i`),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:s.languages.IndentAction.IndentOutdent}},{beforeText:RegExp(`<(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,`i`),action:{indentAction:s.languages.IndentAction.Indent}}]},l={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{c as conf,l as language};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e}from"./editor.api-
|
|
1
|
+
import{m as e}from"./editor.api-2asmmhth.js";var t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a=(e,a,o,s)=>{if(a&&typeof a==`object`||typeof a==`function`)for(let c of r(a))!i.call(e,c)&&c!==o&&t(e,c,{get:()=>a[c],enumerable:!(s=n(a,c))||s.enumerable});return e},o=(e,t,n)=>(a(e,t,`default`),n&&a(n,t,`default`)),s={};o(s,e);var c={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:s.languages.IndentAction.Indent}}]},l={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{c as conf,l as language};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!DOCTYPE html><html><head><title>Kōbō</title><link rel=icon type=image/svg+xml href=/favicon.svg><meta charset=utf-8><meta name=description content="Kōbō — multi-workspace agent manager for Claude Code"><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width,height=device-height"> <script type="module" crossorigin src="/assets/index-
|
|
1
|
+
<!DOCTYPE html><html><head><title>Kōbō</title><link rel=icon type=image/svg+xml href=/favicon.svg><meta charset=utf-8><meta name=description content="Kōbō — multi-workspace agent manager for Claude Code"><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width,height=device-height"> <script type="module" crossorigin src="/assets/index-D6wj_wQ9.js"></script>
|
|
2
2
|
<link rel="modulepreload" crossorigin href="/assets/runtime-core.esm-bundler-C3IgBgY5.js">
|
|
3
3
|
<link rel="modulepreload" crossorigin href="/assets/vue-i18n-BcfTCFFS.js">
|
|
4
4
|
<link rel="modulepreload" crossorigin href="/assets/QIcon-BJuyqdsT.js">
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import type Database from 'better-sqlite3'
|
|
4
4
|
import { nanoid } from 'nanoid'
|
|
5
|
+
import * as cronService from '../server/services/cron-service.js'
|
|
5
6
|
import * as settingsService from '../server/services/settings-service.js'
|
|
6
7
|
import { slugifyProjectName } from '../server/utils/project-slug.js'
|
|
7
8
|
import { resolveWorkspaceWorktreePath } from '../server/utils/worktree-paths.js'
|
|
@@ -75,6 +76,34 @@ export function markAutoLoopReadyHandler(db: Database.Database, workspaceId: str
|
|
|
75
76
|
return { ok: true }
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Update the workspace's agent-side short description (≤ 200 chars). Empty /
|
|
81
|
+
* whitespace-only input clears the field (stored as NULL). This writes the
|
|
82
|
+
* `agent_description` column — a live status line owned by the agent — and
|
|
83
|
+
* leaves the user-side `description` column untouched. The handler does NOT
|
|
84
|
+
* emit a WS event; that's the route/service layer's responsibility. Live UI
|
|
85
|
+
* refresh on agent-driven updates is therefore deferred until next read.
|
|
86
|
+
*/
|
|
87
|
+
export function setWorkspaceAgentDescriptionHandler(
|
|
88
|
+
db: Database.Database,
|
|
89
|
+
workspaceId: string,
|
|
90
|
+
args: { description: string },
|
|
91
|
+
): { ok: true; description: string | null } | { ok: false; error: string } {
|
|
92
|
+
const raw = typeof args?.description === 'string' ? args.description : ''
|
|
93
|
+
const trimmed = raw.trim()
|
|
94
|
+
if (trimmed.length > 200) {
|
|
95
|
+
return { ok: false, error: `Description must be 200 characters or fewer (got ${trimmed.length})` }
|
|
96
|
+
}
|
|
97
|
+
const stored = trimmed.length > 0 ? trimmed : null
|
|
98
|
+
const result = db
|
|
99
|
+
.prepare('UPDATE workspaces SET agent_description = ?, updated_at = ? WHERE id = ?')
|
|
100
|
+
.run(stored, new Date().toISOString(), workspaceId)
|
|
101
|
+
if (result.changes === 0) {
|
|
102
|
+
return { ok: false, error: `Workspace '${workspaceId}' not found` }
|
|
103
|
+
}
|
|
104
|
+
return { ok: true, description: stored }
|
|
105
|
+
}
|
|
106
|
+
|
|
78
107
|
/** Set a task's status to "done" and return the updated task. */
|
|
79
108
|
export function markTaskDoneHandler(db: Database.Database, workspaceId: string, taskId: string): MarkDoneResult {
|
|
80
109
|
const now = new Date().toISOString()
|
|
@@ -240,6 +269,8 @@ export interface WorkspaceInfoDto {
|
|
|
240
269
|
model: string
|
|
241
270
|
notionUrl: string | null
|
|
242
271
|
notionPageId: string | null
|
|
272
|
+
description: string | null
|
|
273
|
+
agentDescription: string | null
|
|
243
274
|
devServerStatus: string
|
|
244
275
|
hasUnread: boolean
|
|
245
276
|
autoLoop: boolean
|
|
@@ -258,6 +289,8 @@ interface WorkspaceRow {
|
|
|
258
289
|
status: string
|
|
259
290
|
notion_url: string | null
|
|
260
291
|
notion_page_id: string | null
|
|
292
|
+
description: string | null
|
|
293
|
+
agent_description: string | null
|
|
261
294
|
model: string
|
|
262
295
|
dev_server_status: string
|
|
263
296
|
has_unread: number
|
|
@@ -271,7 +304,7 @@ interface WorkspaceRow {
|
|
|
271
304
|
export function getWorkspaceInfoHandler(db: Database.Database, workspaceId: string): WorkspaceInfoDto {
|
|
272
305
|
const row = db
|
|
273
306
|
.prepare(
|
|
274
|
-
'SELECT id, name, project_path, source_branch, working_branch, worktree_path, status, notion_url, notion_page_id, model, dev_server_status, has_unread, auto_loop, auto_loop_ready, created_at, updated_at FROM workspaces WHERE id = ?',
|
|
307
|
+
'SELECT id, name, project_path, source_branch, working_branch, worktree_path, status, notion_url, notion_page_id, description, agent_description, model, dev_server_status, has_unread, auto_loop, auto_loop_ready, created_at, updated_at FROM workspaces WHERE id = ?',
|
|
275
308
|
)
|
|
276
309
|
.get(workspaceId) as WorkspaceRow | undefined
|
|
277
310
|
|
|
@@ -297,6 +330,8 @@ export function getWorkspaceInfoHandler(db: Database.Database, workspaceId: stri
|
|
|
297
330
|
model: row.model,
|
|
298
331
|
notionUrl: row.notion_url,
|
|
299
332
|
notionPageId: row.notion_page_id,
|
|
333
|
+
description: row.description ?? null,
|
|
334
|
+
agentDescription: row.agent_description ?? null,
|
|
300
335
|
devServerStatus: row.dev_server_status,
|
|
301
336
|
hasUnread: row.has_unread === 1,
|
|
302
337
|
autoLoop: row.auto_loop === 1,
|
|
@@ -531,3 +566,22 @@ export function getSessionUsageHandler(db: Database.Database, workspaceId: strin
|
|
|
531
566
|
currentSession: { sessionId: currentSessionId, ...current },
|
|
532
567
|
}
|
|
533
568
|
}
|
|
569
|
+
|
|
570
|
+
// ── Crons ────────────────────────────────────────────────────────────────────
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* List every cron currently armed for a workspace.
|
|
574
|
+
*
|
|
575
|
+
* Note: cron_create and cron_delete are NOT exposed as handlers — they route
|
|
576
|
+
* through the backend HTTP API (`POST/DELETE /api/workspaces/:id/crons`)
|
|
577
|
+
* because their `setTimeout` must live in the backend process (which owns
|
|
578
|
+
* the orchestrator). Handlers here would arm timers in the MCP sub-process,
|
|
579
|
+
* which dies with the agent session, and fires would never reach a real
|
|
580
|
+
* session resume. The list handler is a pure read so it's safe to keep local.
|
|
581
|
+
*/
|
|
582
|
+
export function cronListHandler(
|
|
583
|
+
_db: Database.Database,
|
|
584
|
+
workspaceId: string,
|
|
585
|
+
): { ok: true; crons: cronService.PendingCron[] } {
|
|
586
|
+
return { ok: true, crons: cronService.listForWorkspace(workspaceId) }
|
|
587
|
+
}
|
|
@@ -4,9 +4,12 @@ import path from 'node:path'
|
|
|
4
4
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
5
5
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
6
6
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
|
|
7
|
-
import Database from 'better-sqlite3'
|
|
7
|
+
import type Database from 'better-sqlite3'
|
|
8
|
+
import { getDb } from '../server/db/index.js'
|
|
9
|
+
import { runMigrations } from '../server/db/migrations.js'
|
|
8
10
|
import {
|
|
9
11
|
createTaskHandler,
|
|
12
|
+
cronListHandler,
|
|
10
13
|
deleteTaskHandler,
|
|
11
14
|
getDevServerStatusHandler,
|
|
12
15
|
getSessionUsageHandler,
|
|
@@ -19,6 +22,7 @@ import {
|
|
|
19
22
|
markAutoLoopReadyHandler,
|
|
20
23
|
markTaskDoneHandler,
|
|
21
24
|
readDocumentHandler,
|
|
25
|
+
setWorkspaceAgentDescriptionHandler,
|
|
22
26
|
updateTaskHandler,
|
|
23
27
|
} from './kobo-tasks-handlers.js'
|
|
24
28
|
|
|
@@ -39,10 +43,21 @@ if (!dbPath) {
|
|
|
39
43
|
|
|
40
44
|
let db: Database.Database
|
|
41
45
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
// Use the shared `getDb` singleton so any service that calls `getDb()`
|
|
47
|
+
// internally (e.g. cron-service.arm) hits the SAME connection as this
|
|
48
|
+
// MCP server, against the SAME DB file. Without this bootstrap, the
|
|
49
|
+
// singleton would resolve via getDbPath() → getKoboHome() → KOBO_HOME
|
|
50
|
+
// env var, which the agent SDK does NOT pass to the MCP server (only
|
|
51
|
+
// KOBO_DB_PATH is passed). That would silently open a second connection
|
|
52
|
+
// against the user's prod DB at ~/.config/kobo/kobo.db, which may not
|
|
53
|
+
// even have the same schema as the dev DB the backend is using.
|
|
54
|
+
db = getDb(dbPath)
|
|
55
|
+
// Defensive: run migrations to ensure the schema is at the latest version.
|
|
56
|
+
// The backend already ran them at boot, but this MCP server might be the
|
|
57
|
+
// first to touch the DB (e.g. tests, race at first boot, or KOBO_DB_PATH
|
|
58
|
+
// pointing somewhere the backend hasn't migrated). Idempotent — no-op if
|
|
59
|
+
// the DB is already at SCHEMA_VERSION.
|
|
60
|
+
runMigrations(db)
|
|
46
61
|
} catch (err) {
|
|
47
62
|
console.error('[kobo-tasks-server] Failed to open database:', err)
|
|
48
63
|
process.exit(1)
|
|
@@ -87,6 +102,22 @@ async function notifyAutoLoopReady(): Promise<void> {
|
|
|
87
102
|
}
|
|
88
103
|
}
|
|
89
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Fire-and-forget POST that lands on `/agent-description/notify-updated`,
|
|
107
|
+
* which emits the `workspace:agent-description-updated` WS event so the
|
|
108
|
+
* sidebar fallback display + the workspace header italic line refresh live
|
|
109
|
+
* across every connected client. The handler already wrote the column to DB;
|
|
110
|
+
* this call is ONLY for the event emission.
|
|
111
|
+
*/
|
|
112
|
+
async function notifyAgentDescriptionUpdated(): Promise<void> {
|
|
113
|
+
try {
|
|
114
|
+
const url = `${backendUrl}/api/workspaces/${workspaceId}/agent-description/notify-updated`
|
|
115
|
+
await fetch(url, { method: 'POST' })
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.error('[kobo-tasks-server] notify-agent-description-updated failed:', err)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
90
121
|
/** Generic HTTP request to the Kobo backend, returning parsed JSON or null. */
|
|
91
122
|
async function backendRequest(
|
|
92
123
|
method: 'GET' | 'POST' | 'PATCH' | 'DELETE',
|
|
@@ -192,6 +223,73 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
192
223
|
'CALL EARLY in a session to confirm project path, working/source branch, worktree path, model, and notion link. Cheap read — useful when the user refers to "this workspace" or when you need the worktree path to locate files.',
|
|
193
224
|
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
194
225
|
},
|
|
226
|
+
{
|
|
227
|
+
name: 'set_workspace_agent_description',
|
|
228
|
+
description:
|
|
229
|
+
"Set or clear the workspace's agent-side description (≤ 200 chars). Pass an empty string to clear. The user sees this string under the workspace title in the sidebar (it takes precedence over the user-controlled `description` field). Plain text only. The current value is available via get_workspace_info as agentDescription. NOTE: there is a separate user-controlled `description` field — do NOT try to write it; it has no MCP tool.",
|
|
230
|
+
inputSchema: {
|
|
231
|
+
type: 'object',
|
|
232
|
+
properties: {
|
|
233
|
+
description: {
|
|
234
|
+
type: 'string',
|
|
235
|
+
description: 'Plain text, max 200 characters. Empty string clears the description.',
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
required: ['description'],
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: 'cron_create',
|
|
243
|
+
description:
|
|
244
|
+
'Schedule a recurring trigger on THIS workspace. At each fire, Kōbō waits for the workspace to be idle (no active session) and then resumes the same conversation by injecting `prompt` as the next user message — same UX as `schedule_wakeup` but recurring. Skip-if-active: if a session is already running when the timer fires, that occurrence is skipped, the next occurrence is computed, and the cron continues. The cron persists across server restarts (skip-missed semantics on boot — no catchup spam). Delete with `cron_delete(id)`. Multiple crons per workspace are allowed.',
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
properties: {
|
|
248
|
+
expression: {
|
|
249
|
+
type: 'string',
|
|
250
|
+
description:
|
|
251
|
+
'Standard 5-field cron expression (`min hour dom month dow`) or one of the helpers `@hourly`, `@daily`, `@weekly`, `@monthly`, `@yearly`. Example: `*/30 * * * *` = every 30 minutes; `0 9 * * 1` = every Monday at 9am. Validated at create time.',
|
|
252
|
+
},
|
|
253
|
+
prompt: {
|
|
254
|
+
type: 'string',
|
|
255
|
+
description: 'The prompt to inject as the next user message at each fire.',
|
|
256
|
+
},
|
|
257
|
+
label: {
|
|
258
|
+
type: 'string',
|
|
259
|
+
description: 'Optional human-readable label for the cron (shown in the UI).',
|
|
260
|
+
},
|
|
261
|
+
mode: {
|
|
262
|
+
type: 'string',
|
|
263
|
+
enum: ['resume', 'fresh'],
|
|
264
|
+
description:
|
|
265
|
+
"How each fire is handled. 'resume' (default) pins the cron to the session you're calling from, so every fire continues THAT conversation by injecting `prompt` as the next user message — use this when the cron should follow up on ongoing work. 'fresh' starts a brand-new session at every fire with a clean context — use this for periodic checks (e.g. CI watch, daily standup) that don't need conversation continuity.",
|
|
266
|
+
},
|
|
267
|
+
oneShot: {
|
|
268
|
+
type: 'boolean',
|
|
269
|
+
description:
|
|
270
|
+
"When true, the cron cancels itself after the first real fire (default false = recurring). Use this to schedule a single trigger at a specific cron-expressible time (e.g. `0 14 7 6 *` = next 7 June at 14:00) without it repeating yearly. Skip-active fires don't consume the one-shot — the cron retries at the next occurrence until it actually runs once.",
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
required: ['expression', 'prompt'],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: 'cron_delete',
|
|
278
|
+
description:
|
|
279
|
+
"Cancel a previously-armed cron by id. Idempotent — returns ok=true even if the id is unknown. Only the workspace's own crons can be cancelled (cron_list to see them).",
|
|
280
|
+
inputSchema: {
|
|
281
|
+
type: 'object',
|
|
282
|
+
properties: {
|
|
283
|
+
id: { type: 'string', description: 'The cron id returned by cron_create.' },
|
|
284
|
+
},
|
|
285
|
+
required: ['id'],
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: 'cron_list',
|
|
290
|
+
description: 'List all crons currently armed on THIS workspace, including their next and last fire times.',
|
|
291
|
+
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
292
|
+
},
|
|
195
293
|
{
|
|
196
294
|
name: 'get_git_info',
|
|
197
295
|
description:
|
|
@@ -341,13 +439,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
341
439
|
{
|
|
342
440
|
name: 'schedule_wakeup',
|
|
343
441
|
description:
|
|
344
|
-
'CALL to schedule a follow-up turn on THIS workspace after a delay. End the current turn normally; once it finishes and the workspace is idle, Kōbō waits `delaySeconds`, then resumes the same conversation by injecting `prompt` as the next user message. The wakeup is scoped to the current workspace and resumes its latest session — you cannot target another workspace or another session. If a turn is still active when the timer fires, the wakeup is skipped (status: `session-active`). Replaces any previously pending wakeup on this workspace. Delay is clamped to [60,
|
|
442
|
+
'CALL to schedule a follow-up turn on THIS workspace after a delay. End the current turn normally; once it finishes and the workspace is idle, Kōbō waits `delaySeconds`, then resumes the same conversation by injecting `prompt` as the next user message. The wakeup is scoped to the current workspace and resumes its latest session — you cannot target another workspace or another session. If a turn is still active when the timer fires, the wakeup is skipped (status: `session-active`). Replaces any previously pending wakeup on this workspace. Delay is clamped to [60, 21600] seconds (1min to 6h). Prefer this over the built-in `ScheduleWakeup` tool — it is the SDK-supported entry point.',
|
|
345
443
|
inputSchema: {
|
|
346
444
|
type: 'object',
|
|
347
445
|
properties: {
|
|
348
446
|
delaySeconds: {
|
|
349
447
|
type: 'number',
|
|
350
|
-
description: 'Seconds from now until the wakeup fires. Clamped to [60,
|
|
448
|
+
description: 'Seconds from now until the wakeup fires. Clamped to [60, 21600] (1min to 6h).',
|
|
351
449
|
},
|
|
352
450
|
prompt: {
|
|
353
451
|
type: 'string',
|
|
@@ -452,6 +550,66 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
452
550
|
return ok(getWorkspaceInfoHandler(db, workspaceId!))
|
|
453
551
|
}
|
|
454
552
|
|
|
553
|
+
if (name === 'set_workspace_agent_description') {
|
|
554
|
+
const description = a.description as string | undefined
|
|
555
|
+
if (typeof description !== 'string') return fail('description parameter is required')
|
|
556
|
+
const result = setWorkspaceAgentDescriptionHandler(db, workspaceId!, { description })
|
|
557
|
+
if ('ok' in result && result.ok) {
|
|
558
|
+
void notifyAgentDescriptionUpdated()
|
|
559
|
+
}
|
|
560
|
+
return ok(result)
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (name === 'cron_create') {
|
|
564
|
+
const expression = a.expression as string | undefined
|
|
565
|
+
const prompt = a.prompt as string | undefined
|
|
566
|
+
const label = typeof a.label === 'string' ? (a.label as string) : undefined
|
|
567
|
+
const mode = typeof a.mode === 'string' ? (a.mode as string) : undefined
|
|
568
|
+
const oneShot = typeof a.oneShot === 'boolean' ? (a.oneShot as boolean) : undefined
|
|
569
|
+
if (typeof expression !== 'string' || typeof prompt !== 'string') {
|
|
570
|
+
return fail('expression and prompt parameters are required')
|
|
571
|
+
}
|
|
572
|
+
if (mode !== undefined && mode !== 'resume' && mode !== 'fresh') {
|
|
573
|
+
return fail("mode must be 'resume' or 'fresh'")
|
|
574
|
+
}
|
|
575
|
+
// Route through the backend so the in-memory `setTimeout` lives in the
|
|
576
|
+
// backend process (which owns orchestrator + WS broadcast). Calling
|
|
577
|
+
// cron-service.arm() directly here would persist the row but arm the
|
|
578
|
+
// timer in the MCP server sub-process, which dies with the agent
|
|
579
|
+
// session — fires would never trigger a real session resume.
|
|
580
|
+
try {
|
|
581
|
+
const created = (await backendRequest('POST', `/api/workspaces/${workspaceId}/crons`, {
|
|
582
|
+
expression,
|
|
583
|
+
prompt,
|
|
584
|
+
label,
|
|
585
|
+
mode,
|
|
586
|
+
oneShot,
|
|
587
|
+
})) as { cron: unknown }
|
|
588
|
+
return ok({ ok: true, cron: created.cron })
|
|
589
|
+
} catch (err) {
|
|
590
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
591
|
+
return ok({ ok: false, error: message })
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (name === 'cron_delete') {
|
|
596
|
+
const id = a.id as string | undefined
|
|
597
|
+
if (typeof id !== 'string') return fail('id parameter is required')
|
|
598
|
+
// Same reason as cron_create — the backend owns the timer Map.
|
|
599
|
+
try {
|
|
600
|
+
await backendRequest('DELETE', `/api/workspaces/${workspaceId}/crons/${id}`)
|
|
601
|
+
return ok({ ok: true })
|
|
602
|
+
} catch (err) {
|
|
603
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
604
|
+
return ok({ ok: false, error: message })
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (name === 'cron_list') {
|
|
609
|
+
const result = cronListHandler(db, workspaceId!)
|
|
610
|
+
return ok(result)
|
|
611
|
+
}
|
|
612
|
+
|
|
455
613
|
if (name === 'start_dev_server') {
|
|
456
614
|
const result = await backendRequest('POST', `/api/dev-server/${workspaceId}/start`)
|
|
457
615
|
return ok(result)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{F as e,L as t,M as n,Q as r,U as i,_t as ee,bt as a,d as o,f as s,g as c,l,p as u,r as te,rt as ne,u as d,v as re}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{t as f}from"./QIcon-BJuyqdsT.js";import{t as p}from"./QBtn-a6jxWjmW.js";import{a as m,b as h,y as g}from"./index-_ZaIBxd6.js";import{n as ie,t as ae}from"./QItemSection-5YpFpPDm.js";import{t as _}from"./QSpace-CLtL3aPy.js";import{t as oe}from"./QList-D0FtnQJI.js";import{t as v}from"./use-quasar-Sdcq6zzV.js";import{t as y}from"./QPage-ChUKoaKe.js";var b={class:`row items-center q-mb-md`},x={class:`text-h6 q-ml-sm`},S={key:0,class:`text-grey-6 text-center q-pa-lg`},C={key:1,class:`column q-gutter-md`},w={class:`text-subtitle2 q-mb-sm`},T={class:`text-caption text-grey-6`},E={class:`text-body2`},D={class:`row items-center`},O={class:`text-subtitle2`},k={class:`row q-col-gutter-md q-mt-xs`},A={class:`col`},j={class:`text-caption text-grey-6`},M={class:`text-body2`},N={class:`col-auto`},P={class:`text-caption text-grey-6`},F={class:`text-body2`},I={class:`col-auto`},L={class:`text-caption text-grey-6`},R={class:`text-body2`},z={class:`row items-center`},B={class:`text-subtitle2`},V={key:0,class:`text-caption text-grey-5 q-mt-xs`},se={key:1,class:`text-caption text-negative q-mt-xs`},H={class:`row items-center`},U={class:`text-subtitle2`},W={class:`text-caption text-grey-6 q-mt-xs`},G={key:0,class:`q-mt-sm`},K={class:`text-caption text-negative q-mb-xs`},ce={class:`text-body2`},le={class:`text-caption text-grey-6`},ue={class:`row items-center`},de={class:`text-subtitle2`},fe={class:`text-subtitle2 q-mb-sm`},pe={class:`column q-gutter-xs`},me={class:`row items-center`},he={class:`q-ml-sm text-caption text-grey-6`},ge={class:`row items-center`},_e={class:`q-ml-sm text-caption text-grey-6`},ve={class:`row items-center`},ye={class:`q-ml-sm text-caption text-grey-6`},q=re({__name:`HealthPage`,setup(re){let q=v(),be=m(),J=r(null),Y=r(!1);async function X(){Y.value=!0;try{let e=await fetch(`/api/health/report`);if(!e.ok)throw Error(`HTTP ${e.status}`);J.value=await e.json()}catch(e){q.notify({type:`negative`,message:String(e),position:`top`,timeout:4e3})}finally{Y.value=!1}}n(X);let xe=l(()=>{let e=J.value?.db.sizeBytes;return e==null?`—`:e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`}),Z=l(()=>{let e=J.value;return e?e.db.schemaVersion===e.db.currentSchemaVersion:!1});function Q(e){return e?`check_circle`:`error`}function $(e){return e?`positive`:`negative`}return(n,r)=>(e(),o(y,{class:`q-pa-md`,style:{"max-width":`900px`,margin:`0 auto`}},{default:i(()=>[d(`div`,b,[c(p,{flat:``,dense:``,round:``,icon:`arrow_back`,onClick:r[0]||=e=>ne(be).back()}),d(`div`,x,a(n.$t(`health.title`)),1),c(_),c(p,{flat:``,dense:``,icon:`refresh`,loading:Y.value,label:n.$t(`common.refresh`),onClick:X},null,8,[`loading`,`label`])]),!J.value&&Y.value?(e(),u(`div`,S,a(n.$t(`common.loading`)),1)):J.value?(e(),u(`div`,C,[c(g,{dark:``,flat:``,bordered:``},{default:i(()=>[c(h,null,{default:i(()=>[d(`div`,w,a(n.$t(`health.envTitle`)),1),d(`div`,T,a(n.$t(`health.koboHome`)),1),d(`div`,E,a(J.value.koboHome),1)]),_:1})]),_:1}),c(g,{dark:``,flat:``,bordered:``},{default:i(()=>[c(h,null,{default:i(()=>[d(`div`,D,[d(`div`,O,a(n.$t(`health.dbTitle`)),1),c(_),c(f,{name:Q(Z.value),color:$(Z.value)},null,8,[`name`,`color`])]),d(`div`,k,[d(`div`,A,[d(`div`,j,a(n.$t(`health.dbPath`)),1),d(`div`,M,a(J.value.db.path),1)]),d(`div`,N,[d(`div`,P,a(n.$t(`health.dbSize`)),1),d(`div`,F,a(xe.value),1)]),d(`div`,I,[d(`div`,L,a(n.$t(`health.schemaVersion`)),1),d(`div`,R,a(J.value.db.schemaVersion)+` / `+a(J.value.db.currentSchemaVersion),1)])])]),_:1})]),_:1}),c(g,{dark:``,flat:``,bordered:``},{default:i(()=>[c(h,null,{default:i(()=>[d(`div`,z,[d(`div`,B,a(n.$t(`health.cliTitle`)),1),c(_),c(f,{name:Q(J.value.claudeCli.available),color:$(J.value.claudeCli.available)},null,8,[`name`,`color`])]),J.value.claudeCli.available?(e(),u(`div`,V,a(J.value.claudeCli.version),1)):(e(),u(`div`,se,a(n.$t(`health.cliMissing`)),1))]),_:1})]),_:1}),c(g,{dark:``,flat:``,bordered:``},{default:i(()=>[c(h,null,{default:i(()=>[d(`div`,H,[d(`div`,U,a(n.$t(`health.workspacesTitle`)),1),c(_),c(f,{name:Q(J.value.workspaces.worktreesMissing.length===0),color:$(J.value.workspaces.worktreesMissing.length===0)},null,8,[`name`,`color`])]),d(`div`,W,a(n.$t(`health.workspacesCount`,{total:J.value.workspaces.total,archived:J.value.workspaces.archived})),1),J.value.workspaces.worktreesMissing.length>0?(e(),u(`div`,G,[d(`div`,K,a(n.$t(`health.worktreesMissing`,{count:J.value.workspaces.worktreesMissing.length})),1),c(oe,{dense:``,dark:``},{default:i(()=>[(e(!0),u(te,null,t(J.value.workspaces.worktreesMissing,t=>(e(),o(ie,{key:t.workspaceId},{default:i(()=>[c(ae,null,{default:i(()=>[d(`div`,ce,a(t.name),1),d(`div`,le,a(t.path),1)]),_:2},1024)]),_:2},1024))),128))]),_:1})])):s(``,!0)]),_:1})]),_:1}),c(g,{dark:``,flat:``,bordered:``},{default:i(()=>[c(h,null,{default:i(()=>[d(`div`,ue,[d(`div`,de,a(n.$t(`health.sessionsTitle`)),1),c(_),c(f,{name:Q(J.value.agentSessions.orphaned===0),color:$(J.value.agentSessions.orphaned===0)},null,8,[`name`,`color`])]),d(`div`,{class:ee([`text-caption q-mt-xs`,J.value.agentSessions.orphaned>0?`text-negative`:`text-grey-6`])},a(n.$t(`health.sessionsOrphaned`,{n:J.value.agentSessions.orphaned})),3)]),_:1})]),_:1}),c(g,{dark:``,flat:``,bordered:``},{default:i(()=>[c(h,null,{default:i(()=>[d(`div`,fe,a(n.$t(`health.integrationsTitle`)),1),d(`div`,pe,[d(`div`,me,[c(f,{name:Q(J.value.integrations.notion.configured),color:$(J.value.integrations.notion.configured),size:`sm`},null,8,[`name`,`color`]),r[1]||=d(`span`,{class:`q-ml-sm text-body2`},`Notion`,-1),d(`span`,he,a(J.value.integrations.notion.configured?n.$t(`health.integrationConfigured`):n.$t(`health.integrationMissing`)),1)]),d(`div`,ge,[c(f,{name:Q(J.value.integrations.sentry.configured),color:$(J.value.integrations.sentry.configured),size:`sm`},null,8,[`name`,`color`]),r[2]||=d(`span`,{class:`q-ml-sm text-body2`},`Sentry`,-1),d(`span`,_e,a(J.value.integrations.sentry.configured?n.$t(`health.integrationConfigured`):n.$t(`health.integrationMissing`)),1)]),d(`div`,ve,[c(f,{name:Q(J.value.integrations.editor.configured),color:$(J.value.integrations.editor.configured),size:`sm`},null,8,[`name`,`color`]),r[3]||=d(`span`,{class:`q-ml-sm text-body2`},`Editor`,-1),d(`span`,ye,a(J.value.integrations.editor.configured?n.$t(`health.integrationConfigured`):n.$t(`health.integrationMissing`)),1)])])]),_:1})]),_:1})])):s(``,!0)]),_:1}))}});export{q as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.acceptance-item[data-v-20a94a3d]{padding:2px 0}.acceptance-item[data-v-20a94a3d]:hover{background-color:#ffffff08}.acceptance-item:hover .criterion-delete-btn[data-v-20a94a3d]{opacity:1}.criterion-delete-btn[data-v-20a94a3d]{opacity:0;transition:opacity .15s}.acceptance-title[data-v-20a94a3d]{word-break:break-word;cursor:text;line-height:1.4}.text-strike[data-v-20a94a3d]{opacity:.7;text-decoration:line-through}.criterion-input[data-v-20a94a3d] .q-field__control{height:22px;min-height:22px;padding:0}.criterion-input[data-v-20a94a3d] input{color:#e0e0e0;font-size:12px}.criterion-input[data-v-20a94a3d] input::placeholder{color:#555}.criterion-add-row[data-v-20a94a3d],.todo-item[data-v-6a3613f7]{padding:2px 0}.todo-title[data-v-6a3613f7]{word-break:break-word;line-height:1.4}.text-strike[data-v-6a3613f7]{opacity:.7;text-decoration:line-through}.documents-tree[data-v-89e5de13] .q-tree__node--selected>.q-tree__node-header{background:#7986cb26}.documents-tree[data-v-89e5de13] .q-tree__node-header{padding:2px 4px}.document-content[data-v-89e5de13]{color:#d0d0d0;overflow-wrap:break-word;font-size:12px;line-height:1.6}.document-content[data-v-89e5de13] h1{color:#e0e0e0;margin:16px 0 8px;font-size:16px}.document-content[data-v-89e5de13] h2{color:#e0e0e0;margin:14px 0 6px;font-size:14px}.document-content[data-v-89e5de13] h3{color:#e0e0e0;margin:12px 0 4px;font-size:13px}.document-content[data-v-89e5de13] code{background:#1a1a2e;border-radius:3px;padding:1px 4px;font-family:Roboto Mono,monospace;font-size:11px}.document-content[data-v-89e5de13] pre{background:#1a1a2e;border-radius:6px;padding:8px 12px;font-size:11px;overflow-x:auto}.document-content[data-v-89e5de13] pre code{background:0 0;padding:0}.document-content[data-v-89e5de13] ul,.document-content[data-v-89e5de13] ol{padding-left:20px}.document-content[data-v-89e5de13] li{margin-bottom:2px}.document-content[data-v-89e5de13] input[type=checkbox]{pointer-events:none;margin-right:6px}.document-content[data-v-89e5de13] table{border-collapse:collapse;width:100%;margin:8px 0;font-size:11px}.document-content[data-v-89e5de13] th,.document-content[data-v-89e5de13] td{text-align:left;border:1px solid #2a2a4a;padding:4px 8px}.document-content[data-v-89e5de13] th{color:#e0e0e0;background:#1a1a2e}.document-content[data-v-89e5de13] blockquote{color:#a0a0b0;border-left:3px solid #4a4a6a;margin:8px 0;padding:4px 12px}.document-content[data-v-89e5de13] a{color:#818cf8}.document-content[data-v-89e5de13] hr{border:none;border-top:1px solid #2a2a4a;margin:12px 0}.commit-item[data-v-fd1cc184]:hover{background:#ffffff0a}.commit-sha[data-v-fd1cc184]{font-family:Roboto Mono,monospace;font-size:11px}.commit-subject[data-v-fd1cc184]{max-width:480px;font-size:12px}.git-btn[data-v-ff606b69]{padding:2px 8px;font-size:11px}.git-sync-btn[data-v-ff606b69]{padding:2px 0}.git-sync-btn[data-v-ff606b69] .q-btn__content{padding:0 8px}.git-sync-btn[data-v-ff606b69] .q-btn-dropdown__arrow-container{border-left:1px solid #ffffff1f;padding:0 4px}.git-sync-btn[data-v-ff606b69] .q-btn-dropdown--split{gap:0}.commit-toggle[data-v-ff606b69]:hover{background-color:#ffffff08;border-radius:3px}.commit-list[data-v-ff606b69]{border-left:2px solid #ffffff0a;max-height:260px;margin-left:7px;padding-left:6px;overflow-y:auto}.commit-item[data-v-ff606b69]{border-radius:3px;padding:3px 4px;font-size:11px;line-height:1.3;transition:background-color .1s}.commit-item[data-v-ff606b69]:hover{background-color:#818cf814}.commit-item-icon[data-v-ff606b69]{flex-shrink:0}.commit-sha[data-v-ff606b69]{flex-shrink:0;font-family:Roboto Mono,monospace;font-size:10.5px}.commit-subject[data-v-ff606b69]{min-width:0;font-size:11px}.arrow-clickable[data-v-ff606b69]{transition:opacity .15s}.arrow-clickable[data-v-ff606b69]:hover{opacity:.75;text-decoration:underline}.git-branch-code{color:#c7d2fe;white-space:nowrap;background-color:#818cf824;border-radius:3px;padding:1px 5px;font-family:Roboto Mono,monospace;font-size:11px}.subagents-panel[data-v-dd805632]{overflow-y:auto}.subagent-item[data-v-dd805632]{background:#1e1e3a;border:1px solid #2a2a4a}.task-item[data-v-4bc9713a]{padding:2px 0}.task-item[data-v-4bc9713a]:hover{background-color:#ffffff08}.task-item:hover .task-delete-btn[data-v-4bc9713a]{opacity:1}.task-delete-btn[data-v-4bc9713a]{opacity:0;transition:opacity .15s}.task-title[data-v-4bc9713a]{word-break:break-word;cursor:text;line-height:1.4}.text-strike[data-v-4bc9713a]{opacity:.7;text-decoration:line-through}.task-input[data-v-4bc9713a] .q-field__control{height:22px;min-height:22px;padding:0}.task-input[data-v-4bc9713a] input{color:#e0e0e0;font-size:12px}.task-input[data-v-4bc9713a] input::placeholder{color:#555}.task-add-row[data-v-4bc9713a]{padding:2px 0}.xterm{cursor:text;-webkit-user-select:none;user-select:none;position:relative}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{z-index:5;position:absolute;top:0}.xterm .xterm-helper-textarea{opacity:0;z-index:-5;white-space:nowrap;resize:none;border:0;width:0;height:0;margin:0;padding:0;position:absolute;top:0;left:-9999em;overflow:hidden}.xterm .composition-view{color:#fff;white-space:nowrap;z-index:1;background:#000;display:none;position:absolute}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{cursor:default;background-color:#000;position:absolute;top:0;bottom:0;left:0;right:0;overflow-y:scroll}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;top:0;left:0}.xterm-char-measure-element{visibility:hidden;line-height:normal;display:inline-block;position:absolute;top:0;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{z-index:10;color:#0000;pointer-events:none;position:absolute;top:0;bottom:0;left:0;right:0}.xterm .xterm-accessibility-tree:not(.debug) ::-moz-selection{color:#0000}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:#0000}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre;font-family:monospace}.xterm .xterm-accessibility-tree>div{transform-origin:0;width:-moz-fit-content;width:fit-content}.xterm .live-region{width:1px;height:1px;position:absolute;left:-9999px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{-webkit-text-decoration:underline double;text-decoration:underline double}.xterm-underline-3{-webkit-text-decoration:underline wavy;text-decoration:underline wavy}.xterm-underline-4{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.xterm-underline-5{-webkit-text-decoration:underline dashed;text-decoration:underline dashed}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:underline overline}.xterm-overline.xterm-underline-2{-webkit-text-decoration:overline double underline;-webkit-text-decoration:overline double underline;text-decoration:overline double underline}.xterm-overline.xterm-underline-3{-webkit-text-decoration:overline wavy underline;-webkit-text-decoration:overline wavy underline;text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{-webkit-text-decoration:overline dotted underline;-webkit-text-decoration:overline dotted underline;text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{-webkit-text-decoration:overline dashed underline;-webkit-text-decoration:overline dashed underline;text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;pointer-events:none;position:absolute;top:0;right:0}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;z-index:11;background:0 0;transition:opacity .1s linear}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{display:none;position:absolute}.xterm .xterm-scrollable-element>.shadow.top{width:100%;height:3px;box-shadow:var(--vscode-scrollbar-shadow,#000) 0 6px 6px -6px inset;display:block;top:0;left:3px}.xterm .xterm-scrollable-element>.shadow.left{width:3px;height:100%;box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;display:block;top:3px;left:0}.xterm .xterm-scrollable-element>.shadow.top-left-corner{width:3px;height:3px;display:block;top:0;left:0}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset}.log-content[data-v-d988f9ab]{max-height:60vh;overflow-y:auto}.log-text[data-v-d988f9ab]{color:#ccc;white-space:pre-wrap;word-break:break-all;background:#0d0d1a;margin:0;font-family:JetBrains Mono,Fira Code,monospace;font-size:11px}.dd-panel[data-v-9d25d6ee],.tools-panel[data-v-88a83f94]{min-height:48px}.auto-loop-spin[data-v-5bfef979]{animation:2s linear infinite auto-loop-spin-5bfef979}@keyframes auto-loop-spin-5bfef979{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.workspace-list[data-v-45541376]{background-color:#16162a;overflow-x:hidden}.wl-search[data-v-45541376]{background-color:#224;padding:0 8px}.wl-search[data-v-45541376] .q-field__control{height:32px}.wl-search[data-v-45541376] input{color:#ccc;font-size:12px}.wl-group-header[data-v-45541376]:hover{background-color:#ffffff08}.wl-project-group+.wl-project-group[data-v-45541376]{margin-top:6px}.wl-project-label[data-v-45541376]{align-items:center;padding-bottom:2px;font-size:11px;display:flex}.wl-item[data-v-45541376]{background-color:#224;margin-bottom:4px;transition:background-color .15s,box-shadow .15s;position:relative}.wl-item[data-v-45541376]:last-child{margin-bottom:0}.wl-item[data-v-45541376]:hover{background-color:#2a2a4a}.wl-item--selected[data-v-45541376]{background-color:#393969;outline:1.5px solid #6c63fff2;box-shadow:0 0 0 1px #6c63ff4d,0 2px 8px #6c63ff40}.wl-item--selected[data-v-45541376]:hover{background-color:#41417a}.wl-item--selected .wl-item-name[data-v-45541376]{color:#fff!important;opacity:1!important}.wl-item-action[data-v-45541376]{position:absolute;top:4px;right:4px}.wl-item--archived .wl-item-action[data-v-45541376]{opacity:0;transition:opacity .15s}.wl-item--archived:hover .wl-item-action[data-v-45541376]{opacity:1}.wl-item-unarchive[data-v-45541376]{right:28px}.wl-item-delete[data-v-45541376]{right:4px}.wl-item--archived[data-v-45541376]{opacity:.6;background-color:#1a1a30}.wl-item--archived[data-v-45541376]:hover{opacity:.85}.wl-item--archived.wl-item--selected[data-v-45541376]{opacity:1}.dd-dot[data-v-45541376]{border-radius:50%;flex-shrink:0;width:6px;height:6px}.dd-dot--running[data-v-45541376]{background-color:#22c55e;box-shadow:0 0 4px #22c55e80}.bg-dark[data-v-e27e1c92]{background-color:#16162a!important;border-color:#2a2a4a!important}.resize-handle[data-v-e27e1c92]{cursor:col-resize;z-index:10;width:4px;height:100%;transition:background-color .15s;position:absolute;top:0;right:-2px}.resize-handle[data-v-e27e1c92]:hover,.resize-handle[data-v-e27e1c92]:active{background-color:#6c63ff80}.resize-handle--right[data-v-e27e1c92]{left:-2px;right:auto}.vertical-resize-handle[data-v-e27e1c92]{cursor:row-resize;background-color:#2a2a4a;height:4px;transition:background-color .15s}.vertical-resize-handle[data-v-e27e1c92]:hover,.vertical-resize-handle[data-v-e27e1c92]:active{background-color:#6c63ff80}
|