cohorte 2.10.0 → 3.0.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/assets/manifest.json +193 -0
- package/assets/migrations/config/README.md +12 -0
- package/assets/migrations/state/0001_init.sql +154 -0
- package/assets/prompts/README.md +11 -0
- package/assets/prompts/agents/fixer.md +7 -0
- package/assets/prompts/agents/implementer.md +12 -0
- package/assets/prompts/agents/reviewer.md +8 -0
- package/assets/prompts/agents/security-reviewer.md +7 -0
- package/assets/prompts/phases/build.md +5 -0
- package/assets/prompts/phases/review.md +5 -0
- package/assets/prompts/phases/test.md +5 -0
- package/assets/prompts/system.md +11 -0
- package/assets/schemas/agent-output.schema.json +225 -0
- package/assets/schemas/auth-status.schema.json +87 -0
- package/assets/schemas/command-result.schema.json +192 -0
- package/assets/schemas/commands.schema.json +952 -0
- package/assets/schemas/config.schema.json +2259 -0
- package/assets/schemas/doctor-report.schema.json +104 -0
- package/assets/schemas/events.schema.json +8514 -0
- package/assets/schemas/fake-script.schema.json +549 -0
- package/assets/schemas/inspect.schema.json +3133 -0
- package/assets/schemas/manifest.schema.json +79 -0
- package/assets/schemas/ownership.schema.json +52 -0
- package/assets/schemas/policy-verdict.schema.json +282 -0
- package/assets/schemas/project-model.schema.json +783 -0
- package/assets/schemas/project-status.schema.json +217 -0
- package/assets/schemas/reconcile-plan.schema.json +155 -0
- package/assets/schemas/run-diff.schema.json +177 -0
- package/assets/schemas/run-snapshot-manifest.schema.json +2104 -0
- package/assets/schemas/run-state.schema.json +1755 -0
- package/assets/schemas/runtime-capabilities.schema.json +1545 -0
- package/assets/schemas/sandbox-capabilities.schema.json +116 -0
- package/assets/schemas/skill.schema.json +87 -0
- package/assets/schemas/spec.schema.json +202 -0
- package/assets/schemas/tool-catalogue.schema.json +655 -0
- package/assets/schemas/trust-record.schema.json +38 -0
- package/assets/skills/README.md +9 -0
- package/assets/skills/inspect-surface.md +4 -0
- package/dist/agent-host.mjs +50 -0
- package/dist/agent-host.mjs.map +1 -0
- package/dist/bundle-manifest.json +102 -0
- package/dist/chunks/__host-DgGPyco9.mjs +18 -0
- package/dist/chunks/__host-DgGPyco9.mjs.map +1 -0
- package/dist/chunks/approve-Cv135UqI.mjs +19 -0
- package/dist/chunks/approve-Cv135UqI.mjs.map +1 -0
- package/dist/chunks/auth-BT5oVp76.mjs +132 -0
- package/dist/chunks/auth-BT5oVp76.mjs.map +1 -0
- package/dist/chunks/auth-D8rCVY33.mjs +84 -0
- package/dist/chunks/auth-D8rCVY33.mjs.map +1 -0
- package/dist/chunks/brainstorm-CU10T1tm.mjs +7 -0
- package/dist/chunks/brainstorm-CU10T1tm.mjs.map +1 -0
- package/dist/chunks/cancel-KV_cpsqS.mjs +15 -0
- package/dist/chunks/cancel-KV_cpsqS.mjs.map +1 -0
- package/dist/chunks/canonical-C8UxeEmB.mjs +57 -0
- package/dist/chunks/canonical-C8UxeEmB.mjs.map +1 -0
- package/dist/chunks/catalogue-BvwSrM_j.mjs +1549 -0
- package/dist/chunks/catalogue-BvwSrM_j.mjs.map +1 -0
- package/dist/chunks/compose-Cr0hP0qm.mjs +7303 -0
- package/dist/chunks/compose-Cr0hP0qm.mjs.map +1 -0
- package/dist/chunks/config-DefHA2Lw.mjs +96 -0
- package/dist/chunks/config-DefHA2Lw.mjs.map +1 -0
- package/dist/chunks/contract-B0ra2-bm.mjs +295 -0
- package/dist/chunks/contract-B0ra2-bm.mjs.map +1 -0
- package/dist/chunks/contract-vYbwYr0Y.mjs +123 -0
- package/dist/chunks/contract-vYbwYr0Y.mjs.map +1 -0
- package/dist/chunks/deny-ClTxSvHJ.mjs +15 -0
- package/dist/chunks/deny-ClTxSvHJ.mjs.map +1 -0
- package/dist/chunks/diff-Qw0II0sj.mjs +101 -0
- package/dist/chunks/diff-Qw0II0sj.mjs.map +1 -0
- package/dist/chunks/discover-30Ciya_0.mjs +20 -0
- package/dist/chunks/discover-30Ciya_0.mjs.map +1 -0
- package/dist/chunks/doctor-DfCrKpfT.mjs +222 -0
- package/dist/chunks/doctor-DfCrKpfT.mjs.map +1 -0
- package/dist/chunks/fix-D4Tj8RVA.mjs +18 -0
- package/dist/chunks/fix-D4Tj8RVA.mjs.map +1 -0
- package/dist/chunks/gc-BBXlBF5w.mjs +161 -0
- package/dist/chunks/gc-BBXlBF5w.mjs.map +1 -0
- package/dist/chunks/init-Okz4mlhW.mjs +30 -0
- package/dist/chunks/init-Okz4mlhW.mjs.map +1 -0
- package/dist/chunks/inspect-Buby4cQD.mjs +22 -0
- package/dist/chunks/inspect-Buby4cQD.mjs.map +1 -0
- package/dist/chunks/logs-Giq5sUau.mjs +25 -0
- package/dist/chunks/logs-Giq5sUau.mjs.map +1 -0
- package/dist/chunks/migrate-DRoFUg53.mjs +19 -0
- package/dist/chunks/migrate-DRoFUg53.mjs.map +1 -0
- package/dist/chunks/models-Btp6X2YZ.mjs +24 -0
- package/dist/chunks/models-Btp6X2YZ.mjs.map +1 -0
- package/dist/chunks/pause-BASBfKLO.mjs +16 -0
- package/dist/chunks/pause-BASBfKLO.mjs.map +1 -0
- package/dist/chunks/policy-DUIgzF3W.mjs +139 -0
- package/dist/chunks/policy-DUIgzF3W.mjs.map +1 -0
- package/dist/chunks/providers-Dx78imKj.mjs +19 -0
- package/dist/chunks/providers-Dx78imKj.mjs.map +1 -0
- package/dist/chunks/reconcile-BfD785gd.mjs +26 -0
- package/dist/chunks/reconcile-BfD785gd.mjs.map +1 -0
- package/dist/chunks/render-DyZr5Lgi.mjs +53 -0
- package/dist/chunks/render-DyZr5Lgi.mjs.map +1 -0
- package/dist/chunks/resume-GJQRb2Eb.mjs +15 -0
- package/dist/chunks/resume-GJQRb2Eb.mjs.map +1 -0
- package/dist/chunks/retry-CLrUem1H.mjs +15 -0
- package/dist/chunks/retry-CLrUem1H.mjs.map +1 -0
- package/dist/chunks/review-BDdjb6kB.mjs +17 -0
- package/dist/chunks/review-BDdjb6kB.mjs.map +1 -0
- package/dist/chunks/run-CgTWYlcd.mjs +82 -0
- package/dist/chunks/run-CgTWYlcd.mjs.map +1 -0
- package/dist/chunks/run-tool-C3rAv4XF.mjs +26 -0
- package/dist/chunks/run-tool-C3rAv4XF.mjs.map +1 -0
- package/dist/chunks/schema-C9QbUW4H.mjs +578 -0
- package/dist/chunks/schema-C9QbUW4H.mjs.map +1 -0
- package/dist/chunks/send-BdnJ85Nz.mjs +19 -0
- package/dist/chunks/send-BdnJ85Nz.mjs.map +1 -0
- package/dist/chunks/ship-DTuLGoaJ.mjs +19 -0
- package/dist/chunks/ship-DTuLGoaJ.mjs.map +1 -0
- package/dist/chunks/shutdown-BM92Iyv2.mjs +15 -0
- package/dist/chunks/shutdown-BM92Iyv2.mjs.map +1 -0
- package/dist/chunks/skip-DZG5kpcH.mjs +18 -0
- package/dist/chunks/skip-DZG5kpcH.mjs.map +1 -0
- package/dist/chunks/spec-Bq0P3jVh.mjs +20 -0
- package/dist/chunks/spec-Bq0P3jVh.mjs.map +1 -0
- package/dist/chunks/src-CId6kMsY.mjs +1689 -0
- package/dist/chunks/src-CId6kMsY.mjs.map +1 -0
- package/dist/chunks/src-CfAOde7C.mjs +5403 -0
- package/dist/chunks/src-CfAOde7C.mjs.map +1 -0
- package/dist/chunks/src-Cu3TxNob.mjs +783 -0
- package/dist/chunks/src-Cu3TxNob.mjs.map +1 -0
- package/dist/chunks/src-DcU4IXMe.mjs +117 -0
- package/dist/chunks/src-DcU4IXMe.mjs.map +1 -0
- package/dist/chunks/src-JxawBQBn.mjs +446 -0
- package/dist/chunks/src-JxawBQBn.mjs.map +1 -0
- package/dist/chunks/src-s7YRvk0t.mjs +2116 -0
- package/dist/chunks/src-s7YRvk0t.mjs.map +1 -0
- package/dist/chunks/status-SMjiOMYy.mjs +29 -0
- package/dist/chunks/status-SMjiOMYy.mjs.map +1 -0
- package/dist/chunks/tail-BuhT-2PH.mjs +25 -0
- package/dist/chunks/tail-BuhT-2PH.mjs.map +1 -0
- package/dist/chunks/update-BXIppsbi.mjs +16 -0
- package/dist/chunks/update-BXIppsbi.mjs.map +1 -0
- package/dist/cli.mjs +175 -0
- package/dist/cli.mjs.map +1 -0
- package/package.json +17 -32
- package/CHANGELOG.md +0 -1453
- package/bin/cli.js +0 -812
- package/bin/report.js +0 -141
- package/core/adapter/render.js +0 -389
- package/core/agents/implementer.template.md +0 -142
- package/core/agents/profile-reader.md +0 -50
- package/core/agents/release.md +0 -58
- package/core/agents/review.md +0 -179
- package/core/commands/cohorte-align-ds.md +0 -33
- package/core/commands/cohorte-audit.md +0 -58
- package/core/commands/cohorte-brainstorm.md +0 -70
- package/core/commands/cohorte-build.md +0 -219
- package/core/commands/cohorte-doctor.md +0 -170
- package/core/commands/cohorte-fix.md +0 -80
- package/core/commands/cohorte-fleet.md +0 -103
- package/core/commands/cohorte-init-pipeline.md +0 -31
- package/core/commands/cohorte-intake.md +0 -92
- package/core/commands/cohorte-patch.md +0 -118
- package/core/commands/cohorte-refactor.md +0 -59
- package/core/commands/cohorte-retro.md +0 -85
- package/core/commands/cohorte-review.md +0 -270
- package/core/commands/cohorte-ship.md +0 -122
- package/core/commands/cohorte-spec.md +0 -101
- package/core/commands/cohorte-update-pipeline.md +0 -134
- package/core/hooks/gate.py +0 -580
- package/core/runtimes/claude.json +0 -74
- package/core/runtimes/codex.json +0 -83
- package/core/runtimes/cursor.json +0 -76
- package/core/runtimes/gemini.json +0 -76
- package/core/runtimes/opencode.json +0 -73
- package/core/templates/agent-handoff.md +0 -34
- package/core/templates/brainstorm-return.md +0 -36
- package/core/templates/decisions.template.md +0 -42
- package/core/templates/design-brief.md +0 -44
- package/core/templates/patch.template.md +0 -86
- package/core/templates/pr-body.md +0 -25
- package/core/templates/review-feedback.md +0 -38
- package/core/templates/spec.template.md +0 -87
- package/core/templates/steps/init-pipeline/01-detect-stack.md +0 -44
- package/core/templates/steps/init-pipeline/02-interview-gaps.md +0 -55
- package/core/templates/steps/init-pipeline/03-draft-profile.md +0 -10
- package/core/templates/steps/init-pipeline/04-write-render.md +0 -115
- package/core/templates/steps/init-pipeline/05-report.md +0 -12
- package/core/workflows/audit.js +0 -240
- package/core/workflows/loop.js +0 -641
- package/core/workflows/refactor.js +0 -276
- package/core/workflows/review.js +0 -456
- package/install.ps1 +0 -103
- package/install.sh +0 -114
- package/lib/doctor.js +0 -504
- package/lib/runtime.js +0 -134
- package/lib/versions.js +0 -149
- package/lib/yaml.js +0 -129
- package/profile/PIPELINE.template.md +0 -251
- package/profile/SCHEMA.md +0 -698
- package/profile/cohorte.config.template.yaml +0 -43
- package/scripts/assert-gate-hook.mjs +0 -75
- package/scripts/kanban-move.sh +0 -313
- package/scripts/metrics/collect.mjs +0 -531
- package/scripts/metrics/prices.json +0 -42
- package/scripts/new-feature.sh.template +0 -98
- package/scripts/preflight.sh +0 -124
- package/scripts/remove-feature.sh.template +0 -56
- package/scripts/test-adapter.mjs +0 -368
- package/scripts/test-gate.mjs +0 -428
- package/scripts/test-kanban.mjs +0 -304
- package/scripts/test-lib.mjs +0 -310
- package/scripts/test-metrics.mjs +0 -156
- package/scripts/test-workflows.mjs +0 -758
- package/scripts/validate-core.mjs +0 -320
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { n as sha256Hex, t as canonicalJson } from "./canonical-C8UxeEmB.mjs";
|
|
2
|
+
import "./src-Cu3TxNob.mjs";
|
|
3
|
+
import "./contract-vYbwYr0Y.mjs";
|
|
4
|
+
import { s as DEFAULT_CONFIG } from "./schema-C9QbUW4H.mjs";
|
|
5
|
+
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
6
|
+
import { join, relative } from "node:path";
|
|
7
|
+
import { parse, stringify } from "yaml";
|
|
8
|
+
//#region ../../packages/project-model/src/desired/index.ts
|
|
9
|
+
function deriveDesiredState(input) {
|
|
10
|
+
const modelYaml = stringify(input.model);
|
|
11
|
+
const builtin = {
|
|
12
|
+
"manifest.yaml": stringify({
|
|
13
|
+
schemaVersion: 1,
|
|
14
|
+
cohorteVersion: input.cohorteVersion,
|
|
15
|
+
createdWith: input.cohorteVersion,
|
|
16
|
+
protocol: {
|
|
17
|
+
min: "3.0",
|
|
18
|
+
max: "3.x"
|
|
19
|
+
},
|
|
20
|
+
stateSchemaVersion: 1,
|
|
21
|
+
generated: [
|
|
22
|
+
{
|
|
23
|
+
path: "project.yaml",
|
|
24
|
+
templateId: "project-model/v1",
|
|
25
|
+
templateSha256: sha256Hex(modelYaml),
|
|
26
|
+
renderedSha256: sha256Hex(modelYaml)
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
path: ".gitignore",
|
|
30
|
+
templateId: "cohorte/gitignore/v1",
|
|
31
|
+
templateSha256: sha256Hex("state/\nruns/\n"),
|
|
32
|
+
renderedSha256: sha256Hex("state/\nruns/\n")
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
path: "generated/.gitkeep",
|
|
36
|
+
templateId: "cohorte/generated-dir/v1",
|
|
37
|
+
templateSha256: sha256Hex(""),
|
|
38
|
+
renderedSha256: sha256Hex("")
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}),
|
|
42
|
+
"project.yaml": modelYaml,
|
|
43
|
+
"config.yaml": "schemaVersion: 1\n",
|
|
44
|
+
"ownership.yaml": "surfaces: {}\n",
|
|
45
|
+
".gitignore": "state/\nruns/\n",
|
|
46
|
+
"generated/.gitkeep": ""
|
|
47
|
+
};
|
|
48
|
+
const files = new Set(Object.keys(builtin));
|
|
49
|
+
for (const path of input.model.generatedArtifacts.value) files.add(path.replace(/^\.cohorte\//u, ""));
|
|
50
|
+
const stateFiles = [...files].sort().map((path) => ({
|
|
51
|
+
path,
|
|
52
|
+
class: path === "config.yaml" || path === "ownership.yaml" ? "human" : "generated",
|
|
53
|
+
sha256: sha256Hex(builtin[path] ?? canonicalJson({
|
|
54
|
+
path,
|
|
55
|
+
model: input.model,
|
|
56
|
+
config: input.config,
|
|
57
|
+
skills: input.skills
|
|
58
|
+
})),
|
|
59
|
+
...input.skills[path] === void 0 ? {} : { templateId: path }
|
|
60
|
+
}));
|
|
61
|
+
return {
|
|
62
|
+
cohorteVersion: input.cohorteVersion,
|
|
63
|
+
files: stateFiles
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region ../../packages/project-model/src/drift/index.ts
|
|
68
|
+
async function readActualState(root) {
|
|
69
|
+
const stateRoot = join(root, ".cohorte");
|
|
70
|
+
let manifest = null;
|
|
71
|
+
try {
|
|
72
|
+
manifest = parse(await readFile(join(stateRoot, "manifest.yaml"), "utf8"));
|
|
73
|
+
} catch {
|
|
74
|
+
return {
|
|
75
|
+
manifest: null,
|
|
76
|
+
files: []
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const files = [];
|
|
80
|
+
const visit = async (dir) => {
|
|
81
|
+
try {
|
|
82
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
83
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
84
|
+
if (entry.name === "state" || entry.name === "runs") continue;
|
|
85
|
+
const path = join(dir, entry.name);
|
|
86
|
+
if (entry.isDirectory()) await visit(path);
|
|
87
|
+
else if (entry.isFile()) {
|
|
88
|
+
const bytes = await readFile(path);
|
|
89
|
+
const relativePath = relative(stateRoot, path).split("\\").join("/");
|
|
90
|
+
const generated = relativePath === "manifest.yaml" ? {
|
|
91
|
+
path: relativePath,
|
|
92
|
+
templateId: "cohorte/manifest/v1",
|
|
93
|
+
renderedSha256: sha256Hex(bytes)
|
|
94
|
+
} : manifest?.generated.find((item) => item.path === relativePath);
|
|
95
|
+
files.push({
|
|
96
|
+
path: relativePath,
|
|
97
|
+
class: generated ? "generated" : "human",
|
|
98
|
+
sha256: sha256Hex(bytes),
|
|
99
|
+
...generated?.templateId ? { templateId: generated.templateId } : {}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
await visit(stateRoot);
|
|
108
|
+
return {
|
|
109
|
+
manifest,
|
|
110
|
+
files
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function diffStates(desired, actual, clock) {
|
|
114
|
+
const desiredByPath = new Map(desired.files.map((file) => [file.path, file]));
|
|
115
|
+
const actualByPath = new Map(actual.files.map((file) => [file.path, file]));
|
|
116
|
+
const recorded = new Map((actual.manifest?.generated ?? []).map((file) => [file.path, file.renderedSha256]));
|
|
117
|
+
const entries = [.../* @__PURE__ */ new Set([...desiredByPath.keys(), ...actualByPath.keys()])].sort().flatMap((path) => {
|
|
118
|
+
const expected = desiredByPath.get(path);
|
|
119
|
+
const found = actualByPath.get(path);
|
|
120
|
+
const recordedSha256 = recorded.get(path);
|
|
121
|
+
if (!found && expected) return [{
|
|
122
|
+
target: path,
|
|
123
|
+
class: expected.class,
|
|
124
|
+
diff: "absent",
|
|
125
|
+
desiredSha256: expected.sha256,
|
|
126
|
+
...recordedSha256 ? { recordedSha256 } : {},
|
|
127
|
+
detail: "desired file is absent"
|
|
128
|
+
}];
|
|
129
|
+
if (found && !expected) return [{
|
|
130
|
+
target: path,
|
|
131
|
+
class: found.class,
|
|
132
|
+
diff: found.class === "generated" ? "potential-deletion" : "unknown",
|
|
133
|
+
actualSha256: found.sha256,
|
|
134
|
+
...recordedSha256 ? { recordedSha256 } : {},
|
|
135
|
+
detail: "file exists but is not desired"
|
|
136
|
+
}];
|
|
137
|
+
if (!found || !expected) throw new Error("unreachable drift entry");
|
|
138
|
+
if (found.sha256 === expected.sha256) return [];
|
|
139
|
+
if (found.class === "human") return [{
|
|
140
|
+
target: path,
|
|
141
|
+
class: found.class,
|
|
142
|
+
diff: "human-change",
|
|
143
|
+
desiredSha256: expected.sha256,
|
|
144
|
+
actualSha256: found.sha256,
|
|
145
|
+
...recordedSha256 ? { recordedSha256 } : {},
|
|
146
|
+
detail: "content changed in a human-owned file"
|
|
147
|
+
}];
|
|
148
|
+
if (recordedSha256 === void 0 || found.sha256 === recordedSha256) return [{
|
|
149
|
+
target: path,
|
|
150
|
+
class: expected.class,
|
|
151
|
+
diff: "expected-change",
|
|
152
|
+
desiredSha256: expected.sha256,
|
|
153
|
+
actualSha256: found.sha256,
|
|
154
|
+
...recordedSha256 ? { recordedSha256 } : {},
|
|
155
|
+
detail: "generated content differs from the previous render"
|
|
156
|
+
}];
|
|
157
|
+
return [{
|
|
158
|
+
target: path,
|
|
159
|
+
class: found.class,
|
|
160
|
+
diff: "conflict",
|
|
161
|
+
desiredSha256: expected.sha256,
|
|
162
|
+
actualSha256: found.sha256,
|
|
163
|
+
recordedSha256,
|
|
164
|
+
detail: "content changed since the recorded render"
|
|
165
|
+
}];
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
schemaVersion: 1,
|
|
169
|
+
generatedAt: clock.now(),
|
|
170
|
+
entries
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region ../../packages/project-model/src/init/index.ts
|
|
175
|
+
async function planInit(options) {
|
|
176
|
+
if (options.semantic) throw new Error("configuration/phase-not-available: semantic init is not available in V3.0");
|
|
177
|
+
const root = options.root;
|
|
178
|
+
const files = [
|
|
179
|
+
{
|
|
180
|
+
path: "project.yaml",
|
|
181
|
+
class: "generated",
|
|
182
|
+
content: stringify(options.model),
|
|
183
|
+
templateId: "project-model/v1"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
path: "config.yaml",
|
|
187
|
+
class: "human",
|
|
188
|
+
content: "schemaVersion: 1\n"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
path: "ownership.yaml",
|
|
192
|
+
class: "human",
|
|
193
|
+
content: "surfaces: {}\n"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
path: ".gitignore",
|
|
197
|
+
class: "generated",
|
|
198
|
+
content: "state/\nruns/\n",
|
|
199
|
+
templateId: "cohorte/gitignore/v1"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
path: "generated/.gitkeep",
|
|
203
|
+
class: "generated",
|
|
204
|
+
content: "",
|
|
205
|
+
templateId: "cohorte/generated-dir/v1"
|
|
206
|
+
}
|
|
207
|
+
];
|
|
208
|
+
const initFiles = [];
|
|
209
|
+
for (const file of files) {
|
|
210
|
+
let exists = false;
|
|
211
|
+
try {
|
|
212
|
+
await readFile(join(root, ".cohorte", file.path));
|
|
213
|
+
exists = true;
|
|
214
|
+
} catch {}
|
|
215
|
+
const renderedSha256 = sha256Hex(file.content);
|
|
216
|
+
initFiles.push({
|
|
217
|
+
path: file.path,
|
|
218
|
+
action: exists ? "keep-existing" : "create",
|
|
219
|
+
class: file.class,
|
|
220
|
+
content: file.content,
|
|
221
|
+
...file.templateId === void 0 ? {} : { templateId: file.templateId },
|
|
222
|
+
...file.class === "generated" ? {
|
|
223
|
+
templateSha256: renderedSha256,
|
|
224
|
+
renderedSha256
|
|
225
|
+
} : {}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
const generated = initFiles.filter((file) => file.class === "generated").map((file) => ({
|
|
229
|
+
path: file.path,
|
|
230
|
+
templateId: file.templateId ?? "builtin",
|
|
231
|
+
templateSha256: file.templateSha256 ?? sha256Hex(file.content),
|
|
232
|
+
renderedSha256: file.renderedSha256 ?? sha256Hex(file.content)
|
|
233
|
+
}));
|
|
234
|
+
return {
|
|
235
|
+
projectRoot: root,
|
|
236
|
+
model: options.model,
|
|
237
|
+
files: initFiles,
|
|
238
|
+
manifest: {
|
|
239
|
+
schemaVersion: 1,
|
|
240
|
+
cohorteVersion: options.cohorteVersion,
|
|
241
|
+
createdWith: options.cohorteVersion,
|
|
242
|
+
protocol: {
|
|
243
|
+
min: "3.0",
|
|
244
|
+
max: "3.x"
|
|
245
|
+
},
|
|
246
|
+
stateSchemaVersion: 1,
|
|
247
|
+
generated
|
|
248
|
+
},
|
|
249
|
+
warnings: []
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async function applyInit(plan) {
|
|
253
|
+
await mkdir(join(plan.projectRoot, ".cohorte"), {
|
|
254
|
+
recursive: true,
|
|
255
|
+
mode: 448
|
|
256
|
+
});
|
|
257
|
+
const written = [];
|
|
258
|
+
const kept = [];
|
|
259
|
+
for (const file of plan.files) {
|
|
260
|
+
const target = join(plan.projectRoot, ".cohorte", file.path);
|
|
261
|
+
if (file.action === "keep-existing") {
|
|
262
|
+
kept.push(file.path);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
await mkdir(join(target, ".."), {
|
|
266
|
+
recursive: true,
|
|
267
|
+
mode: 448
|
|
268
|
+
});
|
|
269
|
+
await writeFile(target, file.content, { mode: 384 });
|
|
270
|
+
written.push(file.path);
|
|
271
|
+
}
|
|
272
|
+
const manifestPath = join(plan.projectRoot, ".cohorte", "manifest.yaml");
|
|
273
|
+
try {
|
|
274
|
+
await readFile(manifestPath);
|
|
275
|
+
kept.push("manifest.yaml");
|
|
276
|
+
} catch {
|
|
277
|
+
await writeFile(manifestPath, stringify(plan.manifest), { mode: 384 });
|
|
278
|
+
written.push("manifest.yaml");
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
written,
|
|
282
|
+
kept
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
//#endregion
|
|
286
|
+
//#region ../../packages/project-model/src/reconcile/index.ts
|
|
287
|
+
/** READ-ONLY. */
|
|
288
|
+
async function planReconcile(options) {
|
|
289
|
+
const model = await options.scan(options.root);
|
|
290
|
+
let stableModel = model;
|
|
291
|
+
try {
|
|
292
|
+
const generatedAt = parse(await readFile(join(options.root, ".cohorte", "project.yaml"), "utf8")).provenance?.generatedAt;
|
|
293
|
+
const stableGeneratedAt = generatedAt instanceof Date ? generatedAt.toISOString() : typeof generatedAt === "string" ? generatedAt : void 0;
|
|
294
|
+
if (stableGeneratedAt !== void 0) stableModel = {
|
|
295
|
+
...model,
|
|
296
|
+
provenance: {
|
|
297
|
+
...model.provenance,
|
|
298
|
+
generatedAt: stableGeneratedAt
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
} catch {}
|
|
302
|
+
const drift = diffStates(deriveDesiredState({
|
|
303
|
+
model: stableModel,
|
|
304
|
+
config: DEFAULT_CONFIG,
|
|
305
|
+
cohorteVersion: options.cohorteVersion,
|
|
306
|
+
skills: {}
|
|
307
|
+
}), await readActualState(options.root), options.clock);
|
|
308
|
+
const operations = drift.entries.map((entry) => {
|
|
309
|
+
return {
|
|
310
|
+
op: entry.diff === "absent" ? "create" : entry.diff === "expected-change" ? "replace" : entry.diff === "potential-deletion" ? "delete" : entry.diff === "unknown" ? "keep" : "ask",
|
|
311
|
+
target: entry.target,
|
|
312
|
+
diff: entry.diff,
|
|
313
|
+
reason: entry.detail
|
|
314
|
+
};
|
|
315
|
+
});
|
|
316
|
+
return {
|
|
317
|
+
schemaVersion: 1,
|
|
318
|
+
cohorteVersion: options.cohorteVersion,
|
|
319
|
+
generatedAt: drift.generatedAt,
|
|
320
|
+
drift,
|
|
321
|
+
operations,
|
|
322
|
+
conflicts: drift.entries.filter((entry) => entry.diff === "conflict").map((entry) => entry.target),
|
|
323
|
+
applyAvailable: false
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region ../../packages/project-model/src/scan/index.ts
|
|
328
|
+
async function filesUnder(root) {
|
|
329
|
+
const found = [];
|
|
330
|
+
const visit = async (dir) => {
|
|
331
|
+
try {
|
|
332
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
333
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
334
|
+
if (entry.name === ".git" || entry.name === "node_modules" || entry.name === ".cohorte" || entry.name === ".pi" || entry.name === ".build" || entry.name === "dist" || entry.name === "coverage") continue;
|
|
335
|
+
const path = join(dir, entry.name);
|
|
336
|
+
if (entry.isDirectory()) await visit(path);
|
|
337
|
+
else if (entry.isFile()) found.push(relative(root, path).split("\\").join("/"));
|
|
338
|
+
}
|
|
339
|
+
} catch {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
await visit(root);
|
|
344
|
+
return found.sort();
|
|
345
|
+
}
|
|
346
|
+
function field(value, detector, sources, className = "observed") {
|
|
347
|
+
return {
|
|
348
|
+
value,
|
|
349
|
+
class: className,
|
|
350
|
+
provenance: {
|
|
351
|
+
detector,
|
|
352
|
+
sources
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
async function scanRepository(root, options) {
|
|
357
|
+
const files = await filesUnder(root);
|
|
358
|
+
const packagePath = join(root, "package.json");
|
|
359
|
+
let pkg = {};
|
|
360
|
+
try {
|
|
361
|
+
pkg = JSON.parse(await readFile(packagePath, "utf8"));
|
|
362
|
+
} catch {}
|
|
363
|
+
const ext = /* @__PURE__ */ new Map([
|
|
364
|
+
[".ts", "typescript"],
|
|
365
|
+
[".tsx", "typescript"],
|
|
366
|
+
[".js", "javascript"],
|
|
367
|
+
[".jsx", "javascript"],
|
|
368
|
+
[".py", "python"],
|
|
369
|
+
[".go", "go"],
|
|
370
|
+
[".rs", "rust"],
|
|
371
|
+
[".java", "java"]
|
|
372
|
+
]);
|
|
373
|
+
const languages = [...new Set(files.map((file) => ext.get(file.slice(file.lastIndexOf(".")))).filter((value) => value !== void 0))].sort();
|
|
374
|
+
const supportedLocks = [
|
|
375
|
+
"pnpm-lock.yaml",
|
|
376
|
+
"package-lock.json",
|
|
377
|
+
"yarn.lock",
|
|
378
|
+
"bun.lockb"
|
|
379
|
+
];
|
|
380
|
+
const locks = files.filter((file) => supportedLocks.includes(file));
|
|
381
|
+
const lock = locks.length === 1 ? locks[0] : void 0;
|
|
382
|
+
const packageManager = lock === void 0 ? pkg.packageManager?.split("@")[0] ?? null : lock === "pnpm-lock.yaml" ? "pnpm" : lock === "package-lock.json" ? "npm" : lock === "yarn.lock" ? "yarn" : "bun";
|
|
383
|
+
const deps = {
|
|
384
|
+
...pkg.dependencies ?? {},
|
|
385
|
+
...pkg.devDependencies ?? {}
|
|
386
|
+
};
|
|
387
|
+
const frameworks = Object.keys(deps).filter((name) => /^(react|vue|angular|svelte|next|nuxt|express|nestjs|fastify|vite|vitest|jest|playwright)/i.test(name)).sort();
|
|
388
|
+
const scripts = pkg.scripts ?? {};
|
|
389
|
+
const commands = {};
|
|
390
|
+
for (const name of Object.keys(scripts).sort()) {
|
|
391
|
+
const command = scripts[name];
|
|
392
|
+
if (command !== void 0) commands[name] = field(command.split(/\s+/u), "package-json", ["package.json"]);
|
|
393
|
+
}
|
|
394
|
+
const testLocations = files.filter((file) => /(^|\/)(test|tests|__tests__)(\/|$)|\.(test|spec)\./u.test(file));
|
|
395
|
+
const surfaces = {};
|
|
396
|
+
const topLevel = [...new Set(files.map((file) => file.split("/")[0]).filter((name) => Boolean(name && !name.startsWith("."))))];
|
|
397
|
+
for (const name of topLevel.sort()) {
|
|
398
|
+
const paths = files.filter((file) => file === name || file.startsWith(`${name}/`));
|
|
399
|
+
if (paths.length) surfaces[name.replace(/[^A-Za-z0-9_.-]/g, "_")] = { paths: field([`${name}/**`], "workspace-layout", paths) };
|
|
400
|
+
}
|
|
401
|
+
const id = pkg.name?.replace(/^@[^/]+\//u, "") ?? (relative(process.cwd(), root).replace(/[^A-Za-z0-9_.-]/g, "_") || "project");
|
|
402
|
+
const unknowns = locks.length > 1 ? [{
|
|
403
|
+
id: "package-manager",
|
|
404
|
+
question: "more than one supported lockfile was detected; which package manager is authoritative?",
|
|
405
|
+
candidates: [...locks].sort(),
|
|
406
|
+
sources: [...locks].sort()
|
|
407
|
+
}] : [];
|
|
408
|
+
return {
|
|
409
|
+
schemaVersion: 1,
|
|
410
|
+
project: {
|
|
411
|
+
id: field(id, "package-json", ["package.json"], "human"),
|
|
412
|
+
root: field(root, "filesystem", [])
|
|
413
|
+
},
|
|
414
|
+
stack: {
|
|
415
|
+
languages: field(languages, "file-extensions", files),
|
|
416
|
+
packageManager: field(packageManager, "lockfile", lock ? [lock] : []),
|
|
417
|
+
frameworks: field(frameworks, "package-json", ["package.json"])
|
|
418
|
+
},
|
|
419
|
+
surfaces,
|
|
420
|
+
commands,
|
|
421
|
+
testStrategy: field({
|
|
422
|
+
runner: scripts.test ? scripts.test.split(/\s+/u)[0] ?? null : null,
|
|
423
|
+
locations: testLocations
|
|
424
|
+
}, "package-json", ["package.json"]),
|
|
425
|
+
deploymentHints: field(files.filter((file) => /(^|\/)(Dockerfile|Procfile|vercel\.json|fly\.toml)$/u.test(file)), "file-presence", files),
|
|
426
|
+
ownership: field({ codeowners: files.includes(".github/CODEOWNERS") ? ".github/CODEOWNERS" : null }, "file-presence", [".github/CODEOWNERS"]),
|
|
427
|
+
risks: packageManager === null ? [{
|
|
428
|
+
id: "missing-lockfile",
|
|
429
|
+
severity: "medium",
|
|
430
|
+
message: "no supported lockfile was detected",
|
|
431
|
+
sources: []
|
|
432
|
+
}] : [],
|
|
433
|
+
conventions: field(files.filter((file) => /(^|\/)(CONTRIBUTING|conventions)\.(md|yaml)$/iu.test(file)), "file-presence", files),
|
|
434
|
+
generatedArtifacts: field(["manifest.yaml", "project.yaml"], "manifest", [".cohorte/manifest.yaml"]),
|
|
435
|
+
unknowns,
|
|
436
|
+
provenance: {
|
|
437
|
+
generatedAt: options.clock.now(),
|
|
438
|
+
toolVersion: options.toolVersion,
|
|
439
|
+
analysis: "deterministic"
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
//#endregion
|
|
444
|
+
export { planInit as i, planReconcile as n, applyInit as r, scanRepository as t };
|
|
445
|
+
|
|
446
|
+
//# sourceMappingURL=src-JxawBQBn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"src-JxawBQBn.mjs","names":[],"sources":["../../../../packages/project-model/src/desired/index.ts","../../../../packages/project-model/src/drift/index.ts","../../../../packages/project-model/src/init/index.ts","../../../../packages/project-model/src/reconcile/index.ts","../../../../packages/project-model/src/scan/index.ts"],"sourcesContent":["import { canonicalJson, type JsonValue, sha256Hex } from '@cohorte/base';\nimport type { CohorteConfig } from '@cohorte/config/schema';\nimport { stringify } from 'yaml';\nimport type { DesiredState, ProjectModel } from '../contract.ts';\n\nexport interface DesiredStateInput {\n model: ProjectModel;\n config: CohorteConfig;\n cohorteVersion: string;\n /** skill id -> version */\n skills: Readonly<Record<string, string>>;\n}\n\nexport function deriveDesiredState(input: DesiredStateInput): DesiredState {\n const modelYaml = stringify(input.model);\n const builtin: Record<string, string> = {\n 'manifest.yaml': stringify({\n schemaVersion: 1,\n cohorteVersion: input.cohorteVersion,\n createdWith: input.cohorteVersion,\n protocol: { min: '3.0', max: '3.x' },\n stateSchemaVersion: 1,\n generated: [\n {\n path: 'project.yaml',\n templateId: 'project-model/v1',\n templateSha256: sha256Hex(modelYaml),\n renderedSha256: sha256Hex(modelYaml),\n },\n {\n path: '.gitignore',\n templateId: 'cohorte/gitignore/v1',\n templateSha256: sha256Hex('state/\\nruns/\\n'),\n renderedSha256: sha256Hex('state/\\nruns/\\n'),\n },\n {\n path: 'generated/.gitkeep',\n templateId: 'cohorte/generated-dir/v1',\n templateSha256: sha256Hex(''),\n renderedSha256: sha256Hex(''),\n },\n ],\n }),\n 'project.yaml': modelYaml,\n 'config.yaml': 'schemaVersion: 1\\n',\n 'ownership.yaml': 'surfaces: {}\\n',\n '.gitignore': 'state/\\nruns/\\n',\n 'generated/.gitkeep': '',\n };\n const files = new Set(Object.keys(builtin));\n for (const path of input.model.generatedArtifacts.value) files.add(path.replace(/^\\.cohorte\\//u, ''));\n const stateFiles = [...files].sort().map((path) => ({\n path,\n class: path === 'config.yaml' || path === 'ownership.yaml' ? ('human' as const) : ('generated' as const),\n sha256: sha256Hex(\n builtin[path] ??\n canonicalJson({ path, model: input.model, config: input.config, skills: input.skills } as unknown as JsonValue),\n ),\n ...(input.skills[path] === undefined ? {} : { templateId: path }),\n }));\n return { cohorteVersion: input.cohorteVersion, files: stateFiles };\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport { type Clock, sha256Hex } from '@cohorte/base';\nimport { parse } from 'yaml';\nimport type { ActualState, DesiredState, DriftEntry, DriftReport } from '../contract.ts';\n\nexport async function readActualState(root: string): Promise<ActualState> {\n const stateRoot = join(root, '.cohorte');\n let manifest = null;\n try {\n manifest = parse(await readFile(join(stateRoot, 'manifest.yaml'), 'utf8')) as ActualState['manifest'];\n } catch {\n return { manifest: null, files: [] };\n }\n const files: ActualState['files'] = [];\n const visit = async (dir: string): Promise<void> => {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n if (entry.name === 'state' || entry.name === 'runs') continue;\n const path = join(dir, entry.name);\n if (entry.isDirectory()) await visit(path);\n else if (entry.isFile()) {\n const bytes = await readFile(path);\n const relativePath = relative(stateRoot, path).split('\\\\').join('/');\n const generated =\n relativePath === 'manifest.yaml'\n ? { path: relativePath, templateId: 'cohorte/manifest/v1', renderedSha256: sha256Hex(bytes) }\n : manifest?.generated.find((item) => item.path === relativePath);\n files.push({\n path: relativePath,\n class: generated ? 'generated' : 'human',\n sha256: sha256Hex(bytes),\n ...(generated?.templateId ? { templateId: generated.templateId } : {}),\n });\n }\n }\n } catch {\n return;\n }\n };\n await visit(stateRoot);\n return { manifest, files };\n}\n\nexport function diffStates(desired: DesiredState, actual: ActualState, clock: Clock): DriftReport {\n const desiredByPath = new Map(desired.files.map((file) => [file.path, file]));\n const actualByPath = new Map(actual.files.map((file) => [file.path, file]));\n const recorded = new Map((actual.manifest?.generated ?? []).map((file) => [file.path, file.renderedSha256]));\n const paths = [...new Set([...desiredByPath.keys(), ...actualByPath.keys()])].sort();\n const entries = paths.flatMap((path): DriftEntry[] => {\n const expected = desiredByPath.get(path);\n const found = actualByPath.get(path);\n const recordedSha256 = recorded.get(path);\n if (!found && expected)\n return [\n {\n target: path,\n class: expected.class,\n diff: 'absent' as const,\n desiredSha256: expected.sha256,\n ...(recordedSha256 ? { recordedSha256 } : {}),\n detail: 'desired file is absent',\n },\n ];\n if (found && !expected)\n return [\n {\n target: path,\n class: found.class,\n diff: found.class === 'generated' ? ('potential-deletion' as const) : ('unknown' as const),\n actualSha256: found.sha256,\n ...(recordedSha256 ? { recordedSha256 } : {}),\n detail: 'file exists but is not desired',\n },\n ];\n if (!found || !expected) throw new Error('unreachable drift entry');\n if (found.sha256 === expected.sha256) return [];\n if (found.class === 'human')\n return [\n {\n target: path,\n class: found.class,\n diff: 'human-change' as const,\n desiredSha256: expected.sha256,\n actualSha256: found.sha256,\n ...(recordedSha256 ? { recordedSha256 } : {}),\n detail: 'content changed in a human-owned file',\n },\n ];\n if (recordedSha256 === undefined || found.sha256 === recordedSha256)\n return [\n {\n target: path,\n class: expected.class,\n diff: 'expected-change' as const,\n desiredSha256: expected.sha256,\n actualSha256: found.sha256,\n ...(recordedSha256 ? { recordedSha256 } : {}),\n detail: 'generated content differs from the previous render',\n },\n ];\n return [\n {\n target: path,\n class: found.class,\n diff: 'conflict' as const,\n desiredSha256: expected.sha256,\n actualSha256: found.sha256,\n recordedSha256,\n detail: 'content changed since the recorded render',\n },\n ];\n });\n return { schemaVersion: 1, generatedAt: clock.now(), entries };\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { sha256Hex } from '@cohorte/base';\nimport { stringify } from 'yaml';\nimport type { InitPlan, ProjectModel } from '../contract.ts';\n\nexport interface PlanInitOptions {\n root: string;\n model: ProjectModel;\n cohorteVersion: string;\n /** refused in V3.0: `configuration/phase-not-available` */\n semantic?: boolean;\n}\n\nexport async function planInit(options: PlanInitOptions): Promise<InitPlan> {\n if (options.semantic) throw new Error('configuration/phase-not-available: semantic init is not available in V3.0');\n const root = options.root;\n const modelYaml = stringify(options.model);\n const files = [\n { path: 'project.yaml', class: 'generated' as const, content: modelYaml, templateId: 'project-model/v1' },\n { path: 'config.yaml', class: 'human' as const, content: 'schemaVersion: 1\\n' },\n { path: 'ownership.yaml', class: 'human' as const, content: 'surfaces: {}\\n' },\n {\n path: '.gitignore',\n class: 'generated' as const,\n content: 'state/\\nruns/\\n',\n templateId: 'cohorte/gitignore/v1',\n },\n {\n path: 'generated/.gitkeep',\n class: 'generated' as const,\n content: '',\n templateId: 'cohorte/generated-dir/v1',\n },\n ];\n const initFiles: InitPlan['files'] = [];\n for (const file of files) {\n let exists = false;\n try {\n await readFile(join(root, '.cohorte', file.path));\n exists = true;\n } catch {\n /* create */\n }\n const renderedSha256 = sha256Hex(file.content);\n initFiles.push({\n path: file.path,\n action: exists ? 'keep-existing' : 'create',\n class: file.class,\n content: file.content,\n ...(file.templateId === undefined ? {} : { templateId: file.templateId }),\n ...(file.class === 'generated' ? { templateSha256: renderedSha256, renderedSha256 } : {}),\n });\n }\n const generated = initFiles\n .filter((file) => file.class === 'generated')\n .map((file) => ({\n path: file.path,\n templateId: file.templateId ?? 'builtin',\n templateSha256: file.templateSha256 ?? sha256Hex(file.content),\n renderedSha256: file.renderedSha256 ?? sha256Hex(file.content),\n }));\n return {\n projectRoot: root,\n model: options.model,\n files: initFiles,\n manifest: {\n schemaVersion: 1,\n cohorteVersion: options.cohorteVersion,\n createdWith: options.cohorteVersion,\n protocol: { min: '3.0', max: '3.x' },\n stateSchemaVersion: 1,\n generated,\n },\n warnings: [],\n };\n}\n\nexport async function applyInit(plan: InitPlan): Promise<{ written: string[]; kept: string[] }> {\n await mkdir(join(plan.projectRoot, '.cohorte'), { recursive: true, mode: 0o700 });\n const written: string[] = [];\n const kept: string[] = [];\n for (const file of plan.files) {\n const target = join(plan.projectRoot, '.cohorte', file.path);\n if (file.action === 'keep-existing') {\n kept.push(file.path);\n continue;\n }\n await mkdir(join(target, '..'), { recursive: true, mode: 0o700 });\n await writeFile(target, file.content, { mode: 0o600 });\n written.push(file.path);\n }\n const manifestPath = join(plan.projectRoot, '.cohorte', 'manifest.yaml');\n try {\n await readFile(manifestPath);\n kept.push('manifest.yaml');\n } catch {\n await writeFile(manifestPath, stringify(plan.manifest), { mode: 0o600 });\n written.push('manifest.yaml');\n }\n return { written, kept };\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Clock } from '@cohorte/base';\nimport { DEFAULT_CONFIG } from '@cohorte/config/schema';\nimport { parse } from 'yaml';\nimport type { ReconcilePlan, RepositoryScanner } from '../contract.ts';\nimport { deriveDesiredState } from '../desired/index.ts';\nimport { diffStates, readActualState } from '../drift/index.ts';\n\nexport interface PlanReconcileOptions {\n root: string;\n /** injected: this area never imports the scan area */\n scan: RepositoryScanner;\n cohorteVersion: string;\n clock: Clock;\n}\n\n/** READ-ONLY. */\nexport async function planReconcile(options: PlanReconcileOptions): Promise<ReconcilePlan> {\n const model = await options.scan(options.root);\n // `provenance.generatedAt` is metadata, not project content. Reuse the\n // previous generated timestamp so a read-only reconcile is stable; changed\n // model fields still alter the rendered project and manifest hashes.\n let stableModel = model;\n try {\n const actual = parse(await readFile(join(options.root, '.cohorte', 'project.yaml'), 'utf8')) as {\n provenance?: { generatedAt?: unknown };\n };\n const generatedAt = actual.provenance?.generatedAt;\n const stableGeneratedAt =\n generatedAt instanceof Date\n ? generatedAt.toISOString()\n : typeof generatedAt === 'string'\n ? generatedAt\n : undefined;\n if (stableGeneratedAt !== undefined) {\n stableModel = {\n ...model,\n provenance: {\n ...model.provenance,\n generatedAt: stableGeneratedAt as typeof model.provenance.generatedAt,\n },\n };\n }\n } catch {\n /* an uninitialised or malformed project is represented by the normal drift entries */\n }\n const desired = deriveDesiredState({\n model: stableModel,\n config: DEFAULT_CONFIG,\n cohorteVersion: options.cohorteVersion,\n skills: {},\n });\n const actual = await readActualState(options.root);\n const drift = diffStates(desired, actual, options.clock);\n const operations = drift.entries.map((entry) => {\n const op =\n entry.diff === 'absent'\n ? 'create'\n : entry.diff === 'expected-change'\n ? 'replace'\n : entry.diff === 'potential-deletion'\n ? 'delete'\n : entry.diff === 'unknown'\n ? 'keep'\n : 'ask';\n return { op, target: entry.target, diff: entry.diff, reason: entry.detail } as const;\n });\n return {\n schemaVersion: 1,\n cohorteVersion: options.cohorteVersion,\n generatedAt: drift.generatedAt,\n drift,\n operations,\n conflicts: drift.entries.filter((entry) => entry.diff === 'conflict').map((entry) => entry.target),\n applyAvailable: false,\n };\n}\n","// Deterministic repository scan: bounded filesystem observations, never guessed semantics.\n\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport type { Clock } from '@cohorte/base';\nimport type { ProjectModel } from '../contract.ts';\n\nexport interface ScanOptions {\n clock: Clock;\n /** stamped into `provenance.toolVersion` */\n toolVersion: string;\n}\n\nasync function filesUnder(root: string): Promise<string[]> {\n const found: string[] = [];\n const visit = async (dir: string): Promise<void> => {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n if (\n entry.name === '.git' ||\n entry.name === 'node_modules' ||\n entry.name === '.cohorte' ||\n entry.name === '.pi' ||\n entry.name === '.build' ||\n entry.name === 'dist' ||\n entry.name === 'coverage'\n )\n continue;\n const path = join(dir, entry.name);\n if (entry.isDirectory()) await visit(path);\n else if (entry.isFile()) found.push(relative(root, path).split('\\\\').join('/'));\n }\n } catch {\n return;\n }\n };\n await visit(root);\n return found.sort();\n}\n\nfunction field<T>(\n value: T,\n detector: string,\n sources: string[],\n className: ProjectModel['project']['id']['class'] = 'observed',\n) {\n return { value, class: className, provenance: { detector, sources } };\n}\n\nexport async function scanRepository(root: string, options: ScanOptions): Promise<ProjectModel> {\n const files = await filesUnder(root);\n const packagePath = join(root, 'package.json');\n let pkg: {\n name?: string;\n packageManager?: string;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n } = {};\n try {\n pkg = JSON.parse(await readFile(packagePath, 'utf8')) as typeof pkg;\n } catch {\n /* absent or invalid package.json is an observation */\n }\n const ext = new Map<string, string>([\n ['.ts', 'typescript'],\n ['.tsx', 'typescript'],\n ['.js', 'javascript'],\n ['.jsx', 'javascript'],\n ['.py', 'python'],\n ['.go', 'go'],\n ['.rs', 'rust'],\n ['.java', 'java'],\n ]);\n const languages = [\n ...new Set(\n files\n .map((file) => ext.get(file.slice(file.lastIndexOf('.'))))\n .filter((value): value is string => value !== undefined),\n ),\n ].sort();\n const supportedLocks = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock', 'bun.lockb'];\n const locks = files.filter((file) => supportedLocks.includes(file));\n const lock = locks.length === 1 ? locks[0] : undefined;\n const packageManager =\n lock === undefined\n ? (pkg.packageManager?.split('@')[0] ?? null)\n : lock === 'pnpm-lock.yaml'\n ? 'pnpm'\n : lock === 'package-lock.json'\n ? 'npm'\n : lock === 'yarn.lock'\n ? 'yarn'\n : 'bun';\n const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n const frameworks = Object.keys(deps)\n .filter((name) =>\n /^(react|vue|angular|svelte|next|nuxt|express|nestjs|fastify|vite|vitest|jest|playwright)/i.test(name),\n )\n .sort();\n const scripts = pkg.scripts ?? {};\n const commands: Record<string, ReturnType<typeof field<string[]>>> = {};\n for (const name of Object.keys(scripts).sort()) {\n const command = scripts[name];\n if (command !== undefined) commands[name] = field(command.split(/\\s+/u), 'package-json', ['package.json']);\n }\n const testLocations = files.filter((file) => /(^|\\/)(test|tests|__tests__)(\\/|$)|\\.(test|spec)\\./u.test(file));\n const surfaces: ProjectModel['surfaces'] = {};\n const topLevel: string[] = [\n ...new Set(\n files.map((file) => file.split('/')[0]).filter((name): name is string => Boolean(name && !name.startsWith('.'))),\n ),\n ];\n for (const name of topLevel.sort()) {\n const paths = files.filter((file) => file === name || file.startsWith(`${name}/`));\n if (paths.length)\n surfaces[name.replace(/[^A-Za-z0-9_.-]/g, '_')] = { paths: field([`${name}/**`], 'workspace-layout', paths) };\n }\n const id =\n pkg.name?.replace(/^@[^/]+\\//u, '') ??\n (relative(process.cwd(), root).replace(/[^A-Za-z0-9_.-]/g, '_') || 'project');\n const unknowns =\n locks.length > 1\n ? [\n {\n id: 'package-manager',\n question: 'more than one supported lockfile was detected; which package manager is authoritative?',\n candidates: [...locks].sort(),\n sources: [...locks].sort(),\n },\n ]\n : [];\n return {\n schemaVersion: 1,\n project: { id: field(id, 'package-json', ['package.json'], 'human'), root: field(root, 'filesystem', []) },\n stack: {\n languages: field(languages, 'file-extensions', files),\n packageManager: field(packageManager, 'lockfile', lock ? [lock] : []),\n frameworks: field(frameworks, 'package-json', ['package.json']),\n },\n surfaces,\n commands,\n testStrategy: field(\n { runner: scripts.test ? (scripts.test.split(/\\s+/u)[0] ?? null) : null, locations: testLocations },\n 'package-json',\n ['package.json'],\n ),\n deploymentHints: field(\n files.filter((file) => /(^|\\/)(Dockerfile|Procfile|vercel\\.json|fly\\.toml)$/u.test(file)),\n 'file-presence',\n files,\n ),\n ownership: field(\n { codeowners: files.includes('.github/CODEOWNERS') ? '.github/CODEOWNERS' : null },\n 'file-presence',\n ['.github/CODEOWNERS'],\n ),\n risks:\n packageManager === null\n ? [{ id: 'missing-lockfile', severity: 'medium', message: 'no supported lockfile was detected', sources: [] }]\n : [],\n conventions: field(\n files.filter((file) => /(^|\\/)(CONTRIBUTING|conventions)\\.(md|yaml)$/iu.test(file)),\n 'file-presence',\n files,\n ),\n generatedArtifacts: field(['manifest.yaml', 'project.yaml'], 'manifest', ['.cohorte/manifest.yaml']),\n unknowns,\n provenance: { generatedAt: options.clock.now(), toolVersion: options.toolVersion, analysis: 'deterministic' },\n };\n}\n"],"mappings":"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"}
|