@loicngr/kobo 1.4.10 → 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.
Files changed (73) hide show
  1. package/AGENTS.md +16 -10
  2. package/README.md +5 -1
  3. package/dist/mcp-server/kobo-tasks-server.js +1 -1
  4. package/dist/server/db/migrations.js +31 -0
  5. package/dist/server/db/schema.js +2 -1
  6. package/dist/server/index.js +14 -5
  7. package/dist/server/routes/plans.js +89 -0
  8. package/dist/server/routes/templates.js +82 -0
  9. package/dist/server/routes/workspaces.js +108 -11
  10. package/dist/server/services/agent-manager.js +74 -21
  11. package/dist/server/services/templates-service.js +173 -0
  12. package/dist/server/services/workspace-service.js +49 -0
  13. package/dist/server/utils/git-ops.js +10 -0
  14. package/dist/server/utils/paths.js +7 -0
  15. package/package.json +1 -1
  16. package/src/client/dist/spa/assets/ActivityFeed-C8UZFjUM.css +1 -0
  17. package/src/client/dist/spa/assets/ActivityFeed-CahLjg_a.js +10 -0
  18. package/src/client/dist/spa/assets/ClosePopup-jSuaV6dg.js +1 -0
  19. package/src/client/dist/spa/assets/{CreatePage-3gOfOT7H.js → CreatePage-DvenHBbs.js} +2 -2
  20. package/src/client/dist/spa/assets/DiffViewer-BNB89NC_.js +2 -0
  21. package/src/client/dist/spa/assets/MainLayout-8hdHhbFX.css +1 -0
  22. package/src/client/dist/spa/assets/MainLayout-CRZ_pSUI.js +2 -0
  23. package/src/client/dist/spa/assets/QExpansionItem-D6zpEqBV.js +1 -0
  24. package/src/client/dist/spa/assets/{QList-CwbqTC_9.js → QList-oHuiVWr0.js} +1 -1
  25. package/src/client/dist/spa/assets/QMenu-67GqYx0C.js +1 -0
  26. package/src/client/dist/spa/assets/QPage-DE75SzRI.js +1 -0
  27. package/src/client/dist/spa/assets/SettingsPage-BIAu1N7t.js +1 -0
  28. package/src/client/dist/spa/assets/SettingsPage-ai7Q_1KB.css +1 -0
  29. package/src/client/dist/spa/assets/{TouchPan-Dc-xrSIS.js → TouchPan-DuISf80E.js} +1 -1
  30. package/src/client/dist/spa/assets/WorkspacePage-ZtCuDedw.js +4 -0
  31. package/src/client/dist/spa/assets/WorkspacePage-l-yPyCj4.css +1 -0
  32. package/src/client/dist/spa/assets/_plugin-vue_export-helper-fkfRoKj2.js +1 -0
  33. package/src/client/dist/spa/assets/{cssMode-CWtayDKW.js → cssMode-CLNBNUPN.js} +1 -1
  34. package/src/client/dist/spa/assets/{editor.api-DeuH5Z7J.js → editor.api-BOAfcnN4.js} +1 -1
  35. package/src/client/dist/spa/assets/{editor.main-CHvDgl6E.js → editor.main-DHOeskpn.js} +3 -3
  36. package/src/client/dist/spa/assets/{freemarker2-3a7cf3Uc.js → freemarker2-BtrSj5Xy.js} +1 -1
  37. package/src/client/dist/spa/assets/{handlebars-BjDKZgtm.js → handlebars-Dc1FuhPx.js} +1 -1
  38. package/src/client/dist/spa/assets/{html-CxHAAoPw.js → html-CRa_T0ab.js} +1 -1
  39. package/src/client/dist/spa/assets/{htmlMode-DhXtvpCN.js → htmlMode-DiZaznA3.js} +1 -1
  40. package/src/client/dist/spa/assets/i18n-Chh7fA86.js +1 -0
  41. package/src/client/dist/spa/assets/i18n-EPkNuLUF.js +1 -0
  42. package/src/client/dist/spa/assets/index-BIsYlO92.js +5 -0
  43. package/src/client/dist/spa/assets/{javascript-CFSWGGGJ.js → javascript-BmWQzw3l.js} +1 -1
  44. package/src/client/dist/spa/assets/{jsonMode-k2An8_pW.js → jsonMode-CxTgSexO.js} +1 -1
  45. package/src/client/dist/spa/assets/{liquid-Cq4t-XTr.js → liquid-BUs2kSUC.js} +1 -1
  46. package/src/client/dist/spa/assets/marked.esm-BjjOHIBz.js +60 -0
  47. package/src/client/dist/spa/assets/{mdx-UB8RrpeD.js → mdx-Cnd0lVUN.js} +1 -1
  48. package/src/client/dist/spa/assets/{monaco.contribution-B9W_lLWp.js → monaco.contribution-DdEdriCG.js} +2 -2
  49. package/src/client/dist/spa/assets/{python-n8QHA1-s.js → python-DEYsKpRr.js} +1 -1
  50. package/src/client/dist/spa/assets/{razor-DQrmL_Eu.js → razor-D5u_QaVY.js} +1 -1
  51. package/src/client/dist/spa/assets/{tsMode-DqHf0yz_.js → tsMode-DWHAMuFy.js} +1 -1
  52. package/src/client/dist/spa/assets/{typescript-C2OgYZBp.js → typescript-miqJki5_.js} +1 -1
  53. package/src/client/dist/spa/assets/{xml-BD4Y8hNI.js → xml-TsOUFKqU.js} +1 -1
  54. package/src/client/dist/spa/assets/{yaml-dxD-q88Q.js → yaml-Bn4aEJci.js} +1 -1
  55. package/src/client/dist/spa/index.html +2 -2
  56. package/src/mcp-server/kobo-tasks-server.ts +1 -1
  57. package/src/client/dist/spa/assets/ActivityFeed-5m7j8KXA.css +0 -1
  58. package/src/client/dist/spa/assets/ActivityFeed-CWcNkgNz.js +0 -68
  59. package/src/client/dist/spa/assets/DiffViewer-BB0uMnDB.js +0 -2
  60. package/src/client/dist/spa/assets/MainLayout-Di4zIHM4.js +0 -2
  61. package/src/client/dist/spa/assets/MainLayout-Drv4zYbW.css +0 -1
  62. package/src/client/dist/spa/assets/QExpansionItem-dnmzWHId.js +0 -1
  63. package/src/client/dist/spa/assets/QMenu-C_LDF5uF.js +0 -1
  64. package/src/client/dist/spa/assets/QPage-0Qoiu-SJ.js +0 -1
  65. package/src/client/dist/spa/assets/QTooltip-DKxEdudV.js +0 -1
  66. package/src/client/dist/spa/assets/SettingsPage-2D1mycBC.js +0 -1
  67. package/src/client/dist/spa/assets/SettingsPage-CdWgxXBN.css +0 -1
  68. package/src/client/dist/spa/assets/WorkspacePage-D6ilyDGt.js +0 -4
  69. package/src/client/dist/spa/assets/WorkspacePage-DrDOZvHX.css +0 -1
  70. package/src/client/dist/spa/assets/_plugin-vue_export-helper-CLHv3piE.js +0 -1
  71. package/src/client/dist/spa/assets/i18n-BG0I8c3l.js +0 -1
  72. package/src/client/dist/spa/assets/i18n-BvyUMouA.js +0 -1
  73. package/src/client/dist/spa/assets/index-Dh7opLow.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.claudeSessionId);
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
- const lastSession = db
136
- .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')
137
- .get(workspaceId);
138
- const claudeSessionId = sessionIds.get(workspaceId) ?? lastSession?.claude_session_id;
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
- agentSessionId = nanoid();
164
- db.prepare('INSERT INTO agent_sessions (id, workspace_id, pid, status, started_at) VALUES (?, ?, ?, ?, ?)').run(agentSessionId, workspaceId, null, 'running', new Date().toISOString());
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.claudeSessionId);
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.claudeSessionId);
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.claudeSessionId);
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.claudeSessionId);
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.claudeSessionId);
347
+ handleQuota(workspaceId, agent.agentSessionId);
319
348
  }
