@multiclawcli/core 0.3.2 → 0.5.0

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 (81) hide show
  1. package/dist/agents/arch-design-reviewer.d.ts +4 -0
  2. package/dist/agents/architect.d.ts +4 -0
  3. package/dist/agents/backend-developer.d.ts +4 -0
  4. package/dist/agents/code-reviewer.d.ts +4 -0
  5. package/dist/agents/devops.d.ts +4 -0
  6. package/dist/agents/frontend-developer.d.ts +4 -0
  7. package/dist/agents/index.d.ts +26 -0
  8. package/dist/agents/leader.d.ts +4 -0
  9. package/dist/agents/product-manager.d.ts +4 -0
  10. package/dist/agents/reviewer.d.ts +4 -0
  11. package/dist/agents/tester.d.ts +4 -0
  12. package/dist/agents/ui-design-reviewer.d.ts +4 -0
  13. package/dist/agents/ui-designer.d.ts +4 -0
  14. package/dist/config/defineConfig.d.ts +2 -0
  15. package/dist/event/EventBus.d.ts +7 -0
  16. package/dist/index.d.ts +11 -0
  17. package/dist/index.js +2207 -811
  18. package/dist/orchestrator/AgentRunner.d.ts +19 -0
  19. package/dist/orchestrator/ContextManager.d.ts +7 -0
  20. package/dist/orchestrator/Orchestrator.d.ts +109 -0
  21. package/dist/orchestrator/StageBuilder.d.ts +2 -0
  22. package/dist/providers/bridge.d.ts +29 -0
  23. package/dist/providers/catalog.d.ts +73 -0
  24. package/dist/providers/detect.d.ts +34 -0
  25. package/dist/providers/index.d.ts +4 -0
  26. package/dist/providers/resolve.d.ts +26 -0
  27. package/dist/runtime/AcpRuntime.d.ts +17 -0
  28. package/dist/runtime/RuntimeBase.d.ts +6 -0
  29. package/dist/runtime/index.d.ts +12 -0
  30. package/dist/types/agent.d.ts +96 -0
  31. package/dist/types/config.d.ts +126 -0
  32. package/dist/types/event.d.ts +115 -0
  33. package/dist/types/index.d.ts +5 -0
  34. package/dist/types/leader.d.ts +36 -0
  35. package/dist/types/runtime.d.ts +146 -0
  36. package/dist/utils/abort.d.ts +4 -0
  37. package/dist/utils/conversation.d.ts +29 -0
  38. package/dist/utils/errors.d.ts +16 -0
  39. package/dist/utils/json.d.ts +10 -0
  40. package/dist/utils/leaderDecision.d.ts +17 -0
  41. package/dist/utils/prompt.d.ts +1 -0
  42. package/dist/utils/resumePlan.d.ts +35 -0
  43. package/dist/utils/retry.d.ts +36 -0
  44. package/dist/utils/timeout.d.ts +1 -0
  45. package/dist/utils/toolSummary.d.ts +13 -0
  46. package/dist/utils/upstream.d.ts +69 -0
  47. package/package.json +18 -4
  48. package/src/agents/arch-design-reviewer.ts +0 -45
  49. package/src/agents/architect.ts +0 -72
  50. package/src/agents/backend-developer.ts +0 -54
  51. package/src/agents/code-reviewer.ts +0 -54
  52. package/src/agents/devops.ts +0 -59
  53. package/src/agents/frontend-developer.ts +0 -56
  54. package/src/agents/index.ts +0 -27
  55. package/src/agents/leader.ts +0 -37
  56. package/src/agents/product-manager.ts +0 -43
  57. package/src/agents/reviewer.ts +0 -47
  58. package/src/agents/tester.ts +0 -53
  59. package/src/agents/ui-design-reviewer.ts +0 -48
  60. package/src/agents/ui-designer.ts +0 -61
  61. package/src/config/defineConfig.ts +0 -4
  62. package/src/event/EventBus.ts +0 -19
  63. package/src/index.ts +0 -8
  64. package/src/orchestrator/AgentRunner.ts +0 -104
  65. package/src/orchestrator/ContextManager.ts +0 -11
  66. package/src/orchestrator/Orchestrator.ts +0 -180
  67. package/src/orchestrator/StageBuilder.ts +0 -30
  68. package/src/runtime/ClaudeRuntime.ts +0 -41
  69. package/src/runtime/RuntimeBase.ts +0 -7
  70. package/src/runtime/index.ts +0 -20
  71. package/src/types/agent.ts +0 -48
  72. package/src/types/config.ts +0 -26
  73. package/src/types/event.ts +0 -21
  74. package/src/types/index.ts +0 -4
  75. package/src/types/runtime.ts +0 -23
  76. package/src/utils/prompt.ts +0 -24
  77. package/src/utils/retry.ts +0 -24
  78. package/src/utils/timeout.ts +0 -16
  79. package/tsconfig.build.json +0 -9
  80. package/tsconfig.json +0 -5
  81. package/tsup.config.ts +0 -10
