@pat-lewczuk/cezar 0.1.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.
- package/README.md +90 -0
- package/dist/core/agent-runner.d.ts +95 -0
- package/dist/core/agent-runner.js +8 -0
- package/dist/core/agent-runner.js.map +1 -0
- package/dist/core/backend-detect.d.ts +14 -0
- package/dist/core/backend-detect.js +78 -0
- package/dist/core/backend-detect.js.map +1 -0
- package/dist/core/claude-cli-runner.d.ts +68 -0
- package/dist/core/claude-cli-runner.js +424 -0
- package/dist/core/claude-cli-runner.js.map +1 -0
- package/dist/core/usage.d.ts +11 -0
- package/dist/core/usage.js +15 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +294 -0
- package/dist/index.js.map +1 -0
- package/dist/runs/store.d.ts +218 -0
- package/dist/runs/store.js +286 -0
- package/dist/runs/store.js.map +1 -0
- package/dist/server/git.d.ts +21 -0
- package/dist/server/git.js +54 -0
- package/dist/server/git.js.map +1 -0
- package/dist/server/open-in-terminal.d.ts +11 -0
- package/dist/server/open-in-terminal.js +84 -0
- package/dist/server/open-in-terminal.js.map +1 -0
- package/dist/server/server.d.ts +12 -0
- package/dist/server/server.js +243 -0
- package/dist/server/server.js.map +1 -0
- package/dist/skills.d.ts +31 -0
- package/dist/skills.js +117 -0
- package/dist/skills.js.map +1 -0
- package/dist/workflows/load.d.ts +15 -0
- package/dist/workflows/load.js +58 -0
- package/dist/workflows/load.js.map +1 -0
- package/dist/workflows/run.d.ts +52 -0
- package/dist/workflows/run.js +452 -0
- package/dist/workflows/run.js.map +1 -0
- package/dist/workflows/types.d.ts +202 -0
- package/dist/workflows/types.js +56 -0
- package/dist/workflows/types.js.map +1 -0
- package/package.json +44 -0
- package/scripts/mock-claude.mjs +106 -0
- package/web/app.js +634 -0
- package/web/index.html +48 -0
- package/web/style.css +317 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* A workflow is an ordered list of steps. Two step kinds:
|
|
4
|
+
* - `agent` — one claude CLI run (prompt + optional skill + model + tools);
|
|
5
|
+
* - `check` — a shell command; exit 0 passes, non-zero can loop back to an
|
|
6
|
+
* earlier step via `onFail` (bounded by `max`).
|
|
7
|
+
*
|
|
8
|
+
* `{{task}}` in a prompt is replaced with the user's task text. When a check
|
|
9
|
+
* loops back, the failing output is appended to the retried agent's prompt.
|
|
10
|
+
*/
|
|
11
|
+
export declare const workflowStepSchema: z.ZodEffects<z.ZodObject<{
|
|
12
|
+
id: z.ZodString;
|
|
13
|
+
name: z.ZodOptional<z.ZodString>;
|
|
14
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
15
|
+
skill: z.ZodOptional<z.ZodString>;
|
|
16
|
+
model: z.ZodOptional<z.ZodString>;
|
|
17
|
+
allowedTools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
18
|
+
bashAllowlist: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
19
|
+
command: z.ZodOptional<z.ZodString>;
|
|
20
|
+
onFail: z.ZodOptional<z.ZodObject<{
|
|
21
|
+
retry: z.ZodString;
|
|
22
|
+
max: z.ZodDefault<z.ZodNumber>;
|
|
23
|
+
}, "strip", z.ZodTypeAny, {
|
|
24
|
+
retry: string;
|
|
25
|
+
max: number;
|
|
26
|
+
}, {
|
|
27
|
+
retry: string;
|
|
28
|
+
max?: number | undefined;
|
|
29
|
+
}>>;
|
|
30
|
+
}, "strip", z.ZodTypeAny, {
|
|
31
|
+
id: string;
|
|
32
|
+
name?: string | undefined;
|
|
33
|
+
model?: string | undefined;
|
|
34
|
+
prompt?: string | undefined;
|
|
35
|
+
skill?: string | undefined;
|
|
36
|
+
allowedTools?: string[] | undefined;
|
|
37
|
+
bashAllowlist?: string[] | undefined;
|
|
38
|
+
command?: string | undefined;
|
|
39
|
+
onFail?: {
|
|
40
|
+
retry: string;
|
|
41
|
+
max: number;
|
|
42
|
+
} | undefined;
|
|
43
|
+
}, {
|
|
44
|
+
id: string;
|
|
45
|
+
name?: string | undefined;
|
|
46
|
+
model?: string | undefined;
|
|
47
|
+
prompt?: string | undefined;
|
|
48
|
+
skill?: string | undefined;
|
|
49
|
+
allowedTools?: string[] | undefined;
|
|
50
|
+
bashAllowlist?: string[] | undefined;
|
|
51
|
+
command?: string | undefined;
|
|
52
|
+
onFail?: {
|
|
53
|
+
retry: string;
|
|
54
|
+
max?: number | undefined;
|
|
55
|
+
} | undefined;
|
|
56
|
+
}>, {
|
|
57
|
+
id: string;
|
|
58
|
+
name?: string | undefined;
|
|
59
|
+
model?: string | undefined;
|
|
60
|
+
prompt?: string | undefined;
|
|
61
|
+
skill?: string | undefined;
|
|
62
|
+
allowedTools?: string[] | undefined;
|
|
63
|
+
bashAllowlist?: string[] | undefined;
|
|
64
|
+
command?: string | undefined;
|
|
65
|
+
onFail?: {
|
|
66
|
+
retry: string;
|
|
67
|
+
max: number;
|
|
68
|
+
} | undefined;
|
|
69
|
+
}, {
|
|
70
|
+
id: string;
|
|
71
|
+
name?: string | undefined;
|
|
72
|
+
model?: string | undefined;
|
|
73
|
+
prompt?: string | undefined;
|
|
74
|
+
skill?: string | undefined;
|
|
75
|
+
allowedTools?: string[] | undefined;
|
|
76
|
+
bashAllowlist?: string[] | undefined;
|
|
77
|
+
command?: string | undefined;
|
|
78
|
+
onFail?: {
|
|
79
|
+
retry: string;
|
|
80
|
+
max?: number | undefined;
|
|
81
|
+
} | undefined;
|
|
82
|
+
}>;
|
|
83
|
+
export declare const workflowFileSchema: z.ZodObject<{
|
|
84
|
+
name: z.ZodString;
|
|
85
|
+
description: z.ZodOptional<z.ZodString>;
|
|
86
|
+
steps: z.ZodArray<z.ZodEffects<z.ZodObject<{
|
|
87
|
+
id: z.ZodString;
|
|
88
|
+
name: z.ZodOptional<z.ZodString>;
|
|
89
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
90
|
+
skill: z.ZodOptional<z.ZodString>;
|
|
91
|
+
model: z.ZodOptional<z.ZodString>;
|
|
92
|
+
allowedTools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
93
|
+
bashAllowlist: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
94
|
+
command: z.ZodOptional<z.ZodString>;
|
|
95
|
+
onFail: z.ZodOptional<z.ZodObject<{
|
|
96
|
+
retry: z.ZodString;
|
|
97
|
+
max: z.ZodDefault<z.ZodNumber>;
|
|
98
|
+
}, "strip", z.ZodTypeAny, {
|
|
99
|
+
retry: string;
|
|
100
|
+
max: number;
|
|
101
|
+
}, {
|
|
102
|
+
retry: string;
|
|
103
|
+
max?: number | undefined;
|
|
104
|
+
}>>;
|
|
105
|
+
}, "strip", z.ZodTypeAny, {
|
|
106
|
+
id: string;
|
|
107
|
+
name?: string | undefined;
|
|
108
|
+
model?: string | undefined;
|
|
109
|
+
prompt?: string | undefined;
|
|
110
|
+
skill?: string | undefined;
|
|
111
|
+
allowedTools?: string[] | undefined;
|
|
112
|
+
bashAllowlist?: string[] | undefined;
|
|
113
|
+
command?: string | undefined;
|
|
114
|
+
onFail?: {
|
|
115
|
+
retry: string;
|
|
116
|
+
max: number;
|
|
117
|
+
} | undefined;
|
|
118
|
+
}, {
|
|
119
|
+
id: string;
|
|
120
|
+
name?: string | undefined;
|
|
121
|
+
model?: string | undefined;
|
|
122
|
+
prompt?: string | undefined;
|
|
123
|
+
skill?: string | undefined;
|
|
124
|
+
allowedTools?: string[] | undefined;
|
|
125
|
+
bashAllowlist?: string[] | undefined;
|
|
126
|
+
command?: string | undefined;
|
|
127
|
+
onFail?: {
|
|
128
|
+
retry: string;
|
|
129
|
+
max?: number | undefined;
|
|
130
|
+
} | undefined;
|
|
131
|
+
}>, {
|
|
132
|
+
id: string;
|
|
133
|
+
name?: string | undefined;
|
|
134
|
+
model?: string | undefined;
|
|
135
|
+
prompt?: string | undefined;
|
|
136
|
+
skill?: string | undefined;
|
|
137
|
+
allowedTools?: string[] | undefined;
|
|
138
|
+
bashAllowlist?: string[] | undefined;
|
|
139
|
+
command?: string | undefined;
|
|
140
|
+
onFail?: {
|
|
141
|
+
retry: string;
|
|
142
|
+
max: number;
|
|
143
|
+
} | undefined;
|
|
144
|
+
}, {
|
|
145
|
+
id: string;
|
|
146
|
+
name?: string | undefined;
|
|
147
|
+
model?: string | undefined;
|
|
148
|
+
prompt?: string | undefined;
|
|
149
|
+
skill?: string | undefined;
|
|
150
|
+
allowedTools?: string[] | undefined;
|
|
151
|
+
bashAllowlist?: string[] | undefined;
|
|
152
|
+
command?: string | undefined;
|
|
153
|
+
onFail?: {
|
|
154
|
+
retry: string;
|
|
155
|
+
max?: number | undefined;
|
|
156
|
+
} | undefined;
|
|
157
|
+
}>, "many">;
|
|
158
|
+
}, "strip", z.ZodTypeAny, {
|
|
159
|
+
name: string;
|
|
160
|
+
steps: {
|
|
161
|
+
id: string;
|
|
162
|
+
name?: string | undefined;
|
|
163
|
+
model?: string | undefined;
|
|
164
|
+
prompt?: string | undefined;
|
|
165
|
+
skill?: string | undefined;
|
|
166
|
+
allowedTools?: string[] | undefined;
|
|
167
|
+
bashAllowlist?: string[] | undefined;
|
|
168
|
+
command?: string | undefined;
|
|
169
|
+
onFail?: {
|
|
170
|
+
retry: string;
|
|
171
|
+
max: number;
|
|
172
|
+
} | undefined;
|
|
173
|
+
}[];
|
|
174
|
+
description?: string | undefined;
|
|
175
|
+
}, {
|
|
176
|
+
name: string;
|
|
177
|
+
steps: {
|
|
178
|
+
id: string;
|
|
179
|
+
name?: string | undefined;
|
|
180
|
+
model?: string | undefined;
|
|
181
|
+
prompt?: string | undefined;
|
|
182
|
+
skill?: string | undefined;
|
|
183
|
+
allowedTools?: string[] | undefined;
|
|
184
|
+
bashAllowlist?: string[] | undefined;
|
|
185
|
+
command?: string | undefined;
|
|
186
|
+
onFail?: {
|
|
187
|
+
retry: string;
|
|
188
|
+
max?: number | undefined;
|
|
189
|
+
} | undefined;
|
|
190
|
+
}[];
|
|
191
|
+
description?: string | undefined;
|
|
192
|
+
}>;
|
|
193
|
+
export type WorkflowStepDef = z.infer<typeof workflowStepSchema>;
|
|
194
|
+
export interface WorkflowDef extends z.infer<typeof workflowFileSchema> {
|
|
195
|
+
source: 'built-in' | 'file';
|
|
196
|
+
path?: string;
|
|
197
|
+
}
|
|
198
|
+
export declare function stepKind(step: WorkflowStepDef): 'agent' | 'check';
|
|
199
|
+
/** Tools an agent step gets when the workflow doesn't say otherwise. */
|
|
200
|
+
export declare const DEFAULT_ALLOWED_TOOLS: string[];
|
|
201
|
+
/** The zero-config workflow: one agent step that just does the task. */
|
|
202
|
+
export declare const QUICK_TASK_WORKFLOW: WorkflowDef;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* A workflow is an ordered list of steps. Two step kinds:
|
|
4
|
+
* - `agent` — one claude CLI run (prompt + optional skill + model + tools);
|
|
5
|
+
* - `check` — a shell command; exit 0 passes, non-zero can loop back to an
|
|
6
|
+
* earlier step via `onFail` (bounded by `max`).
|
|
7
|
+
*
|
|
8
|
+
* `{{task}}` in a prompt is replaced with the user's task text. When a check
|
|
9
|
+
* loops back, the failing output is appended to the retried agent's prompt.
|
|
10
|
+
*/
|
|
11
|
+
export const workflowStepSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
id: z.string().min(1),
|
|
14
|
+
name: z.string().optional(),
|
|
15
|
+
// agent step
|
|
16
|
+
prompt: z.string().optional(),
|
|
17
|
+
skill: z.string().optional(),
|
|
18
|
+
model: z.string().optional(),
|
|
19
|
+
allowedTools: z.array(z.string()).optional(),
|
|
20
|
+
bashAllowlist: z.array(z.string()).optional(),
|
|
21
|
+
// check step
|
|
22
|
+
command: z.string().optional(),
|
|
23
|
+
onFail: z
|
|
24
|
+
.object({
|
|
25
|
+
retry: z.string().min(1),
|
|
26
|
+
max: z.number().int().positive().default(2),
|
|
27
|
+
})
|
|
28
|
+
.optional(),
|
|
29
|
+
})
|
|
30
|
+
.refine((s) => Boolean(s.command) !== Boolean(s.prompt ?? s.skill), {
|
|
31
|
+
message: 'a step is either an agent step (prompt/skill) or a check step (command), not both',
|
|
32
|
+
});
|
|
33
|
+
export const workflowFileSchema = z.object({
|
|
34
|
+
name: z.string().min(1),
|
|
35
|
+
description: z.string().optional(),
|
|
36
|
+
steps: z.array(workflowStepSchema).min(1),
|
|
37
|
+
});
|
|
38
|
+
export function stepKind(step) {
|
|
39
|
+
return step.command ? 'check' : 'agent';
|
|
40
|
+
}
|
|
41
|
+
/** Tools an agent step gets when the workflow doesn't say otherwise. */
|
|
42
|
+
export const DEFAULT_ALLOWED_TOOLS = ['Read', 'Edit', 'Write', 'Grep', 'Glob', 'Bash'];
|
|
43
|
+
/** The zero-config workflow: one agent step that just does the task. */
|
|
44
|
+
export const QUICK_TASK_WORKFLOW = {
|
|
45
|
+
name: 'quick-task',
|
|
46
|
+
description: 'One agent run on your task — no ceremony.',
|
|
47
|
+
source: 'built-in',
|
|
48
|
+
steps: [
|
|
49
|
+
{
|
|
50
|
+
id: 'task',
|
|
51
|
+
name: 'Do the task',
|
|
52
|
+
prompt: '{{task}}',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/workflows/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC;KAChC,MAAM,CAAC;IACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,aAAa;IACb,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC5C,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC7C,aAAa;IACb,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,MAAM,EAAE,CAAC;SACN,MAAM,CAAC;QACN,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACxB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;KAC5C,CAAC;SACD,QAAQ,EAAE;CACd,CAAC;KACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE;IAClE,OAAO,EAAE,mFAAmF;CAC7F,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CAC1C,CAAC,CAAC;AASH,MAAM,UAAU,QAAQ,CAAC,IAAqB;IAC5C,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;AAC1C,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEvF,wEAAwE;AACxE,MAAM,CAAC,MAAM,mBAAmB,GAAgB;IAC9C,IAAI,EAAE,YAAY;IAClB,WAAW,EAAE,2CAA2C;IACxD,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE;QACL;YACE,EAAE,EAAE,MAAM;YACV,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,UAAU;SACnB;KACF;CACF,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pat-lewczuk/cezar",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local cockpit for running and tracking AI agent tasks in your repo. Uses your logged-in `claude` CLI and `gh` — zero config, zero database.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"cezar": "dist/index.js",
|
|
9
|
+
"cez": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"web",
|
|
14
|
+
"scripts",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=20"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"dev": "tsx src/index.ts",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@hono/node-server": "^1.13.0",
|
|
28
|
+
"hono": "^4.6.0",
|
|
29
|
+
"yaml": "^2.5.0",
|
|
30
|
+
"zod": "^3.23.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.14.0",
|
|
34
|
+
"tsx": "^4.19.0",
|
|
35
|
+
"typescript": "^5.6.0"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/open-mercato/cezar"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Mock `claude` binary for CEZ_DRY_RUN=1 — emits a plausible stream-json
|
|
3
|
+
// session so the engine / store / GUI can be exercised without tokens.
|
|
4
|
+
// Mirrors the real CLI's contract in SESSION mode: keeps reading {type:user}
|
|
5
|
+
// NDJSON lines from stdin, answers each with a scripted turn (assistant
|
|
6
|
+
// events + a terminal `result`), and exits when stdin closes (EOF).
|
|
7
|
+
|
|
8
|
+
import { createInterface } from 'node:readline';
|
|
9
|
+
|
|
10
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
11
|
+
const emit = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
|
|
12
|
+
|
|
13
|
+
emit({ type: 'system', subtype: 'init' });
|
|
14
|
+
|
|
15
|
+
let turn = 0;
|
|
16
|
+
|
|
17
|
+
async function respond(userText, imageCount) {
|
|
18
|
+
turn += 1;
|
|
19
|
+
await sleep(250);
|
|
20
|
+
|
|
21
|
+
if (turn === 1) {
|
|
22
|
+
emit({
|
|
23
|
+
type: 'assistant',
|
|
24
|
+
message: {
|
|
25
|
+
role: 'assistant',
|
|
26
|
+
content: [{ type: 'text', text: `Okay — looking into: ${userText.slice(0, 120)}` }],
|
|
27
|
+
usage: { input_tokens: 850, output_tokens: 40 },
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
await sleep(300);
|
|
31
|
+
emit({
|
|
32
|
+
type: 'assistant',
|
|
33
|
+
message: {
|
|
34
|
+
role: 'assistant',
|
|
35
|
+
content: [{ type: 'tool_use', id: `toolu_mock_${turn}`, name: 'Bash', input: { command: 'git status --short' } }],
|
|
36
|
+
usage: { input_tokens: 120, output_tokens: 55 },
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
await sleep(300);
|
|
40
|
+
emit({
|
|
41
|
+
type: 'user',
|
|
42
|
+
message: {
|
|
43
|
+
role: 'user',
|
|
44
|
+
content: [{ type: 'tool_result', tool_use_id: `toolu_mock_${turn}`, content: ' M src/example.ts' }],
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
await sleep(300);
|
|
48
|
+
emit({
|
|
49
|
+
type: 'assistant',
|
|
50
|
+
message: {
|
|
51
|
+
role: 'assistant',
|
|
52
|
+
content: [{ type: 'text', text: 'Done with the first pass — opened a draft PR: https://github.com/open-mercato/demo/pull/123. Anything to adjust? (dry-run mock)' }],
|
|
53
|
+
usage: { input_tokens: 300, output_tokens: 90 },
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
await sleep(150);
|
|
57
|
+
emit({
|
|
58
|
+
type: 'result',
|
|
59
|
+
subtype: 'success',
|
|
60
|
+
result: 'Done with the first pass (dry run).',
|
|
61
|
+
usage: { input_tokens: 1270, output_tokens: 185 },
|
|
62
|
+
total_cost_usd: 0.0342,
|
|
63
|
+
});
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const imgNote = imageCount > 0 ? ` I can see ${imageCount} image(s) you attached.` : '';
|
|
68
|
+
emit({
|
|
69
|
+
type: 'assistant',
|
|
70
|
+
message: {
|
|
71
|
+
role: 'assistant',
|
|
72
|
+
content: [{ type: 'text', text: `Follow-up #${turn - 1} received: "${userText.slice(0, 100)}".${imgNote} Applied (dry run).` }],
|
|
73
|
+
usage: { input_tokens: 200, output_tokens: 60 },
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
await sleep(150);
|
|
77
|
+
emit({
|
|
78
|
+
type: 'result',
|
|
79
|
+
subtype: 'success',
|
|
80
|
+
result: `Follow-up #${turn - 1} handled (dry run).`,
|
|
81
|
+
usage: { input_tokens: 200, output_tokens: 60 },
|
|
82
|
+
total_cost_usd: 0.0051,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const rl = createInterface({ input: process.stdin });
|
|
87
|
+
let queue = Promise.resolve();
|
|
88
|
+
rl.on('line', (line) => {
|
|
89
|
+
const trimmed = line.trim();
|
|
90
|
+
if (!trimmed) return;
|
|
91
|
+
let userText = '(unparseable message)';
|
|
92
|
+
let imageCount = 0;
|
|
93
|
+
try {
|
|
94
|
+
const msg = JSON.parse(trimmed);
|
|
95
|
+
const blocks = msg?.message?.content ?? [];
|
|
96
|
+
userText = blocks.filter((b) => b.type === 'text').map((b) => b.text).join('\n') || '(no text)';
|
|
97
|
+
imageCount = blocks.filter((b) => b.type === 'image').length;
|
|
98
|
+
} catch {
|
|
99
|
+
// keep placeholders
|
|
100
|
+
}
|
|
101
|
+
queue = queue.then(() => respond(userText, imageCount));
|
|
102
|
+
});
|
|
103
|
+
rl.on('close', () => {
|
|
104
|
+
// EOF = session over; finish any in-flight turn then exit cleanly.
|
|
105
|
+
queue.then(() => process.exit(0));
|
|
106
|
+
});
|