tickmarkr 1.37.0 → 1.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -23
- 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 +29 -11
- 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 +17 -17
- package/dist/adapters/types.d.ts +4 -4
- package/dist/adapters/types.js +3 -2
- 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 +57 -19
- package/dist/cli/commands/plan.js +12 -1
- package/dist/cli/commands/profile.js +6 -6
- package/dist/cli/commands/resume.d.ts +4 -1
- package/dist/cli/commands/resume.js +4 -1
- package/dist/cli/commands/run.d.ts +4 -1
- package/dist/cli/commands/run.js +9 -1
- package/dist/cli/commands/status.js +2 -1
- package/dist/cli/commands/unlock.js +1 -1
- package/dist/cli/commands/version.d.ts +1 -0
- package/dist/cli/commands/version.js +8 -0
- package/dist/cli/index.d.ts +6 -3
- package/dist/cli/index.js +13 -18
- 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 +12 -4
- package/dist/config/config.d.ts +9 -6
- package/dist/config/config.js +15 -13
- package/dist/drivers/herdr.js +5 -5
- package/dist/drivers/index.d.ts +2 -2
- package/dist/drivers/subprocess.js +12 -5
- package/dist/drivers/types.js +4 -6
- package/dist/gates/acceptance.d.ts +1 -0
- package/dist/gates/acceptance.js +25 -5
- 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 +27 -8
- package/dist/gates/run-gates.d.ts +2 -2
- package/dist/gates/run-gates.js +2 -2
- package/dist/graph/graph.d.ts +2 -2
- package/dist/graph/graph.js +4 -16
- package/dist/graph/schema.d.ts +2 -0
- package/dist/graph/schema.js +16 -1
- 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 +5 -2
- package/dist/run/consult.js +31 -7
- package/dist/run/daemon.js +31 -18
- 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 +4 -4
- package/dist/run/lock.js +11 -11
- package/dist/run/merge.d.ts +2 -2
- package/dist/run/merge.js +3 -3
- package/dist/run/reconcile.js +1 -1
- package/package.json +2 -2
- package/schema/rungraph.schema.json +4 -0
- package/skills/tickmarkr-auto/SKILL.md +23 -2
- package/skills/tickmarkr-loop/SKILL.md +23 -2
|
@@ -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 -->";
|
|
@@ -13,18 +13,51 @@ const DOCS_END = "<!-- tickmarkr:agent-docs end -->";
|
|
|
13
13
|
const AGENT_DOCS = `${DOCS_BEGIN}
|
|
14
14
|
## tickmarkr
|
|
15
15
|
|
|
16
|
-
tickmarkr
|
|
16
|
+
tickmarkr compiles repository specs into isolated, independently verified agent work.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
### Invariants
|
|
19
19
|
|
|
20
20
|
- Never run two tickmarkr runs in the same repository concurrently.
|
|
21
|
-
- Never
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
- Treat missing or unparseable results as failures.
|
|
21
|
+
- Never let tickmarkr merge work to main; new runs consolidate on \`tickmarkr/<runId>\`.
|
|
22
|
+
- Do not edit compiled graphs to force outcomes; fix source specs and recompile.
|
|
23
|
+
- Gates verify commits, diffs, acceptance criteria, and reviews independently — never trust a worker's completion claim.
|
|
24
|
+
- Treat missing or unparseable machine results and verdicts as failures.
|
|
25
|
+
|
|
26
|
+
### Commands
|
|
27
|
+
|
|
28
|
+
- \`tickmarkr compile <spec>\` — spec → RunGraph
|
|
29
|
+
- \`tickmarkr plan\` — routing table and human gates
|
|
30
|
+
- \`tickmarkr run\` — execute the graph
|
|
31
|
+
- \`tickmarkr status <runId>\` — run progress
|
|
32
|
+
- \`tickmarkr resume <runId>\` — continue a paused or failed run
|
|
33
|
+
- \`tickmarkr approve <runId> <taskId>\` — release a human gate
|
|
34
|
+
- \`tickmarkr report <runId> --md\` — execution record beside the spec
|
|
35
|
+
|
|
36
|
+
Loop: compile → plan → run → report. Watch the journal for run-end rather than polling workers.
|
|
37
|
+
|
|
38
|
+
### Role check (multi-agent environments)
|
|
39
|
+
|
|
40
|
+
- **Orchestrator:** run the loop in your session; do not start a second run.
|
|
41
|
+
- **Supervisor with a live orchestrator:** relay the mission via verified handoff (below), then supervise — do not duplicate the loop.
|
|
42
|
+
- **Primary session without an orchestrator:** spawn one child orchestration session, give it the mission and these rules, then supervise.
|
|
43
|
+
|
|
44
|
+
Outside multi-agent environments, run the loop directly.
|
|
45
|
+
|
|
46
|
+
### Version preflight
|
|
47
|
+
|
|
48
|
+
Before \`tickmarkr compile\` or \`tickmarkr run\`: run \`tickmarkr version\`, read \`package.json\` version, and if the binary is older on major.minor, stop and tell the operator to update. Never proceed on hope — stale binaries silently skip daemon gates.
|
|
49
|
+
|
|
50
|
+
### Tip-verify-before-green
|
|
51
|
+
|
|
52
|
+
A run is green only when the run-end event exists in the journal AND tip verify is not "failed". Never report green to the operator, tab titles, or records until both hold.
|
|
53
|
+
|
|
54
|
+
### Verified handoffs
|
|
55
|
+
|
|
56
|
+
When relaying missions between agents, never use bare send-text (\`herdr agent send\` / pane send-text) — it omits Enter. Use \`herdr pane run <pane> "<message>"\` or \`herdr notification show "<message>"\`. Confirm delivery by reading the target pane afterward; never report "relayed" without read-back.
|
|
25
57
|
${DOCS_END}
|
|
26
58
|
`;
|
|
27
|
-
const
|
|
59
|
+
const skillsRoot = (cwd) => existsSync(join(cwd, ".claude", "skills")) ? join(cwd, ".claude", "skills") : join(cwd, ".agents", "skills");
|
|
60
|
+
const skillPath = (cwd, skill) => join(skillsRoot(cwd), skill, "SKILL.md");
|
|
28
61
|
const skillsInstalled = (cwd) => AGENT_SKILLS.every((s) => existsSync(skillPath(cwd, s)));
|
|
29
62
|
const wizardDriverDefault = () => process.env.HERDR_ENV === "1" ? "herdr" : "auto";
|
|
30
63
|
async function installAgentFiles(cwd, force, docs, notes) {
|
|
@@ -44,7 +77,7 @@ async function installAgentFiles(cwd, force, docs, notes) {
|
|
|
44
77
|
notes.push(`skipped existing ${dest}; pass --force to overwrite it`);
|
|
45
78
|
continue;
|
|
46
79
|
}
|
|
47
|
-
mkdirSync(join(cwd,
|
|
80
|
+
mkdirSync(join(skillsRoot(cwd), skill), { recursive: true });
|
|
48
81
|
writeFileSync(dest, readFileSync(new URL(`../../../skills/${skill}/SKILL.md`, import.meta.url)), exists ? undefined : { flag: "wx" });
|
|
49
82
|
notes.push(`${exists ? "overwrote" : "wrote"} ${dest}`);
|
|
50
83
|
}
|
|
@@ -52,7 +85,8 @@ async function installAgentFiles(cwd, force, docs, notes) {
|
|
|
52
85
|
const agents = join(cwd, "AGENTS.md");
|
|
53
86
|
const docPath = existsSync(claude) || !existsSync(agents) ? claude : agents;
|
|
54
87
|
const current = existsSync(docPath) ? readFileSync(docPath, "utf8") : "";
|
|
55
|
-
if (current.includes(DOCS_BEGIN) || current.includes(
|
|
88
|
+
if (current.includes(DOCS_BEGIN) || current.includes(`<!-- ${LEGACY_PREFIX}:agent-docs begin -->`)
|
|
89
|
+
|| current.includes(DOCS_END) || current.includes(`<!-- ${LEGACY_PREFIX}:agent-docs end -->`)) {
|
|
56
90
|
notes.push(`kept existing tickmarkr agent docs in ${docPath}`);
|
|
57
91
|
}
|
|
58
92
|
else if (docs || await confirm(`Append tickmarkr agent docs to ${docPath}?`)) {
|
|
@@ -94,8 +128,8 @@ async function runInitWizard(cwd) {
|
|
|
94
128
|
const llm = llmRaw === "pane" || llmRaw === "headless" ? llmRaw : llmDef;
|
|
95
129
|
let installSkills = false;
|
|
96
130
|
if (!skillsInstalled(cwd)) {
|
|
97
|
-
const skillsDef = existsSync(join(cwd, ".claude")) && !skillsInstalled(cwd);
|
|
98
|
-
installSkills = await askYesNo(rl, "Install agent skills (tickmarkr-loop/tickmarkr-auto)
|
|
131
|
+
const skillsDef = existsSync(join(cwd, ".claude", "skills")) && !skillsInstalled(cwd);
|
|
132
|
+
installSkills = await askYesNo(rl, "Install agent skills (tickmarkr-loop/tickmarkr-auto)?", skillsDef);
|
|
99
133
|
}
|
|
100
134
|
return { overlay: { driver, concurrency, visibility: { llm } }, installSkills };
|
|
101
135
|
}
|
|
@@ -126,7 +160,7 @@ export async function init(argv, cwd = process.cwd()) {
|
|
|
126
160
|
else {
|
|
127
161
|
notes.push(`kept existing ${globalPath}`);
|
|
128
162
|
}
|
|
129
|
-
const repoConfigPath = join(
|
|
163
|
+
const repoConfigPath = join(tickmarkrDir(cwd), "config.yaml");
|
|
130
164
|
const repoConfigExists = existsSync(repoConfigPath);
|
|
131
165
|
if (repoConfigExists)
|
|
132
166
|
notes.push(`kept existing ${repoConfigPath}`);
|
|
@@ -134,12 +168,16 @@ export async function init(argv, cwd = process.cwd()) {
|
|
|
134
168
|
if (existsSync(specPath)) {
|
|
135
169
|
notes.push(`kept existing ${specPath}`);
|
|
136
170
|
}
|
|
137
|
-
else if (existsSync(join(cwd, "drovr.spec.md"))) {
|
|
138
|
-
notes.push(`kept existing ${join(cwd, "drovr.spec.md")}`);
|
|
139
|
-
}
|
|
140
171
|
else {
|
|
141
|
-
|
|
142
|
-
|
|
172
|
+
const legacySpec = join(cwd, `${LEGACY_PREFIX}.spec.md`);
|
|
173
|
+
if (existsSync(legacySpec)) {
|
|
174
|
+
writeFileSync(specPath, readFileSync(legacySpec, "utf8"));
|
|
175
|
+
notes.push(`wrote ${specPath}`);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
writeFileSync(specPath, specTemplate());
|
|
179
|
+
notes.push(`wrote ${specPath}`);
|
|
180
|
+
}
|
|
143
181
|
}
|
|
144
182
|
const fresh = values.fresh ?? false;
|
|
145
183
|
const { reuse, ageMs, health } = initDoctorReuse(cwd, fresh);
|
|
@@ -5,7 +5,15 @@ import { loadConfig } from "../../config/config.js";
|
|
|
5
5
|
import { loadGraph } from "../../graph/graph.js";
|
|
6
6
|
import { excludedChannels, exclusionLine } from "../../route/preference.js";
|
|
7
7
|
import { route, RoutingError } from "../../route/router.js";
|
|
8
|
+
import { modelId } from "../../gates/review.js";
|
|
8
9
|
import { loadRoutingProfile } from "../../run/journal.js";
|
|
10
|
+
const fleetCanCrossVendorReview = (channels) => {
|
|
11
|
+
for (let i = 0; i < channels.length; i++)
|
|
12
|
+
for (let j = 0; j < channels.length; j++)
|
|
13
|
+
if (i !== j && channels[i].vendor !== channels[j].vendor && modelId(channels[i].model) !== modelId(channels[j].model))
|
|
14
|
+
return true;
|
|
15
|
+
return false;
|
|
16
|
+
};
|
|
9
17
|
export async function plan(_argv, cwd = process.cwd(), adapters = allAdapters()) {
|
|
10
18
|
// ponytail: hardcoded 24h TTL — promote to config when an operator asks. mtime is the signal because
|
|
11
19
|
// doctor.json has no probe timestamp and a schema field would break the existing-files compat invariant.
|
|
@@ -59,6 +67,9 @@ export async function plan(_argv, cwd = process.cwd(), adapters = allAdapters())
|
|
|
59
67
|
lints.push(`${role}: ${sel.adapter}:${sel.model} not installed — that gate/consult will fail closed`);
|
|
60
68
|
}
|
|
61
69
|
lints.push(...modelLints(cfg, health, adapters, { tty: ttyVisual() })); // health may be pre-v1.5/probeAll-fallback — no-detection branch covers both
|
|
70
|
+
if (cfg.review.required && channels.length && !fleetCanCrossVendorReview(channels)) {
|
|
71
|
+
lints.push("review: no cross-vendor reviewer pair in fleet — set review.required: false to waive");
|
|
72
|
+
}
|
|
62
73
|
let cost = 0;
|
|
63
74
|
for (const t of g.tasks) {
|
|
64
75
|
try {
|
|
@@ -69,7 +80,7 @@ export async function plan(_argv, cwd = process.cwd(), adapters = allAdapters())
|
|
|
69
80
|
}
|
|
70
81
|
const est = r.assignment.channel === "sub" ? 0 : cfg.pricing[r.assignment.tier];
|
|
71
82
|
cost += est;
|
|
72
|
-
lines.push(` ${t.id.padEnd(6)} ${t.shape.padEnd(10)} c${String(t.complexity).padEnd(3)}→ ${r.assignment.adapter}:${r.assignment.model} [${r.assignment.channel}/${r.assignment.tier}]${t.humanGate ? " (human gate)" : ""}${est ? ` ~$${est.toFixed(2)}` : ""} — ${r.provenance}`);
|
|
83
|
+
lines.push(` ${t.id.padEnd(6)} ${t.shape.padEnd(10)} c${String(t.complexity).padEnd(3)}→ ${r.assignment.adapter}:${r.assignment.model} [${r.assignment.channel}/${r.assignment.tier}]${t.timeoutMinutes !== undefined ? ` (timeout ${t.timeoutMinutes}m)` : ""}${t.humanGate ? " (human gate)" : ""}${est ? ` ~$${est.toFixed(2)}` : ""} — ${r.provenance}`);
|
|
73
84
|
if (r.deviation) {
|
|
74
85
|
deviations++;
|
|
75
86
|
const d = r.deviation;
|
|
@@ -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);
|
|
@@ -2,10 +2,13 @@ import { loadConfig } from "../../config/config.js";
|
|
|
2
2
|
import { pickDriver } from "../../drivers/index.js";
|
|
3
3
|
import { formatSummary, runDaemon } from "../../run/daemon.js";
|
|
4
4
|
import { formatJournalNarration } from "../../run/journal.js";
|
|
5
|
+
const summaryGreen = (s) => s.failed.length === 0 && s.human.length === 0 && s.blocked.length === 0 && s.pending.length === 0
|
|
6
|
+
&& s.tipVerify !== "failed";
|
|
5
7
|
export async function resume(argv, cwd = process.cwd()) {
|
|
6
8
|
const runId = argv[0];
|
|
7
9
|
if (!runId)
|
|
8
10
|
throw new Error("usage: tickmarkr resume <run-id>");
|
|
9
11
|
const s = await runDaemon(cwd, { runId, resume: true, driver: pickDriver(loadConfig(cwd)), narrate: (event) => console.log(formatJournalNarration(event)) });
|
|
10
|
-
|
|
12
|
+
const out = `resumed ${s.runId} — ${formatSummary(s)}`;
|
|
13
|
+
return { out, code: summaryGreen(s) ? 0 : 2 };
|
|
11
14
|
}
|
package/dist/cli/commands/run.js
CHANGED
|
@@ -6,6 +6,8 @@ import { loadGraph } from "../../graph/graph.js";
|
|
|
6
6
|
import { formatSummary, runDaemon } from "../../run/daemon.js";
|
|
7
7
|
import { route } from "../../route/router.js";
|
|
8
8
|
import { formatJournalNarration, loadRoutingProfile } from "../../run/journal.js";
|
|
9
|
+
const summaryGreen = (s) => s.failed.length === 0 && s.human.length === 0 && s.blocked.length === 0 && s.pending.length === 0
|
|
10
|
+
&& s.tipVerify !== "failed";
|
|
9
11
|
export async function run(argv, cwd = process.cwd()) {
|
|
10
12
|
const { values } = parseArgs({
|
|
11
13
|
args: argv,
|
|
@@ -15,6 +17,11 @@ export async function run(argv, cwd = process.cwd()) {
|
|
|
15
17
|
"route-strict": { type: "boolean" },
|
|
16
18
|
},
|
|
17
19
|
});
|
|
20
|
+
if (values.concurrency !== undefined) {
|
|
21
|
+
const n = Number(values.concurrency);
|
|
22
|
+
if (!Number.isInteger(n) || n <= 0)
|
|
23
|
+
throw new Error(`--concurrency must be a positive integer (got ${values.concurrency})`);
|
|
24
|
+
}
|
|
18
25
|
const cfg = loadConfig(cwd);
|
|
19
26
|
if (values["route-strict"]) {
|
|
20
27
|
const adapters = allAdapters();
|
|
@@ -31,5 +38,6 @@ export async function run(argv, cwd = process.cwd()) {
|
|
|
31
38
|
driver: pickDriver(cfg, values.driver),
|
|
32
39
|
narrate: (event) => console.log(formatJournalNarration(event)),
|
|
33
40
|
});
|
|
34
|
-
|
|
41
|
+
const out = `run ${s.runId} finished — ${formatSummary(s)} (merge to main is a human decision)`;
|
|
42
|
+
return { out, code: summaryGreen(s) ? 0 : 2 };
|
|
35
43
|
}
|
|
@@ -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)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function version(_argv?: string[]): Promise<string>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "../../../package.json");
|
|
5
|
+
export async function version(_argv = []) {
|
|
6
|
+
const { version: v } = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
7
|
+
return v;
|
|
8
|
+
}
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export type
|
|
2
|
+
export type CommandResult = string | {
|
|
3
|
+
out: string;
|
|
4
|
+
code: number;
|
|
5
|
+
};
|
|
6
|
+
export type CommandMap = Record<string, (argv: string[]) => Promise<CommandResult>>;
|
|
3
7
|
export declare const COMMANDS: CommandMap;
|
|
4
|
-
export declare const
|
|
5
|
-
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 .drovr/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";
|
|
8
|
+
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";
|
|
6
9
|
export declare function dispatch(cmd: string | undefined, argv: string[], commands?: CommandMap): Promise<{
|
|
7
10
|
out: string;
|
|
8
11
|
code: number;
|
package/dist/cli/index.js
CHANGED
|
@@ -13,26 +13,19 @@ import { run } from "./commands/run.js";
|
|
|
13
13
|
import { scope } from "./commands/scope.js";
|
|
14
14
|
import { status } from "./commands/status.js";
|
|
15
15
|
import { unlock } from "./commands/unlock.js";
|
|
16
|
+
import { version } from "./commands/version.js";
|
|
17
|
+
const normalize = (r) => typeof r === "string" ? { out: r, code: 0 } : r;
|
|
16
18
|
export const COMMANDS = {
|
|
17
|
-
init, doctor, compile, scope, plan, run, status, resume, report, profile, unlock, approve,
|
|
19
|
+
init, doctor, compile, scope, plan, run, status, resume, report, profile, unlock, approve, version,
|
|
18
20
|
};
|
|
21
|
+
const VERSION_FLAGS = new Set(["version", "--version", "-v"]);
|
|
19
22
|
const HELP_CMDS = new Set(["help", "-h", "--help"]);
|
|
20
|
-
|
|
21
|
-
// Never printed to pipes — the non-TTY stdout surface is byte-pinned by tests and consumed by machines.
|
|
22
|
-
const B = "\x1b[1m", R = "\x1b[0m";
|
|
23
|
-
const g = (n, s) => `\x1b[38;5;${n}m${s}${R}`; // 256-color green ramp, bright → deep
|
|
24
|
-
export const BANNER = [
|
|
25
|
-
` ${g(84, "▄▄████")}`,
|
|
26
|
-
` ${g(78, "▄▄████▀▀")}`,
|
|
27
|
-
`${g(41, "████▄▄▄▄████▀▀")} ${B}tickmarkr${R}`,
|
|
28
|
-
` ${g(35, "▀▀████▀▀")} spec in, verified work out.`,
|
|
29
|
-
"",
|
|
30
|
-
].join("\n");
|
|
23
|
+
import { BANNER } from "../brand.js";
|
|
31
24
|
export const USAGE = `tickmarkr — spec-driven orchestration harness for AI coding agents
|
|
32
25
|
usage: tickmarkr <command>
|
|
33
26
|
init guided setup + doctor; init --agent [--force] [--docs] adds agent skills/docs
|
|
34
27
|
doctor re-probe adapters, herdr, auth; print capability matrix
|
|
35
|
-
compile <src> spec → .
|
|
28
|
+
compile <src> spec → .tickmarkr/graph.json (fails without acceptance criteria)
|
|
36
29
|
scope <intent> draft a compiled native spec beside an answered intent (--force to overwrite)
|
|
37
30
|
plan dry-run routing table + cost estimate + floor lints
|
|
38
31
|
run execute the graph (--concurrency N --driver herdr|subprocess --route-strict)
|
|
@@ -43,9 +36,11 @@ usage: tickmarkr <command>
|
|
|
43
36
|
unlock remove a stale/garbage run lock (refuses if the holder is alive)
|
|
44
37
|
approve <id> <task> approve a parked human gate (--by <name> --reason <text>); takes effect on resume`;
|
|
45
38
|
// pure, testable dispatcher: resolves a command, forwards argv, shapes the result — no side effects.
|
|
46
|
-
// unknown/missing cmd → USAGE (exit 1 if a cmd was typed, 0 for bare `
|
|
47
|
-
// a one-line `
|
|
39
|
+
// unknown/missing cmd → USAGE (exit 1 if a cmd was typed, 0 for bare `tickmarkr`); a handler throw becomes
|
|
40
|
+
// a one-line `tickmarkr <cmd>: <message>` (never a raw stack) at exit 1.
|
|
48
41
|
export async function dispatch(cmd, argv, commands = COMMANDS) {
|
|
42
|
+
if (cmd && VERSION_FLAGS.has(cmd))
|
|
43
|
+
return { out: await version(argv), code: 0 };
|
|
49
44
|
const usage = process.stdout.isTTY ? BANNER + USAGE : USAGE;
|
|
50
45
|
if (!cmd || HELP_CMDS.has(cmd))
|
|
51
46
|
return { out: usage, code: 0 };
|
|
@@ -53,7 +48,7 @@ export async function dispatch(cmd, argv, commands = COMMANDS) {
|
|
|
53
48
|
if (!fn)
|
|
54
49
|
return { out: usage, code: 1 };
|
|
55
50
|
try {
|
|
56
|
-
return
|
|
51
|
+
return normalize(await fn(argv));
|
|
57
52
|
}
|
|
58
53
|
catch (err) {
|
|
59
54
|
return { out: `tickmarkr ${cmd}: ${err.message}`, code: 1 };
|
|
@@ -61,7 +56,7 @@ export async function dispatch(cmd, argv, commands = COMMANDS) {
|
|
|
61
56
|
}
|
|
62
57
|
/* v8 ignore start -- binary entry: printing + process.exit side effects, not unit-testable (ROADMAP crit 2) */
|
|
63
58
|
// node realpaths the main module (import.meta.url) but argv[1] keeps the symlink path —
|
|
64
|
-
// a globally-linked `
|
|
59
|
+
// a globally-linked `tickmarkr` bin silently no-oped here (OBS-10); compare realpaths
|
|
65
60
|
const argv1Real = (() => { try {
|
|
66
61
|
return process.argv[1] ? realpathSync(process.argv[1]) : "";
|
|
67
62
|
}
|
|
@@ -72,7 +67,7 @@ if (argv1Real && import.meta.url === pathToFileURL(argv1Real).href) {
|
|
|
72
67
|
const [cmd, ...argv] = process.argv.slice(2);
|
|
73
68
|
dispatch(cmd, argv).then(({ out, code }) => {
|
|
74
69
|
// byte-identical streams: usage + success → stdout; a handler throw → stderr (original behavior)
|
|
75
|
-
(out.endsWith(USAGE)
|
|
70
|
+
(code === 1 && !out.endsWith(USAGE) ? console.error : console.log)(out);
|
|
76
71
|
if (code !== 0)
|
|
77
72
|
process.exit(code);
|
|
78
73
|
});
|
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,14 +1,16 @@
|
|
|
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+- (.+)$/;
|
|
8
10
|
// v1.19: a typed acceptance oracle line — "command: ...", "test: ...", or "judge: ...". Anything
|
|
9
11
|
// without one of these prefixes is a plain-string judge criterion (compat path, emits a warning).
|
|
10
12
|
const ORACLE_RE = new RegExp(`^(${ORACLES.join("|")}):\\s*(.*)$`);
|
|
11
|
-
const FIELDS = new Set(["goal", "shape", "deps", "files", "context", "complexity", "humangate", "pin", "floor", "gates", "acceptance"]);
|
|
13
|
+
const FIELDS = new Set(["goal", "shape", "deps", "files", "context", "complexity", "humangate", "pin", "floor", "gates", "acceptance", "timeout"]);
|
|
12
14
|
function invalid(task, field, detail) {
|
|
13
15
|
throw new CompileError(`Task ${task} field "${field}" ${detail}`);
|
|
14
16
|
}
|
|
@@ -108,6 +110,10 @@ export function compileNative(file) {
|
|
|
108
110
|
const badGate = draft.gates.find((gate) => !GATE_NAMES.includes(gate));
|
|
109
111
|
if (badGate)
|
|
110
112
|
invalid(draft.id, "gates", `contains invalid gate "${badGate}"`);
|
|
113
|
+
const timeoutMinutes = fields.timeout === undefined ? undefined : Number(fields.timeout);
|
|
114
|
+
if (fields.timeout !== undefined && (!Number.isFinite(timeoutMinutes) || timeoutMinutes <= 0)) {
|
|
115
|
+
invalid(draft.id, "timeout", "must be a positive number");
|
|
116
|
+
}
|
|
111
117
|
const routingHints = pin || fields.floor ? {
|
|
112
118
|
...(pin ? { pin: { via: pin[0], model: pin[1] } } : {}),
|
|
113
119
|
...(fields.floor ? { floor: fields.floor } : {}),
|
|
@@ -123,6 +129,7 @@ export function compileNative(file) {
|
|
|
123
129
|
context: csv(fields.context),
|
|
124
130
|
acceptance: draft.acceptance,
|
|
125
131
|
...(fields.humangate === "true" ? { humanGate: true } : {}),
|
|
132
|
+
...(timeoutMinutes !== undefined ? { timeoutMinutes } : {}),
|
|
126
133
|
...(routingHints ? { routingHints } : {}),
|
|
127
134
|
...(draft.hasGates ? { gates: draft.gates } : {}),
|
|
128
135
|
};
|
|
@@ -139,7 +146,7 @@ export function compileNative(file) {
|
|
|
139
146
|
}
|
|
140
147
|
return result;
|
|
141
148
|
}
|
|
142
|
-
// Commented native spec written to
|
|
149
|
+
// Commented native spec written to tickmarkr.spec.md by `tickmarkr init`. Documented via HTML comments (which the
|
|
143
150
|
// parser ignores) so the template itself round-trips through compileSource() unchanged. Every field is
|
|
144
151
|
// documented; the two example tasks illustrate plain vs deps+routing-hint, each with acceptance[].
|
|
145
152
|
export function specTemplate() {
|
|
@@ -164,7 +171,8 @@ acceptance is required on every task (a nested list of observable outcomes).
|
|
|
164
171
|
humanGate: true | false — pauses for a human review before merging this task
|
|
165
172
|
pin: "via model" — pin an exact channel, e.g. "claude-code opus"
|
|
166
173
|
floor: cheap | mid | frontier — minimum capability tier for routing
|
|
167
|
-
gates: nested list
|
|
174
|
+
gates: nested list; build | test | lint | evidence | scope are mandatory;
|
|
175
|
+
acceptance | review may be omitted
|
|
168
176
|
acceptance: nested list (REQUIRED, non-empty). Each item is either a typed oracle or plain text:
|
|
169
177
|
- command: <shell> (oracle: command — exit code)
|
|
170
178
|
- test: <name> (oracle: test — named test)
|
package/dist/config/config.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ declare const TierEnum: z.ZodEnum<{
|
|
|
6
6
|
}>;
|
|
7
7
|
export type Tier = z.infer<typeof TierEnum>;
|
|
8
8
|
export declare const TIER_RANK: Record<Tier, number>;
|
|
9
|
+
export declare const DEFAULT_DIFF_CAP = 60000;
|
|
9
10
|
export declare const MapEntrySchema: z.ZodObject<{
|
|
10
11
|
pin: z.ZodOptional<z.ZodObject<{
|
|
11
12
|
via: z.ZodString;
|
|
@@ -46,7 +47,7 @@ export declare const SubPricingSchema: z.ZodObject<{
|
|
|
46
47
|
windowsPerMonthHigh: z.ZodNumber;
|
|
47
48
|
}, z.core.$strip>;
|
|
48
49
|
export type SubPricing = z.infer<typeof SubPricingSchema>;
|
|
49
|
-
export declare const
|
|
50
|
+
export declare const TickmarkrConfigSchema: z.ZodObject<{
|
|
50
51
|
concurrency: z.ZodNumber;
|
|
51
52
|
driver: z.ZodEnum<{
|
|
52
53
|
auto: "auto";
|
|
@@ -84,6 +85,7 @@ export declare const DrovrConfigSchema: z.ZodObject<{
|
|
|
84
85
|
halfLifeRuns: z.ZodOptional<z.ZodNumber>;
|
|
85
86
|
availWeight: z.ZodOptional<z.ZodNumber>;
|
|
86
87
|
}, z.core.$strip>>;
|
|
88
|
+
allowUnverifiedModels: z.ZodBoolean;
|
|
87
89
|
allow: z.ZodOptional<z.ZodObject<{
|
|
88
90
|
adapters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
89
91
|
models: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -123,6 +125,7 @@ export declare const DrovrConfigSchema: z.ZodObject<{
|
|
|
123
125
|
build: z.ZodOptional<z.ZodString>;
|
|
124
126
|
test: z.ZodOptional<z.ZodString>;
|
|
125
127
|
lint: z.ZodOptional<z.ZodString>;
|
|
128
|
+
diffCap: z.ZodOptional<z.ZodNumber>;
|
|
126
129
|
byShape: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodEnum<{
|
|
127
130
|
plan: "plan";
|
|
128
131
|
spec: "spec";
|
|
@@ -171,23 +174,23 @@ export declare const DrovrConfigSchema: z.ZodObject<{
|
|
|
171
174
|
workersPerTab: z.ZodNumber;
|
|
172
175
|
}, z.core.$strip>;
|
|
173
176
|
}, z.core.$strip>;
|
|
174
|
-
export type
|
|
177
|
+
export type TickmarkrConfig = z.infer<typeof TickmarkrConfigSchema>;
|
|
175
178
|
export declare class ConfigError extends Error {
|
|
176
179
|
constructor(message: string);
|
|
177
180
|
}
|
|
178
|
-
export declare const DEFAULT_CONFIG:
|
|
181
|
+
export declare const DEFAULT_CONFIG: TickmarkrConfig;
|
|
179
182
|
export declare function globalConfigDir(): string;
|
|
180
183
|
export declare function overlayPreferShapes(repoRoot: string, opts?: {
|
|
181
184
|
globalDir?: string;
|
|
182
185
|
}): ReadonlySet<string>;
|
|
183
186
|
export declare function loadConfig(repoRoot: string, opts?: {
|
|
184
187
|
globalDir?: string;
|
|
185
|
-
}):
|
|
188
|
+
}): TickmarkrConfig;
|
|
186
189
|
export type InitConfigOverlay = {
|
|
187
190
|
concurrency?: number;
|
|
188
|
-
driver?:
|
|
191
|
+
driver?: TickmarkrConfig["driver"];
|
|
189
192
|
visibility?: {
|
|
190
|
-
llm?:
|
|
193
|
+
llm?: TickmarkrConfig["visibility"]["llm"];
|
|
191
194
|
};
|
|
192
195
|
};
|
|
193
196
|
export declare function configTemplate(overlay?: InitConfigOverlay): string;
|