@shawnstack/quickforge 1.6.8 → 1.6.9

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 (161) hide show
  1. package/README.md +10 -10
  2. package/dist/assets/AgentProfilesPage-5eqU0Cjk.js +1 -0
  3. package/dist/assets/ChatPanelHost-BNFeNRy-.js +244 -0
  4. package/dist/assets/{PluginsPage-3pZG7D7e.js → PluginsPage-C5OClNdT.js} +1 -1
  5. package/dist/assets/ScheduledTasksPage-yT10oaaM.js +2 -0
  6. package/dist/assets/{SettingsWorkspacePage-DWkPyGP_.js → SettingsWorkspacePage-DuXwIeLd.js} +346 -335
  7. package/dist/assets/SharedConversationPage-ClpUSa0F.js +1 -0
  8. package/dist/assets/TerminalDock-BmL-FeKv.js +2 -0
  9. package/dist/assets/WorkspaceInspector-yH23pXpj.js +13 -0
  10. package/dist/assets/abnfDiagram-VRR7QNED-Dl_Fvlqg.js +1 -0
  11. package/dist/assets/arc-Bhg4c0ht.js +1 -0
  12. package/dist/assets/architecture-TIHT7OUA-BgP-IMeo.js +1 -0
  13. package/dist/assets/architectureDiagram-ZJ3FMSHR-BuojN27B.js +36 -0
  14. package/dist/assets/array-CYkMkqnU.js +1 -0
  15. package/dist/assets/blockDiagram-677ZJIJ3-alDLbrR1.js +132 -0
  16. package/dist/assets/c4Diagram-LMCZKHZV-D1lKa4VT.js +10 -0
  17. package/dist/assets/channel-Rz20dKTK.js +1 -0
  18. package/dist/assets/chunk-2Q5K7J3B-Cb14ZF_k.js +1 -0
  19. package/dist/assets/chunk-32BRIVSS-CwWLNjMZ.js +1 -0
  20. package/dist/assets/chunk-52WLFC77-Be569m7d.js +10 -0
  21. package/dist/assets/chunk-5FCAYU7R-BSvoiWV1.js +1 -0
  22. package/dist/assets/chunk-5HE753X5-CRgsy8WS.js +1 -0
  23. package/dist/assets/chunk-5JV3BV7I-CdW477UR.js +1 -0
  24. package/dist/assets/chunk-5TONJI2A-CdtKzSmp.js +2 -0
  25. package/dist/assets/chunk-5VM5RSS4-BKjjssLZ.js +15 -0
  26. package/dist/assets/chunk-7BUUIJ7U-ByAXiXdn.js +1 -0
  27. package/dist/assets/chunk-BIQX33UG-DeG8SCT1.js +1 -0
  28. package/dist/assets/chunk-C7G6YPKG-D_zHT68Q.js +1 -0
  29. package/dist/assets/chunk-CQNSW5MT-CHpHoUSu.js +1 -0
  30. package/dist/assets/chunk-CYSBUYHQ-CRP50Vov.js +1 -0
  31. package/dist/assets/chunk-EMLP6XTP-DX6uQQ2v.js +1 -0
  32. package/dist/assets/chunk-EX3LRPZG-DAJRNkHR.js +231 -0
  33. package/dist/assets/chunk-FWX5IMBZ-XbM5yomC.js +2 -0
  34. package/dist/assets/chunk-HOUHSVGY-Ci0ljnb1.js +1 -0
  35. package/dist/assets/chunk-ICXQ74PX-Dy2WJ71z.js +2 -0
  36. package/dist/assets/chunk-JG7HCLWE-2PrkzHkt.js +2 -0
  37. package/dist/assets/chunk-JWPE2WC7-D32uacSM.js +1 -0
  38. package/dist/assets/chunk-KEIR6QF5-77hsebd7.js +161 -0
  39. package/dist/assets/chunk-MOJQB5TN-DIAFLtiS.js +88 -0
  40. package/dist/assets/chunk-MOZMSUNE-BbscOaEz.js +1 -0
  41. package/dist/assets/chunk-OGEWGWER-ivBVxzKe.js +1 -0
  42. package/dist/assets/chunk-OSBZ3O6U-frD0hkdx.js +1 -0
  43. package/dist/assets/chunk-PUDLZKDR-B-37Rxae.js +156 -0
  44. package/dist/assets/chunk-Q4XR5HBZ-Y90m2RTe.js +11 -0
  45. package/dist/assets/chunk-QBLGF6JB-DixBqslB.js +1 -0
  46. package/dist/assets/chunk-R7FJI6CG-DXS1UXn_.js +1 -0
  47. package/dist/assets/chunk-RYQCIY6F-n4JxhNd0.js +1 -0
  48. package/dist/assets/chunk-U6XO7XAA-yHlgxMmn.js +2 -0
  49. package/dist/assets/chunk-V7JOEXUC-Z6kuZfbl.js +206 -0
  50. package/dist/assets/chunk-VAUOI2AC-BOZvd2Sn.js +1 -0
  51. package/dist/assets/chunk-VR4S4FIN-Cs_SIQma.js +1 -0
  52. package/dist/assets/chunk-WYO6CB5R-CGExYbV-.js +128 -0
  53. package/dist/assets/chunk-XXDRQBXY-CD_EGCtT.js +1 -0
  54. package/dist/assets/chunk-Y2CYZVJY-CQltPwYk.js +1 -0
  55. package/dist/assets/chunk-YOTPTUD7-DopPXWSl.js +1 -0
  56. package/dist/assets/chunk-ZGVPDNZ5-CJHOeqMb.js +62 -0
  57. package/dist/assets/chunk-ZIRB5QZD-BBVc5mf6.js +32 -0
  58. package/dist/assets/classDiagram-OUVF2IWQ-C_ozTKCt.js +1 -0
  59. package/dist/assets/classDiagram-v2-EOCWNBFH-FOfFbbZF.js +1 -0
  60. package/dist/assets/cose-bilkent-JH36ORCC-DLjV-dTm.js +1 -0
  61. package/dist/assets/cynefin-VYW2F7L2-BzPhLtd5.js +1 -0
  62. package/dist/assets/cynefinDiagram-TSTJHNR4-BfH3zVS-.js +62 -0
  63. package/dist/assets/cytoscape.esm-DwdZnGOm.js +321 -0
  64. package/dist/assets/dagre-CY3D84Cl.js +1 -0
  65. package/dist/assets/dagre-VKFMJZFB-BIhwZCIZ.js +4 -0
  66. package/dist/assets/defaultLocale-Beh6XjaL.js +1 -0
  67. package/dist/assets/diagram-FQU43EPY-BHnzNM2w.js +3 -0
  68. package/dist/assets/diagram-G47NLZAW-C7-ZlmFT.js +24 -0
  69. package/dist/assets/diagram-NH7WQ7WH-C-P9Y7IR.js +24 -0
  70. package/dist/assets/diagram-OA4YK3LP-Ci5lZ_T5.js +30 -0
  71. package/dist/assets/diagram-WEI45ONY-C1xuK2bs.js +41 -0
  72. package/dist/assets/dist-DelZBZpK.js +1 -0
  73. package/dist/assets/ebnfDiagram-CCIWWBDH-DKQ5NWMD.js +1 -0
  74. package/dist/assets/erDiagram-Q63AITRT-HAWySadP.js +85 -0
  75. package/dist/assets/eventmodeling-45OFAUF4-BcgGbVJ-.js +1 -0
  76. package/dist/assets/flowDiagram-23GEKE2U-dtkzcC8E.js +1 -0
  77. package/dist/assets/ganttDiagram-NO4QXBWP-C1hwIcOA.js +292 -0
  78. package/dist/assets/gitGraph-TEB2WS4Q-VByWboBU.js +1 -0
  79. package/dist/assets/gitGraphDiagram-IHSO6WYX-DYUYxs1R.js +106 -0
  80. package/dist/assets/graphlib-CAISHZh4.js +1 -0
  81. package/dist/assets/icons-C7j5jdKo.js +1 -0
  82. package/dist/assets/index-Qof73j0o.css +3 -0
  83. package/dist/assets/index-_ut7awrd.js +63 -0
  84. package/dist/assets/info-DKCQHKI2-CZeDLZxx.js +1 -0
  85. package/dist/assets/infoDiagram-FWYZ7A6U-CnIL6tv4.js +2 -0
  86. package/dist/assets/init-Rr1s_RiX.js +1 -0
  87. package/dist/assets/ishikawaDiagram-FXEZZL3T-B7WXNycQ.js +70 -0
  88. package/dist/assets/journeyDiagram-5HDEW3XC-D4HvioyY.js +139 -0
  89. package/dist/assets/kanban-definition-HUTT4EX6-CDs763eS.js +89 -0
  90. package/dist/assets/line-ikKkQiFp.js +1 -0
  91. package/dist/assets/linear-GqPyxQkN.js +1 -0
  92. package/dist/assets/map-CJwjCvwr.js +1 -0
  93. package/dist/assets/{mcp-servers-dialog-VbGvNekJ.js → mcp-servers-dialog-DC2Zfojn.js} +2 -2
  94. package/dist/assets/mermaid-parser.core-CoE8cccr.js +4 -0
  95. package/dist/assets/mermaid-renderer-BmbSAmpG.js +2 -0
  96. package/dist/assets/mermaid.core-DlFSL90j.js +11 -0
  97. package/dist/assets/mindmap-definition-LN4V7U3C-DLdq0K9C.js +96 -0
  98. package/dist/assets/{monaco-CzxeAjEw.js → monaco-CdOyGlWD.js} +1 -1
  99. package/dist/assets/ordinal-CibZU3PM.js +1 -0
  100. package/dist/assets/{otel-DQf3c6v2.js → otel-BNGM50C-.js} +1 -1
  101. package/dist/assets/packet-7NZHBO7P-decrrxyX.js +1 -0
  102. package/dist/assets/path-BAQ3hXlG.js +1 -0
  103. package/dist/assets/pegDiagram-2B236MQR-Cm1kPRC2.js +1 -0
  104. package/dist/assets/{pi-ai-DjJwSQBQ.js → pi-ai-Cr3rpCXT.js} +2 -2
  105. package/dist/assets/{pi-web-ui-DqxtBlZL.js → pi-web-ui-DoRGtREc.js} +371 -371
  106. package/dist/assets/pie-RZYD4A2V-J1CasQZl.js +1 -0
  107. package/dist/assets/pieDiagram-ENE6RG2P-Df05wjjo.js +39 -0
  108. package/dist/assets/quadrantDiagram-ABIIQ3AL-BjCwGxJC.js +7 -0
  109. package/dist/assets/radar-I7S5WNFK-B-sZytUA.js +1 -0
  110. package/dist/assets/railroad-3IZDKUUU-BSaqNPm8.js +1 -0
  111. package/dist/assets/railroad-abnf-AHOZXSZD-lzZR7PxT.js +1 -0
  112. package/dist/assets/railroad-ebnf-EBAXGLYW-DQtz1Fh9.js +1 -0
  113. package/dist/assets/railroad-peg-LSFZ7HO6-CpMsuGY3.js +1 -0
  114. package/dist/assets/railroadDiagram-RFXS5EU6-CTUvpKkv.js +1 -0
  115. package/dist/assets/{react-vendor-BR9MG2D-.js → react-vendor-VqdnQHHS.js} +1 -1
  116. package/dist/assets/requirementDiagram-TGXJPOKE-BU1kvEku.js +84 -0
  117. package/dist/assets/rough.esm-Djo4Abte.js +1 -0
  118. package/dist/assets/sankeyDiagram-HTMAVEWB-BHmvxVqH.js +40 -0
  119. package/dist/assets/sequenceDiagram-DBY2YBRQ-CTpwICgg.js +162 -0
  120. package/dist/assets/sizeCapture-X5ZJPWSS-BbLhGaa4.js +1 -0
  121. package/dist/assets/{skills-dialog-B0jNVGZW.js → skills-dialog-Mnj8fZwq.js} +1 -1
  122. package/dist/assets/src-B4Tvg3gT.js +1 -0
  123. package/dist/assets/stateDiagram-2N3HPSRC-BxuDwIgT.js +1 -0
  124. package/dist/assets/stateDiagram-v2-6OUMAXLB-BGLshxcY.js +1 -0
  125. package/dist/assets/swimlanes-5IMT3BWC-EkZsiylV.js +2 -0
  126. package/dist/assets/swimlanesDiagram-G3AALYLV-CqdqxA-b.js +8 -0
  127. package/dist/assets/timeline-definition-FHXFAJF6-C9ewOCTa.js +120 -0
  128. package/dist/assets/treeView-QDETBFTQ-Byb71heW.js +1 -0
  129. package/dist/assets/treemap-6X3UGDF4-CMV1SJsq.js +1 -0
  130. package/dist/assets/vennDiagram-L72KCM5P-CR49Y4Lp.js +34 -0
  131. package/dist/assets/wardley-OPB4EBWU-DtCHtr2V.js +1 -0
  132. package/dist/assets/wardleyDiagram-EHGQE667-Cn0GgwWi.js +78 -0
  133. package/dist/assets/xychartDiagram-FW5EYKEG-DlmPNAvd.js +7 -0
  134. package/dist/index.html +8 -8
  135. package/package.json +2 -1
  136. package/server/agent-manager.mjs +174 -9
  137. package/server/agent-profile-files.mjs +86 -6
  138. package/server/agent-profile-schema.mjs +197 -0
  139. package/server/agent-profiles.mjs +172 -41
  140. package/server/auto-compaction.mjs +2 -2
  141. package/server/project-route-context.mjs +25 -0
  142. package/server/routes/agent-profiles.mjs +9 -3
  143. package/server/routes/project.mjs +6 -10
  144. package/server/routes/workspace.mjs +184 -11
  145. package/server/storage.mjs +34 -0
  146. package/server/subagents.mjs +64 -0
  147. package/server/tools/definitions.mjs +17 -1
  148. package/dist/assets/AgentProfilesPage-R7N7eDq5.js +0 -1
  149. package/dist/assets/ChatPanelHost-C4orgd5l.js +0 -242
  150. package/dist/assets/ScheduledTasksPage-BAqoK4xk.js +0 -2
  151. package/dist/assets/SharedConversationPage-C-1n_MmB.js +0 -1
  152. package/dist/assets/TerminalDock-B6KAXkIU.js +0 -2
  153. package/dist/assets/WorkspaceInspector-CkXyD_k4.js +0 -3
  154. package/dist/assets/WorkspaceReaderDialog-Dp2QAwBo.js +0 -1
  155. package/dist/assets/diff-line-counts-Dm5kKQOo.js +0 -10
  156. package/dist/assets/icons-DHoaB5uq.js +0 -1
  157. package/dist/assets/index-C77QCHE9.css +0 -3
  158. package/dist/assets/index-CZb_mGGF.js +0 -63
  159. package/dist/assets/useAppTheme-DsabFF9D.js +0 -1
  160. /package/dist/assets/{plugin-api-Ds5xGIL7.js → plugin-api-BbUkYm3Y.js} +0 -0
  161. /package/dist/assets/{xterm-aPHkK8RR.js → xterm-CGiQzDiR.js} +0 -0
