@difflab/pi 0.7.0-rc.202609271854.6af8600 → 0.7.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/agents/diffpi-planner.md +5 -5
- package/dist/cli.js +100 -53
- package/dist/extensions/index.js +86 -90
- package/dist/index.js +87 -90
- package/dist/plan/store.d.ts.map +1 -1
- package/dist/tools/index.js +86 -90
- package/package.json +1 -1
- package/skills/plan/SKILL.md +5 -5
- package/skills/plan/references/design-quality.md +29 -18
- package/skills/plan/references/workflows/new.md +5 -5
- package/skills/plan/references/workflows/update.md +5 -5
- package/templates/plan/PLAN.md +11 -17
- package/templates/plan/implementation.md +12 -28
- package/agents/diffpi-plan-reviewer.md +0 -27
package/agents/diffpi-planner.md
CHANGED
|
@@ -8,22 +8,22 @@ run_in_background: true
|
|
|
8
8
|
model: openai-codex/gpt-5.6-sol
|
|
9
9
|
model_fallbacks: meridian/claude-opus-4-8, meridian/claude-opus-5, deepseek/deepseek-v4-pro, qwen-token-plan/qwen3.7-plus
|
|
10
10
|
thinking: high
|
|
11
|
-
tools: read, grep, find,
|
|
11
|
+
tools: read, grep, find, Agent, get_subagent_result, steer_subagent, ask_user_question, plan_context, plan_init, plan_apply_revision, plan_validate, plan_review
|
|
12
12
|
metadata:
|
|
13
13
|
model-tier: frontier
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
You are the Diffpi planning agent. Execute the plan skill's direct
|
|
16
|
+
You are the Diffpi planning agent. Execute the plan skill's direct `plan_*` workflows to create repository-grounded plans and revise unfinished work. Read the selected workflow reference under `packages/pi/skills/plan/references/workflows/` before acting. Preserve incoming request text exactly when it is revision input. Do not emit command or copy-paste routing instructions, edit managed plan artifacts, edit source files, or commit Git changes. Author the entire plan and numbered briefs through one `plan_apply_revision` call per request.
|
|
17
17
|
|
|
18
18
|
## Plan quality
|
|
19
19
|
|
|
20
|
-
-
|
|
20
|
+
- Call `plan_context` before changing, annotating, finalizing, or executing a plan.
|
|
21
21
|
- Inspect the repository before proposing phases. Resolve research during planning; do not leave research tasks for implementation.
|
|
22
22
|
- Keep stable lowercase phase and task IDs. Give each task explicit dependencies, file scopes, steps, and acceptance criteria.
|
|
23
23
|
- Keep Design at 300 words or fewer when practical and never finalize it above 800 words.
|
|
24
24
|
- Preserve completed work and evidence. Amend only draft, pending, or blocked work.
|
|
25
|
-
-
|
|
26
|
-
-
|
|
25
|
+
- Call `plan_validate` after authoring and use strict validation before marking a plan ready.
|
|
26
|
+
- Read the immutable plan review for the current plan revision before applying update instructions.
|
|
27
27
|
|
|
28
28
|
Use `ask_user_question` for every interactive decision. Never ask a question in plain chat. When running in the background, do not ask questions. Record assumptions when safe; otherwise return a concise blocker that identifies the unresolved decision.
|
|
29
29
|
|
package/dist/cli.js
CHANGED
|
@@ -2384,8 +2384,8 @@ function appendBounded(current, next) {
|
|
|
2384
2384
|
|
|
2385
2385
|
// src/plan/store.ts
|
|
2386
2386
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
2387
|
-
import { mkdir as mkdir3, readdir, readFile as
|
|
2388
|
-
import { basename as basename3, dirname as
|
|
2387
|
+
import { mkdir as mkdir3, readdir, readFile as readFile3, realpath as realpath3, rename as rename2, rm as rm2 } from "node:fs/promises";
|
|
2388
|
+
import { basename as basename3, dirname as dirname4, join as join7, resolve as resolve3, sep } from "node:path";
|
|
2389
2389
|
|
|
2390
2390
|
// src/extensions/fsx.ts
|
|
2391
2391
|
import { mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
@@ -2557,6 +2557,57 @@ function projectSlug(repository) {
|
|
|
2557
2557
|
return `${readable}-${digest}`;
|
|
2558
2558
|
}
|
|
2559
2559
|
|
|
2560
|
+
// src/templates.ts
|
|
2561
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
2562
|
+
import { homedir as homedir2 } from "node:os";
|
|
2563
|
+
import { join as join5, normalize } from "node:path";
|
|
2564
|
+
|
|
2565
|
+
// src/assets.ts
|
|
2566
|
+
import { existsSync } from "node:fs";
|
|
2567
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
2568
|
+
import { fileURLToPath } from "node:url";
|
|
2569
|
+
function resolveBundledAssetDir(name, moduleUrl = import.meta.url) {
|
|
2570
|
+
const moduleDir = dirname3(fileURLToPath(moduleUrl));
|
|
2571
|
+
const candidates = [join4(moduleDir, name), join4(moduleDir, "..", name), join4(moduleDir, "..", "..", name)];
|
|
2572
|
+
return candidates.find((path) => existsSync(path)) ?? candidates[1];
|
|
2573
|
+
}
|
|
2574
|
+
function resolveBundledTemplatesDir(moduleUrl = import.meta.url) {
|
|
2575
|
+
return resolveBundledAssetDir("templates", moduleUrl);
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
// src/templates.ts
|
|
2579
|
+
async function loadTemplate(name, options = {}) {
|
|
2580
|
+
const relative = templateRelativePath(name);
|
|
2581
|
+
const userPath = join5(options.homeDir ?? homedir2(), ".difflab", "diffpi", "templates", relative);
|
|
2582
|
+
const bundledPath = join5(options.bundledDir ?? resolveBundledTemplatesDir(), relative);
|
|
2583
|
+
const user = await readOptionalFile(userPath);
|
|
2584
|
+
if (user !== undefined)
|
|
2585
|
+
return { name, path: userPath, source: "user", content: user };
|
|
2586
|
+
const bundled = await readOptionalFile(bundledPath);
|
|
2587
|
+
if (bundled !== undefined)
|
|
2588
|
+
return { name, path: bundledPath, source: "bundled", content: bundled };
|
|
2589
|
+
throw new Error(`Template "${name}" was not found at ${userPath} or ${bundledPath}.`);
|
|
2590
|
+
}
|
|
2591
|
+
function renderTemplate(content, variables) {
|
|
2592
|
+
return content.replace(/\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g, (match, key) => variables[key] ?? match);
|
|
2593
|
+
}
|
|
2594
|
+
function templateRelativePath(name) {
|
|
2595
|
+
const normalized = normalize(name.replaceAll("\\", "/")).replace(/^\.\//, "");
|
|
2596
|
+
if (!normalized || normalized.startsWith("../") || normalized.includes("/../") || normalized.startsWith("/")) {
|
|
2597
|
+
throw new Error(`Invalid template name: ${name}`);
|
|
2598
|
+
}
|
|
2599
|
+
return normalized.endsWith(".md") ? normalized : `${normalized}.md`;
|
|
2600
|
+
}
|
|
2601
|
+
async function readOptionalFile(path) {
|
|
2602
|
+
try {
|
|
2603
|
+
return await readFile2(path, "utf8");
|
|
2604
|
+
} catch (error) {
|
|
2605
|
+
if (error.code === "ENOENT")
|
|
2606
|
+
return;
|
|
2607
|
+
throw error;
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2560
2611
|
// src/plan/markdown.ts
|
|
2561
2612
|
var import_mdast_builder = __toESM(require_src(), 1);
|
|
2562
2613
|
|
|
@@ -3903,7 +3954,7 @@ var handle = {
|
|
|
3903
3954
|
};
|
|
3904
3955
|
|
|
3905
3956
|
// ../../node_modules/mdast-util-to-markdown/lib/join.js
|
|
3906
|
-
var
|
|
3957
|
+
var join6 = [joinDefaults];
|
|
3907
3958
|
function joinDefaults(left, right, parent, state) {
|
|
3908
3959
|
if (right.type === "code" && formatCodeAsIndented(right, state) && (left.type === "list" || left.type === right.type && formatCodeAsIndented(left, state))) {
|
|
3909
3960
|
return false;
|
|
@@ -6449,7 +6500,7 @@ function toMarkdown(tree, options) {
|
|
|
6449
6500
|
handle: undefined,
|
|
6450
6501
|
indentLines,
|
|
6451
6502
|
indexStack: [],
|
|
6452
|
-
join: [...
|
|
6503
|
+
join: [...join6],
|
|
6453
6504
|
options: {},
|
|
6454
6505
|
safe: safeBound,
|
|
6455
6506
|
stack: [],
|
|
@@ -6753,7 +6804,7 @@ import { default as default2 } from "node:path";
|
|
|
6753
6804
|
import { default as default3 } from "node:process";
|
|
6754
6805
|
|
|
6755
6806
|
// ../../node_modules/vfile/lib/minurl.js
|
|
6756
|
-
import { fileURLToPath } from "node:url";
|
|
6807
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6757
6808
|
|
|
6758
6809
|
// ../../node_modules/vfile/lib/minurl.shared.js
|
|
6759
6810
|
function isUrl(fileUrlOrPath) {
|
|
@@ -6839,7 +6890,7 @@ class VFile {
|
|
|
6839
6890
|
}
|
|
6840
6891
|
set path(path) {
|
|
6841
6892
|
if (isUrl(path)) {
|
|
6842
|
-
path =
|
|
6893
|
+
path = fileURLToPath2(path);
|
|
6843
6894
|
}
|
|
6844
6895
|
assertNonEmpty(path, "path");
|
|
6845
6896
|
if (this.path !== path) {
|
|
@@ -7763,7 +7814,7 @@ async function resolvePlan(cwd, query, filters = {}, options = {}) {
|
|
|
7763
7814
|
continue;
|
|
7764
7815
|
if (normalizedQuery && entry.name !== normalizedQuery && stripDate(entry.name) !== normalizedQuery)
|
|
7765
7816
|
continue;
|
|
7766
|
-
const dir =
|
|
7817
|
+
const dir = join7(root, entry.name);
|
|
7767
7818
|
await assertContained(root, dir);
|
|
7768
7819
|
try {
|
|
7769
7820
|
const record = await readRecord(dir);
|
|
@@ -7798,30 +7849,26 @@ function createPlanStore(options = {}) {
|
|
|
7798
7849
|
async init(input) {
|
|
7799
7850
|
const root = await plansDir(input.cwd, options.homeDir);
|
|
7800
7851
|
const id = planRecordName(input.shortSlug, now());
|
|
7801
|
-
const dir =
|
|
7852
|
+
const dir = join7(root, id);
|
|
7802
7853
|
await createPlanDirectory(root, dir, id);
|
|
7803
7854
|
try {
|
|
7804
7855
|
const timestamp = now().toISOString();
|
|
7856
|
+
const template = await loadTemplate("plan/PLAN", {
|
|
7857
|
+
homeDir: options.homeDir,
|
|
7858
|
+
bundledDir: options.bundledTemplatesDir
|
|
7859
|
+
});
|
|
7805
7860
|
const title = input.title?.trim() || titleFromSlug(input.shortSlug);
|
|
7806
|
-
const
|
|
7807
|
-
schemaVersion: 1,
|
|
7861
|
+
const source = renderTemplate(template.content, {
|
|
7808
7862
|
id,
|
|
7809
|
-
revision: 0,
|
|
7810
|
-
title,
|
|
7811
7863
|
branch: input.branch,
|
|
7812
|
-
|
|
7813
|
-
...input.issueUrl?.trim() ? { issueUrl: input.issueUrl.trim() } : {},
|
|
7864
|
+
title,
|
|
7814
7865
|
intent: input.intent?.trim() || "<!-- Describe the intended outcome. -->",
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
updatedAt: timestamp
|
|
7822
|
-
};
|
|
7823
|
-
const source = renderPlanDocument(document);
|
|
7824
|
-
parsePlanDocument(source, join5(dir, "PLAN.md"));
|
|
7866
|
+
issue_id: input.issueId?.trim() || "",
|
|
7867
|
+
issue_url: input.issueUrl?.trim() || "",
|
|
7868
|
+
created_at: timestamp,
|
|
7869
|
+
updated_at: timestamp
|
|
7870
|
+
});
|
|
7871
|
+
const document = parsePlanDocument(source, join7(dir, "PLAN.md"));
|
|
7825
7872
|
const authoringRequest = input.request ?? { kind: "user", text: input.intent?.trim() || `Initialize phase-less plan ${title}.` };
|
|
7826
7873
|
await writeRevisionSnapshot(dir, document, source, authoringRequest, []);
|
|
7827
7874
|
await writeLatestView(dir, document, source, []);
|
|
@@ -7836,14 +7883,14 @@ function createPlanStore(options = {}) {
|
|
|
7836
7883
|
if (request.mode === "create") {
|
|
7837
7884
|
const root = await plansDir(cwd, options.homeDir);
|
|
7838
7885
|
const id = planRecordName(request.shortSlug, now());
|
|
7839
|
-
const dir =
|
|
7886
|
+
const dir = join7(root, id);
|
|
7840
7887
|
await createPlanDirectory(root, dir, id);
|
|
7841
7888
|
try {
|
|
7842
7889
|
const timestamp = now().toISOString();
|
|
7843
7890
|
const document = normalizeRevision(undefined, request, id, 0, timestamp);
|
|
7844
7891
|
assertCompleteRevision(document, request.briefs);
|
|
7845
7892
|
const source = renderPlanDocument(document);
|
|
7846
|
-
parsePlanDocument(source,
|
|
7893
|
+
parsePlanDocument(source, join7(dir, "PLAN.md"));
|
|
7847
7894
|
await writeRevisionSnapshot(dir, document, source, request.request, request.briefs);
|
|
7848
7895
|
await writeLatestView(dir, document, source, request.briefs);
|
|
7849
7896
|
return await readRecord(dir);
|
|
@@ -7853,7 +7900,7 @@ function createPlanStore(options = {}) {
|
|
|
7853
7900
|
}
|
|
7854
7901
|
}
|
|
7855
7902
|
const initial = await read(cwd, request.plan);
|
|
7856
|
-
return withDirectoryLock(
|
|
7903
|
+
return withDirectoryLock(join7(initial.dir, ".lock"), async () => {
|
|
7857
7904
|
const current = await readRecord(initial.dir);
|
|
7858
7905
|
if (current.document.revision !== request.expectedPlanRevision)
|
|
7859
7906
|
throw new Error(`Expected plan revision ${request.expectedPlanRevision}, found ${current.document.revision}.`);
|
|
@@ -7866,7 +7913,7 @@ function createPlanStore(options = {}) {
|
|
|
7866
7913
|
try {
|
|
7867
7914
|
await writeLatestView(current.dir, document, source, request.briefs);
|
|
7868
7915
|
} catch (error) {
|
|
7869
|
-
await rm2(
|
|
7916
|
+
await rm2(join7(current.dir, "revisions", String(revision)), { recursive: true, force: true });
|
|
7870
7917
|
throw error;
|
|
7871
7918
|
}
|
|
7872
7919
|
return await readRecord(current.dir);
|
|
@@ -7874,7 +7921,7 @@ function createPlanStore(options = {}) {
|
|
|
7874
7921
|
},
|
|
7875
7922
|
async mutate(cwd, query, operation, update) {
|
|
7876
7923
|
const initial = await read(cwd, query);
|
|
7877
|
-
return withDirectoryLock(
|
|
7924
|
+
return withDirectoryLock(join7(initial.dir, ".lock"), async () => {
|
|
7878
7925
|
const current = await readRecord(initial.dir);
|
|
7879
7926
|
const next = await update(structuredClone(current.document));
|
|
7880
7927
|
if (next.id !== current.id)
|
|
@@ -7897,22 +7944,22 @@ function createPlanStore(options = {}) {
|
|
|
7897
7944
|
};
|
|
7898
7945
|
}
|
|
7899
7946
|
async function readRecord(dir) {
|
|
7900
|
-
const planPath =
|
|
7901
|
-
const logPath =
|
|
7902
|
-
const source = await
|
|
7947
|
+
const planPath = join7(dir, "PLAN.md");
|
|
7948
|
+
const logPath = join7(dir, "logs.txt");
|
|
7949
|
+
const source = await readFile3(planPath, "utf8");
|
|
7903
7950
|
const document = parsePlanDocument(source, planPath);
|
|
7904
7951
|
return {
|
|
7905
7952
|
id: basename3(dir),
|
|
7906
7953
|
dir,
|
|
7907
7954
|
planPath,
|
|
7908
7955
|
logPath,
|
|
7909
|
-
implementationDir:
|
|
7956
|
+
implementationDir: join7(dir, "implementation"),
|
|
7910
7957
|
document,
|
|
7911
7958
|
source
|
|
7912
7959
|
};
|
|
7913
7960
|
}
|
|
7914
7961
|
async function createPlanDirectory(root, dir, id) {
|
|
7915
|
-
await assertContained(root,
|
|
7962
|
+
await assertContained(root, dirname4(dir));
|
|
7916
7963
|
try {
|
|
7917
7964
|
await mkdir3(dir);
|
|
7918
7965
|
} catch (error) {
|
|
@@ -7926,17 +7973,17 @@ async function writeRevisionSnapshot(planDir, document, source, request, briefs)
|
|
|
7926
7973
|
throw new Error("An exact non-empty authoring request is required.");
|
|
7927
7974
|
if (request.kind === "annotation" && !request.response?.trim())
|
|
7928
7975
|
throw new Error("Annotation requests require a non-empty response.");
|
|
7929
|
-
const revisionsDir =
|
|
7976
|
+
const revisionsDir = join7(planDir, "revisions");
|
|
7930
7977
|
await mkdir3(revisionsDir, { recursive: true });
|
|
7931
|
-
const target =
|
|
7978
|
+
const target = join7(revisionsDir, String(document.revision));
|
|
7932
7979
|
try {
|
|
7933
|
-
await
|
|
7980
|
+
await readFile3(join7(target, "metadata.json"), "utf8");
|
|
7934
7981
|
throw new Error(`Revision ${document.revision} already exists and is immutable.`);
|
|
7935
7982
|
} catch (error) {
|
|
7936
7983
|
if (error.code !== "ENOENT")
|
|
7937
7984
|
throw error;
|
|
7938
7985
|
}
|
|
7939
|
-
const staging =
|
|
7986
|
+
const staging = join7(revisionsDir, `.tmp-${document.revision}-${randomUUID2()}`);
|
|
7940
7987
|
await mkdir3(staging);
|
|
7941
7988
|
try {
|
|
7942
7989
|
const requestSource = request.kind === "annotation" ? `## Original annotation
|
|
@@ -7944,8 +7991,8 @@ ${request.text}
|
|
|
7944
7991
|
|
|
7945
7992
|
## LLM response
|
|
7946
7993
|
${request.response}` : request.text;
|
|
7947
|
-
await atomicWrite(
|
|
7948
|
-
await atomicWrite(
|
|
7994
|
+
await atomicWrite(join7(staging, "request.md"), requestSource);
|
|
7995
|
+
await atomicWrite(join7(staging, "metadata.json"), `${JSON.stringify({
|
|
7949
7996
|
schemaVersion: 1,
|
|
7950
7997
|
id: document.id,
|
|
7951
7998
|
revision: document.revision,
|
|
@@ -7955,7 +8002,7 @@ ${request.response}` : request.text;
|
|
|
7955
8002
|
createdAt: document.updatedAt
|
|
7956
8003
|
}, null, 2)}
|
|
7957
8004
|
`);
|
|
7958
|
-
await atomicWrite(
|
|
8005
|
+
await atomicWrite(join7(staging, "PLAN.md"), source);
|
|
7959
8006
|
await writeImplementationDirectory(staging, document, briefs);
|
|
7960
8007
|
await rename2(staging, target);
|
|
7961
8008
|
} catch (error) {
|
|
@@ -7964,10 +8011,10 @@ ${request.response}` : request.text;
|
|
|
7964
8011
|
}
|
|
7965
8012
|
}
|
|
7966
8013
|
async function writeLatestView(planDir, document, source, briefs) {
|
|
7967
|
-
const current =
|
|
7968
|
-
const stagingRoot =
|
|
7969
|
-
const stagedImplementation =
|
|
7970
|
-
const backup =
|
|
8014
|
+
const current = join7(planDir, "implementation");
|
|
8015
|
+
const stagingRoot = join7(planDir, `.latest-${randomUUID2()}`);
|
|
8016
|
+
const stagedImplementation = join7(stagingRoot, "implementation");
|
|
8017
|
+
const backup = join7(planDir, `.implementation-backup-${randomUUID2()}`);
|
|
7971
8018
|
await mkdir3(stagingRoot);
|
|
7972
8019
|
await writeImplementationDirectory(stagingRoot, document, briefs);
|
|
7973
8020
|
let backedUp = false;
|
|
@@ -7980,7 +8027,7 @@ async function writeLatestView(planDir, document, source, briefs) {
|
|
|
7980
8027
|
throw error;
|
|
7981
8028
|
}
|
|
7982
8029
|
await rename2(stagedImplementation, current);
|
|
7983
|
-
await atomicWrite(
|
|
8030
|
+
await atomicWrite(join7(planDir, "PLAN.md"), source);
|
|
7984
8031
|
if (backedUp)
|
|
7985
8032
|
await rm2(backup, { recursive: true, force: true });
|
|
7986
8033
|
} catch (error) {
|
|
@@ -7993,13 +8040,13 @@ async function writeLatestView(planDir, document, source, briefs) {
|
|
|
7993
8040
|
}
|
|
7994
8041
|
}
|
|
7995
8042
|
async function writeImplementationDirectory(baseDir, document, briefs) {
|
|
7996
|
-
const implementationDir =
|
|
8043
|
+
const implementationDir = join7(baseDir, "implementation");
|
|
7997
8044
|
await mkdir3(implementationDir, { recursive: true });
|
|
7998
8045
|
for (const [index, phase] of document.phases.entries()) {
|
|
7999
8046
|
const brief = briefs[index];
|
|
8000
8047
|
if (!brief)
|
|
8001
8048
|
throw new Error(`Missing implementation brief for phase ${phase.id}.`);
|
|
8002
|
-
await atomicWrite(
|
|
8049
|
+
await atomicWrite(join7(implementationDir, `phase-${index + 1}.md`), renderImplementationBrief(document.revision, index + 1, phase, brief));
|
|
8003
8050
|
}
|
|
8004
8051
|
}
|
|
8005
8052
|
function normalizeRevision(current, request, id, revision, updatedAt) {
|
|
@@ -8151,8 +8198,8 @@ function titleFromSlug(value) {
|
|
|
8151
8198
|
});
|
|
8152
8199
|
}
|
|
8153
8200
|
// src/plan/reviews.ts
|
|
8154
|
-
import { mkdir as mkdir4, readFile as
|
|
8155
|
-
import { dirname as
|
|
8201
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
|
|
8202
|
+
import { dirname as dirname5, join as join8 } from "node:path";
|
|
8156
8203
|
async function createPlanReview(record, runtime = {}) {
|
|
8157
8204
|
if (await readPlanReview(record))
|
|
8158
8205
|
throw new Error(`Plan revision ${record.document.revision} already has a review.`);
|
|
@@ -8185,7 +8232,7 @@ async function readPlanReview(record) {
|
|
|
8185
8232
|
const path = reviewPath(record);
|
|
8186
8233
|
let source;
|
|
8187
8234
|
try {
|
|
8188
|
-
source = await
|
|
8235
|
+
source = await readFile4(path, "utf8");
|
|
8189
8236
|
} catch (error) {
|
|
8190
8237
|
if (error.code === "ENOENT")
|
|
8191
8238
|
return;
|
|
@@ -8202,10 +8249,10 @@ async function readPlanReview(record) {
|
|
|
8202
8249
|
return { path, dump };
|
|
8203
8250
|
}
|
|
8204
8251
|
function reviewPath(record) {
|
|
8205
|
-
return
|
|
8252
|
+
return join8(record.dir, "reviews", `${record.document.revision}.json`);
|
|
8206
8253
|
}
|
|
8207
8254
|
async function writeReview(review) {
|
|
8208
|
-
await mkdir4(
|
|
8255
|
+
await mkdir4(dirname5(review.path), { recursive: true });
|
|
8209
8256
|
await writeFile2(review.path, `${JSON.stringify(review.dump, null, 2)}
|
|
8210
8257
|
`, {
|
|
8211
8258
|
encoding: "utf8",
|