@danieljvdm/dev-kit 0.11.3 → 0.13.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 +111 -78
- package/dev-kit.example.jsonc +0 -4
- package/package.json +10 -6
- package/schema/dev-kit.schema.json +1 -46
- package/skills/build-effect-apis/SKILL.md +77 -0
- package/skills/build-effect-apis/agents/openai.yaml +4 -0
- package/skills/build-effect-apis/references/cloudflare-workers.md +71 -0
- package/skills/build-effect-apis/references/effect-atom-client.md +161 -0
- package/skills/build-effect-apis/references/effect-atom-lifecycle.md +78 -0
- package/skills/build-effect-apis/references/effect-atom-testing.md +74 -0
- package/skills/build-effect-apis/references/runtime-assembly.md +56 -0
- package/skills/build-effect-apis/references/server-and-middleware.md +174 -0
- package/skills/build-effect-apis/references/shared-contracts.md +108 -0
- package/skills/build-effect-apis/references/tanstack-start.md +86 -0
- package/skills/build-effect-apis/references/verification.md +50 -0
- package/skills/build-effect-clis/SKILL.md +61 -0
- package/skills/build-effect-clis/agents/openai.yaml +4 -0
- package/skills/build-effect-clis/references/command-design.md +93 -0
- package/skills/build-effect-clis/references/entrypoints-and-testing.md +59 -0
- package/skills/build-effect-clis/references/processes-and-platform.md +73 -0
- package/skills/dev-kit/SKILL.md +77 -48
- package/skills/effect-architecture-audit/SKILL.md +26 -0
- package/skills/effect-architecture-audit/agents/openai.yaml +4 -0
- package/skills/effect-architecture-audit/references/service-and-boundary-audit.md +150 -0
- package/skills/effect-ts/SKILL.md +21 -256
- package/skills/effect-ts/agents/openai.yaml +3 -3
- package/skills/testing/SKILL.md +5 -0
- package/src/catalog-manager.ts +16 -17
- package/src/catalog.ts +71 -16
- package/src/effect-source.ts +46 -24
- package/src/effect-tsgo.ts +86 -24
- package/src/gitignore.ts +5 -5
- package/src/index.ts +3 -6
- package/src/manifest.ts +0 -34
- package/src/node-symbolic-link.ts +2 -2
- package/src/oxfmt.js +5 -0
- package/src/oxfmt.ts +5 -0
- package/src/oxlint.js +5 -0
- package/src/oxlint.ts +5 -0
- package/src/package-skill-source.ts +51 -59
- package/src/path-digest.ts +7 -7
- package/src/project-package.ts +8 -7
- package/src/project-process-lock.ts +17 -12
- package/src/project-state.ts +1 -1
- package/src/skill-manager.ts +18 -16
- package/src/skill-selector.ts +12 -0
- package/src/sync.ts +181 -131
- package/src/tool-ignore-patterns.js +9 -0
- package/src/tool-ignore-patterns.ts +15 -0
- package/src/vendor.ts +67 -61
- package/src/vite-plus-dependency.ts +10 -11
- package/src/vite-plus-hooks.ts +24 -14
- package/src/vite-plus-quality.ts +21 -172
- package/src/vite-plus.js +81 -0
- package/src/vite-plus.ts +102 -0
- package/templates/AGENTS.md +1 -1
- package/skills/effect-ts/UPSTREAM.md +0 -28
- package/skills/effect-ts/references/atom-cache-lifecycle.md +0 -78
- package/skills/effect-ts/references/atom-http-and-invalidation.md +0 -97
- package/skills/effect-ts/references/atom-tanstack-start.md +0 -69
- package/skills/effect-ts/references/atom-testing.md +0 -67
- package/skills/effect-ts/references/audit-services.md +0 -144
- package/skills/effect-ts/references/features.md +0 -525
- package/skills/effect-ts/references/guide-atom-data-fetching.md +0 -44
- package/skills/effect-ts/references/guide-cli.md +0 -107
- package/skills/effect-ts/references/guide-datetime.md +0 -72
- package/skills/effect-ts/references/guide-effect.md +0 -440
- package/skills/effect-ts/references/guide-error-handling.md +0 -565
- package/skills/effect-ts/references/guide-http-boundaries.md +0 -55
- package/skills/effect-ts/references/guide-layers.md +0 -989
- package/skills/effect-ts/references/guide-observability.md +0 -746
- package/skills/effect-ts/references/guide-retries.md +0 -434
- package/skills/effect-ts/references/guide-schedule.md +0 -343
- package/skills/effect-ts/references/guide-schema.md +0 -664
- package/skills/effect-ts/references/guide-sql.md +0 -536
- package/skills/effect-ts/references/guide-testing.md +0 -532
- package/skills/effect-ts/references/guide-type-safety-and-boundaries.md +0 -131
- package/skills/effect-ts/references/version-and-source.md +0 -86
- package/templates/vite-plus/vite.config.ts +0 -22
package/src/sync.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cause, Effect, FileSystem, Path, Schema, Stream } from "effect";
|
|
1
|
+
import { Cause, Effect, FileSystem, Path, Schema, SchemaGetter, Stream } from "effect";
|
|
2
2
|
import { ChildProcess } from "effect/unstable/process";
|
|
3
3
|
import { parse as parseJsonc, printParseErrorCode, type ParseError } from "jsonc-parser";
|
|
4
4
|
|
|
@@ -31,6 +31,9 @@ import { acquireProjectProcessLock, PROJECT_PROCESS_LOCK_PATH } from "./project-
|
|
|
31
31
|
import {
|
|
32
32
|
AppliedStateSchema,
|
|
33
33
|
DevKitLockSchema,
|
|
34
|
+
EffectSourceLockSchema,
|
|
35
|
+
EffectTsgoLockSchema,
|
|
36
|
+
ManagedOutputSchema,
|
|
34
37
|
type AppliedState,
|
|
35
38
|
type DevKitLock,
|
|
36
39
|
type ManagedAgentInstructionsOutput,
|
|
@@ -48,11 +51,8 @@ import {
|
|
|
48
51
|
type VitePlusHooksPlan,
|
|
49
52
|
} from "./vite-plus-hooks.ts";
|
|
50
53
|
import {
|
|
51
|
-
renderVitePlusConfigTemplate,
|
|
52
54
|
renderVitePlusWorkflowTemplate,
|
|
53
55
|
validateVitePlusQualitySupport,
|
|
54
|
-
VITE_PLUS_CONFIG_PATH,
|
|
55
|
-
VITE_PLUS_CONFIG_TEMPLATE,
|
|
56
56
|
VITE_PLUS_GITHUB_ACTIONS_PATH,
|
|
57
57
|
VITE_PLUS_GITHUB_ACTIONS_TEMPLATE,
|
|
58
58
|
} from "./vite-plus-quality.ts";
|
|
@@ -149,7 +149,7 @@ export type SkillPlan = {
|
|
|
149
149
|
readonly metadataChanged: boolean;
|
|
150
150
|
};
|
|
151
151
|
|
|
152
|
-
class ManifestNotFoundError extends Schema.
|
|
152
|
+
class ManifestNotFoundError extends Schema.TaggedError<ManifestNotFoundError>()(
|
|
153
153
|
"ManifestNotFoundError",
|
|
154
154
|
{ path: Schema.String },
|
|
155
155
|
) {
|
|
@@ -158,12 +158,12 @@ class ManifestNotFoundError extends Schema.TaggedErrorClass<ManifestNotFoundErro
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
class StructuredFileError extends Schema.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
) {}
|
|
161
|
+
class StructuredFileError extends Schema.TaggedError<StructuredFileError>()("StructuredFileError", {
|
|
162
|
+
path: Schema.String,
|
|
163
|
+
message: Schema.String,
|
|
164
|
+
}) {}
|
|
165
165
|
|
|
166
|
-
class UnknownSkillOrFamilyError extends Schema.
|
|
166
|
+
class UnknownSkillOrFamilyError extends Schema.TaggedError<UnknownSkillOrFamilyError>()(
|
|
167
167
|
"UnknownSkillOrFamilyError",
|
|
168
168
|
{ name: Schema.String, known: Schema.Array(Schema.String) },
|
|
169
169
|
) {
|
|
@@ -172,12 +172,12 @@ class UnknownSkillOrFamilyError extends Schema.TaggedErrorClass<UnknownSkillOrFa
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
class InvalidSkillCatalogError extends Schema.
|
|
175
|
+
class InvalidSkillCatalogError extends Schema.TaggedError<InvalidSkillCatalogError>()(
|
|
176
176
|
"InvalidSkillCatalogError",
|
|
177
177
|
{ family: Schema.String, message: Schema.String },
|
|
178
178
|
) {}
|
|
179
179
|
|
|
180
|
-
class CommandError extends Schema.
|
|
180
|
+
class CommandError extends Schema.TaggedError<CommandError>()("CommandError", {
|
|
181
181
|
command: Schema.String,
|
|
182
182
|
exitCode: Schema.Int,
|
|
183
183
|
output: Schema.String,
|
|
@@ -189,7 +189,7 @@ class CommandError extends Schema.TaggedErrorClass<CommandError>()("CommandError
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
class UnsafeManagedPathError extends Schema.
|
|
192
|
+
class UnsafeManagedPathError extends Schema.TaggedError<UnsafeManagedPathError>()(
|
|
193
193
|
"UnsafeManagedPathError",
|
|
194
194
|
{ path: Schema.String, reason: Schema.String },
|
|
195
195
|
) {
|
|
@@ -198,17 +198,17 @@ class UnsafeManagedPathError extends Schema.TaggedErrorClass<UnsafeManagedPathEr
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
class InvalidProjectStateError extends Schema.
|
|
201
|
+
class InvalidProjectStateError extends Schema.TaggedError<InvalidProjectStateError>()(
|
|
202
202
|
"InvalidProjectStateError",
|
|
203
203
|
{ message: Schema.String },
|
|
204
204
|
) {}
|
|
205
205
|
|
|
206
|
-
class LockedPlanMismatchError extends Schema.
|
|
206
|
+
class LockedPlanMismatchError extends Schema.TaggedError<LockedPlanMismatchError>()(
|
|
207
207
|
"LockedPlanMismatchError",
|
|
208
208
|
{ message: Schema.String },
|
|
209
209
|
) {}
|
|
210
210
|
|
|
211
|
-
class PlanConflictError extends Schema.
|
|
211
|
+
class PlanConflictError extends Schema.TaggedError<PlanConflictError>()("PlanConflictError", {
|
|
212
212
|
conflicts: Schema.Array(Schema.String),
|
|
213
213
|
}) {
|
|
214
214
|
override get message() {
|
|
@@ -218,7 +218,7 @@ class PlanConflictError extends Schema.TaggedErrorClass<PlanConflictError>()("Pl
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
class ApplyRaceError extends Schema.
|
|
221
|
+
class ApplyRaceError extends Schema.TaggedError<ApplyRaceError>()("ApplyRaceError", {
|
|
222
222
|
path: Schema.String,
|
|
223
223
|
}) {
|
|
224
224
|
override get message() {
|
|
@@ -226,9 +226,51 @@ class ApplyRaceError extends Schema.TaggedErrorClass<ApplyRaceError>()("ApplyRac
|
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
const fromJsonString = <S extends Schema.Constraint>(schema: S, space?: number) =>
|
|
230
|
+
space === undefined
|
|
231
|
+
? Schema.fromJsonString(schema)
|
|
232
|
+
: Schema.String.pipe(
|
|
233
|
+
Schema.decodeTo(Schema.toCodecJson(schema), {
|
|
234
|
+
decode: SchemaGetter.parseJson(),
|
|
235
|
+
encode: SchemaGetter.stringifyJson({ space }),
|
|
236
|
+
}),
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const DevKitSetupSchema = Schema.Struct({
|
|
240
|
+
effectSource: Schema.optional(EffectSourceLockSchema),
|
|
241
|
+
effectTsgo: Schema.optional(EffectTsgoLockSchema),
|
|
242
|
+
});
|
|
243
|
+
const OutputOwnershipIdentitySchema = Schema.Union([
|
|
244
|
+
Schema.Struct({
|
|
245
|
+
resourceId: Schema.String,
|
|
246
|
+
path: Schema.String,
|
|
247
|
+
mode: Schema.Literals(["copy", "symlink"]),
|
|
248
|
+
kind: Schema.Literals(["directory", "symlink"]),
|
|
249
|
+
skill: Schema.String,
|
|
250
|
+
target: Schema.Literals(["agents", "claude", "opencode"]),
|
|
251
|
+
}),
|
|
252
|
+
Schema.Struct({
|
|
253
|
+
resourceId: Schema.String,
|
|
254
|
+
path: Schema.String,
|
|
255
|
+
mode: Schema.Literals(["copy", "symlink"]),
|
|
256
|
+
kind: Schema.Literals(["file", "symlink"]),
|
|
257
|
+
sourcePath: Schema.String,
|
|
258
|
+
}),
|
|
259
|
+
]);
|
|
260
|
+
const encodeAppliedStateJson = Schema.encodeSync(fromJsonString(AppliedStateSchema));
|
|
261
|
+
const encodeDevKitLockJson = Schema.encodeSync(fromJsonString(DevKitLockSchema));
|
|
262
|
+
const encodeDevKitLockPrettyJson = Schema.encodeSync(fromJsonString(DevKitLockSchema, 2));
|
|
263
|
+
const encodeDevKitSetupJson = Schema.encodeSync(fromJsonString(DevKitSetupSchema));
|
|
264
|
+
const encodeManifestJson = Schema.encodeSync(fromJsonString(DevKitManifestSchema));
|
|
265
|
+
const encodeManagedOutputJson = Schema.encodeSync(fromJsonString(ManagedOutputSchema));
|
|
266
|
+
const encodeOutputOwnershipIdentityJson = Schema.encodeSync(
|
|
267
|
+
fromJsonString(OutputOwnershipIdentitySchema),
|
|
268
|
+
);
|
|
269
|
+
const encodePlanSnapshotJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
|
|
270
|
+
const encodeAppliedStatePrettyJson = Schema.encodeSync(fromJsonString(AppliedStateSchema, 2));
|
|
271
|
+
|
|
229
272
|
const SKILL_FAMILIES: SkillCatalog = {
|
|
230
|
-
effect: ["effect-ts"],
|
|
231
|
-
"effect-atom-data-fetching": ["effect-ts"],
|
|
273
|
+
effect: ["effect-ts", "effect-architecture-audit", "build-effect-apis", "build-effect-clis"],
|
|
232
274
|
};
|
|
233
275
|
|
|
234
276
|
export const DEFAULT_MANIFEST = "dev-kit.jsonc";
|
|
@@ -236,6 +278,7 @@ const DEFAULT_LOCKFILE = "dev-kit.lock.json";
|
|
|
236
278
|
const DEFAULT_STATE = ".dev-kit/state.json";
|
|
237
279
|
const AGENT_INSTRUCTIONS_TEMPLATE = "templates/AGENTS.md";
|
|
238
280
|
const DEV_KIT_SKILL_PATH_PLACEHOLDER = "{{DEV_KIT_SKILL_PATH}}";
|
|
281
|
+
const EFFECT_INSTRUCTIONS_PLACEHOLDER = "{{EFFECT_INSTRUCTIONS}}";
|
|
239
282
|
const PROJECT_COMMAND_POLICY_PLACEHOLDER = "{{PROJECT_COMMAND_POLICY}}";
|
|
240
283
|
const AGENT_INSTRUCTION_MARKERS = [
|
|
241
284
|
{ start: "<!-- DEV KIT START -->", end: "<!-- DEV KIT END -->" },
|
|
@@ -507,7 +550,7 @@ const runCommand = Effect.fn("runCommand")(function* (
|
|
|
507
550
|
const trimmed = output.trim();
|
|
508
551
|
|
|
509
552
|
if (exitCode !== 0) {
|
|
510
|
-
return yield*
|
|
553
|
+
return yield* CommandError.make({ command: formatted, exitCode, output: trimmed });
|
|
511
554
|
}
|
|
512
555
|
|
|
513
556
|
return trimmed;
|
|
@@ -521,20 +564,26 @@ const parseStructuredFile = Effect.fn("parseStructuredFile")(function* <A>(
|
|
|
521
564
|
filePath: string,
|
|
522
565
|
raw: string,
|
|
523
566
|
schema: Schema.ConstraintDecoder<A>,
|
|
567
|
+
options: { readonly rejectExcessProperties?: boolean } = {},
|
|
524
568
|
) {
|
|
525
569
|
const errors: Array<ParseError> = [];
|
|
526
570
|
const parsed = parseJsonc(raw, errors, { allowTrailingComma: true });
|
|
527
571
|
const first = errors[0];
|
|
528
572
|
|
|
529
573
|
if (first !== undefined) {
|
|
530
|
-
return yield*
|
|
574
|
+
return yield* StructuredFileError.make({
|
|
531
575
|
path: filePath,
|
|
532
576
|
message: `${printParseErrorCode(first.error)} at offset ${first.offset}`,
|
|
533
577
|
});
|
|
534
578
|
}
|
|
535
579
|
|
|
536
|
-
return yield* Schema.decodeUnknownEffect(
|
|
537
|
-
|
|
580
|
+
return yield* Schema.decodeUnknownEffect(
|
|
581
|
+
schema,
|
|
582
|
+
options.rejectExcessProperties ? { onExcessProperty: "error" } : undefined,
|
|
583
|
+
)(parsed).pipe(
|
|
584
|
+
Effect.mapError((cause) =>
|
|
585
|
+
StructuredFileError.make({ path: filePath, message: cause.message }),
|
|
586
|
+
),
|
|
538
587
|
);
|
|
539
588
|
});
|
|
540
589
|
|
|
@@ -542,11 +591,13 @@ const readManifest = Effect.fn("readManifest")(function* (manifestPath: string)
|
|
|
542
591
|
const fs = yield* FileSystem.FileSystem;
|
|
543
592
|
|
|
544
593
|
if (!(yield* fs.exists(manifestPath))) {
|
|
545
|
-
return yield*
|
|
594
|
+
return yield* ManifestNotFoundError.make({ path: manifestPath });
|
|
546
595
|
}
|
|
547
596
|
const raw = yield* fs.readFileString(manifestPath);
|
|
548
597
|
|
|
549
|
-
return yield* parseStructuredFile(manifestPath, raw, DevKitManifestSchema
|
|
598
|
+
return yield* parseStructuredFile(manifestPath, raw, DevKitManifestSchema, {
|
|
599
|
+
rejectExcessProperties: true,
|
|
600
|
+
});
|
|
550
601
|
});
|
|
551
602
|
|
|
552
603
|
const readOptionalStructuredFile = Effect.fn("readOptionalStructuredFile")(function* <A>(
|
|
@@ -577,7 +628,7 @@ const expandSelection = (
|
|
|
577
628
|
} else if (availableSkills.includes(name) || parseSkillSelector(name)?.type === "package") {
|
|
578
629
|
selected.add(name);
|
|
579
630
|
} else {
|
|
580
|
-
return Effect.fail(
|
|
631
|
+
return Effect.fail(UnknownSkillOrFamilyError.make({ name, known }));
|
|
581
632
|
}
|
|
582
633
|
}
|
|
583
634
|
for (const name of exclude) {
|
|
@@ -600,7 +651,7 @@ const resolveManagedPath = Effect.fn("resolveManagedPath")(function* (
|
|
|
600
651
|
const path = yield* Path.Path;
|
|
601
652
|
|
|
602
653
|
if (candidate.length === 0 || path.isAbsolute(candidate)) {
|
|
603
|
-
return yield*
|
|
654
|
+
return yield* UnsafeManagedPathError.make({
|
|
604
655
|
path: candidate,
|
|
605
656
|
reason: "must be a non-empty project-relative path",
|
|
606
657
|
});
|
|
@@ -614,7 +665,7 @@ const resolveManagedPath = Effect.fn("resolveManagedPath")(function* (
|
|
|
614
665
|
relative.startsWith(`..${path.sep}`) ||
|
|
615
666
|
path.isAbsolute(relative)
|
|
616
667
|
) {
|
|
617
|
-
return yield*
|
|
668
|
+
return yield* UnsafeManagedPathError.make({
|
|
618
669
|
path: candidate,
|
|
619
670
|
reason: "resolves outside the project",
|
|
620
671
|
});
|
|
@@ -628,7 +679,7 @@ const resolveManagedPath = Effect.fn("resolveManagedPath")(function* (
|
|
|
628
679
|
const target = yield* observeSymbolicLink(ancestor);
|
|
629
680
|
|
|
630
681
|
if (target.kind === "symlink") {
|
|
631
|
-
return yield*
|
|
682
|
+
return yield* UnsafeManagedPathError.make({
|
|
632
683
|
path: candidate,
|
|
633
684
|
reason: `ancestor is a symlink: ${portablePath(path, path.relative(projectDir, ancestor))}`,
|
|
634
685
|
});
|
|
@@ -658,14 +709,14 @@ const validateReservedPaths = Effect.fn("validateReservedPaths")(function* (
|
|
|
658
709
|
if (current === undefined) continue;
|
|
659
710
|
for (const other of reserved.slice(index + 1)) {
|
|
660
711
|
if (pathsOverlap(current.path, other.path)) {
|
|
661
|
-
return yield*
|
|
712
|
+
return yield* InvalidProjectStateError.make({
|
|
662
713
|
message: `${current.label} path ${current.path} overlaps ${other.label} path ${other.path}`,
|
|
663
714
|
});
|
|
664
715
|
}
|
|
665
716
|
}
|
|
666
717
|
for (const outputPath of outputPaths) {
|
|
667
718
|
if (pathsOverlap(current.path, outputPath)) {
|
|
668
|
-
return yield*
|
|
719
|
+
return yield* InvalidProjectStateError.make({
|
|
669
720
|
message: `${current.label} path ${current.path} overlaps managed output ${outputPath}`,
|
|
670
721
|
});
|
|
671
722
|
}
|
|
@@ -673,28 +724,27 @@ const validateReservedPaths = Effect.fn("validateReservedPaths")(function* (
|
|
|
673
724
|
}
|
|
674
725
|
});
|
|
675
726
|
|
|
676
|
-
const outputIdentity = (output: ManagedOutput) =>
|
|
677
|
-
JSON.stringify({
|
|
678
|
-
resourceId: output.resourceId,
|
|
679
|
-
path: output.path,
|
|
680
|
-
mode: output.mode,
|
|
681
|
-
kind: output.kind,
|
|
682
|
-
digest: output.digest,
|
|
683
|
-
...("skill" in output
|
|
684
|
-
? { skill: output.skill, target: output.target, catalog: output.catalog }
|
|
685
|
-
: { sourcePath: output.sourcePath }),
|
|
686
|
-
});
|
|
727
|
+
const outputIdentity = (output: ManagedOutput) => encodeManagedOutputJson(output);
|
|
687
728
|
|
|
688
729
|
const outputOwnershipIdentity = (output: ManagedOutput) =>
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
730
|
+
encodeOutputOwnershipIdentityJson(
|
|
731
|
+
"skill" in output
|
|
732
|
+
? {
|
|
733
|
+
resourceId: output.resourceId,
|
|
734
|
+
path: output.path,
|
|
735
|
+
mode: output.mode,
|
|
736
|
+
kind: output.kind,
|
|
737
|
+
skill: output.skill,
|
|
738
|
+
target: output.target,
|
|
739
|
+
}
|
|
740
|
+
: {
|
|
741
|
+
resourceId: output.resourceId,
|
|
742
|
+
path: output.path,
|
|
743
|
+
mode: output.mode,
|
|
744
|
+
kind: output.kind,
|
|
745
|
+
sourcePath: output.sourcePath,
|
|
746
|
+
},
|
|
747
|
+
);
|
|
698
748
|
|
|
699
749
|
const usesRawFileModeDigests = (toolVersion: string): boolean => {
|
|
700
750
|
const match = /^(\d+)\.(\d+)\./.exec(toolVersion);
|
|
@@ -717,12 +767,12 @@ const validateInventory = Effect.fn("validateManagedInventory")(function* (
|
|
|
717
767
|
|
|
718
768
|
for (const output of outputs) {
|
|
719
769
|
if (ids.has(output.resourceId)) {
|
|
720
|
-
return yield*
|
|
770
|
+
return yield* InvalidProjectStateError.make({
|
|
721
771
|
message: `${label} contains duplicate resource id ${output.resourceId}`,
|
|
722
772
|
});
|
|
723
773
|
}
|
|
724
774
|
if (paths.has(output.path)) {
|
|
725
|
-
return yield*
|
|
775
|
+
return yield* InvalidProjectStateError.make({
|
|
726
776
|
message: `${label} contains duplicate path ${output.path}`,
|
|
727
777
|
});
|
|
728
778
|
}
|
|
@@ -737,7 +787,7 @@ const validateInventory = Effect.fn("validateManagedInventory")(function* (
|
|
|
737
787
|
|
|
738
788
|
if (previous === undefined || current === undefined) continue;
|
|
739
789
|
if (current.startsWith(`${previous}/`)) {
|
|
740
|
-
return yield*
|
|
790
|
+
return yield* InvalidProjectStateError.make({
|
|
741
791
|
message: `${label} contains overlapping paths ${previous} and ${current}`,
|
|
742
792
|
});
|
|
743
793
|
}
|
|
@@ -761,7 +811,7 @@ const validateCrossInventoryPaths = Effect.fn("validateCrossInventoryPaths")(fun
|
|
|
761
811
|
|
|
762
812
|
if (previous === undefined || current === undefined) continue;
|
|
763
813
|
if (current.startsWith(`${previous}/`)) {
|
|
764
|
-
return yield*
|
|
814
|
+
return yield* InvalidProjectStateError.make({
|
|
765
815
|
message: `desired and previously owned paths overlap: ${previous} and ${current}`,
|
|
766
816
|
});
|
|
767
817
|
}
|
|
@@ -772,7 +822,7 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
772
822
|
packageRoot: string,
|
|
773
823
|
projectDir: string,
|
|
774
824
|
sourceBySkill: ReadonlyMap<string, ResolvedSkillSource>,
|
|
775
|
-
|
|
825
|
+
usesRecommendedVitePlusTasks: boolean,
|
|
776
826
|
targets: ReturnType<typeof normalizeManifest>["targets"],
|
|
777
827
|
) {
|
|
778
828
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -780,19 +830,24 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
780
830
|
const templatePath = path.join(packageRoot, AGENT_INSTRUCTIONS_TEMPLATE);
|
|
781
831
|
|
|
782
832
|
if ((yield* observePath(templatePath)).kind !== "file") {
|
|
783
|
-
return yield*
|
|
833
|
+
return yield* InvalidProjectStateError.make({
|
|
784
834
|
message: `dev-kit agent instructions template is not a regular file: ${AGENT_INSTRUCTIONS_TEMPLATE}`,
|
|
785
835
|
});
|
|
786
836
|
}
|
|
787
837
|
const template = yield* fs.readFileString(templatePath);
|
|
788
838
|
|
|
789
839
|
if (!template.includes(DEV_KIT_SKILL_PATH_PLACEHOLDER)) {
|
|
790
|
-
return yield*
|
|
840
|
+
return yield* InvalidProjectStateError.make({
|
|
791
841
|
message: `dev-kit agent instructions template is missing ${DEV_KIT_SKILL_PATH_PLACEHOLDER}`,
|
|
792
842
|
});
|
|
793
843
|
}
|
|
844
|
+
if (!template.includes(EFFECT_INSTRUCTIONS_PLACEHOLDER)) {
|
|
845
|
+
return yield* InvalidProjectStateError.make({
|
|
846
|
+
message: `dev-kit agent instructions template is missing ${EFFECT_INSTRUCTIONS_PLACEHOLDER}`,
|
|
847
|
+
});
|
|
848
|
+
}
|
|
794
849
|
if (!template.includes(PROJECT_COMMAND_POLICY_PLACEHOLDER)) {
|
|
795
|
-
return yield*
|
|
850
|
+
return yield* InvalidProjectStateError.make({
|
|
796
851
|
message: `dev-kit agent instructions template is missing ${PROJECT_COMMAND_POLICY_PLACEHOLDER}`,
|
|
797
852
|
});
|
|
798
853
|
}
|
|
@@ -813,7 +868,24 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
813
868
|
path.join(devKitSkill.linkPath ?? devKitSkill.path, "SKILL.md"),
|
|
814
869
|
),
|
|
815
870
|
);
|
|
816
|
-
const
|
|
871
|
+
const directDependencyNames = yield* readDirectDependencyNames(projectDir);
|
|
872
|
+
const usesVitePlus = directDependencyNames.includes("vite-plus");
|
|
873
|
+
const effectInstructions =
|
|
874
|
+
directDependencyNames.includes("effect") &&
|
|
875
|
+
(yield* observePath(path.join(projectDir, "node_modules", "effect", "AGENTS.md"))).kind ===
|
|
876
|
+
"file"
|
|
877
|
+
? `# Learning more about the Effect
|
|
878
|
+
|
|
879
|
+
This repository uses the Effect Typescript library.
|
|
880
|
+
|
|
881
|
+
Before writing any Effect code, first read \`node_modules/effect/AGENTS.md\`
|
|
882
|
+
**completely**, and follow the links in the file when required.
|
|
883
|
+
|
|
884
|
+
If you need to learn more about particular Effect apis and concepts that the
|
|
885
|
+
guide doesn't cover, search through the source code in \`node_modules/effect/src\`.
|
|
886
|
+
|
|
887
|
+
`
|
|
888
|
+
: "";
|
|
817
889
|
const projectPackage = yield* readProjectPackage(projectDir).pipe(
|
|
818
890
|
Effect.catchTag("ProjectPackageError", (error) =>
|
|
819
891
|
error.message.startsWith("package.json not found:") ? Effect.void : Effect.fail(error),
|
|
@@ -821,10 +893,11 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
821
893
|
);
|
|
822
894
|
const manager = yield* detectPackageManager(projectDir, projectPackage?.packageManager);
|
|
823
895
|
const commandPolicy = usesVitePlus
|
|
824
|
-
? renderVitePlusCommandPolicy(projectPackage?.scripts ?? {},
|
|
896
|
+
? renderVitePlusCommandPolicy(projectPackage?.scripts ?? {}, usesRecommendedVitePlusTasks)
|
|
825
897
|
: renderPackageScriptCommandPolicy(manager, projectPackage?.scripts ?? {});
|
|
826
898
|
const devKitInstructions = template
|
|
827
899
|
.replaceAll(DEV_KIT_SKILL_PATH_PLACEHOLDER, devKitSkillPath)
|
|
900
|
+
.replaceAll(EFFECT_INSTRUCTIONS_PLACEHOLDER, effectInstructions)
|
|
828
901
|
.replaceAll(PROJECT_COMMAND_POLICY_PLACEHOLDER, commandPolicy)
|
|
829
902
|
.trimEnd();
|
|
830
903
|
|
|
@@ -840,7 +913,7 @@ const readGeneratedFileTemplate = Effect.fn("readGeneratedFileTemplate")(functio
|
|
|
840
913
|
const templatePath = path.join(packageRoot, sourcePath);
|
|
841
914
|
|
|
842
915
|
if ((yield* observePath(templatePath)).kind !== "file") {
|
|
843
|
-
return yield*
|
|
916
|
+
return yield* InvalidProjectStateError.make({
|
|
844
917
|
message: `dev-kit generated file template is not a regular file: ${sourcePath}`,
|
|
845
918
|
});
|
|
846
919
|
}
|
|
@@ -865,7 +938,9 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
865
938
|
packageRoot,
|
|
866
939
|
projectDir,
|
|
867
940
|
sourceBySkill,
|
|
868
|
-
setup.vitePlus.quality.
|
|
941
|
+
setup.vitePlus.quality.workflow.enabled &&
|
|
942
|
+
setup.vitePlus.quality.workflow.typecheck.length === 1 &&
|
|
943
|
+
setup.vitePlus.quality.workflow.typecheck[0] === "vp run typecheck",
|
|
869
944
|
targets,
|
|
870
945
|
);
|
|
871
946
|
|
|
@@ -887,7 +962,7 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
887
962
|
: yield* observePath(source.absolute);
|
|
888
963
|
|
|
889
964
|
if (!setup.agentInstructions.enabled && sourceObservation?.kind !== "file") {
|
|
890
|
-
return yield*
|
|
965
|
+
return yield* InvalidProjectStateError.make({
|
|
891
966
|
message: "Claude instructions source is not a regular file: AGENTS.md",
|
|
892
967
|
});
|
|
893
968
|
}
|
|
@@ -905,23 +980,6 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
905
980
|
linkTarget,
|
|
906
981
|
});
|
|
907
982
|
}
|
|
908
|
-
if (setup.vitePlus.quality.config.enabled) {
|
|
909
|
-
const managed = yield* resolveManagedPath(projectDir, VITE_PLUS_CONFIG_PATH);
|
|
910
|
-
const template = yield* readGeneratedFileTemplate(packageRoot, VITE_PLUS_CONFIG_TEMPLATE);
|
|
911
|
-
const content = renderVitePlusConfigTemplate(template, setup.vitePlus.quality.config.typecheck);
|
|
912
|
-
|
|
913
|
-
outputs.push({
|
|
914
|
-
resourceId: "setup:vite-plus-config",
|
|
915
|
-
path: managed.relative,
|
|
916
|
-
sourcePath: VITE_PLUS_CONFIG_TEMPLATE,
|
|
917
|
-
mode: "copy",
|
|
918
|
-
kind: "file",
|
|
919
|
-
digest: yield* digestFileContent(content),
|
|
920
|
-
destination: managed.absolute,
|
|
921
|
-
content,
|
|
922
|
-
adoptIfExact: true,
|
|
923
|
-
});
|
|
924
|
-
}
|
|
925
983
|
if (setup.vitePlus.quality.workflow.enabled) {
|
|
926
984
|
const managed = yield* resolveManagedPath(projectDir, VITE_PLUS_GITHUB_ACTIONS_PATH);
|
|
927
985
|
const template = yield* readGeneratedFileTemplate(
|
|
@@ -958,7 +1016,7 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
958
1016
|
.filter((skill) => skill.name === duplicateOutput.name)
|
|
959
1017
|
.map((skill) => skill.selector);
|
|
960
1018
|
|
|
961
|
-
return yield*
|
|
1019
|
+
return yield* InvalidProjectStateError.make({
|
|
962
1020
|
message: `selected skills would both install as ${duplicateOutput.name}: ${selectors.join(", ")}`,
|
|
963
1021
|
});
|
|
964
1022
|
}
|
|
@@ -966,7 +1024,7 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
966
1024
|
const resolvedSource = sourceBySkill.get(skill.selector);
|
|
967
1025
|
|
|
968
1026
|
if (resolvedSource === undefined) {
|
|
969
|
-
return yield*
|
|
1027
|
+
return yield* InvalidProjectStateError.make({
|
|
970
1028
|
message: `skill source is unavailable: ${skill.selector}`,
|
|
971
1029
|
});
|
|
972
1030
|
}
|
|
@@ -974,7 +1032,7 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
974
1032
|
const sourceObservation = yield* observePath(source);
|
|
975
1033
|
|
|
976
1034
|
if (sourceObservation.kind !== "directory") {
|
|
977
|
-
return yield*
|
|
1035
|
+
return yield* InvalidProjectStateError.make({
|
|
978
1036
|
message: `skill source is not a directory: ${source}`,
|
|
979
1037
|
});
|
|
980
1038
|
}
|
|
@@ -1027,8 +1085,8 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
1027
1085
|
return outputs.sort((left, right) => left.path.localeCompare(right.path));
|
|
1028
1086
|
});
|
|
1029
1087
|
|
|
1030
|
-
const canonicalLock = (lock: DevKitLock): string => `${
|
|
1031
|
-
const canonicalState = (state: AppliedState): string => `${
|
|
1088
|
+
const canonicalLock = (lock: DevKitLock): string => `${encodeDevKitLockPrettyJson(lock)}\n`;
|
|
1089
|
+
const canonicalState = (state: AppliedState): string => `${encodeAppliedStatePrettyJson(state)}\n`;
|
|
1032
1090
|
|
|
1033
1091
|
const planDesiredOutputs = Effect.fn("planDesiredSkillOutputs")(function* (
|
|
1034
1092
|
projectDir: string,
|
|
@@ -1284,7 +1342,7 @@ const planDesiredOutputs = Effect.fn("planDesiredSkillOutputs")(function* (
|
|
|
1284
1342
|
const lockedPlanMatches = (current: DevKitLock, next: DevKitLock): boolean =>
|
|
1285
1343
|
current.toolVersion === next.toolVersion &&
|
|
1286
1344
|
current.manifestDigest === next.manifestDigest &&
|
|
1287
|
-
|
|
1345
|
+
encodeDevKitSetupJson(current.setup ?? {}) === encodeDevKitSetupJson(next.setup ?? {}) &&
|
|
1288
1346
|
current.outputs.length === next.outputs.length &&
|
|
1289
1347
|
current.outputs.every((output, index) => {
|
|
1290
1348
|
const nextOutput = next.outputs[index];
|
|
@@ -1318,11 +1376,11 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1318
1376
|
const manifest = normalizeManifest(yield* readManifest(manifestManaged.absolute));
|
|
1319
1377
|
|
|
1320
1378
|
const vitePlusQuality = manifest.setup.vitePlus.quality;
|
|
1321
|
-
const vitePlusQualityEnabled = vitePlusQuality.
|
|
1379
|
+
const vitePlusQualityEnabled = vitePlusQuality.workflow.enabled;
|
|
1322
1380
|
|
|
1323
1381
|
if (vitePlusQualityEnabled) {
|
|
1324
1382
|
if (!manifest.setup.effectTsgo.enabled) {
|
|
1325
|
-
return yield*
|
|
1383
|
+
return yield* InvalidProjectStateError.make({
|
|
1326
1384
|
message:
|
|
1327
1385
|
"setup.vitePlus.quality requires setup.effectTsgo.enabled so managed quality setup converges the Effect-patched compiler",
|
|
1328
1386
|
});
|
|
@@ -1332,15 +1390,10 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1332
1390
|
packageRoot,
|
|
1333
1391
|
manifest.setup.effectTsgo.typescriptPackage,
|
|
1334
1392
|
{
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
beforeChecks: vitePlusQuality.workflow.beforeChecks,
|
|
1340
|
-
typecheck: vitePlusQuality.workflow.typecheck,
|
|
1341
|
-
},
|
|
1342
|
-
}
|
|
1343
|
-
: {}),
|
|
1393
|
+
workflow: {
|
|
1394
|
+
beforeChecks: vitePlusQuality.workflow.beforeChecks,
|
|
1395
|
+
typecheck: vitePlusQuality.workflow.typecheck,
|
|
1396
|
+
},
|
|
1344
1397
|
},
|
|
1345
1398
|
);
|
|
1346
1399
|
}
|
|
@@ -1368,7 +1421,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1368
1421
|
|
|
1369
1422
|
for (const [family, familySkills] of Object.entries(skillFamilies)) {
|
|
1370
1423
|
if (availableSkills.includes(family)) {
|
|
1371
|
-
return yield*
|
|
1424
|
+
return yield* InvalidSkillCatalogError.make({
|
|
1372
1425
|
family,
|
|
1373
1426
|
message: `family name conflicts with a skill name: ${family}`,
|
|
1374
1427
|
});
|
|
@@ -1376,7 +1429,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1376
1429
|
const missing = familySkills.filter((skill) => !availableSkills.includes(skill));
|
|
1377
1430
|
|
|
1378
1431
|
if (missing.length > 0) {
|
|
1379
|
-
return yield*
|
|
1432
|
+
return yield* InvalidSkillCatalogError.make({
|
|
1380
1433
|
family,
|
|
1381
1434
|
message: `family references missing skills: ${missing.join(", ")}`,
|
|
1382
1435
|
});
|
|
@@ -1405,7 +1458,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1405
1458
|
const catalogSkill = catalogBySelector.get(selector);
|
|
1406
1459
|
|
|
1407
1460
|
if (catalogSkill === undefined) {
|
|
1408
|
-
return yield*
|
|
1461
|
+
return yield* InvalidProjectStateError.make({
|
|
1409
1462
|
message: `selected skill is unavailable: ${selector}`,
|
|
1410
1463
|
});
|
|
1411
1464
|
}
|
|
@@ -1422,7 +1475,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1422
1475
|
const nextLock: DevKitLock = {
|
|
1423
1476
|
version: 1,
|
|
1424
1477
|
toolVersion: DEV_KIT_VERSION,
|
|
1425
|
-
manifestDigest: yield* digestText(
|
|
1478
|
+
manifestDigest: yield* digestText(encodeManifestJson(manifest)),
|
|
1426
1479
|
setup: {
|
|
1427
1480
|
...(effectSource === undefined
|
|
1428
1481
|
? {}
|
|
@@ -1509,12 +1562,12 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1509
1562
|
]);
|
|
1510
1563
|
if (options.locked) {
|
|
1511
1564
|
if (!currentLock) {
|
|
1512
|
-
return yield*
|
|
1565
|
+
return yield* LockedPlanMismatchError.make({
|
|
1513
1566
|
message: "dev-kit.lock.json is required with --locked",
|
|
1514
1567
|
});
|
|
1515
1568
|
}
|
|
1516
1569
|
if (!lockedPlanMatches(currentLock, nextLock)) {
|
|
1517
|
-
return yield*
|
|
1570
|
+
return yield* LockedPlanMismatchError.make({
|
|
1518
1571
|
message: "manifest or packaged skills differ from dev-kit.lock.json",
|
|
1519
1572
|
});
|
|
1520
1573
|
}
|
|
@@ -1534,7 +1587,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1534
1587
|
);
|
|
1535
1588
|
|
|
1536
1589
|
if (manifest.setup.claudeInstructions.enabled && removesClaudeInstructionsSource) {
|
|
1537
|
-
return yield*
|
|
1590
|
+
return yield* InvalidProjectStateError.make({
|
|
1538
1591
|
message:
|
|
1539
1592
|
"cannot disable agentInstructions while claudeInstructions still links to an AGENTS.md that would be removed",
|
|
1540
1593
|
});
|
|
@@ -1551,8 +1604,10 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1551
1604
|
nextLock,
|
|
1552
1605
|
nextState: planned.nextState,
|
|
1553
1606
|
metadataChanged:
|
|
1554
|
-
|
|
1555
|
-
|
|
1607
|
+
currentLock === undefined ||
|
|
1608
|
+
encodeDevKitLockJson(currentLock) !== encodeDevKitLockJson(nextLock) ||
|
|
1609
|
+
currentState === undefined ||
|
|
1610
|
+
encodeAppliedStateJson(currentState) !== encodeAppliedStateJson(planned.nextState),
|
|
1556
1611
|
} satisfies SkillPlan;
|
|
1557
1612
|
});
|
|
1558
1613
|
|
|
@@ -1652,12 +1707,11 @@ const verifyPackageSkillSources = Effect.fn("verifyPackageSkillSources")(functio
|
|
|
1652
1707
|
const observation = yield* observePath(resolved.path);
|
|
1653
1708
|
|
|
1654
1709
|
if (
|
|
1655
|
-
resolved.path !== action.desired.source ||
|
|
1656
1710
|
resolved.version !== catalog.version ||
|
|
1657
1711
|
observation.kind !== "directory" ||
|
|
1658
1712
|
observation.digest !== catalog.digest
|
|
1659
1713
|
) {
|
|
1660
|
-
return yield*
|
|
1714
|
+
return yield* ApplyRaceError.make({ path: action.desired.source });
|
|
1661
1715
|
}
|
|
1662
1716
|
verified.add(key);
|
|
1663
1717
|
}
|
|
@@ -1667,7 +1721,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1667
1721
|
const conflicts = plan.actions.filter((action) => action.action === "conflict");
|
|
1668
1722
|
|
|
1669
1723
|
if (conflicts.length > 0) {
|
|
1670
|
-
return yield*
|
|
1724
|
+
return yield* PlanConflictError.make({
|
|
1671
1725
|
conflicts: conflicts.map((action) =>
|
|
1672
1726
|
action.action === "conflict" ? `${action.path}: ${action.reason}` : "",
|
|
1673
1727
|
),
|
|
@@ -1677,7 +1731,9 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1677
1731
|
const fs = yield* FileSystem.FileSystem;
|
|
1678
1732
|
const path = yield* Path.Path;
|
|
1679
1733
|
const mutating = plan.actions.filter(
|
|
1680
|
-
(
|
|
1734
|
+
(
|
|
1735
|
+
action,
|
|
1736
|
+
): action is Extract<SkillPlanAction, { readonly action: "create" | "update" | "remove" }> =>
|
|
1681
1737
|
action.action === "create" || action.action === "update" || action.action === "remove",
|
|
1682
1738
|
);
|
|
1683
1739
|
|
|
@@ -1686,7 +1742,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1686
1742
|
action.action === "remove" ? action.destination : action.desired.destination;
|
|
1687
1743
|
|
|
1688
1744
|
if (!observationsEqual(yield* observePath(destination), action.observed)) {
|
|
1689
|
-
return yield*
|
|
1745
|
+
return yield* ApplyRaceError.make({
|
|
1690
1746
|
path: action.action === "remove" ? action.previous.path : action.desired.path,
|
|
1691
1747
|
});
|
|
1692
1748
|
}
|
|
@@ -1702,7 +1758,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1702
1758
|
});
|
|
1703
1759
|
const stageDir = path.join(tempDir, "stage");
|
|
1704
1760
|
const backupDir = path.join(tempDir, "backup");
|
|
1705
|
-
const
|
|
1761
|
+
const stagedByAction = new Map<(typeof mutating)[number], string>();
|
|
1706
1762
|
let stageIndex = 0;
|
|
1707
1763
|
|
|
1708
1764
|
for (const action of mutating) {
|
|
@@ -1718,7 +1774,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1718
1774
|
yield* fs.writeFileString(staged, action.stagedContent);
|
|
1719
1775
|
} else if (action.action === "remove") {
|
|
1720
1776
|
if (action.stagedContent === undefined) {
|
|
1721
|
-
return yield*
|
|
1777
|
+
return yield* InvalidProjectStateError.make({
|
|
1722
1778
|
message: `missing staged content for ${action.previous.resourceId}`,
|
|
1723
1779
|
});
|
|
1724
1780
|
}
|
|
@@ -1733,7 +1789,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1733
1789
|
const symbolicLink = yield* findNestedSymbolicLink(staged);
|
|
1734
1790
|
|
|
1735
1791
|
if (symbolicLink !== undefined) {
|
|
1736
|
-
return yield*
|
|
1792
|
+
return yield* InvalidProjectStateError.make({
|
|
1737
1793
|
message: `staged skill contains a symlink: ${action.desired.path}`,
|
|
1738
1794
|
});
|
|
1739
1795
|
}
|
|
@@ -1753,7 +1809,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1753
1809
|
: undefined;
|
|
1754
1810
|
|
|
1755
1811
|
if (observation.kind !== "file" || digest !== action.desired.digest) {
|
|
1756
|
-
return yield*
|
|
1812
|
+
return yield* InvalidProjectStateError.make({
|
|
1757
1813
|
message: `staged output digest mismatch for ${action.desired.path}`,
|
|
1758
1814
|
});
|
|
1759
1815
|
}
|
|
@@ -1761,15 +1817,12 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1761
1817
|
observation.kind !== action.desired.kind ||
|
|
1762
1818
|
observation.digest !== action.desired.digest
|
|
1763
1819
|
) {
|
|
1764
|
-
return yield*
|
|
1820
|
+
return yield* InvalidProjectStateError.make({
|
|
1765
1821
|
message: `staged output digest mismatch for ${action.desired.path}`,
|
|
1766
1822
|
});
|
|
1767
1823
|
}
|
|
1768
1824
|
}
|
|
1769
|
-
|
|
1770
|
-
action.action === "remove" ? action.previous.resourceId : action.desired.resourceId,
|
|
1771
|
-
staged,
|
|
1772
|
-
);
|
|
1825
|
+
stagedByAction.set(action, staged);
|
|
1773
1826
|
}
|
|
1774
1827
|
|
|
1775
1828
|
yield* verifyPackageSkillSources(plan);
|
|
@@ -1791,16 +1844,13 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1791
1844
|
let replacementIndex = 0;
|
|
1792
1845
|
|
|
1793
1846
|
for (const action of mutating) {
|
|
1794
|
-
const staged =
|
|
1795
|
-
action.action === "remove"
|
|
1796
|
-
? stagedByResource.get(action.previous.resourceId)
|
|
1797
|
-
: stagedByResource.get(action.desired.resourceId);
|
|
1847
|
+
const staged = stagedByAction.get(action);
|
|
1798
1848
|
|
|
1799
1849
|
if (
|
|
1800
1850
|
(action.action !== "remove" || action.stagedContent !== undefined) &&
|
|
1801
1851
|
staged === undefined
|
|
1802
1852
|
) {
|
|
1803
|
-
return yield*
|
|
1853
|
+
return yield* InvalidProjectStateError.make({
|
|
1804
1854
|
message: `missing staged output for ${
|
|
1805
1855
|
action.action === "remove" ? action.previous.resourceId : action.desired.resourceId
|
|
1806
1856
|
}`,
|
|
@@ -1849,7 +1899,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1849
1899
|
replacement.expected !== undefined &&
|
|
1850
1900
|
!observationsEqual(observed, replacement.expected)
|
|
1851
1901
|
) {
|
|
1852
|
-
return yield*
|
|
1902
|
+
return yield* ApplyRaceError.make({ path: replacement.path });
|
|
1853
1903
|
}
|
|
1854
1904
|
if (observed.kind !== "missing") {
|
|
1855
1905
|
yield* fs.makeDirectory(path.dirname(replacement.backup), { recursive: true });
|
|
@@ -1887,7 +1937,7 @@ export const runProjectSkillPlan = Effect.fn("runProjectSkillPlan")(function* (
|
|
|
1887
1937
|
const conflicts = plan.actions.filter((action) => action.action === "conflict");
|
|
1888
1938
|
|
|
1889
1939
|
if (conflicts.length > 0) {
|
|
1890
|
-
return yield*
|
|
1940
|
+
return yield* PlanConflictError.make({
|
|
1891
1941
|
conflicts: conflicts.map((action) =>
|
|
1892
1942
|
action.action === "conflict" ? `${action.path}: ${action.reason}` : "",
|
|
1893
1943
|
),
|
|
@@ -1897,7 +1947,7 @@ export const runProjectSkillPlan = Effect.fn("runProjectSkillPlan")(function* (
|
|
|
1897
1947
|
|
|
1898
1948
|
yield* acquireProjectProcessLock(plan.projectDir);
|
|
1899
1949
|
const replanned = yield* planProjectSkills(options);
|
|
1900
|
-
const originalSignature =
|
|
1950
|
+
const originalSignature = encodePlanSnapshotJson({
|
|
1901
1951
|
actions: plan.actions,
|
|
1902
1952
|
effectSource: plan.effectSource,
|
|
1903
1953
|
effectTsgo: plan.effectTsgo,
|
|
@@ -1905,7 +1955,7 @@ export const runProjectSkillPlan = Effect.fn("runProjectSkillPlan")(function* (
|
|
|
1905
1955
|
nextLock: plan.nextLock,
|
|
1906
1956
|
nextState: plan.nextState,
|
|
1907
1957
|
});
|
|
1908
|
-
const nextSignature =
|
|
1958
|
+
const nextSignature = encodePlanSnapshotJson({
|
|
1909
1959
|
actions: replanned.actions,
|
|
1910
1960
|
effectSource: replanned.effectSource,
|
|
1911
1961
|
effectTsgo: replanned.effectTsgo,
|
|
@@ -1915,7 +1965,7 @@ export const runProjectSkillPlan = Effect.fn("runProjectSkillPlan")(function* (
|
|
|
1915
1965
|
});
|
|
1916
1966
|
|
|
1917
1967
|
if (originalSignature !== nextSignature) {
|
|
1918
|
-
return yield*
|
|
1968
|
+
return yield* ApplyRaceError.make({ path: "project state" });
|
|
1919
1969
|
}
|
|
1920
1970
|
const changes = plannedChangeCount(replanned);
|
|
1921
1971
|
|