@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
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type TaskRefs } from './task-refs.js';
|
|
2
|
+
/**
|
|
3
|
+
* The one-shot LLM namer (spec 2026-07-17-task-auto-naming): turns a task's
|
|
4
|
+
* INTENT — skill + arguments + prompt + PR/issue context, never the agent's
|
|
5
|
+
* streamed words — into a short `<number>: <gerund phrase>` display title with
|
|
6
|
+
* structured, regex-cross-checked PR/issue numbers. Mirrors the planner (spec
|
|
7
|
+
* 008): `[cez-namer]` dry-run marker, strict JSON, never blocks, never fails a
|
|
8
|
+
* run. This module is the pure half; the runner call lives in `generateRunName`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const NAMER_TIMEOUT_MS = 20000;
|
|
11
|
+
/** Title budget in code points — the tasks table shows ~20-25 chars, 40 is the hard cap. */
|
|
12
|
+
export declare const TITLE_MAX = 40;
|
|
13
|
+
/**
|
|
14
|
+
* Master switch for ALL LLM naming (creation-time and live refresh):
|
|
15
|
+
* `CEZ_AUTONAME=0` kills it outright; under `CEZ_DRY_RUN=1` naming is off by
|
|
16
|
+
* default too (the mock's canned title would REPLACE honest heuristic titles
|
|
17
|
+
* in demos and e2e) unless `CEZ_AUTONAME=1` forces it — the hook the dry-run
|
|
18
|
+
* naming tests use.
|
|
19
|
+
*/
|
|
20
|
+
export declare function autoNamingActive(env?: NodeJS.ProcessEnv): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Live title updates switch (owner decision on PR #479 — deliberately ON by
|
|
23
|
+
* default, deviating from the cost-opt-in house rule; cost is bounded by the
|
|
24
|
+
* cheap `namerModel`, one call per turn end, and the skip conditions in
|
|
25
|
+
* `RunManager.recordTurnEnd`). Precedence: `config.liveTitleUpdates` (the
|
|
26
|
+
* Settings toggle) wins over the `CEZ_TITLE_UPDATES` env default (`'0'` = off)
|
|
27
|
+
* wins over the built-in ON.
|
|
28
|
+
*/
|
|
29
|
+
export declare function liveTitleUpdatesEnabled(config: {
|
|
30
|
+
liveTitleUpdates?: boolean;
|
|
31
|
+
}, env?: NodeJS.ProcessEnv): boolean;
|
|
32
|
+
export declare const NAMER_SYSTEM_PROMPT: string;
|
|
33
|
+
export interface NamerContext {
|
|
34
|
+
task: string;
|
|
35
|
+
skillName?: string;
|
|
36
|
+
skillDescription?: string;
|
|
37
|
+
/** Live refresh only (spec step 3): the just-finished turn's text. */
|
|
38
|
+
turnText?: string;
|
|
39
|
+
/** Live refresh only: current `git diff --shortstat` line. */
|
|
40
|
+
diffStat?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface NameResult {
|
|
43
|
+
titleSummary: string;
|
|
44
|
+
prNumber?: number;
|
|
45
|
+
issueNumber?: number;
|
|
46
|
+
}
|
|
47
|
+
/** The `[cez-namer]` marker lets the CEZ_DRY_RUN mock recognize a naming call. */
|
|
48
|
+
export declare function buildNamerPrompt(ctx: NamerContext): string;
|
|
49
|
+
/**
|
|
50
|
+
* Anti-hallucination cross-check: the model's `pr`/`issue` is accepted only
|
|
51
|
+
* when the regex layer agrees or the number literally occurs in the task text;
|
|
52
|
+
* on any disagreement the regex wins. The regex's own explicit findings always
|
|
53
|
+
* carry through, so a lying model can add nothing and remove nothing.
|
|
54
|
+
*/
|
|
55
|
+
export declare function crossCheckRefs(raw: {
|
|
56
|
+
pr?: number;
|
|
57
|
+
issue?: number;
|
|
58
|
+
}, task: string, refs: TaskRefs): {
|
|
59
|
+
prNumber?: number;
|
|
60
|
+
issueNumber?: number;
|
|
61
|
+
};
|
|
62
|
+
/** Enforce the title contract regardless of what the model produced. */
|
|
63
|
+
export declare function postValidateTitle(title: string, refNumber?: number): string;
|
|
64
|
+
/**
|
|
65
|
+
* The one-shot runner call: never throws, never blocks a run — every failure
|
|
66
|
+
* path (runner error, timeout, junk twice) answers null and the caller keeps
|
|
67
|
+
* the heuristic title. `namerModel` is a Claude alias, so it is passed only
|
|
68
|
+
* when the namer runs on Claude (the `plannerModel` precedent).
|
|
69
|
+
*/
|
|
70
|
+
export declare function generateRunName(repoRoot: string, ctx: NamerContext): Promise<NameResult | null>;
|
|
71
|
+
/**
|
|
72
|
+
* Raw model text → validated NameResult, or null when the answer is junk
|
|
73
|
+
* (caller retries once, then keeps the heuristic title).
|
|
74
|
+
*/
|
|
75
|
+
export declare function composeNameResult(rawText: string, ctx: NamerContext): NameResult | null;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { loadConfig } from '../config.js';
|
|
3
|
+
import { createRunner } from '../core/runner-factory.js';
|
|
4
|
+
import { parseStructured } from '../planner.js';
|
|
5
|
+
import { extractTaskRefs, refineTaskRefs, titleRefNumber } from './task-refs.js';
|
|
6
|
+
/**
|
|
7
|
+
* The one-shot LLM namer (spec 2026-07-17-task-auto-naming): turns a task's
|
|
8
|
+
* INTENT — skill + arguments + prompt + PR/issue context, never the agent's
|
|
9
|
+
* streamed words — into a short `<number>: <gerund phrase>` display title with
|
|
10
|
+
* structured, regex-cross-checked PR/issue numbers. Mirrors the planner (spec
|
|
11
|
+
* 008): `[cez-namer]` dry-run marker, strict JSON, never blocks, never fails a
|
|
12
|
+
* run. This module is the pure half; the runner call lives in `generateRunName`.
|
|
13
|
+
*/
|
|
14
|
+
export const NAMER_TIMEOUT_MS = 20_000;
|
|
15
|
+
/** Title budget in code points — the tasks table shows ~20-25 chars, 40 is the hard cap. */
|
|
16
|
+
export const TITLE_MAX = 40;
|
|
17
|
+
/**
|
|
18
|
+
* Master switch for ALL LLM naming (creation-time and live refresh):
|
|
19
|
+
* `CEZ_AUTONAME=0` kills it outright; under `CEZ_DRY_RUN=1` naming is off by
|
|
20
|
+
* default too (the mock's canned title would REPLACE honest heuristic titles
|
|
21
|
+
* in demos and e2e) unless `CEZ_AUTONAME=1` forces it — the hook the dry-run
|
|
22
|
+
* naming tests use.
|
|
23
|
+
*/
|
|
24
|
+
export function autoNamingActive(env = process.env) {
|
|
25
|
+
if (env.CEZ_AUTONAME === '0')
|
|
26
|
+
return false;
|
|
27
|
+
if (env.CEZ_DRY_RUN === '1' && env.CEZ_AUTONAME !== '1')
|
|
28
|
+
return false;
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Live title updates switch (owner decision on PR #479 — deliberately ON by
|
|
33
|
+
* default, deviating from the cost-opt-in house rule; cost is bounded by the
|
|
34
|
+
* cheap `namerModel`, one call per turn end, and the skip conditions in
|
|
35
|
+
* `RunManager.recordTurnEnd`). Precedence: `config.liveTitleUpdates` (the
|
|
36
|
+
* Settings toggle) wins over the `CEZ_TITLE_UPDATES` env default (`'0'` = off)
|
|
37
|
+
* wins over the built-in ON.
|
|
38
|
+
*/
|
|
39
|
+
export function liveTitleUpdatesEnabled(config, env = process.env) {
|
|
40
|
+
if (config.liveTitleUpdates !== undefined)
|
|
41
|
+
return config.liveTitleUpdates;
|
|
42
|
+
return env.CEZ_TITLE_UPDATES !== '0';
|
|
43
|
+
}
|
|
44
|
+
export const NAMER_SYSTEM_PROMPT = 'You are a task-naming assistant for an AI coding agent cockpit. Respond with ONLY a JSON object: ' +
|
|
45
|
+
'{"title": string, "pr"?: number, "issue"?: number}. Rules: "title" is a terse lowercase gerund phrase ' +
|
|
46
|
+
'naming the task INTENT (e.g. "implementing cr fixes", "verifying pr ui"), at most 40 characters, no ' +
|
|
47
|
+
'trailing period, and NEVER a restatement of an assistant reply. When the task is about a GitHub pull ' +
|
|
48
|
+
'request or issue, set "pr" or "issue" to that number and do NOT repeat the number inside the title ' +
|
|
49
|
+
'text. Never invent a number that is not in the task.';
|
|
50
|
+
const namerResponseSchema = z.object({
|
|
51
|
+
title: z.string().min(1),
|
|
52
|
+
pr: z.number().int().positive().optional(),
|
|
53
|
+
issue: z.number().int().positive().optional(),
|
|
54
|
+
});
|
|
55
|
+
/** The `[cez-namer]` marker lets the CEZ_DRY_RUN mock recognize a naming call. */
|
|
56
|
+
export function buildNamerPrompt(ctx) {
|
|
57
|
+
const refs = advisoryRefs(ctx);
|
|
58
|
+
const lines = [
|
|
59
|
+
'[cez-namer] Name this task.',
|
|
60
|
+
'',
|
|
61
|
+
...(ctx.skillName ? [`Selected skill: /${ctx.skillName}`] : []),
|
|
62
|
+
...(ctx.skillDescription ? [`Skill description: ${ctx.skillDescription}`] : []),
|
|
63
|
+
'Task:',
|
|
64
|
+
clip(ctx.task, 500),
|
|
65
|
+
];
|
|
66
|
+
if (refs.length)
|
|
67
|
+
lines.push('', `References found programmatically (advisory): ${refs.join(', ')}`);
|
|
68
|
+
if (ctx.turnText)
|
|
69
|
+
lines.push('', 'Latest progress (context only — do not quote it):', clip(ctx.turnText, 400));
|
|
70
|
+
if (ctx.diffStat)
|
|
71
|
+
lines.push('', `Change size so far: ${ctx.diffStat}`);
|
|
72
|
+
return lines.join('\n');
|
|
73
|
+
}
|
|
74
|
+
function advisoryRefs(ctx) {
|
|
75
|
+
const refs = refineTaskRefs(extractTaskRefs(ctx.task), ctx.skillName);
|
|
76
|
+
const out = [];
|
|
77
|
+
if (refs.prNumber !== undefined)
|
|
78
|
+
out.push(`pr ${refs.prNumber}`);
|
|
79
|
+
if (refs.issueNumber !== undefined)
|
|
80
|
+
out.push(`issue ${refs.issueNumber}`);
|
|
81
|
+
if (refs.ambiguousNumber !== undefined)
|
|
82
|
+
out.push(`number ${refs.ambiguousNumber} (kind unknown)`);
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
function clip(text, max) {
|
|
86
|
+
const chars = [...text.trim()];
|
|
87
|
+
return chars.length > max ? `${chars.slice(0, max).join('')}…` : chars.join('');
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Anti-hallucination cross-check: the model's `pr`/`issue` is accepted only
|
|
91
|
+
* when the regex layer agrees or the number literally occurs in the task text;
|
|
92
|
+
* on any disagreement the regex wins. The regex's own explicit findings always
|
|
93
|
+
* carry through, so a lying model can add nothing and remove nothing.
|
|
94
|
+
*/
|
|
95
|
+
export function crossCheckRefs(raw, task, refs) {
|
|
96
|
+
const inTask = (n) => n !== undefined && new RegExp(`(^|\\D)${n}(\\D|$)`).test(task);
|
|
97
|
+
const claimedPr = refs.prNumber ?? (raw.pr !== undefined && (inTask(raw.pr) || refs.ambiguousNumber === raw.pr) ? raw.pr : undefined);
|
|
98
|
+
const claimedIssue = refs.issueNumber ?? (raw.issue !== undefined && (inTask(raw.issue) || refs.ambiguousNumber === raw.issue) ? raw.issue : undefined);
|
|
99
|
+
return {
|
|
100
|
+
...(claimedPr !== undefined ? { prNumber: claimedPr } : {}),
|
|
101
|
+
...(claimedIssue !== undefined ? { issueNumber: claimedIssue } : {}),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** Enforce the title contract regardless of what the model produced. */
|
|
105
|
+
export function postValidateTitle(title, refNumber) {
|
|
106
|
+
let t = title.trim().replace(/\.+$/, '').replace(/\s+/g, ' ');
|
|
107
|
+
// The number leads; strip a model-repeated number from the phrase first.
|
|
108
|
+
if (refNumber !== undefined) {
|
|
109
|
+
t = t.replace(new RegExp(`^#?${refNumber}\\s*[:—-]?\\s*`), '');
|
|
110
|
+
}
|
|
111
|
+
if (t)
|
|
112
|
+
t = t[0]?.toLowerCase() + t.slice(1);
|
|
113
|
+
const chars = [...t];
|
|
114
|
+
if (chars.length > TITLE_MAX)
|
|
115
|
+
t = `${chars.slice(0, TITLE_MAX - 1).join('').trimEnd()}…`;
|
|
116
|
+
return refNumber !== undefined ? `${refNumber}: ${t}`.trim().replace(/:\s*$/, ':') : t;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The one-shot runner call: never throws, never blocks a run — every failure
|
|
120
|
+
* path (runner error, timeout, junk twice) answers null and the caller keeps
|
|
121
|
+
* the heuristic title. `namerModel` is a Claude alias, so it is passed only
|
|
122
|
+
* when the namer runs on Claude (the `plannerModel` precedent).
|
|
123
|
+
*/
|
|
124
|
+
export async function generateRunName(repoRoot, ctx) {
|
|
125
|
+
try {
|
|
126
|
+
const config = await loadConfig(repoRoot);
|
|
127
|
+
const runner = createRunner(config.defaultRunner);
|
|
128
|
+
const model = config.defaultRunner === 'claude' ? config.namerModel : undefined;
|
|
129
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
130
|
+
const result = await runner.run({
|
|
131
|
+
systemPrompt: NAMER_SYSTEM_PROMPT,
|
|
132
|
+
userPrompt: buildNamerPrompt(ctx),
|
|
133
|
+
cwd: repoRoot,
|
|
134
|
+
allowedTools: [],
|
|
135
|
+
model,
|
|
136
|
+
timeoutMs: NAMER_TIMEOUT_MS,
|
|
137
|
+
// Shadow the argv-capture hook: dry-run tests capture the AGENT's argv
|
|
138
|
+
// through CEZ_MOCK_ARGS_FILE; the namer's bookkeeping call must not
|
|
139
|
+
// interleave lines into that file.
|
|
140
|
+
env: { CEZ_MOCK_ARGS_FILE: '' },
|
|
141
|
+
});
|
|
142
|
+
const composed = composeNameResult(result.text, ctx);
|
|
143
|
+
if (composed)
|
|
144
|
+
return composed;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// runner unavailable / crashed — the heuristic title stays
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Raw model text → validated NameResult, or null when the answer is junk
|
|
154
|
+
* (caller retries once, then keeps the heuristic title).
|
|
155
|
+
*/
|
|
156
|
+
export function composeNameResult(rawText, ctx) {
|
|
157
|
+
const parsed = parseStructured(rawText, namerResponseSchema);
|
|
158
|
+
if (!parsed)
|
|
159
|
+
return null;
|
|
160
|
+
const refs = refineTaskRefs(extractTaskRefs(ctx.task), ctx.skillName);
|
|
161
|
+
const checked = crossCheckRefs(parsed, ctx.task, refs);
|
|
162
|
+
const refNumber = checked.prNumber ?? checked.issueNumber ?? titleRefNumber(refs);
|
|
163
|
+
const titleSummary = postValidateTitle(parsed.title, refNumber);
|
|
164
|
+
if (!titleSummary || titleSummary === String(refNumber ?? ''))
|
|
165
|
+
return null;
|
|
166
|
+
return { titleSummary, ...checked };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=auto-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-name.js","sourceRoot":"","sources":["../../src/runs/auto-name.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { RunRecord } from './store.js';
|
|
2
|
+
/** A run is reclaimable when it is finished, still has a materialized worktree
|
|
3
|
+
* directory, and has not already been reclaimed. */
|
|
4
|
+
export declare function isReclaimable(run: RunRecord): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Given every run and the keep-count `keep`, return the ids of the finished
|
|
7
|
+
* worktrees whose *directory* should be reclaimed: keep the `keep`
|
|
8
|
+
* most-recently-finished reclaimable worktrees, reclaim the rest.
|
|
9
|
+
*
|
|
10
|
+
* `keep === 0` means "unlimited — never auto-reclaim" and returns `[]`.
|
|
11
|
+
* Pure: no I/O, no mutation of the input.
|
|
12
|
+
*/
|
|
13
|
+
export declare function selectReclaimableWorktrees(runs: readonly RunRecord[], keep: number): string[];
|
|
14
|
+
/** The slice of the runs store the enforcer needs. Kept structural so the
|
|
15
|
+
* enforcer stays easy to test and never imports the concrete store. */
|
|
16
|
+
export interface RetentionStore {
|
|
17
|
+
listRuns(): RunRecord[];
|
|
18
|
+
updateRun(id: string, patch: {
|
|
19
|
+
worktreeReclaimedAt?: string;
|
|
20
|
+
}): unknown;
|
|
21
|
+
}
|
|
22
|
+
/** The slice of the store the re-materializer needs. */
|
|
23
|
+
export interface RematerializeStore {
|
|
24
|
+
getRun(id: string): RunRecord | undefined;
|
|
25
|
+
updateRun(id: string, patch: {
|
|
26
|
+
worktreeReclaimedAt?: string;
|
|
27
|
+
}): unknown;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* If retention (#483) reclaimed this run's worktree — branch kept, directory
|
|
31
|
+
* gone, `worktreeReclaimedAt` stamped — re-materialize the directory (via the
|
|
32
|
+
* idempotent `createWorktree`, which reattaches the surviving `cez/<id8>`
|
|
33
|
+
* branch) and CLEAR the stamp. Called on the resume/continue path so a resumed
|
|
34
|
+
* run regains its isolated tree and becomes eligible for retention again;
|
|
35
|
+
* without it the run would keep a directory on disk while staying invisible to
|
|
36
|
+
* the enforcer forever (a leak). Returns true when it re-materialized.
|
|
37
|
+
* Best-effort: never throws (the caller falls back to the repo root).
|
|
38
|
+
*/
|
|
39
|
+
export declare function rematerializeReclaimedWorktree(repoRoot: string, store: RematerializeStore, runId: string): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Enforce the retention budget: reclaim the *directory* of every over-limit
|
|
42
|
+
* finished worktree (branch kept via `removeWorktree` without the branch arg),
|
|
43
|
+
* stamping `worktreeReclaimedAt` on each run actually reclaimed. Returns the
|
|
44
|
+
* reclaimed run ids (for logging/SSE).
|
|
45
|
+
*
|
|
46
|
+
* Never throws (helper discipline). `removeWorktree` is best-effort and does not
|
|
47
|
+
* report failure, so a run is stamped only once its directory is confirmed gone
|
|
48
|
+
* — a locked/permission failure leaves the stamp unset so the next pass retries.
|
|
49
|
+
* Idempotent under races: `removeWorktree` is `--force` + `prune` and a repeated
|
|
50
|
+
* stamp is harmless.
|
|
51
|
+
*/
|
|
52
|
+
export interface ReclaimOptions {
|
|
53
|
+
/** Timestamp source for the stamp — injectable for deterministic tests. */
|
|
54
|
+
now?: () => string;
|
|
55
|
+
/** Directory reclaimer — defaults to the real `removeWorktree` (branch kept).
|
|
56
|
+
* Injectable so tests can exercise the "removal failed" branch without brittle
|
|
57
|
+
* filesystem-permission tricks. */
|
|
58
|
+
remove?: (repoRoot: string, worktreePath: string) => Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
export declare function reclaimWorktrees(repoRoot: string, store: RetentionStore, keep: number, opts?: ReclaimOptions): Promise<string[]>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Count-based worktree retention (#483). A busy cockpit leaves one full repo
|
|
2
|
+
// checkout per finished task under `.ai/cezar/worktrees/<runId>`; nothing bounds
|
|
3
|
+
// the total, so disk saturates. This module decides *which* finished worktrees
|
|
4
|
+
// to reclaim (directory only — the `cez/<id8>` branch is kept, so the work stays
|
|
5
|
+
// recoverable) and the thin I/O enforcer that performs the reclaim. The selector
|
|
6
|
+
// is pure and unit-testable; the enforcer never throws (helper discipline).
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { createWorktree, removeWorktree } from '../git-worktree.js';
|
|
9
|
+
/** The "finished" status set — mirrors `RunStore.archiveFinished`. A run at the
|
|
10
|
+
* `review` gate is deliberately excluded: it still needs its worktree to render
|
|
11
|
+
* the diff and open a draft PR, so reclaiming it would break the gate. */
|
|
12
|
+
const FINISHED = new Set(['done', 'failed', 'cancelled']);
|
|
13
|
+
/** Recency key for retention ordering: when a run finished, falling back to when
|
|
14
|
+
* it was created (a finished run should always have `finishedAt`, but old
|
|
15
|
+
* records may not). Lexicographic compare is correct for ISO-8601 timestamps. */
|
|
16
|
+
function recencyKey(run) {
|
|
17
|
+
return run.finishedAt ?? run.createdAt;
|
|
18
|
+
}
|
|
19
|
+
/** A run is reclaimable when it is finished, still has a materialized worktree
|
|
20
|
+
* directory, and has not already been reclaimed. */
|
|
21
|
+
export function isReclaimable(run) {
|
|
22
|
+
return FINISHED.has(run.status) && !!run.worktreePath && !run.worktreeReclaimedAt;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Given every run and the keep-count `keep`, return the ids of the finished
|
|
26
|
+
* worktrees whose *directory* should be reclaimed: keep the `keep`
|
|
27
|
+
* most-recently-finished reclaimable worktrees, reclaim the rest.
|
|
28
|
+
*
|
|
29
|
+
* `keep === 0` means "unlimited — never auto-reclaim" and returns `[]`.
|
|
30
|
+
* Pure: no I/O, no mutation of the input.
|
|
31
|
+
*/
|
|
32
|
+
export function selectReclaimableWorktrees(runs, keep) {
|
|
33
|
+
if (!Number.isFinite(keep) || keep <= 0)
|
|
34
|
+
return [];
|
|
35
|
+
const reclaimable = runs
|
|
36
|
+
.filter(isReclaimable)
|
|
37
|
+
.sort((a, b) => (recencyKey(a) < recencyKey(b) ? 1 : recencyKey(a) > recencyKey(b) ? -1 : 0));
|
|
38
|
+
return reclaimable.slice(keep).map((r) => r.id);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* If retention (#483) reclaimed this run's worktree — branch kept, directory
|
|
42
|
+
* gone, `worktreeReclaimedAt` stamped — re-materialize the directory (via the
|
|
43
|
+
* idempotent `createWorktree`, which reattaches the surviving `cez/<id8>`
|
|
44
|
+
* branch) and CLEAR the stamp. Called on the resume/continue path so a resumed
|
|
45
|
+
* run regains its isolated tree and becomes eligible for retention again;
|
|
46
|
+
* without it the run would keep a directory on disk while staying invisible to
|
|
47
|
+
* the enforcer forever (a leak). Returns true when it re-materialized.
|
|
48
|
+
* Best-effort: never throws (the caller falls back to the repo root).
|
|
49
|
+
*/
|
|
50
|
+
export async function rematerializeReclaimedWorktree(repoRoot, store, runId) {
|
|
51
|
+
const run = store.getRun(runId);
|
|
52
|
+
if (!run?.worktreePath || !run.worktreeReclaimedAt || existsSync(run.worktreePath))
|
|
53
|
+
return false;
|
|
54
|
+
try {
|
|
55
|
+
await createWorktree(repoRoot, runId, run.baseBranch ?? 'HEAD');
|
|
56
|
+
store.updateRun(runId, { worktreeReclaimedAt: undefined });
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export async function reclaimWorktrees(repoRoot, store, keep, opts = {}) {
|
|
64
|
+
const now = opts.now ?? (() => new Date().toISOString());
|
|
65
|
+
const remove = opts.remove ?? ((root, path) => removeWorktree(root, path)); // branch kept
|
|
66
|
+
const runs = store.listRuns();
|
|
67
|
+
const byId = new Map(runs.map((r) => [r.id, r]));
|
|
68
|
+
const reclaimed = [];
|
|
69
|
+
for (const id of selectReclaimableWorktrees(runs, keep)) {
|
|
70
|
+
const run = byId.get(id);
|
|
71
|
+
if (!run?.worktreePath)
|
|
72
|
+
continue;
|
|
73
|
+
try {
|
|
74
|
+
await remove(repoRoot, run.worktreePath);
|
|
75
|
+
if (existsSync(run.worktreePath))
|
|
76
|
+
continue; // reclaim failed; retry next pass
|
|
77
|
+
store.updateRun(id, { worktreeReclaimedAt: now() });
|
|
78
|
+
reclaimed.push(id);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// best-effort: never let retention crash a terminal transition or startup.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return reclaimed;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=retention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retention.js","sourceRoot":"","sources":["../../src/runs/retention.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional diff-first review gate (#489, spec `2026-07-18-optional-review-gate`).
|
|
3
|
+
*
|
|
4
|
+
* The review gate (spec 009) used to fire for EVERY successful run whose worktree
|
|
5
|
+
* held changes — parking it at `review` until a human accepts. This resolver makes
|
|
6
|
+
* that opt-in. It mirrors `liveTitleUpdatesEnabled` (`src/runs/auto-name.ts`) but
|
|
7
|
+
* with the OPPOSITE default: OFF unless explicitly turned on.
|
|
8
|
+
*
|
|
9
|
+
* Precedence: the `config.reviewGate` Settings toggle wins when set; otherwise the
|
|
10
|
+
* `CEZ_REVIEW_GATE` env decides — enabled ONLY by the exact string `'1'` (anything
|
|
11
|
+
* else, including `'true'`/`'yes'`/unset, is off); otherwise off.
|
|
12
|
+
*
|
|
13
|
+
* Autonomy is applied by the callers, not here: a run parks at `review` only when
|
|
14
|
+
* `worktreeHasDiff && reviewGateEnabled(config) && !run.autonomous`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function reviewGateEnabled(config: {
|
|
17
|
+
reviewGate?: boolean;
|
|
18
|
+
}, env?: NodeJS.ProcessEnv): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional diff-first review gate (#489, spec `2026-07-18-optional-review-gate`).
|
|
3
|
+
*
|
|
4
|
+
* The review gate (spec 009) used to fire for EVERY successful run whose worktree
|
|
5
|
+
* held changes — parking it at `review` until a human accepts. This resolver makes
|
|
6
|
+
* that opt-in. It mirrors `liveTitleUpdatesEnabled` (`src/runs/auto-name.ts`) but
|
|
7
|
+
* with the OPPOSITE default: OFF unless explicitly turned on.
|
|
8
|
+
*
|
|
9
|
+
* Precedence: the `config.reviewGate` Settings toggle wins when set; otherwise the
|
|
10
|
+
* `CEZ_REVIEW_GATE` env decides — enabled ONLY by the exact string `'1'` (anything
|
|
11
|
+
* else, including `'true'`/`'yes'`/unset, is off); otherwise off.
|
|
12
|
+
*
|
|
13
|
+
* Autonomy is applied by the callers, not here: a run parks at `review` only when
|
|
14
|
+
* `worktreeHasDiff && reviewGateEnabled(config) && !run.autonomous`.
|
|
15
|
+
*/
|
|
16
|
+
export function reviewGateEnabled(config, env = process.env) {
|
|
17
|
+
if (config.reviewGate !== undefined)
|
|
18
|
+
return config.reviewGate;
|
|
19
|
+
return env.CEZ_REVIEW_GATE === '1';
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=review-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-gate.js","sourceRoot":"","sources":["../../src/runs/review-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAgC,EAChC,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,UAAU,CAAC;IAC9D,OAAO,GAAG,CAAC,eAAe,KAAK,GAAG,CAAC;AACrC,CAAC"}
|