@open-mercato/cezar 0.1.5-pr506.357
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 +573 -0
- package/dist/config.d.ts +144 -0
- package/dist/config.js +106 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-runner.d.ts +169 -0
- package/dist/core/agent-runner.js +23 -0
- package/dist/core/agent-runner.js.map +1 -0
- package/dist/core/backend-detect.d.ts +16 -0
- package/dist/core/backend-detect.js +118 -0
- package/dist/core/backend-detect.js.map +1 -0
- package/dist/core/claude-cli-runner.d.ts +56 -0
- package/dist/core/claude-cli-runner.js +416 -0
- package/dist/core/claude-cli-runner.js.map +1 -0
- package/dist/core/claude-ui-mapper.d.ts +71 -0
- package/dist/core/claude-ui-mapper.js +567 -0
- package/dist/core/claude-ui-mapper.js.map +1 -0
- package/dist/core/codex-app-server-runner.d.ts +29 -0
- package/dist/core/codex-app-server-runner.js +489 -0
- package/dist/core/codex-app-server-runner.js.map +1 -0
- package/dist/core/codex-ui-mapper.d.ts +63 -0
- package/dist/core/codex-ui-mapper.js +491 -0
- package/dist/core/codex-ui-mapper.js.map +1 -0
- package/dist/core/ndjson.d.ts +6 -0
- package/dist/core/ndjson.js +23 -0
- package/dist/core/ndjson.js.map +1 -0
- package/dist/core/opencode-server-runner.d.ts +30 -0
- package/dist/core/opencode-server-runner.js +488 -0
- package/dist/core/opencode-server-runner.js.map +1 -0
- package/dist/core/opencode-ui-mapper.d.ts +105 -0
- package/dist/core/opencode-ui-mapper.js +588 -0
- package/dist/core/opencode-ui-mapper.js.map +1 -0
- package/dist/core/process-usage.d.ts +63 -0
- package/dist/core/process-usage.js +181 -0
- package/dist/core/process-usage.js.map +1 -0
- package/dist/core/runner-factory.d.ts +8 -0
- package/dist/core/runner-factory.js +22 -0
- package/dist/core/runner-factory.js.map +1 -0
- package/dist/core/tool-display.d.ts +30 -0
- package/dist/core/tool-display.js +156 -0
- package/dist/core/tool-display.js.map +1 -0
- package/dist/core/ui-events.d.ts +308 -0
- package/dist/core/ui-events.js +25 -0
- package/dist/core/ui-events.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/git-worktree.d.ts +88 -0
- package/dist/git-worktree.js +297 -0
- package/dist/git-worktree.js.map +1 -0
- package/dist/handoff.d.ts +62 -0
- package/dist/handoff.js +147 -0
- package/dist/handoff.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +466 -0
- package/dist/index.js.map +1 -0
- package/dist/install-as-command.d.ts +39 -0
- package/dist/install-as-command.js +51 -0
- package/dist/install-as-command.js.map +1 -0
- package/dist/pack-check.d.ts +17 -0
- package/dist/pack-check.js +27 -0
- package/dist/pack-check.js.map +1 -0
- package/dist/paths.d.ts +23 -0
- package/dist/paths.js +34 -0
- package/dist/paths.js.map +1 -0
- package/dist/planner.d.ts +17 -0
- package/dist/planner.js +248 -0
- package/dist/planner.js.map +1 -0
- package/dist/release/snapshot.d.ts +64 -0
- package/dist/release/snapshot.js +75 -0
- package/dist/release/snapshot.js.map +1 -0
- package/dist/release/stable.d.ts +46 -0
- package/dist/release/stable.js +57 -0
- package/dist/release/stable.js.map +1 -0
- package/dist/runs/auto-name.d.ts +75 -0
- package/dist/runs/auto-name.js +168 -0
- package/dist/runs/auto-name.js.map +1 -0
- package/dist/runs/retention.d.ts +60 -0
- package/dist/runs/retention.js +86 -0
- package/dist/runs/retention.js.map +1 -0
- package/dist/runs/review-gate.d.ts +18 -0
- package/dist/runs/review-gate.js +21 -0
- package/dist/runs/review-gate.js.map +1 -0
- package/dist/runs/store.d.ts +408 -0
- package/dist/runs/store.js +526 -0
- package/dist/runs/store.js.map +1 -0
- package/dist/runs/task-refs.d.ts +24 -0
- package/dist/runs/task-refs.js +74 -0
- package/dist/runs/task-refs.js.map +1 -0
- package/dist/runs/ui-event-sink.d.ts +81 -0
- package/dist/runs/ui-event-sink.js +210 -0
- package/dist/runs/ui-event-sink.js.map +1 -0
- package/dist/server/capabilities.d.ts +33 -0
- package/dist/server/capabilities.js +43 -0
- package/dist/server/capabilities.js.map +1 -0
- package/dist/server/forge/github.d.ts +70 -0
- package/dist/server/forge/github.js +375 -0
- package/dist/server/forge/github.js.map +1 -0
- package/dist/server/forge/index.d.ts +24 -0
- package/dist/server/forge/index.js +45 -0
- package/dist/server/forge/index.js.map +1 -0
- package/dist/server/forge/types.d.ts +80 -0
- package/dist/server/forge/types.js +2 -0
- package/dist/server/forge/types.js.map +1 -0
- package/dist/server/git-changes.d.ts +195 -0
- package/dist/server/git-changes.js +497 -0
- package/dist/server/git-changes.js.map +1 -0
- package/dist/server/git.d.ts +26 -0
- package/dist/server/git.js +103 -0
- package/dist/server/git.js.map +1 -0
- package/dist/server/github.d.ts +8 -0
- package/dist/server/github.js +8 -0
- package/dist/server/github.js.map +1 -0
- package/dist/server/launch-key.d.ts +7 -0
- package/dist/server/launch-key.js +33 -0
- package/dist/server/launch-key.js.map +1 -0
- package/dist/server/open-in-app.d.ts +51 -0
- package/dist/server/open-in-app.js +189 -0
- package/dist/server/open-in-app.js.map +1 -0
- package/dist/server/open-in-terminal.d.ts +25 -0
- package/dist/server/open-in-terminal.js +120 -0
- package/dist/server/open-in-terminal.js.map +1 -0
- package/dist/server/pr.d.ts +7 -0
- package/dist/server/pr.js +7 -0
- package/dist/server/pr.js.map +1 -0
- package/dist/server/server.d.ts +41 -0
- package/dist/server/server.js +1390 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/static-ui.d.ts +49 -0
- package/dist/server/static-ui.js +92 -0
- package/dist/server/static-ui.js.map +1 -0
- package/dist/server/wsl.d.ts +25 -0
- package/dist/server/wsl.js +92 -0
- package/dist/server/wsl.js.map +1 -0
- package/dist/server-install/engine.d.ts +33 -0
- package/dist/server-install/engine.js +314 -0
- package/dist/server-install/engine.js.map +1 -0
- package/dist/server-install/platforms/macosx-ngrok.d.ts +6 -0
- package/dist/server-install/platforms/macosx-ngrok.js +345 -0
- package/dist/server-install/platforms/macosx-ngrok.js.map +1 -0
- package/dist/server-install/platforms/ubuntu-vps.d.ts +39 -0
- package/dist/server-install/platforms/ubuntu-vps.js +746 -0
- package/dist/server-install/platforms/ubuntu-vps.js.map +1 -0
- package/dist/server-install/state.d.ts +26 -0
- package/dist/server-install/state.js +125 -0
- package/dist/server-install/state.js.map +1 -0
- package/dist/server-install/steps.d.ts +109 -0
- package/dist/server-install/steps.js +330 -0
- package/dist/server-install/steps.js.map +1 -0
- package/dist/server-install/strategies.d.ts +4 -0
- package/dist/server-install/strategies.js +20 -0
- package/dist/server-install/strategies.js.map +1 -0
- package/dist/server-install/types.d.ts +597 -0
- package/dist/server-install/types.js +98 -0
- package/dist/server-install/types.js.map +1 -0
- package/dist/server-install/ui.d.ts +42 -0
- package/dist/server-install/ui.js +130 -0
- package/dist/server-install/ui.js.map +1 -0
- package/dist/skills-banner.d.ts +21 -0
- package/dist/skills-banner.js +40 -0
- package/dist/skills-banner.js.map +1 -0
- package/dist/skills-remote.d.ts +35 -0
- package/dist/skills-remote.js +266 -0
- package/dist/skills-remote.js.map +1 -0
- package/dist/skills.d.ts +45 -0
- package/dist/skills.js +182 -0
- package/dist/skills.js.map +1 -0
- package/dist/todos.d.ts +63 -0
- package/dist/todos.js +168 -0
- package/dist/todos.js.map +1 -0
- package/dist/ui-state.d.ts +12 -0
- package/dist/ui-state.js +25 -0
- package/dist/ui-state.js.map +1 -0
- package/dist/update-check.d.ts +12 -0
- package/dist/update-check.js +41 -0
- package/dist/update-check.js.map +1 -0
- package/dist/workflows/load.d.ts +15 -0
- package/dist/workflows/load.js +56 -0
- package/dist/workflows/load.js.map +1 -0
- package/dist/workflows/run.d.ts +281 -0
- package/dist/workflows/run.js +1511 -0
- package/dist/workflows/run.js.map +1 -0
- package/dist/workflows/types.d.ts +307 -0
- package/dist/workflows/types.js +171 -0
- package/dist/workflows/types.js.map +1 -0
- package/package.json +90 -0
- package/scripts/check-pack.mjs +46 -0
- package/scripts/dev.mjs +74 -0
- package/scripts/install-as-command.mjs +120 -0
- package/scripts/mock-claude.mjs +310 -0
- package/scripts/release-snapshot.mjs +130 -0
- package/scripts/release.mjs +119 -0
- package/scripts/test-process-usage.mjs +47 -0
- package/web/dist/assets/arrow-left-BE957rJs.js +1 -0
- package/web/dist/assets/bundle-mjs-DLPpRFyl.js +1 -0
- package/web/dist/assets/centered-state-BddhEEtO.js +43 -0
- package/web/dist/assets/chunk-BO2N2NFS-f5igTtpB.js +131 -0
- package/web/dist/assets/command-CjhFbABo.js +1 -0
- package/web/dist/assets/compare-variants-Cs52IAEG.js +1 -0
- package/web/dist/assets/core-BuMqAF_1.js +12 -0
- package/web/dist/assets/css-BsVw1vtW.js +1 -0
- package/web/dist/assets/dialog-DFvXTkFv.js +1 -0
- package/web/dist/assets/diff-view-C9b8AITq.js +4 -0
- package/web/dist/assets/diff-woXpYk--.js +1 -0
- package/web/dist/assets/dist-FBs4wmIY.js +1 -0
- package/web/dist/assets/dist-o_kVMXjg.js +1 -0
- package/web/dist/assets/engine-javascript-FQvzRocB.js +141 -0
- package/web/dist/assets/file-BVuztAOd.js +1 -0
- package/web/dist/assets/folder-0pJ1NZ_S.js +1 -0
- package/web/dist/assets/git-pull-request-DJkAMT0f.js +1 -0
- package/web/dist/assets/git-toolbar-CwhQdx3J.js +1 -0
- package/web/dist/assets/github-D0rAX4-r.js +1 -0
- package/web/dist/assets/go-rLFTqkRN.js +1 -0
- package/web/dist/assets/highlighted-body-OFNGDK62-DGHHie_q.js +1 -0
- package/web/dist/assets/highlighter-BKVEsACy.js +3 -0
- package/web/dist/assets/html-BY76lXLH.js +1 -0
- package/web/dist/assets/index-CEkI7QxN.css +2 -0
- package/web/dist/assets/index-CO2lH2et.js +11 -0
- package/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/web/dist/assets/javascript-BgS3c2Ky.js +1 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/web/dist/assets/json-qhed-kSA.js +1 -0
- package/web/dist/assets/jsonc-CYpm1nAK.js +1 -0
- package/web/dist/assets/jsx-Bz0zcwM4.js +1 -0
- package/web/dist/assets/lib-CNGa3Dig.js +1 -0
- package/web/dist/assets/loader-circle-BV3DO9mE.js +1 -0
- package/web/dist/assets/markdown-BYOwaDjH.js +1 -0
- package/web/dist/assets/markdown-D4-ADgH9.js +1 -0
- package/web/dist/assets/mermaid-GHXKKRXX-DM2VsGBG.js +1 -0
- package/web/dist/assets/open-mercato-toBr6SOa.svg +11 -0
- package/web/dist/assets/prompt-templates-C7ptKvDd.js +15 -0
- package/web/dist/assets/python-gzcpVVnB.js +1 -0
- package/web/dist/assets/react-dom-Ddtik4qM.js +1 -0
- package/web/dist/assets/refresh-cw-DCa-Rlwz.js +1 -0
- package/web/dist/assets/repo-git-DIhryGDx.js +1 -0
- package/web/dist/assets/run-diff-Dyow8B1q.js +3 -0
- package/web/dist/assets/run-header-jx9-18b8.js +1 -0
- package/web/dist/assets/rust-Cfkwpbl8.js +1 -0
- package/web/dist/assets/search-x-CoCyl7zT.js +1 -0
- package/web/dist/assets/shellscript-CLZ0U2zV.js +1 -0
- package/web/dist/assets/skill-detail-DB7x_FQp.js +1 -0
- package/web/dist/assets/skills-98vCroU8.js +1 -0
- package/web/dist/assets/skills-BKIoqD6K.js +1 -0
- package/web/dist/assets/sql-BsFa4tDR.js +1 -0
- package/web/dist/assets/square-terminal-B6PZX4Qp.js +1 -0
- package/web/dist/assets/tab-link-C5--YMQd.js +1 -0
- package/web/dist/assets/task-changes-ejxPGVJw.js +1 -0
- package/web/dist/assets/task-commits-C6GO5qT-.js +1 -0
- package/web/dist/assets/task-files-CatpCBXa.js +2 -0
- package/web/dist/assets/task-thread-BMbXDM3d.js +5 -0
- package/web/dist/assets/tasks-table-dgezlCcg.js +1 -0
- package/web/dist/assets/trash-2-CDtxmmc8.js +1 -0
- package/web/dist/assets/triangle-alert-6su4Js5O.js +1 -0
- package/web/dist/assets/tsx-udAQXfEw.js +1 -0
- package/web/dist/assets/typescript-bsJCZSQ-.js +1 -0
- package/web/dist/assets/upload-Bp7hQxll.js +1 -0
- package/web/dist/assets/use-desktop-CvTwojOJ.js +3 -0
- package/web/dist/assets/utils-rQGbOrwc.js +1 -0
- package/web/dist/assets/workflows-Dy2c6790.js +11 -0
- package/web/dist/assets/x-Br_jIDBw.js +1 -0
- package/web/dist/assets/yaml-rwi0_p6S.js +1 -0
- package/web/dist/index.html +70 -0
- package/web/open-mercato.svg +11 -0
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@open-mercato/cezar",
|
|
3
|
+
"version": "0.1.5-pr506.357",
|
|
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
|
+
"cezar-cli": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"web/open-mercato.svg",
|
|
15
|
+
"web/dist",
|
|
16
|
+
"scripts",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc && npm run build:web && npm run check:pack",
|
|
24
|
+
"build:web": "vite build --config web/app/vite.config.ts",
|
|
25
|
+
"check:pack": "node scripts/check-pack.mjs",
|
|
26
|
+
"dev": "node scripts/dev.mjs",
|
|
27
|
+
"install-as-command": "node scripts/install-as-command.mjs --mode link",
|
|
28
|
+
"install-as-command:global": "node scripts/install-as-command.mjs --mode global",
|
|
29
|
+
"uninstall-as-command": "node scripts/install-as-command.mjs --mode uninstall",
|
|
30
|
+
"dev:server": "tsx src/index.ts",
|
|
31
|
+
"server-install": "tsx src/index.ts server-install",
|
|
32
|
+
"server-deploy": "tsx src/index.ts server-deploy",
|
|
33
|
+
"server-uninstall": "tsx src/index.ts server-uninstall",
|
|
34
|
+
"dev:web": "vite --config web/app/vite.config.ts",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"test:unit": "node --import tsx --test test/unit/*.test.ts",
|
|
38
|
+
"test:package": "node --import tsx --test test/e2e/*.test.ts",
|
|
39
|
+
"test:e2e": "sh .ai/scripts/e2e.sh",
|
|
40
|
+
"typecheck": "npm run typecheck:server && npm run typecheck:web",
|
|
41
|
+
"typecheck:server": "tsc --noEmit -p tsconfig.test.json",
|
|
42
|
+
"typecheck:web": "tsc --noEmit -p web/app/tsconfig.json",
|
|
43
|
+
"prepublishOnly": "npm run build"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@clack/prompts": "^0.11.0",
|
|
47
|
+
"@hono/node-server": "^1.13.0",
|
|
48
|
+
"hono": "^4.6.0",
|
|
49
|
+
"yaml": "^2.5.0",
|
|
50
|
+
"zod": "^3.23.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@dnd-kit/core": "^6.3.1",
|
|
54
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
55
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
56
|
+
"@tailwindcss/vite": "^4.3.2",
|
|
57
|
+
"@tanstack/react-query": "^5.101.2",
|
|
58
|
+
"@testing-library/react": "^16.3.2",
|
|
59
|
+
"@types/node": "^20.14.0",
|
|
60
|
+
"@types/react": "^19.2.17",
|
|
61
|
+
"@types/react-dom": "^19.2.3",
|
|
62
|
+
"@vitejs/plugin-react": "^6.0.3",
|
|
63
|
+
"class-variance-authority": "^0.7.1",
|
|
64
|
+
"clsx": "^2.1.1",
|
|
65
|
+
"cmdk": "^1.1.1",
|
|
66
|
+
"jsdom": "^29.1.1",
|
|
67
|
+
"lucide-react": "^1.24.0",
|
|
68
|
+
"radix-ui": "^1.6.2",
|
|
69
|
+
"react": "^19.2.7",
|
|
70
|
+
"react-dom": "^19.2.7",
|
|
71
|
+
"react-router": "^7.18.1",
|
|
72
|
+
"shiki": "^4.3.1",
|
|
73
|
+
"streamdown": "^2.5.0",
|
|
74
|
+
"tailwind-merge": "^3.6.0",
|
|
75
|
+
"tailwindcss": "^4.3.2",
|
|
76
|
+
"tsx": "^4.19.0",
|
|
77
|
+
"tw-animate-css": "^1.4.0",
|
|
78
|
+
"typescript": "^5.6.0",
|
|
79
|
+
"virtua": "^0.49.3",
|
|
80
|
+
"vite": "^8.1.4",
|
|
81
|
+
"vitest": "^4.1.10"
|
|
82
|
+
},
|
|
83
|
+
"repository": {
|
|
84
|
+
"type": "git",
|
|
85
|
+
"url": "https://github.com/open-mercato/cezar"
|
|
86
|
+
},
|
|
87
|
+
"publishConfig": {
|
|
88
|
+
"access": "public"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Tarball bundle check (`npm run check:pack`, last leg of `npm run build`):
|
|
3
|
+
// fail loudly when the published package would ship without the built UI —
|
|
4
|
+
// phase R1 of the cockpit redesign once published a tarball with no cockpit
|
|
5
|
+
// in it, and this pins that bug class before every publish (prepublishOnly →
|
|
6
|
+
// build → check:pack).
|
|
7
|
+
//
|
|
8
|
+
// Asks npm itself what it would pack (`npm pack --dry-run --json`) and hands
|
|
9
|
+
// the file list to the pure, unit-tested decision in src/pack-check.ts.
|
|
10
|
+
// Imports from dist/, so it must run AFTER `tsc` — `npm run build` orders
|
|
11
|
+
// tsc → build:web → check:pack, which also guarantees web/dist exists in a
|
|
12
|
+
// fresh checkout by the time the check looks for it.
|
|
13
|
+
|
|
14
|
+
import { execFileSync } from 'node:child_process';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { findPackGaps } from '../dist/pack-check.js';
|
|
18
|
+
|
|
19
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
20
|
+
|
|
21
|
+
// Under `npm run`, npm_execpath is npm's own cli.js — running it through
|
|
22
|
+
// process.execPath works on every platform (no .cmd shim needed on Windows).
|
|
23
|
+
const npmExecpath = process.env.npm_execpath;
|
|
24
|
+
const packArgs = ['pack', '--dry-run', '--json'];
|
|
25
|
+
const npmCli = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
26
|
+
const stdout = npmExecpath
|
|
27
|
+
? execFileSync(process.execPath, [npmExecpath, ...packArgs], { cwd: repoRoot, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 })
|
|
28
|
+
: execFileSync(npmCli, packArgs, { cwd: repoRoot, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, shell: process.platform === 'win32' });
|
|
29
|
+
|
|
30
|
+
/** @type {{ files?: { path: string }[] }[]} */
|
|
31
|
+
const reports = JSON.parse(stdout);
|
|
32
|
+
const files = (reports[0]?.files ?? []).map((f) => f.path);
|
|
33
|
+
if (files.length === 0) {
|
|
34
|
+
console.error('check:pack: `npm pack --dry-run --json` reported no files — refusing to publish an empty package');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const gaps = findPackGaps(files);
|
|
39
|
+
if (gaps.length > 0) {
|
|
40
|
+
console.error('check:pack: the npm tarball would ship a broken cockpit:');
|
|
41
|
+
for (const gap of gaps) console.error(` - ${gap}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const uiFiles = files.filter((f) => f.startsWith('web/dist/')).length;
|
|
46
|
+
console.log(`check:pack ok — ${files.length} files, ${uiFiles} under web/dist (shell + assets present)`);
|
package/scripts/dev.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// One-command dev (`npm run dev`): boots the cockpit server (API) and the Vite
|
|
3
|
+
// dev server together, then lets Vite open the browser once it's serving —
|
|
4
|
+
// same "run one thing, get a working cockpit in the browser" flow the pre-Vite
|
|
5
|
+
// UI had, now with HMR.
|
|
6
|
+
//
|
|
7
|
+
// The API port is probed here and pinned into BOTH processes (server via -p,
|
|
8
|
+
// Vite proxy via CEZ_API_PORT — see web/app/vite.config.ts). Without that, a
|
|
9
|
+
// cockpit already sitting on 4321 (another repo, an older npm install) would
|
|
10
|
+
// silently answer the proxy with stale endpoints — 404s all over the UI.
|
|
11
|
+
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import { createServer } from 'node:net';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
18
|
+
|
|
19
|
+
const canListen = (port) =>
|
|
20
|
+
new Promise((done) => {
|
|
21
|
+
const probe = createServer();
|
|
22
|
+
probe.once('error', () => done(false));
|
|
23
|
+
probe.once('listening', () => probe.close(() => done(true)));
|
|
24
|
+
probe.listen(port, '127.0.0.1');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
let apiPort = 4321;
|
|
28
|
+
while (!(await canListen(apiPort))) apiPort++;
|
|
29
|
+
if (apiPort !== 4321) console.log(`dev: port 4321 is taken (another cockpit?) — using ${apiPort}`);
|
|
30
|
+
|
|
31
|
+
// Under `npm run`, npm_execpath is npm's own cli.js — running it through
|
|
32
|
+
// process.execPath works on every platform (no .cmd shim needed on Windows).
|
|
33
|
+
const npmExecpath = process.env.npm_execpath;
|
|
34
|
+
const npmCli = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
35
|
+
const run = (script, extraArgs) => {
|
|
36
|
+
const args = ['run', script, '--', ...extraArgs];
|
|
37
|
+
const env = { ...process.env, CEZ_API_PORT: String(apiPort) };
|
|
38
|
+
return npmExecpath
|
|
39
|
+
? spawn(process.execPath, [npmExecpath, ...args], { cwd: repoRoot, stdio: 'inherit', env })
|
|
40
|
+
: spawn(npmCli, args, { cwd: repoRoot, stdio: 'inherit', env, shell: process.platform === 'win32' });
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const server = run('dev:server', ['--port', String(apiPort), '--no-open']);
|
|
44
|
+
|
|
45
|
+
// Give the API a moment to answer before Vite opens the browser, so the first
|
|
46
|
+
// paint has live data instead of proxy errors. Best-effort: on timeout Vite
|
|
47
|
+
// starts anyway and the proxy recovers on reload.
|
|
48
|
+
const deadline = Date.now() + 15_000;
|
|
49
|
+
while (Date.now() < deadline) {
|
|
50
|
+
if (server.exitCode !== null) break; // server died — the exit handler below reports it
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(`http://127.0.0.1:${apiPort}/api/health`);
|
|
53
|
+
if (res.ok) break;
|
|
54
|
+
} catch {
|
|
55
|
+
// not listening yet
|
|
56
|
+
}
|
|
57
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const web = server.exitCode === null ? run('dev:web', ['--open']) : null;
|
|
61
|
+
|
|
62
|
+
// Either process dying takes the whole dev session down — a half-running
|
|
63
|
+
// cockpit (UI without API, or API without UI) just looks broken.
|
|
64
|
+
const children = [server, web].filter(Boolean);
|
|
65
|
+
let shuttingDown = false;
|
|
66
|
+
const shutdown = (code) => {
|
|
67
|
+
if (shuttingDown) return;
|
|
68
|
+
shuttingDown = true;
|
|
69
|
+
for (const child of children) if (child.exitCode === null) child.kill('SIGTERM');
|
|
70
|
+
process.exitCode = code;
|
|
71
|
+
};
|
|
72
|
+
for (const child of children) child.on('exit', (code) => shutdown(code ?? 0));
|
|
73
|
+
process.on('SIGINT', () => shutdown(0));
|
|
74
|
+
process.on('SIGTERM', () => shutdown(0));
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `npm run install-as-command` (+ `:global` and the `uninstall-as-command`
|
|
3
|
+
// counterpart): build this checkout and put a global `cezar`/`cez`/`cezar-cli`
|
|
4
|
+
// command on PATH pointing at THIS working tree — the local-dev equivalent of
|
|
5
|
+
// `npx cezar-cli`, with no publish and no `npx` download. Spec 013.
|
|
6
|
+
//
|
|
7
|
+
// --mode link build → `npm link` (live; `npm run build` refreshes it)
|
|
8
|
+
// --mode global build → `npm install --global .` (self-contained snapshot)
|
|
9
|
+
// --mode uninstall `npm rm --global @open-mercato/cezar`
|
|
10
|
+
// --no-build skip the build (relink an already-built dist)
|
|
11
|
+
//
|
|
12
|
+
// The install decisions live in the unit-tested src/install-as-command.ts
|
|
13
|
+
// (compiled to dist by the build); this script owns spawning + exit codes.
|
|
14
|
+
// npm is invoked through process.execPath + npm_execpath — same cross-platform
|
|
15
|
+
// pattern as scripts/dev.mjs / scripts/check-pack.mjs (no .cmd shim assumptions).
|
|
16
|
+
|
|
17
|
+
import { spawnSync, execFileSync } from 'node:child_process';
|
|
18
|
+
import { existsSync } from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
21
|
+
|
|
22
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
23
|
+
const argv = process.argv.slice(2);
|
|
24
|
+
|
|
25
|
+
const modeIdx = argv.indexOf('--mode');
|
|
26
|
+
const mode = modeIdx >= 0 ? argv[modeIdx + 1] : 'link';
|
|
27
|
+
const noBuild = argv.includes('--no-build');
|
|
28
|
+
if (!['link', 'global', 'uninstall'].includes(mode)) {
|
|
29
|
+
console.error(`install-as-command: unknown --mode "${mode}" (expected link | global | uninstall)`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// npm invocation shim (identical to dev.mjs / check-pack.mjs).
|
|
34
|
+
const npmExecpath = process.env.npm_execpath;
|
|
35
|
+
const npmCli = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
36
|
+
function npm(args, { capture = false } = {}) {
|
|
37
|
+
const viaNode = Boolean(npmExecpath);
|
|
38
|
+
const file = viaNode ? process.execPath : npmCli;
|
|
39
|
+
const full = viaNode ? [npmExecpath, ...args] : args;
|
|
40
|
+
const shell = !viaNode && process.platform === 'win32';
|
|
41
|
+
if (capture) {
|
|
42
|
+
return execFileSync(file, full, { cwd: repoRoot, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024, shell });
|
|
43
|
+
}
|
|
44
|
+
const res = spawnSync(file, full, { cwd: repoRoot, stdio: 'inherit', shell });
|
|
45
|
+
return res.status ?? 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const distDir = path.join(repoRoot, 'dist');
|
|
49
|
+
const build = mode !== 'uninstall' && !noBuild;
|
|
50
|
+
|
|
51
|
+
if (mode !== 'uninstall' && noBuild && !existsSync(path.join(distDir, 'index.js'))) {
|
|
52
|
+
console.error('install-as-command: --no-build was set but dist/ is missing — run once without --no-build first.');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (build) {
|
|
57
|
+
console.log('install-as-command: building (npm run build)…');
|
|
58
|
+
const code = npm(['run', 'build']);
|
|
59
|
+
if (code !== 0) {
|
|
60
|
+
console.error('install-as-command: build failed — nothing installed.');
|
|
61
|
+
process.exit(code);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Load the tested planner from dist. Present after any build; for uninstall on a
|
|
66
|
+
// never-built checkout it may be absent, so fall back to the known command.
|
|
67
|
+
let planner = null;
|
|
68
|
+
const plannerPath = path.join(distDir, 'install-as-command.js');
|
|
69
|
+
if (existsSync(plannerPath)) {
|
|
70
|
+
planner = await import(pathToFileURL(plannerPath).href);
|
|
71
|
+
}
|
|
72
|
+
const PACKAGE_NAME = planner?.PACKAGE_NAME ?? '@open-mercato/cezar';
|
|
73
|
+
const steps = planner
|
|
74
|
+
? planner.planInstall({ mode, build }).steps
|
|
75
|
+
: [{ args: ['rm', '--global', PACKAGE_NAME], label: `remove global ${PACKAGE_NAME}` }];
|
|
76
|
+
|
|
77
|
+
for (const step of steps) {
|
|
78
|
+
console.log(`install-as-command: npm ${step.args.join(' ')} (${step.label})`);
|
|
79
|
+
const code = npm(step.args);
|
|
80
|
+
if (code !== 0) {
|
|
81
|
+
if (mode === 'uninstall') {
|
|
82
|
+
// Idempotent: `npm rm -g` of an absent package is a no-op success.
|
|
83
|
+
console.log('install-as-command: nothing to uninstall (already absent).');
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
console.error(`install-as-command: \`npm ${step.args.join(' ')}\` failed (see npm output above).`);
|
|
87
|
+
console.error(
|
|
88
|
+
' If this is an EACCES on a root-owned global prefix, set a user-writable one\n' +
|
|
89
|
+
' (e.g. `npm config set prefix ~/.npm-global`) and retry — do NOT use sudo.',
|
|
90
|
+
);
|
|
91
|
+
process.exit(code);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (mode === 'uninstall') {
|
|
96
|
+
console.log('install-as-command: global cezar / cez / cezar-cli removed.');
|
|
97
|
+
process.exit(0);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Verify the shims and print a PATH hint.
|
|
101
|
+
let prefix = '';
|
|
102
|
+
try {
|
|
103
|
+
prefix = npm(['prefix', '--global'], { capture: true }).trim();
|
|
104
|
+
} catch {
|
|
105
|
+
// best-effort — the install already succeeded above
|
|
106
|
+
}
|
|
107
|
+
if (planner && prefix) {
|
|
108
|
+
const binDir = planner.globalBinDir(prefix, process.platform);
|
|
109
|
+
const present = planner.globalShimPaths(prefix, process.platform).filter((p) => existsSync(p));
|
|
110
|
+
const names = planner.BIN_NAMES.join(', ');
|
|
111
|
+
if (present.length === planner.BIN_NAMES.length) {
|
|
112
|
+
console.log(`\ninstall-as-command: installed ${names} → ${binDir}`);
|
|
113
|
+
} else {
|
|
114
|
+
console.log(`\ninstall-as-command: done, but only ${present.length}/${planner.BIN_NAMES.length} shims found in ${binDir}`);
|
|
115
|
+
}
|
|
116
|
+
console.log(` Ensure this is on your PATH:\n ${binDir}`);
|
|
117
|
+
console.log(' Then, from any repo: cezar --help');
|
|
118
|
+
} else {
|
|
119
|
+
console.log('\ninstall-as-command: done. Ensure your npm global bin dir is on PATH, then run `cezar --help`.');
|
|
120
|
+
}
|
|
@@ -0,0 +1,310 @@
|
|
|
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
|
+
import { appendFileSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
|
+
|
|
11
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12
|
+
const emit = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
|
|
13
|
+
|
|
14
|
+
// Testability hook: CEZ_MOCK_ARGS_FILE=<path> appends the argv this mock was
|
|
15
|
+
// spawned with (one JSON array per line), so tests and dry-run proofs can
|
|
16
|
+
// assert exactly what reached the CLI (e.g. `--append-system-prompt …`).
|
|
17
|
+
if (process.env.CEZ_MOCK_ARGS_FILE) {
|
|
18
|
+
try {
|
|
19
|
+
appendFileSync(process.env.CEZ_MOCK_ARGS_FILE, `${JSON.stringify(process.argv.slice(2))}\n`);
|
|
20
|
+
} catch {
|
|
21
|
+
// best effort — never break the mock over the hook
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
emit({ type: 'system', subtype: 'init' });
|
|
26
|
+
|
|
27
|
+
let turn = 0;
|
|
28
|
+
|
|
29
|
+
// A tiny generated PNG (320x200) standing in for a browser screenshot.
|
|
30
|
+
const MOCK_SCREENSHOT_B64 =
|
|
31
|
+
'iVBORw0KGgoAAAANSUhEUgAAAUAAAADICAIAAAAWZq/8AAACMklEQVR42u3csQnAIBRFUQdJY+b4tfvP4AhCLKyyghAMMRw4Ezz/bU0RBdhUMgEIGBAwIGAQMCBgQMCAgEHAgIABAYOAAQEDAgYEDAIGBAwsCLhfDdiUgEHAgIABAYOAAQEDAgYEDAIGBAwIGAT8vlEr/ImAQcACBgELGAQsYAQsYBCwgEHAAgYBe3IELGAQsIBBwAJGwAIGAQsYBCxgELCAEbCAQcACBgELGAQMAhYwCFjAIGABI2D/QgMCBgEDAgYEDAgYBAwIGBAwCBgQMCBgQMAgYEDAgIBBwCYAAQMCBgQMAgYEDAgYEDAIGBAwIGAQMCBgQMCAgEHAgIABAQMCBgEDAgYEDAIGBAwIGBAwCBgQMCBgQMAgYEDAgIBBwICAAQEDAgYBAwIGBAwCtgIIGBAwIGAQMCBgQMCAgEHAgIABAYOAeejMB/McjIAFLGABW0HAAhYwmhSwgAUsYAQsYAELGAELWMACRsACFrCAEbCABSxgBCxgAQtYwAhYwAIWMAIWsIAFjIAFLGABI2ABC1jACFjAAhYwAhawgAWMgAUsYAEjYAELWMACRsACFrCAEbCABSxgBCxgAQsYAQtYwAJGwAIWMCBgQMAgYEDAgIBBwICAAQEDAgYBAwIGBAwCBgQMCBgQMAgYEDAgYEDAIGBAwICAQcCAgAEBAwIGAQMCBgQMCBgEDAgYEDAIGBAwIGBAwCBgQMCAgAEBg4ABAQMCBgEDAgYEDAgYBAx8xQ1bxBr9kelHqgAAAABJRU5ErkJggg==';
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
// Spec 007: behave like an agent that read the handoff/todos instructions —
|
|
35
|
+
// append a progress line to CEZ_HANDOFF_FILE and a follow-up entry to
|
|
36
|
+
// CEZ_TODOS_FILE, so the inbox loop is testable without tokens.
|
|
37
|
+
function writeHandoffAndTodo() {
|
|
38
|
+
try {
|
|
39
|
+
const handoff = process.env.CEZ_HANDOFF_FILE;
|
|
40
|
+
if (handoff) {
|
|
41
|
+
const text = readFileSync(handoff, 'utf8');
|
|
42
|
+
const line = `- ${new Date().toISOString()} — mock: implemented the change (dry run)\n`;
|
|
43
|
+
const marker = '## Progress log\n';
|
|
44
|
+
const idx = text.indexOf(marker);
|
|
45
|
+
const next =
|
|
46
|
+
idx >= 0
|
|
47
|
+
? text.slice(0, idx + marker.length) + '\n' + line + text.slice(idx + marker.length).replace(/^\n+/, '')
|
|
48
|
+
: text + line;
|
|
49
|
+
writeFileSync(handoff, next, 'utf8');
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
// best effort — the mock still answers without a handoff file
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const todosFile = process.env.CEZ_TODOS_FILE;
|
|
56
|
+
if (todosFile) {
|
|
57
|
+
let items = [];
|
|
58
|
+
try {
|
|
59
|
+
items = JSON.parse(readFileSync(todosFile, 'utf8'));
|
|
60
|
+
} catch {
|
|
61
|
+
items = []; // missing or broken file — start fresh, append-only
|
|
62
|
+
}
|
|
63
|
+
if (!Array.isArray(items)) items = [];
|
|
64
|
+
items.push({
|
|
65
|
+
ts: new Date().toISOString(),
|
|
66
|
+
taskId: process.env.CEZ_TASK_ID,
|
|
67
|
+
summary: 'Follow up: verify the mock change',
|
|
68
|
+
suggestedPrompt: 'Verify the change from the previous task',
|
|
69
|
+
});
|
|
70
|
+
writeFileSync(todosFile, JSON.stringify(items, null, 2), 'utf8');
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
// best effort
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function respond(userText, imageCount) {
|
|
78
|
+
turn += 1;
|
|
79
|
+
await sleep(250);
|
|
80
|
+
// `mock:done` anywhere in the message → the reply ends with the CEZ:DONE
|
|
81
|
+
// completion marker (#347), so the auto-close path is testable dry.
|
|
82
|
+
const doneMarker = userText.includes('mock:done') ? '\n\nCEZ:DONE' : '';
|
|
83
|
+
// `mock:monitoring` → the reply ends with CEZ:MONITORING, the "still working
|
|
84
|
+
// on downstream work" marker (#490), so the monitoring-status path is testable dry.
|
|
85
|
+
const monitoringMarker = userText.includes('mock:monitoring') ? '\n\nCEZ:MONITORING' : '';
|
|
86
|
+
|
|
87
|
+
// `mock:slow` → hold the turn for ~25 s so queue states are observable.
|
|
88
|
+
if (userText.includes('mock:slow')) await sleep(25_000);
|
|
89
|
+
|
|
90
|
+
// `mock:md` → answer with a markdown-rich reply (#346 QA: tables, nested
|
|
91
|
+
// lists, emphasis, fences, quotes) instead of the scripted turn.
|
|
92
|
+
if (userText.includes('mock:md')) {
|
|
93
|
+
const md = [
|
|
94
|
+
'## Markdown fixture',
|
|
95
|
+
'This paragraph is soft-wrapped\nacross two source lines.',
|
|
96
|
+
'',
|
|
97
|
+
'| Column | Align | Result |',
|
|
98
|
+
'|:-------|:-----:|-------:|',
|
|
99
|
+
'| left `code` | *center* | **42** |',
|
|
100
|
+
'| ~~old~~ new | mid | $1.50 |',
|
|
101
|
+
'',
|
|
102
|
+
'- top item with *emphasis*',
|
|
103
|
+
' - nested child with `code`',
|
|
104
|
+
' - [x] deep done task',
|
|
105
|
+
' - [ ] nested todo',
|
|
106
|
+
'- second top',
|
|
107
|
+
'',
|
|
108
|
+
'> quoted intro',
|
|
109
|
+
'> - quoted list item',
|
|
110
|
+
'',
|
|
111
|
+
'```ts',
|
|
112
|
+
'const answer: number = 42;',
|
|
113
|
+
'```',
|
|
114
|
+
'Literal guards: snake_case_name, 2*3 and <script>alert(1)</script>.',
|
|
115
|
+
].join('\n');
|
|
116
|
+
emit({
|
|
117
|
+
type: 'assistant',
|
|
118
|
+
message: { role: 'assistant', content: [{ type: 'text', text: md }], usage: { input_tokens: 100, output_tokens: 200 } },
|
|
119
|
+
});
|
|
120
|
+
await sleep(100);
|
|
121
|
+
emit({ type: 'result', subtype: 'success', result: md, usage: { input_tokens: 100, output_tokens: 200 }, total_cost_usd: 0.001 });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Task auto-naming spec: a naming call (marked `[cez-namer]`) answers a
|
|
126
|
+
// deterministic short title + a PR classification of the sample number so
|
|
127
|
+
// dry-run tests can assert the full apply pipeline.
|
|
128
|
+
if (userText.includes('[cez-namer]')) {
|
|
129
|
+
const numbered = /(?:^|\D)(\d{1,7})(?:\D|$)/.exec(userText);
|
|
130
|
+
const name = JSON.stringify({
|
|
131
|
+
title: 'implementing cr fixes',
|
|
132
|
+
...(numbered ? { pr: Number(numbered[1]) } : {}),
|
|
133
|
+
});
|
|
134
|
+
emit({
|
|
135
|
+
type: 'assistant',
|
|
136
|
+
message: {
|
|
137
|
+
role: 'assistant',
|
|
138
|
+
content: [{ type: 'text', text: name }],
|
|
139
|
+
usage: { input_tokens: 200, output_tokens: 30 },
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
await sleep(50);
|
|
143
|
+
emit({
|
|
144
|
+
type: 'result',
|
|
145
|
+
subtype: 'success',
|
|
146
|
+
result: name,
|
|
147
|
+
usage: { input_tokens: 200, output_tokens: 30 },
|
|
148
|
+
total_cost_usd: 0.0002,
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Spec 008: a planning call (marked `[cez-planner]` in the user prompt)
|
|
154
|
+
// gets a canned chain plan. The `code-review` skill is deliberately made up:
|
|
155
|
+
// the planner's sanitizer strips unknown skills, and the step survives on
|
|
156
|
+
// its prompt — which is exactly the path worth exercising in dry runs.
|
|
157
|
+
if (userText.includes('[cez-planner]')) {
|
|
158
|
+
const plan = JSON.stringify({
|
|
159
|
+
steps: [
|
|
160
|
+
{ name: 'Implement', prompt: '{{task}}' },
|
|
161
|
+
{ name: 'Verify', command: 'npm test' },
|
|
162
|
+
{ name: 'Review', skill: 'code-review', prompt: 'Review the changes for {{task}}' },
|
|
163
|
+
],
|
|
164
|
+
rationale: 'Implement, verify with tests, then review.',
|
|
165
|
+
});
|
|
166
|
+
emit({
|
|
167
|
+
type: 'assistant',
|
|
168
|
+
message: {
|
|
169
|
+
role: 'assistant',
|
|
170
|
+
content: [{ type: 'text', text: plan }],
|
|
171
|
+
usage: { input_tokens: 500, output_tokens: 120 },
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
await sleep(100);
|
|
175
|
+
emit({
|
|
176
|
+
type: 'result',
|
|
177
|
+
subtype: 'success',
|
|
178
|
+
result: plan,
|
|
179
|
+
usage: { input_tokens: 500, output_tokens: 120 },
|
|
180
|
+
total_cost_usd: 0.0011,
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (turn === 1) {
|
|
186
|
+
// Leave a visible trace in the cwd (the task worktree under spec 006) so
|
|
187
|
+
// the Diff view has something real to show in dry runs. Exactly one line
|
|
188
|
+
// per spawned session: tests read this file back as a per-step trace, so
|
|
189
|
+
// a multi-line prompt must not become multiple lines here.
|
|
190
|
+
try {
|
|
191
|
+
const head = userText.replace(/\s+/g, ' ').trim().slice(0, 400);
|
|
192
|
+
appendFileSync('notes.md', `mock notes — ${new Date().toISOString()}: ${head}\n`);
|
|
193
|
+
} catch {
|
|
194
|
+
// read-only cwd — the mock still works, just without a diff
|
|
195
|
+
}
|
|
196
|
+
writeHandoffAndTodo();
|
|
197
|
+
emit({
|
|
198
|
+
type: 'assistant',
|
|
199
|
+
message: {
|
|
200
|
+
role: 'assistant',
|
|
201
|
+
content: [{ type: 'text', text: `Okay — looking into: ${userText.slice(0, 120)}` }],
|
|
202
|
+
usage: { input_tokens: 850, output_tokens: 40 },
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
await sleep(300);
|
|
206
|
+
emit({
|
|
207
|
+
type: 'assistant',
|
|
208
|
+
message: {
|
|
209
|
+
role: 'assistant',
|
|
210
|
+
content: [{ type: 'tool_use', id: `toolu_mock_${turn}`, name: 'Bash', input: { command: 'git status --short' } }],
|
|
211
|
+
usage: { input_tokens: 120, output_tokens: 55 },
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
await sleep(300);
|
|
215
|
+
emit({
|
|
216
|
+
type: 'user',
|
|
217
|
+
message: {
|
|
218
|
+
role: 'user',
|
|
219
|
+
content: [{ type: 'tool_result', tool_use_id: `toolu_mock_${turn}`, content: ' M src/example.ts' }],
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
await sleep(200);
|
|
223
|
+
// Agent screenshot in a tool result — exercises the image pipeline
|
|
224
|
+
// (persist to <id>-images/, serve via /api/runs/:id/images/, zoomable
|
|
225
|
+
// card + lightbox in the transcript).
|
|
226
|
+
emit({
|
|
227
|
+
type: 'assistant',
|
|
228
|
+
message: {
|
|
229
|
+
role: 'assistant',
|
|
230
|
+
content: [{ type: 'tool_use', id: `toolu_mock_shot_${turn}`, name: 'Screenshot', input: { name: 'repro-login-redirect.png' } }],
|
|
231
|
+
usage: { input_tokens: 80, output_tokens: 30 },
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
await sleep(200);
|
|
235
|
+
emit({
|
|
236
|
+
type: 'user',
|
|
237
|
+
message: {
|
|
238
|
+
role: 'user',
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: 'tool_result',
|
|
242
|
+
tool_use_id: `toolu_mock_shot_${turn}`,
|
|
243
|
+
content: [
|
|
244
|
+
{ type: 'text', text: 'Repro captured: after reload the session toast appears and you are back on /login.' },
|
|
245
|
+
{ type: 'image', source: { type: 'base64', media_type: 'image/png', data: MOCK_SCREENSHOT_B64 } },
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
await sleep(300);
|
|
252
|
+
emit({
|
|
253
|
+
type: 'assistant',
|
|
254
|
+
message: {
|
|
255
|
+
role: 'assistant',
|
|
256
|
+
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)${doneMarker}${monitoringMarker}` }],
|
|
257
|
+
usage: { input_tokens: 300, output_tokens: 90 },
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
await sleep(150);
|
|
261
|
+
emit({
|
|
262
|
+
type: 'result',
|
|
263
|
+
subtype: 'success',
|
|
264
|
+
result: 'Done with the first pass (dry run).',
|
|
265
|
+
usage: { input_tokens: 1270, output_tokens: 185 },
|
|
266
|
+
total_cost_usd: 0.0342,
|
|
267
|
+
});
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const imgNote = imageCount > 0 ? ` I can see ${imageCount} image(s) you attached.` : '';
|
|
272
|
+
emit({
|
|
273
|
+
type: 'assistant',
|
|
274
|
+
message: {
|
|
275
|
+
role: 'assistant',
|
|
276
|
+
content: [{ type: 'text', text: `Follow-up #${turn - 1} received: "${userText.slice(0, 100)}".${imgNote} Applied (dry run).${doneMarker}${monitoringMarker}` }],
|
|
277
|
+
usage: { input_tokens: 200, output_tokens: 60 },
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
await sleep(150);
|
|
281
|
+
emit({
|
|
282
|
+
type: 'result',
|
|
283
|
+
subtype: 'success',
|
|
284
|
+
result: `Follow-up #${turn - 1} handled (dry run).`,
|
|
285
|
+
usage: { input_tokens: 200, output_tokens: 60 },
|
|
286
|
+
total_cost_usd: 0.0051,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const rl = createInterface({ input: process.stdin });
|
|
291
|
+
let queue = Promise.resolve();
|
|
292
|
+
rl.on('line', (line) => {
|
|
293
|
+
const trimmed = line.trim();
|
|
294
|
+
if (!trimmed) return;
|
|
295
|
+
let userText = '(unparseable message)';
|
|
296
|
+
let imageCount = 0;
|
|
297
|
+
try {
|
|
298
|
+
const msg = JSON.parse(trimmed);
|
|
299
|
+
const blocks = msg?.message?.content ?? [];
|
|
300
|
+
userText = blocks.filter((b) => b.type === 'text').map((b) => b.text).join('\n') || '(no text)';
|
|
301
|
+
imageCount = blocks.filter((b) => b.type === 'image').length;
|
|
302
|
+
} catch {
|
|
303
|
+
// keep placeholders
|
|
304
|
+
}
|
|
305
|
+
queue = queue.then(() => respond(userText, imageCount));
|
|
306
|
+
});
|
|
307
|
+
rl.on('close', () => {
|
|
308
|
+
// EOF = session over; finish any in-flight turn then exit cleanly.
|
|
309
|
+
queue.then(() => process.exit(0));
|
|
310
|
+
});
|