cohorte 3.0.0-dev.0 → 3.0.0-dev.2
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/assets/manifest.json +4 -4
- package/assets/schemas/reconcile-plan.schema.json +1 -2
- package/dist/agent-host.mjs +2142 -5
- package/dist/agent-host.mjs.map +1 -1
- package/dist/bundle-manifest.json +38 -36
- package/dist/chunks/{auth-D8rCVY33.mjs → auth-ybswxUPK.mjs} +54 -2
- package/dist/chunks/auth-ybswxUPK.mjs.map +1 -0
- package/dist/chunks/{brainstorm-CU10T1tm.mjs → brainstorm-BwKA6yZP.mjs} +2 -2
- package/dist/chunks/{brainstorm-CU10T1tm.mjs.map → brainstorm-BwKA6yZP.mjs.map} +1 -1
- package/dist/chunks/{catalogue-BvwSrM_j.mjs → catalogue-B5uFqfzJ.mjs} +2 -2
- package/dist/chunks/{catalogue-BvwSrM_j.mjs.map → catalogue-B5uFqfzJ.mjs.map} +1 -1
- package/dist/chunks/{compose-Cr0hP0qm.mjs → compose-C0rEh5wB.mjs} +3354 -3343
- package/dist/chunks/compose-C0rEh5wB.mjs.map +1 -0
- package/dist/chunks/{contract-B0ra2-bm.mjs → contract-BkMjU1kR.mjs} +2 -2
- package/dist/chunks/{contract-B0ra2-bm.mjs.map → contract-BkMjU1kR.mjs.map} +1 -1
- package/dist/chunks/{contract-vYbwYr0Y.mjs → contract-bQ9FJl4N.mjs} +2 -2
- package/dist/chunks/contract-bQ9FJl4N.mjs.map +1 -0
- package/dist/chunks/{diff-Qw0II0sj.mjs → diff-D_0hBwHl.mjs} +2 -2
- package/dist/chunks/{diff-Qw0II0sj.mjs.map → diff-D_0hBwHl.mjs.map} +1 -1
- package/dist/chunks/{discover-30Ciya_0.mjs → discover-eS01-me1.mjs} +3 -2
- package/dist/chunks/{discover-30Ciya_0.mjs.map → discover-eS01-me1.mjs.map} +1 -1
- package/dist/chunks/{doctor-DfCrKpfT.mjs → doctor-CginW8y8.mjs} +5 -5
- package/dist/chunks/{doctor-DfCrKpfT.mjs.map → doctor-CginW8y8.mjs.map} +1 -1
- package/dist/chunks/init-BUnzIkeE.mjs +63 -0
- package/dist/chunks/init-BUnzIkeE.mjs.map +1 -0
- package/dist/chunks/migrate-DybihKzZ.mjs +33 -0
- package/dist/chunks/migrate-DybihKzZ.mjs.map +1 -0
- package/dist/chunks/{policy-DUIgzF3W.mjs → policy-Clypo20s.mjs} +3 -3
- package/dist/chunks/{policy-DUIgzF3W.mjs.map → policy-Clypo20s.mjs.map} +1 -1
- package/dist/chunks/reconcile-Bhy3IIYo.mjs +50 -0
- package/dist/chunks/reconcile-Bhy3IIYo.mjs.map +1 -0
- package/dist/chunks/scan-8Gy_B6kI.mjs +122 -0
- package/dist/chunks/scan-8Gy_B6kI.mjs.map +1 -0
- package/dist/chunks/src-CmFjh4M0.mjs +928 -0
- package/dist/chunks/src-CmFjh4M0.mjs.map +1 -0
- package/dist/chunks/{src-CfAOde7C.mjs → src-DZazJCp1.mjs} +2 -2
- package/dist/chunks/{src-CfAOde7C.mjs.map → src-DZazJCp1.mjs.map} +1 -1
- package/dist/chunks/{src-CId6kMsY.mjs → src-r0ECB1UJ.mjs} +2 -2
- package/dist/chunks/{src-CId6kMsY.mjs.map → src-r0ECB1UJ.mjs.map} +1 -1
- package/dist/cli.mjs +12 -12
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/auth-D8rCVY33.mjs.map +0 -1
- package/dist/chunks/compose-Cr0hP0qm.mjs.map +0 -1
- package/dist/chunks/contract-vYbwYr0Y.mjs.map +0 -1
- package/dist/chunks/init-Okz4mlhW.mjs +0 -30
- package/dist/chunks/init-Okz4mlhW.mjs.map +0 -1
- package/dist/chunks/migrate-DRoFUg53.mjs +0 -19
- package/dist/chunks/migrate-DRoFUg53.mjs.map +0 -1
- package/dist/chunks/reconcile-BfD785gd.mjs +0 -26
- package/dist/chunks/reconcile-BfD785gd.mjs.map +0 -1
- package/dist/chunks/src-JxawBQBn.mjs +0 -446
- package/dist/chunks/src-JxawBQBn.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contract-vYbwYr0Y.mjs","names":[],"sources":["../../../../packages/project-model/src/contract.ts"],"sourcesContent":["// @cohorte/project-model/contract (PLAN PC-4, spec 12-13): frozen in Wave 0 so that the scan unit and the drift unit\n// never import each other. Types are written by hand and the schema consts annotated: see docs/v3/requests/U0.02.md R1.\nimport { ID_PATTERN, IsoInstant, Sha256 } from '@cohorte/base';\nimport type { Manifest } from '@cohorte/config/schema';\nimport { type TSchema, type TUnsafe, Type } from 'typebox';\n\nconst closed = { additionalProperties: false } as const;\nconst oneOf = <const V extends readonly string[]>(values: V): TUnsafe<V[number]> =>\n Type.Unsafe<V[number]>({ type: 'string', enum: [...values] });\n\n/** Spec 13: every field of the model, and every file of `.cohorte/`, has exactly one class. */\nexport const FIELD_CLASSES = ['human', 'generated', 'derived', 'observed', 'mixed'] as const;\nexport type FieldClass = (typeof FIELD_CLASSES)[number];\n\n/** Spec 13: what the diff engine must tell apart. */\nexport const DIFF_CLASSES = [\n 'absent',\n 'expected-change',\n 'human-change',\n 'conflict',\n 'potential-deletion',\n 'unknown',\n] as const;\nexport type DiffClass = (typeof DIFF_CLASSES)[number];\n\nexport interface Provenance {\n /** which deterministic detector produced the value, e.g. 'package-json', 'lockfile', 'git', 'human-config' */\n detector: string;\n /** repo-relative POSIX paths the value was read from */\n sources: string[];\n /** 0..1; only a SEMANTIC inference carries one (spec 12), and V3.0 has none */\n confidence?: number;\n}\n\nexport interface ModelField<T> {\n value: T;\n class: FieldClass;\n provenance: Provenance;\n}\n\n/** Ambiguity is recorded, NEVER resolved by inventing a value (a command least of all). */\nexport interface Unknown {\n id: string;\n question: string;\n candidates: string[];\n sources: string[];\n}\n\nexport interface Risk {\n id: string;\n severity: 'high' | 'medium' | 'low';\n message: string;\n sources: string[];\n}\n\n/** Spec 12. `commands` are argv arrays, not the shell strings of the spec's example: the product is argv-only (I3, D-25). */\nexport interface ProjectModel {\n schemaVersion: 1;\n project: { id: ModelField<string>; root: ModelField<string> };\n stack: {\n languages: ModelField<string[]>;\n packageManager: ModelField<string | null>;\n frameworks: ModelField<string[]>;\n };\n surfaces: Record<string, { paths: ModelField<string[]> }>;\n commands: Record<string, ModelField<string[]>>;\n testStrategy: ModelField<{ runner: string | null; locations: string[] }>;\n deploymentHints: ModelField<string[]>;\n ownership: ModelField<{ codeowners: string | null }>;\n risks: Risk[];\n conventions: ModelField<string[]>;\n generatedArtifacts: ModelField<string[]>;\n unknowns: Unknown[];\n provenance: { generatedAt: IsoInstant; toolVersion: string; analysis: 'deterministic' };\n}\n\nconst strings = () => Type.Array(Type.String());\nconst ProvenanceSchema = Type.Object(\n {\n detector: Type.String({ minLength: 1 }),\n sources: strings(),\n confidence: Type.Optional(Type.Number({ minimum: 0, maximum: 1 })),\n },\n closed,\n);\nconst field = (value: TSchema) =>\n Type.Object({ value, class: oneOf(FIELD_CLASSES), provenance: ProvenanceSchema }, closed);\nconst nullableString = () => Type.Union([Type.String(), Type.Null()]);\n\n/** [S] `.cohorte/project.yaml`, and the document `cohorte discover` prints. */\nexport const ProjectModel: TUnsafe<ProjectModel> = Type.Unsafe<ProjectModel>(\n Type.Object(\n {\n schemaVersion: Type.Literal(1),\n project: Type.Object({ id: field(Type.String({ pattern: ID_PATTERN })), root: field(Type.String()) }, closed),\n stack: Type.Object(\n { languages: field(strings()), packageManager: field(nullableString()), frameworks: field(strings()) },\n closed,\n ),\n surfaces: Type.Record(Type.String({ pattern: ID_PATTERN }), Type.Object({ paths: field(strings()) }, closed)),\n commands: Type.Record(Type.String(), field(Type.Array(Type.String(), { minItems: 1 }))),\n testStrategy: field(Type.Object({ runner: nullableString(), locations: strings() }, closed)),\n deploymentHints: field(strings()),\n ownership: field(Type.Object({ codeowners: nullableString() }, closed)),\n risks: Type.Array(\n Type.Object(\n {\n id: Type.String(),\n severity: oneOf(['high', 'medium', 'low']),\n message: Type.String(),\n sources: strings(),\n },\n closed,\n ),\n ),\n conventions: field(strings()),\n generatedArtifacts: field(strings()),\n unknowns: Type.Array(\n Type.Object({ id: Type.String(), question: Type.String(), candidates: strings(), sources: strings() }, closed),\n ),\n provenance: Type.Object(\n { generatedAt: IsoInstant, toolVersion: Type.String(), analysis: Type.Literal('deterministic') },\n closed,\n ),\n },\n closed,\n ),\n);\n\n/** One file Cohorte wants to exist under `.cohorte/` (desired) or found there (actual). */\nexport interface StateFile {\n /** `.cohorte`-relative POSIX path */\n path: string;\n class: FieldClass;\n sha256: Sha256;\n templateId?: string;\n}\nexport interface DesiredState {\n cohorteVersion: string;\n files: StateFile[];\n}\nexport interface ActualState {\n /** the manifest found on disk; null = not initialised */\n manifest: Manifest | null;\n files: StateFile[];\n}\n\nexport interface DriftEntry {\n /** `.cohorte`-relative POSIX path, or a JSON pointer into project.yaml prefixed with `project.yaml#` */\n target: string;\n class: FieldClass;\n diff: DiffClass;\n /** what the desired state renders to */\n desiredSha256?: Sha256;\n /** what is on disk */\n actualSha256?: Sha256;\n /** `renderedSha256` of the manifest: the previous-hash guard of spec 14 */\n recordedSha256?: Sha256;\n detail: string;\n}\n\nexport interface DriftReport {\n schemaVersion: 1;\n generatedAt: IsoInstant;\n entries: DriftEntry[];\n}\n\nexport const RECONCILE_OPS = ['create', 'replace', 'delete', 'keep', 'ask'] as const;\n\nexport interface ReconcileOperation {\n op: (typeof RECONCILE_OPS)[number];\n target: string;\n diff: DiffClass;\n reason: string;\n}\n\n/** The `--json` document of `cohorte reconcile --plan`. READ-ONLY: `--apply` is rejected in V3.0. */\nexport interface ReconcilePlan {\n schemaVersion: 1;\n cohorteVersion: string;\n generatedAt: IsoInstant;\n drift: DriftReport;\n /** a human override is never in here as `replace` or `delete`: a collision is a `conflict` */\n operations: ReconcileOperation[];\n /** targets whose diff class is `conflict` */\n conflicts: string[];\n applyAvailable: false;\n}\n\nconst sha = () => Type.Optional(Sha256);\nconst DriftReportSchema = Type.Object(\n {\n schemaVersion: Type.Literal(1),\n generatedAt: IsoInstant,\n entries: Type.Array(\n Type.Object(\n {\n target: Type.String({ minLength: 1 }),\n class: oneOf(FIELD_CLASSES),\n diff: oneOf(DIFF_CLASSES),\n desiredSha256: sha(),\n actualSha256: sha(),\n recordedSha256: sha(),\n detail: Type.String(),\n },\n closed,\n ),\n ),\n },\n closed,\n);\n\n/** [S] */\nexport const DriftReport: TUnsafe<DriftReport> = Type.Unsafe<DriftReport>(DriftReportSchema);\n\n/** [S] published by `gen-schemas` */\nexport const ReconcilePlan: TUnsafe<ReconcilePlan> = Type.Unsafe<ReconcilePlan>(\n Type.Object(\n {\n schemaVersion: Type.Literal(1),\n cohorteVersion: Type.String({ minLength: 1 }),\n generatedAt: IsoInstant,\n drift: DriftReportSchema,\n operations: Type.Array(\n Type.Object(\n {\n op: oneOf(RECONCILE_OPS),\n target: Type.String({ minLength: 1 }),\n diff: oneOf(DIFF_CLASSES),\n reason: Type.String(),\n },\n closed,\n ),\n ),\n conflicts: Type.Array(Type.String()),\n applyAvailable: Type.Literal(false),\n },\n closed,\n ),\n);\n\nexport interface InitFile {\n /** `.cohorte`-relative POSIX path */\n path: string;\n /** an existing human file is NEVER overwritten */\n action: 'create' | 'keep-existing';\n class: FieldClass;\n content: string;\n templateId?: string;\n templateSha256?: Sha256;\n renderedSha256?: Sha256;\n}\n\n/** What `cohorte init` will do, computed without writing anything. Idempotent: planning twice yields the same plan. */\nexport interface InitPlan {\n projectRoot: string;\n model: ProjectModel;\n files: InitFile[];\n manifest: Manifest;\n warnings: string[];\n}\n\n/** INJECTED into `planReconcile`, so the drift unit never imports the scan unit. */\nexport type RepositoryScanner = (root: string) => Promise<ProjectModel>;\n"],"mappings":"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"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { i as planInit, r as applyInit, t as scanRepository } from "./src-JxawBQBn.mjs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
//#region src/commands/init/index.ts
|
|
4
|
-
const init = {
|
|
5
|
-
verb: "init",
|
|
6
|
-
async run(ctx, args) {
|
|
7
|
-
const requestedRoot = args.positionals.find((value) => !value.startsWith("--"));
|
|
8
|
-
const root = requestedRoot === void 0 ? ctx.cwd : resolve(ctx.cwd, requestedRoot);
|
|
9
|
-
const model = await scanRepository(root, {
|
|
10
|
-
clock: ctx.clock,
|
|
11
|
-
toolVersion: "3.0.0"
|
|
12
|
-
});
|
|
13
|
-
const plan = await planInit({
|
|
14
|
-
root,
|
|
15
|
-
model,
|
|
16
|
-
cohorteVersion: "3.0.0"
|
|
17
|
-
});
|
|
18
|
-
if (args.positionals.includes("--plan") || args.json) {
|
|
19
|
-
ctx.stdio.stdout.write(`${JSON.stringify(plan)}\n`);
|
|
20
|
-
return 0;
|
|
21
|
-
}
|
|
22
|
-
const result = await applyInit(plan);
|
|
23
|
-
ctx.stdio.stdout.write(`written: ${result.written.join(", ") || "none"}\n`);
|
|
24
|
-
return 0;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
//#endregion
|
|
28
|
-
export { init as default };
|
|
29
|
-
|
|
30
|
-
//# sourceMappingURL=init-Okz4mlhW.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init-Okz4mlhW.mjs","names":[],"sources":["../../../../apps/cli/src/commands/init/index.ts"],"sourcesContent":["// apps/cli/src/commands/init/index.ts — DESIGN §9 verb `init` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/init/**`.\nimport { resolve } from 'node:path';\nimport { applyInit, planInit, scanRepository } from '@cohorte/project-model';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst init: CommandModule = {\n verb: 'init',\n async run(ctx, args) {\n const requestedRoot = args.positionals.find((value) => !value.startsWith('--'));\n const root = requestedRoot === undefined ? ctx.cwd : resolve(ctx.cwd, requestedRoot);\n const model = await scanRepository(root, { clock: ctx.clock, toolVersion: '3.0.0' });\n const plan = await planInit({ root, model, cohorteVersion: '3.0.0' });\n if (args.positionals.includes('--plan') || args.json) {\n ctx.stdio.stdout.write(`${JSON.stringify(plan)}\\n`);\n return 0;\n }\n const result = await applyInit(plan);\n ctx.stdio.stdout.write(`written: ${result.written.join(', ') || 'none'}\\n`);\n return 0;\n },\n};\n\nexport default init;\n"],"mappings":";;;AAQA,MAAM,OAAsB;CAC1B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,gBAAgB,KAAK,YAAY,MAAM,UAAU,CAAC,MAAM,WAAW,IAAI,CAAC;EAC9E,MAAM,OAAO,kBAAkB,KAAA,IAAY,IAAI,MAAM,QAAQ,IAAI,KAAK,aAAa;EACnF,MAAM,QAAQ,MAAM,eAAe,MAAM;GAAE,OAAO,IAAI;GAAO,aAAa;EAAQ,CAAC;EACnF,MAAM,OAAO,MAAM,SAAS;GAAE;GAAM;GAAO,gBAAgB;EAAQ,CAAC;EACpE,IAAI,KAAK,YAAY,SAAS,QAAQ,KAAK,KAAK,MAAM;GACpD,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,IAAI,EAAE,GAAG;GAClD,OAAO;EACT;EACA,MAAM,SAAS,MAAM,UAAU,IAAI;EACnC,IAAI,MAAM,OAAO,MAAM,YAAY,OAAO,QAAQ,KAAK,IAAI,KAAK,OAAO,GAAG;EAC1E,OAAO;CACT;AACF"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
//#region src/commands/migrate/index.ts
|
|
2
|
-
const migrate = {
|
|
3
|
-
verb: "migrate",
|
|
4
|
-
async run(ctx, args) {
|
|
5
|
-
const store = await (ctx.openMigrationStore ?? ctx.openStore)();
|
|
6
|
-
try {
|
|
7
|
-
const apply = args.positionals.includes("--apply");
|
|
8
|
-
const report = await store.migrate(apply ? "apply" : "check");
|
|
9
|
-
ctx.stdio.stdout.write(`${JSON.stringify(report)}\n`);
|
|
10
|
-
return !apply && report.pending.length > 0 ? 3 : 0;
|
|
11
|
-
} finally {
|
|
12
|
-
await store.close();
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
//#endregion
|
|
17
|
-
export { migrate as default };
|
|
18
|
-
|
|
19
|
-
//# sourceMappingURL=migrate-DRoFUg53.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"migrate-DRoFUg53.mjs","names":[],"sources":["../../../../apps/cli/src/commands/migrate/index.ts"],"sourcesContent":["// apps/cli/src/commands/migrate/index.ts — DESIGN §9 verb `migrate` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/migrate/**`.\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst migrate: CommandModule = {\n verb: 'migrate',\n async run(ctx, args) {\n const store = await (ctx.openMigrationStore ?? ctx.openStore)();\n try {\n const apply = args.positionals.includes('--apply');\n const report = await store.migrate(apply ? 'apply' : 'check');\n ctx.stdio.stdout.write(`${JSON.stringify(report)}\\n`);\n return !apply && report.pending.length > 0 ? 3 : 0;\n } finally {\n await store.close();\n }\n },\n};\n\nexport default migrate;\n"],"mappings":";AAMA,MAAM,UAAyB;CAC7B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,QAAQ,OAAO,IAAI,sBAAsB,IAAI,UAAA,CAAW;EAC9D,IAAI;GACF,MAAM,QAAQ,KAAK,YAAY,SAAS,SAAS;GACjD,MAAM,SAAS,MAAM,MAAM,QAAQ,QAAQ,UAAU,OAAO;GAC5D,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,EAAE,GAAG;GACpD,OAAO,CAAC,SAAS,OAAO,QAAQ,SAAS,IAAI,IAAI;EACnD,UAAU;GACR,MAAM,MAAM,MAAM;EACpB;CACF;AACF"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { N as errorOf, j as CohorteError } from "./src-Cu3TxNob.mjs";
|
|
2
|
-
import { n as planReconcile, t as scanRepository } from "./src-JxawBQBn.mjs";
|
|
3
|
-
//#region src/commands/reconcile/index.ts
|
|
4
|
-
const reconcile = {
|
|
5
|
-
verb: "reconcile",
|
|
6
|
-
async run(ctx, args) {
|
|
7
|
-
if (args.positionals.includes("--apply")) throw new CohorteError(errorOf("configuration/phase-not-available", "reconcile --apply is not available in V3.0"));
|
|
8
|
-
if (!args.positionals.includes("--plan")) return 2;
|
|
9
|
-
const plan = await planReconcile({
|
|
10
|
-
root: ctx.cwd,
|
|
11
|
-
scan: (root) => scanRepository(root, {
|
|
12
|
-
clock: ctx.clock,
|
|
13
|
-
toolVersion: "3.0.0"
|
|
14
|
-
}),
|
|
15
|
-
cohorteVersion: "3.0.0",
|
|
16
|
-
clock: ctx.clock
|
|
17
|
-
});
|
|
18
|
-
if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(plan)}\n`);
|
|
19
|
-
else ctx.stdio.stdout.write(`${plan.operations.length} operation(s) planned\n`);
|
|
20
|
-
return 0;
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
//#endregion
|
|
24
|
-
export { reconcile as default };
|
|
25
|
-
|
|
26
|
-
//# sourceMappingURL=reconcile-BfD785gd.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-BfD785gd.mjs","names":[],"sources":["../../../../apps/cli/src/commands/reconcile/index.ts"],"sourcesContent":["import { CohorteError, errorOf } from '@cohorte/base';\nimport { planReconcile, scanRepository } from '@cohorte/project-model';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst reconcile: CommandModule = {\n verb: 'reconcile',\n async run(ctx, args) {\n if (args.positionals.includes('--apply')) {\n throw new CohorteError(\n errorOf('configuration/phase-not-available', 'reconcile --apply is not available in V3.0'),\n );\n }\n if (!args.positionals.includes('--plan')) return 2;\n const plan = await planReconcile({\n root: ctx.cwd,\n scan: (root) => scanRepository(root, { clock: ctx.clock, toolVersion: '3.0.0' }),\n cohorteVersion: '3.0.0',\n clock: ctx.clock,\n });\n if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(plan)}\\n`);\n else ctx.stdio.stdout.write(`${plan.operations.length} operation(s) planned\\n`);\n return 0;\n },\n};\n\nexport default reconcile;\n"],"mappings":";;;AAIA,MAAM,YAA2B;CAC/B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,IAAI,KAAK,YAAY,SAAS,SAAS,GACrC,MAAM,IAAI,aACR,QAAQ,qCAAqC,4CAA4C,CAC3F;EAEF,IAAI,CAAC,KAAK,YAAY,SAAS,QAAQ,GAAG,OAAO;EACjD,MAAM,OAAO,MAAM,cAAc;GAC/B,MAAM,IAAI;GACV,OAAO,SAAS,eAAe,MAAM;IAAE,OAAO,IAAI;IAAO,aAAa;GAAQ,CAAC;GAC/E,gBAAgB;GAChB,OAAO,IAAI;EACb,CAAC;EACD,IAAI,KAAK,MAAM,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,IAAI,EAAE,GAAG;OAC5D,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,WAAW,OAAO,wBAAwB;EAC9E,OAAO;CACT;AACF"}
|
|
@@ -1,446 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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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"}
|