@jskit-ai/jskit-cli 0.2.88 → 0.2.89
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/jskit-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.89",
|
|
4
4
|
"description": "Bundle and package orchestration CLI for JSKIT apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"test": "node --test"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@jskit-ai/jskit-catalog": "0.1.
|
|
24
|
-
"@jskit-ai/kernel": "0.1.
|
|
25
|
-
"@jskit-ai/shell-web": "0.1.
|
|
23
|
+
"@jskit-ai/jskit-catalog": "0.1.88",
|
|
24
|
+
"@jskit-ai/kernel": "0.1.80",
|
|
25
|
+
"@jskit-ai/shell-web": "0.1.79",
|
|
26
26
|
"@vue/compiler-sfc": "^3.5.29",
|
|
27
27
|
"ts-morph": "^28.0.0"
|
|
28
28
|
},
|
|
@@ -477,28 +477,28 @@ const STEP_DEFINITIONS = Object.freeze([
|
|
|
477
477
|
})
|
|
478
478
|
])
|
|
479
479
|
}),
|
|
480
|
-
defineStep({
|
|
481
|
-
buttonLabel: "Commit accepted changes",
|
|
482
|
-
description: "JSKIT commits the user-accepted session changes in the session worktree.",
|
|
483
|
-
id: "changes_committed",
|
|
484
|
-
label: "Changes committed",
|
|
485
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "issue_url_exists", "github_auth", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed"]
|
|
486
|
-
}),
|
|
487
480
|
defineStep({
|
|
488
481
|
buttonLabel: "Update blueprint",
|
|
489
482
|
codex: BLUEPRINT_CODEX_HANDOFF,
|
|
490
|
-
description: "JSKIT asks Codex to update durable app memory from the accepted work; Codex edits .jskit/APP_BLUEPRINT.md; JSKIT records
|
|
483
|
+
description: "JSKIT asks Codex to update durable app memory from the accepted work; Codex edits .jskit/APP_BLUEPRINT.md; JSKIT records the update for the accepted-work commit.",
|
|
491
484
|
id: "blueprint_updated",
|
|
492
485
|
kind: "codex_prompt",
|
|
493
486
|
label: "Blueprint updated",
|
|
494
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed"
|
|
487
|
+
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed"]
|
|
488
|
+
}),
|
|
489
|
+
defineStep({
|
|
490
|
+
buttonLabel: "Commit accepted changes",
|
|
491
|
+
description: "JSKIT commits the accepted session changes, including durable app memory updates, in the session worktree.",
|
|
492
|
+
id: "changes_committed",
|
|
493
|
+
label: "Changes committed",
|
|
494
|
+
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "issue_url_exists", "github_auth", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed", "blueprint_update_satisfied"]
|
|
495
495
|
}),
|
|
496
496
|
defineStep({
|
|
497
497
|
buttonLabel: "Create final report",
|
|
498
498
|
description: "JSKIT creates the deterministic final session report and comments it on the GitHub issue.",
|
|
499
499
|
id: "final_report_created",
|
|
500
500
|
label: "Final report created",
|
|
501
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed", "
|
|
501
|
+
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed", "blueprint_update_satisfied", "accepted_changes_committed"]
|
|
502
502
|
}),
|
|
503
503
|
defineStep({
|
|
504
504
|
buttonLabel: "Push branch and create PR",
|
|
@@ -379,7 +379,7 @@ async function assertAcceptedChangesCommitted(paths) {
|
|
|
379
379
|
ok: false,
|
|
380
380
|
error: createError({
|
|
381
381
|
code: "accepted_changes_not_committed",
|
|
382
|
-
message: "Accepted changes must be committed before
|
|
382
|
+
message: "Accepted changes must be committed before finalization steps continue.",
|
|
383
383
|
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
384
384
|
}),
|
|
385
385
|
precondition: createPrecondition({
|
|
@@ -56,6 +56,19 @@ function createPrecondition({
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function createWarning({
|
|
60
|
+
code,
|
|
61
|
+
message,
|
|
62
|
+
repairCommand = ""
|
|
63
|
+
}) {
|
|
64
|
+
return createError({ code, message, repairCommand });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const ACCEPTED_CHANGES_NOOP_WARNING = createWarning({
|
|
68
|
+
code: "accepted_changes_noop",
|
|
69
|
+
message: "No accepted worktree changes were found; continuing without a new commit."
|
|
70
|
+
});
|
|
71
|
+
|
|
59
72
|
function normalizeStepId(stepId) {
|
|
60
73
|
return normalizeText(stepId);
|
|
61
74
|
}
|
|
@@ -530,6 +543,43 @@ function cloneContractValue(value) {
|
|
|
530
543
|
);
|
|
531
544
|
}
|
|
532
545
|
|
|
546
|
+
function normalizeWarning(warning) {
|
|
547
|
+
if (typeof warning === "string") {
|
|
548
|
+
return createWarning({
|
|
549
|
+
code: "session_warning",
|
|
550
|
+
message: warning
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
if (!warning || typeof warning !== "object" || Array.isArray(warning)) {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
return createWarning({
|
|
557
|
+
code: warning.code || "session_warning",
|
|
558
|
+
message: warning.message || "",
|
|
559
|
+
repairCommand: warning.repairCommand || ""
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function mergeWarnings(...warningLists) {
|
|
564
|
+
const merged = [];
|
|
565
|
+
const seen = new Set();
|
|
566
|
+
for (const warnings of warningLists) {
|
|
567
|
+
for (const warning of Array.isArray(warnings) ? warnings : []) {
|
|
568
|
+
const normalized = normalizeWarning(warning);
|
|
569
|
+
if (!normalized?.message) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const key = `${normalized.code}\n${normalized.message}`;
|
|
573
|
+
if (seen.has(key)) {
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
seen.add(key);
|
|
577
|
+
merged.push(normalized);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return merged;
|
|
581
|
+
}
|
|
582
|
+
|
|
533
583
|
async function publicCodexContract(codex = null) {
|
|
534
584
|
if (!codex || typeof codex !== "object" || Array.isArray(codex)) {
|
|
535
585
|
return null;
|
|
@@ -814,7 +864,8 @@ async function readSessionArtifacts(paths) {
|
|
|
814
864
|
issueMetadataText,
|
|
815
865
|
planExecutionReceipt,
|
|
816
866
|
prOutcomeText,
|
|
817
|
-
mainCheckoutSyncText
|
|
867
|
+
mainCheckoutSyncText,
|
|
868
|
+
changesCommittedText
|
|
818
869
|
] = await Promise.all([
|
|
819
870
|
readTrimmedFile(path.join(paths.sessionRoot, "status")),
|
|
820
871
|
readTrimmedFile(path.join(paths.sessionRoot, "current_step")),
|
|
@@ -834,7 +885,8 @@ async function readSessionArtifacts(paths) {
|
|
|
834
885
|
readTextIfExists(path.join(paths.sessionRoot, "issue_metadata.json")),
|
|
835
886
|
readTextIfExists(path.join(cycleStepsRoot(paths, activeCycle), "plan_executed")),
|
|
836
887
|
readTextIfExists(path.join(paths.sessionRoot, "pr_outcome.json")),
|
|
837
|
-
readTextIfExists(path.join(paths.sessionRoot, "main_checkout_sync.json"))
|
|
888
|
+
readTextIfExists(path.join(paths.sessionRoot, "main_checkout_sync.json")),
|
|
889
|
+
readTextIfExists(path.join(paths.sessionRoot, "changes_committed.json"))
|
|
838
890
|
]);
|
|
839
891
|
let issueMetadata = null;
|
|
840
892
|
if (issueMetadataText) {
|
|
@@ -871,6 +923,18 @@ async function readSessionArtifacts(paths) {
|
|
|
871
923
|
mainCheckoutSync = null;
|
|
872
924
|
}
|
|
873
925
|
}
|
|
926
|
+
let acceptedChangesCommit = null;
|
|
927
|
+
if (changesCommittedText) {
|
|
928
|
+
try {
|
|
929
|
+
const parsed = JSON.parse(changesCommittedText);
|
|
930
|
+
acceptedChangesCommit = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
931
|
+
} catch {
|
|
932
|
+
acceptedChangesCommit = null;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
const warnings = acceptedChangesCommit?.noChanges === true
|
|
936
|
+
? [ACCEPTED_CHANGES_NOOP_WARNING]
|
|
937
|
+
: [];
|
|
874
938
|
const cycles = await readCycles(paths, activeCycle);
|
|
875
939
|
const checks = await readStructuredChecks(paths);
|
|
876
940
|
const uiChecks = await readStructuredUiChecks(paths);
|
|
@@ -960,6 +1024,7 @@ async function readSessionArtifacts(paths) {
|
|
|
960
1024
|
finalReportText: finalReportText.trim(),
|
|
961
1025
|
prompt: prompt.trim(),
|
|
962
1026
|
status: status || SESSION_STATUS.PENDING,
|
|
1027
|
+
warnings,
|
|
963
1028
|
workflowVersion,
|
|
964
1029
|
worktreeReady,
|
|
965
1030
|
worktreeStatus
|
|
@@ -980,7 +1045,8 @@ async function buildSessionResponse(paths, {
|
|
|
980
1045
|
errors = [],
|
|
981
1046
|
preconditions = [],
|
|
982
1047
|
prompt = undefined,
|
|
983
|
-
status = undefined
|
|
1048
|
+
status = undefined,
|
|
1049
|
+
warnings = []
|
|
984
1050
|
} = {}) {
|
|
985
1051
|
const responsePaths = paths.sessionId ? await pathsForExistingSession(paths) : paths;
|
|
986
1052
|
const artifacts = responsePaths.sessionRoot ? await readSessionArtifacts(responsePaths) : {};
|
|
@@ -1039,6 +1105,7 @@ async function buildSessionResponse(paths, {
|
|
|
1039
1105
|
message: `Session ${paths.sessionId || ""} uses workflow version ${artifacts.workflowVersion || "missing"}, but this JSKIT runtime expects ${SESSION_WORKFLOW_VERSION}.`
|
|
1040
1106
|
})
|
|
1041
1107
|
],
|
|
1108
|
+
warnings: [],
|
|
1042
1109
|
archive: responsePaths.archive || "active",
|
|
1043
1110
|
sessionRoot: responsePaths.sessionRoot || "",
|
|
1044
1111
|
worktree: paths.worktree || "",
|
|
@@ -1052,6 +1119,7 @@ async function buildSessionResponse(paths, {
|
|
|
1052
1119
|
const responsePrompt = typeof prompt === "string"
|
|
1053
1120
|
? prompt
|
|
1054
1121
|
: stepCanExposeStoredPrompt(currentStep) ? artifacts.prompt || "" : "";
|
|
1122
|
+
const responseWarnings = mergeWarnings(artifacts.warnings || [], warnings);
|
|
1055
1123
|
|
|
1056
1124
|
return {
|
|
1057
1125
|
ok: ok === true,
|
|
@@ -1101,6 +1169,7 @@ async function buildSessionResponse(paths, {
|
|
|
1101
1169
|
mainCheckoutSync: cloneContractValue(artifacts.mainCheckoutSync || null),
|
|
1102
1170
|
preconditions,
|
|
1103
1171
|
errors,
|
|
1172
|
+
warnings: responseWarnings,
|
|
1104
1173
|
archive: responsePaths.archive || (resolvedStatus === SESSION_STATUS.FINISHED ? "completed" : resolvedStatus === SESSION_STATUS.ABANDONED ? "abandoned" : "active"),
|
|
1105
1174
|
sessionRoot: responsePaths.sessionRoot || "",
|
|
1106
1175
|
worktree: paths.worktree || "",
|
|
@@ -1179,6 +1248,7 @@ function buildSessionErrorResponse({
|
|
|
1179
1248
|
prOutcome: null,
|
|
1180
1249
|
preconditions,
|
|
1181
1250
|
errors: errorList,
|
|
1251
|
+
warnings: [],
|
|
1182
1252
|
archive: "",
|
|
1183
1253
|
sessionRoot: "",
|
|
1184
1254
|
worktree: "",
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createHash
|
|
3
|
+
} from "node:crypto";
|
|
1
4
|
import {
|
|
2
5
|
appendFile,
|
|
3
6
|
mkdir,
|
|
@@ -1581,6 +1584,7 @@ const STEP_CANCELERS = Object.freeze({
|
|
|
1581
1584
|
blueprint_updated: async (paths) => {
|
|
1582
1585
|
await Promise.all([
|
|
1583
1586
|
removePromptArtifact(paths, "update_blueprint.md"),
|
|
1587
|
+
removeSessionRootFile(paths, BLUEPRINT_BASELINE_FILE),
|
|
1584
1588
|
removeGlobalCodexResult(paths, "blueprint_updated")
|
|
1585
1589
|
]);
|
|
1586
1590
|
},
|
|
@@ -1830,6 +1834,79 @@ async function changedFilesInWorktree(paths) {
|
|
|
1830
1834
|
]);
|
|
1831
1835
|
}
|
|
1832
1836
|
|
|
1837
|
+
const BLUEPRINT_RELATIVE_PATH = ".jskit/APP_BLUEPRINT.md";
|
|
1838
|
+
const BLUEPRINT_BASELINE_FILE = "blueprint_update_baseline.json";
|
|
1839
|
+
|
|
1840
|
+
function blueprintBaselinePath(paths) {
|
|
1841
|
+
return path.join(paths.sessionRoot, BLUEPRINT_BASELINE_FILE);
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
function isBlueprintRelativePath(filePath = "") {
|
|
1845
|
+
return normalizeText(filePath) === BLUEPRINT_RELATIVE_PATH;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
function nonBlueprintChangedFiles(files = []) {
|
|
1849
|
+
return files.filter((file) => !isBlueprintRelativePath(file));
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
async function hashWorktreeFile(paths, filePath) {
|
|
1853
|
+
try {
|
|
1854
|
+
const buffer = await readFile(path.join(paths.worktree, filePath));
|
|
1855
|
+
return createHash("sha256").update(buffer).digest("hex");
|
|
1856
|
+
} catch {
|
|
1857
|
+
return "missing";
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
async function buildDirtyFileSnapshot(paths, files = []) {
|
|
1862
|
+
const entries = await Promise.all(nonBlueprintChangedFiles(files).map(async (file) => [
|
|
1863
|
+
file,
|
|
1864
|
+
await hashWorktreeFile(paths, file)
|
|
1865
|
+
]));
|
|
1866
|
+
return Object.fromEntries(entries);
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
async function writeBlueprintBaseline(paths) {
|
|
1870
|
+
const changedFiles = await changedFilesInWorktree(paths);
|
|
1871
|
+
const snapshot = await buildDirtyFileSnapshot(paths, changedFiles);
|
|
1872
|
+
const payload = {
|
|
1873
|
+
changedFiles: Object.keys(snapshot).sort((left, right) => left.localeCompare(right)),
|
|
1874
|
+
files: snapshot,
|
|
1875
|
+
recordedAt: timestampForReceipt()
|
|
1876
|
+
};
|
|
1877
|
+
await writeTextFile(blueprintBaselinePath(paths), `${JSON.stringify(payload, null, 2)}\n`);
|
|
1878
|
+
return payload;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
async function readBlueprintBaseline(paths) {
|
|
1882
|
+
return parseJsonObject(await readTextIfExists(blueprintBaselinePath(paths))) || null;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
async function unexpectedBlueprintStepChanges(paths, changedFiles = []) {
|
|
1886
|
+
const baseline = await readBlueprintBaseline(paths);
|
|
1887
|
+
if (!baseline?.files || typeof baseline.files !== "object" || Array.isArray(baseline.files)) {
|
|
1888
|
+
return nonBlueprintChangedFiles(changedFiles);
|
|
1889
|
+
}
|
|
1890
|
+
const baselineFiles = baseline.files;
|
|
1891
|
+
const currentFiles = new Set(nonBlueprintChangedFiles(changedFiles));
|
|
1892
|
+
const candidates = new Set([
|
|
1893
|
+
...Object.keys(baselineFiles),
|
|
1894
|
+
...currentFiles
|
|
1895
|
+
]);
|
|
1896
|
+
const unexpected = [];
|
|
1897
|
+
for (const file of [...candidates].sort((left, right) => left.localeCompare(right))) {
|
|
1898
|
+
if (!Object.prototype.hasOwnProperty.call(baselineFiles, file)) {
|
|
1899
|
+
unexpected.push(file);
|
|
1900
|
+
continue;
|
|
1901
|
+
}
|
|
1902
|
+
const currentHash = await hashWorktreeFile(paths, file);
|
|
1903
|
+
if (currentHash !== baselineFiles[file]) {
|
|
1904
|
+
unexpected.push(file);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
return unexpected;
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1833
1910
|
async function changedFilesSinceBase(paths) {
|
|
1834
1911
|
const baseCommit = await readTrimmedFile(path.join(paths.sessionRoot, "base_commit"));
|
|
1835
1912
|
const args = baseCommit
|
|
@@ -2178,17 +2255,10 @@ async function commitAcceptedChanges(paths, _options = {}, context = {}) {
|
|
|
2178
2255
|
|
|
2179
2256
|
if (!commitInfo?.commit) {
|
|
2180
2257
|
const result = await commitWorktree(paths, {
|
|
2258
|
+
allowNoChanges: true,
|
|
2181
2259
|
message: `Implement JSKIT session ${paths.sessionId}`
|
|
2182
2260
|
});
|
|
2183
2261
|
if (!result.ok) {
|
|
2184
|
-
if (result.output === "No changes found.") {
|
|
2185
|
-
return failSession(paths, {
|
|
2186
|
-
code: "accepted_changes_missing",
|
|
2187
|
-
message: "No accepted worktree changes found to commit.",
|
|
2188
|
-
repairCommand: `git -C ${paths.worktree} status --short`,
|
|
2189
|
-
preconditions
|
|
2190
|
-
});
|
|
2191
|
-
}
|
|
2192
2262
|
return failSession(paths, {
|
|
2193
2263
|
code: "accepted_changes_commit_failed",
|
|
2194
2264
|
message: result.output || "Failed to commit accepted changes.",
|
|
@@ -2199,15 +2269,30 @@ async function commitAcceptedChanges(paths, _options = {}, context = {}) {
|
|
|
2199
2269
|
commitInfo = {
|
|
2200
2270
|
changedFiles: result.changedFiles || [],
|
|
2201
2271
|
commit: await currentHead(paths),
|
|
2202
|
-
committedAt: timestampForReceipt()
|
|
2272
|
+
committedAt: timestampForReceipt(),
|
|
2273
|
+
noChanges: (result.changedFiles || []).length < 1
|
|
2203
2274
|
};
|
|
2204
2275
|
await writeTextFile(path.join(paths.sessionRoot, "changes_committed.json"), `${JSON.stringify(commitInfo, null, 2)}\n`);
|
|
2205
2276
|
}
|
|
2206
2277
|
|
|
2207
|
-
|
|
2278
|
+
const warnings = [];
|
|
2279
|
+
if (commitInfo.noChanges === true) {
|
|
2280
|
+
warnings.push({
|
|
2281
|
+
code: "accepted_changes_noop",
|
|
2282
|
+
message: "No accepted worktree changes were found; continuing without a new commit."
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
await writeReceipt(
|
|
2286
|
+
paths,
|
|
2287
|
+
"changes_committed",
|
|
2288
|
+
commitInfo.noChanges === true
|
|
2289
|
+
? "No accepted worktree changes were found; continued without a new commit."
|
|
2290
|
+
: `Committed accepted changes at ${commitInfo.commit || "unknown"}.`
|
|
2291
|
+
);
|
|
2208
2292
|
await markStatus(paths, SESSION_STATUS.RUNNING);
|
|
2209
2293
|
return buildSessionResponse(paths, {
|
|
2210
|
-
preconditions
|
|
2294
|
+
preconditions,
|
|
2295
|
+
warnings
|
|
2211
2296
|
});
|
|
2212
2297
|
}
|
|
2213
2298
|
|
|
@@ -2220,7 +2305,7 @@ async function updateBlueprint(paths, options = {}, context = {}) {
|
|
|
2220
2305
|
const { planPath } = await readCurrentPlan(paths);
|
|
2221
2306
|
const issueDetailsPath = path.join(paths.sessionRoot, "issue_details.md");
|
|
2222
2307
|
const agentDecisionsPath = path.join(paths.sessionRoot, "agent_decisions.md");
|
|
2223
|
-
const blueprintPath = path.join(paths.worktree,
|
|
2308
|
+
const blueprintPath = path.join(paths.worktree, BLUEPRINT_RELATIVE_PATH);
|
|
2224
2309
|
const blueprintPromptPath = path.join(paths.sessionRoot, "prompts", "update_blueprint.md");
|
|
2225
2310
|
|
|
2226
2311
|
if (await fileExists(blueprintPromptPath)) {
|
|
@@ -2229,11 +2314,11 @@ async function updateBlueprint(paths, options = {}, context = {}) {
|
|
|
2229
2314
|
return codexResultFailure;
|
|
2230
2315
|
}
|
|
2231
2316
|
const changedFiles = await changedFilesInWorktree(paths);
|
|
2232
|
-
const unexpectedChanges =
|
|
2317
|
+
const unexpectedChanges = await unexpectedBlueprintStepChanges(paths, changedFiles);
|
|
2233
2318
|
if (unexpectedChanges.length > 0) {
|
|
2234
2319
|
return failSession(paths, {
|
|
2235
2320
|
code: "blueprint_unexpected_changes",
|
|
2236
|
-
message: `The blueprint step changed files outside
|
|
2321
|
+
message: `The blueprint step changed files outside ${BLUEPRINT_RELATIVE_PATH}: ${unexpectedChanges.join(", ")}`,
|
|
2237
2322
|
repairCommand: `git -C ${paths.worktree} status --short`,
|
|
2238
2323
|
preconditions
|
|
2239
2324
|
});
|
|
@@ -2249,30 +2334,8 @@ async function updateBlueprint(paths, options = {}, context = {}) {
|
|
|
2249
2334
|
});
|
|
2250
2335
|
}
|
|
2251
2336
|
|
|
2252
|
-
if (changedFiles.includes(
|
|
2253
|
-
|
|
2254
|
-
timeout: 15000
|
|
2255
|
-
});
|
|
2256
|
-
if (!addResult.ok) {
|
|
2257
|
-
return failSession(paths, {
|
|
2258
|
-
code: "blueprint_stage_failed",
|
|
2259
|
-
message: addResult.output || "Failed to stage app blueprint update.",
|
|
2260
|
-
repairCommand: `git -C ${paths.worktree} add .jskit/APP_BLUEPRINT.md`,
|
|
2261
|
-
preconditions
|
|
2262
|
-
});
|
|
2263
|
-
}
|
|
2264
|
-
const commitResult = await runGitInWorktree(paths.worktree, ["commit", "-m", `Update app blueprint for ${paths.sessionId}`], {
|
|
2265
|
-
timeout: 1000 * 60
|
|
2266
|
-
});
|
|
2267
|
-
if (!commitResult.ok) {
|
|
2268
|
-
return failSession(paths, {
|
|
2269
|
-
code: "blueprint_commit_failed",
|
|
2270
|
-
message: commitResult.output || "Failed to commit app blueprint update.",
|
|
2271
|
-
repairCommand: `git -C ${paths.worktree} status --short`,
|
|
2272
|
-
preconditions
|
|
2273
|
-
});
|
|
2274
|
-
}
|
|
2275
|
-
await writeReceipt(paths, "blueprint_updated", "Codex updated and JSKIT committed the app blueprint.");
|
|
2337
|
+
if (changedFiles.includes(BLUEPRINT_RELATIVE_PATH)) {
|
|
2338
|
+
await writeReceipt(paths, "blueprint_updated", "Codex updated the app blueprint; JSKIT will include it in the accepted changes commit.");
|
|
2276
2339
|
} else {
|
|
2277
2340
|
await writeReceipt(paths, "blueprint_updated", "Codex reviewed the app blueprint; no blueprint changes were needed.");
|
|
2278
2341
|
}
|
|
@@ -2283,6 +2346,7 @@ async function updateBlueprint(paths, options = {}, context = {}) {
|
|
|
2283
2346
|
});
|
|
2284
2347
|
}
|
|
2285
2348
|
|
|
2349
|
+
await writeBlueprintBaseline(paths);
|
|
2286
2350
|
const prompt = await renderPrompt(paths, "update_blueprint.md", {
|
|
2287
2351
|
agent_decisions_file: agentDecisionsPath,
|
|
2288
2352
|
app_blueprint_file: blueprintPath,
|