cleargate 0.2.0 → 0.2.1
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/dist/MANIFEST.json +5 -5
- package/dist/cli.cjs +119 -132
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +119 -132
- package/dist/cli.js.map +1 -1
- package/dist/templates/cleargate-planning/.claude/agents/reporter.md +15 -0
- package/dist/templates/cleargate-planning/.claude/skills/flashcard/SKILL.md +31 -12
- package/dist/templates/cleargate-planning/.cleargate/FLASHCARD.md +2 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/story.md +10 -0
- package/dist/templates/cleargate-planning/CLAUDE.md +1 -0
- package/dist/templates/cleargate-planning/MANIFEST.json +5 -5
- package/package.json +1 -1
- package/templates/cleargate-planning/.claude/agents/reporter.md +15 -0
- package/templates/cleargate-planning/.claude/skills/flashcard/SKILL.md +31 -12
- package/templates/cleargate-planning/.cleargate/FLASHCARD.md +2 -1
- package/templates/cleargate-planning/.cleargate/templates/story.md +10 -0
- package/templates/cleargate-planning/CLAUDE.md +1 -0
- package/templates/cleargate-planning/MANIFEST.json +5 -5
package/dist/MANIFEST.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"cleargate_version": "0.2.
|
|
3
|
-
"generated_at": "2026-04-
|
|
2
|
+
"cleargate_version": "0.2.1",
|
|
3
|
+
"generated_at": "2026-04-19T18:33:06.178Z",
|
|
4
4
|
"files": [
|
|
5
5
|
{
|
|
6
6
|
"path": ".claude/agents/architect.md",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
"path": ".claude/agents/reporter.md",
|
|
49
|
-
"sha256": "
|
|
49
|
+
"sha256": "a9b7e248fd1baaef7fb0c23d4ba92bc94bcd46e9261a6dabbd183477fab96359",
|
|
50
50
|
"tier": "agent",
|
|
51
51
|
"overwrite_policy": "always",
|
|
52
52
|
"preserve_on_uninstall": false
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"path": ".claude/skills/flashcard/SKILL.md",
|
|
84
|
-
"sha256": "
|
|
84
|
+
"sha256": "4c0fe5bb74697e1eb1f37203ff44b8e6a800f024a2f8c3fc8542aa206cd2c777",
|
|
85
85
|
"tier": "skill",
|
|
86
86
|
"overwrite_policy": "always",
|
|
87
87
|
"preserve_on_uninstall": false
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
},
|
|
152
152
|
{
|
|
153
153
|
"path": ".cleargate/templates/story.md",
|
|
154
|
-
"sha256": "
|
|
154
|
+
"sha256": "6c32fcabc9332cb928ac3b8fbfe618751b0a4cb05cd0f16463c3c4e22f8a67f1",
|
|
155
155
|
"tier": "template",
|
|
156
156
|
"overwrite_policy": "merge-3way",
|
|
157
157
|
"preserve_on_uninstall": false
|
package/dist/cli.cjs
CHANGED
|
@@ -33,7 +33,7 @@ var import_commander = require("commander");
|
|
|
33
33
|
// package.json
|
|
34
34
|
var package_default = {
|
|
35
35
|
name: "cleargate",
|
|
36
|
-
version: "0.2.
|
|
36
|
+
version: "0.2.1",
|
|
37
37
|
private: false,
|
|
38
38
|
type: "module",
|
|
39
39
|
description: "Planning framework for Claude Code agents \u2014 sprint/epic/story protocol, four-agent loop (architect/developer/qa/reporter), Karpathy-style awareness wiki.",
|
|
@@ -541,6 +541,7 @@ function getCodebaseVersion(opts) {
|
|
|
541
541
|
var fs4 = __toESM(require("fs/promises"), 1);
|
|
542
542
|
|
|
543
543
|
// src/wiki/parse-frontmatter.ts
|
|
544
|
+
var import_js_yaml = __toESM(require("js-yaml"), 1);
|
|
544
545
|
function parseFrontmatter(raw) {
|
|
545
546
|
const lines = raw.split("\n");
|
|
546
547
|
if (lines[0] !== "---") {
|
|
@@ -556,71 +557,45 @@ function parseFrontmatter(raw) {
|
|
|
556
557
|
if (closeIdx === -1) {
|
|
557
558
|
throw new Error("parseFrontmatter: missing closing ---");
|
|
558
559
|
}
|
|
559
|
-
const
|
|
560
|
+
const yamlText = lines.slice(1, closeIdx).join("\n");
|
|
560
561
|
const bodyLines = lines.slice(closeIdx + 1);
|
|
561
562
|
if (bodyLines[0] === "") bodyLines.shift();
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
const colon = line.indexOf(":");
|
|
566
|
-
if (colon === -1) continue;
|
|
567
|
-
const key = line.slice(0, colon).trim();
|
|
568
|
-
const val = line.slice(colon + 1).trim();
|
|
569
|
-
if (val === "" || val === "[]") {
|
|
570
|
-
fm[key] = [];
|
|
571
|
-
continue;
|
|
572
|
-
}
|
|
573
|
-
if (val.startsWith("[") && val.endsWith("]")) {
|
|
574
|
-
const inner = val.slice(1, -1).trim();
|
|
575
|
-
if (inner === "") {
|
|
576
|
-
fm[key] = [];
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
fm[key] = inner.split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
|
|
580
|
-
continue;
|
|
581
|
-
}
|
|
582
|
-
if (val.startsWith("{")) {
|
|
583
|
-
fm[key] = val;
|
|
584
|
-
continue;
|
|
585
|
-
}
|
|
586
|
-
fm[key] = val.replace(/^["']|["']$/g, "");
|
|
563
|
+
const body = bodyLines.join("\n");
|
|
564
|
+
if (yamlText.trim() === "") {
|
|
565
|
+
return { fm: {}, body };
|
|
587
566
|
}
|
|
588
|
-
|
|
567
|
+
let parsed;
|
|
568
|
+
try {
|
|
569
|
+
parsed = import_js_yaml.default.load(yamlText, { schema: import_js_yaml.default.CORE_SCHEMA });
|
|
570
|
+
} catch (err) {
|
|
571
|
+
throw new Error(`parseFrontmatter: invalid YAML: ${err.message}`);
|
|
572
|
+
}
|
|
573
|
+
if (parsed === null || parsed === void 0) {
|
|
574
|
+
return { fm: {}, body };
|
|
575
|
+
}
|
|
576
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
577
|
+
throw new Error("parseFrontmatter: frontmatter is not a YAML mapping");
|
|
578
|
+
}
|
|
579
|
+
return { fm: parsed, body };
|
|
589
580
|
}
|
|
590
581
|
|
|
591
582
|
// src/lib/frontmatter-yaml.ts
|
|
583
|
+
var import_js_yaml2 = __toESM(require("js-yaml"), 1);
|
|
592
584
|
function serializeFrontmatter(fm) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
} else {
|
|
609
|
-
const s = String(val);
|
|
610
|
-
if (s.startsWith("{")) {
|
|
611
|
-
lines.push(`${key}: ${s}`);
|
|
612
|
-
} else {
|
|
613
|
-
const needsQuotes = /[:#\[\]{}&*!|>'"%@`\n]/.test(s) || s.trim() !== s || s === "" || s === "null" || s === "true" || s === "false";
|
|
614
|
-
if (needsQuotes) {
|
|
615
|
-
lines.push(`${key}: "${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`);
|
|
616
|
-
} else {
|
|
617
|
-
lines.push(`${key}: ${s}`);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
lines.push("---");
|
|
623
|
-
return lines.join("\n");
|
|
585
|
+
if (Object.keys(fm).length === 0) {
|
|
586
|
+
return "---\n---";
|
|
587
|
+
}
|
|
588
|
+
const yamlBody = import_js_yaml2.default.dump(fm, {
|
|
589
|
+
schema: import_js_yaml2.default.CORE_SCHEMA,
|
|
590
|
+
lineWidth: -1,
|
|
591
|
+
noRefs: true,
|
|
592
|
+
noCompatMode: true,
|
|
593
|
+
quotingType: '"',
|
|
594
|
+
forceQuotes: false
|
|
595
|
+
});
|
|
596
|
+
return `---
|
|
597
|
+
${yamlBody.replace(/\n+$/, "")}
|
|
598
|
+
---`;
|
|
624
599
|
}
|
|
625
600
|
function toIsoSecond(d) {
|
|
626
601
|
return d.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -2217,7 +2192,7 @@ function loadWikiPages(wikiRoot) {
|
|
|
2217
2192
|
var fs16 = __toESM(require("fs"), 1);
|
|
2218
2193
|
var path17 = __toESM(require("path"), 1);
|
|
2219
2194
|
var import_node_child_process3 = require("child_process");
|
|
2220
|
-
var
|
|
2195
|
+
var import_js_yaml3 = __toESM(require("js-yaml"), 1);
|
|
2221
2196
|
|
|
2222
2197
|
// src/lib/work-item-type.ts
|
|
2223
2198
|
var FM_KEY_MAP = [
|
|
@@ -2464,7 +2439,7 @@ function parseCachedGateResult(raw) {
|
|
|
2464
2439
|
if (typeof raw === "string") {
|
|
2465
2440
|
if (!raw.startsWith("{")) return null;
|
|
2466
2441
|
try {
|
|
2467
|
-
const parsed =
|
|
2442
|
+
const parsed = import_js_yaml3.default.load(raw);
|
|
2468
2443
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
2469
2444
|
const p = parsed;
|
|
2470
2445
|
return { pass: p["pass"], failing_criteria: p["failing_criteria"], last_gate_check: p["last_gate_check"] };
|
|
@@ -2947,15 +2922,23 @@ function emitSummary(driftMap, verbose, stdout) {
|
|
|
2947
2922
|
var SESSION_START_MAX_ITEMS = 10;
|
|
2948
2923
|
var SESSION_START_MAX_CHARS = 400;
|
|
2949
2924
|
function parseCachedGateResult2(raw) {
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2925
|
+
if (raw == null) return null;
|
|
2926
|
+
let parsed = null;
|
|
2927
|
+
if (typeof raw === "object" && !Array.isArray(raw)) {
|
|
2928
|
+
parsed = raw;
|
|
2929
|
+
} else if (typeof raw === "string") {
|
|
2930
|
+
try {
|
|
2931
|
+
parsed = JSON.parse(raw);
|
|
2932
|
+
} catch {
|
|
2933
|
+
return null;
|
|
2934
|
+
}
|
|
2935
|
+
} else {
|
|
2957
2936
|
return null;
|
|
2958
2937
|
}
|
|
2938
|
+
return {
|
|
2939
|
+
pass: parsed.pass ?? null,
|
|
2940
|
+
failing_criteria: parsed.failing_criteria ?? []
|
|
2941
|
+
};
|
|
2959
2942
|
}
|
|
2960
2943
|
async function runSessionStart(cwd, stdout) {
|
|
2961
2944
|
const pendingSyncDir = path20.join(cwd, ".cleargate", "delivery", "pending-sync");
|
|
@@ -2980,9 +2963,7 @@ async function runSessionStart(cwd, stdout) {
|
|
|
2980
2963
|
} catch {
|
|
2981
2964
|
continue;
|
|
2982
2965
|
}
|
|
2983
|
-
const
|
|
2984
|
-
if (typeof gateRaw !== "string") continue;
|
|
2985
|
-
const gate2 = parseCachedGateResult2(gateRaw);
|
|
2966
|
+
const gate2 = parseCachedGateResult2(fm["cached_gate_result"]);
|
|
2986
2967
|
if (!gate2 || gate2.pass !== false) continue;
|
|
2987
2968
|
const idKeys = ["story_id", "epic_id", "proposal_id", "cr_id", "bug_id", "sprint_id"];
|
|
2988
2969
|
let itemId = "";
|
|
@@ -3047,15 +3028,23 @@ async function runPricing(filePath, cwd, stdout, stderr, exit) {
|
|
|
3047
3028
|
return;
|
|
3048
3029
|
}
|
|
3049
3030
|
const draftTokensRaw = fm["draft_tokens"];
|
|
3050
|
-
if (!draftTokensRaw
|
|
3031
|
+
if (!draftTokensRaw) {
|
|
3051
3032
|
stdout("draft_tokens unpopulated \u2014 run cleargate stamp-tokens first");
|
|
3052
3033
|
exit(1);
|
|
3053
3034
|
return;
|
|
3054
3035
|
}
|
|
3055
3036
|
let draftTokens;
|
|
3056
|
-
|
|
3057
|
-
draftTokens =
|
|
3058
|
-
}
|
|
3037
|
+
if (typeof draftTokensRaw === "object" && !Array.isArray(draftTokensRaw)) {
|
|
3038
|
+
draftTokens = draftTokensRaw;
|
|
3039
|
+
} else if (typeof draftTokensRaw === "string") {
|
|
3040
|
+
try {
|
|
3041
|
+
draftTokens = JSON.parse(draftTokensRaw);
|
|
3042
|
+
} catch {
|
|
3043
|
+
stdout("draft_tokens unpopulated \u2014 run cleargate stamp-tokens first");
|
|
3044
|
+
exit(1);
|
|
3045
|
+
return;
|
|
3046
|
+
}
|
|
3047
|
+
} else {
|
|
3059
3048
|
stdout("draft_tokens unpopulated \u2014 run cleargate stamp-tokens first");
|
|
3060
3049
|
exit(1);
|
|
3061
3050
|
return;
|
|
@@ -3117,7 +3106,7 @@ async function doctorHandler(flags, cli) {
|
|
|
3117
3106
|
// src/commands/gate.ts
|
|
3118
3107
|
var fs21 = __toESM(require("fs"), 1);
|
|
3119
3108
|
var path22 = __toESM(require("path"), 1);
|
|
3120
|
-
var
|
|
3109
|
+
var import_js_yaml5 = __toESM(require("js-yaml"), 1);
|
|
3121
3110
|
|
|
3122
3111
|
// src/lib/readiness-predicates.ts
|
|
3123
3112
|
var fs19 = __toESM(require("fs"), 1);
|
|
@@ -3445,7 +3434,7 @@ function evalStatusOf(parsed, opts, projectRoot) {
|
|
|
3445
3434
|
|
|
3446
3435
|
// src/lib/frontmatter-cache.ts
|
|
3447
3436
|
var fs20 = __toESM(require("fs/promises"), 1);
|
|
3448
|
-
var
|
|
3437
|
+
var import_js_yaml4 = __toESM(require("js-yaml"), 1);
|
|
3449
3438
|
async function readCachedGate(absPath) {
|
|
3450
3439
|
let raw;
|
|
3451
3440
|
try {
|
|
@@ -3459,20 +3448,7 @@ async function readCachedGate(absPath) {
|
|
|
3459
3448
|
} catch {
|
|
3460
3449
|
return null;
|
|
3461
3450
|
}
|
|
3462
|
-
|
|
3463
|
-
if (cached === void 0 || cached === null) return null;
|
|
3464
|
-
if (typeof cached === "string") {
|
|
3465
|
-
return parseCachedGateString(cached);
|
|
3466
|
-
}
|
|
3467
|
-
if (typeof cached === "object" && !Array.isArray(cached)) {
|
|
3468
|
-
const c = cached;
|
|
3469
|
-
return {
|
|
3470
|
-
pass: Boolean(c["pass"]),
|
|
3471
|
-
failing_criteria: Array.isArray(c["failing_criteria"]) ? c["failing_criteria"] : [],
|
|
3472
|
-
last_gate_check: String(c["last_gate_check"] ?? "")
|
|
3473
|
-
};
|
|
3474
|
-
}
|
|
3475
|
-
return null;
|
|
3451
|
+
return coerceCachedGate(fm["cached_gate_result"]);
|
|
3476
3452
|
}
|
|
3477
3453
|
async function writeCachedGate(absPath, result, opts) {
|
|
3478
3454
|
const nowFn = opts?.now ?? (() => /* @__PURE__ */ new Date());
|
|
@@ -3490,29 +3466,22 @@ async function writeCachedGate(absPath, result, opts) {
|
|
|
3490
3466
|
} catch {
|
|
3491
3467
|
throw new Error(`writeCachedGate: failed to parse frontmatter in ${absPath}`);
|
|
3492
3468
|
}
|
|
3493
|
-
const
|
|
3494
|
-
if (
|
|
3495
|
-
|
|
3496
|
-
const existingParsed = typeof existingCached === "string" ? parseCachedGateString(existingCached) : null;
|
|
3497
|
-
if (existingParsed && JSON.stringify(existingParsed) === JSON.stringify(newResult)) {
|
|
3498
|
-
return;
|
|
3499
|
-
}
|
|
3500
|
-
} catch {
|
|
3501
|
-
}
|
|
3469
|
+
const existing = coerceCachedGate(fm["cached_gate_result"]);
|
|
3470
|
+
if (existing && JSON.stringify(existing) === JSON.stringify(newResult)) {
|
|
3471
|
+
return;
|
|
3502
3472
|
}
|
|
3503
|
-
const cachedStr = serializeCachedGate(newResult);
|
|
3504
3473
|
const newFm = {};
|
|
3505
3474
|
let inserted = false;
|
|
3506
3475
|
for (const [k, v] of Object.entries(fm)) {
|
|
3507
3476
|
if (k === "cached_gate_result") {
|
|
3508
|
-
newFm["cached_gate_result"] =
|
|
3477
|
+
newFm["cached_gate_result"] = newResult;
|
|
3509
3478
|
inserted = true;
|
|
3510
3479
|
} else {
|
|
3511
3480
|
newFm[k] = v;
|
|
3512
3481
|
}
|
|
3513
3482
|
}
|
|
3514
3483
|
if (!inserted) {
|
|
3515
|
-
newFm["cached_gate_result"] =
|
|
3484
|
+
newFm["cached_gate_result"] = newResult;
|
|
3516
3485
|
}
|
|
3517
3486
|
const fmBlock = serializeFrontmatter(newFm);
|
|
3518
3487
|
const newContent = body.length > 0 ? `${fmBlock}
|
|
@@ -3521,24 +3490,31 @@ ${body}` : `${fmBlock}
|
|
|
3521
3490
|
`;
|
|
3522
3491
|
await fs20.writeFile(absPath, newContent, "utf8");
|
|
3523
3492
|
}
|
|
3524
|
-
function
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
function parseCachedGateString(s) {
|
|
3529
|
-
if (!s.startsWith("{")) return null;
|
|
3530
|
-
try {
|
|
3531
|
-
const parsed = import_js_yaml2.default.load(s);
|
|
3532
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
3533
|
-
const p = parsed;
|
|
3493
|
+
function coerceCachedGate(val) {
|
|
3494
|
+
if (val === void 0 || val === null) return null;
|
|
3495
|
+
if (typeof val === "object" && !Array.isArray(val)) {
|
|
3496
|
+
const c = val;
|
|
3534
3497
|
return {
|
|
3535
|
-
pass: Boolean(
|
|
3536
|
-
failing_criteria: Array.isArray(
|
|
3537
|
-
last_gate_check: String(
|
|
3498
|
+
pass: Boolean(c["pass"]),
|
|
3499
|
+
failing_criteria: Array.isArray(c["failing_criteria"]) ? c["failing_criteria"] : [],
|
|
3500
|
+
last_gate_check: String(c["last_gate_check"] ?? "")
|
|
3538
3501
|
};
|
|
3539
|
-
} catch {
|
|
3540
|
-
return null;
|
|
3541
3502
|
}
|
|
3503
|
+
if (typeof val === "string" && val.startsWith("{")) {
|
|
3504
|
+
try {
|
|
3505
|
+
const parsed = import_js_yaml4.default.load(val, { schema: import_js_yaml4.default.CORE_SCHEMA });
|
|
3506
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
3507
|
+
const p = parsed;
|
|
3508
|
+
return {
|
|
3509
|
+
pass: Boolean(p["pass"]),
|
|
3510
|
+
failing_criteria: Array.isArray(p["failing_criteria"]) ? p["failing_criteria"] : [],
|
|
3511
|
+
last_gate_check: String(p["last_gate_check"] ?? "")
|
|
3512
|
+
};
|
|
3513
|
+
} catch {
|
|
3514
|
+
return null;
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
return null;
|
|
3542
3518
|
}
|
|
3543
3519
|
|
|
3544
3520
|
// src/commands/gate.ts
|
|
@@ -3549,7 +3525,7 @@ function loadGateBlocks(gatesDocPath) {
|
|
|
3549
3525
|
let match;
|
|
3550
3526
|
while ((match = fenceRe.exec(raw)) !== null) {
|
|
3551
3527
|
const yamlContent = match[1];
|
|
3552
|
-
const parsed =
|
|
3528
|
+
const parsed = import_js_yaml5.default.load(yamlContent);
|
|
3553
3529
|
const block = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
3554
3530
|
if (block && typeof block === "object" && "work_item_type" in block && "transition" in block && "severity" in block && "criteria" in block) {
|
|
3555
3531
|
blocks.push(block);
|
|
@@ -3877,7 +3853,7 @@ async function stampTokensHandler(file, opts, cli) {
|
|
|
3877
3853
|
exitFn(1);
|
|
3878
3854
|
return;
|
|
3879
3855
|
}
|
|
3880
|
-
const existingDraftTokens =
|
|
3856
|
+
const existingDraftTokens = coerceDraftTokens(fm["draft_tokens"]);
|
|
3881
3857
|
const existingLastStamp = existingDraftTokens?.last_stamp ?? null;
|
|
3882
3858
|
const buckets = readLedgerForWorkItem(workItemId, { sprintRunsRoot: cli?.sprintRunsRoot });
|
|
3883
3859
|
if (existingLastStamp && buckets.length > 0) {
|
|
@@ -3913,7 +3889,7 @@ async function stampTokensHandler(file, opts, cli) {
|
|
|
3913
3889
|
if (opts.dryRun) {
|
|
3914
3890
|
stdoutFn(`[dry-run] stamp-tokens would write draft_tokens for ${workItemId}:`);
|
|
3915
3891
|
const draftTokensVal = newFm["draft_tokens"];
|
|
3916
|
-
stdoutFn(` draft_tokens: ${
|
|
3892
|
+
stdoutFn(` draft_tokens: ${JSON.stringify(draftTokensVal)}`);
|
|
3917
3893
|
if (stampError) {
|
|
3918
3894
|
stdoutFn(` stamp_error: "${stampError}"`);
|
|
3919
3895
|
}
|
|
@@ -3952,15 +3928,29 @@ function extractWorkItemId(fm, absPath) {
|
|
|
3952
3928
|
}
|
|
3953
3929
|
return null;
|
|
3954
3930
|
}
|
|
3955
|
-
function
|
|
3931
|
+
function coerceDraftTokens(val) {
|
|
3956
3932
|
if (val == null) return null;
|
|
3957
|
-
if (typeof val
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3933
|
+
if (typeof val === "object" && !Array.isArray(val)) {
|
|
3934
|
+
const o = val;
|
|
3935
|
+
return {
|
|
3936
|
+
input: typeof o["input"] === "number" ? o["input"] : null,
|
|
3937
|
+
output: typeof o["output"] === "number" ? o["output"] : null,
|
|
3938
|
+
cache_creation: typeof o["cache_creation"] === "number" ? o["cache_creation"] : null,
|
|
3939
|
+
cache_read: typeof o["cache_read"] === "number" ? o["cache_read"] : null,
|
|
3940
|
+
model: typeof o["model"] === "string" ? o["model"] : null,
|
|
3941
|
+
last_stamp: typeof o["last_stamp"] === "string" ? o["last_stamp"] : "",
|
|
3942
|
+
sessions: Array.isArray(o["sessions"]) ? o["sessions"] : []
|
|
3943
|
+
};
|
|
3963
3944
|
}
|
|
3945
|
+
if (typeof val === "string") {
|
|
3946
|
+
try {
|
|
3947
|
+
const parsed = JSON.parse(val);
|
|
3948
|
+
return parsed;
|
|
3949
|
+
} catch {
|
|
3950
|
+
return null;
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
return null;
|
|
3964
3954
|
}
|
|
3965
3955
|
function aggregateBuckets(buckets, nowIso) {
|
|
3966
3956
|
let totalInput = 0;
|
|
@@ -4013,12 +4003,9 @@ function buildNewFrontmatter(existingFm, tokens, stampError) {
|
|
|
4013
4003
|
if (stampError) {
|
|
4014
4004
|
newFm["stamp_error"] = stampError;
|
|
4015
4005
|
}
|
|
4016
|
-
newFm["draft_tokens"] =
|
|
4006
|
+
newFm["draft_tokens"] = tokens;
|
|
4017
4007
|
return newFm;
|
|
4018
4008
|
}
|
|
4019
|
-
function serializeDraftTokens(tokens) {
|
|
4020
|
-
return JSON.stringify(tokens);
|
|
4021
|
-
}
|
|
4022
4009
|
function buildSerializedContent(fm, body) {
|
|
4023
4010
|
const fmBlock = serializeFrontmatter(fm);
|
|
4024
4011
|
if (body.length > 0) {
|