@@ -139,13 +139,17 @@ function parseGitStatus(buffer) {
139
139
  return files.sort((left, right) => left.path.localeCompare(right.path, undefined, { sensitivity: 'base' }))
140
140
  }
141
141
 
142
- async function currentGitBranch(workspaceRoot) {
142
+ async function currentGitHead(workspaceRoot) {
143
143
  const result = await git(['branch', '--show-current'], workspaceRoot, { allowFailure: true })
144
144
  const branch = result.stdout.toString('utf8').trim()
145
- if (branch) return branch
145
+ if (branch) return { branch, detached: false }
146
146
  const head = await git(['rev-parse', '--short', 'HEAD'], workspaceRoot, { allowFailure: true })
147
147
  const commit = head.stdout.toString('utf8').trim()
148
- return commit ? `HEAD ${commit}` : undefined
148
+ return { branch: commit ? `HEAD ${commit}` : undefined, detached: Boolean(commit) }
149
+ }
150
+
151
+ async function currentGitBranch(workspaceRoot) {
152
+ return (await currentGitHead(workspaceRoot)).branch
149
153
  }
150
154
 
151
155
  async function assertValidBranchName(workspaceRoot, branch) {
@@ -317,9 +321,11 @@ async function listGitStatus(context) {
317
321
  }
318
322
  }
319
323
  }
