@loicngr/kobo 1.4.9 → 1.4.11
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/AGENTS.md +16 -10
- package/README.md +5 -1
- package/dist/mcp-server/kobo-tasks-server.js +1 -1
- package/dist/server/db/migrations.js +31 -0
- package/dist/server/db/schema.js +2 -1
- package/dist/server/index.js +14 -5
- package/dist/server/routes/plans.js +89 -0
- package/dist/server/routes/templates.js +82 -0
- package/dist/server/routes/workspaces.js +110 -11
- package/dist/server/services/agent-manager.js +74 -21
- package/dist/server/services/settings-service.js +10 -0
- package/dist/server/services/templates-service.js +173 -0
- package/dist/server/services/workspace-service.js +52 -3
- package/dist/server/utils/git-ops.js +10 -0
- package/dist/server/utils/paths.js +7 -0
- package/package.json +1 -1
- package/src/client/dist/spa/assets/ActivityFeed-C8UZFjUM.css +1 -0
- package/src/client/dist/spa/assets/ActivityFeed-CahLjg_a.js +10 -0
- package/src/client/dist/spa/assets/ClosePopup-jSuaV6dg.js +1 -0
- package/src/client/dist/spa/assets/CreatePage-DByy8YPu.css +1 -0
- package/src/client/dist/spa/assets/CreatePage-DvenHBbs.js +2 -0
- package/src/client/dist/spa/assets/DiffViewer-BNB89NC_.js +2 -0
- package/src/client/dist/spa/assets/MainLayout-8hdHhbFX.css +1 -0
- package/src/client/dist/spa/assets/MainLayout-CRZ_pSUI.js +2 -0
- package/src/client/dist/spa/assets/QExpansionItem-D6zpEqBV.js +1 -0
- package/src/client/dist/spa/assets/{QList-tXJCQz3K.js → QList-oHuiVWr0.js} +1 -1
- package/src/client/dist/spa/assets/QMenu-67GqYx0C.js +1 -0
- package/src/client/dist/spa/assets/QPage-DE75SzRI.js +1 -0
- package/src/client/dist/spa/assets/SettingsPage-BIAu1N7t.js +1 -0
- package/src/client/dist/spa/assets/SettingsPage-ai7Q_1KB.css +1 -0
- package/src/client/dist/spa/assets/{TouchPan-Dc-xrSIS.js → TouchPan-DuISf80E.js} +1 -1
- package/src/client/dist/spa/assets/WorkspacePage-ZtCuDedw.js +4 -0
- package/src/client/dist/spa/assets/WorkspacePage-l-yPyCj4.css +1 -0
- package/src/client/dist/spa/assets/_plugin-vue_export-helper-fkfRoKj2.js +1 -0
- package/src/client/dist/spa/assets/{cssMode-hVzqeL3Y.js → cssMode-CLNBNUPN.js} +1 -1
- package/src/client/dist/spa/assets/{editor.api-CDy2oNdA.js → editor.api-BOAfcnN4.js} +1 -1
- package/src/client/dist/spa/assets/{editor.main-By6Nm4Uo.js → editor.main-DHOeskpn.js} +3 -3
- package/src/client/dist/spa/assets/{freemarker2-DsZ7OE8f.js → freemarker2-BtrSj5Xy.js} +1 -1
- package/src/client/dist/spa/assets/{handlebars-BwM3WWoa.js → handlebars-Dc1FuhPx.js} +1 -1
- package/src/client/dist/spa/assets/{html-BevRIlIZ.js → html-CRa_T0ab.js} +1 -1
- package/src/client/dist/spa/assets/{htmlMode-B1A5H5QY.js → htmlMode-DiZaznA3.js} +1 -1
- package/src/client/dist/spa/assets/i18n-Chh7fA86.js +1 -0
- package/src/client/dist/spa/assets/i18n-EPkNuLUF.js +1 -0
- package/src/client/dist/spa/assets/index-BIsYlO92.js +5 -0
- package/src/client/dist/spa/assets/{javascript-Cbwf_WZd.js → javascript-BmWQzw3l.js} +1 -1
- package/src/client/dist/spa/assets/{jsonMode-DXxkqMuk.js → jsonMode-CxTgSexO.js} +1 -1
- package/src/client/dist/spa/assets/{liquid-CMWSadqH.js → liquid-BUs2kSUC.js} +1 -1
- package/src/client/dist/spa/assets/marked.esm-BjjOHIBz.js +60 -0
- package/src/client/dist/spa/assets/{mdx-CXFMxs0m.js → mdx-Cnd0lVUN.js} +1 -1
- package/src/client/dist/spa/assets/{monaco.contribution-DojwMy68.js → monaco.contribution-DdEdriCG.js} +2 -2
- package/src/client/dist/spa/assets/{python-Ct1-RX08.js → python-DEYsKpRr.js} +1 -1
- package/src/client/dist/spa/assets/{razor-BLUxJqNg.js → razor-D5u_QaVY.js} +1 -1
- package/src/client/dist/spa/assets/{settings-B0JkmJA1.js → settings-CuK-S6HH.js} +1 -1
- package/src/client/dist/spa/assets/{tsMode-DfoQS6sD.js → tsMode-DWHAMuFy.js} +1 -1
- package/src/client/dist/spa/assets/{typescript-Ce2TQPtr.js → typescript-miqJki5_.js} +1 -1
- package/src/client/dist/spa/assets/{xml-C9QcFOCj.js → xml-TsOUFKqU.js} +1 -1
- package/src/client/dist/spa/assets/{yaml-CpUUJmRX.js → yaml-Bn4aEJci.js} +1 -1
- package/src/client/dist/spa/index.html +3 -3
- package/src/mcp-server/kobo-tasks-server.ts +1 -1
- package/src/client/dist/spa/assets/ActivityFeed-5m7j8KXA.css +0 -1
- package/src/client/dist/spa/assets/ActivityFeed-jhus97Cf.js +0 -68
- package/src/client/dist/spa/assets/CreatePage-BErPWWmP.js +0 -2
- package/src/client/dist/spa/assets/CreatePage-CgqSs5JM.css +0 -1
- package/src/client/dist/spa/assets/DiffViewer-BmAd7k-Q.js +0 -2
- package/src/client/dist/spa/assets/MainLayout-BW7daPf7.js +0 -2
- package/src/client/dist/spa/assets/MainLayout-Drv4zYbW.css +0 -1
- package/src/client/dist/spa/assets/QExpansionItem-Ch5P6bzC.js +0 -1
- package/src/client/dist/spa/assets/QMenu-BE-OO3N4.js +0 -1
- package/src/client/dist/spa/assets/QPage-CkDrNXyM.js +0 -1
- package/src/client/dist/spa/assets/QTooltip-DKxEdudV.js +0 -1
- package/src/client/dist/spa/assets/SettingsPage-B5j-Zv91.js +0 -1
- package/src/client/dist/spa/assets/SettingsPage-BzsaegMp.css +0 -1
- package/src/client/dist/spa/assets/WorkspacePage-DrDOZvHX.css +0 -1
- package/src/client/dist/spa/assets/WorkspacePage-LkjOnfKV.js +0 -4
- package/src/client/dist/spa/assets/_plugin-vue_export-helper-CLHv3piE.js +0 -1
- package/src/client/dist/spa/assets/i18n-BLHRwr9N.js +0 -1
- package/src/client/dist/spa/assets/i18n-yBCUfaG0.js +0 -1
- package/src/client/dist/spa/assets/index-BxBqipDT.js +0 -5
|
@@ -89,7 +89,7 @@ function runWatchdog() {
|
|
|
89
89
|
catch {
|
|
90
90
|
// best-effort
|
|
91
91
|
}
|
|
92
|
-
emit(workspaceId, 'agent:status', { status: 'error', message: 'Agent process died unexpectedly' }, agent.
|
|
92
|
+
emit(workspaceId, 'agent:status', { status: 'error', message: 'Agent process died unexpectedly' }, agent.agentSessionId);
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
/** Start the watchdog (called once from server bootstrap). */
|
|
@@ -108,7 +108,7 @@ export function stopWatchdog() {
|
|
|
108
108
|
}
|
|
109
109
|
// ── Start agent ────────────────────────────────────────────────────────────────
|
|
110
110
|
/** Spawn a Claude Code CLI process for a workspace and wire up stdout/stderr/exit handling. */
|
|
111
|
-
export function startAgent(workspaceId, workingDir, prompt, model, resume = false, permissionMode = 'auto-accept') {
|
|
111
|
+
export function startAgent(workspaceId, workingDir, prompt, model, resume = false, permissionMode = 'auto-accept', existingSessionId) {
|
|
112
112
|
// Check if agent already running for this workspace
|
|
113
113
|
if (agents.has(workspaceId)) {
|
|
114
114
|
throw new Error(`Agent already running for workspace '${workspaceId}'`);
|
|
@@ -132,10 +132,28 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
132
132
|
args.push('--model', model);
|
|
133
133
|
}
|
|
134
134
|
if (resume) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
// Prefer resuming the specific session requested by the caller (existingSessionId).
|
|
136
|
+
// Otherwise fall back to the most recent session for the workspace.
|
|
137
|
+
let lastSession;
|
|
138
|
+
if (existingSessionId) {
|
|
139
|
+
lastSession = db
|
|
140
|
+
.prepare('SELECT id, claude_session_id FROM agent_sessions WHERE id = ? AND workspace_id = ? AND claude_session_id IS NOT NULL LIMIT 1')
|
|
141
|
+
.get(existingSessionId, workspaceId);
|
|
142
|
+
// If the caller explicitly asked to resume a specific session, fail loudly
|
|
143
|
+
// when it cannot be resumed (no claude_session_id, wrong workspace, or
|
|
144
|
+
// missing row). Silently creating a new session would orphan the target.
|
|
145
|
+
if (!lastSession) {
|
|
146
|
+
throw new Error(`Cannot resume session '${existingSessionId}' for workspace '${workspaceId}': ` +
|
|
147
|
+
'session not found or has no associated Claude conversation');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
lastSession = db
|
|
152
|
+
.prepare('SELECT id, claude_session_id FROM agent_sessions WHERE workspace_id = ? AND claude_session_id IS NOT NULL ORDER BY started_at DESC LIMIT 1')
|
|
153
|
+
.get(workspaceId);
|
|
154
|
+
}
|
|
155
|
+
// The in-memory cache is workspace-scoped, only useful when no specific session was requested.
|
|
156
|
+
const claudeSessionId = lastSession?.claude_session_id ?? (existingSessionId ? undefined : sessionIds.get(workspaceId));
|
|
139
157
|
if (claudeSessionId) {
|
|
140
158
|
resumedClaudeSessionId = claudeSessionId;
|
|
141
159
|
args.push('--resume', claudeSessionId, '-p', prompt);
|
|
@@ -160,8 +178,19 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
160
178
|
}
|
|
161
179
|
else {
|
|
162
180
|
args.push('-p', prompt);
|
|
163
|
-
|
|
164
|
-
|
|
181
|
+
if (existingSessionId) {
|
|
182
|
+
const result = db
|
|
183
|
+
.prepare('UPDATE agent_sessions SET status = ?, started_at = ?, ended_at = NULL WHERE id = ? AND workspace_id = ?')
|
|
184
|
+
.run('running', new Date().toISOString(), existingSessionId, workspaceId);
|
|
185
|
+
if (result.changes === 0) {
|
|
186
|
+
throw new Error(`Agent session '${existingSessionId}' not found for workspace '${workspaceId}'`);
|
|
187
|
+
}
|
|
188
|
+
agentSessionId = existingSessionId;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
agentSessionId = nanoid();
|
|
192
|
+
db.prepare('INSERT INTO agent_sessions (id, workspace_id, pid, status, started_at) VALUES (?, ?, ?, ?, ?)').run(agentSessionId, workspaceId, null, 'running', new Date().toISOString());
|
|
193
|
+
}
|
|
165
194
|
}
|
|
166
195
|
// Write .mcp.json to workingDir so claude picks up the kobo-tasks MCP server
|
|
167
196
|
const mcpConfigPath = path.join(workingDir, '.mcp.json');
|
|
@@ -220,12 +249,12 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
220
249
|
}
|
|
221
250
|
catch {
|
|
222
251
|
// Parsing failed — emit raw line
|
|
223
|
-
emit(workspaceId, 'agent:output', { type: 'raw', content: line }, agent.
|
|
252
|
+
emit(workspaceId, 'agent:output', { type: 'raw', content: line }, agent.agentSessionId);
|
|
224
253
|
// Check for BRAINSTORM_COMPLETE marker in raw lines
|
|
225
254
|
if (line.includes('[BRAINSTORM_COMPLETE]')) {
|
|
226
255
|
try {
|
|
227
256
|
updateWorkspaceStatus(workspaceId, 'executing');
|
|
228
|
-
emit(workspaceId, 'agent:status', { status: 'executing' }, agent.
|
|
257
|
+
emit(workspaceId, 'agent:status', { status: 'executing' }, agent.agentSessionId);
|
|
229
258
|
}
|
|
230
259
|
catch (err) {
|
|
231
260
|
console.error('[agent] Failed to transition to executing:', err);
|
|
@@ -289,7 +318,7 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
289
318
|
if (msgType === 'user') {
|
|
290
319
|
return;
|
|
291
320
|
}
|
|
292
|
-
emit(workspaceId, 'agent:output', parsed, agent.
|
|
321
|
+
emit(workspaceId, 'agent:output', parsed, agent.agentSessionId);
|
|
293
322
|
// Detect brainstorming completion from parsed output
|
|
294
323
|
if (msgType === 'assistant' && Array.isArray(p.content)) {
|
|
295
324
|
const hasMarker = p.content.some((block) => {
|
|
@@ -299,7 +328,7 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
299
328
|
if (hasMarker) {
|
|
300
329
|
try {
|
|
301
330
|
updateWorkspaceStatus(workspaceId, 'executing');
|
|
302
|
-
emit(workspaceId, 'agent:status', { status: 'executing' }, agent.
|
|
331
|
+
emit(workspaceId, 'agent:status', { status: 'executing' }, agent.agentSessionId);
|
|
303
332
|
}
|
|
304
333
|
catch (err) {
|
|
305
334
|
console.error('[agent] Failed to transition to executing:', err);
|
|
@@ -315,10 +344,10 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
315
344
|
const text = data.toString();
|
|
316
345
|
const lowerText = text.toLowerCase();
|
|
317
346
|
if (lowerText.includes('rate limit') || lowerText.includes('quota') || lowerText.includes('limit exceeded')) {
|
|
318
|
-
handleQuota(workspaceId, agent.
|
|
347
|
+
handleQuota(workspaceId, agent.agentSessionId);
|
|
319
348
|
}
|
|
320
349
|
// Also emit stderr for visibility
|
|
321
|
-
emit(workspaceId, 'agent:stderr', { content: text }, agent.
|
|
350
|
+
emit(workspaceId, 'agent:stderr', { content: text }, agent.agentSessionId);
|
|
322
351
|
});
|
|
323
352
|
// ── process exit ──
|
|
324
353
|
proc.on('exit', (code) => {
|
|
@@ -353,7 +382,7 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
353
382
|
}
|
|
354
383
|
if (agent.status === 'stopping') {
|
|
355
384
|
// Clean stop requested
|
|
356
|
-
emit(workspaceId, 'agent:status', { status: 'stopped' }, agent.
|
|
385
|
+
emit(workspaceId, 'agent:status', { status: 'stopped' }, agent.agentSessionId);
|
|
357
386
|
return;
|
|
358
387
|
}
|
|
359
388
|
// Also clear backoff timers on non-stopping exit
|
|
@@ -376,7 +405,7 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
376
405
|
catch {
|
|
377
406
|
// best-effort
|
|
378
407
|
}
|
|
379
|
-
emit(workspaceId, 'agent:status', { status: 'error', exitCode: code }, agent.
|
|
408
|
+
emit(workspaceId, 'agent:status', { status: 'error', exitCode: code }, agent.agentSessionId);
|
|
380
409
|
}
|
|
381
410
|
else {
|
|
382
411
|
try {
|
|
@@ -392,15 +421,39 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
392
421
|
catch {
|
|
393
422
|
// best-effort
|
|
394
423
|
}
|
|
395
|
-
emit(workspaceId, 'agent:status', { status: 'completed' }, agent.
|
|
424
|
+
emit(workspaceId, 'agent:status', { status: 'completed' }, agent.agentSessionId);
|
|
396
425
|
}
|
|
397
426
|
});
|
|
398
427
|
// Store in agents map
|
|
399
428
|
agents.set(workspaceId, agent);
|
|
400
429
|
// Notify frontend that agent is now running
|
|
401
|
-
emit(workspaceId, 'agent:status', { status: 'executing' }, agent.
|
|
430
|
+
emit(workspaceId, 'agent:status', { status: 'executing' }, agent.agentSessionId);
|
|
402
431
|
return agent;
|
|
403
432
|
}
|
|
433
|
+
// ── Interrupt agent ────────────────────────────────────────────────────────────
|
|
434
|
+
/**
|
|
435
|
+
* Soft-interrupt the running agent by sending SIGINT. This mirrors pressing
|
|
436
|
+
* Escape in Claude Code CLI: the current tool call is aborted but the process
|
|
437
|
+
* stays alive and waits for the next user message. The agent session remains
|
|
438
|
+
* in 'running' status.
|
|
439
|
+
*/
|
|
440
|
+
export function interruptAgent(workspaceId) {
|
|
441
|
+
const agent = agents.get(workspaceId);
|
|
442
|
+
if (!agent) {
|
|
443
|
+
throw new Error(`No agent running for workspace '${workspaceId}'`);
|
|
444
|
+
}
|
|
445
|
+
const pid = agent.process.pid;
|
|
446
|
+
if (pid === undefined) {
|
|
447
|
+
throw new Error(`Agent process has no PID for workspace '${workspaceId}'`);
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
process.kill(pid, 'SIGINT');
|
|
451
|
+
}
|
|
452
|
+
catch (err) {
|
|
453
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
454
|
+
throw new Error(`Failed to interrupt agent for workspace '${workspaceId}': ${message}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
404
457
|
// ── Stop agent ─────────────────────────────────────────────────────────────────
|
|
405
458
|
/** Gracefully stop an agent (SIGTERM, then SIGKILL after 5s). */
|
|
406
459
|
export function stopAgent(workspaceId) {
|
|
@@ -484,7 +537,7 @@ export function getAvailableSkills() {
|
|
|
484
537
|
return [...KOBO_COMMANDS, ...availableSkills];
|
|
485
538
|
}
|
|
486
539
|
// ── Quota handling ─────────────────────────────────────────────────────────────
|
|
487
|
-
function handleQuota(workspaceId,
|
|
540
|
+
function handleQuota(workspaceId, agentSessionId) {
|
|
488
541
|
// Update workspace status
|
|
489
542
|
try {
|
|
490
543
|
updateWorkspaceStatus(workspaceId, 'quota');
|
|
@@ -493,7 +546,7 @@ function handleQuota(workspaceId, claudeSessionId) {
|
|
|
493
546
|
// May fail if transition is not valid
|
|
494
547
|
}
|
|
495
548
|
// Emit status event
|
|
496
|
-
emit(workspaceId, 'agent:status', { status: 'quota' },
|
|
549
|
+
emit(workspaceId, 'agent:status', { status: 'quota' }, agentSessionId);
|
|
497
550
|
// Calculate backoff: 15min first, then 30min, then 60min cap
|
|
498
551
|
const retryCount = retryCounts.get(workspaceId) ?? 0;
|
|
499
552
|
const backoffMinutes = Math.min(15 * 2 ** retryCount, 60);
|
|
@@ -503,7 +556,7 @@ function handleQuota(workspaceId, claudeSessionId) {
|
|
|
503
556
|
status: 'quota:backoff',
|
|
504
557
|
retryCount: retryCount + 1,
|
|
505
558
|
backoffMinutes,
|
|
506
|
-
},
|
|
559
|
+
}, agentSessionId);
|
|
507
560
|
// Set timer to restart agent
|
|
508
561
|
const timer = setTimeout(() => {
|
|
509
562
|
backoffTimers.delete(workspaceId);
|
|
@@ -117,6 +117,14 @@ const settingsMigrations = [
|
|
|
117
117
|
}
|
|
118
118
|
},
|
|
119
119
|
},
|
|
120
|
+
{
|
|
121
|
+
version: 6,
|
|
122
|
+
name: 'add-default-permission-mode',
|
|
123
|
+
migrate({ global }) {
|
|
124
|
+
if (typeof global.defaultPermissionMode !== 'string')
|
|
125
|
+
global.defaultPermissionMode = 'plan';
|
|
126
|
+
},
|
|
127
|
+
},
|
|
120
128
|
];
|
|
121
129
|
/** Current settings schema version — always equals the highest migration version. */
|
|
122
130
|
export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
|
|
@@ -138,6 +146,7 @@ function defaultSettings() {
|
|
|
138
146
|
audioNotifications: true,
|
|
139
147
|
notionStatusProperty: '',
|
|
140
148
|
notionInProgressStatus: '',
|
|
149
|
+
defaultPermissionMode: 'plan',
|
|
141
150
|
},
|
|
142
151
|
projects: [],
|
|
143
152
|
};
|
|
@@ -276,6 +285,7 @@ export function updateGlobalSettings(data) {
|
|
|
276
285
|
'audioNotifications',
|
|
277
286
|
'notionStatusProperty',
|
|
278
287
|
'notionInProgressStatus',
|
|
288
|
+
'defaultPermissionMode',
|
|
279
289
|
];
|
|
280
290
|
const filtered = pickKnownKeys(data, allowedGlobalKeys);
|
|
281
291
|
settings.global = { ...settings.global, ...filtered };
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getTemplatesPath } from '../utils/paths.js';
|
|
4
|
+
const CURRENT_FILE_VERSION = 1;
|
|
5
|
+
const SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
6
|
+
const MAX_CONTENT_LENGTH = 4096;
|
|
7
|
+
const MAX_DESCRIPTION_LENGTH = 120;
|
|
8
|
+
/**
|
|
9
|
+
* Read the templates list from disk. Seeds with defaults if the file does
|
|
10
|
+
* not exist yet. Returns an empty array (with a logged error) on corruption.
|
|
11
|
+
*/
|
|
12
|
+
export function listTemplates() {
|
|
13
|
+
const filePath = getTemplatesPath();
|
|
14
|
+
if (!existsSync(filePath)) {
|
|
15
|
+
seedTemplates();
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
if (parsed.version !== CURRENT_FILE_VERSION) {
|
|
21
|
+
console.warn(`[templates-service] templates.json has version ${parsed.version}, expected ${CURRENT_FILE_VERSION}. Reading best-effort.`);
|
|
22
|
+
}
|
|
23
|
+
return Array.isArray(parsed.templates) ? parsed.templates : [];
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
console.error('[templates-service] Failed to read templates.json:', err);
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Create a new template. Throws on invalid input or duplicate slug. */
|
|
31
|
+
export function createTemplate(input) {
|
|
32
|
+
validateTemplateInput(input);
|
|
33
|
+
const templates = listTemplates();
|
|
34
|
+
if (templates.some((t) => t.slug === input.slug)) {
|
|
35
|
+
throw new Error(`Template '${input.slug}' already exists`);
|
|
36
|
+
}
|
|
37
|
+
const now = new Date().toISOString();
|
|
38
|
+
const template = {
|
|
39
|
+
slug: input.slug,
|
|
40
|
+
description: input.description,
|
|
41
|
+
content: input.content,
|
|
42
|
+
createdAt: now,
|
|
43
|
+
updatedAt: now,
|
|
44
|
+
};
|
|
45
|
+
writeTemplates([...templates, template]);
|
|
46
|
+
return template;
|
|
47
|
+
}
|
|
48
|
+
/** Update an existing template. Returns null if slug not found. */
|
|
49
|
+
export function updateTemplate(slug, updates) {
|
|
50
|
+
const templates = listTemplates();
|
|
51
|
+
const idx = templates.findIndex((t) => t.slug === slug);
|
|
52
|
+
if (idx < 0)
|
|
53
|
+
return null;
|
|
54
|
+
const current = templates[idx];
|
|
55
|
+
const next = {
|
|
56
|
+
...current,
|
|
57
|
+
description: updates.description ?? current.description,
|
|
58
|
+
content: updates.content ?? current.content,
|
|
59
|
+
updatedAt: new Date().toISOString(),
|
|
60
|
+
};
|
|
61
|
+
validateTemplateInput({ slug: next.slug, description: next.description, content: next.content });
|
|
62
|
+
templates[idx] = next;
|
|
63
|
+
writeTemplates(templates);
|
|
64
|
+
return next;
|
|
65
|
+
}
|
|
66
|
+
/** Delete a template. Returns true if deleted, false if not found. */
|
|
67
|
+
export function deleteTemplate(slug) {
|
|
68
|
+
const templates = listTemplates();
|
|
69
|
+
const next = templates.filter((t) => t.slug !== slug);
|
|
70
|
+
if (next.length === templates.length)
|
|
71
|
+
return false;
|
|
72
|
+
writeTemplates(next);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
// ── Internals ──────────────────────────────────────────────────────────────
|
|
76
|
+
function validateTemplateInput(input) {
|
|
77
|
+
if (!SLUG_PATTERN.test(input.slug)) {
|
|
78
|
+
throw new Error(`Invalid slug '${input.slug}': must match ${SLUG_PATTERN} (lowercase letters, digits, hyphens; 1–64 chars)`);
|
|
79
|
+
}
|
|
80
|
+
// Consistent rule for both description and content:
|
|
81
|
+
// - reject if empty after trim (all-whitespace is not valid)
|
|
82
|
+
// - reject if raw length exceeds the max (trailing whitespace still counts)
|
|
83
|
+
const rawDescription = input.description ?? '';
|
|
84
|
+
if (rawDescription.trim().length === 0 || rawDescription.length > MAX_DESCRIPTION_LENGTH) {
|
|
85
|
+
throw new Error(`Invalid description: must be 1..${MAX_DESCRIPTION_LENGTH} chars`);
|
|
86
|
+
}
|
|
87
|
+
const rawContent = input.content ?? '';
|
|
88
|
+
if (rawContent.trim().length === 0 || rawContent.length > MAX_CONTENT_LENGTH) {
|
|
89
|
+
throw new Error(`Invalid content: must be 1..${MAX_CONTENT_LENGTH} chars`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function writeTemplates(templates) {
|
|
93
|
+
const filePath = getTemplatesPath();
|
|
94
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
95
|
+
const file = { version: CURRENT_FILE_VERSION, templates };
|
|
96
|
+
writeFileSync(filePath, JSON.stringify(file, null, 2), 'utf-8');
|
|
97
|
+
}
|
|
98
|
+
function seedTemplates() {
|
|
99
|
+
const now = new Date().toISOString();
|
|
100
|
+
const seed = [
|
|
101
|
+
{
|
|
102
|
+
slug: 'review-quality',
|
|
103
|
+
description: 'Code quality review',
|
|
104
|
+
content: 'Review the recently modified code in {working_branch} for:\n- Logic bugs\n- Missing error handling\n- Style issues\n\nReport only high-confidence findings.',
|
|
105
|
+
createdAt: now,
|
|
106
|
+
updatedAt: now,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
slug: 'add-tests',
|
|
110
|
+
description: 'Add unit tests following existing patterns',
|
|
111
|
+
content: 'Add unit tests for the recently modified code. Follow the existing test patterns in this project. Focus on:\n- Happy paths\n- Edge cases\n- Error handling',
|
|
112
|
+
createdAt: now,
|
|
113
|
+
updatedAt: now,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
slug: 'explain',
|
|
117
|
+
description: 'Explain the recent changes',
|
|
118
|
+
content: 'Explain what the recently modified code does in {working_branch}, focusing on the non-obvious parts.',
|
|
119
|
+
createdAt: now,
|
|
120
|
+
updatedAt: now,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
slug: 'refactor',
|
|
124
|
+
description: 'Safe refactoring',
|
|
125
|
+
content: 'Refactor the selected code to improve readability without changing its behavior. Explain your reasoning as you go.',
|
|
126
|
+
createdAt: now,
|
|
127
|
+
updatedAt: now,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
slug: 'plan-tasks',
|
|
131
|
+
description: 'Break work into kobo tasks',
|
|
132
|
+
content: 'Break down the work for this workspace ({workspace_name}) into concrete tasks. Use the kobo-tasks MCP tool `create_task` to register each one with a short, actionable title. Start with a high-level analysis of what needs to happen.',
|
|
133
|
+
createdAt: now,
|
|
134
|
+
updatedAt: now,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
slug: 'show-tasks',
|
|
138
|
+
description: 'List current kobo tasks',
|
|
139
|
+
content: 'List the current tasks for this workspace using the kobo-tasks MCP tool `list_tasks`. Show their status and highlight what is still pending.',
|
|
140
|
+
createdAt: now,
|
|
141
|
+
updatedAt: now,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
slug: 'mark-done',
|
|
145
|
+
description: 'Mark completed kobo tasks',
|
|
146
|
+
content: 'Review the work completed so far. Identify which tasks from the kobo-tasks list are now done, and mark them using the `mark_task_done` MCP tool.',
|
|
147
|
+
createdAt: now,
|
|
148
|
+
updatedAt: now,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
slug: 'sync-tasks',
|
|
152
|
+
description: 'Sync kobo tasks with the codebase',
|
|
153
|
+
content: 'Compare the current state of the codebase against the kobo-tasks list. Create missing tasks with `create_task`, mark completed ones with `mark_task_done`, and delete stale ones with `delete_task`. Explain each change before making it.',
|
|
154
|
+
createdAt: now,
|
|
155
|
+
updatedAt: now,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
slug: 'pr-review-comments',
|
|
159
|
+
description: 'List PR review comments requesting changes',
|
|
160
|
+
content: 'Check if a pull request exists for branch {working_branch}.\n\nIf a PR exists (PR {pr_url}):\n1. Use the GitHub MCP tools to fetch the PR reviews and comments\n2. Filter for reviews with status "CHANGES_REQUESTED"\n3. List each review comment with:\n - The reviewer name\n - The file and line referenced\n - The comment body\n - Whether it has been resolved\n4. Summarize the outstanding requested changes that still need to be addressed\n\nIf no PR exists, say so and suggest pushing the branch first.',
|
|
161
|
+
createdAt: now,
|
|
162
|
+
updatedAt: now,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
slug: 'ci-status',
|
|
166
|
+
description: 'Check GitHub Actions status on PR',
|
|
167
|
+
content: 'Check the CI/CD status for the pull request on branch {working_branch}.\n\nIf a PR exists (PR {pr_url}):\n1. Use the GitHub MCP tools to list the check runs / status checks on the latest commit of the PR\n2. For each check, report:\n - Check name\n - Status (queued, in_progress, completed)\n - Conclusion (success, failure, neutral, skipped, etc.)\n - Duration if available\n3. If any checks failed, fetch the logs or annotations and summarize what went wrong\n4. Give an overall summary: all green, some failing, or still running\n\nIf no PR exists, say so and suggest creating one first.',
|
|
168
|
+
createdAt: now,
|
|
169
|
+
updatedAt: now,
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
writeTemplates(seed);
|
|
173
|
+
}
|
|
@@ -48,9 +48,9 @@ export function createWorkspace(data) {
|
|
|
48
48
|
const now = new Date().toISOString();
|
|
49
49
|
const id = nanoid();
|
|
50
50
|
db.prepare(`
|
|
51
|
-
INSERT INTO workspaces (id, name, project_path, source_branch, working_branch, status, notion_url, notion_page_id, model, created_at, updated_at)
|
|
52
|
-
VALUES (?, ?, ?, ?, ?, 'created', ?, ?, ?, ?, ?)
|
|
53
|
-
`).run(id, data.name, data.projectPath, data.sourceBranch, data.workingBranch, data.notionUrl ?? null, data.notionPageId ?? null, data.model ?? 'claude-opus-4-6', now, now);
|
|
51
|
+
INSERT INTO workspaces (id, name, project_path, source_branch, working_branch, status, notion_url, notion_page_id, model, permission_mode, created_at, updated_at)
|
|
52
|
+
VALUES (?, ?, ?, ?, ?, 'created', ?, ?, ?, ?, ?, ?)
|
|
53
|
+
`).run(id, data.name, data.projectPath, data.sourceBranch, data.workingBranch, data.notionUrl ?? null, data.notionPageId ?? null, data.model ?? 'claude-opus-4-6', data.permissionMode ?? 'auto-accept', now, now);
|
|
54
54
|
return getWorkspace(id);
|
|
55
55
|
}
|
|
56
56
|
/** Fetch a single workspace by ID, or null if not found. */
|
|
@@ -248,6 +248,7 @@ function mapSession(row) {
|
|
|
248
248
|
status: row.status,
|
|
249
249
|
startedAt: row.started_at,
|
|
250
250
|
endedAt: row.ended_at,
|
|
251
|
+
name: row.name,
|
|
251
252
|
};
|
|
252
253
|
}
|
|
253
254
|
/** List all agent sessions for a workspace, most recent first. */
|
|
@@ -266,3 +267,51 @@ export function getLatestSession(workspaceId) {
|
|
|
266
267
|
.get(workspaceId);
|
|
267
268
|
return row ? mapSession(row) : null;
|
|
268
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* Return the "active" session for tagging events like push/pull/open-pr traces
|
|
272
|
+
* or resumed chat messages. Skips idle sessions (never started) and prefers a
|
|
273
|
+
* running session, falling back to the most recent session that has actually
|
|
274
|
+
* been started (any non-idle status).
|
|
275
|
+
*/
|
|
276
|
+
export function getActiveSession(workspaceId) {
|
|
277
|
+
const db = getDb();
|
|
278
|
+
// Prefer a running session first (the agent the user is actively interacting with)
|
|
279
|
+
const running = db
|
|
280
|
+
.prepare("SELECT * FROM agent_sessions WHERE workspace_id = ? AND status = 'running' ORDER BY started_at DESC LIMIT 1")
|
|
281
|
+
.get(workspaceId);
|
|
282
|
+
if (running)
|
|
283
|
+
return mapSession(running);
|
|
284
|
+
// Otherwise the most recent non-idle session (completed, error, quota, etc.)
|
|
285
|
+
const latestNonIdle = db
|
|
286
|
+
.prepare("SELECT * FROM agent_sessions WHERE workspace_id = ? AND status != 'idle' ORDER BY started_at DESC LIMIT 1")
|
|
287
|
+
.get(workspaceId);
|
|
288
|
+
return latestNonIdle ? mapSession(latestNonIdle) : null;
|
|
289
|
+
}
|
|
290
|
+
/** Create an idle agent session (no Claude process yet) for a workspace. */
|
|
291
|
+
export function createIdleSession(workspaceId) {
|
|
292
|
+
const db = getDb();
|
|
293
|
+
const id = nanoid();
|
|
294
|
+
const now = new Date().toISOString();
|
|
295
|
+
db.prepare('INSERT INTO agent_sessions (id, workspace_id, pid, status, started_at) VALUES (?, ?, NULL, ?, ?)').run(id, workspaceId, 'idle', now);
|
|
296
|
+
return {
|
|
297
|
+
id,
|
|
298
|
+
workspaceId,
|
|
299
|
+
pid: null,
|
|
300
|
+
claudeSessionId: null,
|
|
301
|
+
status: 'idle',
|
|
302
|
+
startedAt: now,
|
|
303
|
+
endedAt: null,
|
|
304
|
+
name: null,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
/** Rename an agent session. Returns the updated session or null if not found. */
|
|
308
|
+
export function renameSession(sessionId, workspaceId, name) {
|
|
309
|
+
const db = getDb();
|
|
310
|
+
const result = db
|
|
311
|
+
.prepare('UPDATE agent_sessions SET name = ? WHERE id = ? AND workspace_id = ?')
|
|
312
|
+
.run(name, sessionId, workspaceId);
|
|
313
|
+
if (result.changes === 0)
|
|
314
|
+
return null;
|
|
315
|
+
const row = db.prepare('SELECT * FROM agent_sessions WHERE id = ?').get(sessionId);
|
|
316
|
+
return row ? mapSession(row) : null;
|
|
317
|
+
}
|
|
@@ -109,6 +109,16 @@ export function pushBranch(repoPath, branchName, remote = 'origin') {
|
|
|
109
109
|
throw new Error(`Failed to push branch '${branchName}' to '${remote}': ${message}`);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
+
/** Pull the current branch from the remote using fast-forward only. */
|
|
113
|
+
export function pullBranch(repoPath, branchName, remote = 'origin') {
|
|
114
|
+
try {
|
|
115
|
+
git(repoPath, ['pull', '--ff-only', remote, branchName]);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
119
|
+
throw new Error(`Failed to pull branch '${branchName}' from '${remote}': ${message}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
112
122
|
/** Rebase the current branch onto the given base branch. Fetches origin first to get latest changes. */
|
|
113
123
|
export function rebaseBranch(repoPath, baseBranch) {
|
|
114
124
|
try {
|
|
@@ -69,6 +69,13 @@ export function getSettingsPath() {
|
|
|
69
69
|
export function getSkillsPath() {
|
|
70
70
|
return path.join(getKoboHome(), 'skills.json');
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Absolute path to templates.json under the Kōbō home — user's personal
|
|
74
|
+
* prompt templates library, editable via Settings > Templates.
|
|
75
|
+
*/
|
|
76
|
+
export function getTemplatesPath() {
|
|
77
|
+
return path.join(getKoboHome(), 'templates.json');
|
|
78
|
+
}
|
|
72
79
|
/**
|
|
73
80
|
* Absolute path to the compiled MCP server entry (shipped in the published
|
|
74
81
|
* package as dist/mcp-server/kobo-tasks-server.js). Returns null if not
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.11",
|
|
4
4
|
"description": "Kōbō — multi-workspace agent manager for Claude Code. Orchestrates isolated git worktrees with dev servers, Notion integration, and MCP tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.activity-feed[data-v-faa59666]{flex-direction:column;gap:4px;display:flex;position:relative;overflow:hidden auto}.af-item[data-v-faa59666]{word-break:break-word;overflow-wrap:break-word;flex-shrink:0;padding:6px 10px;overflow-x:hidden}.af-time[data-v-faa59666]{color:#555;flex-shrink:0;font-size:10px}.af-tool-label[data-v-faa59666]{font-family:Roboto Mono,monospace;font-size:11px}.af-tool-desc[data-v-faa59666]{text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.af-ask-option-item[data-v-faa59666]{padding:2px 0}.af-tool-args[data-v-faa59666]{background-color:#ffffff0a;padding:6px 8px;overflow-x:auto}.af-args-pre[data-v-faa59666]{color:#888;white-space:pre-wrap;word-break:break-word;margin:0;font-family:Roboto Mono,monospace;font-size:10px}.af-file-change[data-v-faa59666]{background:#ffffff08;border:1px solid #ffffff0f;border-radius:6px;padding:6px 8px}.af-file-header[data-v-faa59666]{font-family:Roboto Mono,monospace;font-size:11px}.af-lang-badge[data-v-faa59666]{text-align:center;background:#ffffff0f;border-radius:3px;min-width:20px;padding:1px 4px;font-family:Roboto Mono,monospace;font-size:9px;font-weight:700}.af-file-path[data-v-faa59666]{max-width:70%;font-size:11px}.af-diff-stats[data-v-faa59666]{white-space:nowrap;font-family:Roboto Mono,monospace;font-size:10px}.af-diff-body[data-v-faa59666]{background:#0003;border-radius:4px;max-height:300px;padding:4px 0;overflow:auto}.af-diff-line[data-v-faa59666]{white-space:pre;min-width:-moz-fit-content;min-width:fit-content;padding:0 8px;font-family:Roboto Mono,monospace;font-size:10px;line-height:1.5}.af-diff-sign[data-v-faa59666]{-webkit-user-select:none;user-select:none;width:12px;display:inline-block}.af-diff-del[data-v-faa59666]{color:#f85149;background:#f851491a}.af-diff-add[data-v-faa59666]{color:#3fb950;background:#3fb9501a}.af-item--text[data-v-faa59666]{background-color:#1a2a3a;border-left:3px solid #3b82f6}.af-item--user[data-v-faa59666]{background-color:#1a2a1a;border-left:3px solid #22c55e}.af-item--prompt[data-v-faa59666]{background-color:#1a1a2e;border-left:3px solid #6c63ff}.af-text-content[data-v-faa59666]{color:#d0d0d0;word-break:break-word;line-height:1.5}.af-markdown[data-v-faa59666] p{margin:0 0 8px}.af-markdown[data-v-faa59666] p:last-child{margin-bottom:0}.af-markdown[data-v-faa59666] h1,.af-markdown[data-v-faa59666] h2,.af-markdown[data-v-faa59666] h3{color:#e0e0e0;margin:12px 0 6px}.af-markdown[data-v-faa59666] h1{font-size:16px}.af-markdown[data-v-faa59666] h2{font-size:14px}.af-markdown[data-v-faa59666] h3{font-size:13px}.af-markdown[data-v-faa59666] ul,.af-markdown[data-v-faa59666] ol{margin:4px 0;padding-left:20px}.af-markdown[data-v-faa59666] li{margin:2px 0}.af-markdown[data-v-faa59666] code{background-color:#ffffff14;border-radius:3px;padding:1px 4px;font-family:Roboto Mono,monospace;font-size:11px}.af-markdown[data-v-faa59666] pre{background-color:#0000004d;border-radius:4px;margin:6px 0;padding:8px;overflow-x:auto}.af-markdown[data-v-faa59666] pre code{background:0 0;padding:0}.af-markdown[data-v-faa59666] strong{color:#fff}.af-markdown[data-v-faa59666] a{color:#6c63ff}.af-markdown[data-v-faa59666] blockquote{color:#aaa;border-left:3px solid #6c63ff;margin:6px 0;padding:4px 12px}.af-markdown[data-v-faa59666] table{border-collapse:collapse;margin:6px 0;font-size:11px}.af-markdown[data-v-faa59666] table th,.af-markdown[data-v-faa59666] table td{border:1px solid #2a2a4a;padding:4px 8px}.af-markdown[data-v-faa59666] table th{background-color:#ffffff0d}.af-item--system[data-v-faa59666]{background-color:#2a2a1a;border-left:3px solid #f59e0b}.af-system-details[data-v-faa59666]{background-color:#ffffff0a;padding:6px 8px;overflow-x:auto}.af-system-content[data-v-faa59666]{white-space:pre-wrap}.af-item--error[data-v-faa59666]{background-color:#2a1a1a;border-left:3px solid #ef4444}.af-item--raw[data-v-faa59666]{white-space:pre-wrap;font-family:Roboto Mono,monospace}.scroll-buttons[data-v-faa59666]{align-self:flex-end;gap:6px;margin-right:8px;display:flex;position:sticky;bottom:8px}.scroll-btn[data-v-faa59666]{opacity:.7;transition:opacity .15s}.scroll-btn[data-v-faa59666]:hover{opacity:1}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import{E as e,F as t,H as n,L as r,M as i,N as a,Q as o,U as s,_ as c,_t as l,a as u,d,f,g as p,m,n as ee,p as h,rt as te,u as g,y as ne,yt as _}from"./vue-i18n-nv59vAyH.js";import{L as v,T as y,bt as re}from"./scroll-CWjBCoBR.js";import{E as ie,O as ae}from"./index-BIsYlO92.js";import{t as b}from"./QSpinnerDots-FJCnAvfw.js";import{n as x,p as S,t as C}from"./_plugin-vue_export-helper-fkfRoKj2.js";import{n as oe,t as se}from"./marked.esm-BjjOHIBz.js";var ce={key:0,class:`af-empty column items-center justify-center text-center q-pa-xl`},le={class:`text-grey-6 q-mt-md text-body2`},ue={class:`text-grey-8 text-caption q-mt-xs`},de={key:1,class:`row justify-center q-my-sm`},fe=[`data-item-id`],pe={class:`af-tool row items-center q-gutter-xs`},me={class:`af-tool-label text-indigo-4`},he={class:`af-time`},ge={key:0,class:`text-grey-4 q-mb-xs`},_e={class:`af-ask-options-list q-mb-sm`},ve={class:`text-weight-bold text-grey-3`},ye={key:0},be={key:1,class:`af-ask-buttons q-gutter-xs`},xe={key:0,class:`q-mt-sm row justify-end`},Se=[`onClick`],Ce={class:`af-file-header row items-center no-wrap q-gutter-xs`},we={class:`af-file-path text-grey-4 ellipsis`},Te={class:`af-diff-stats`},Ee={key:0,class:`text-green-5`},De={key:1,class:`text-red-5 q-ml-xs`},Oe={class:`af-time`},ke={key:1,class:`af-diff-line af-diff-del`},w={key:0,class:`af-diff-line text-grey-7 text-italic`},Ae=[`onClick`],je={class:`af-tool-label text-grey-7`},Me={key:0,class:`af-tool-desc text-grey-8`},Ne={class:`af-time`},Pe={key:0,class:`af-tool-args q-mt-xs rounded-borders`},Fe={class:`af-args-pre`},Ie={class:`af-text-header row items-center q-mb-xs`},Le={class:`af-time`},Re=[`innerHTML`],ze=[`onClick`],Be={class:`af-system-content text-caption text-amber-6`},Ve={class:`af-time`},He={key:0,class:`af-system-details q-mt-xs rounded-borders`},Ue={class:`af-args-pre`},We={key:5,class:`row items-center`},Ge={class:`af-error-content text-red-5`},Ke={class:`af-time`},qe={key:6,class:`row items-center`},Je={class:`af-raw-content text-grey-7`},Ye={class:`af-time`},Xe={class:`scroll-buttons`},T=50,Ze=50,E=C(ne({__name:`ActivityFeed`,setup(ne){function C(e){let t=se.parse(e,{async:!1,breaks:!0,gfm:!0});return oe.sanitize(t)}let{t:E}=ee(),D=ae(),Qe=ie(),O=o(null),k=o(!1),A=o(new Set),j=new Map;function M(e,t){return j.has(e)||j.set(e,ft(t)),j.get(e)}let N=new Map;function $e(e,t){return N.has(e)||N.set(e,C(t)),N.get(e)}let P=g(()=>{let e=D.activityFeed;for(let t=e.length-1;t>=0;t--){let n=e[t];if(n.meta?.sender===`user`)return null;if(n.type===`tool_use`&&n.content===`AskUserQuestion`)return n.id}return null}),F=o(new Map);function et(e,t,n){F.value.has(e)||F.value.set(e,new Map);let r=F.value.get(e);r.get(t)===n?r.delete(t):r.set(t,n)}function I(e,t,n){return F.value.get(e)?.get(t)===n}function tt(e){let t=F.value.get(e);return!!t&&t.size>0}function nt(e,t){let n=D.selectedWorkspaceId;if(!n)return;let r=F.value.get(e);if(!r||r.size===0)return;let i=[];for(let[e,n]of r.entries()){let r=t[e];if(!r)continue;let a=r.options[n];a&&i.push(`${r.question}: ${a.label}`)}i.length>0&&Qe.sendChatMessage(n,i.join(`
|
|
2
|
+
`))}let L=o(-1);function rt(){let t=D.activityFeed,n=t.map((e,t)=>({item:e,idx:t})).filter(({item:e})=>e.meta?.sender===`user`);if(n.length===0)return;L.value<=0?L.value=n.length-1:L.value--;let r=n[L.value].idx,i=n[L.value].item.id,a=t.length-r;a>R.value&&(R.value=Math.min(a+10,t.length)),e(()=>{let e=O.value?.querySelector(`[data-item-id="${i}"]`);e&&e.scrollIntoView({behavior:`smooth`,block:`center`})})}n(()=>D.selectedWorkspaceId,()=>{L.value=-1,j.clear(),F.value.clear(),N.clear(),V=0,k.value=!1});let R=o(T),z=g(()=>{let e=D.activityFeed;return e.length<=R.value?e:e.slice(-R.value)});n(()=>D.selectedWorkspaceId,()=>{R.value=T}),n(z,e=>{let t=new Set(e.map(e=>e.id));for(let e of j.keys())t.has(e)||j.delete(e);for(let e of F.value.keys())t.has(e)||F.value.delete(e);for(let e of N.keys())t.has(e)||N.delete(e)},{flush:`post`});let B=o(!1),V=0;function H(){let t=O.value;if(t&&(t.scrollHeight-t.scrollTop-t.clientHeight<50?B.value=!1:t.scrollTop<V&&(B.value=!0),V=t.scrollTop,t.scrollTop<200&&!k.value)){let n=D.activityFeed.length;if(R.value<n){k.value=!0;let r=t.scrollHeight;R.value=Math.min(R.value+Ze,n),e(()=>{O.value&&(O.value.scrollTop+=O.value.scrollHeight-r),k.value=!1})}else if(D.selectedWorkspaceId&&D.hasMoreEvents[D.selectedWorkspaceId]!==!1){k.value=!0;let n=t.scrollHeight;D.fetchOlderEvents(D.selectedWorkspaceId).then(t=>{t?(R.value=D.activityFeed.length,e(()=>{O.value&&(O.value.scrollTop+=O.value.scrollHeight-n),k.value=!1})):k.value=!1})}}}function U(){if(B.value)return;let e=O.value;e&&(e.scrollTop=e.scrollHeight)}function it(){B.value=!1,R.value=T,e(()=>{let e=O.value;e&&(e.scrollTop=e.scrollHeight)})}n(()=>D.activityFeed.length,()=>{e(U)}),i(()=>{O.value?.addEventListener(`scroll`,H),e(U)}),a(()=>{O.value?.removeEventListener(`scroll`,H)});function W(e){return new Date(e).toLocaleTimeString(void 0,{hour:`2-digit`,minute:`2-digit`,second:`2-digit`})}function G(e){if(e.type!==`tool_use`)return null;let t=e.meta?.input;if(e.content===`Edit`){if(!t?.file_path)return null;let e=t.file_path,n=t.old_string??``,r=t.new_string??``,i=n.split(`
|
|
3
|
+
`),a=r.split(`
|
|
4
|
+
`);return{toolName:`Edit`,filePath:e,oldString:n,newString:r,replaceAll:t.replace_all??!1,additions:a.length,deletions:i.length}}if(e.content===`Write`){if(!t?.file_path)return null;let e=t.file_path,n=t.content??``;return{toolName:`Write`,filePath:e,content:n,additions:n.split(`
|
|
5
|
+
`).length,deletions:0}}if(e.content===`Bash`){let e=(t?.command??``).match(/^\s*rm\s+(?:-[a-zA-Z]*\s+)*(.+)/);if(e)return{toolName:`Bash:rm`,filePath:e[1].trim().replace(/["']/g,``),additions:0,deletions:1}}return null}function K(e){let t=D.selectedWorkspace;if(t){let n=`${t.projectPath}/.worktrees/${t.workingBranch}/`;if(e.startsWith(n))return e.slice(n.length);if(e.startsWith(`${t.projectPath}/`))return e.slice(t.projectPath.length+1)}return e.startsWith(`/`)&&e.split(`/`).length>4?`…/${e.split(`/`).slice(-3).join(`/`)}`:e}function at(e){return e.split(`/`).pop()??e}function q(e){let t=at(e),n=t.lastIndexOf(`.`);return n>=0?t.substring(n+1):``}function ot(e){return{ts:`JS`,tsx:`TS`,js:`JS`,jsx:`JS`,vue:`VU`,py:`PY`,rs:`RS`,go:`GO`,java:`JA`,php:`PH`,css:`CS`,scss:`SC`,html:`HT`,md:`MD`,json:`JS`,sql:`SQ`,sh:`SH`,yaml:`YA`,yml:`YA`,toml:`TM`}[e.toLowerCase()]??e.substring(0,2).toUpperCase()}function st(e){return{ts:`blue-5`,tsx:`blue-5`,js:`yellow-8`,jsx:`yellow-8`,vue:`green-5`,py:`blue-4`,rs:`orange-5`,go:`cyan-5`,java:`red-5`,php:`indigo-4`,css:`purple-4`,scss:`pink-4`,html:`orange-4`,md:`grey-5`,json:`yellow-6`}[e.toLowerCase()]??`grey-5`}function ct(e){let t=e.toLowerCase();return t.includes(`read`)||t.includes(`grep`)||t.includes(`glob`)?`search`:t.includes(`write`)||t.includes(`edit`)?`edit`:t.includes(`bash`)||t.includes(`terminal`)?`terminal`:t.includes(`agent`)||t.includes(`task`)?`smart_toy`:`build`}function lt(e){switch(e.type){case`text`:return e.meta?.sender===`system-prompt`?`af-item--prompt`:e.meta?.sender===`user`?`af-item--user`:`af-item--text`;case`system`:return`af-item--system`;case`error`:return`af-item--error`;case`tool_use`:return`af-item--tool`;case`raw`:return`af-item--raw`;default:return``}}function J(e){switch(e.meta?.sender){case`system-prompt`:return E(`activityFeed.initialPrompt`);case`user`:return E(`activityFeed.you`);default:return E(`activityFeed.agent`)}}function ut(e){switch(e.meta?.sender){case`system-prompt`:return`text-indigo-4`;case`user`:return`text-green-4`;default:return`text-blue-4`}}function dt(e){if(e.content===`Skill`&&e.meta){let t=e.meta.input;if(t&&typeof t.skill==`string`)return`Skill — ${t.skill}`}return e.content}function Y(e){if(!e.meta)return``;let t=e.meta.input;if(!t)return``;if(typeof t.description==`string`)return t.description;if(typeof t.file_path==`string`)return K(t.file_path);if(typeof t.pattern==`string`)return`${typeof t.path==`string`?`${K(t.path)}/`:``}${t.pattern}`;if(typeof t.path==`string`)return K(t.path);if(typeof t.command==`string`){let e=t.command;return e.length>80?`${e.slice(0,80)}…`:e}return``}function ft(e){if(e.type!==`tool_use`||e.content!==`AskUserQuestion`)return null;let t=e.meta?.input;return!t?.questions||!Array.isArray(t.questions)?null:t.questions.filter(e=>Array.isArray(e.options)&&e.options.length>0).map(e=>({question:e.question??``,options:e.options.map(e=>({label:e.label??``,description:e.description??``}))}))}function X(e){if(!e.meta)return!1;let t=e.meta;return t.input!==void 0&&t.input!==null}function Z(e){A.value.has(e)?A.value.delete(e):A.value.add(e)}function Q(e){return A.value.has(e)}function pt(e){if(!e.meta)return``;let t=e.meta;if(!t.input)return``;try{return JSON.stringify(t.input,null,2)}catch{return String(t.input)}}let mt=g(()=>D.activityFeed.some(e=>e.meta?.sender===`user`));function $(e){return e.type!==`system`||!e.meta?!1:Object.keys(e.meta).length>0}function ht(e){if(!e.meta)return``;try{return JSON.stringify(e.meta,null,2)}catch{return``}}return(e,n)=>(t(),m(`div`,{ref_key:`feedContainer`,ref:O,class:`activity-feed q-pa-sm`},[te(D).activityFeed.length===0?(t(),m(`div`,ce,[c(v,{name:`forum`,size:`48px`,color:`grey-8`}),d(`div`,le,_(e.$t(`activityFeed.empty`)),1),d(`div`,ue,_(e.$t(`activityFeed.emptyHint`)),1)])):h(``,!0),k.value?(t(),m(`div`,de,[c(b,{size:`24px`,color:`grey-6`})])):h(``,!0),(t(!0),m(u,null,r(z.value,i=>(t(),m(`div`,{key:i.id,"data-item-id":i.id,class:l([`af-item text-caption rounded-borders`,lt(i)])},[i.type===`tool_use`&&M(i.id,i)?(t(),m(u,{key:0},[d(`div`,pe,[c(v,{name:`help_outline`,size:`14px`,color:`indigo-4`}),d(`span`,me,_(e.$t(`activityFeed.question`)),1),c(S),d(`span`,he,_(W(i.timestamp)),1)]),(t(!0),m(u,null,r(M(i.id,i),(e,n)=>(t(),m(`div`,{key:n,class:`q-mt-sm`},[e.question?(t(),m(`div`,ge,_(e.question),1)):h(``,!0),d(`div`,_e,[(t(!0),m(u,null,r(e.options,(e,n)=>(t(),m(`div`,{key:n,class:`af-ask-option-item text-caption text-grey-5`},[d(`span`,ve,_(n+1)+`. `+_(e.label),1),e.description?(t(),m(`span`,ye,` — `+_(e.description),1)):h(``,!0)]))),128))]),i.id===P.value?(t(),m(`div`,be,[(t(!0),m(u,null,r(e.options,(e,r)=>(t(),f(y,{key:e.label,"no-caps":``,dense:``,outline:!I(i.id,n,r),unelevated:I(i.id,n,r),color:I(i.id,n,r)?`indigo-6`:`indigo-4`,"text-color":I(i.id,n,r)?`white`:void 0,class:`af-option-btn`,onClick:e=>et(i.id,n,r)},{default:s(()=>[p(_(e.label),1)]),_:2},1032,[`outline`,`unelevated`,`color`,`text-color`,`onClick`]))),128))])):h(``,!0)]))),128)),i.id===P.value?(t(),m(`div`,xe,[c(y,{"no-caps":``,unelevated:``,dense:``,color:`indigo-6`,label:e.$t(`activityFeed.sendAnswers`),icon:`send`,disable:!tt(i.id),onClick:e=>nt(i.id,M(i.id,i))},null,8,[`label`,`disable`,`onClick`])])):h(``,!0)],64)):i.type===`tool_use`&&G(i)?(t(),m(`div`,{key:1,class:`af-file-change cursor-pointer`,onClick:e=>Z(i.id)},[d(`div`,Ce,[d(`span`,{class:l([`af-lang-badge`,`text-${st(q(G(i).filePath))}`])},_(ot(q(G(i).filePath))),3),d(`span`,we,_(K(G(i).filePath)),1),d(`span`,Te,[G(i).additions?(t(),m(`span`,Ee,`+`+_(G(i).additions),1)):h(``,!0),G(i).deletions?(t(),m(`span`,De,`-`+_(G(i).deletions),1)):h(``,!0)]),c(v,{name:Q(i.id)?`expand_less`:`expand_more`,size:`14px`,color:`grey-6`},null,8,[`name`]),c(S),d(`span`,Oe,_(W(i.timestamp)),1)]),Q(i.id)?(t(),m(`div`,{key:0,class:`af-diff-body q-mt-xs`,onClick:n[0]||=re(()=>{},[`stop`])},[G(i).toolName===`Edit`?(t(),m(u,{key:0},[(t(!0),m(u,null,r((G(i).oldString??``).split(`
|
|
6
|
+
`),(e,r)=>(t(),m(`div`,{key:`del-${r}`,class:`af-diff-line af-diff-del`},[n[1]||=d(`span`,{class:`af-diff-sign`},`-`,-1),p(_(e),1)]))),128)),(t(!0),m(u,null,r((G(i).newString??``).split(`
|
|
7
|
+
`),(e,r)=>(t(),m(`div`,{key:`add-${r}`,class:`af-diff-line af-diff-add`},[n[2]||=d(`span`,{class:`af-diff-sign`},`+`,-1),p(_(e),1)]))),128))],64)):G(i).toolName===`Bash:rm`?(t(),m(`div`,ke,[...n[3]||=[d(`span`,{class:`af-diff-sign`},`-`,-1),p(`File deleted`,-1)]])):(t(),m(u,{key:2},[(t(!0),m(u,null,r((G(i).content??``).split(`
|
|
8
|
+
`).slice(0,30),(e,r)=>(t(),m(`div`,{key:`w-${r}`,class:`af-diff-line af-diff-add`},[n[4]||=d(`span`,{class:`af-diff-sign`},`+`,-1),p(_(e),1)]))),128)),(G(i).content??``).split(`
|
|
9
|
+
`).length>30?(t(),m(`div`,w,`… `+_((G(i).content??``).split(`
|
|
10
|
+
`).length-30)+` more lines`,1)):h(``,!0)],64))])):h(``,!0)],8,Se)):i.type===`tool_use`?(t(),m(u,{key:2},[d(`div`,{class:l([`af-tool row items-center q-gutter-xs`,{"cursor-pointer":X(i)}]),onClick:e=>X(i)&&Z(i.id)},[c(v,{name:ct(i.content),size:`14px`,color:`grey-6`},null,8,[`name`]),d(`span`,je,_(dt(i)),1),Y(i)?(t(),m(`span`,Me,`— `+_(Y(i)),1)):h(``,!0),X(i)?(t(),f(v,{key:1,name:Q(i.id)?`expand_less`:`expand_more`,size:`14px`,color:`grey-7`},null,8,[`name`])):h(``,!0),c(S),d(`span`,Ne,_(W(i.timestamp)),1)],10,Ae),Q(i.id)?(t(),m(`div`,Pe,[d(`pre`,Fe,_(pt(i)),1)])):h(``,!0)],64)):i.type===`text`?(t(),m(u,{key:3},[d(`div`,Ie,[d(`span`,{class:l([`text-caption text-weight-bold`,ut(i)])},_(J(i)),3),i.meta?.pending?(t(),f(b,{key:0,size:`14px`,color:`grey-5`,class:`q-ml-sm`})):h(``,!0),c(S),d(`span`,Le,_(W(i.timestamp)),1)]),d(`div`,{class:`af-text-content af-markdown`,innerHTML:$e(i.id,i.content)},null,8,Re)],64)):i.type===`system`?(t(),m(u,{key:4},[d(`div`,{class:l([`row items-center`,{"cursor-pointer":$(i)}]),onClick:e=>$(i)&&Z(i.id)},[c(v,{name:`info`,size:`14px`,color:`amber-6`,class:`q-mr-xs`}),d(`span`,Be,_(i.content),1),$(i)?(t(),f(v,{key:0,name:Q(i.id)?`expand_less`:`expand_more`,size:`14px`,color:`amber-8`,class:`q-ml-xs`},null,8,[`name`])):h(``,!0),c(S),d(`span`,Ve,_(W(i.timestamp)),1)],10,ze),Q(i.id)&&$(i)?(t(),m(`div`,He,[d(`pre`,Ue,_(ht(i)),1)])):h(``,!0)],64)):i.type===`error`?(t(),m(`div`,We,[c(v,{name:`error`,size:`14px`,color:`red-5`,class:`q-mr-xs`}),d(`span`,Ge,_(i.content),1),c(S),d(`span`,Ke,_(W(i.timestamp)),1)])):(t(),m(`div`,qe,[d(`span`,Je,_(i.content),1),c(S),d(`span`,Ye,_(W(i.timestamp)),1)]))],10,fe))),128)),d(`div`,Xe,[mt.value?(t(),f(y,{key:0,round:``,dense:``,size:`sm`,icon:`person_search`,color:`indigo-8`,class:`scroll-btn`,onClick:rt},{default:s(()=>[c(x,null,{default:s(()=>[p(_(e.$t(`activityFeed.goToPrevious`)),1)]),_:1})]),_:1})):h(``,!0),B.value?(t(),f(y,{key:1,round:``,dense:``,size:`sm`,icon:`keyboard_double_arrow_down`,color:`indigo-8`,class:`scroll-btn`,onClick:it},{default:s(()=>[c(x,null,{default:s(()=>[p(_(e.$t(`activityFeed.scrollToBottom`)),1)]),_:1})]),_:1})):h(``,!0)])],512))}}),[[`__scopeId`,`data-v-faa59666`]]);export{E as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{J as e,d as t,f as n,lt as r}from"./scroll-CWjBCoBR.js";function i(e){if(e===!1)return 0;if(e===!0||e===void 0)return 1;let t=parseInt(e,10);return isNaN(t)?0:t}var a=r({name:`close-popup`,beforeMount(r,{value:a}){let o={depth:i(a),handler(e){o.depth!==0&&setTimeout(()=>{let i=n(r);i!==void 0&&t(i,e,o.depth)})},handlerKey(t){e(t,13)===!0&&o.handler(t)}};r.__qclosepopup=o,r.addEventListener(`click`,o.handler),r.addEventListener(`keyup`,o.handlerKey)},updated(e,{value:t,oldValue:n}){t!==n&&(e.__qclosepopup.depth=i(t))},beforeUnmount(e){let t=e.__qclosepopup;e.removeEventListener(`click`,t.handler),e.removeEventListener(`keyup`,t.handlerKey),delete e.__qclosepopup}});export{a as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.create-page[data-v-9b31762d]{background-color:#1a1a2e;min-height:100%;padding:48px 24px}.create-inner[data-v-9b31762d]{width:100%;max-width:700px}.create-title[data-v-9b31762d]{font-size:24px;line-height:1.3}.create-card[data-v-9b31762d]{background:#224;border:1px solid #444;overflow:hidden}.card-top-bar[data-v-9b31762d]{background:#1e1e3a;min-height:36px}.card-name-wrap[data-v-9b31762d]{background:#224;padding:8px 16px 4px}.card-name-wrap[data-v-9b31762d] .q-field__control{height:32px;min-height:32px;padding:0}.card-name-wrap[data-v-9b31762d] input{color:#e0e0e0;font-size:15px;font-weight:500}.card-name-wrap[data-v-9b31762d] input::placeholder{color:#555}.card-textarea-wrap[data-v-9b31762d]{background:#224}.repo-select[data-v-9b31762d]{min-width:160px;max-width:260px}.repo-select[data-v-9b31762d] .q-field__prepend{align-items:center;height:auto;padding-top:0}.create-textarea[data-v-9b31762d]{color:#d0d0d0;width:100%;padding:12px 16px 4px}.create-textarea[data-v-9b31762d] .q-field__control{padding:0}.create-textarea[data-v-9b31762d] textarea{color:#d0d0d0;resize:none;min-height:100px;font-size:14px;line-height:1.6}.create-textarea[data-v-9b31762d] textarea::placeholder{color:#666}.notion-toggle-btn[data-v-9b31762d]{background:#333;padding:2px 10px}.notion-url-wrap[data-v-9b31762d]{background:#1e1e3a;padding:8px 0 0}.notion-url-input[data-v-9b31762d]{padding:0 12px}.notion-url-input[data-v-9b31762d] .q-field__control{height:36px;min-height:36px;padding:0}.notion-url-input[data-v-9b31762d] input{color:#d0d0d0;font-size:13px}.notion-url-input[data-v-9b31762d] input::placeholder{color:#555;font-size:12px}.notion-error[data-v-9b31762d],.notion-valid[data-v-9b31762d]{padding-bottom:6px}.slide-enter-active[data-v-9b31762d],.slide-leave-active[data-v-9b31762d]{transition:all .2s;overflow:hidden}.slide-enter-from[data-v-9b31762d],.slide-leave-to[data-v-9b31762d]{opacity:0;max-height:0}.slide-enter-to[data-v-9b31762d],.slide-leave-from[data-v-9b31762d]{opacity:1;max-height:120px}.card-bottom-bar[data-v-9b31762d]{background:#1e1e3a;min-height:40px}.bottom-select[data-v-9b31762d]{background:#333;min-width:60px;height:28px;padding:0 6px}.bottom-select[data-v-9b31762d] .q-field__control{height:28px;min-height:28px;padding:0}.bottom-select[data-v-9b31762d] .q-field__native{min-height:unset;padding:0}.bottom-select-label[data-v-9b31762d]{color:#bbb;gap:2px;font-size:11px}.bottom-sep[data-v-9b31762d]{color:#555;padding:0 2px;font-size:12px;line-height:1}.repo-path-wrap[data-v-9b31762d]{background:#333;border-radius:6px;height:28px;padding:0 8px}.repo-input[data-v-9b31762d]{min-width:140px}.repo-input[data-v-9b31762d] .q-field__control{height:28px;min-height:28px;padding:0}.repo-input[data-v-9b31762d] input{color:#bbb;font-size:11px}.repo-input[data-v-9b31762d] input::placeholder{color:#666;font-size:11px}.branch-select[data-v-9b31762d]{min-width:80px}.create-btn[data-v-9b31762d]{color:#fff;background:#4f46e5;height:28px;padding:0 14px;font-size:12px}.create-btn[data-v-9b31762d] .q-btn__content{height:28px}.create-hint[data-v-9b31762d]{line-height:1.5}.fade-enter-active[data-v-9b31762d],.fade-leave-active[data-v-9b31762d]{transition:opacity .2s}.fade-enter-from[data-v-9b31762d],.fade-leave-to[data-v-9b31762d]{opacity:0}.manual-hint[data-v-9b31762d]{background:#1e1e3a;line-height:1.4}.manual-expansion[data-v-9b31762d]{background:#1e1e3a;border:1px solid #333;border-radius:4px;margin-top:6px;overflow:hidden}.manual-expansion[data-v-9b31762d] .manual-expansion-header{min-height:32px;padding:4px 10px;font-size:12px}.manual-expansion[data-v-9b31762d] .q-expansion-item__content,.manual-section-body[data-v-9b31762d]{background:#1a1a2e}.manual-input[data-v-9b31762d] .q-field__control{height:26px;min-height:26px;padding:0}.manual-input[data-v-9b31762d] input{color:#e0e0e0;font-size:12px}.manual-input[data-v-9b31762d] input::placeholder{color:#555}.manual-item[data-v-9b31762d]{border-top:1px solid #ffffff0a}.manual-item[data-v-9b31762d]:first-child{border-top:none}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{F as e,H as t,L as n,M as r,N as i,Q as a,T as ee,U as o,_ as s,a as c,d as l,f as u,g as d,m as f,n as te,p,rt as ne,u as m,y as re,yt as h}from"./vue-i18n-nv59vAyH.js";import{L as g,T as _,bt as v,ht as ie,yt as y}from"./scroll-CWjBCoBR.js";import{i as b}from"./private.use-form-1cZLVjGN.js";import{t as ae}from"./settings-CuK-S6HH.js";import{E as oe,O as se,r as x,w as ce}from"./index-BIsYlO92.js";import{n as S,p as C,t as w}from"./_plugin-vue_export-helper-fkfRoKj2.js";import{a as le,i as T,n as E,r as ue}from"./QMenu-67GqYx0C.js";import{t as de}from"./QExpansionItem-D6zpEqBV.js";import{n as D,t as fe}from"./QPage-DE75SzRI.js";var pe={class:`create-inner`},me={class:`create-title text-center text-weight-bold q-mb-lg text-grey-3`},he={class:`create-card rounded-borders`},ge={class:`card-top-bar row items-center q-px-md q-py-xs`},_e={class:`model-badge cursor-default row items-center q-gutter-xs`},ve={class:`text-indigo-3 text-weight-medium text-caption`},ye={key:0,class:`notion-url-wrap`},be={key:0,class:`notion-error text-caption q-px-md q-pb-xs text-red-5`},xe={key:1,class:`notion-valid text-caption q-px-md q-pb-xs text-green-4`},Se={class:`card-name-wrap`},Ce={class:`card-textarea-wrap`},we={class:`manual-hint q-px-md q-py-sm text-caption text-grey-6`},Te={class:`q-pa-sm manual-section-body`},Ee={class:`row items-center q-gutter-sm q-mb-sm`},De={class:`col text-caption text-grey-4`},Oe={class:`q-pa-sm manual-section-body`},ke={class:`row items-center q-gutter-sm q-mb-sm`},Ae={class:`col text-caption text-grey-4`},je={class:`card-bottom-bar row items-center wrap q-px-sm q-py-xs q-gutter-xs`},Me={class:`bottom-select-label row items-center no-wrap`},Ne={class:`bottom-select-label row items-center no-wrap`},Pe={class:`bottom-select-label row items-center no-wrap`},Fe={class:`bottom-select-label row items-center no-wrap`},Ie={class:`create-hint text-center text-body2 q-mt-md text-grey-8`},O=w(re({__name:`CreatePage`,setup(re){let w=ce(),O=le(),k=se(),A=ae(),{t:j}=te(),M=a([]),N=a(``),P=a(``),F=a(``),I=a(!1),L=a(`auto`),R=a(``),z=a(null),B=a(!1),V=a(A.global.defaultPermissionMode||`plan`),H=a([]),U=a(!1),W=a(!1),Le=m(()=>[{label:j(`model.auto`),value:`auto`,description:`Claude picks the optimal model`},{label:j(`model.opus`),value:`claude-opus-4-6`,description:`Most powerful`},{label:j(`model.sonnet`),value:`claude-sonnet-4-6`,description:`Balanced`},{label:j(`model.haiku`),value:`claude-haiku-4-5-20251001`,description:`Fastest`}]),G=m(()=>F.value.trim().startsWith(`https://www.notion.so/`)),K=a([]),q=a([]),J=a(``),Y=a(``),X=m(()=>!I.value||!G.value);function Re(){let e=J.value.trim();e&&(K.value.push(e),J.value=``)}function ze(e){K.value.splice(e,1)}function Be(){let e=Y.value.trim();e&&(q.value.push(e),Y.value=``)}function Ve(e){q.value.splice(e,1)}function He(){I.value=!I.value,I.value||(F.value=``)}async function Ue(e){if(!e.trim()){H.value=[],z.value=null;return}U.value=!0;try{let t=await fetch(`/api/git/branches?path=${encodeURIComponent(e.trim())}`);if(!t.ok)throw Error(`HTTP ${t.status}`);let n=await t.json();H.value=n.local??n.branches??[],H.value.length>0&&!z.value&&(z.value=H.value[0]??null)}catch{H.value=[],z.value=null}finally{U.value=!1}}function We(e){let t=A.getProjectByPath(e);t&&(t.defaultSourceBranch&&(z.value=t.defaultSourceBranch),t.defaultModel?L.value=t.defaultModel:A.global.defaultModel&&(L.value=A.global.defaultModel))}let Z=null;t(R,e=>{Z&&clearTimeout(Z),Z=setTimeout(()=>{z.value=null,Ue(e),We(e)},500)});function Ge(e,t){t(()=>{M.value=A.projectPaths.filter(t=>t.toLowerCase().includes(e.toLowerCase()))})}r(()=>{A.fetchSettings()}),i(()=>{Z&&clearTimeout(Z)});function Ke(e){return e.toLowerCase().replace(/[^a-z0-9\s-]/g,``).trim().replace(/\s+/g,`-`).replace(/-+/g,`-`).substring(0,50)}function Q(){return N.value.trim()?N.value.trim().substring(0,80):!I.value&&P.value.trim()&&(P.value.trim().split(`
|
|
2
|
+
`)[0]??``).substring(0,80)||`workspace`}function qe(e){let t=(e.split(`/`).pop()??``).split(`-`);return t.length>1&&/^[0-9a-f]{12,}$/i.test(t[t.length-1])&&t.pop(),t.join(`-`).toLowerCase().substring(0,50)||`task-${Date.now()}`}function Je(){return I.value&&!G.value?j(`createPage.validationNotionUrl`):!I.value&&!P.value.trim()?j(`createPage.validationDescription`):!I.value&&(!Q()||Q()===`workspace`)&&!N.value.trim()&&!P.value.trim()?j(`createPage.validationName`):R.value.trim()?z.value?null:j(`createPage.validationBranch`):j(`createPage.validationPath`)}async function $(){let e=Je();if(e){O.notify({type:`negative`,message:e,position:`top`});return}W.value=!0;try{let e=Q(),t;t=e===`workspace`?I.value&&G.value?qe(F.value.trim()):`task-${Date.now()}`:Ke(e);let n=`feature/${t}`,r={name:e,projectPath:R.value.trim(),sourceBranch:z.value,workingBranch:n,model:L.value,...I.value&&G.value?{notionUrl:F.value.trim()}:{},...X.value&&K.value.length>0?{tasks:K.value}:{},...X.value&&q.value.length>0?{acceptanceCriteria:q.value}:{},...B.value?{skipSetupScript:!0}:{},...P.value.trim()?{description:P.value.trim()}:{},permissionMode:V.value},i=await k.createWorkspace(r);oe().subscribe(i.id),k.selectWorkspace(i.id),w.push({name:`workspace`,params:{id:i.id}})}catch{O.notify({type:`negative`,message:j(`createPage.errorCreating`),position:`top`})}finally{W.value=!1}}return(t,r)=>(e(),u(fe,{class:`create-page flex flex-center column`},{default:o(()=>[l(`div`,pe,[l(`div`,me,h(t.$t(`createPage.title`)),1),l(`div`,he,[l(`div`,ge,[l(`span`,_e,[s(g,{name:`auto_awesome`,size:`14px`,color:`indigo-4`}),l(`span`,ve,h(t.$t(`createPage.claudeCode`)),1)]),s(C),s(_,{flat:``,dense:``,"no-caps":``,size:`sm`,color:I.value?`green-4`:`grey-5`,class:`notion-toggle-btn text-caption rounded-borders`,onClick:He},{default:o(()=>[s(g,{name:`description`,size:`14px`,class:`q-mr-xs`}),d(` `+h(I.value?t.$t(`createPage.notionEnabled`):t.$t(`createPage.importNotion`)),1)]),_:1},8,[`color`])]),s(b,{color:`grey-9`}),s(ie,{name:`slide`},{default:o(()=>[I.value?(e(),f(`div`,ye,[s(x,{modelValue:F.value,"onUpdate:modelValue":r[0]||=e=>F.value=e,borderless:``,dense:``,placeholder:t.$t(`createPage.notionPlaceholder`),class:`notion-url-input`,"input-class":`notion-url-input-inner`},{prepend:o(()=>[s(g,{name:`link`,size:`16px`,color:G.value?`green-4`:`grey-6`},null,8,[`color`])]),_:1},8,[`modelValue`,`placeholder`]),F.value.trim()&&!G.value?(e(),f(`div`,be,h(t.$t(`createPage.notionValidation`)),1)):p(``,!0),G.value?(e(),f(`div`,xe,h(t.$t(`createPage.notionAutoExtract`)),1)):p(``,!0)])):p(``,!0)]),_:1}),I.value?(e(),u(b,{key:0,color:`grey-9`})):p(``,!0),l(`div`,Se,[s(x,{modelValue:N.value,"onUpdate:modelValue":r[1]||=e=>N.value=e,borderless:``,dense:``,placeholder:I.value&&G.value?t.$t(`createPage.workspaceName`):t.$t(`createPage.workspaceNamePlaceholder`),class:`name-input`,"input-class":`name-input-inner`},null,8,[`modelValue`,`placeholder`])]),s(b,{color:`grey-9`}),l(`div`,Ce,[s(x,{modelValue:P.value,"onUpdate:modelValue":r[2]||=e=>P.value=e,type:`textarea`,borderless:``,autogrow:``,rows:3,placeholder:I.value?t.$t(`createPage.instructions`):t.$t(`createPage.instructionsPlaceholder`),class:`create-textarea`,"input-class":`create-textarea-input`,onKeydown:[y(v($,[`ctrl`]),[`enter`]),y(v($,[`meta`]),[`enter`])]},null,8,[`modelValue`,`placeholder`,`onKeydown`])]),s(b,{color:`grey-9`}),X.value?(e(),f(c,{key:1},[l(`div`,we,h(t.$t(`createPage.manualHint`)),1),s(de,{dark:``,dense:``,label:t.$t(`createPage.tasks`,{count:K.value.length}),"header-class":`text-grey-4 manual-expansion-header`,class:`manual-expansion q-mx-sm`},{default:o(()=>[l(`div`,Te,[l(`div`,Ee,[s(x,{modelValue:J.value,"onUpdate:modelValue":r[3]||=e=>J.value=e,dark:``,dense:``,borderless:``,placeholder:t.$t(`createPage.addTask`),class:`col manual-input`,"input-class":`manual-input-inner`,onKeydown:y(v(Re,[`prevent`]),[`enter`])},null,8,[`modelValue`,`placeholder`,`onKeydown`]),s(_,{flat:``,dense:``,round:``,icon:`add`,color:`indigo-4`,disable:!J.value.trim(),onClick:Re},{default:o(()=>[s(S,null,{default:o(()=>[d(h(t.$t(`tooltip.addTask`)),1)]),_:1})]),_:1},8,[`disable`])]),(e(!0),f(c,null,n(K.value,(n,r)=>(e(),f(`div`,{key:`task-${r}`,class:`row items-center q-py-xs manual-item`},[l(`span`,De,h(n),1),s(_,{flat:``,dense:``,round:``,icon:`close`,size:`xs`,color:`grey-6`,onClick:e=>ze(r)},{default:o(()=>[s(S,null,{default:o(()=>[d(h(t.$t(`tooltip.removeTask`)),1)]),_:1})]),_:1},8,[`onClick`])]))),128))])]),_:1},8,[`label`]),s(de,{dark:``,dense:``,label:t.$t(`createPage.acceptanceCriteria`,{count:q.value.length}),"header-class":`text-grey-4 manual-expansion-header`,class:`manual-expansion q-mx-sm q-mb-sm`},{default:o(()=>[l(`div`,Oe,[l(`div`,ke,[s(x,{modelValue:Y.value,"onUpdate:modelValue":r[4]||=e=>Y.value=e,dark:``,dense:``,borderless:``,placeholder:t.$t(`createPage.addCriterion`),class:`col manual-input`,"input-class":`manual-input-inner`,onKeydown:y(v(Be,[`prevent`]),[`enter`])},null,8,[`modelValue`,`placeholder`,`onKeydown`]),s(_,{flat:``,dense:``,round:``,icon:`add`,color:`indigo-4`,disable:!Y.value.trim(),onClick:Be},{default:o(()=>[s(S,null,{default:o(()=>[d(h(t.$t(`tooltip.addCriterion`)),1)]),_:1})]),_:1},8,[`disable`])]),(e(!0),f(c,null,n(q.value,(n,r)=>(e(),f(`div`,{key:`crit-${r}`,class:`row items-center q-py-xs manual-item`},[l(`span`,Ae,h(n),1),s(_,{flat:``,dense:``,round:``,icon:`close`,size:`xs`,color:`grey-6`,onClick:e=>Ve(r)},{default:o(()=>[s(S,null,{default:o(()=>[d(h(t.$t(`tooltip.removeCriterion`)),1)]),_:1})]),_:1},8,[`onClick`])]))),128))])]),_:1},8,[`label`]),s(b,{color:`grey-9`})],64)):p(``,!0),l(`div`,je,[s(D,{modelValue:L.value,"onUpdate:modelValue":r[5]||=e=>L.value=e,options:Le.value,dense:``,borderless:``,class:`bottom-select rounded-borders model-select`,"hide-dropdown-icon":``,"emit-value":``,"map-options":``,"option-value":`value`,"option-label":`label`},{selected:o(()=>[l(`span`,Me,[d(h(Le.value.find(e=>e.value===L.value)?.label??L.value)+` `,1),s(g,{name:`expand_more`,size:`12px`,color:`grey-5`})])]),option:o(({opt:e,itemProps:t})=>[s(E,ee(t,{class:`model-option`}),{default:o(()=>[s(T,null,{default:o(()=>[s(ue,{class:`text-white`},{default:o(()=>[d(h(e.label),1)]),_:2},1024),s(ue,{caption:``,class:`text-grey-5`},{default:o(()=>[d(h(e.description),1)]),_:2},1024)]),_:2},1024)]),_:2},1040)]),_:1},8,[`modelValue`,`options`]),s(D,{modelValue:V.value,"onUpdate:modelValue":r[6]||=e=>V.value=e,options:[{label:t.$t(`permissionMode.plan`),value:`plan`},{label:t.$t(`permissionMode.autoAccept`),value:`auto-accept`}],dense:``,borderless:``,class:`bottom-select rounded-borders`,"hide-dropdown-icon":``,"emit-value":``,"map-options":``},{selected:o(()=>[l(`span`,Ne,[s(g,{name:V.value===`plan`?`visibility`:`flash_on`,size:`12px`,color:`amber-6`,class:`q-mr-xs`},null,8,[`name`]),d(` `+h(V.value===`plan`?t.$t(`permissionMode.plan`):t.$t(`permissionMode.autoAccept`))+` `,1),s(g,{name:`expand_more`,size:`12px`,color:`grey-5`})])]),_:1},8,[`modelValue`,`options`]),s(_,{flat:``,round:``,dense:``,size:`sm`,icon:B.value?`play_disabled`:`play_circle`,color:B.value?`orange-4`:`grey-6`,onClick:r[7]||=e=>B.value=!B.value},{default:o(()=>[s(S,null,{default:o(()=>[d(h(t.$t(`createPage.skipSetupScript`)),1)]),_:1})]),_:1},8,[`icon`,`color`]),s(C),s(D,{modelValue:R.value,"onUpdate:modelValue":r[8]||=e=>R.value=e,options:M.value,dense:``,borderless:``,"use-input":``,"hide-selected":``,"fill-input":``,"input-debounce":`0`,"new-value-mode":`add`,class:`bottom-select rounded-borders repo-select`,"hide-dropdown-icon":``,behavior:ne(A).projectPaths.length>0?`menu`:`dialog`,onFilter:Ge,onInputValue:r[9]||=e=>{R.value=e}},{prepend:o(()=>[s(g,{name:`attach_file`,size:`12px`,color:`grey-5`})]),selected:o(()=>[l(`span`,Pe,h(R.value||t.$t(`createPage.projectPath`)),1)]),"no-option":o(()=>[s(E,null,{default:o(()=>[s(T,{class:`text-grey-6 text-caption`},{default:o(()=>[d(h(t.$t(`createPage.enterPath`)),1)]),_:1})]),_:1})]),_:1},8,[`modelValue`,`options`,`behavior`]),s(D,{modelValue:z.value,"onUpdate:modelValue":r[10]||=e=>z.value=e,options:H.value,dense:``,borderless:``,class:`bottom-select rounded-borders branch-select`,"hide-dropdown-icon":``,loading:U.value,disable:!R.value.trim()||U.value},{selected:o(()=>[l(`span`,Fe,[s(g,{name:`call_split`,size:`12px`,color:`grey-5`,class:`q-mr-xs`}),d(` `+h(z.value??t.$t(`createPage.branch`))+` `,1),s(g,{name:`expand_more`,size:`12px`,color:`grey-5`})])]),"no-option":o(()=>[s(E,null,{default:o(()=>[s(T,{class:`text-grey-6 text-caption`},{default:o(()=>[d(h(R.value.trim()?t.$t(`createPage.noBranches`):t.$t(`createPage.enterPath`)),1)]),_:1})]),_:1})]),_:1},8,[`modelValue`,`options`,`loading`,`disable`]),s(_,{label:t.$t(`createPage.create`),"no-caps":``,unelevated:``,class:`create-btn text-weight-bold rounded-borders`,loading:W.value,onClick:$},null,8,[`label`,`loading`])])]),l(`div`,Ie,h(I.value?t.$t(`createPage.notionExtractHint`):t.$t(`createPage.notionImportHint`)),1)])]),_:1}))}}),[[`__scopeId`,`data-v-9b31762d`]]);export{O as default};
|