320
349
  // Also emit stderr for visibility
321
- emit(workspaceId, 'agent:stderr', { content: text }, agent.claudeSessionId);
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.claudeSessionId);
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.claudeSessionId);
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.claudeSessionId);
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.claudeSessionId);
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, claudeSessionId) {
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' }, claudeSessionId);
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
- }, claudeSessionId);
559
+ }, agentSessionId);
507
560
  // Set timer to restart agent
508
561
  const timer = setTimeout(() => {
509
562
  backoffTimers.delete(workspaceId);
@@ -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
+ }
@@ -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.10",
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};
@@ -1,2 +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-Dh7opLow.js";import{p as S,t as C}from"./_plugin-vue_export-helper-CLHv3piE.js";import{t as w}from"./QTooltip-DKxEdudV.js";import{i as le,n as T,r as E}from"./QMenu-C_LDF5uF.js";import{n as D,r as ue,t as de}from"./QPage-0Qoiu-SJ.js";import{t as fe}from"./QExpansionItem-dnmzWHId.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=C(re({__name:`CreatePage`,setup(re){let C=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),C.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(de,{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(S),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(fe,{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(w,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(w,null,{default:o(()=>[d(h(t.$t(`tooltip.removeTask`)),1)]),_:1})]),_:1},8,[`onClick`])]))),128))])]),_:1},8,[`label`]),s(fe,{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(w,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(w,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(T,ee(t,{class:`model-option`}),{default:o(()=>[s(E,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(w,null,{default:o(()=>[d(h(t.$t(`createPage.skipSetupScript`)),1)]),_:1})]),_:1},8,[`icon`,`color`]),s(S),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(T,null,{default:o(()=>[s(E,{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(T,null,{default:o(()=>[s(E,{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};
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};