@@ -1,104 +0,0 @@
1
- import * as fs from "fs"
2
- import * as path from "path"
3
- import type { AgentDefinition, AgentResult } from "../types/agent"
4
- import { runtimeRegistry } from "../runtime"
5
- import { EventBus } from "../event/EventBus"
6
- import { ContextManager } from "./ContextManager"
7
- import { withRetry } from "../utils/retry"
8
- import { withTimeout } from "../utils/timeout"
9
- import { renderPrompt } from "../utils/prompt"
10
-
11
- export class AgentRunner {
12
- constructor(
13
- private eventBus: EventBus,
14
- private contextManager: ContextManager,
15
- private globalWorkDir: string
16
- ) {}
17
-
18
- async run(agent: AgentDefinition): Promise<AgentResult> {
19
- const startTime = new Date()
20
- const workDir = agent.workDir
21
- ? path.resolve(this.globalWorkDir, agent.workDir)
22
- : this.globalWorkDir
23
- fs.mkdirSync(workDir, { recursive: true })
24
-
25
- const runtime = runtimeRegistry.get(agent.runtime ?? "claude")
26
- const retries = agent.retries ?? 0
27
- const timeout = agent.timeout ?? 600_000
28
- const tools = agent.tools ?? ["Read", "Write", "Bash"]
29
- const model = agent.model ?? agent.models?.[agent.runtime ?? "claude"]
30
-
31
- this.eventBus.emit({
32
- type: "agent:start",
33
- timestamp: new Date().toISOString(),
34
- payload: { agentId: agent.id, agentName: agent.name, icon: agent.icon, model, taskTitle: agent.taskTitle },
35
- })
36
-
37
- let attempts = 0
38
- let output = ""
39
-
40
- try {
41
- output = await withTimeout(
42
- (signal) => withRetry(async (attempt) => {
43
- attempts = attempt
44
- const prompt = await renderPrompt(
45
- agent.taskPrompt,
46
- this.contextManager.snapshot(),
47
- this.globalWorkDir
48
- )
49
- return (await runtime.execute({
50
- agentId: agent.id,
51
- systemPrompt: agent.systemPrompt,
52
- prompt, tools, workDir, timeout, signal,
53
- model,
54
- onOutput: (chunk) => {
55
- this.eventBus.emit({
56
- type: "agent:output",
57
- timestamp: new Date().toISOString(),
58
- payload: { agentId: agent.id, chunk },
59
- })
60
- },
61
- })).output
62
- }, retries, (attempt, err) => {
63
- this.eventBus.emit({
64
- type: "agent:retrying",
65
- timestamp: new Date().toISOString(),
66
- payload: {
67
- agentId: agent.id,
68
- agentName: agent.name,
69
- attempt,
70
- maxAttempts: retries + 1,
71
- error: (err as Error).message,
72
- },
73
- })
74
- }),
75
- timeout,
76
- agent.name
77
- )
78
-
79
- const endTime = new Date()
80
- const result: AgentResult = {
81
- agentId: agent.id, agentName: agent.name, status: "success",
82
- output, duration: endTime.getTime() - startTime.getTime(),
83
- attempts, startTime: startTime.toISOString(), endTime: endTime.toISOString(),
84
- }
85
- this.eventBus.emit({
86
- type: "agent:complete", timestamp: new Date().toISOString(), payload: result,
87
- })
88
- return result
89
- } catch (err) {
90
- const endTime = new Date()
91
- const result: AgentResult = {
92
- agentId: agent.id, agentName: agent.name, status: "failed",
93
- output, duration: endTime.getTime() - startTime.getTime(),
94
- attempts, error: (err as Error).message,
95
- startTime: startTime.toISOString(), endTime: endTime.toISOString(),
96
- }
97
- this.eventBus.emit({
98
- type: "agent:failed", timestamp: new Date().toISOString(), payload: result,
99
- })
100
- return result
101
- }
102
- }
103
- }
104
-
@@ -1,11 +0,0 @@
1
- export class ContextManager {
2
- private store = new Map<string, string>()
3
- constructor(initial?: Record<string, string>) {
4
- if (initial) for (const [k, v] of Object.entries(initial)) this.store.set(k, v)
5
- }
6
- set(key: string, value: string) { this.store.set(key, value) }
7
- get(key: string) { return this.store.get(key) }
8
- snapshot(): Record<string, string> {
9
- return Object.fromEntries(this.store.entries())
10
- }
11
- }
@@ -1,180 +0,0 @@
1
- import * as fs from "fs"
2
- import * as path from "path"
3
- import type { MultiClawConfig, OrchestratorResult } from "../types/config"
4
- import type { AgentDefinition, AgentResult, TaskPlan } from "../types/agent"
5
- import type { StageInfo } from "../types/event"
6
- import { EventBus } from "../event/EventBus"
7
- import { ContextManager } from "./ContextManager"
8
- import { AgentRunner } from "./AgentRunner"
9
- import { buildStages } from "./StageBuilder"
10
-
11
- export class Orchestrator {
12
- readonly eventBus = new EventBus()
13
- private contextManager: ContextManager
14
- private runner: AgentRunner
15
- private workDir: string
16
-
17
- constructor(private config: MultiClawConfig) {
18
- this.workDir = path.resolve(config.workDir)
19
- fs.mkdirSync(this.workDir, { recursive: true })
20
- this.contextManager = new ContextManager(config.context)
21
- this.runner = new AgentRunner(this.eventBus, this.contextManager, this.workDir)
22
- }
23
-
24
- async run(): Promise<OrchestratorResult> {
25
- const startTime = new Date()
26
- const agents = await this.resolveAgents()
27
- this.contextManager.set(
28
- "pipelineAgents",
29
- agents.map((a) => `${a.id}: ${a.name}`).join("\n")
30
- )
31
-
32
- let stages = buildStages(agents)
33
-
34
- const getModel = (agent: AgentDefinition): string | undefined => {
35
- return agent.model ?? agent.models?.[agent.runtime ?? "claude"]
36
- }
37
-
38
- const stageInfos: StageInfo[] = stages.map((stage, i) => ({
39
- stageIndex: i,
40
- agents: stage.map((a) => ({ id: a.id, name: a.name, icon: a.icon, model: getModel(a), taskTitle: a.taskTitle })),
41
- }))
42
-
43
- this.eventBus.emit({
44
- type: "orchestration:start",
45
- timestamp: new Date().toISOString(),
46
- payload: { name: this.config.name, requirement: this.config.context?.requirement, totalAgents: agents.length, stages: stageInfos },
47
- })
48
-
49
- const results: AgentResult[] = []
50
- let allSuccess = true
51
-
52
- for (let i = 0; i < stages.length; i++) {
53
- const stage = stages[i]
54
-
55
- this.eventBus.emit({
56
- type: "stage:start", timestamp: new Date().toISOString(),
57
- payload: { stageIndex: i, agentIds: stage.map((a) => a.id) },
58
- })
59
-
60
- const stageResults = await this.runStage(stage)
61
- results.push(...stageResults)
62
-
63
- // After architect stage completes, apply task plan and rebuild stages
64
- if (i === 0 && stage.some((a) => a.id === "architect") && stageResults.some((r) => r.status === "success")) {
65
- await this.applyTaskPlan(agents)
66
- stages = buildStages(agents)
67
- }
68
-
69
- for (const r of stageResults) {
70
- if (r.status !== "success") {
71
- allSuccess = false
72
- if (!this.config.continueOnError) break
73
- }
74
- }
75
-
76
- this.eventBus.emit({
77
- type: "stage:complete", timestamp: new Date().toISOString(),
78
- payload: { stageIndex: i },
79
- })
80
-
81
- if (!allSuccess && !this.config.continueOnError) {
82
- for (const remainingStage of stages.slice(i + 1)) {
83
- for (const agent of remainingStage) {
84
- this.eventBus.emit({
85
- type: "agent:skipped",
86
- timestamp: new Date().toISOString(),
87
- payload: { agentId: agent.id, agentName: agent.name, icon: agent.icon },
88
- })
89
- }
90
- }
91
- break
92
- }
93
- }
94
-
95
- const endTime = new Date()
96
- const result: OrchestratorResult = {
97
- name: this.config.name, success: allSuccess,
98
- totalDuration: endTime.getTime() - startTime.getTime(),
99
- agentResults: results,
100
- startTime: startTime.toISOString(), endTime: endTime.toISOString(),
101
- }
102
-
103
- this.eventBus.emit({
104
- type: "orchestration:complete", timestamp: new Date().toISOString(), payload: result,
105
- })
106
- return result
107
- }
108
-
109
- private async resolveAgents(): Promise<AgentDefinition[]> {
110
- if (!this.config.leader || this.config.useLeader === false) return this.config.agents
111
-
112
- this.contextManager.set(
113
- "availableAgents",
114
- this.config.agents.map((a) => `${a.id}: ${a.name}`).join("\n")
115
- )
116
-
117
- const result = await this.runner.run(this.config.leader)
118
- if (result.status !== "success") return this.config.agents
119
-
120
- const match = result.output.match(/\{[\s\S]*?"run"\s*:\s*\[[\s\S]*?\]\s*\}/)
121
- if (!match) return this.config.agents
122
-
123
- let plan: { run: string[] }
124
- try { plan = JSON.parse(match[0]) } catch { return this.config.agents }
125
-
126
- const runSet = new Set(plan.run)
127
- const skippedIds = new Set(
128
- this.config.agents.filter((a) => !runSet.has(a.id)).map((a) => a.id)
129
- )
130
-
131
- for (const agent of this.config.agents.filter((a) => skippedIds.has(a.id))) {
132
- this.eventBus.emit({
133
- type: "agent:skipped",
134
- timestamp: new Date().toISOString(),
135
- payload: { agentId: agent.id, agentName: agent.name, icon: agent.icon },
136
- })
137
- }
138
-
139
- return this.config.agents
140
- .filter((a) => runSet.has(a.id))
141
- .map((a) => ({
142
- ...a,
143
- dependsOn: a.dependsOn?.filter((dep) => !skippedIds.has(dep)),
144
- }))
145
- }
146
-
147
- private async runStage(agents: AgentDefinition[]): Promise<AgentResult[]> {
148
- const { maxConcurrency } = this.config
149
- if (!maxConcurrency || agents.length <= maxConcurrency) {
150
- return Promise.all(agents.map((a) => this.runner.run(a)))
151
- }
152
- const results: AgentResult[] = []
153
- for (let i = 0; i < agents.length; i += maxConcurrency) {
154
- const batch = agents.slice(i, i + maxConcurrency)
155
- results.push(...(await Promise.all(batch.map((a) => this.runner.run(a)))))
156
- }
157
- return results
158
- }
159
-
160
- private async applyTaskPlan(agents: AgentDefinition[]): Promise<void> {
161
- try {
162
- const taskPlanPath = path.join(this.workDir, "task-plan.json")
163
- if (!fs.existsSync(taskPlanPath)) return
164
-
165
- const content = fs.readFileSync(taskPlanPath, "utf-8")
166
- const taskPlan: TaskPlan = JSON.parse(content)
167
-
168
- const taskMap = new Map(taskPlan.tasks.map((t) => [t.id, t]))
169
- for (const agent of agents) {
170
- const task = taskMap.get(agent.id)
171
- if (task) {
172
- agent.taskTitle = task.title
173
- agent.dependsOn = task.dependsOn
174
- }
175
- }
176
- } catch {
177
- // Silently ignore parsing errors
178
- }
179
- }
180
- }
@@ -1,30 +0,0 @@
1
- import type { AgentDefinition } from "../types/agent"
2
-
3
- export function buildStages(agents: AgentDefinition[]): AgentDefinition[][] {
4
- const stages: AgentDefinition[][] = []
5
- const completed = new Set<string>()
6
- let remaining = [...agents]
7
-
8
- const allIds = new Set(agents.map((a) => a.id))
9
- for (const a of agents) {
10
- for (const dep of a.dependsOn ?? []) {
11
- if (!allIds.has(dep)) {
12
- throw new Error(`Agent "${a.id}" depends on "${dep}" which does not exist`)
13
- }
14
- }
15
- }
16
-
17
- while (remaining.length > 0) {
18
- const stage = remaining.filter((a) =>
19
- (a.dependsOn ?? []).every((dep) => completed.has(dep))
20
- )
21
- if (stage.length === 0) {
22
- const ids = remaining.map((a) => a.id).join(", ")
23
- throw new Error(`Circular or unsatisfiable dependency detected: ${ids}`)
24
- }
25
- stages.push(stage)
26
- stage.forEach((a) => completed.add(a.id))
27
- remaining = remaining.filter((a) => !stage.includes(a))
28
- }
29
- return stages
30
- }
@@ -1,41 +0,0 @@
1
- import { spawn, execSync } from "child_process"
2
- import { RuntimeBase } from "./RuntimeBase"
3
- import type { AgentTask, AgentOutput } from "../types/runtime"
4
-
5
- export class ClaudeRuntime extends RuntimeBase {
6
- readonly name = "claude"
7
-
8
- async checkAvailable(): Promise<boolean> {
9
- try { execSync("claude --version", { stdio: "ignore" }); return true }
10
- catch { return false }
11
- }
12
-
13
- async execute(task: AgentTask): Promise<AgentOutput> {
14
- const args = [
15
- "-p", task.prompt,
16
- "--allowedTools", task.tools.join(","),
17
- ]
18
- if (task.systemPrompt) args.push("--system-prompt", task.systemPrompt)
19
- if (task.model) args.push("--model", task.model)
20
-
21
- return new Promise((resolve, reject) => {
22
- const child = spawn("claude", args, { cwd: task.workDir, env: process.env })
23
- task.signal?.addEventListener("abort", () => child.kill())
24
-
25
- let output = ""
26
- let errorOutput = ""
27
- child.stdout.on("data", (d: Buffer) => {
28
- const chunk = d.toString(); output += chunk; task.onOutput?.(chunk)
29
- })
30
- child.stderr.on("data", (d: Buffer) => { errorOutput += d.toString() })
31
- child.on("close", (code) => {
32
- if (task.signal?.aborted) return
33
- code === 0
34
- ? resolve({ output, exitCode: code })
35
- : reject(new Error(`claude exited with code ${code}: ${errorOutput}`))
36
- })
37
- child.on("error", (err) => reject(new Error(`Failed to start claude: ${err.message}`)))
38
- })
39
- }
40
- }
41
-
@@ -1,7 +0,0 @@
1
- import type { AgentRuntime, AgentTask, AgentOutput } from "../types/runtime"
2
-
3
- export abstract class RuntimeBase implements AgentRuntime {
4
- abstract readonly name: string
5
- abstract checkAvailable(): Promise<boolean>
6
- abstract execute(task: AgentTask): Promise<AgentOutput>
7
- }
@@ -1,20 +0,0 @@
1
- import type { AgentRuntime } from "../types/runtime"
2
- import { ClaudeRuntime } from "./ClaudeRuntime"
3
-
4
- class RuntimeRegistry {
5
- private runtimes = new Map<string, AgentRuntime>()
6
- constructor() {
7
- this.register(new ClaudeRuntime())
8
- }
9
- register(r: AgentRuntime) { this.runtimes.set(r.name, r) }
10
- get(name: string): AgentRuntime {
11
- const r = this.runtimes.get(name)
12
- if (!r) throw new Error(`Runtime not found: "${name}". Available: ${[...this.runtimes.keys()].join(", ")}`)
13
- return r
14
- }
15
- has(name: string) { return this.runtimes.has(name) }
16
- list(): string[] { return [...this.runtimes.keys()] }
17
- }
18
-
19
- export const runtimeRegistry = new RuntimeRegistry()
20
- export { ClaudeRuntime }
@@ -1,48 +0,0 @@
1
- export type ToolName =
2
- | "Read" | "Write" | "Edit" | "MultiEdit"
3
- | "Bash" | "Glob" | "Grep" | "WebFetch"
4
-
5
- export interface AgentDefinition {
6
- id: string
7
- name: string
8
- icon?: string
9
- runtime?: string
10
- model?: string
11
- models?: Partial<Record<string, string>>
12
- systemPrompt: string
13
- taskPrompt: string
14
- taskTitle?: string
15
- tools?: ToolName[]
16
- dependsOn?: string[]
17
- timeout?: number
18
- retries?: number
19
- workDir?: string
20
- }
21
-
22
- export type AgentStatus =
23
- | "pending" | "running" | "retrying" | "success" | "failed" | "skipped"
24
-
25
- export interface AgentResult {
26
- agentId: string
27
- agentName: string
28
- status: AgentStatus
29
- output: string
30
- duration: number
31
- attempts: number
32
- error?: string
33
- startTime: string
34
- endTime: string
35
- }
36
-
37
- export interface SubTask {
38
- id: string // matches AgentDefinition.id
39
- title: string
40
- scope: string
41
- dependsOn?: string[]
42
- }
43
-
44
- export interface TaskPlan {
45
- projectName: string
46
- requirement: string
47
- tasks: SubTask[]
48
- }
@@ -1,26 +0,0 @@
1
- import type { AgentDefinition } from "./agent"
2
-
3
- export interface MultiClawConfig {
4
- name: string
5
- workDir: string
6
- leader?: AgentDefinition
7
- agents: AgentDefinition[]
8
- context?: Record<string, string>
9
- logDir?: string
10
- continueOnError?: boolean
11
- maxConcurrency?: number
12
- useLeader?: boolean
13
- dashboard?: {
14
- port?: number
15
- autoOpen?: boolean
16
- }
17
- }
18
-
19
- export interface OrchestratorResult {
20
- name: string
21
- success: boolean
22
- totalDuration: number
23
- agentResults: import("./agent").AgentResult[]
24
- startTime: string
25
- endTime: string
26
- }
@@ -1,21 +0,0 @@
1
- import type { AgentResult, OrchestratorResult } from "./index"
2
-
3
- export interface StageInfo {
4
- stageIndex: number
5
- agents: Array<{ id: string; name: string; icon?: string; model?: string; taskTitle?: string }>
6
- }
7
-
8
- export type MultiClawEvent =
9
- | { type: "orchestration:start"; timestamp: string; payload: { name: string; requirement?: string; totalAgents: number; stages: StageInfo[] } }
10
- | { type: "orchestration:complete"; timestamp: string; payload: OrchestratorResult }
11
- | { type: "stage:start"; timestamp: string; payload: { stageIndex: number; agentIds: string[] } }
12
- | { type: "stage:complete"; timestamp: string; payload: { stageIndex: number } }
13
- | { type: "agent:start"; timestamp: string; payload: { agentId: string; agentName: string; icon?: string; model?: string; taskTitle?: string } }
14
- | { type: "agent:output"; timestamp: string; payload: { agentId: string; chunk: string } }
15
- | { type: "agent:complete"; timestamp: string; payload: AgentResult }
16
- | { type: "agent:failed"; timestamp: string; payload: AgentResult }
17
- | { type: "agent:skipped"; timestamp: string; payload: { agentId: string; agentName: string; icon?: string } }
18
- | { type: "agent:retrying"; timestamp: string; payload: { agentId: string; agentName: string; attempt: number; maxAttempts: number; error: string } }
19
-
20
- export type EventListener = (event: MultiClawEvent) => void
21
-
@@ -1,4 +0,0 @@
1
- export * from "./agent"
2
- export * from "./runtime"
3
- export * from "./config"
4
- export * from "./event"
@@ -1,23 +0,0 @@
1
- export interface AgentTask {
2
- agentId: string
3
- systemPrompt: string
4
- prompt: string
5
- tools: string[]
6
- workDir: string
7
- timeout: number
8
- signal?: AbortSignal
9
- onOutput?: (chunk: string) => void
10
- model?: string
11
- }
12
-
13
- export interface AgentOutput {
14
- output: string
15
- exitCode: number
16
- }
17
-
18
- export interface AgentRuntime {
19
- readonly name: string
20
- checkAvailable(): Promise<boolean>
21
- execute(task: AgentTask): Promise<AgentOutput>
22
- }
23
-
@@ -1,24 +0,0 @@
1
- import * as fs from "fs/promises"
2
- import * as path from "path"
3
-
4
- export async function renderPrompt(
5
- template: string,
6
- context: Record<string, string>,
7
- workDir: string
8
- ): Promise<string> {
9
- let result = template
10
- const fileMatches = [...template.matchAll(/\{\{file:([^}]+)\}\}/g)]
11
- for (const match of fileMatches) {
12
- const filePath = path.resolve(workDir, match[1].trim())
13
- try {
14
- const content = await fs.readFile(filePath, "utf-8")
15
- result = result.replace(match[0], content)
16
- } catch {
17
- result = result.replace(match[0], `[file not found: ${match[1]}]`)
18
- }
19
- }
20
- for (const [key, value] of Object.entries(context)) {
21
- result = result.replaceAll(`{{${key}}}`, value)
22
- }
23
- return result
24
- }
@@ -1,24 +0,0 @@
1
- export function sleep(ms: number): Promise<void> {
2
- return new Promise((r) => setTimeout(r, ms))
3
- }
4
-
5
- export async function withRetry<T>(
6
- fn: (attempt: number) => Promise<T>,
7
- retries: number,
8
- onRetry?: (failedAttempt: number, error: unknown) => void,
9
- delayMs = 2000
10
- ): Promise<T> {
11
- let lastError: Error = new Error("Unknown error")
12
- for (let attempt = 1; attempt <= retries + 1; attempt++) {
13
- try { return await fn(attempt) }
14
- catch (err) {
15
- lastError = err as Error
16
- if (attempt <= retries) {
17
- onRetry?.(attempt, err)
18
- await sleep(delayMs * attempt)
19
- }
20
- }
21
- }
22
- throw lastError
23
- }
24
-
@@ -1,16 +0,0 @@
1
- export function withTimeout<T>(
2
- factory: (signal: AbortSignal) => Promise<T>,
3
- ms: number,
4
- label: string
5
- ): Promise<T> {
6
- const controller = new AbortController()
7
- let timer: NodeJS.Timeout
8
- const timeout = new Promise<never>((_, reject) => {
9
- timer = setTimeout(() => {
10
- controller.abort()
11
- reject(new Error(`[${label}] timed out (${ms / 1000}s)`))
12
- }, ms)
13
- })
14
- return Promise.race([factory(controller.signal), timeout]).finally(() => clearTimeout(timer))
15
- }
16
-
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "rootDir": "src",
5
- "composite": false,
6
- "incremental": false
7
- },
8
- "include": ["src"]
9
- }
package/tsconfig.json DELETED
@@ -1,5 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": { "outDir": "dist", "rootDir": "src" },
4
- "include": ["src"]
5
- }
package/tsup.config.ts DELETED
@@ -1,10 +0,0 @@
1
- import { defineConfig } from "tsup"
2
-
3
- export default defineConfig({
4
- entry: { index: "src/index.ts" },
5
- format: ["esm"],
6
- outDir: "dist",
7
- dts: { compilerOptions: { composite: false, incremental: false } },
8
- clean: true,
9
- shims: true,
10
- })