@shawnstack/quickforge 1.3.18 → 1.3.20
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/bin/quickforge.mjs +258 -49
- package/dist/assets/anthropic-Bj3HAZgj.js +39 -0
- package/dist/assets/azure-openai-responses-IdZZrSrI.js +1 -0
- package/dist/assets/github-copilot-headers-CMb2BbzT.js +1 -0
- package/dist/assets/google-Brt_lS1J.js +1 -0
- package/dist/assets/{google-shared-XhYUKiGZ.js → google-shared-CLc4ziON.js} +3 -3
- package/dist/assets/google-vertex-B6HsoZ34.js +1 -0
- package/dist/assets/{index-Dm7aEWvT.js → index-D0CVLdX_.js} +525 -489
- package/dist/assets/index-D0W9hAl_.css +3 -0
- package/dist/assets/{mistral-DxhS4Wkn.js → mistral-CenXqwPz.js} +3 -3
- package/dist/assets/openai-codex-responses-D9ffGwbj.js +7 -0
- package/dist/assets/openai-completions-eWdeSGBG.js +5 -0
- package/dist/assets/openai-responses-Cavpmjeu.js +1 -0
- package/dist/assets/{openai-responses-shared-f_P3e1nz.js → openai-responses-shared-DF3ZGaUx.js} +5 -3
- package/dist/assets/transform-messages-CmnxG9RB.js +1 -0
- package/dist/index.html +2 -2
- package/node_modules/@anthropic-ai/sdk/CHANGELOG.md +34 -0
- package/node_modules/@anthropic-ai/sdk/bin/migration-config.json +185 -0
- package/node_modules/@anthropic-ai/sdk/package.json +1 -1
- package/node_modules/@anthropic-ai/sdk/resources/beta/beta.js +4 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/beta.mjs +4 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/files.js +5 -5
- package/node_modules/@anthropic-ai/sdk/resources/beta/files.mjs +5 -5
- package/node_modules/@anthropic-ai/sdk/resources/beta/index.js +11 -9
- package/node_modules/@anthropic-ai/sdk/resources/beta/index.mjs +1 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/index.js +11 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/index.mjs +5 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memories.js +130 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memories.mjs +126 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-stores.js +145 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-stores.mjs +140 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-versions.js +81 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-versions.mjs +77 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores.js +6 -0
- package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores.mjs +3 -0
- package/node_modules/@anthropic-ai/sdk/tools/memory/node.js +12 -5
- package/node_modules/@anthropic-ai/sdk/tools/memory/node.mjs +12 -5
- package/node_modules/@anthropic-ai/sdk/version.js +1 -1
- package/node_modules/@anthropic-ai/sdk/version.mjs +1 -1
- package/node_modules/@aws-sdk/client-bedrock-runtime/package.json +5 -5
- package/node_modules/@aws-sdk/core/package.json +2 -2
- package/node_modules/@aws-sdk/credential-provider-env/package.json +2 -2
- package/node_modules/@aws-sdk/credential-provider-http/dist-cjs/fromHttp/fromHttp.js +12 -6
- package/node_modules/@aws-sdk/credential-provider-http/dist-es/fromHttp/fromHttp.js +12 -6
- package/node_modules/@aws-sdk/credential-provider-http/package.json +3 -2
- package/node_modules/@aws-sdk/credential-provider-ini/package.json +9 -9
- package/node_modules/@aws-sdk/credential-provider-login/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-node/package.json +7 -7
- package/node_modules/@aws-sdk/credential-provider-process/package.json +2 -2
- package/node_modules/@aws-sdk/credential-provider-sso/package.json +4 -4
- package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +3 -3
- package/node_modules/@aws-sdk/middleware-websocket/package.json +2 -2
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/package.json +3 -3
- package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +1 -2
- package/node_modules/@aws-sdk/token-providers/package.json +3 -3
- package/node_modules/@aws-sdk/xml-builder/package.json +2 -2
- package/node_modules/@mariozechner/pi-agent-core/README.md +14 -0
- package/node_modules/@mariozechner/pi-agent-core/dist/agent-loop.js +9 -0
- package/node_modules/@mariozechner/pi-agent-core/dist/agent.js +1 -1
- package/node_modules/@mariozechner/pi-agent-core/package.json +2 -2
- package/node_modules/@mariozechner/pi-ai/README.md +20 -31
- package/node_modules/@mariozechner/pi-ai/dist/env-api-keys.js +7 -0
- package/node_modules/@mariozechner/pi-ai/dist/index.js +2 -0
- package/node_modules/@mariozechner/pi-ai/dist/models.generated.js +2420 -1213
- package/node_modules/@mariozechner/pi-ai/dist/models.js +28 -20
- package/node_modules/@mariozechner/pi-ai/dist/providers/amazon-bedrock.js +11 -11
- package/node_modules/@mariozechner/pi-ai/dist/providers/anthropic.js +43 -26
- package/node_modules/@mariozechner/pi-ai/dist/providers/azure-openai-responses.js +12 -6
- package/node_modules/@mariozechner/pi-ai/dist/providers/cloudflare.js +10 -3
- package/node_modules/@mariozechner/pi-ai/dist/providers/google-shared.js +4 -13
- package/node_modules/@mariozechner/pi-ai/dist/providers/google-vertex.js +4 -3
- package/node_modules/@mariozechner/pi-ai/dist/providers/google.js +4 -3
- package/node_modules/@mariozechner/pi-ai/dist/providers/mistral.js +8 -7
- package/node_modules/@mariozechner/pi-ai/dist/providers/openai-codex-responses.js +296 -41
- package/node_modules/@mariozechner/pi-ai/dist/providers/openai-completions.js +169 -153
- package/node_modules/@mariozechner/pi-ai/dist/providers/openai-responses-shared.js +14 -1
- package/node_modules/@mariozechner/pi-ai/dist/providers/openai-responses.js +22 -8
- package/node_modules/@mariozechner/pi-ai/dist/providers/register-builtins.js +0 -18
- package/node_modules/@mariozechner/pi-ai/dist/providers/simple-options.js +1 -0
- package/node_modules/@mariozechner/pi-ai/dist/session-resources.js +22 -0
- package/node_modules/@mariozechner/pi-ai/dist/utils/diagnostics.js +25 -0
- package/node_modules/@mariozechner/pi-ai/dist/utils/oauth/index.js +0 -10
- package/node_modules/@mariozechner/pi-ai/dist/utils/oauth/openai-codex.js +25 -14
- package/node_modules/@mariozechner/pi-ai/dist/utils/overflow.js +14 -0
- package/node_modules/@mariozechner/pi-ai/package.json +2 -6
- package/package.json +3 -3
- package/server/agent-manager.mjs +279 -12
- package/server/auto-compaction.mjs +1 -2
- package/server/conversation-compaction.mjs +0 -5
- package/server/index.mjs +1 -0
- package/server/routes/static.mjs +1 -0
- package/server/routes/tools.mjs +3 -1
- package/server/session-utils.mjs +6 -1
- package/server/share-store.mjs +27 -4
- package/server/subagents.mjs +101 -0
- package/server/system-prompt.mjs +30 -1
- package/server/tools/definitions.mjs +18 -0
- package/server/tools/index.mjs +1013 -911
- package/dist/assets/anthropic-Ck2DxOfr.js +0 -39
- package/dist/assets/azure-openai-responses-DIoz5q4Z.js +0 -1
- package/dist/assets/github-copilot-headers-CrI0CIJ7.js +0 -1
- package/dist/assets/google-Dau-4ve_.js +0 -1
- package/dist/assets/google-gemini-cli-DttMmbGb.js +0 -2
- package/dist/assets/google-vertex-BeukMl44.js +0 -1
- package/dist/assets/index-DgJVElbv.css +0 -3
- package/dist/assets/openai-codex-responses-X3sTzNAa.js +0 -7
- package/dist/assets/openai-completions-CRB9Vm0w.js +0 -5
- package/dist/assets/openai-responses-DXluu3oi.js +0 -1
- package/dist/assets/transform-messages-CV4kCtBB.js +0 -1
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/LICENSE +0 -201
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/README.md +0 -62
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-cjs/index.js +0 -156
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/constants.js +0 -2
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/fromEnvSigningName.js +0 -16
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/fromSso.js +0 -80
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/fromStatic.js +0 -8
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/getNewSsoOidcToken.js +0 -11
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/getSsoOidcClient.js +0 -10
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/index.js +0 -4
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/nodeProvider.js +0 -5
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/validateTokenExpiry.js +0 -7
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/validateTokenKey.js +0 -7
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/writeSSOTokenToFile.js +0 -8
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/package.json +0 -69
- package/node_modules/@mariozechner/pi-ai/dist/providers/google-gemini-cli.js +0 -779
- package/node_modules/@mariozechner/pi-ai/dist/utils/oauth/google-antigravity.js +0 -377
- package/node_modules/@mariozechner/pi-ai/dist/utils/oauth/google-gemini-cli.js +0 -482
|
@@ -70,7 +70,6 @@ function estimateMessageTokens(message) {
|
|
|
70
70
|
const parts = [message.role || '', contentToText(message.content)]
|
|
71
71
|
if (message.toolName) parts.push(message.toolName)
|
|
72
72
|
if (message.toolCallId) parts.push(message.toolCallId)
|
|
73
|
-
if (message.details !== undefined) parts.push(safeJson(message.details))
|
|
74
73
|
if (message.attachments !== undefined) parts.push(safeJson(message.attachments))
|
|
75
74
|
return estimateTextTokens(parts.join('\n'))
|
|
76
75
|
}
|
|
@@ -82,7 +81,7 @@ function estimateMessagesTokens(messages) {
|
|
|
82
81
|
function estimateMessagesChars(messages) {
|
|
83
82
|
return (Array.isArray(messages) ? messages : []).reduce((total, message) => {
|
|
84
83
|
if (!message || typeof message !== 'object') return total
|
|
85
|
-
return total + [message.role || '', contentToText(message.content), safeJson(message.
|
|
84
|
+
return total + [message.role || '', contentToText(message.content), safeJson(message.attachments)].join('\n').length
|
|
86
85
|
}, 0)
|
|
87
86
|
}
|
|
88
87
|
|
|
@@ -134,10 +134,6 @@ function formatMessageForTranscript(message, index) {
|
|
|
134
134
|
const attachments = attachmentsSummary(message)
|
|
135
135
|
if (attachments) bodyParts.push(attachments)
|
|
136
136
|
|
|
137
|
-
if (role === 'toolResult' && message?.details !== undefined) {
|
|
138
|
-
bodyParts.push(`[tool result details]\n${safeJson(message.details, 4000)}`)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
137
|
const body = bodyParts.join('\n\n').trim() || '[empty]'
|
|
142
138
|
return redactSensitive(`### Message ${index + 1}: ${role}\n${body}`)
|
|
143
139
|
}
|
|
@@ -158,7 +154,6 @@ function approximateMessageChars(message) {
|
|
|
158
154
|
if (message?.attachments) total += safeJson(message.attachments, 1000).length
|
|
159
155
|
if (message?.toolName) total += String(message.toolName).length
|
|
160
156
|
if (message?.toolCallId) total += String(message.toolCallId).length
|
|
161
|
-
if (message?.details) total += safeJson(message.details, 1000).length
|
|
162
157
|
return total
|
|
163
158
|
}
|
|
164
159
|
|
package/server/index.mjs
CHANGED
package/server/routes/static.mjs
CHANGED
|
@@ -13,6 +13,7 @@ function getContentType(filePath) {
|
|
|
13
13
|
'.mjs': 'text/javascript; charset=utf-8',
|
|
14
14
|
'.css': 'text/css; charset=utf-8',
|
|
15
15
|
'.json': 'application/json; charset=utf-8',
|
|
16
|
+
'.webmanifest': 'application/manifest+json; charset=utf-8',
|
|
16
17
|
'.svg': 'image/svg+xml',
|
|
17
18
|
'.png': 'image/png',
|
|
18
19
|
'.jpg': 'image/jpeg',
|
package/server/routes/tools.mjs
CHANGED
|
@@ -4,6 +4,8 @@ import { toolHandlers, loadSkillToolContext } from '../tools/index.mjs'
|
|
|
4
4
|
import { createSkillTools, workspaceTools } from '../tools/definitions.mjs'
|
|
5
5
|
import { projectContextFromId, readProjectConfig } from '../project-config.mjs'
|
|
6
6
|
|
|
7
|
+
const directRouteDisabledTools = new Set(['run_subagent'])
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* GET /api/tools — returns canonical tool definitions (no project context needed).
|
|
9
11
|
*/
|
|
@@ -68,7 +70,7 @@ export async function handleToolApi(req, res, url) {
|
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
const handler = toolHandlers[name]
|
|
71
|
-
if (!handler) {
|
|
73
|
+
if (!handler || directRouteDisabledTools.has(name)) {
|
|
72
74
|
const error = new Error(`Unknown tool: ${name}`)
|
|
73
75
|
error.statusCode = 404
|
|
74
76
|
throw error
|
package/server/session-utils.mjs
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { streamSimple } from '@mariozechner/pi-ai'
|
|
2
2
|
import { buildInstructionsPayload } from './project-config.mjs'
|
|
3
3
|
import { composeSystemPrompt } from './system-prompt.mjs'
|
|
4
|
+
import { listSubagentSummaries } from './subagents.mjs'
|
|
4
5
|
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
6
7
|
// System prompt
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
9
|
|
|
9
10
|
export async function buildSystemPrompt(projectId) {
|
|
10
|
-
|
|
11
|
+
const instructions = await buildInstructionsPayload(projectId)
|
|
12
|
+
return composeSystemPrompt({
|
|
13
|
+
...instructions,
|
|
14
|
+
subagents: listSubagentSummaries(),
|
|
15
|
+
})
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
// ---------------------------------------------------------------------------
|
package/server/share-store.mjs
CHANGED
|
@@ -370,6 +370,29 @@ export async function issueConversationShareToken(shareId) {
|
|
|
370
370
|
})
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
+
function messageTimestampMs(message) {
|
|
374
|
+
const timestamp = message?.timestamp
|
|
375
|
+
if (typeof timestamp === 'number' && Number.isFinite(timestamp)) return timestamp
|
|
376
|
+
if (typeof timestamp === 'string') {
|
|
377
|
+
const trimmed = timestamp.trim()
|
|
378
|
+
if (!trimmed) return undefined
|
|
379
|
+
const numeric = Number(trimmed)
|
|
380
|
+
if (Number.isFinite(numeric)) return numeric
|
|
381
|
+
const parsed = Date.parse(trimmed)
|
|
382
|
+
return Number.isNaN(parsed) ? undefined : parsed
|
|
383
|
+
}
|
|
384
|
+
return undefined
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function lastModifiedFromMessages(messages, fallback) {
|
|
388
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
389
|
+
const timestamp = messageTimestampMs(messages[index])
|
|
390
|
+
if (timestamp !== undefined) return new Date(timestamp).toISOString()
|
|
391
|
+
}
|
|
392
|
+
const fallbackMs = Date.parse(fallback)
|
|
393
|
+
return Number.isNaN(fallbackMs) ? new Date().toISOString() : new Date(fallbackMs).toISOString()
|
|
394
|
+
}
|
|
395
|
+
|
|
373
396
|
export async function rollbackSharedSessionMessages(record, rollbackMessageIndex) {
|
|
374
397
|
const { readSessionValue, writeSessionValue, atomicUpdate } = await import('./storage.mjs')
|
|
375
398
|
const { rollbackSessionMessages, rollbackStartIndexFromMessage } = await import('./agent-manager.mjs')
|
|
@@ -394,11 +417,11 @@ export async function rollbackSharedSessionMessages(record, rollbackMessageIndex
|
|
|
394
417
|
}
|
|
395
418
|
|
|
396
419
|
const nextMessages = messages.slice(0, rollbackIndex)
|
|
397
|
-
const
|
|
420
|
+
const lastModified = lastModifiedFromMessages(nextMessages, session.createdAt || session.lastModified)
|
|
398
421
|
await writeSessionValue(record.sessionId, {
|
|
399
422
|
...session,
|
|
400
423
|
messages: nextMessages,
|
|
401
|
-
lastModified
|
|
424
|
+
lastModified,
|
|
402
425
|
})
|
|
403
426
|
await atomicUpdate('sessions-metadata', (metadata) => {
|
|
404
427
|
const existing = metadata[record.sessionId]
|
|
@@ -406,13 +429,13 @@ export async function rollbackSharedSessionMessages(record, rollbackMessageIndex
|
|
|
406
429
|
metadata[record.sessionId] = {
|
|
407
430
|
...existing,
|
|
408
431
|
messageCount: nextMessages.length,
|
|
409
|
-
lastModified
|
|
432
|
+
lastModified,
|
|
410
433
|
preview: previewFromMessages(nextMessages),
|
|
411
434
|
}
|
|
412
435
|
}
|
|
413
436
|
return metadata
|
|
414
437
|
})
|
|
415
|
-
return { session: { ...session, messages: nextMessages, lastModified
|
|
438
|
+
return { session: { ...session, messages: nextMessages, lastModified }, rollbackIndex }
|
|
416
439
|
}
|
|
417
440
|
|
|
418
441
|
function textFromContent(content) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const commonSubagentRules = `
|
|
2
|
+
You are a focused QuickForge subagent invoked by a parent coding assistant.
|
|
3
|
+
|
|
4
|
+
Rules:
|
|
5
|
+
- Work only on the delegated task. Do not broaden scope.
|
|
6
|
+
- Do not ask the user questions directly. If required information is missing, report it under "Needs clarification".
|
|
7
|
+
- Prefer evidence from read_file and grep_files before making claims.
|
|
8
|
+
- Treat your findings as advisory; the parent assistant makes final decisions.
|
|
9
|
+
- Do not attempt to call or simulate other subagents.
|
|
10
|
+
- Keep the response concise and structured.
|
|
11
|
+
|
|
12
|
+
Return this structure when practical:
|
|
13
|
+
1. Summary
|
|
14
|
+
2. Work performed or findings with evidence, including file paths when relevant
|
|
15
|
+
3. Risks or unknowns
|
|
16
|
+
4. Suggested next steps
|
|
17
|
+
`.trim()
|
|
18
|
+
|
|
19
|
+
export const subagentDefinitions = [
|
|
20
|
+
{
|
|
21
|
+
name: 'general',
|
|
22
|
+
label: 'General',
|
|
23
|
+
mode: 'subagent',
|
|
24
|
+
description: 'A general-purpose agent for researching complex problems and executing multi-step tasks. It has full built-in workspace tool access, excluding MCP tools and Agent Skills, so it can modify files when needed. Use it for substantial independent work units, including parallelizable work.',
|
|
25
|
+
allowedTools: ['read_file', 'grep_files', 'write_file', 'edit_file', 'run_command'],
|
|
26
|
+
allowFileMutations: true,
|
|
27
|
+
maxRuntimeMs: 30 * 60 * 1000,
|
|
28
|
+
maxToolCalls: 300,
|
|
29
|
+
systemPrompt: `You are General, a general-purpose subagent for complex research and multi-step implementation tasks. You may inspect, edit, write files, and run commands using the built-in workspace tools when needed. You do not have MCP tools or Agent Skills. Make focused, minimal changes that satisfy the delegated task, and verify your changes when appropriate.`,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'explore',
|
|
33
|
+
label: 'Explore',
|
|
34
|
+
mode: 'subagent',
|
|
35
|
+
description: 'A fast read-only agent for targeted exploration and focused questions. It cannot modify files. Use it to quickly find relevant files, search keywords, identify patterns, or summarize findings.',
|
|
36
|
+
allowedTools: ['read_file', 'grep_files'],
|
|
37
|
+
allowFileMutations: false,
|
|
38
|
+
maxRuntimeMs: 30 * 60 * 1000,
|
|
39
|
+
maxToolCalls: 300,
|
|
40
|
+
systemPrompt: `You are Explore, a fast read-only exploration subagent. Use read_file and grep_files to locate relevant files, search keywords, identify patterns, and answer focused questions. You cannot modify files or run commands.`,
|
|
41
|
+
},
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
const subagentByName = new Map(subagentDefinitions.map((definition) => [definition.name, definition]))
|
|
45
|
+
|
|
46
|
+
export function listSubagentSummaries() {
|
|
47
|
+
return subagentDefinitions.map(({ name, label, mode, description, allowedTools }) => ({
|
|
48
|
+
name,
|
|
49
|
+
label,
|
|
50
|
+
mode,
|
|
51
|
+
description,
|
|
52
|
+
allowedTools: [...allowedTools],
|
|
53
|
+
}))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function getSubagentDefinition(name) {
|
|
57
|
+
return subagentByName.get(String(name || '').trim().toLowerCase()) || null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function composeSubagentSystemPrompt({ definition, parentSystemPrompt, projectContext }) {
|
|
61
|
+
const workspaceLines = []
|
|
62
|
+
if (projectContext?.project?.name) workspaceLines.push(`- Project name: ${projectContext.project.name}`)
|
|
63
|
+
if (projectContext?.workspaceRoot) workspaceLines.push(`- Workspace root: ${projectContext.workspaceRoot}`)
|
|
64
|
+
if (projectContext?.project?.id) workspaceLines.push(`- Project ID: ${projectContext.project.id}`)
|
|
65
|
+
|
|
66
|
+
return [
|
|
67
|
+
parentSystemPrompt || '',
|
|
68
|
+
'<subagent_instructions>',
|
|
69
|
+
`Subagent: ${definition.label || definition.name}`,
|
|
70
|
+
`Mode: ${definition.mode || 'subagent'}`,
|
|
71
|
+
`Description: ${definition.description}`,
|
|
72
|
+
'',
|
|
73
|
+
definition.systemPrompt,
|
|
74
|
+
'',
|
|
75
|
+
commonSubagentRules,
|
|
76
|
+
'',
|
|
77
|
+
'Tool constraints:',
|
|
78
|
+
`- Allowed tools: ${definition.allowedTools.join(', ')}`,
|
|
79
|
+
'- MCP tools and Agent Skill tools are not available to subagents.',
|
|
80
|
+
'- run_subagent is not available to subagents.',
|
|
81
|
+
definition.allowFileMutations
|
|
82
|
+
? '- File modification tools are available when needed, subject to the parent session approval/YOLO policy.'
|
|
83
|
+
: '- This subagent is read-only. Do not modify files or run commands.',
|
|
84
|
+
workspaceLines.length ? `\nWorkspace context:\n${workspaceLines.join('\n')}` : '',
|
|
85
|
+
'</subagent_instructions>',
|
|
86
|
+
].filter(Boolean).join('\n')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function formatSubagentTask(params) {
|
|
90
|
+
const task = String(params?.task || '').trim()
|
|
91
|
+
const context = String(params?.context || '').trim()
|
|
92
|
+
const expectedOutput = String(params?.expectedOutput || '').trim()
|
|
93
|
+
|
|
94
|
+
return [
|
|
95
|
+
'<delegated_task>',
|
|
96
|
+
task,
|
|
97
|
+
'</delegated_task>',
|
|
98
|
+
context ? `\n<context>\n${context}\n</context>` : '',
|
|
99
|
+
expectedOutput ? `\n<expected_output>\n${expectedOutput}\n</expected_output>` : '',
|
|
100
|
+
].filter(Boolean).join('\n')
|
|
101
|
+
}
|
package/server/system-prompt.mjs
CHANGED
|
@@ -6,7 +6,10 @@ For project tasks:
|
|
|
6
6
|
- Make surgical changes only. Do not refactor unrelated code.
|
|
7
7
|
- Match existing style.
|
|
8
8
|
- For multi-step work, use a brief plan.
|
|
9
|
-
-
|
|
9
|
+
- Before changing files, gather sufficient context: relevant files, entry points or call chains, existing patterns, tests or validation commands, and docs/wiki impact.
|
|
10
|
+
- Before taking action, confirm with the user.
|
|
11
|
+
- Unless the change is trivial and localized to an already-known file, use Explore first for read-only repository research; prefer Explore for broad searches, pattern lookup, impact analysis, and locating related tests, docs, or build scripts.
|
|
12
|
+
- For complex multi-step work, use General only for bounded assistance; the parent assistant remains responsible for final decisions, minimal edits, and verification.
|
|
10
13
|
- Make minimal, focused changes.
|
|
11
14
|
- Prefer dedicated workspace tools for reading, editing, and searching files.
|
|
12
15
|
- If dedicated tools are unavailable or insufficient, use the shell/command tool.
|
|
@@ -36,6 +39,31 @@ function formatSkillCatalogItem(skill) {
|
|
|
36
39
|
return ` <skill>\n${details.join('\n')}\n </skill>`
|
|
37
40
|
}
|
|
38
41
|
|
|
42
|
+
function formatSubagentCatalogItem(subagent) {
|
|
43
|
+
const details = [
|
|
44
|
+
` <name>${escapeXml(subagent.name)}</name>`,
|
|
45
|
+
` <description>${escapeXml(subagent.description)}</description>`,
|
|
46
|
+
]
|
|
47
|
+
if (Array.isArray(subagent.allowedTools)) details.push(` <allowed_tools>${escapeXml(subagent.allowedTools.join(', '))}</allowed_tools>`)
|
|
48
|
+
return ` <subagent>\n${details.join('\n')}\n </subagent>`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function appendSubagentCatalog(parts, subagents) {
|
|
52
|
+
if (!Array.isArray(subagents) || subagents.length === 0) return
|
|
53
|
+
|
|
54
|
+
const subagentParts = subagents.map(formatSubagentCatalogItem)
|
|
55
|
+
parts.push(`
|
|
56
|
+
<available_subagents>
|
|
57
|
+
The run_subagent tool can delegate work to one of QuickForge's built-in temporary subagents.
|
|
58
|
+
|
|
59
|
+
Use Explore for fast read-only codebase search, pattern lookup, and repository questions. Use General for complex research or multi-step implementation work that may need built-in workspace tools, including file edits. Keep delegation concrete and include relevant context. Treat subagent output as advisory; you remain responsible for the final answer.
|
|
60
|
+
|
|
61
|
+
Subagents are short-lived, cannot call other subagents, and do not receive MCP tools or Agent Skill tools. General may modify files subject to the parent session approval/YOLO policy; Explore is read-only.
|
|
62
|
+
|
|
63
|
+
${subagentParts.join('\n')}
|
|
64
|
+
</available_subagents>`)
|
|
65
|
+
}
|
|
66
|
+
|
|
39
67
|
function appendSkillsCatalog(parts, skills) {
|
|
40
68
|
if (!Array.isArray(skills) || skills.length === 0) return
|
|
41
69
|
|
|
@@ -86,6 +114,7 @@ ${lines.join('\n')}
|
|
|
86
114
|
]
|
|
87
115
|
|
|
88
116
|
appendSkillsCatalog(parts, skills)
|
|
117
|
+
appendSubagentCatalog(parts, instructions.subagents)
|
|
89
118
|
|
|
90
119
|
return parts.join('\n')
|
|
91
120
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type } from 'typebox'
|
|
2
2
|
import { loadSelectedGlobalSkills, loadSelectedProjectSkills, mergeSkills } from '../skills.mjs'
|
|
3
|
+
import { listSubagentSummaries } from '../subagents.mjs'
|
|
3
4
|
|
|
4
5
|
// ---------------------------------------------------------------------------
|
|
5
6
|
// Canonical workspace tool definitions.
|
|
@@ -12,7 +13,24 @@ import { loadSelectedGlobalSkills, loadSelectedProjectSkills, mergeSkills } from
|
|
|
12
13
|
// it to a handler, and the frontend can fetch definitions from /api/tools.
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
|
|
16
|
+
const subagentNames = listSubagentSummaries().map((subagent) => subagent.name)
|
|
17
|
+
|
|
18
|
+
export const subagentTool = {
|
|
19
|
+
name: 'run_subagent',
|
|
20
|
+
label: 'Run subagent',
|
|
21
|
+
description: 'Delegate a bounded task to a built-in temporary subagent. Use general for complex multi-step work with full built-in workspace tools, or explore for fast read-only lookup and focused analysis. Subagents are short-lived and do not receive MCP or Agent Skill tools.',
|
|
22
|
+
parameters: Type.Object({
|
|
23
|
+
subagent: subagentNames.length
|
|
24
|
+
? Type.String({ enum: subagentNames, description: 'Specialized subagent to invoke.' })
|
|
25
|
+
: Type.String({ description: 'Specialized subagent to invoke.' }),
|
|
26
|
+
task: Type.String({ description: 'Concrete, bounded task for the subagent. Do not delegate vague or open-ended work.' }),
|
|
27
|
+
context: Type.Optional(Type.String({ description: 'Relevant context from the parent conversation or current plan. Keep this focused.' })),
|
|
28
|
+
expectedOutput: Type.Optional(Type.String({ description: 'Optional output requirements for the subagent result.' })),
|
|
29
|
+
}),
|
|
30
|
+
}
|
|
31
|
+
|
|
15
32
|
export const workspaceTools = [
|
|
33
|
+
subagentTool,
|
|
16
34
|
{
|
|
17
35
|
name: 'read_file',
|
|
18
36
|
label: 'Read file',
|