@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.
- package/README.md +10 -10
- package/dist/assets/AgentProfilesPage-5eqU0Cjk.js +1 -0
- package/dist/assets/ChatPanelHost-BNFeNRy-.js +244 -0
- package/dist/assets/{PluginsPage-3pZG7D7e.js → PluginsPage-C5OClNdT.js} +1 -1
- package/dist/assets/ScheduledTasksPage-yT10oaaM.js +2 -0
- package/dist/assets/{SettingsWorkspacePage-DWkPyGP_.js → SettingsWorkspacePage-DuXwIeLd.js} +346 -335
- package/dist/assets/SharedConversationPage-ClpUSa0F.js +1 -0
- package/dist/assets/TerminalDock-BmL-FeKv.js +2 -0
- package/dist/assets/WorkspaceInspector-yH23pXpj.js +13 -0
- package/dist/assets/abnfDiagram-VRR7QNED-Dl_Fvlqg.js +1 -0
- package/dist/assets/arc-Bhg4c0ht.js +1 -0
- package/dist/assets/architecture-TIHT7OUA-BgP-IMeo.js +1 -0
- package/dist/assets/architectureDiagram-ZJ3FMSHR-BuojN27B.js +36 -0
- package/dist/assets/array-CYkMkqnU.js +1 -0
- package/dist/assets/blockDiagram-677ZJIJ3-alDLbrR1.js +132 -0
- package/dist/assets/c4Diagram-LMCZKHZV-D1lKa4VT.js +10 -0
- package/dist/assets/channel-Rz20dKTK.js +1 -0
- package/dist/assets/chunk-2Q5K7J3B-Cb14ZF_k.js +1 -0
- package/dist/assets/chunk-32BRIVSS-CwWLNjMZ.js +1 -0
- package/dist/assets/chunk-52WLFC77-Be569m7d.js +10 -0
- package/dist/assets/chunk-5FCAYU7R-BSvoiWV1.js +1 -0
- package/dist/assets/chunk-5HE753X5-CRgsy8WS.js +1 -0
- package/dist/assets/chunk-5JV3BV7I-CdW477UR.js +1 -0
- package/dist/assets/chunk-5TONJI2A-CdtKzSmp.js +2 -0
- package/dist/assets/chunk-5VM5RSS4-BKjjssLZ.js +15 -0
- package/dist/assets/chunk-7BUUIJ7U-ByAXiXdn.js +1 -0
- package/dist/assets/chunk-BIQX33UG-DeG8SCT1.js +1 -0
- package/dist/assets/chunk-C7G6YPKG-D_zHT68Q.js +1 -0
- package/dist/assets/chunk-CQNSW5MT-CHpHoUSu.js +1 -0
- package/dist/assets/chunk-CYSBUYHQ-CRP50Vov.js +1 -0
- package/dist/assets/chunk-EMLP6XTP-DX6uQQ2v.js +1 -0
- package/dist/assets/chunk-EX3LRPZG-DAJRNkHR.js +231 -0
- package/dist/assets/chunk-FWX5IMBZ-XbM5yomC.js +2 -0
- package/dist/assets/chunk-HOUHSVGY-Ci0ljnb1.js +1 -0
- package/dist/assets/chunk-ICXQ74PX-Dy2WJ71z.js +2 -0
- package/dist/assets/chunk-JG7HCLWE-2PrkzHkt.js +2 -0
- package/dist/assets/chunk-JWPE2WC7-D32uacSM.js +1 -0
- package/dist/assets/chunk-KEIR6QF5-77hsebd7.js +161 -0
- package/dist/assets/chunk-MOJQB5TN-DIAFLtiS.js +88 -0
- package/dist/assets/chunk-MOZMSUNE-BbscOaEz.js +1 -0
- package/dist/assets/chunk-OGEWGWER-ivBVxzKe.js +1 -0
- package/dist/assets/chunk-OSBZ3O6U-frD0hkdx.js +1 -0
- package/dist/assets/chunk-PUDLZKDR-B-37Rxae.js +156 -0
- package/dist/assets/chunk-Q4XR5HBZ-Y90m2RTe.js +11 -0
- package/dist/assets/chunk-QBLGF6JB-DixBqslB.js +1 -0
- package/dist/assets/chunk-R7FJI6CG-DXS1UXn_.js +1 -0
- package/dist/assets/chunk-RYQCIY6F-n4JxhNd0.js +1 -0
- package/dist/assets/chunk-U6XO7XAA-yHlgxMmn.js +2 -0
- package/dist/assets/chunk-V7JOEXUC-Z6kuZfbl.js +206 -0
- package/dist/assets/chunk-VAUOI2AC-BOZvd2Sn.js +1 -0
- package/dist/assets/chunk-VR4S4FIN-Cs_SIQma.js +1 -0
- package/dist/assets/chunk-WYO6CB5R-CGExYbV-.js +128 -0
- package/dist/assets/chunk-XXDRQBXY-CD_EGCtT.js +1 -0
- package/dist/assets/chunk-Y2CYZVJY-CQltPwYk.js +1 -0
- package/dist/assets/chunk-YOTPTUD7-DopPXWSl.js +1 -0
- package/dist/assets/chunk-ZGVPDNZ5-CJHOeqMb.js +62 -0
- package/dist/assets/chunk-ZIRB5QZD-BBVc5mf6.js +32 -0
- package/dist/assets/classDiagram-OUVF2IWQ-C_ozTKCt.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-FOfFbbZF.js +1 -0
- package/dist/assets/cose-bilkent-JH36ORCC-DLjV-dTm.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-BzPhLtd5.js +1 -0
- package/dist/assets/cynefinDiagram-TSTJHNR4-BfH3zVS-.js +62 -0
- package/dist/assets/cytoscape.esm-DwdZnGOm.js +321 -0
- package/dist/assets/dagre-CY3D84Cl.js +1 -0
- package/dist/assets/dagre-VKFMJZFB-BIhwZCIZ.js +4 -0
- package/dist/assets/defaultLocale-Beh6XjaL.js +1 -0
- package/dist/assets/diagram-FQU43EPY-BHnzNM2w.js +3 -0
- package/dist/assets/diagram-G47NLZAW-C7-ZlmFT.js +24 -0
- package/dist/assets/diagram-NH7WQ7WH-C-P9Y7IR.js +24 -0
- package/dist/assets/diagram-OA4YK3LP-Ci5lZ_T5.js +30 -0
- package/dist/assets/diagram-WEI45ONY-C1xuK2bs.js +41 -0
- package/dist/assets/dist-DelZBZpK.js +1 -0
- package/dist/assets/ebnfDiagram-CCIWWBDH-DKQ5NWMD.js +1 -0
- package/dist/assets/erDiagram-Q63AITRT-HAWySadP.js +85 -0
- package/dist/assets/eventmodeling-45OFAUF4-BcgGbVJ-.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-dtkzcC8E.js +1 -0
- package/dist/assets/ganttDiagram-NO4QXBWP-C1hwIcOA.js +292 -0
- package/dist/assets/gitGraph-TEB2WS4Q-VByWboBU.js +1 -0
- package/dist/assets/gitGraphDiagram-IHSO6WYX-DYUYxs1R.js +106 -0
- package/dist/assets/graphlib-CAISHZh4.js +1 -0
- package/dist/assets/icons-C7j5jdKo.js +1 -0
- package/dist/assets/index-Qof73j0o.css +3 -0
- package/dist/assets/index-_ut7awrd.js +63 -0
- package/dist/assets/info-DKCQHKI2-CZeDLZxx.js +1 -0
- package/dist/assets/infoDiagram-FWYZ7A6U-CnIL6tv4.js +2 -0
- package/dist/assets/init-Rr1s_RiX.js +1 -0
- package/dist/assets/ishikawaDiagram-FXEZZL3T-B7WXNycQ.js +70 -0
- package/dist/assets/journeyDiagram-5HDEW3XC-D4HvioyY.js +139 -0
- package/dist/assets/kanban-definition-HUTT4EX6-CDs763eS.js +89 -0
- package/dist/assets/line-ikKkQiFp.js +1 -0
- package/dist/assets/linear-GqPyxQkN.js +1 -0
- package/dist/assets/map-CJwjCvwr.js +1 -0
- package/dist/assets/{mcp-servers-dialog-VbGvNekJ.js → mcp-servers-dialog-DC2Zfojn.js} +2 -2
- package/dist/assets/mermaid-parser.core-CoE8cccr.js +4 -0
- package/dist/assets/mermaid-renderer-BmbSAmpG.js +2 -0
- package/dist/assets/mermaid.core-DlFSL90j.js +11 -0
- package/dist/assets/mindmap-definition-LN4V7U3C-DLdq0K9C.js +96 -0
- package/dist/assets/{monaco-CzxeAjEw.js → monaco-CdOyGlWD.js} +1 -1
- package/dist/assets/ordinal-CibZU3PM.js +1 -0
- package/dist/assets/{otel-DQf3c6v2.js → otel-BNGM50C-.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-decrrxyX.js +1 -0
- package/dist/assets/path-BAQ3hXlG.js +1 -0
- package/dist/assets/pegDiagram-2B236MQR-Cm1kPRC2.js +1 -0
- package/dist/assets/{pi-ai-DjJwSQBQ.js → pi-ai-Cr3rpCXT.js} +2 -2
- package/dist/assets/{pi-web-ui-DqxtBlZL.js → pi-web-ui-DoRGtREc.js} +371 -371
- package/dist/assets/pie-RZYD4A2V-J1CasQZl.js +1 -0
- package/dist/assets/pieDiagram-ENE6RG2P-Df05wjjo.js +39 -0
- package/dist/assets/quadrantDiagram-ABIIQ3AL-BjCwGxJC.js +7 -0
- package/dist/assets/radar-I7S5WNFK-B-sZytUA.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-BSaqNPm8.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-lzZR7PxT.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-DQtz1Fh9.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-CpMsuGY3.js +1 -0
- package/dist/assets/railroadDiagram-RFXS5EU6-CTUvpKkv.js +1 -0
- package/dist/assets/{react-vendor-BR9MG2D-.js → react-vendor-VqdnQHHS.js} +1 -1
- package/dist/assets/requirementDiagram-TGXJPOKE-BU1kvEku.js +84 -0
- package/dist/assets/rough.esm-Djo4Abte.js +1 -0
- package/dist/assets/sankeyDiagram-HTMAVEWB-BHmvxVqH.js +40 -0
- package/dist/assets/sequenceDiagram-DBY2YBRQ-CTpwICgg.js +162 -0
- package/dist/assets/sizeCapture-X5ZJPWSS-BbLhGaa4.js +1 -0
- package/dist/assets/{skills-dialog-B0jNVGZW.js → skills-dialog-Mnj8fZwq.js} +1 -1
- package/dist/assets/src-B4Tvg3gT.js +1 -0
- package/dist/assets/stateDiagram-2N3HPSRC-BxuDwIgT.js +1 -0
- package/dist/assets/stateDiagram-v2-6OUMAXLB-BGLshxcY.js +1 -0
- package/dist/assets/swimlanes-5IMT3BWC-EkZsiylV.js +2 -0
- package/dist/assets/swimlanesDiagram-G3AALYLV-CqdqxA-b.js +8 -0
- package/dist/assets/timeline-definition-FHXFAJF6-C9ewOCTa.js +120 -0
- package/dist/assets/treeView-QDETBFTQ-Byb71heW.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-CMV1SJsq.js +1 -0
- package/dist/assets/vennDiagram-L72KCM5P-CR49Y4Lp.js +34 -0
- package/dist/assets/wardley-OPB4EBWU-DtCHtr2V.js +1 -0
- package/dist/assets/wardleyDiagram-EHGQE667-Cn0GgwWi.js +78 -0
- package/dist/assets/xychartDiagram-FW5EYKEG-DlmPNAvd.js +7 -0
- package/dist/index.html +8 -8
- package/package.json +2 -1
- package/server/agent-manager.mjs +174 -9
- package/server/agent-profile-files.mjs +86 -6
- package/server/agent-profile-schema.mjs +197 -0
- package/server/agent-profiles.mjs +172 -41
- package/server/auto-compaction.mjs +2 -2
- package/server/project-route-context.mjs +25 -0
- package/server/routes/agent-profiles.mjs +9 -3
- package/server/routes/project.mjs +6 -10
- package/server/routes/workspace.mjs +184 -11
- package/server/storage.mjs +34 -0
- package/server/subagents.mjs +64 -0
- package/server/tools/definitions.mjs +17 -1
- package/dist/assets/AgentProfilesPage-R7N7eDq5.js +0 -1
- package/dist/assets/ChatPanelHost-C4orgd5l.js +0 -242
- package/dist/assets/ScheduledTasksPage-BAqoK4xk.js +0 -2
- package/dist/assets/SharedConversationPage-C-1n_MmB.js +0 -1
- package/dist/assets/TerminalDock-B6KAXkIU.js +0 -2
- package/dist/assets/WorkspaceInspector-CkXyD_k4.js +0 -3
- package/dist/assets/WorkspaceReaderDialog-Dp2QAwBo.js +0 -1
- package/dist/assets/diff-line-counts-Dm5kKQOo.js +0 -10
- package/dist/assets/icons-DHoaB5uq.js +0 -1
- package/dist/assets/index-C77QCHE9.css +0 -3
- package/dist/assets/index-CZb_mGGF.js +0 -63
- package/dist/assets/useAppTheme-DsabFF9D.js +0 -1
- /package/dist/assets/{plugin-api-Ds5xGIL7.js → plugin-api-BbUkYm3Y.js} +0 -0
- /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
|
|
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:
|
|
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 =
|
|
419
|
-
|
|
420
|
-
|
|
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
|
|
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
|
package/server/storage.mjs
CHANGED
|
@@ -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()
|
package/server/subagents.mjs
CHANGED
|
@@ -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.
|
|
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};
|