bearings 0.4.0 → 0.5.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/dist/{chunk-KQPOIH7N.js → chunk-5D5YO3E3.js} +135 -42
- package/dist/cli.js +49 -13
- package/dist/{update-TBMTUJUT.js → update-QTWNGAGX.js} +212 -25
- package/package.json +1 -1
- package/templates/agents/commands/setup-repo.md +72 -21
- package/templates/agents/skills/checklist/SKILL.md +69 -0
- package/templates/agents/skills/checklist/previewer/index.html +38 -0
- package/templates/agents/skills/checklist/previewer/viewer.css +93 -0
- package/templates/agents/skills/checklist/previewer/viewer.js +340 -0
- package/templates/agents/skills/checklist/schema.reference.json +90 -0
- package/templates/agents/skills/checklist/scripts/build.mjs +98 -0
- package/templates/agents/skills/checklist/scripts/validate.mjs +175 -0
- package/templates/agents/skills/forge-a-skill/SKILL.md +162 -0
- package/templates/agents/skills/forge-a-skill/templates/guardrail.md +45 -0
- package/templates/agents/skills/forge-a-skill/templates/probe-set.json +45 -0
- package/templates/agents/skills/manual-testplan/SKILL.md +68 -38
- package/templates/agents/skills/manual-testplan/schema.reference.json +0 -67
- package/templates/agents/skills/manual-testplan/scripts/validate.mjs +0 -131
- package/templates/agents/skills/manual-testplan/scripts/view.mjs +0 -132
- package/templates/agents/skills/manual-testplan/viewer.html +0 -313
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
SCAFFOLD,
|
|
5
5
|
canonicalAdapterEntries,
|
|
6
6
|
entriesMatch,
|
|
7
|
+
freeBackupPath,
|
|
7
8
|
isStarterSkillTarget,
|
|
8
9
|
migrateV1,
|
|
9
10
|
renderUpdatePlan,
|
|
@@ -13,8 +14,9 @@ import {
|
|
|
13
14
|
skillIncomingPath,
|
|
14
15
|
starterSkillNameFromTarget,
|
|
15
16
|
templatesDir,
|
|
16
|
-
validateHarnesses
|
|
17
|
-
|
|
17
|
+
validateHarnesses,
|
|
18
|
+
writeBackup
|
|
19
|
+
} from "./chunk-5D5YO3E3.js";
|
|
18
20
|
|
|
19
21
|
// src/semver.ts
|
|
20
22
|
function parseSemver(value) {
|
|
@@ -75,11 +77,6 @@ async function exists(path) {
|
|
|
75
77
|
return false;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
|
-
async function freeBackupPath(repoDir, target) {
|
|
79
|
-
let candidate = `${target}.bkp`;
|
|
80
|
-
for (let i = 1; await exists(join(repoDir, candidate)); i++) candidate = `${target}.bkp.${i}`;
|
|
81
|
-
return candidate;
|
|
82
|
-
}
|
|
83
80
|
function lineDiffStats(current, incoming) {
|
|
84
81
|
const left = current.split("\n");
|
|
85
82
|
const right = incoming.split("\n");
|
|
@@ -562,12 +559,12 @@ async function planAdapterActions(input) {
|
|
|
562
559
|
for (const action of fileActions) {
|
|
563
560
|
const root = parseAdapterRoot(actionPath(action));
|
|
564
561
|
if (!root) continue;
|
|
565
|
-
if (action.kind === "delete") {
|
|
562
|
+
if (action.kind === "delete" || action.kind === "untrack-missing") {
|
|
566
563
|
if (!desiredRootState.has(root)) desiredRootState.set(root, false);
|
|
567
564
|
} else {
|
|
568
565
|
desiredRootState.set(root, true);
|
|
569
566
|
}
|
|
570
|
-
if (action.kind === "write" || action.kind === "merge" || action.kind === "delete") {
|
|
567
|
+
if (action.kind === "write" || action.kind === "merge" || action.kind === "delete" || action.kind === "untrack-missing") {
|
|
571
568
|
affectedRootKeys.add(root);
|
|
572
569
|
}
|
|
573
570
|
}
|
|
@@ -638,12 +635,12 @@ async function capture(repoDir, txDir, relativePath, snapshots) {
|
|
|
638
635
|
await rename(target, stored);
|
|
639
636
|
snapshots.push({ target, stored });
|
|
640
637
|
}
|
|
641
|
-
async function captureToBackup(repoDir, relativePath, backupRelativePath, snapshots) {
|
|
642
|
-
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
await
|
|
646
|
-
snapshots.push({ target,
|
|
638
|
+
async function captureToBackup(repoDir, txDir, relativePath, backupRelativePath, snapshots) {
|
|
639
|
+
await capture(repoDir, txDir, relativePath, snapshots);
|
|
640
|
+
const sourceSnapshot = snapshots.at(-1);
|
|
641
|
+
if (!sourceSnapshot?.stored) throw new Error(`Cannot back up missing path: ${relativePath}`);
|
|
642
|
+
await writeBackup(repoDir, relativePath, await readFile3(sourceSnapshot.stored), { path: backupRelativePath });
|
|
643
|
+
snapshots.push({ target: join3(repoDir, backupRelativePath) });
|
|
647
644
|
}
|
|
648
645
|
async function ensureDir(repoDir, absDir, createdDirs) {
|
|
649
646
|
const rel = relative(repoDir, absDir);
|
|
@@ -664,10 +661,6 @@ async function writeContent(repoDir, txDir, relativePath, content, snapshots, cr
|
|
|
664
661
|
await ensureDir(repoDir, dirname(target), createdDirs);
|
|
665
662
|
await writeFile(target, content);
|
|
666
663
|
}
|
|
667
|
-
async function writeCopy(repoDir, txDir, fromRelativePath, toRelativePath, snapshots, createdDirs) {
|
|
668
|
-
const content = await readFile3(join3(repoDir, fromRelativePath));
|
|
669
|
-
await writeContent(repoDir, txDir, toRelativePath, content, snapshots, createdDirs);
|
|
670
|
-
}
|
|
671
664
|
async function restore(snapshots, hooks, txDir) {
|
|
672
665
|
for (const snapshot of [...snapshots].reverse()) {
|
|
673
666
|
try {
|
|
@@ -693,6 +686,25 @@ async function removeEmptyDirs(createdDirs) {
|
|
|
693
686
|
}
|
|
694
687
|
}
|
|
695
688
|
}
|
|
689
|
+
async function removeEmptyCanonicalParents(repoDir, paths) {
|
|
690
|
+
const skillsRoot = join3(repoDir, ".agents", "skills");
|
|
691
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
692
|
+
for (const path of paths) {
|
|
693
|
+
let current = dirname(join3(repoDir, path));
|
|
694
|
+
while (current.startsWith(`${skillsRoot}${sep}`)) {
|
|
695
|
+
candidates.add(current);
|
|
696
|
+
current = dirname(current);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
for (const dir of [...candidates].sort((a, b) => b.length - a.length)) {
|
|
700
|
+
try {
|
|
701
|
+
await rmdir(dir);
|
|
702
|
+
} catch (error) {
|
|
703
|
+
const code = error.code;
|
|
704
|
+
if (code !== "ENOENT" && code !== "ENOTEMPTY") throw error;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
696
708
|
async function applyUpdate(repoDir, plan, hooks = {}) {
|
|
697
709
|
const txDir = join3(repoDir, ".agents", `.bearings-txn-${randomUUID()}`);
|
|
698
710
|
const snapshots = [];
|
|
@@ -720,11 +732,12 @@ async function applyUpdate(repoDir, plan, hooks = {}) {
|
|
|
720
732
|
}
|
|
721
733
|
await afterOp();
|
|
722
734
|
} else if (action.kind === "merge") {
|
|
723
|
-
await captureToBackup(repoDir, action.path, action.backup, snapshots);
|
|
735
|
+
await captureToBackup(repoDir, txDir, action.path, action.backup, snapshots);
|
|
724
736
|
await writeFile(abs(action.path), action.content);
|
|
725
737
|
await afterOp();
|
|
726
738
|
} else if (action.kind === "skill-handoff") {
|
|
727
|
-
await
|
|
739
|
+
await writeBackup(repoDir, action.path, await readFile3(abs(action.path)), { path: action.backup });
|
|
740
|
+
snapshots.push({ target: abs(action.backup) });
|
|
728
741
|
await writeContent(repoDir, txDir, action.incomingPath, action.content, snapshots, createdDirs);
|
|
729
742
|
await afterOp();
|
|
730
743
|
} else if (action.kind === "delete") {
|
|
@@ -732,6 +745,27 @@ async function applyUpdate(repoDir, plan, hooks = {}) {
|
|
|
732
745
|
await afterOp();
|
|
733
746
|
}
|
|
734
747
|
}
|
|
748
|
+
await removeEmptyCanonicalParents(
|
|
749
|
+
repoDir,
|
|
750
|
+
plan.files.filter((action) => action.kind === "delete").map((action) => action.path)
|
|
751
|
+
);
|
|
752
|
+
for (const action of plan.migrations ?? []) {
|
|
753
|
+
if (action.kind === "migrate-testplan") {
|
|
754
|
+
if (action.backup) await captureToBackup(repoDir, txDir, action.target, action.backup, snapshots);
|
|
755
|
+
else await capture(repoDir, txDir, action.target, snapshots);
|
|
756
|
+
await capture(repoDir, txDir, action.source, snapshots);
|
|
757
|
+
await ensureDir(repoDir, dirname(abs(action.target)), createdDirs);
|
|
758
|
+
await writeFile(abs(action.target), action.content);
|
|
759
|
+
await afterOp();
|
|
760
|
+
} else if (action.kind === "preserve-invalid-testplan") {
|
|
761
|
+
await captureToBackup(repoDir, txDir, action.source, action.backup, snapshots);
|
|
762
|
+
await afterOp();
|
|
763
|
+
} else {
|
|
764
|
+
const entries = await readdir2(abs(action.path));
|
|
765
|
+
if (entries.length === 0) await capture(repoDir, txDir, action.path, snapshots);
|
|
766
|
+
await afterOp();
|
|
767
|
+
}
|
|
768
|
+
}
|
|
735
769
|
for (const action of plan.adapters) {
|
|
736
770
|
if (action.kind === "write-symlink") {
|
|
737
771
|
await capture(repoDir, txDir, action.path, snapshots);
|
|
@@ -766,6 +800,151 @@ async function applyUpdate(repoDir, plan, hooks = {}) {
|
|
|
766
800
|
}
|
|
767
801
|
}
|
|
768
802
|
|
|
803
|
+
// src/update/testplan-migration.ts
|
|
804
|
+
import { access as access2, readFile as readFile4, readdir as readdir3 } from "fs/promises";
|
|
805
|
+
import { join as join4 } from "path";
|
|
806
|
+
var LEGACY_NAME = /^(.+)\.testplan\.json$/;
|
|
807
|
+
var SCOPE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
808
|
+
async function exists2(path) {
|
|
809
|
+
return access2(path).then(() => true, () => false);
|
|
810
|
+
}
|
|
811
|
+
function isObject(value) {
|
|
812
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
813
|
+
}
|
|
814
|
+
function nonEmptyString(value) {
|
|
815
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
816
|
+
}
|
|
817
|
+
function optionalString(value) {
|
|
818
|
+
return value === void 0 || typeof value === "string";
|
|
819
|
+
}
|
|
820
|
+
function validPairs(value) {
|
|
821
|
+
return value === void 0 || Array.isArray(value) && value.every((pair) => Array.isArray(pair) && pair.length === 2 && pair.every((part) => typeof part === "string"));
|
|
822
|
+
}
|
|
823
|
+
function hasOnlyKeys(value, keys) {
|
|
824
|
+
const allowed = new Set(keys);
|
|
825
|
+
return Object.keys(value).every((key) => allowed.has(key));
|
|
826
|
+
}
|
|
827
|
+
function isValidLegacyPlan(value) {
|
|
828
|
+
if (!isObject(value) || !nonEmptyString(value.scope) || !SCOPE.test(value.scope)) return false;
|
|
829
|
+
if (!hasOnlyKeys(value, ["scope", "title", "subtitle", "intro", "callout", "conventions", "sections"])) return false;
|
|
830
|
+
if (!nonEmptyString(value.title)) return false;
|
|
831
|
+
if (!["subtitle", "intro", "callout", "conventions"].every((field) => optionalString(value[field]))) return false;
|
|
832
|
+
if (!Array.isArray(value.sections) || value.sections.length === 0) return false;
|
|
833
|
+
const ids = /* @__PURE__ */ new Set();
|
|
834
|
+
for (const section of value.sections) {
|
|
835
|
+
if (!isObject(section) || !nonEmptyString(section.n) || !nonEmptyString(section.title)) return false;
|
|
836
|
+
if (!hasOnlyKeys(section, ["n", "title", "kind", "intro", "tests"])) return false;
|
|
837
|
+
if (!["checklist", "info"].includes(section.kind) || !optionalString(section.intro)) return false;
|
|
838
|
+
if (!Array.isArray(section.tests) || section.tests.length === 0) return false;
|
|
839
|
+
for (const test of section.tests) {
|
|
840
|
+
if (!isObject(test) || !nonEmptyString(test.id) || !nonEmptyString(test.title) || ids.has(test.id)) return false;
|
|
841
|
+
if (!hasOnlyKeys(test, ["id", "title", "note", "expect", "optional", "code", "subs"])) return false;
|
|
842
|
+
ids.add(test.id);
|
|
843
|
+
if (!optionalString(test.note) || !optionalString(test.expect)) return false;
|
|
844
|
+
if (test.optional !== void 0 && typeof test.optional !== "boolean") return false;
|
|
845
|
+
if (!validPairs(test.code) || !validPairs(test.subs)) return false;
|
|
846
|
+
for (const pair of test.subs ?? []) {
|
|
847
|
+
if (!nonEmptyString(pair[0]) || ids.has(pair[0])) return false;
|
|
848
|
+
ids.add(pair[0]);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return true;
|
|
853
|
+
}
|
|
854
|
+
function convertLegacyPlan(plan) {
|
|
855
|
+
const checklist = {
|
|
856
|
+
title: plan.title,
|
|
857
|
+
summary: nonEmptyString(plan.subtitle) ? plan.subtitle : plan.title,
|
|
858
|
+
...plan.intro !== void 0 ? { intro: plan.intro } : {},
|
|
859
|
+
...plan.callout !== void 0 ? { callout: plan.callout } : {},
|
|
860
|
+
...plan.conventions !== void 0 ? { conventions: plan.conventions } : {},
|
|
861
|
+
sections: plan.sections.map((section) => ({
|
|
862
|
+
number: section.n,
|
|
863
|
+
title: section.title,
|
|
864
|
+
kind: section.kind,
|
|
865
|
+
...section.intro !== void 0 ? { intro: section.intro } : {},
|
|
866
|
+
items: section.tests.map((test) => ({
|
|
867
|
+
id: test.id,
|
|
868
|
+
title: test.title,
|
|
869
|
+
...test.note !== void 0 ? { instruction: test.note } : {},
|
|
870
|
+
...test.expect !== void 0 ? { expectedResult: test.expect } : {},
|
|
871
|
+
...test.code !== void 0 ? {
|
|
872
|
+
codeBlocks: test.code.map(([language, content]) => ({
|
|
873
|
+
language: nonEmptyString(language) ? language : "text",
|
|
874
|
+
content
|
|
875
|
+
}))
|
|
876
|
+
} : {},
|
|
877
|
+
...test.optional !== void 0 ? { optional: test.optional } : {},
|
|
878
|
+
...test.subs !== void 0 ? {
|
|
879
|
+
subItems: test.subs.map(([id, title]) => ({
|
|
880
|
+
id,
|
|
881
|
+
title: nonEmptyString(title) ? title : id
|
|
882
|
+
}))
|
|
883
|
+
} : {}
|
|
884
|
+
}))
|
|
885
|
+
}))
|
|
886
|
+
};
|
|
887
|
+
return JSON.stringify(checklist, null, 2) + "\n";
|
|
888
|
+
}
|
|
889
|
+
async function planTestplanMigrations(repoDir) {
|
|
890
|
+
const dir = join4(repoDir, "testplans");
|
|
891
|
+
const entries = await readdir3(dir, { withFileTypes: true }).catch((error) => {
|
|
892
|
+
if (error.code === "ENOENT") return null;
|
|
893
|
+
throw error;
|
|
894
|
+
});
|
|
895
|
+
if (entries === null) return { actions: [], reconciliations: [] };
|
|
896
|
+
const actions = [];
|
|
897
|
+
const reconciliations = [];
|
|
898
|
+
let canRemoveDirectory = entries.length === 0;
|
|
899
|
+
for (const entry of entries) {
|
|
900
|
+
const match = entry.isFile() ? LEGACY_NAME.exec(entry.name) : null;
|
|
901
|
+
if (!match) continue;
|
|
902
|
+
const source = `testplans/${entry.name}`;
|
|
903
|
+
const slug = match[1];
|
|
904
|
+
const target = SCOPE.test(slug) ? `checklists/tests/${slug}.json` : void 0;
|
|
905
|
+
const raw = await readFile4(join4(repoDir, source), "utf8");
|
|
906
|
+
let parsed;
|
|
907
|
+
try {
|
|
908
|
+
parsed = JSON.parse(raw);
|
|
909
|
+
} catch {
|
|
910
|
+
parsed = void 0;
|
|
911
|
+
}
|
|
912
|
+
if (!target || !isValidLegacyPlan(parsed)) {
|
|
913
|
+
const backup = await freeBackupPath(repoDir, source);
|
|
914
|
+
actions.push({ kind: "preserve-invalid-testplan", source, backup });
|
|
915
|
+
reconciliations.push({
|
|
916
|
+
kind: "invalid-testplan",
|
|
917
|
+
source,
|
|
918
|
+
...target ? { target } : {},
|
|
919
|
+
backup,
|
|
920
|
+
sourceHash: sha256(raw)
|
|
921
|
+
});
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
const content = convertLegacyPlan(parsed);
|
|
925
|
+
if (await exists2(join4(repoDir, target))) {
|
|
926
|
+
const backup = await freeBackupPath(repoDir, target);
|
|
927
|
+
const collided = await readFile4(join4(repoDir, target), "utf8");
|
|
928
|
+
actions.push({ kind: "migrate-testplan", source, target, content, backup });
|
|
929
|
+
reconciliations.push({
|
|
930
|
+
kind: "testplan-collision",
|
|
931
|
+
source,
|
|
932
|
+
target,
|
|
933
|
+
backup,
|
|
934
|
+
sourceHash: sha256(collided),
|
|
935
|
+
incomingHash: sha256(content)
|
|
936
|
+
});
|
|
937
|
+
} else {
|
|
938
|
+
actions.push({ kind: "migrate-testplan", source, target, content });
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (entries.length > 0) {
|
|
942
|
+
canRemoveDirectory = entries.every((entry) => entry.isFile() && LEGACY_NAME.test(entry.name) && actions.some((action) => action.kind === "migrate-testplan" && action.source === `testplans/${entry.name}`));
|
|
943
|
+
}
|
|
944
|
+
if (canRemoveDirectory) actions.push({ kind: "remove-empty-testplan-dir", path: "testplans" });
|
|
945
|
+
return { actions, reconciliations };
|
|
946
|
+
}
|
|
947
|
+
|
|
769
948
|
// src/update/prompts.ts
|
|
770
949
|
function cancelUpdate(p) {
|
|
771
950
|
p.cancel("Init cancelled.");
|
|
@@ -836,7 +1015,7 @@ async function runUpdate(repoDir, flags, version, state) {
|
|
|
836
1015
|
}
|
|
837
1016
|
const migratedFromV1 = state.kind === "valid" && state.manifest.version === 1;
|
|
838
1017
|
const current = state.kind === "valid" ? migratedFromV1 ? await migrateV1(repoDir, state.manifest) : state.manifest : null;
|
|
839
|
-
if (!migratedFromV1 && (current?.setupPending || current?.files.some((file) => file.reconciliations?.length))) {
|
|
1018
|
+
if (!migratedFromV1 && (current?.setupPending || current?.migrationReconciliations?.length || current?.files.some((file) => file.reconciliations?.length))) {
|
|
840
1019
|
throw new Error("Finish the pending /setup-repo handoff before running bearings init again.");
|
|
841
1020
|
}
|
|
842
1021
|
if (current && compareSemver(version, current.bearingsVersion) < 0) {
|
|
@@ -876,6 +1055,7 @@ async function runUpdate(repoDir, flags, version, state) {
|
|
|
876
1055
|
else if (action.kind === "removed-conflict") decisions.push(await chooseRemovedConflict(p, action));
|
|
877
1056
|
}
|
|
878
1057
|
const resolved = await resolveFilePlan(repoDir, draft, decisions);
|
|
1058
|
+
const migrationPlan = await planTestplanMigrations(repoDir);
|
|
879
1059
|
const adapterActions = await planAdapterActions({
|
|
880
1060
|
repoDir,
|
|
881
1061
|
currentHarnesses,
|
|
@@ -886,13 +1066,13 @@ async function runUpdate(repoDir, flags, version, state) {
|
|
|
886
1066
|
});
|
|
887
1067
|
const harnessesUnchanged = current ? current.harnesses.length === harnesses.length && current.harnesses.every((h) => harnesses.includes(h)) : false;
|
|
888
1068
|
const exposureUnchanged = current ? current.exposure === exposure : false;
|
|
889
|
-
const noArtifactChanges = resolved.actions.every((a) => a.kind === "keep") && adapterActions.length === 0;
|
|
1069
|
+
const noArtifactChanges = resolved.actions.every((a) => a.kind === "keep") && migrationPlan.actions.length === 0 && adapterActions.length === 0;
|
|
890
1070
|
const versionUnchanged = !!current && version === current.bearingsVersion;
|
|
891
1071
|
if (!reconstruction && !migratedFromV1 && noArtifactChanges && versionUnchanged && harnessesUnchanged && exposureUnchanged) {
|
|
892
1072
|
return { manifest: current, report: "bearings init: already up to date." };
|
|
893
1073
|
}
|
|
894
1074
|
const migratedAbsorbedBackup = migratedFromV1 && (current?.files.some((file) => file.reconciliations?.length) ?? false);
|
|
895
|
-
const setupRequired = resolved.setupRequired || adapterActions.length > 0 || migratedAbsorbedBackup;
|
|
1075
|
+
const setupRequired = resolved.setupRequired || migrationPlan.actions.length > 0 || adapterActions.length > 0 || migratedAbsorbedBackup;
|
|
896
1076
|
const finalManifest = {
|
|
897
1077
|
version: 2,
|
|
898
1078
|
bearingsVersion: version,
|
|
@@ -905,10 +1085,12 @@ async function runUpdate(repoDir, flags, version, state) {
|
|
|
905
1085
|
toVersion: version
|
|
906
1086
|
}
|
|
907
1087
|
} : {},
|
|
1088
|
+
...migrationPlan.reconciliations.length > 0 ? { migrationReconciliations: [...migrationPlan.reconciliations] } : {},
|
|
908
1089
|
files: [...resolved.files]
|
|
909
1090
|
};
|
|
910
1091
|
const summaryInput = {
|
|
911
1092
|
actions: resolved.actions,
|
|
1093
|
+
migrationActions: migrationPlan.actions,
|
|
912
1094
|
adapterActions,
|
|
913
1095
|
fromVersion: current?.bearingsVersion,
|
|
914
1096
|
toVersion: version,
|
|
@@ -917,7 +1099,12 @@ async function runUpdate(repoDir, flags, version, state) {
|
|
|
917
1099
|
reconstruction
|
|
918
1100
|
};
|
|
919
1101
|
await confirmUpdatePlan(p, renderUpdatePlan(summaryInput));
|
|
920
|
-
await applyUpdate(repoDir, {
|
|
1102
|
+
await applyUpdate(repoDir, {
|
|
1103
|
+
files: resolved.actions,
|
|
1104
|
+
migrations: migrationPlan.actions,
|
|
1105
|
+
adapters: adapterActions,
|
|
1106
|
+
manifest: finalManifest
|
|
1107
|
+
});
|
|
921
1108
|
return { manifest: finalManifest, report: renderUpdateReport({ ...summaryInput, setupRequired }) };
|
|
922
1109
|
}
|
|
923
1110
|
export {
|
package/package.json
CHANGED
|
@@ -11,26 +11,29 @@ answer per question — and explore the code before asking anything the code
|
|
|
11
11
|
can answer.
|
|
12
12
|
|
|
13
13
|
This is the only operation the temporary `## Setup Required` gate in
|
|
14
|
-
`AGENTS.md` permits. Do not remove the gate until step
|
|
14
|
+
`AGENTS.md` permits. Do not remove the gate until step 12 below.
|
|
15
15
|
|
|
16
16
|
## Post-update reconciliation
|
|
17
17
|
|
|
18
18
|
When `.agents/bearings.json` is manifest v2 and has `setupPending`:
|
|
19
19
|
|
|
20
|
-
1. Read every file record's ordered `reconciliations
|
|
21
|
-
|
|
20
|
+
1. Read every file record's ordered `reconciliations` and every top-level
|
|
21
|
+
`migrationReconciliations` record.
|
|
22
|
+
2. Resolve every `migrationReconciliations` record through **Migrated checklist
|
|
23
|
+
repairs** below.
|
|
24
|
+
3. For each non-`skill-update` reconciliation, read its Backup File and current target, summarize
|
|
22
25
|
the differences, and ask which backed-up changes to apply to the target.
|
|
23
|
-
|
|
26
|
+
4. Apply the developer's choice directly to the target regardless of owner,
|
|
24
27
|
delete the resolved Backup File, and remove its reconciliation record.
|
|
25
|
-
|
|
28
|
+
5. A `skippedTemplate` records a declined template revision.
|
|
26
29
|
Do not apply the declined template — normal project maintenance may
|
|
27
30
|
still update that file.
|
|
28
|
-
|
|
31
|
+
6. Run only setup steps supported by current drift; do not reset completed
|
|
29
32
|
project tailoring.
|
|
30
|
-
|
|
33
|
+
7. Run `bearings verify`. Resolve every failure and warning except the
|
|
31
34
|
expected `setup-pending` warning, then remove `setupPending` from the
|
|
32
35
|
manifest.
|
|
33
|
-
|
|
36
|
+
8. Run `bearings verify` again and finish only at zero failures and warnings.
|
|
34
37
|
|
|
35
38
|
### Starter skill updates (`skill-update`)
|
|
36
39
|
|
|
@@ -61,6 +64,52 @@ For each file record with a `skill-update` reconciliation:
|
|
|
61
64
|
new content hash.
|
|
62
65
|
5. Delete backup, incoming file, and the reconciliation entry.
|
|
63
66
|
|
|
67
|
+
### Migrated checklist repairs (`migrationReconciliations`)
|
|
68
|
+
|
|
69
|
+
Resolve each record through the generic checklist workflow. Invoke the
|
|
70
|
+
`checklist` skill directly, or delegate one record to a subagent that loads and
|
|
71
|
+
follows `.agents/skills/checklist/SKILL.md`.
|
|
72
|
+
|
|
73
|
+
1. For `testplan-collision`, read the current `target` and the pre-existing
|
|
74
|
+
checklist in `backup`. Summarize their authored content and ask the developer
|
|
75
|
+
whether to keep either version or combine them. Write the choice to `target`.
|
|
76
|
+
2. For `invalid-testplan`, read `backup`, explain each contract problem, and
|
|
77
|
+
repair its useful authored content into `target`. When the record has no
|
|
78
|
+
`target`, ask the developer to choose a safe
|
|
79
|
+
`checklists/<category...>/<slug>.json` path first. Do not discard content
|
|
80
|
+
that cannot be repaired without the developer's decision.
|
|
81
|
+
3. Follow the checklist skill's schema, validation, and full-library build
|
|
82
|
+
steps for the resolved target. Continue only after validation and build both
|
|
83
|
+
pass.
|
|
84
|
+
4. Delete the resolved `backup` and remove its
|
|
85
|
+
`migrationReconciliations` record. Remove the top-level property when no
|
|
86
|
+
records remain.
|
|
87
|
+
|
|
88
|
+
## Checklist opener
|
|
89
|
+
|
|
90
|
+
Add or reuse one project-native command that runs `npx bearings checklist` (or
|
|
91
|
+
the repository's established local bearings invocation). This command builds
|
|
92
|
+
the checklist payload and opens the static previewer.
|
|
93
|
+
|
|
94
|
+
1. Search existing automation and documentation for an existing equivalent
|
|
95
|
+
runner command that already builds and opens the checklist previewer. Reuse
|
|
96
|
+
it and report its exact invocation without editing its runner.
|
|
97
|
+
2. Otherwise, select the first existing safe automation home in this exact
|
|
98
|
+
priority: `justfile`, `Makefile`, `Taskfile.yml`, `mise.toml`, then
|
|
99
|
+
`package.json`.
|
|
100
|
+
3. Before editing an existing automation file, show the proposed command and
|
|
101
|
+
ask the developer for approval. Edit it only after approval. If approval is
|
|
102
|
+
declined, leave it unchanged and continue through the priority order.
|
|
103
|
+
4. If no existing home is safe and approved, ask the developer to choose
|
|
104
|
+
between creating one missing automation file from the list above and using
|
|
105
|
+
the direct fallback. Create a missing file only after the developer selects
|
|
106
|
+
that strategy and file.
|
|
107
|
+
5. When no automation file is selected, make no automation edit and report the
|
|
108
|
+
stack-agnostic direct build-and-open fallback: `npx bearings checklist`.
|
|
109
|
+
6. Run the selected command, or the direct fallback, and confirm that it builds
|
|
110
|
+
successfully and opens the static previewer. Report its exact invocation for
|
|
111
|
+
future use.
|
|
112
|
+
|
|
64
113
|
## Required workflow
|
|
65
114
|
|
|
66
115
|
1. Read `AGENTS.md` and `.agents/bearings.json`.
|
|
@@ -96,17 +145,18 @@ For each file record with a `skill-update` reconciliation:
|
|
|
96
145
|
`lastTemplateHash` equals `hash`).
|
|
97
146
|
- Write `.agents/.bearings-baseline/skills/<name>/SKILL.md` equal to the
|
|
98
147
|
live file bytes.
|
|
99
|
-
7.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
10.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
148
|
+
7. Complete the **Checklist opener** workflow above.
|
|
149
|
+
8. Run the `/refresh-repo-map` workflow to initialize `docs/DOMAIN.md`,
|
|
150
|
+
`docs/ARCHITECTURE.md`, `docs/CODEBASE_MAP.md`, and
|
|
151
|
+
`docs/diagrams/c4-component.puml`.
|
|
152
|
+
9. Expose any newly created skills into each configured harness using the
|
|
153
|
+
manifest's existing symlink/copy mode.
|
|
154
|
+
10. Run `bearings verify`. Fix every failure and every warning.
|
|
155
|
+
11. Confirm the run reports zero failures and zero warnings before
|
|
156
|
+
proceeding.
|
|
157
|
+
12. Remove the `## Setup Required` section from `AGENTS.md` — only after
|
|
158
|
+
step 11 confirms zero failures and zero warnings.
|
|
159
|
+
13. Run `bearings verify` again and report the completed setup to the
|
|
110
160
|
developer.
|
|
111
161
|
|
|
112
162
|
## Rules
|
|
@@ -118,8 +168,9 @@ For each file record with a `skill-update` reconciliation:
|
|
|
118
168
|
- Do edit starter **skills** during setup (adapt + claim). After claim they
|
|
119
169
|
are agent-owned; maintainers may edit them freely.
|
|
120
170
|
- Manifest edits are limited to: skill `owner` / `hash` / `lastTemplateHash` /
|
|
121
|
-
`skippedTemplate` / `reconciliations`,
|
|
122
|
-
and clearing
|
|
171
|
+
`skippedTemplate` / `reconciliations`, resolving
|
|
172
|
+
`migrationReconciliations`, deleting resolved backups/incoming, and clearing
|
|
173
|
+
`setupPending`.
|
|
123
174
|
- Do not add a skill registry row to `AGENTS.md` — native skill discovery
|
|
124
175
|
replaces it.
|
|
125
176
|
- Do not invent domain or technical constraints — every stated constraint
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: checklist
|
|
3
|
+
description: Create a generic executable checklist for human-run procedures. Use when work needs an operational checklist for setup, deployment, release, verification, maintenance, or another process a person must perform.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Checklist
|
|
7
|
+
|
|
8
|
+
Create one evidence-backed checklist for a human operator. The JSON contains
|
|
9
|
+
authored instructions; the static previewer owns execution state.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
1. Read `.agents/skills/checklist/schema.reference.json` and inspect the
|
|
14
|
+
repository sources that define the requested procedure. Resolve every
|
|
15
|
+
mechanic, command, URL, prerequisite, expected result, and hazard from
|
|
16
|
+
running code, configuration, authoritative documentation, or developer
|
|
17
|
+
confirmation. Ask the developer about any required detail the evidence does
|
|
18
|
+
not establish.
|
|
19
|
+
2. Choose a lowercase slug path under `checklists/`. The final segment is the
|
|
20
|
+
checklist slug and every preceding segment is its category. Use nested
|
|
21
|
+
categories when useful, for example `checklists/operations/servers/rotate-keys.json`
|
|
22
|
+
has category `operations/servers` and identity `operations/servers/rotate-keys`.
|
|
23
|
+
3. Write exactly one JSON file at `checklists/<category...>/<slug>.json`.
|
|
24
|
+
Category identity and slug come only from the path; omit them from the JSON.
|
|
25
|
+
4. Run
|
|
26
|
+
`node .agents/skills/checklist/scripts/validate.mjs checklists/<category...>/<slug>.json`.
|
|
27
|
+
Fix every reported violation until it exits zero and prints `OK`.
|
|
28
|
+
5. From the repository root, run
|
|
29
|
+
`node .agents/skills/checklist/scripts/build.mjs`. Fix every reported
|
|
30
|
+
checklist-library error until the build exits zero.
|
|
31
|
+
6. Open `.agents/skills/checklist/previewer/index.html` with the environment's
|
|
32
|
+
browser-opening tool. If opening fails, report the absolute file path so the
|
|
33
|
+
developer can open it. The command must finish; the static previewer needs
|
|
34
|
+
no server process.
|
|
35
|
+
|
|
36
|
+
## Content Policy
|
|
37
|
+
|
|
38
|
+
- Organize sections in execution order and keep each checklist item to one
|
|
39
|
+
primary action or verification. Use `info` sections for concise context that
|
|
40
|
+
the operator does not mark as executed.
|
|
41
|
+
- Give every executable item exact, evidence-backed mechanics and an observable
|
|
42
|
+
`expectedResult`. Put commands in `codeBlocks`; distinguish literal values
|
|
43
|
+
from placeholders and state where each command runs.
|
|
44
|
+
- Use `links` for authoritative background material instead of expanding the
|
|
45
|
+
checklist into a manual. Include only verified, safe destination URLs and
|
|
46
|
+
clear labels.
|
|
47
|
+
- Put a `warning` before destructive, irreversible, privileged,
|
|
48
|
+
security-sensitive, externally visible, data-changing, or access-loss
|
|
49
|
+
actions. State the concrete hazard and any prerequisite that preserves
|
|
50
|
+
operator control.
|
|
51
|
+
- Add `failureGuidance` when an item can fail or block progress. Give an
|
|
52
|
+
evidence-backed next action such as stop, retry, diagnose, roll back, or
|
|
53
|
+
escalate, plus the observable recovered state when recovery applies.
|
|
54
|
+
- Refer to credentials through the repository's established secret mechanism
|
|
55
|
+
or documented location. Keep passwords, tokens, private keys, and other
|
|
56
|
+
secret values out of checklist content.
|
|
57
|
+
- Keep authored content only in the JSON. Statuses such as `not-started`,
|
|
58
|
+
`complete`, `failed`, `blocked`, and `not-applicable`, and all operator
|
|
59
|
+
progress, belong to browser-local previewer state.
|
|
60
|
+
- Treat absent evidence as an unresolved input. Ask for it or state a verified
|
|
61
|
+
limitation; do not invent mechanics, URLs, commands, credentials, expected
|
|
62
|
+
behavior, warnings, or recovery steps.
|
|
63
|
+
|
|
64
|
+
## Completion
|
|
65
|
+
|
|
66
|
+
Finish only when exactly one authored checklist file exists for this request,
|
|
67
|
+
its mechanics and outcomes trace to evidence, its validator prints `OK`, the
|
|
68
|
+
full library build succeeds, and the static previewer opens or its absolute path
|
|
69
|
+
is reported after an opening failure.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; base-uri 'none'; form-action 'none'">
|
|
7
|
+
<title>Checklist library</title>
|
|
8
|
+
<link rel="stylesheet" href="./viewer.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<header class="masthead">
|
|
12
|
+
<div class="masthead-inner">
|
|
13
|
+
<p class="eyebrow">Bearings / local checklist library</p>
|
|
14
|
+
<h1 id="title">Choose a checklist</h1>
|
|
15
|
+
<p id="summary" class="summary">Browse the checklists generated from this repository.</p>
|
|
16
|
+
</div>
|
|
17
|
+
</header>
|
|
18
|
+
<div class="toolbar">
|
|
19
|
+
<div class="toolbar-inner">
|
|
20
|
+
<label class="picker-label" for="checklist-picker">Checklist</label>
|
|
21
|
+
<select id="checklist-picker"><option value="">Loading...</option></select>
|
|
22
|
+
<div class="progress" aria-live="polite">
|
|
23
|
+
<span id="progress-copy">0 / 0 resolved</span>
|
|
24
|
+
<span class="progress-track" aria-hidden="true"><span id="progress-fill"></span></span>
|
|
25
|
+
</div>
|
|
26
|
+
<button id="export-progress" type="button">Export</button>
|
|
27
|
+
<button id="import-progress" type="button">Import</button>
|
|
28
|
+
<input id="import-file" type="file" accept="application/json,.json" hidden>
|
|
29
|
+
<button id="reset-progress" type="button">Reset</button>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<p id="storage-warning" class="storage-warning" role="status" hidden></p>
|
|
33
|
+
<p id="status" class="status" role="status" aria-live="polite"></p>
|
|
34
|
+
<main id="content"></main>
|
|
35
|
+
<script src="./data.generated.js"></script>
|
|
36
|
+
<script src="./viewer.js"></script>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|