324
+ const head = await currentGitHead(context.workspaceRoot)
320
325
  return {
321
326
  isGitRepository: true,
322
- branch: await currentGitBranch(context.workspaceRoot),
327
+ branch: head.branch,
328
+ detached: head.detached,
323
329
  counts: countGitStatus(files),
324
330
  files,
325
331
  }
@@ -347,8 +353,17 @@ async function hasStagedChanges(context) {
347
353
  return result.code === 1
348
354
  }
349
355
 
356
+ async function assertAttachedGitHead(context) {
357
+ const head = await currentGitHead(context.workspaceRoot)
358
+ if (!head.detached) return head
359
+ const error = new Error('Cannot commit or push while HEAD is detached')
360
+ error.statusCode = 400
361
+ throw error
362
+ }
363
+
350
364
  async function commitGitChanges(context, message, includeUnstaged) {
351
365
  await assertGitRepository(context)
366
+ await assertAttachedGitHead(context)
352
367
  const commitMessage = normalizeCommitMessage(message)
353
368
  if (includeUnstaged) await git(['add', '-A'], context.workspaceRoot)
354
369
  if (!(await hasStagedChanges(context))) {
@@ -360,8 +375,88 @@ async function commitGitChanges(context, message, includeUnstaged) {
360
375
  return listGitStatus(context)
361
376
  }
362
377
 
378
+ async function normalizeGitOperationPath(context, value) {
379
+ const relativePath = typeof value === 'string' ? value.trim().replace(/\\/g, '/') : ''
380
+ if (!relativePath || relativePath === '.') {
381
+ const error = new Error('path is required')
382
+ error.statusCode = 400
383
+ throw error
384
+ }
385
+ const file = resolveWorkspacePath(relativePath, context)
386
+ await assertSafeWorkspacePath(file, context, { allowSensitive: true, ignoreMissing: true })
387
+ return toWorkspaceRelative(file, context)
388
+ }
389
+
390
+ async function stageGitPath(context, value) {
391
+ await assertGitRepository(context)
392
+ const relativePath = await normalizeGitOperationPath(context, value)
393
+ await git(['add', '-A', '--', relativePath], context.workspaceRoot)
394
+ return listGitStatus(context)
395
+ }
396
+
397
+ async function stageAllGitChanges(context) {
398
+ await assertGitRepository(context)
399
+ await git(['add', '-A'], context.workspaceRoot)
400
+ return listGitStatus(context)
401
+ }
402
+
403
+ async function unstageGitPath(context, value) {
404
+ await assertGitRepository(context)
405
+ const relativePath = await normalizeGitOperationPath(context, value)
406
+ await git(['restore', '--staged', '--', relativePath], context.workspaceRoot)
407
+ return listGitStatus(context)
408
+ }
409
+
410
+ async function unstageAllGitChanges(context) {
411
+ await assertGitRepository(context)
412
+ await git(['restore', '--staged', '--', '.'], context.workspaceRoot)
413
+ return listGitStatus(context)
414
+ }
415
+
416
+ async function restoreGitPath(context, value) {
417
+ await assertGitRepository(context)
418
+ const relativePath = await normalizeGitOperationPath(context, value)
419
+ const status = await listGitStatus(context)
420
+ const changedFile = status.files.find((file) => file.path === relativePath || file.oldPath === relativePath)
421
+ if (!changedFile) return status
422
+
423
+ if (changedFile.status === 'untracked') {
424
+ const file = resolveWorkspacePath(changedFile.path, context)
425
+ await assertSafeWorkspacePath(file, context, { allowSensitive: true, ignoreMissing: true })
426
+ await fs.rm(file, { recursive: true, force: true })
427
+ return listGitStatus(context)
428
+ }
429
+
430
+ const targets = [...new Set([changedFile.path, changedFile.oldPath].filter(Boolean))]
431
+ if (changedFile.staged) {
432
+ await git(['restore', '--staged', '--', ...targets], context.workspaceRoot, { allowFailure: true })
433
+ }
434
+
435
+ if (changedFile.status === 'added') {
436
+ await git(['clean', '-fd', '--', changedFile.path], context.workspaceRoot)
437
+ return listGitStatus(context)
438
+ }
439
+
440
+ if (changedFile.oldPath) {
441
+ await git(['restore', '--worktree', '--', changedFile.oldPath], context.workspaceRoot, { allowFailure: true })
442
+ await git(['clean', '-fd', '--', changedFile.path], context.workspaceRoot, { allowFailure: true })
443
+ } else {
444
+ await git(['restore', '--worktree', '--', changedFile.path], context.workspaceRoot)
445
+ }
446
+ return listGitStatus(context)
447
+ }
448
+
449
+ async function restoreAllGitChanges(context) {
450
+ await assertGitRepository(context)
451
+ await git(['restore', '--staged', '--', '.'], context.workspaceRoot, { allowFailure: true })
452
+ await git(['restore', '--worktree', '--', '.'], context.workspaceRoot, { allowFailure: true })
453
+ await git(['clean', '-fd'], context.workspaceRoot)
454
+ return listGitStatus(context)
455
+ }
456
+
363
457
  async function pushGitBranch(context) {
364
458
  await assertGitRepository(context)
459
+ await assertAttachedGitHead(context)
365
460
  await git(['push'], context.workspaceRoot)
366
461
  return listGitStatus(context)
367
462
  }
@@ -398,7 +493,7 @@ function normalizeAiCommitMessage(text) {
398
493
  return message
399
494
  }
400
495
 
401
- async function generateGitCommitMessage(context, model, thinkingLevel = 'off') {
496
+ async function generateGitCommitMessage(context, model, thinkingLevel = 'off', includeUnstaged = false) {
402
497
  await assertGitRepository(context)
403
498
  if (!model) {
404
499
  const error = new Error('Please configure a default model first')
@@ -415,9 +510,19 @@ async function generateGitCommitMessage(context, model, thinkingLevel = 'off') {
415
510
 
416
511
  const cachedStat = (await git(['diff', '--cached', '--stat'], context.workspaceRoot, { allowFailure: true })).stdout.toString('utf8')
417
512
  const cachedDiff = (await git(['diff', '--cached'], context.workspaceRoot, { allowFailure: true })).stdout.toString('utf8')
418
- const worktreeStat = (await git(['diff', '--stat'], context.workspaceRoot, { allowFailure: true })).stdout.toString('utf8')
419
- const worktreeDiff = (await git(['diff'], context.workspaceRoot, { allowFailure: true })).stdout.toString('utf8')
420
- const files = status.files.map((file) => `- ${file.status}${file.staged ? ' staged' : ''}${file.unstaged ? ' unstaged' : ''}: ${file.oldPath ? `${file.oldPath} -> ` : ''}${file.path}`).join('\n')
513
+ const worktreeStat = includeUnstaged
514
+ ? (await git(['diff', '--stat'], context.workspaceRoot, { allowFailure: true })).stdout.toString('utf8')
515
+ : ''
516
+ const worktreeDiff = includeUnstaged
517
+ ? (await git(['diff'], context.workspaceRoot, { allowFailure: true })).stdout.toString('utf8')
518
+ : ''
519
+ const selectedFiles = includeUnstaged ? status.files : status.files.filter((file) => file.staged)
520
+ if (!selectedFiles.length) {
521
+ const error = new Error('No staged changes to summarize')
522
+ error.statusCode = 400
523
+ throw error
524
+ }
525
+ const files = selectedFiles.map((file) => `- ${file.status}${file.staged ? ' staged' : ''}${file.unstaged ? ' unstaged' : ''}: ${file.oldPath ? `${file.oldPath} -> ` : ''}${file.path}`).join('\n')
421
526
  const systemPrompt = `You generate Git commit messages.
422
527
  Return only the commit message, no Markdown, no explanation.
423
528
  Use Conventional Commit style when possible, for example: feat: add git tools summary.
@@ -752,10 +857,40 @@ async function contextFromGitBody(req) {
752
857
 
753
858
  async function handleGitGenerateCommitMessage(req, res) {
754
859
  const { context, body } = await contextFromGitBody(req)
755
- const message = await generateGitCommitMessage(context, body?.model, body?.thinkingLevel)
860
+ const message = await generateGitCommitMessage(context, body?.model, body?.thinkingLevel, Boolean(body?.includeUnstaged))
756
861
  sendJson(res, 200, { message })
757
862
  }
758
863
 
864
+ async function handleGitStage(req, res) {
865
+ const { context, body } = await contextFromGitBody(req)
866
+ sendJson(res, 200, await stageGitPath(context, body?.path))
867
+ }
868
+
869
+ async function handleGitStageAll(req, res) {
870
+ const { context } = await contextFromGitBody(req)
871
+ sendJson(res, 200, await stageAllGitChanges(context))
872
+ }
873
+
874
+ async function handleGitUnstage(req, res) {
875
+ const { context, body } = await contextFromGitBody(req)
876
+ sendJson(res, 200, await unstageGitPath(context, body?.path))
877
+ }
878
+
879
+ async function handleGitUnstageAll(req, res) {
880
+ const { context } = await contextFromGitBody(req)
881
+ sendJson(res, 200, await unstageAllGitChanges(context))
882
+ }
883
+
884
+ async function handleGitRestore(req, res) {
885
+ const { context, body } = await contextFromGitBody(req)
886
+ sendJson(res, 200, await restoreGitPath(context, body?.path))
887
+ }
888
+
889
+ async function handleGitRestoreAll(req, res) {
890
+ const { context } = await contextFromGitBody(req)
891
+ sendJson(res, 200, await restoreAllGitChanges(context))
892
+ }
893
+
759
894
  async function handleGitCommit(req, res) {
760
895
  const { context, body } = await contextFromGitBody(req)
761
896
  sendJson(res, 200, await commitGitChanges(context, body?.message, Boolean(body?.includeUnstaged)))
@@ -766,10 +901,24 @@ async function handleGitPush(req, res) {
766
901
  sendJson(res, 200, await pushGitBranch(context))
767
902
  }
768
903
 
904
+ export async function commitAndPushGitChanges(context, message, includeUnstaged) {
905
+ const committedStatus = await commitGitChanges(context, message, includeUnstaged)
906
+ try {
907
+ const pushedStatus = await pushGitBranch(context)
908
+ return { ...pushedStatus, committed: true, pushed: true }
909
+ } catch (error) {
910
+ return {
911
+ ...committedStatus,
912
+ committed: true,
913
+ pushed: false,
914
+ pushError: error?.message || 'Push failed',
915
+ }
916
+ }
917
+ }
918
+
769
919
  async function handleGitCommitAndPush(req, res) {
770
920
  const { context, body } = await contextFromGitBody(req)
771
- await commitGitChanges(context, body?.message, Boolean(body?.includeUnstaged))
772
- sendJson(res, 200, await pushGitBranch(context))
921
+ sendJson(res, 200, await commitAndPushGitChanges(context, body?.message, Boolean(body?.includeUnstaged)))
773
922
  }
774
923
 
775
924
  export async function handleWorkspaceApi(req, res, url) {
@@ -824,6 +973,30 @@ export async function handleGitApi(req, res, url) {
824
973
  await handleGitGenerateCommitMessage(req, res)
825
974
  return
826
975
  }
976
+ if (req.method === 'POST' && url.pathname === '/api/git/stage') {
977
+ await handleGitStage(req, res)
978
+ return
979
+ }
980
+ if (req.method === 'POST' && url.pathname === '/api/git/stage-all') {
981
+ await handleGitStageAll(req, res)
982
+ return
983
+ }
984
+ if (req.method === 'POST' && url.pathname === '/api/git/unstage') {
985
+ await handleGitUnstage(req, res)
986
+ return
987
+ }
988
+ if (req.method === 'POST' && url.pathname === '/api/git/unstage-all') {
989
+ await handleGitUnstageAll(req, res)
990
+ return
991
+ }
992
+ if (req.method === 'POST' && url.pathname === '/api/git/restore') {
993
+ await handleGitRestore(req, res)
994
+ return
995
+ }
996
+ if (req.method === 'POST' && url.pathname === '/api/git/restore-all') {
997
+ await handleGitRestoreAll(req, res)
998
+ return
999
+ }
827
1000
  if (req.method === 'POST' && url.pathname === '/api/git/commit') {
828
1001
  await handleGitCommit(req, res)
829
1002
  return
@@ -54,6 +54,7 @@ export const stores = new Set([
54
54
  'sessions-metadata',
55
55
  'scheduled-tasks',
56
56
  'custom-agents',
57
+ 'agent-profile-overrides',
57
58
  ])
58
59
 
59
60
  // --- In-memory session bucket index ---
@@ -84,6 +85,7 @@ const soloConfigStores = {
84
85
  settings: 'settings.json',
85
86
  mcp: 'mcp-servers.json',
86
87
  plugins: 'plugins.json',
88
+ 'agent-profile-overrides': 'agent-profile-overrides.json',
87
89
  }
88
90
 
89
91
  const sharedConfigGroups = {
@@ -184,6 +186,9 @@ export const storageDir = path.join(dataDir, 'storage')
184
186
  export const cacheDir = path.join(dataDir, 'cache')
185
187
  export const logsDir = path.join(dataDir, 'logs')
186
188
  export const userCommandsDir = path.join(dataDir, 'commands')
189
+ export const userAgentsDir = path.join(dataDir, 'agents')
190
+ export const builtinAgentsDir = path.join(userAgentsDir, 'builtin')
191
+ export const tempAgentsDir = path.join(cacheDir, 'global', 'tmp', 'agents')
187
192
 
188
193
  const quickForgeConfigFile = path.join(configDir, 'config.json')
189
194
  const configMigrationMarkerFile = path.join(configDir, '.layout-migrated')
@@ -878,6 +883,31 @@ export async function atomicProjectConfigUpdate(updateFn) {
878
883
  })
879
884
  }
880
885
 
886
+ export async function cleanOldTempAgents(retentionMs = 7 * 24 * 60 * 60 * 1000) {
887
+ try {
888
+ await fs.mkdir(tempAgentsDir, { recursive: true })
889
+ const cutoff = Date.now() - retentionMs
890
+ const sessionDirs = await fs.readdir(tempAgentsDir, { withFileTypes: true }).catch(() => [])
891
+ for (const sessionDir of sessionDirs) {
892
+ if (!sessionDir.isDirectory()) continue
893
+ const sessionPath = path.join(tempAgentsDir, sessionDir.name)
894
+ const runDirs = await fs.readdir(sessionPath, { withFileTypes: true }).catch(() => [])
895
+ await Promise.all(runDirs.map(async (runDir) => {
896
+ if (!runDir.isDirectory()) return
897
+ const runPath = path.join(sessionPath, runDir.name)
898
+ try {
899
+ const stat = await fs.stat(runPath)
900
+ if (stat.mtimeMs < cutoff) await fs.rm(runPath, { recursive: true, force: true })
901
+ } catch { /* ignore cleanup errors */ }
902
+ }))
903
+ const remaining = await fs.readdir(sessionPath).catch(() => [])
904
+ if (remaining.length === 0) await fs.rm(sessionPath, { recursive: true, force: true }).catch(() => {})
905
+ }
906
+ } catch {
907
+ // ignore cleanup errors
908
+ }
909
+ }
910
+
881
911
  // Cached storage-initialization promise. ensureStorage() is idempotent (mkdir
882
912
  // recursive, one-shot migration gated by a marker file, ensureJsonFile), so once
883
913
  // it succeeds we can skip the redundant syscalls (~20 per call) every later call
@@ -894,6 +924,9 @@ export function ensureStorage() {
894
924
  await Promise.all([
895
925
  fs.mkdir(path.join(cacheDir, 'global', 'llm'), { recursive: true }),
896
926
  fs.mkdir(path.join(cacheDir, 'global', 'tmp'), { recursive: true }),
927
+ fs.mkdir(tempAgentsDir, { recursive: true }),
928
+ fs.mkdir(userAgentsDir, { recursive: true }),
929
+ fs.mkdir(builtinAgentsDir, { recursive: true }),
897
930
  fs.mkdir(path.join(cacheDir, 'projects'), { recursive: true }),
898
931
  fs.mkdir(path.join(storageDir, 'conversations', 'global', 'sessions'), { recursive: true }),
899
932
  fs.mkdir(path.join(storageDir, 'conversations', 'projects'), { recursive: true }),
@@ -901,6 +934,7 @@ export function ensureStorage() {
901
934
  // they share the same file-tool capabilities as project conversations.
902
935
  fs.mkdir(path.join(dataDir, 'workspace'), { recursive: true }),
903
936
  cleanOldLogs(),
937
+ cleanOldTempAgents(),
904
938
  ])
905
939
 
906
940
  await migrateUnifiedConfig()
@@ -1,3 +1,8 @@
1
+ import { createHash } from 'node:crypto'
2
+ import { existsSync, promises as fs } from 'node:fs'
3
+ import path from 'node:path'
4
+ import { builtinAgentsDir, ensureStorage } from './storage.mjs'
5
+
1
6
  const commonSubagentRules = `
2
7
  You are a focused QuickForge subagent invoked by a parent coding assistant.
3
8
 
@@ -23,6 +28,9 @@ export const subagentDefinitions = [
23
28
  mode: 'subagent',
24
29
  description: 'A general-purpose agent for bounded complex multi-step implementation or broader independent work. It has full built-in workspace tool access, excluding MCP tools and Agent Skills, so it can modify files when needed. Prefer Explore for focused read-only repository discovery, source search, call-chain lookup, tests/docs discovery, and impact analysis.',
25
30
  allowedTools: ['read_file', 'grep_files', 'write_file', 'edit_file', 'run_command'],
31
+ capabilityPolicy: 'code-edit',
32
+ model: { mode: 'inherit' },
33
+ lifecycle: 'builtin',
26
34
  allowFileMutations: true,
27
35
  maxRuntimeMs: 30 * 60 * 1000,
28
36
  maxToolCalls: 300,
@@ -34,6 +42,9 @@ export const subagentDefinitions = [
34
42
  mode: 'subagent',
35
43
  description: 'The preferred subagent for focused read-only repository exploration, file discovery, source search, call-chain lookup, related tests/docs/wiki discovery, safe inspection commands, pattern lookup, and impact analysis before non-trivial implementation. It cannot modify files.',
36
44
  allowedTools: ['read_file', 'grep_files', 'run_command'],
45
+ capabilityPolicy: 'readonly-research',
46
+ model: { mode: 'inherit' },
47
+ lifecycle: 'builtin',
37
48
  allowFileMutations: false,
38
49
  maxRuntimeMs: 30 * 60 * 1000,
39
50
  maxToolCalls: 300,
@@ -43,6 +54,59 @@ export const subagentDefinitions = [
43
54
 
44
55
  const subagentByName = new Map(subagentDefinitions.map((definition) => [definition.name, definition]))
45
56
 
57
+ function markdownValue(value) {
58
+ return String(value ?? '').replace(/\r?\n/g, ' ').replace(/"/g, '\\"')
59
+ }
60
+
61
+ function builtinSubagentMarkdown(definition) {
62
+ const body = definition.systemPrompt || ''
63
+ const hash = createHash('sha256').update(JSON.stringify({
64
+ name: definition.name,
65
+ label: definition.label,
66
+ description: definition.description,
67
+ allowedTools: definition.allowedTools,
68
+ capabilityPolicy: definition.capabilityPolicy,
69
+ body,
70
+ })).digest('hex')
71
+ return `---
72
+ name: ${definition.name}
73
+ label: "${markdownValue(definition.label || definition.name)}"
74
+ description: "${markdownValue(definition.description || '')}"
75
+ source: builtin
76
+ lifecycle: builtin
77
+ managed: true
78
+ managedBy: quickforge
79
+ managedHash: ${hash}
80
+ enabled-as-subagent: true
81
+ capabilityPolicy: ${definition.capabilityPolicy || 'review-only'}
82
+ tools: ${definition.allowedTools.join(', ')}
83
+ model:
84
+ mode: inherit
85
+ max-runtime-ms: ${definition.maxRuntimeMs || 30 * 60 * 1000}
86
+ max-tool-calls: ${definition.maxToolCalls || 300}
87
+ ---
88
+ ${body}
89
+ `
90
+ }
91
+
92
+ export async function ensureBuiltinSubagentMarkdownFiles() {
93
+ await ensureStorage()
94
+ await fs.mkdir(builtinAgentsDir, { recursive: true })
95
+ await Promise.all(subagentDefinitions.map(async (definition) => {
96
+ const file = path.join(builtinAgentsDir, `${definition.name}.md`)
97
+ const content = builtinSubagentMarkdown(definition)
98
+ if (existsSync(file)) {
99
+ const current = await fs.readFile(file, 'utf8').catch(() => '')
100
+ if (current === content) return
101
+ }
102
+ await fs.writeFile(file, content, 'utf8')
103
+ }))
104
+ }
105
+
106
+ export function builtinSubagentProfilePath(name) {
107
+ return path.join(builtinAgentsDir, `${String(name || '').trim().toLowerCase()}.md`)
108
+ }
109
+
46
110
  export function listSubagentSummaries() {
47
111
  return subagentDefinitions.map(({ name, label, mode, description, allowedTools }) => ({
48
112
  name,
@@ -1,6 +1,19 @@
1
1
  import { Type } from 'typebox'
2
2
  import { loadSelectedGlobalSkills, loadSelectedProjectSkills, mergeSkills } from '../skills.mjs'
3
3
 
4
+ const temporarySubagentSchema = Type.Object({
5
+ type: Type.Literal('temporary'),
6
+ name: Type.String({ description: 'Temporary subagent name, lowercase ASCII identifier.' }),
7
+ label: Type.Optional(Type.String({ description: 'Optional display name.' })),
8
+ description: Type.Optional(Type.String({ description: 'Short description of the temporary subagent.' })),
9
+ instructions: Type.String({ description: 'System instructions for the temporary subagent.' }),
10
+ capabilityPolicy: Type.Optional(Type.String({ description: 'Capability policy, defaults to readonly-research.' })),
11
+ tools: Type.Optional(Type.Array(Type.String({ description: 'Requested built-in tool names, constrained by capabilityPolicy.' }))),
12
+ model: Type.Optional(Type.Any({ description: 'Model reference. Omit or use { mode: "inherit" } to inherit the parent model.' })),
13
+ maxRuntimeMs: Type.Optional(Type.Number({ description: 'Optional max runtime in milliseconds.' })),
14
+ maxToolCalls: Type.Optional(Type.Number({ description: 'Optional max tool-call budget.' })),
15
+ })
16
+
4
17
  // ---------------------------------------------------------------------------
5
18
  // Canonical workspace tool definitions.
6
19
  // These are the single source of truth for tool metadata (name, label,
@@ -17,7 +30,10 @@ export const subagentTool = {
17
30
  label: 'Run subagent',
18
31
  description: 'Delegate a bounded task to an enabled temporary Agent Profile. Prefer explore for focused read-only repository discovery before implementation decisions, including locating files, searching source, tracing call chains, finding related tests/docs/wiki pages, and impact analysis. Use general for bounded complex multi-step implementation or broader independent work. Custom profiles can also be enabled as subagents. Subagents are short-lived and do not receive MCP or Agent Skill tools.',
19
32
  parameters: Type.Object({
20
- subagent: Type.String({ description: 'Agent Profile name to invoke.' }),
33
+ subagent: Type.Union([
34
+ Type.String({ description: 'Agent Profile name to invoke.' }),
35
+ temporarySubagentSchema,
36
+ ], { description: 'Agent Profile name or an inline temporary subagent spec to create and run once.' }),
21
37
  task: Type.String({ description: 'Concrete, bounded task for the subagent. Do not delegate vague or open-ended work.' }),
22
38
  context: Type.Optional(Type.String({ description: 'Relevant context from the parent conversation or current plan. Keep this focused.' })),
23
39
  expectedOutput: Type.Optional(Type.String({ description: 'Optional output requirements for the subagent result.' })),
@@ -1 +0,0 @@
1
- import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{E as t,Ft as n,Nt as r,Rt as i,a,l as o,lt as s}from"./icons-DHoaB5uq.js";import{n as c}from"./react-vendor-BR9MG2D-.js";import{F as l,G as u,J as d,K as f,Y as p,nt as m,q as h,rt as g,st as _,tt as v}from"./index-CZb_mGGF.js";var y=e(i(),1),b=c();function x(){return{name:``,label:``,description:``,systemPrompt:``,allowedTools:[`read_file`,`grep_files`],maxRuntimeMs:`1800000`,maxToolCalls:`300`,enabledAsSubagent:!0}}function S(e){return{name:e.name,label:e.label,description:e.description??``,systemPrompt:e.systemPrompt??``,allowedTools:e.allowedTools??[],maxRuntimeMs:String(e.maxRuntimeMs??18e5),maxToolCalls:String(e.maxToolCalls??300),enabledAsSubagent:e.enabledAsSubagent}}function C(e){return{name:e.name.trim().toLowerCase(),label:e.label.trim(),description:e.description.trim(),systemPrompt:e.systemPrompt.trim(),allowedTools:e.allowedTools,maxRuntimeMs:Number(e.maxRuntimeMs||18e5),maxToolCalls:Number(e.maxToolCalls||300),enabledAsSubagent:e.enabledAsSubagent}}function w(e){return!!(e.name.trim()&&e.label.trim()&&e.allowedTools.length>0)}async function T(e,t){let n=await fetch(e,{...t,headers:{"content-type":`application/json`,...t?.headers}}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||`请求失败`);return r}function E(){let[e,i]=(0,y.useState)([]),[c,E]=(0,y.useState)([]),[D,O]=(0,y.useState)(!1),[k,A]=(0,y.useState)(null),[j,M]=(0,y.useState)(()=>x()),[N,P]=(0,y.useState)(!1),[F,I]=(0,y.useState)(``),[L,R]=(0,y.useState)(!1),[z,B]=(0,y.useState)(),[V,H]=(0,y.useState)(`off`),[U,W]=(0,y.useState)(``),[G,K]=(0,y.useState)(null);async function q(){let[e,t]=await Promise.all([T(`/api/agent-profiles`),T(`/api/agent-profiles/available-tools`)]);i(e.agents),E(t.tools)}(0,y.useEffect)(()=>{let e=!1;async function t(){try{let[t,n]=await Promise.all([T(`/api/agent-profiles`),T(`/api/agent-profiles/available-tools`)]);if(e)return;i(t.agents),E(n.tools)}catch(t){e||W(t instanceof Error?t.message:_(`requestFailed`))}}return t(),()=>{e=!0}},[]),(0,y.useEffect)(()=>{let e=!1;async function t(){try{let t=await h(),n=await f(t),r=await d(t),i=r.model??await p(t)??n[0];if(e)return;B(i),H(r.thinkingLevel??u(i))}catch{}}return t(),()=>{e=!0}},[]),(0,y.useEffect)(()=>{if(!G)return;let e=()=>K(null);return window.addEventListener(`click`,e),window.addEventListener(`blur`,e),()=>{window.removeEventListener(`click`,e),window.removeEventListener(`blur`,e)}},[G]);let J=(0,y.useMemo)(()=>e.find(e=>e.id===k)??null,[e,k]);function Y(e,t){M(n=>({...n,[e]:t}))}function X(e){M(t=>({...t,allowedTools:t.allowedTools.includes(e)?t.allowedTools.filter(t=>t!==e):[...t.allowedTools,e]}))}function Z(){A(null),M(x()),I(``),W(``),O(!0)}function Q(e){A(e.id),M(S(e)),I(``),W(``),O(!0)}function $(){N||L||(O(!1),A(null),M(x()),I(``))}async function ee(){let e=F.trim();if(!e){W(_(`aiFillAgentInputRequired`));return}if(!z){W(_(`aiFillAgentNoModel`));return}R(!0),W(``);try{let t=await T(`/api/agent-profiles/ai-fill`,{method:`POST`,body:JSON.stringify({instruction:e,model:z,thinkingLevel:V})});M(e=>({...e,name:t.agent.name,label:t.agent.label,description:t.agent.description,systemPrompt:t.agent.systemPrompt}))}catch(e){W(e instanceof Error?e.message:_(`aiFillAgentFailed`))}finally{R(!1)}}async function te(){if(w(j)){P(!0),W(``);try{let e=C(j);k?await T(`/api/agent-profiles/${encodeURIComponent(k)}`,{method:`PATCH`,body:JSON.stringify(e)}):await T(`/api/agent-profiles`,{method:`POST`,body:JSON.stringify(e)}),$(),await q()}catch(e){W(e instanceof Error?e.message:_(`requestFailed`))}finally{P(!1)}}}async function ne(e){if(e.builtin||e.readonly)return;let t=!e.enabledAsSubagent,n=e.enabledAsSubagent;i(n=>n.map(n=>n.id===e.id?{...n,enabledAsSubagent:t}:n)),K(null);try{await T(`/api/agent-profiles/${encodeURIComponent(e.id)}`,{method:`PATCH`,body:JSON.stringify({enabledAsSubagent:t})})}catch(t){i(t=>t.map(t=>t.id===e.id?{...t,enabledAsSubagent:n}:t)),W(t instanceof Error?t.message:_(`requestFailed`))}}async function re(e){if(!(e.builtin||e.readonly)&&await l({description:_(`confirmDeleteAgent`),confirmLabel:_(`confirmDelete`),cancelLabel:_(`cancel`),variant:`destructive`})){W(``);try{await T(`/api/agent-profiles/${encodeURIComponent(e.id)}`,{method:`DELETE`}),await q()}catch(e){W(e instanceof Error?e.message:_(`requestFailed`))}}}return D?(0,b.jsxs)(`div`,{className:`quickforge-settings-stack`,children:[(0,b.jsx)(`div`,{className:`quickforge-settings-heading`,children:(0,b.jsxs)(`h3`,{className:`quickforge-settings-title`,children:[_(J?`editAgent`:`createAgent`),(0,b.jsx)(v,{label:J?.readonly?_(`builtinAgentReadonly`):_(`agentsDescription`)})]})}),(0,b.jsxs)(`section`,{className:`quickforge-settings-section`,"aria-label":_(J?`editAgent`:`createAgent`),children:[(0,b.jsxs)(`div`,{className:`quickforge-settings-toolbar`,children:[(0,b.jsxs)(`button`,{className:`quickforge-settings-button quickforge-settings-button-secondary`,type:`button`,onClick:$,disabled:N||L,children:[(0,b.jsx)(n,{className:`mr-2 size-4`}),_(`back`)]}),(0,b.jsxs)(`div`,{className:`quickforge-settings-row-main`,children:[(0,b.jsx)(`div`,{className:`quickforge-settings-row-title`,children:_(J?`editAgent`:`createAgent`)}),J?.readonly?(0,b.jsx)(`div`,{className:`quickforge-settings-row-description`,children:_(`builtinAgentReadonly`)}):null]})]}),(0,b.jsx)(`div`,{className:`px-5 py-4`,children:(0,b.jsxs)(`div`,{className:`space-y-4`,children:[(0,b.jsxs)(`div`,{className:`rounded-2xl border border-border bg-muted/20 p-3`,children:[(0,b.jsxs)(`div`,{className:`mb-2 flex items-center gap-2 text-sm font-medium text-foreground`,children:[(0,b.jsx)(o,{className:`size-4 text-primary`}),_(`aiFillAgent`),(0,b.jsx)(v,{label:_(`aiFillAgentDescription`)})]}),(0,b.jsx)(`textarea`,{className:`min-h-20 w-full resize-y rounded-xl border border-input bg-background px-3 py-2 text-sm outline-none transition-colors placeholder:text-muted-foreground/65 focus:border-ring disabled:opacity-60`,value:F,disabled:!!J?.readonly||L,onChange:e=>I(e.target.value),placeholder:_(`aiFillAgentPlaceholder`)}),(0,b.jsx)(`div`,{className:`mt-2 flex justify-end`,children:(0,b.jsxs)(m,{variant:`outline`,size:`sm`,onClick:()=>void ee(),disabled:!!J?.readonly||L||!F.trim(),children:[(0,b.jsx)(o,{className:`mr-1 size-3.5`}),_(L?`aiFillAgentLoading`:`aiFillAgent`)]})})]}),(0,b.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,b.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[_(`agentName`),(0,b.jsx)(`input`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:j.name,disabled:!!J?.readonly,onChange:e=>Y(`name`,e.target.value),placeholder:`reviewer`})]}),(0,b.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[_(`agentLabel`),(0,b.jsx)(`input`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:j.label,disabled:!!J?.readonly,onChange:e=>Y(`label`,e.target.value),placeholder:_(`agentLabelPlaceholder`)})]})]}),(0,b.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[_(`agentDescription`),(0,b.jsx)(`input`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:j.description,disabled:!!J?.readonly,onChange:e=>Y(`description`,e.target.value)})]}),(0,b.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[_(`agentSystemPrompt`),(0,b.jsx)(`textarea`,{className:`mt-1 min-h-36 w-full resize-y rounded-xl border border-input bg-background px-3 py-2 text-sm outline-none focus:border-ring disabled:opacity-60`,value:j.systemPrompt,disabled:!!J?.readonly,onChange:e=>Y(`systemPrompt`,e.target.value)})]}),(0,b.jsxs)(`div`,{children:[(0,b.jsx)(`div`,{className:`mb-2 text-sm font-medium text-foreground`,children:_(`allowedTools`)}),(0,b.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-2`,children:c.map(e=>(0,b.jsxs)(`label`,{className:`flex items-start gap-2 rounded-xl border border-border bg-muted/20 p-3 text-sm disabled:opacity-60`,children:[(0,b.jsx)(`input`,{type:`checkbox`,className:`mt-1`,disabled:!!J?.readonly,checked:j.allowedTools.includes(e.name),onChange:()=>X(e.name)}),(0,b.jsxs)(`span`,{children:[(0,b.jsx)(`span`,{className:`font-medium text-foreground`,children:e.label}),(0,b.jsx)(`span`,{className:`ml-2 font-mono text-xs text-muted-foreground`,children:e.name}),e.riskLevel===`dangerous`?(0,b.jsx)(`span`,{className:`ml-2 rounded-full bg-amber-500/10 px-2 py-0.5 text-xs text-amber-700`,children:_(`highRiskTool`)}):null,(0,b.jsx)(`span`,{className:`mt-1 block text-xs text-muted-foreground`,children:e.description})]})]},e.name))})]}),(0,b.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,b.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[_(`maxRuntimeMs`),(0,b.jsx)(`input`,{type:`number`,className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:j.maxRuntimeMs,disabled:!!J?.readonly,onChange:e=>Y(`maxRuntimeMs`,e.target.value)})]}),(0,b.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[_(`maxToolCalls`),(0,b.jsx)(`input`,{type:`number`,className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:j.maxToolCalls,disabled:!!J?.readonly,onChange:e=>Y(`maxToolCalls`,e.target.value)})]})]}),(0,b.jsxs)(`label`,{className:`flex items-center gap-2 text-sm text-foreground`,children:[(0,b.jsx)(`input`,{type:`checkbox`,checked:j.enabledAsSubagent,disabled:!!J?.readonly,onChange:e=>Y(`enabledAsSubagent`,e.target.checked)}),_(`enabledAsSubagent`)]}),U?(0,b.jsx)(`div`,{className:`rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive`,children:U}):null]})}),(0,b.jsxs)(`div`,{className:`quickforge-settings-divider flex justify-end gap-2 px-5 py-4`,children:[(0,b.jsx)(m,{variant:`outline`,onClick:$,disabled:N||L,children:_(`cancel`)}),(0,b.jsx)(m,{onClick:te,disabled:N||L||!!J?.readonly||!w(j),children:_(`save`)})]})]})]}):(0,b.jsx)(`div`,{className:`quickforge-settings-stack`,children:(0,b.jsxs)(`section`,{className:`quickforge-settings-section`,"aria-label":_(`agentsTab`),children:[(0,b.jsxs)(`div`,{className:`quickforge-settings-toolbar`,children:[(0,b.jsxs)(`div`,{className:`quickforge-settings-row-main`,children:[(0,b.jsxs)(`div`,{className:`quickforge-settings-row-title`,children:[(0,b.jsx)(r,{className:`size-4 text-primary`}),_(`agentsTab`)]}),(0,b.jsx)(`div`,{className:`quickforge-settings-row-description`,children:_(`agentsDescription`)})]}),(0,b.jsx)(`button`,{className:`quickforge-settings-button quickforge-settings-button-primary`,type:`button`,onClick:Z,children:_(`createAgent`)})]}),U?(0,b.jsx)(`div`,{className:`quickforge-settings-alert quickforge-settings-warning-attached`,children:U}):null,e.length===0?(0,b.jsx)(`div`,{className:`quickforge-settings-empty-row`,children:_(`loading`)}):e.map(e=>(0,b.jsxs)(`div`,{className:`quickforge-settings-list-item`,children:[(0,b.jsxs)(`div`,{className:`quickforge-settings-list-item-main`,children:[(0,b.jsxs)(`div`,{className:`quickforge-settings-row-title`,children:[e.label,e.builtin?(0,b.jsx)(`span`,{className:`quickforge-settings-badge quickforge-settings-badge-info`,children:_(`builtinAgent`)}):null,(0,b.jsx)(`span`,{className:g(`quickforge-settings-badge`,e.enabledAsSubagent?`quickforge-settings-badge-success`:`quickforge-settings-badge-muted`),children:e.enabledAsSubagent?_(`enabled`):_(`disabled`)})]}),(0,b.jsx)(`div`,{className:`quickforge-settings-row-description quickforge-settings-mono`,children:e.name}),e.source&&!e.builtin?(0,b.jsxs)(`div`,{className:`quickforge-settings-row-description`,children:[e.source,e.relativePath?` · ${e.relativePath}`:``]}):null,(0,b.jsx)(`div`,{className:`quickforge-settings-row-description`,children:e.description||_(`noDescription`)}),(0,b.jsx)(`div`,{className:`quickforge-settings-meta`,children:e.allowedTools.map(e=>(0,b.jsx)(`code`,{className:`quickforge-settings-command-name`,children:e},e))}),(0,b.jsxs)(`div`,{className:`quickforge-settings-meta`,children:[(0,b.jsxs)(`span`,{className:`quickforge-settings-badge quickforge-settings-badge-muted`,children:[_(`maxRuntimeMs`),e.maxRuntimeMs??`-`]}),(0,b.jsxs)(`span`,{className:`quickforge-settings-badge quickforge-settings-badge-muted`,children:[_(`maxToolCalls`),e.maxToolCalls??`-`]})]})]}),(0,b.jsxs)(`div`,{className:`quickforge-settings-list-item-actions`,onClick:e=>e.stopPropagation(),children:[(0,b.jsxs)(`label`,{className:`quickforge-settings-switch`,"aria-disabled":e.builtin||e.readonly?`true`:`false`,title:e.enabledAsSubagent?_(`disableAsSubagent`):_(`enableAsSubagent`),children:[(0,b.jsx)(`input`,{type:`checkbox`,checked:e.enabledAsSubagent,disabled:e.builtin||e.readonly,onChange:()=>void ne(e)}),(0,b.jsx)(`span`,{"aria-hidden":`true`})]}),(0,b.jsxs)(`div`,{className:`relative`,children:[(0,b.jsx)(`button`,{className:`quickforge-settings-icon-action`,type:`button`,onClick:()=>K(G===e.id?null:e.id),title:_(`moreActions`),children:(0,b.jsx)(s,{className:`size-4`})}),G===e.id?(0,b.jsxs)(`div`,{className:`absolute right-0 z-20 mt-1 w-36 overflow-hidden rounded-xl border border-border bg-popover py-1 text-sm shadow-quickforge`,children:[(0,b.jsxs)(`button`,{className:`flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50`,disabled:e.builtin||e.readonly,onClick:()=>{K(null),Q(e)},children:[(0,b.jsx)(t,{className:`size-3.5`}),_(`editTask`)]}),(0,b.jsxs)(`button`,{className:`flex w-full items-center gap-2 px-3 py-2 text-left text-destructive hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50`,disabled:e.builtin||e.readonly,onClick:()=>{K(null),re(e)},children:[(0,b.jsx)(a,{className:`size-3.5`}),_(`delete`)]})]}):null]})]})]},e.id))]})})}export{E as AgentProfilesPage};