tickmarkr 1.36.0 → 1.38.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/LICENSE +21 -0
- package/README.md +83 -207
- package/dist/adapters/claude-code.js +4 -4
- package/dist/adapters/codex.js +1 -1
- package/dist/adapters/fake.d.ts +2 -2
- package/dist/adapters/fake.js +8 -8
- package/dist/adapters/grok.js +5 -5
- package/dist/adapters/model-lints.d.ts +4 -4
- package/dist/adapters/model-lints.js +1 -1
- package/dist/adapters/prompt.js +8 -13
- package/dist/adapters/registry.d.ts +8 -8
- package/dist/adapters/registry.js +5 -5
- package/dist/adapters/types.d.ts +3 -3
- package/dist/adapters/types.js +1 -1
- package/dist/brand.d.ts +2 -0
- package/dist/brand.js +17 -0
- package/dist/cli/commands/approve.js +3 -3
- package/dist/cli/commands/compile.js +1 -1
- package/dist/cli/commands/doctor.js +3 -3
- package/dist/cli/commands/init.js +14 -9
- package/dist/cli/commands/profile.js +6 -6
- package/dist/cli/commands/status.js +2 -1
- package/dist/cli/commands/unlock.js +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +9 -7
- package/dist/compile/gsd.js +1 -1
- package/dist/compile/index.js +9 -3
- package/dist/compile/native.d.ts +2 -0
- package/dist/compile/native.js +4 -2
- package/dist/config/config.d.ts +6 -6
- package/dist/config/config.js +13 -12
- package/dist/drivers/herdr.js +5 -5
- package/dist/drivers/index.d.ts +2 -2
- package/dist/drivers/subprocess.js +3 -3
- package/dist/drivers/types.js +4 -6
- package/dist/gates/acceptance.js +1 -1
- package/dist/gates/baseline.d.ts +2 -2
- package/dist/gates/llm.js +12 -9
- package/dist/gates/review.d.ts +2 -2
- package/dist/gates/review.js +1 -1
- package/dist/gates/run-gates.d.ts +2 -2
- package/dist/graph/graph.d.ts +2 -2
- package/dist/graph/graph.js +4 -16
- package/dist/plan/prompt.js +1 -1
- package/dist/plan/scope.d.ts +2 -2
- package/dist/plan/scope.js +5 -5
- package/dist/route/preference.d.ts +4 -4
- package/dist/route/router.d.ts +3 -3
- package/dist/run/consult.d.ts +2 -2
- package/dist/run/consult.js +5 -5
- package/dist/run/daemon.d.ts +2 -0
- package/dist/run/daemon.js +48 -17
- package/dist/run/git.d.ts +1 -1
- package/dist/run/git.js +5 -9
- package/dist/run/journal.d.ts +2 -2
- package/dist/run/journal.js +9 -5
- package/dist/run/lock.js +11 -11
- package/dist/run/merge.d.ts +11 -2
- package/dist/run/merge.js +21 -3
- package/dist/run/reconcile.js +1 -1
- package/package.json +4 -2
- package/skills/tickmarkr-auto/SKILL.md +1 -1
- package/skills/tickmarkr-loop/SKILL.md +1 -1
package/dist/adapters/prompt.js
CHANGED
|
@@ -18,7 +18,7 @@ ${task.files.length ? `\n## File scope — touch ONLY paths matching:\n${list(ta
|
|
|
18
18
|
- Do not ask questions; you are unattended. Make the smallest correct change.
|
|
19
19
|
${feedback ? `\n## Previous attempt failed gates — fix these specifically\n${feedback}\n` : ""}
|
|
20
20
|
When finished, end your final message with exactly one line (no code fence):
|
|
21
|
-
|
|
21
|
+
TICKMARKR_RESULT_${nonce} {"ok":true|false,"summary":"<one sentence>","deviations":["<path or reason>"]}
|
|
22
22
|
`;
|
|
23
23
|
}
|
|
24
24
|
export function writePrompt(dir, task, attempt, feedback = "", nonce = "") {
|
|
@@ -31,26 +31,21 @@ export function writePrompt(dir, task, attempt, feedback = "", nonce = "") {
|
|
|
31
31
|
// across lines by a TUI renderer ([\s\S] bridges newlines between tokens) — but never the template
|
|
32
32
|
// line above, whose literal `true|false` is not a bool followed by , or }. v1.4: the run-tagged nonce
|
|
33
33
|
// anchors it so displayed source/diffs (which can't know the nonce) can never premature-harvest.
|
|
34
|
-
// v1.16 (REN-02): dual-parse — accepts both the new marker spelling and the legacy alternation, so a
|
|
35
|
-
// worker/fixture still emitting the old marker keeps parsing through the rename.
|
|
36
34
|
export function trailerPattern(nonce) {
|
|
37
|
-
return `
|
|
35
|
+
return `TICKMARKR_RESULT_${nonce} \\{[\\s\\S]{0,120}?"ok":\\s*(true|false)\\s*[,}]`;
|
|
38
36
|
}
|
|
39
|
-
// all start positions of either marker spelling, ascending — merged so "last occurrence" and
|
|
40
|
-
// "scan backward for the first parseable one" work identically across both spellings.
|
|
41
37
|
function trailerTokenPositions(raw, nonce) {
|
|
38
|
+
const token = `TICKMARKR_RESULT_${nonce}`;
|
|
42
39
|
const positions = [];
|
|
43
|
-
for (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
return positions.sort((a, b) => a - b);
|
|
40
|
+
for (let i = raw.indexOf(token); i !== -1; i = raw.indexOf(token, i + 1))
|
|
41
|
+
positions.push(i);
|
|
42
|
+
return positions;
|
|
48
43
|
}
|
|
49
44
|
export function parseWorkerResult(raw, nonce) {
|
|
50
45
|
const fail = (summary) => ({ ok: false, summary, deviations: [], raw });
|
|
51
46
|
const positions = trailerTokenPositions(raw, nonce);
|
|
52
47
|
if (positions.length === 0)
|
|
53
|
-
return fail("worker produced no
|
|
48
|
+
return fail("worker produced no TICKMARKR_RESULT trailer");
|
|
54
49
|
// TUIs echo the prompt template, redraw lines, and HARD-wrap the JSON with per-line margins
|
|
55
50
|
// (cursor does; recent-unwrapped can't rejoin hard newlines). Scan occurrences backward — last
|
|
56
51
|
// parseable wins — joining wrapped lines, stripping margin/box chrome, and growing the candidate
|
|
@@ -80,5 +75,5 @@ export function parseWorkerResult(raw, nonce) {
|
|
|
80
75
|
}
|
|
81
76
|
}
|
|
82
77
|
}
|
|
83
|
-
return fail("unparseable
|
|
78
|
+
return fail("unparseable TICKMARKR_RESULT trailer");
|
|
84
79
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type TickmarkrConfig } from "../config/config.js";
|
|
2
2
|
import { type RoutingProfile } from "../route/profile.js";
|
|
3
3
|
import { type AuthHealth, type BillingChannel, type WorkerAdapter } from "./types.js";
|
|
4
4
|
export declare function allAdapters(opts?: {
|
|
@@ -14,20 +14,20 @@ export interface AutoPreferDoc {
|
|
|
14
14
|
}
|
|
15
15
|
export declare const pendingAutoPreferKey: unique symbol;
|
|
16
16
|
export declare const DOCTOR_ROUTING_STALE_MS: number;
|
|
17
|
-
export declare function deriveAutoPrefer(cfg:
|
|
17
|
+
export declare function deriveAutoPrefer(cfg: TickmarkrConfig, adapters: WorkerAdapter[], health: Record<string, AuthHealth>, profile?: RoutingProfile): AutoPreferDoc;
|
|
18
18
|
export declare function readAutoPrefer(repoRoot: string): AutoPreferDoc | null;
|
|
19
|
-
export declare function routingPreferContext(repoRoot: string, cfg:
|
|
19
|
+
export declare function routingPreferContext(repoRoot: string, cfg: TickmarkrConfig, opts?: {
|
|
20
20
|
globalDir?: string;
|
|
21
21
|
}): {
|
|
22
22
|
autoPrefer?: AutoPreferDoc;
|
|
23
23
|
doctorFresh: boolean;
|
|
24
24
|
overlayPreferShapes: ReadonlySet<string>;
|
|
25
25
|
};
|
|
26
|
-
export declare function probeModels(cfg:
|
|
26
|
+
export declare function probeModels(cfg: TickmarkrConfig, repoRoot: string, adapters: WorkerAdapter[], health: Record<string, AuthHealth>, onProgress?: ProbeModelProgress): Promise<void>;
|
|
27
27
|
export declare function writeDoctor(repoRoot: string, health: Record<string, AuthHealth>): void;
|
|
28
28
|
export declare function readDoctor(repoRoot: string): Record<string, AuthHealth> | null;
|
|
29
|
-
export declare function discoverChannels(cfg:
|
|
30
|
-
export declare function servableExclusions(cfg:
|
|
29
|
+
export declare function discoverChannels(cfg: TickmarkrConfig, adapters: WorkerAdapter[], health: Record<string, AuthHealth>): BillingChannel[];
|
|
30
|
+
export declare function servableExclusions(cfg: TickmarkrConfig, adapters: WorkerAdapter[], health: Record<string, AuthHealth>): {
|
|
31
31
|
key: string;
|
|
32
32
|
adapter: string;
|
|
33
33
|
}[];
|
|
@@ -35,7 +35,7 @@ export declare function servabilityLine(excluded: {
|
|
|
35
35
|
key: string;
|
|
36
36
|
adapter: string;
|
|
37
37
|
}[]): string;
|
|
38
|
-
export declare function modelAuthExclusions(cfg:
|
|
38
|
+
export declare function modelAuthExclusions(cfg: TickmarkrConfig, adapters: WorkerAdapter[], health: Record<string, AuthHealth>): {
|
|
39
39
|
key: string;
|
|
40
40
|
adapter: string;
|
|
41
41
|
reason: string;
|
|
@@ -54,6 +54,6 @@ export declare function initDoctorReuse(repoRoot: string, fresh: boolean): {
|
|
|
54
54
|
ageMs: number | null;
|
|
55
55
|
health: Record<string, AuthHealth> | null;
|
|
56
56
|
};
|
|
57
|
-
export declare function formatDoctorReport(cwd: string, cfg:
|
|
57
|
+
export declare function formatDoctorReport(cwd: string, cfg: TickmarkrConfig, health: Record<string, AuthHealth>, adapters: WorkerAdapter[], opts?: {
|
|
58
58
|
wrote?: boolean;
|
|
59
59
|
}): string;
|
|
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { overlayPreferShapes, TIER_RANK } from "../config/config.js";
|
|
5
5
|
import { modelLints, suggestOverlay } from "./model-lints.js";
|
|
6
6
|
import { HerdrDriver } from "../drivers/herdr.js";
|
|
7
|
-
import {
|
|
7
|
+
import { tickmarkrDir, stateDirName } from "../graph/graph.js";
|
|
8
8
|
import { disallowedBy, excludedChannels, exclusionLine, preferRanks } from "../route/preference.js";
|
|
9
9
|
import { learnedScore } from "../route/profile.js";
|
|
10
10
|
import { marginalCostRank } from "../route/router.js";
|
|
@@ -22,7 +22,7 @@ export function allAdapters(opts = {}) {
|
|
|
22
22
|
// keeps the Phase 6 shape→channel matrix byte-identical; inserting anywhere else silently
|
|
23
23
|
// reassigns shapes on same-tier ties. grok is appended AFTER pi for the same reason.
|
|
24
24
|
const real = [claudeCode, codex, cursorAgent, opencode, pi, grok];
|
|
25
|
-
const fakePath = opts.fakeScriptPath ?? process.env.
|
|
25
|
+
const fakePath = opts.fakeScriptPath ?? process.env.TICKMARKR_FAKE_SCRIPT;
|
|
26
26
|
return fakePath ? [new FakeAdapter(fakePath), ...real] : real;
|
|
27
27
|
}
|
|
28
28
|
export function getAdapter(id, adapters) {
|
|
@@ -131,7 +131,7 @@ export async function probeModels(cfg, repoRoot, adapters, health, onProgress) {
|
|
|
131
131
|
verdict = { authed: false, reason: "headless probe unavailable", probedAt, durationMs: Date.now() - t0 };
|
|
132
132
|
}
|
|
133
133
|
else {
|
|
134
|
-
const promptFile = join(mkdtempSync(join(tmpdir(), "
|
|
134
|
+
const promptFile = join(mkdtempSync(join(tmpdir(), "tickmarkr-auth-")), "probe.md");
|
|
135
135
|
writeFileSync(promptFile, MODEL_PROBE_PROMPT);
|
|
136
136
|
const r = await sh(a.headlessCommand(promptFile, model), probeRoot, MODEL_PROBE_TIMEOUT_MS);
|
|
137
137
|
if (r.timedOut && priorTimedOut) {
|
|
@@ -181,7 +181,7 @@ function readDoctorFile(repoRoot) {
|
|
|
181
181
|
return existsSync(doctorPath(repoRoot)) ? JSON.parse(readFileSync(doctorPath(repoRoot), "utf8")) : null;
|
|
182
182
|
}
|
|
183
183
|
export function writeDoctor(repoRoot, health) {
|
|
184
|
-
|
|
184
|
+
tickmarkrDir(repoRoot);
|
|
185
185
|
const pending = health[pendingAutoPreferKey];
|
|
186
186
|
const payload = { ...health };
|
|
187
187
|
delete payload[pendingAutoPreferKey];
|
|
@@ -327,7 +327,7 @@ export function formatDoctorReport(cwd, cfg, health, adapters, opts = {}) {
|
|
|
327
327
|
let drift = "";
|
|
328
328
|
if (frag) {
|
|
329
329
|
if (visual) {
|
|
330
|
-
const overlayPath = join(
|
|
330
|
+
const overlayPath = join(tickmarkrDir(cwd), "doctor-overlay.yaml");
|
|
331
331
|
writeFileSync(overlayPath, frag);
|
|
332
332
|
drift = `\nmodel drift: unclassified models detected — paste-ready overlay written to ${overlayPath} (advisory; tickmarkr never applies it)`;
|
|
333
333
|
}
|
package/dist/adapters/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TickmarkrConfig, Tier } from "../config/config.js";
|
|
3
3
|
import type { Task } from "../graph/schema.js";
|
|
4
4
|
export declare const TokenUsageSchema: z.ZodObject<{
|
|
5
5
|
input: z.ZodNumber;
|
|
@@ -74,7 +74,7 @@ export interface WorkerAdapter {
|
|
|
74
74
|
vendor: string;
|
|
75
75
|
probeCwd?: "repo" | "neutral";
|
|
76
76
|
probe(): Promise<AuthHealth>;
|
|
77
|
-
channels(cfg:
|
|
77
|
+
channels(cfg: TickmarkrConfig): BillingChannel[];
|
|
78
78
|
headlessCommand(promptFile: string, model: string): string;
|
|
79
79
|
interactiveCommand(promptFile: string, model: string): string | null;
|
|
80
80
|
resumeCommand?(sessionId: string, promptFile: string, model: string): string;
|
|
@@ -89,7 +89,7 @@ export interface WorkerAdapter {
|
|
|
89
89
|
trust?(repoRoot: string): TrustVerdict;
|
|
90
90
|
trustDialog?: TrustDialog;
|
|
91
91
|
}
|
|
92
|
-
export declare function channelsFromConfig(adapterId: string, cfg:
|
|
92
|
+
export declare function channelsFromConfig(adapterId: string, cfg: TickmarkrConfig): BillingChannel[];
|
|
93
93
|
export declare function channelKey(c: {
|
|
94
94
|
adapter: string;
|
|
95
95
|
model: string;
|
package/dist/adapters/types.js
CHANGED
|
@@ -53,4 +53,4 @@ export const QUOTA_RE = /rate.?limit|quota|usage limit|out of credits|insufficie
|
|
|
53
53
|
// operator-facing lint text and persisted to doctor.json — defense-in-depth for MODEL-05 (config
|
|
54
54
|
// suggestions that could reach a shell). Covers observed ids incl. zai-coding-plan/glm-5.2,
|
|
55
55
|
// gpt-5.6-sol, composer-2.5, gpt-5.3-codex; non-conforming (ANSI/control/shell-metachar) ids dropped.
|
|
56
|
-
export const MODEL_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._
|
|
56
|
+
export const MODEL_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._/:[\]=,-]*$/;
|
package/dist/brand.d.ts
ADDED
package/dist/brand.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// TTY-only pixel-tick logo (assets/mark.svg is the image twin — same block geometry).
|
|
2
|
+
// Never printed to pipes — the non-TTY stdout surface is byte-pinned by tests and consumed by machines.
|
|
3
|
+
const B = "\x1b[1m", R = "\x1b[0m";
|
|
4
|
+
const g = (n, s) => `\x1b[38;5;${n}m${s}${R}`; // 256-color green ramp, bright → deep
|
|
5
|
+
export const BANNER = [
|
|
6
|
+
` ${g(84, "▄▄████")}`,
|
|
7
|
+
` ${g(78, "▄▄████▀▀")}`,
|
|
8
|
+
`${g(41, "████▄▄▄▄████▀▀")} ${B}tickmarkr${R}`,
|
|
9
|
+
` ${g(35, "▀▀████▀▀")} spec in, verified work out.`,
|
|
10
|
+
"",
|
|
11
|
+
].join("\n");
|
|
12
|
+
// Shell one-liner that prints the banner inside a pane before a gate command runs. ESC bytes are
|
|
13
|
+
// carried as printf %b escapes (never raw control bytes in a command string crossing the herdr socket).
|
|
14
|
+
export function bannerShell() {
|
|
15
|
+
const printable = BANNER.replaceAll("\x1b", "\\033").replaceAll("\n", "\\n");
|
|
16
|
+
return `printf '%b\\n' '${printable}'`;
|
|
17
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { userInfo } from "node:os";
|
|
2
2
|
import { ATTEMPT_CAP_RELEASE, Journal } from "../../run/journal.js";
|
|
3
|
-
// GATE-08 (v1.12): approve a parked human gate so the next `
|
|
3
|
+
// GATE-08 (v1.12): approve a parked human gate so the next `tickmarkr resume <runId>` dispatches it.
|
|
4
4
|
//
|
|
5
5
|
// The approval is a JOURNAL EVENT (task-approved) carrying who and when — it touches ONLY the
|
|
6
|
-
// append-only journal. Writing it into
|
|
6
|
+
// append-only journal. Writing it into tickmarkr's compiled graph artifact would be silently erased by
|
|
7
7
|
// the next recompile (which re-emits humanGate:true from the plan frontmatter) — Phase 42 D-02.
|
|
8
8
|
//
|
|
9
9
|
// v1.24 OBS-18: when the park was attempt-cap (not a humanGate pre-dispatch park), the event also
|
|
@@ -13,7 +13,7 @@ import { ATTEMPT_CAP_RELEASE, Journal } from "../../run/journal.js";
|
|
|
13
13
|
//
|
|
14
14
|
// Fail-closed (D-05): unknown runId, unknown taskId, a not-parked task, and a double-approve are all
|
|
15
15
|
// LOUD refusals that name the reason and append NO event — never a silent no-op. A handler throw
|
|
16
|
-
// becomes `
|
|
16
|
+
// becomes `tickmarkr approve: <message>` at exit 1 (src/cli/index.ts dispatch).
|
|
17
17
|
//
|
|
18
18
|
// Who/when is truthful, not dressed-up auth (D-03): default actor os.userInfo().username; --by overrides
|
|
19
19
|
// for delegated approval; optional --reason; the event's ts (stamped by Journal.append) is the when.
|
|
@@ -18,7 +18,7 @@ export async function compile(argv, cwd = process.cwd()) {
|
|
|
18
18
|
// as a second daemon. Read-only check — compile never acquires/holds (it's instantaneous).
|
|
19
19
|
const stateDir = stateDirName(cwd);
|
|
20
20
|
if (isRunLockLive(cwd))
|
|
21
|
-
throw new Error(`${stateDir}/graph.lock is held by another
|
|
21
|
+
throw new Error(`${stateDir}/graph.lock is held by another tickmarkr run, or is a stale/garbage lock — refusing to overwrite graph.json. If no run is active, run \`tickmarkr unlock\`.`);
|
|
22
22
|
saveGraph(cwd, g);
|
|
23
23
|
return `compiled ${src} → ${stateDir}/graph.json (${g.tasks.length} tasks, source ${g.spec.source}, hash ${g.spec.hash.slice(0, 12)})`;
|
|
24
24
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { allAdapters, probeAll, probeModels, readAutoPrefer, servableExclusions, servabilityLine, writeDoctor } from "../../adapters/registry.js";
|
|
4
|
-
import {
|
|
4
|
+
import { tickmarkrDir, stateDirName } from "../../graph/graph.js";
|
|
5
5
|
import { modelLints, suggestOverlay, ttyVisual } from "../../adapters/model-lints.js";
|
|
6
6
|
import { DEFAULT_CONFIG, loadConfig, overlayPreferShapes } from "../../config/config.js";
|
|
7
7
|
import { HerdrDriver } from "../../drivers/herdr.js";
|
|
@@ -99,14 +99,14 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
99
99
|
const servable = servableExclusions(cfg, adapters, health);
|
|
100
100
|
if (servable.length)
|
|
101
101
|
rows.push(` ! ${servabilityLine(servable)}`);
|
|
102
|
-
// MODEL-05/06: print-only drift fragment; advisory, whole-line-commented additions,
|
|
102
|
+
// MODEL-05/06: print-only drift fragment; advisory, whole-line-commented additions, tickmarkr NEVER applies it.
|
|
103
103
|
// TTY gets a one-line summary + the fragment as a file (the full dump drowned everything else,
|
|
104
104
|
// v1.33.1 onboarding); machine/CI surface keeps the inline dump — layout is pinned by tests.
|
|
105
105
|
const frag = suggestOverlay(cfg, health, adapters, stateDirName(cwd));
|
|
106
106
|
let drift = "";
|
|
107
107
|
if (frag) {
|
|
108
108
|
if (visual()) {
|
|
109
|
-
const overlayPath = join(
|
|
109
|
+
const overlayPath = join(tickmarkrDir(cwd), "doctor-overlay.yaml");
|
|
110
110
|
writeFileSync(overlayPath, frag);
|
|
111
111
|
drift = `\nmodel drift: unclassified models detected — paste-ready overlay written to ${overlayPath} (advisory; tickmarkr never applies it)`;
|
|
112
112
|
}
|
|
@@ -4,8 +4,8 @@ import { createInterface } from "node:readline/promises";
|
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { allAdapters, formatDoctorAgeForInit, formatDoctorReport, initDoctorReuse } from "../../adapters/registry.js";
|
|
6
6
|
import { configTemplate, DEFAULT_CONFIG, globalConfigDir, loadConfig } from "../../config/config.js";
|
|
7
|
-
import { specTemplate } from "../../compile/native.js";
|
|
8
|
-
import {
|
|
7
|
+
import { LEGACY_PREFIX, specTemplate } from "../../compile/native.js";
|
|
8
|
+
import { tickmarkrDir } from "../../graph/graph.js";
|
|
9
9
|
import { doctor } from "./doctor.js";
|
|
10
10
|
const AGENT_SKILLS = ["tickmarkr-loop", "tickmarkr-auto"];
|
|
11
11
|
const DOCS_BEGIN = "<!-- tickmarkr:agent-docs begin -->";
|
|
@@ -52,7 +52,8 @@ async function installAgentFiles(cwd, force, docs, notes) {
|
|
|
52
52
|
const agents = join(cwd, "AGENTS.md");
|
|
53
53
|
const docPath = existsSync(claude) || !existsSync(agents) ? claude : agents;
|
|
54
54
|
const current = existsSync(docPath) ? readFileSync(docPath, "utf8") : "";
|
|
55
|
-
if (current.includes(DOCS_BEGIN) || current.includes(
|
|
55
|
+
if (current.includes(DOCS_BEGIN) || current.includes(`<!-- ${LEGACY_PREFIX}:agent-docs begin -->`)
|
|
56
|
+
|| current.includes(DOCS_END) || current.includes(`<!-- ${LEGACY_PREFIX}:agent-docs end -->`)) {
|
|
56
57
|
notes.push(`kept existing tickmarkr agent docs in ${docPath}`);
|
|
57
58
|
}
|
|
58
59
|
else if (docs || await confirm(`Append tickmarkr agent docs to ${docPath}?`)) {
|
|
@@ -126,7 +127,7 @@ export async function init(argv, cwd = process.cwd()) {
|
|
|
126
127
|
else {
|
|
127
128
|
notes.push(`kept existing ${globalPath}`);
|
|
128
129
|
}
|
|
129
|
-
const repoConfigPath = join(
|
|
130
|
+
const repoConfigPath = join(tickmarkrDir(cwd), "config.yaml");
|
|
130
131
|
const repoConfigExists = existsSync(repoConfigPath);
|
|
131
132
|
if (repoConfigExists)
|
|
132
133
|
notes.push(`kept existing ${repoConfigPath}`);
|
|
@@ -134,12 +135,16 @@ export async function init(argv, cwd = process.cwd()) {
|
|
|
134
135
|
if (existsSync(specPath)) {
|
|
135
136
|
notes.push(`kept existing ${specPath}`);
|
|
136
137
|
}
|
|
137
|
-
else if (existsSync(join(cwd, "drovr.spec.md"))) {
|
|
138
|
-
notes.push(`kept existing ${join(cwd, "drovr.spec.md")}`);
|
|
139
|
-
}
|
|
140
138
|
else {
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
const legacySpec = join(cwd, `${LEGACY_PREFIX}.spec.md`);
|
|
140
|
+
if (existsSync(legacySpec)) {
|
|
141
|
+
writeFileSync(specPath, readFileSync(legacySpec, "utf8"));
|
|
142
|
+
notes.push(`wrote ${specPath}`);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
writeFileSync(specPath, specTemplate());
|
|
146
|
+
notes.push(`wrote ${specPath}`);
|
|
147
|
+
}
|
|
143
148
|
}
|
|
144
149
|
const fresh = values.fresh ?? false;
|
|
145
150
|
const { reuse, ageMs, health } = initDoctorReuse(cwd, fresh);
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { loadConfig } from "../../config/config.js";
|
|
4
|
-
import {
|
|
4
|
+
import { tickmarkrDir, stateDirName } from "../../graph/graph.js";
|
|
5
5
|
import { learnedScore, MIN_SAMPLES, cellsOf } from "../../route/profile.js";
|
|
6
6
|
import { Journal, loadRoutingProfile, readProfileCursor, RUNS_WINDOW } from "../../run/journal.js";
|
|
7
|
-
//
|
|
7
|
+
// tickmarkr profile — inspect (show) and forget (reset) the derived learned-routing profile (VIS-03).
|
|
8
8
|
// Read-only class like plan/status/report: no run lock. `reset` writes ONE cursor scalar, nothing else.
|
|
9
9
|
export async function profile(argv, cwd = process.cwd()) {
|
|
10
10
|
return argv[0] === "reset" ? reset(cwd) : show(cwd);
|
|
11
11
|
}
|
|
12
|
-
// Non-destructive reset (T-13-06): a one-line runId cutoff at .
|
|
12
|
+
// Non-destructive reset (T-13-06): a one-line runId cutoff at .tickmarkr/profile-since. NEVER deletes,
|
|
13
13
|
// truncates, or rotates any telemetry — the profile is derived (PROF-01), so there is nothing to delete;
|
|
14
|
-
// the cursor only bounds loadRoutingProfile's telemetry window.
|
|
14
|
+
// the cursor only bounds loadRoutingProfile's telemetry window. tickmarkrDir guarantees .tickmarkr exists and
|
|
15
15
|
// blanket-gitignores it, so the cursor is never git-addable. Opaque string: used only in a runId > compare.
|
|
16
16
|
function reset(cwd) {
|
|
17
17
|
const cursor = Journal.latestRunId(cwd) ?? ""; // empty repo ⇒ empty cursor ⇒ readProfileCursor === undefined
|
|
18
18
|
const stateDir = stateDirName(cwd);
|
|
19
|
-
const path = join(
|
|
19
|
+
const path = join(tickmarkrDir(cwd), "profile-since");
|
|
20
20
|
writeFileSync(path, cursor + "\n");
|
|
21
21
|
return [
|
|
22
22
|
cursor
|
|
@@ -27,7 +27,7 @@ function reset(cwd) {
|
|
|
27
27
|
].join("\n");
|
|
28
28
|
}
|
|
29
29
|
// Inspection surface: preview:true bypasses the routing.learned:off short-circuit so `show` renders the
|
|
30
|
-
// profile even under the default off — same trust ramp as `
|
|
30
|
+
// profile even under the default off — same trust ramp as `tickmarkr plan`. The routing path stays inert.
|
|
31
31
|
function show(cwd) {
|
|
32
32
|
const cfg = loadConfig(cwd);
|
|
33
33
|
const cursor = readProfileCursor(cwd);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BANNER } from "../../brand.js";
|
|
1
2
|
import { blockedTasks, loadGraph, pendingTasks } from "../../graph/graph.js";
|
|
2
3
|
import { GATE_NAMES } from "../../graph/schema.js";
|
|
3
4
|
import { Journal } from "../../run/journal.js";
|
|
@@ -211,7 +212,7 @@ export async function status(argv, cwd = process.cwd(), opts = {}) {
|
|
|
211
212
|
for (let i = 0; i < iterations; i++) {
|
|
212
213
|
const frame = renderFrame(cwd);
|
|
213
214
|
if (tty)
|
|
214
|
-
process.stdout.write(`\x1b[2J\x1b[H${frame}\n\x1b[2m watching · refresh ${REFRESH_MS / 1000}s · ^C to quit\x1b[0m`);
|
|
215
|
+
process.stdout.write(`\x1b[2J\x1b[H${BANNER}${frame}\n\x1b[2m watching · refresh ${REFRESH_MS / 1000}s · ^C to quit\x1b[0m`);
|
|
215
216
|
else
|
|
216
217
|
process.stdout.write(frame + sep);
|
|
217
218
|
if (bounded)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { unlockRun } from "../../run/lock.js";
|
|
2
2
|
// LOCK-03: thin formatter over unlockRun. A live-holder refusal propagates as the throw — the
|
|
3
|
-
// dispatcher prints `
|
|
3
|
+
// dispatcher prints `tickmarkr unlock: …` and exits 1 (src/cli/index.ts).
|
|
4
4
|
export async function unlock(_argv, cwd = process.cwd()) {
|
|
5
5
|
const r = unlockRun(cwd);
|
|
6
6
|
if (!r.held)
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
export type CommandMap = Record<string, (argv: string[]) => Promise<string>>;
|
|
3
3
|
export declare const COMMANDS: CommandMap;
|
|
4
|
-
export declare const USAGE = "tickmarkr \u2014 spec-driven orchestration harness for AI coding agents\nusage: tickmarkr <command>\n init guided setup + doctor; init --agent [--force] [--docs] adds agent skills/docs\n doctor re-probe adapters, herdr, auth; print capability matrix\n compile <src> spec \u2192 .
|
|
4
|
+
export declare const USAGE = "tickmarkr \u2014 spec-driven orchestration harness for AI coding agents\nusage: tickmarkr <command>\n init guided setup + doctor; init --agent [--force] [--docs] adds agent skills/docs\n doctor re-probe adapters, herdr, auth; print capability matrix\n compile <src> spec \u2192 .tickmarkr/graph.json (fails without acceptance criteria)\n scope <intent> draft a compiled native spec beside an answered intent (--force to overwrite)\n plan dry-run routing table + cost estimate + floor lints\n run execute the graph (--concurrency N --driver herdr|subprocess --route-strict)\n status live run state\n resume <id> continue a run from its journal\n report <id> cost/quality report (--md for committable execution record)\n profile show learned routing profile (profile reset = forget history via cursor, keeps telemetry)\n unlock remove a stale/garbage run lock (refuses if the holder is alive)\n approve <id> <task> approve a parked human gate (--by <name> --reason <text>); takes effect on resume";
|
|
5
5
|
export declare function dispatch(cmd: string | undefined, argv: string[], commands?: CommandMap): Promise<{
|
|
6
6
|
out: string;
|
|
7
7
|
code: number;
|
package/dist/cli/index.js
CHANGED
|
@@ -17,11 +17,12 @@ export const COMMANDS = {
|
|
|
17
17
|
init, doctor, compile, scope, plan, run, status, resume, report, profile, unlock, approve,
|
|
18
18
|
};
|
|
19
19
|
const HELP_CMDS = new Set(["help", "-h", "--help"]);
|
|
20
|
+
import { BANNER } from "../brand.js";
|
|
20
21
|
export const USAGE = `tickmarkr — spec-driven orchestration harness for AI coding agents
|
|
21
22
|
usage: tickmarkr <command>
|
|
22
23
|
init guided setup + doctor; init --agent [--force] [--docs] adds agent skills/docs
|
|
23
24
|
doctor re-probe adapters, herdr, auth; print capability matrix
|
|
24
|
-
compile <src> spec → .
|
|
25
|
+
compile <src> spec → .tickmarkr/graph.json (fails without acceptance criteria)
|
|
25
26
|
scope <intent> draft a compiled native spec beside an answered intent (--force to overwrite)
|
|
26
27
|
plan dry-run routing table + cost estimate + floor lints
|
|
27
28
|
run execute the graph (--concurrency N --driver herdr|subprocess --route-strict)
|
|
@@ -32,14 +33,15 @@ usage: tickmarkr <command>
|
|
|
32
33
|
unlock remove a stale/garbage run lock (refuses if the holder is alive)
|
|
33
34
|
approve <id> <task> approve a parked human gate (--by <name> --reason <text>); takes effect on resume`;
|
|
34
35
|
// pure, testable dispatcher: resolves a command, forwards argv, shapes the result — no side effects.
|
|
35
|
-
// unknown/missing cmd → USAGE (exit 1 if a cmd was typed, 0 for bare `
|
|
36
|
-
// a one-line `
|
|
36
|
+
// unknown/missing cmd → USAGE (exit 1 if a cmd was typed, 0 for bare `tickmarkr`); a handler throw becomes
|
|
37
|
+
// a one-line `tickmarkr <cmd>: <message>` (never a raw stack) at exit 1.
|
|
37
38
|
export async function dispatch(cmd, argv, commands = COMMANDS) {
|
|
39
|
+
const usage = process.stdout.isTTY ? BANNER + USAGE : USAGE;
|
|
38
40
|
if (!cmd || HELP_CMDS.has(cmd))
|
|
39
|
-
return { out:
|
|
41
|
+
return { out: usage, code: 0 };
|
|
40
42
|
const fn = commands[cmd];
|
|
41
43
|
if (!fn)
|
|
42
|
-
return { out:
|
|
44
|
+
return { out: usage, code: 1 };
|
|
43
45
|
try {
|
|
44
46
|
return { out: await fn(argv), code: 0 };
|
|
45
47
|
}
|
|
@@ -49,7 +51,7 @@ export async function dispatch(cmd, argv, commands = COMMANDS) {
|
|
|
49
51
|
}
|
|
50
52
|
/* v8 ignore start -- binary entry: printing + process.exit side effects, not unit-testable (ROADMAP crit 2) */
|
|
51
53
|
// node realpaths the main module (import.meta.url) but argv[1] keeps the symlink path —
|
|
52
|
-
// a globally-linked `
|
|
54
|
+
// a globally-linked `tickmarkr` bin silently no-oped here (OBS-10); compare realpaths
|
|
53
55
|
const argv1Real = (() => { try {
|
|
54
56
|
return process.argv[1] ? realpathSync(process.argv[1]) : "";
|
|
55
57
|
}
|
|
@@ -60,7 +62,7 @@ if (argv1Real && import.meta.url === pathToFileURL(argv1Real).href) {
|
|
|
60
62
|
const [cmd, ...argv] = process.argv.slice(2);
|
|
61
63
|
dispatch(cmd, argv).then(({ out, code }) => {
|
|
62
64
|
// byte-identical streams: usage + success → stdout; a handler throw → stderr (original behavior)
|
|
63
|
-
(out
|
|
65
|
+
(out.endsWith(USAGE) || code === 0 ? console.log : console.error)(out);
|
|
64
66
|
if (code !== 0)
|
|
65
67
|
process.exit(code);
|
|
66
68
|
});
|
package/dist/compile/gsd.js
CHANGED
|
@@ -3,7 +3,7 @@ import { basename, dirname, isAbsolute, join, relative } from "node:path";
|
|
|
3
3
|
import { parse as parseYaml } from "yaml";
|
|
4
4
|
import { TIERS, validateGraph } from "../graph/schema.js";
|
|
5
5
|
import { CompileError, assertWriteScope, inferShape, sha256 } from "./common.js";
|
|
6
|
-
// GSD artifact front-end (spec v1.3): one GSD *plan* is one
|
|
6
|
+
// GSD artifact front-end (spec v1.3): one GSD *plan* is one tickmarkr *task* — a plan is
|
|
7
7
|
// worktree-sized; its inner <task> steps stay in the worker prompt via context[0] = the plan file.
|
|
8
8
|
// Artifact-level only: parses .planning/ markdown, never GSD repo/command internals.
|
|
9
9
|
const PLAN_SUFFIX = "-PLAN.md";
|
package/dist/compile/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { CompileError } from "./common.js";
|
|
4
4
|
import { compileGsd, isGsdPhaseDir } from "./gsd.js";
|
|
5
|
-
import { compileNative,
|
|
5
|
+
import { compileNative, TICKMARKR_NATIVE_MARKER } from "./native.js";
|
|
6
6
|
import { compilePrd } from "./prd.js";
|
|
7
7
|
import { compileSpecKit } from "./speckit.js";
|
|
8
8
|
function detect(src) {
|
|
@@ -15,8 +15,14 @@ function detect(src) {
|
|
|
15
15
|
}
|
|
16
16
|
if (src.endsWith("-PLAN.md"))
|
|
17
17
|
return "gsd"; // before the generic .md → prd rule
|
|
18
|
-
if (src.endsWith(".md"))
|
|
19
|
-
|
|
18
|
+
if (src.endsWith(".md")) {
|
|
19
|
+
if (!existsSync(src))
|
|
20
|
+
return "prd";
|
|
21
|
+
const content = readFileSync(src, "utf8");
|
|
22
|
+
if (TICKMARKR_NATIVE_MARKER.test(content))
|
|
23
|
+
return "native";
|
|
24
|
+
return "prd";
|
|
25
|
+
}
|
|
20
26
|
return null;
|
|
21
27
|
}
|
|
22
28
|
export function compileSource(src, type, root) {
|
package/dist/compile/native.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type RunGraph } from "../graph/schema.js";
|
|
2
|
+
export declare const LEGACY_PREFIX: string;
|
|
3
|
+
export declare const TICKMARKR_NATIVE_MARKER: RegExp;
|
|
2
4
|
export declare const NATIVE_MARKER: RegExp;
|
|
3
5
|
export declare function compileNative(file: string): RunGraph;
|
|
4
6
|
export declare function specTemplate(): string;
|
package/dist/compile/native.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { GATE_NAMES, ORACLES, SHAPES, TIERS, validateGraph } from "../graph/schema.js";
|
|
3
3
|
import { CompileError, inferShape, sha256 } from "./common.js";
|
|
4
|
-
export const
|
|
4
|
+
export const LEGACY_PREFIX = ["dro", "vr"].join("");
|
|
5
|
+
export const TICKMARKR_NATIVE_MARKER = /^<!--\s*tickmarkr:spec(?:\s+v1)?\s*-->\s*$/m;
|
|
6
|
+
export const NATIVE_MARKER = new RegExp(`^<!--\\s*(?:tickmarkr|${LEGACY_PREFIX}):spec(?:\\s+v1)?\\s*-->\\s*$`, "m");
|
|
5
7
|
const HEAD_RE = /^## (T\d+):\s*(.+)$/;
|
|
6
8
|
const FIELD_RE = /^- (\w+):\s*(.*)$/;
|
|
7
9
|
const NESTED_RE = /^\s+- (.+)$/;
|
|
@@ -139,7 +141,7 @@ export function compileNative(file) {
|
|
|
139
141
|
}
|
|
140
142
|
return result;
|
|
141
143
|
}
|
|
142
|
-
// Commented native spec written to
|
|
144
|
+
// Commented native spec written to tickmarkr.spec.md by `tickmarkr init`. Documented via HTML comments (which the
|
|
143
145
|
// parser ignores) so the template itself round-trips through compileSource() unchanged. Every field is
|
|
144
146
|
// documented; the two example tasks illustrate plain vs deps+routing-hint, each with acceptance[].
|
|
145
147
|
export function specTemplate() {
|
package/dist/config/config.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ export declare const SubPricingSchema: z.ZodObject<{
|
|
|
46
46
|
windowsPerMonthHigh: z.ZodNumber;
|
|
47
47
|
}, z.core.$strip>;
|
|
48
48
|
export type SubPricing = z.infer<typeof SubPricingSchema>;
|
|
49
|
-
export declare const
|
|
49
|
+
export declare const TickmarkrConfigSchema: z.ZodObject<{
|
|
50
50
|
concurrency: z.ZodNumber;
|
|
51
51
|
driver: z.ZodEnum<{
|
|
52
52
|
auto: "auto";
|
|
@@ -171,23 +171,23 @@ export declare const DrovrConfigSchema: z.ZodObject<{
|
|
|
171
171
|
workersPerTab: z.ZodNumber;
|
|
172
172
|
}, z.core.$strip>;
|
|
173
173
|
}, z.core.$strip>;
|
|
174
|
-
export type
|
|
174
|
+
export type TickmarkrConfig = z.infer<typeof TickmarkrConfigSchema>;
|
|
175
175
|
export declare class ConfigError extends Error {
|
|
176
176
|
constructor(message: string);
|
|
177
177
|
}
|
|
178
|
-
export declare const DEFAULT_CONFIG:
|
|
178
|
+
export declare const DEFAULT_CONFIG: TickmarkrConfig;
|
|
179
179
|
export declare function globalConfigDir(): string;
|
|
180
180
|
export declare function overlayPreferShapes(repoRoot: string, opts?: {
|
|
181
181
|
globalDir?: string;
|
|
182
182
|
}): ReadonlySet<string>;
|
|
183
183
|
export declare function loadConfig(repoRoot: string, opts?: {
|
|
184
184
|
globalDir?: string;
|
|
185
|
-
}):
|
|
185
|
+
}): TickmarkrConfig;
|
|
186
186
|
export type InitConfigOverlay = {
|
|
187
187
|
concurrency?: number;
|
|
188
|
-
driver?:
|
|
188
|
+
driver?: TickmarkrConfig["driver"];
|
|
189
189
|
visibility?: {
|
|
190
|
-
llm?:
|
|
190
|
+
llm?: TickmarkrConfig["visibility"]["llm"];
|
|
191
191
|
};
|
|
192
192
|
};
|
|
193
193
|
export declare function configTemplate(overlay?: InitConfigOverlay): string;
|
package/dist/config/config.js
CHANGED
|
@@ -63,7 +63,7 @@ const ShapeGateParticipationSchema = z
|
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
|
-
export const
|
|
66
|
+
export const TickmarkrConfigSchema = z.object({
|
|
67
67
|
concurrency: z.number().int().positive(),
|
|
68
68
|
driver: z.enum(["auto", "herdr", "subprocess"]),
|
|
69
69
|
integrationBranchPrefix: z
|
|
@@ -92,7 +92,7 @@ export const DrovrConfigSchema = z.object({
|
|
|
92
92
|
tiers: z.record(z.string(), TierEntrySchema),
|
|
93
93
|
pricing: z.record(z.string(), z.number()),
|
|
94
94
|
// v1.20 REC-02: optional detailed price table for cost estimation. Distinct from `pricing` above
|
|
95
|
-
// (the coarse per-task tier estimate `
|
|
95
|
+
// (the coarse per-task tier estimate `tickmarkr plan` shows) — this one drives the usage/cost report.
|
|
96
96
|
// Absent ⇒ every channel reports "not measurable" (never $0). models keyed by model id (LiteLLM
|
|
97
97
|
// convention); subs keyed by adapter id (subscription plans are per-account, not per-token).
|
|
98
98
|
cost: z
|
|
@@ -136,7 +136,7 @@ export class ConfigError extends Error {
|
|
|
136
136
|
export const DEFAULT_CONFIG = {
|
|
137
137
|
concurrency: 3,
|
|
138
138
|
driver: "auto",
|
|
139
|
-
integrationBranchPrefix: "
|
|
139
|
+
integrationBranchPrefix: "tickmarkr/",
|
|
140
140
|
taskTimeoutMinutes: 30,
|
|
141
141
|
contextWarnTokens: 170_000, // v1.23 T2: overseer ctx-watch.sh proven threshold
|
|
142
142
|
routing: {
|
|
@@ -154,11 +154,11 @@ export const DEFAULT_CONFIG = {
|
|
|
154
154
|
},
|
|
155
155
|
// ROUTE-14 (2026-07-11, operator-adopted): learned reordering ON by default. Shipped OFF through v1.6/v1.7
|
|
156
156
|
// as a preview-then-adopt trust ramp (ROUTE-09); the operator adopts it here after v1.8 made learning
|
|
157
|
-
// auditable (VIS-05 `
|
|
157
|
+
// auditable (VIS-05 `tickmarkr report` learning section) and matured (decay ROUTE-11, scored utilization
|
|
158
158
|
// ROUTE-12, escalation tiebreak ROUTE-13). SAFE BY CONSTRUCTION: an empty/cold profile ⇒ every
|
|
159
159
|
// learnedScore returns exactly NEUTRAL ⇒ byte-identical v1.5 static routing, so ON only changes routing in
|
|
160
160
|
// a workspace that has accumulated ≥MIN_SAMPLES warm telemetry per cell. Preview any workspace's effect
|
|
161
|
-
// first with `
|
|
161
|
+
// first with `tickmarkr plan` / `tickmarkr report`; flip to "off" to pin exact static routing (the kill switch stands).
|
|
162
162
|
learned: "on",
|
|
163
163
|
},
|
|
164
164
|
// Seed table (spec §13). New models = edit this (or your config.yaml), never code.
|
|
@@ -185,12 +185,17 @@ export const DEFAULT_CONFIG = {
|
|
|
185
185
|
// promoted from overlay 2026-07-11 (MODEL-10).
|
|
186
186
|
// grok-4.5-fast → cheap: speed-optimized variant, no independent benchmark scores yet → floor tier.
|
|
187
187
|
// RETIRED 2026-07-13: cursor-agent seeds grok-4.5-xhigh / grok-4.5-fast-xhigh — CLI no longer reports
|
|
188
|
-
// either id (
|
|
188
|
+
// either id (tickmarkr plan lint, 2026-07-13). Tombstoned here; re-seed if cursor re-exposes them.
|
|
189
189
|
// (Benchmark provenance above still informs the native grok adapter seeds below.)
|
|
190
190
|
"cursor-agent": {
|
|
191
191
|
vendor: "cursor", channel: "sub",
|
|
192
192
|
models: {
|
|
193
193
|
"composer-2.5": "mid",
|
|
194
|
+
// composer-2.5-fast → cheap: speed-optimized variant, no independent benchmark scores yet → floor
|
|
195
|
+
// tier (same policy call as grok-composer-2.5-fast below). Id live-verified in doctor.json probe
|
|
196
|
+
// 2026-07-16 (cursor-agent 2026.07.09); gives cursor a cheap-tier channel so low-complexity shapes
|
|
197
|
+
// stop burning its mid. Operator-approved 2026-07-16.
|
|
198
|
+
"composer-2.5-fast": "cheap",
|
|
194
199
|
},
|
|
195
200
|
},
|
|
196
201
|
// GLM-5.2 → mid per benchmark policy (2026-07): SWE-bench Pro 62.1 (> GPT-5.5 58.6), FrontierSWE 74.4 ≈ Opus 4.8;
|
|
@@ -255,12 +260,8 @@ function readYaml(path) {
|
|
|
255
260
|
return parse(readFileSync(path, "utf8"));
|
|
256
261
|
}
|
|
257
262
|
export function globalConfigDir() {
|
|
258
|
-
// read-old/write-new (operator-ordered rename 2026-07-15): prefer ~/.config/tickmarkr; an
|
|
259
|
-
// existing ~/.config/drovr keeps working until the operator moves it. New scaffolds use tickmarkr.
|
|
260
263
|
const base = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
|
|
261
|
-
|
|
262
|
-
const legacy = join(base, "drovr");
|
|
263
|
-
return existsSync(join(next, "config.yaml")) || !existsSync(join(legacy, "config.yaml")) ? next : legacy;
|
|
264
|
+
return join(base, "tickmarkr");
|
|
264
265
|
}
|
|
265
266
|
export function overlayPreferShapes(repoRoot, opts = {}) {
|
|
266
267
|
const shapes = new Set();
|
|
@@ -279,7 +280,7 @@ export function loadConfig(repoRoot, opts = {}) {
|
|
|
279
280
|
const globalCfg = readYaml(join(opts.globalDir ?? globalConfigDir(), "config.yaml"));
|
|
280
281
|
const repoCfg = readYaml(join(repoRoot, stateDirName(repoRoot), "config.yaml"));
|
|
281
282
|
const merged = deepMerge(deepMerge(structuredClone(DEFAULT_CONFIG), globalCfg), repoCfg);
|
|
282
|
-
const r =
|
|
283
|
+
const r = TickmarkrConfigSchema.safeParse(merged);
|
|
283
284
|
if (!r.success)
|
|
284
285
|
throw new ConfigError(z.prettifyError(r.error));
|
|
285
286
|
return r.data;
|