@danieljvdm/dev-kit 0.11.3 → 0.12.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 +70 -76
- 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/dev-kit/SKILL.md +58 -46
- 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 +49 -24
- package/src/gitignore.ts +5 -5
- package/src/index.ts +0 -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 +16 -14
- package/src/skill-selector.ts +12 -0
- package/src/sync.ts +170 -120
- 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"],
|
|
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;
|
|
@@ -527,14 +570,16 @@ const parseStructuredFile = Effect.fn("parseStructuredFile")(function* <A>(
|
|
|
527
570
|
const first = errors[0];
|
|
528
571
|
|
|
529
572
|
if (first !== undefined) {
|
|
530
|
-
return yield*
|
|
573
|
+
return yield* StructuredFileError.make({
|
|
531
574
|
path: filePath,
|
|
532
575
|
message: `${printParseErrorCode(first.error)} at offset ${first.offset}`,
|
|
533
576
|
});
|
|
534
577
|
}
|
|
535
578
|
|
|
536
579
|
return yield* Schema.decodeUnknownEffect(schema)(parsed).pipe(
|
|
537
|
-
Effect.mapError((cause) =>
|
|
580
|
+
Effect.mapError((cause) =>
|
|
581
|
+
StructuredFileError.make({ path: filePath, message: cause.message }),
|
|
582
|
+
),
|
|
538
583
|
);
|
|
539
584
|
});
|
|
540
585
|
|
|
@@ -542,7 +587,7 @@ const readManifest = Effect.fn("readManifest")(function* (manifestPath: string)
|
|
|
542
587
|
const fs = yield* FileSystem.FileSystem;
|
|
543
588
|
|
|
544
589
|
if (!(yield* fs.exists(manifestPath))) {
|
|
545
|
-
return yield*
|
|
590
|
+
return yield* ManifestNotFoundError.make({ path: manifestPath });
|
|
546
591
|
}
|
|
547
592
|
const raw = yield* fs.readFileString(manifestPath);
|
|
548
593
|
|
|
@@ -577,7 +622,7 @@ const expandSelection = (
|
|
|
577
622
|
} else if (availableSkills.includes(name) || parseSkillSelector(name)?.type === "package") {
|
|
578
623
|
selected.add(name);
|
|
579
624
|
} else {
|
|
580
|
-
return Effect.fail(
|
|
625
|
+
return Effect.fail(UnknownSkillOrFamilyError.make({ name, known }));
|
|
581
626
|
}
|
|
582
627
|
}
|
|
583
628
|
for (const name of exclude) {
|
|
@@ -600,7 +645,7 @@ const resolveManagedPath = Effect.fn("resolveManagedPath")(function* (
|
|
|
600
645
|
const path = yield* Path.Path;
|
|
601
646
|
|
|
602
647
|
if (candidate.length === 0 || path.isAbsolute(candidate)) {
|
|
603
|
-
return yield*
|
|
648
|
+
return yield* UnsafeManagedPathError.make({
|
|
604
649
|
path: candidate,
|
|
605
650
|
reason: "must be a non-empty project-relative path",
|
|
606
651
|
});
|
|
@@ -614,7 +659,7 @@ const resolveManagedPath = Effect.fn("resolveManagedPath")(function* (
|
|
|
614
659
|
relative.startsWith(`..${path.sep}`) ||
|
|
615
660
|
path.isAbsolute(relative)
|
|
616
661
|
) {
|
|
617
|
-
return yield*
|
|
662
|
+
return yield* UnsafeManagedPathError.make({
|
|
618
663
|
path: candidate,
|
|
619
664
|
reason: "resolves outside the project",
|
|
620
665
|
});
|
|
@@ -628,7 +673,7 @@ const resolveManagedPath = Effect.fn("resolveManagedPath")(function* (
|
|
|
628
673
|
const target = yield* observeSymbolicLink(ancestor);
|
|
629
674
|
|
|
630
675
|
if (target.kind === "symlink") {
|
|
631
|
-
return yield*
|
|
676
|
+
return yield* UnsafeManagedPathError.make({
|
|
632
677
|
path: candidate,
|
|
633
678
|
reason: `ancestor is a symlink: ${portablePath(path, path.relative(projectDir, ancestor))}`,
|
|
634
679
|
});
|
|
@@ -658,14 +703,14 @@ const validateReservedPaths = Effect.fn("validateReservedPaths")(function* (
|
|
|
658
703
|
if (current === undefined) continue;
|
|
659
704
|
for (const other of reserved.slice(index + 1)) {
|
|
660
705
|
if (pathsOverlap(current.path, other.path)) {
|
|
661
|
-
return yield*
|
|
706
|
+
return yield* InvalidProjectStateError.make({
|
|
662
707
|
message: `${current.label} path ${current.path} overlaps ${other.label} path ${other.path}`,
|
|
663
708
|
});
|
|
664
709
|
}
|
|
665
710
|
}
|
|
666
711
|
for (const outputPath of outputPaths) {
|
|
667
712
|
if (pathsOverlap(current.path, outputPath)) {
|
|
668
|
-
return yield*
|
|
713
|
+
return yield* InvalidProjectStateError.make({
|
|
669
714
|
message: `${current.label} path ${current.path} overlaps managed output ${outputPath}`,
|
|
670
715
|
});
|
|
671
716
|
}
|
|
@@ -673,28 +718,27 @@ const validateReservedPaths = Effect.fn("validateReservedPaths")(function* (
|
|
|
673
718
|
}
|
|
674
719
|
});
|
|
675
720
|
|
|
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
|
-
});
|
|
721
|
+
const outputIdentity = (output: ManagedOutput) => encodeManagedOutputJson(output);
|
|
687
722
|
|
|
688
723
|
const outputOwnershipIdentity = (output: ManagedOutput) =>
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
724
|
+
encodeOutputOwnershipIdentityJson(
|
|
725
|
+
"skill" in output
|
|
726
|
+
? {
|
|
727
|
+
resourceId: output.resourceId,
|
|
728
|
+
path: output.path,
|
|
729
|
+
mode: output.mode,
|
|
730
|
+
kind: output.kind,
|
|
731
|
+
skill: output.skill,
|
|
732
|
+
target: output.target,
|
|
733
|
+
}
|
|
734
|
+
: {
|
|
735
|
+
resourceId: output.resourceId,
|
|
736
|
+
path: output.path,
|
|
737
|
+
mode: output.mode,
|
|
738
|
+
kind: output.kind,
|
|
739
|
+
sourcePath: output.sourcePath,
|
|
740
|
+
},
|
|
741
|
+
);
|
|
698
742
|
|
|
699
743
|
const usesRawFileModeDigests = (toolVersion: string): boolean => {
|
|
700
744
|
const match = /^(\d+)\.(\d+)\./.exec(toolVersion);
|
|
@@ -717,12 +761,12 @@ const validateInventory = Effect.fn("validateManagedInventory")(function* (
|
|
|
717
761
|
|
|
718
762
|
for (const output of outputs) {
|
|
719
763
|
if (ids.has(output.resourceId)) {
|
|
720
|
-
return yield*
|
|
764
|
+
return yield* InvalidProjectStateError.make({
|
|
721
765
|
message: `${label} contains duplicate resource id ${output.resourceId}`,
|
|
722
766
|
});
|
|
723
767
|
}
|
|
724
768
|
if (paths.has(output.path)) {
|
|
725
|
-
return yield*
|
|
769
|
+
return yield* InvalidProjectStateError.make({
|
|
726
770
|
message: `${label} contains duplicate path ${output.path}`,
|
|
727
771
|
});
|
|
728
772
|
}
|
|
@@ -737,7 +781,7 @@ const validateInventory = Effect.fn("validateManagedInventory")(function* (
|
|
|
737
781
|
|
|
738
782
|
if (previous === undefined || current === undefined) continue;
|
|
739
783
|
if (current.startsWith(`${previous}/`)) {
|
|
740
|
-
return yield*
|
|
784
|
+
return yield* InvalidProjectStateError.make({
|
|
741
785
|
message: `${label} contains overlapping paths ${previous} and ${current}`,
|
|
742
786
|
});
|
|
743
787
|
}
|
|
@@ -761,7 +805,7 @@ const validateCrossInventoryPaths = Effect.fn("validateCrossInventoryPaths")(fun
|
|
|
761
805
|
|
|
762
806
|
if (previous === undefined || current === undefined) continue;
|
|
763
807
|
if (current.startsWith(`${previous}/`)) {
|
|
764
|
-
return yield*
|
|
808
|
+
return yield* InvalidProjectStateError.make({
|
|
765
809
|
message: `desired and previously owned paths overlap: ${previous} and ${current}`,
|
|
766
810
|
});
|
|
767
811
|
}
|
|
@@ -772,7 +816,7 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
772
816
|
packageRoot: string,
|
|
773
817
|
projectDir: string,
|
|
774
818
|
sourceBySkill: ReadonlyMap<string, ResolvedSkillSource>,
|
|
775
|
-
|
|
819
|
+
usesRecommendedVitePlusTasks: boolean,
|
|
776
820
|
targets: ReturnType<typeof normalizeManifest>["targets"],
|
|
777
821
|
) {
|
|
778
822
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -780,19 +824,24 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
780
824
|
const templatePath = path.join(packageRoot, AGENT_INSTRUCTIONS_TEMPLATE);
|
|
781
825
|
|
|
782
826
|
if ((yield* observePath(templatePath)).kind !== "file") {
|
|
783
|
-
return yield*
|
|
827
|
+
return yield* InvalidProjectStateError.make({
|
|
784
828
|
message: `dev-kit agent instructions template is not a regular file: ${AGENT_INSTRUCTIONS_TEMPLATE}`,
|
|
785
829
|
});
|
|
786
830
|
}
|
|
787
831
|
const template = yield* fs.readFileString(templatePath);
|
|
788
832
|
|
|
789
833
|
if (!template.includes(DEV_KIT_SKILL_PATH_PLACEHOLDER)) {
|
|
790
|
-
return yield*
|
|
834
|
+
return yield* InvalidProjectStateError.make({
|
|
791
835
|
message: `dev-kit agent instructions template is missing ${DEV_KIT_SKILL_PATH_PLACEHOLDER}`,
|
|
792
836
|
});
|
|
793
837
|
}
|
|
838
|
+
if (!template.includes(EFFECT_INSTRUCTIONS_PLACEHOLDER)) {
|
|
839
|
+
return yield* InvalidProjectStateError.make({
|
|
840
|
+
message: `dev-kit agent instructions template is missing ${EFFECT_INSTRUCTIONS_PLACEHOLDER}`,
|
|
841
|
+
});
|
|
842
|
+
}
|
|
794
843
|
if (!template.includes(PROJECT_COMMAND_POLICY_PLACEHOLDER)) {
|
|
795
|
-
return yield*
|
|
844
|
+
return yield* InvalidProjectStateError.make({
|
|
796
845
|
message: `dev-kit agent instructions template is missing ${PROJECT_COMMAND_POLICY_PLACEHOLDER}`,
|
|
797
846
|
});
|
|
798
847
|
}
|
|
@@ -813,7 +862,24 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
813
862
|
path.join(devKitSkill.linkPath ?? devKitSkill.path, "SKILL.md"),
|
|
814
863
|
),
|
|
815
864
|
);
|
|
816
|
-
const
|
|
865
|
+
const directDependencyNames = yield* readDirectDependencyNames(projectDir);
|
|
866
|
+
const usesVitePlus = directDependencyNames.includes("vite-plus");
|
|
867
|
+
const effectInstructions =
|
|
868
|
+
directDependencyNames.includes("effect") &&
|
|
869
|
+
(yield* observePath(path.join(projectDir, "node_modules", "effect", "AGENTS.md"))).kind ===
|
|
870
|
+
"file"
|
|
871
|
+
? `# Learning more about the Effect
|
|
872
|
+
|
|
873
|
+
This repository uses the Effect Typescript library.
|
|
874
|
+
|
|
875
|
+
Before writing any Effect code, first read \`node_modules/effect/AGENTS.md\`
|
|
876
|
+
**completely**, and follow the links in the file when required.
|
|
877
|
+
|
|
878
|
+
If you need to learn more about particular Effect apis and concepts that the
|
|
879
|
+
guide doesn't cover, search through the source code in \`node_modules/effect/src\`.
|
|
880
|
+
|
|
881
|
+
`
|
|
882
|
+
: "";
|
|
817
883
|
const projectPackage = yield* readProjectPackage(projectDir).pipe(
|
|
818
884
|
Effect.catchTag("ProjectPackageError", (error) =>
|
|
819
885
|
error.message.startsWith("package.json not found:") ? Effect.void : Effect.fail(error),
|
|
@@ -821,10 +887,11 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
821
887
|
);
|
|
822
888
|
const manager = yield* detectPackageManager(projectDir, projectPackage?.packageManager);
|
|
823
889
|
const commandPolicy = usesVitePlus
|
|
824
|
-
? renderVitePlusCommandPolicy(projectPackage?.scripts ?? {},
|
|
890
|
+
? renderVitePlusCommandPolicy(projectPackage?.scripts ?? {}, usesRecommendedVitePlusTasks)
|
|
825
891
|
: renderPackageScriptCommandPolicy(manager, projectPackage?.scripts ?? {});
|
|
826
892
|
const devKitInstructions = template
|
|
827
893
|
.replaceAll(DEV_KIT_SKILL_PATH_PLACEHOLDER, devKitSkillPath)
|
|
894
|
+
.replaceAll(EFFECT_INSTRUCTIONS_PLACEHOLDER, effectInstructions)
|
|
828
895
|
.replaceAll(PROJECT_COMMAND_POLICY_PLACEHOLDER, commandPolicy)
|
|
829
896
|
.trimEnd();
|
|
830
897
|
|
|
@@ -840,7 +907,7 @@ const readGeneratedFileTemplate = Effect.fn("readGeneratedFileTemplate")(functio
|
|
|
840
907
|
const templatePath = path.join(packageRoot, sourcePath);
|
|
841
908
|
|
|
842
909
|
if ((yield* observePath(templatePath)).kind !== "file") {
|
|
843
|
-
return yield*
|
|
910
|
+
return yield* InvalidProjectStateError.make({
|
|
844
911
|
message: `dev-kit generated file template is not a regular file: ${sourcePath}`,
|
|
845
912
|
});
|
|
846
913
|
}
|
|
@@ -865,7 +932,9 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
865
932
|
packageRoot,
|
|
866
933
|
projectDir,
|
|
867
934
|
sourceBySkill,
|
|
868
|
-
setup.vitePlus.quality.
|
|
935
|
+
setup.vitePlus.quality.workflow.enabled &&
|
|
936
|
+
setup.vitePlus.quality.workflow.typecheck.length === 1 &&
|
|
937
|
+
setup.vitePlus.quality.workflow.typecheck[0] === "vp run typecheck",
|
|
869
938
|
targets,
|
|
870
939
|
);
|
|
871
940
|
|
|
@@ -887,7 +956,7 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
887
956
|
: yield* observePath(source.absolute);
|
|
888
957
|
|
|
889
958
|
if (!setup.agentInstructions.enabled && sourceObservation?.kind !== "file") {
|
|
890
|
-
return yield*
|
|
959
|
+
return yield* InvalidProjectStateError.make({
|
|
891
960
|
message: "Claude instructions source is not a regular file: AGENTS.md",
|
|
892
961
|
});
|
|
893
962
|
}
|
|
@@ -905,23 +974,6 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
905
974
|
linkTarget,
|
|
906
975
|
});
|
|
907
976
|
}
|
|
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
977
|
if (setup.vitePlus.quality.workflow.enabled) {
|
|
926
978
|
const managed = yield* resolveManagedPath(projectDir, VITE_PLUS_GITHUB_ACTIONS_PATH);
|
|
927
979
|
const template = yield* readGeneratedFileTemplate(
|
|
@@ -958,7 +1010,7 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
958
1010
|
.filter((skill) => skill.name === duplicateOutput.name)
|
|
959
1011
|
.map((skill) => skill.selector);
|
|
960
1012
|
|
|
961
|
-
return yield*
|
|
1013
|
+
return yield* InvalidProjectStateError.make({
|
|
962
1014
|
message: `selected skills would both install as ${duplicateOutput.name}: ${selectors.join(", ")}`,
|
|
963
1015
|
});
|
|
964
1016
|
}
|
|
@@ -966,7 +1018,7 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
966
1018
|
const resolvedSource = sourceBySkill.get(skill.selector);
|
|
967
1019
|
|
|
968
1020
|
if (resolvedSource === undefined) {
|
|
969
|
-
return yield*
|
|
1021
|
+
return yield* InvalidProjectStateError.make({
|
|
970
1022
|
message: `skill source is unavailable: ${skill.selector}`,
|
|
971
1023
|
});
|
|
972
1024
|
}
|
|
@@ -974,7 +1026,7 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
974
1026
|
const sourceObservation = yield* observePath(source);
|
|
975
1027
|
|
|
976
1028
|
if (sourceObservation.kind !== "directory") {
|
|
977
|
-
return yield*
|
|
1029
|
+
return yield* InvalidProjectStateError.make({
|
|
978
1030
|
message: `skill source is not a directory: ${source}`,
|
|
979
1031
|
});
|
|
980
1032
|
}
|
|
@@ -1027,8 +1079,8 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
1027
1079
|
return outputs.sort((left, right) => left.path.localeCompare(right.path));
|
|
1028
1080
|
});
|
|
1029
1081
|
|
|
1030
|
-
const canonicalLock = (lock: DevKitLock): string => `${
|
|
1031
|
-
const canonicalState = (state: AppliedState): string => `${
|
|
1082
|
+
const canonicalLock = (lock: DevKitLock): string => `${encodeDevKitLockPrettyJson(lock)}\n`;
|
|
1083
|
+
const canonicalState = (state: AppliedState): string => `${encodeAppliedStatePrettyJson(state)}\n`;
|
|
1032
1084
|
|
|
1033
1085
|
const planDesiredOutputs = Effect.fn("planDesiredSkillOutputs")(function* (
|
|
1034
1086
|
projectDir: string,
|
|
@@ -1284,7 +1336,7 @@ const planDesiredOutputs = Effect.fn("planDesiredSkillOutputs")(function* (
|
|
|
1284
1336
|
const lockedPlanMatches = (current: DevKitLock, next: DevKitLock): boolean =>
|
|
1285
1337
|
current.toolVersion === next.toolVersion &&
|
|
1286
1338
|
current.manifestDigest === next.manifestDigest &&
|
|
1287
|
-
|
|
1339
|
+
encodeDevKitSetupJson(current.setup ?? {}) === encodeDevKitSetupJson(next.setup ?? {}) &&
|
|
1288
1340
|
current.outputs.length === next.outputs.length &&
|
|
1289
1341
|
current.outputs.every((output, index) => {
|
|
1290
1342
|
const nextOutput = next.outputs[index];
|
|
@@ -1318,11 +1370,11 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1318
1370
|
const manifest = normalizeManifest(yield* readManifest(manifestManaged.absolute));
|
|
1319
1371
|
|
|
1320
1372
|
const vitePlusQuality = manifest.setup.vitePlus.quality;
|
|
1321
|
-
const vitePlusQualityEnabled = vitePlusQuality.
|
|
1373
|
+
const vitePlusQualityEnabled = vitePlusQuality.workflow.enabled;
|
|
1322
1374
|
|
|
1323
1375
|
if (vitePlusQualityEnabled) {
|
|
1324
1376
|
if (!manifest.setup.effectTsgo.enabled) {
|
|
1325
|
-
return yield*
|
|
1377
|
+
return yield* InvalidProjectStateError.make({
|
|
1326
1378
|
message:
|
|
1327
1379
|
"setup.vitePlus.quality requires setup.effectTsgo.enabled so managed quality setup converges the Effect-patched compiler",
|
|
1328
1380
|
});
|
|
@@ -1332,15 +1384,10 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1332
1384
|
packageRoot,
|
|
1333
1385
|
manifest.setup.effectTsgo.typescriptPackage,
|
|
1334
1386
|
{
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
beforeChecks: vitePlusQuality.workflow.beforeChecks,
|
|
1340
|
-
typecheck: vitePlusQuality.workflow.typecheck,
|
|
1341
|
-
},
|
|
1342
|
-
}
|
|
1343
|
-
: {}),
|
|
1387
|
+
workflow: {
|
|
1388
|
+
beforeChecks: vitePlusQuality.workflow.beforeChecks,
|
|
1389
|
+
typecheck: vitePlusQuality.workflow.typecheck,
|
|
1390
|
+
},
|
|
1344
1391
|
},
|
|
1345
1392
|
);
|
|
1346
1393
|
}
|
|
@@ -1368,7 +1415,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1368
1415
|
|
|
1369
1416
|
for (const [family, familySkills] of Object.entries(skillFamilies)) {
|
|
1370
1417
|
if (availableSkills.includes(family)) {
|
|
1371
|
-
return yield*
|
|
1418
|
+
return yield* InvalidSkillCatalogError.make({
|
|
1372
1419
|
family,
|
|
1373
1420
|
message: `family name conflicts with a skill name: ${family}`,
|
|
1374
1421
|
});
|
|
@@ -1376,7 +1423,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1376
1423
|
const missing = familySkills.filter((skill) => !availableSkills.includes(skill));
|
|
1377
1424
|
|
|
1378
1425
|
if (missing.length > 0) {
|
|
1379
|
-
return yield*
|
|
1426
|
+
return yield* InvalidSkillCatalogError.make({
|
|
1380
1427
|
family,
|
|
1381
1428
|
message: `family references missing skills: ${missing.join(", ")}`,
|
|
1382
1429
|
});
|
|
@@ -1405,7 +1452,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1405
1452
|
const catalogSkill = catalogBySelector.get(selector);
|
|
1406
1453
|
|
|
1407
1454
|
if (catalogSkill === undefined) {
|
|
1408
|
-
return yield*
|
|
1455
|
+
return yield* InvalidProjectStateError.make({
|
|
1409
1456
|
message: `selected skill is unavailable: ${selector}`,
|
|
1410
1457
|
});
|
|
1411
1458
|
}
|
|
@@ -1422,7 +1469,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1422
1469
|
const nextLock: DevKitLock = {
|
|
1423
1470
|
version: 1,
|
|
1424
1471
|
toolVersion: DEV_KIT_VERSION,
|
|
1425
|
-
manifestDigest: yield* digestText(
|
|
1472
|
+
manifestDigest: yield* digestText(encodeManifestJson(manifest)),
|
|
1426
1473
|
setup: {
|
|
1427
1474
|
...(effectSource === undefined
|
|
1428
1475
|
? {}
|
|
@@ -1509,12 +1556,12 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1509
1556
|
]);
|
|
1510
1557
|
if (options.locked) {
|
|
1511
1558
|
if (!currentLock) {
|
|
1512
|
-
return yield*
|
|
1559
|
+
return yield* LockedPlanMismatchError.make({
|
|
1513
1560
|
message: "dev-kit.lock.json is required with --locked",
|
|
1514
1561
|
});
|
|
1515
1562
|
}
|
|
1516
1563
|
if (!lockedPlanMatches(currentLock, nextLock)) {
|
|
1517
|
-
return yield*
|
|
1564
|
+
return yield* LockedPlanMismatchError.make({
|
|
1518
1565
|
message: "manifest or packaged skills differ from dev-kit.lock.json",
|
|
1519
1566
|
});
|
|
1520
1567
|
}
|
|
@@ -1534,7 +1581,7 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1534
1581
|
);
|
|
1535
1582
|
|
|
1536
1583
|
if (manifest.setup.claudeInstructions.enabled && removesClaudeInstructionsSource) {
|
|
1537
|
-
return yield*
|
|
1584
|
+
return yield* InvalidProjectStateError.make({
|
|
1538
1585
|
message:
|
|
1539
1586
|
"cannot disable agentInstructions while claudeInstructions still links to an AGENTS.md that would be removed",
|
|
1540
1587
|
});
|
|
@@ -1551,8 +1598,10 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1551
1598
|
nextLock,
|
|
1552
1599
|
nextState: planned.nextState,
|
|
1553
1600
|
metadataChanged:
|
|
1554
|
-
|
|
1555
|
-
|
|
1601
|
+
currentLock === undefined ||
|
|
1602
|
+
encodeDevKitLockJson(currentLock) !== encodeDevKitLockJson(nextLock) ||
|
|
1603
|
+
currentState === undefined ||
|
|
1604
|
+
encodeAppliedStateJson(currentState) !== encodeAppliedStateJson(planned.nextState),
|
|
1556
1605
|
} satisfies SkillPlan;
|
|
1557
1606
|
});
|
|
1558
1607
|
|
|
@@ -1652,12 +1701,11 @@ const verifyPackageSkillSources = Effect.fn("verifyPackageSkillSources")(functio
|
|
|
1652
1701
|
const observation = yield* observePath(resolved.path);
|
|
1653
1702
|
|
|
1654
1703
|
if (
|
|
1655
|
-
resolved.path !== action.desired.source ||
|
|
1656
1704
|
resolved.version !== catalog.version ||
|
|
1657
1705
|
observation.kind !== "directory" ||
|
|
1658
1706
|
observation.digest !== catalog.digest
|
|
1659
1707
|
) {
|
|
1660
|
-
return yield*
|
|
1708
|
+
return yield* ApplyRaceError.make({ path: action.desired.source });
|
|
1661
1709
|
}
|
|
1662
1710
|
verified.add(key);
|
|
1663
1711
|
}
|
|
@@ -1667,7 +1715,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1667
1715
|
const conflicts = plan.actions.filter((action) => action.action === "conflict");
|
|
1668
1716
|
|
|
1669
1717
|
if (conflicts.length > 0) {
|
|
1670
|
-
return yield*
|
|
1718
|
+
return yield* PlanConflictError.make({
|
|
1671
1719
|
conflicts: conflicts.map((action) =>
|
|
1672
1720
|
action.action === "conflict" ? `${action.path}: ${action.reason}` : "",
|
|
1673
1721
|
),
|
|
@@ -1677,7 +1725,9 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1677
1725
|
const fs = yield* FileSystem.FileSystem;
|
|
1678
1726
|
const path = yield* Path.Path;
|
|
1679
1727
|
const mutating = plan.actions.filter(
|
|
1680
|
-
(
|
|
1728
|
+
(
|
|
1729
|
+
action,
|
|
1730
|
+
): action is Extract<SkillPlanAction, { readonly action: "create" | "update" | "remove" }> =>
|
|
1681
1731
|
action.action === "create" || action.action === "update" || action.action === "remove",
|
|
1682
1732
|
);
|
|
1683
1733
|
|
|
@@ -1686,7 +1736,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1686
1736
|
action.action === "remove" ? action.destination : action.desired.destination;
|
|
1687
1737
|
|
|
1688
1738
|
if (!observationsEqual(yield* observePath(destination), action.observed)) {
|
|
1689
|
-
return yield*
|
|
1739
|
+
return yield* ApplyRaceError.make({
|
|
1690
1740
|
path: action.action === "remove" ? action.previous.path : action.desired.path,
|
|
1691
1741
|
});
|
|
1692
1742
|
}
|
|
@@ -1718,7 +1768,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1718
1768
|
yield* fs.writeFileString(staged, action.stagedContent);
|
|
1719
1769
|
} else if (action.action === "remove") {
|
|
1720
1770
|
if (action.stagedContent === undefined) {
|
|
1721
|
-
return yield*
|
|
1771
|
+
return yield* InvalidProjectStateError.make({
|
|
1722
1772
|
message: `missing staged content for ${action.previous.resourceId}`,
|
|
1723
1773
|
});
|
|
1724
1774
|
}
|
|
@@ -1733,7 +1783,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1733
1783
|
const symbolicLink = yield* findNestedSymbolicLink(staged);
|
|
1734
1784
|
|
|
1735
1785
|
if (symbolicLink !== undefined) {
|
|
1736
|
-
return yield*
|
|
1786
|
+
return yield* InvalidProjectStateError.make({
|
|
1737
1787
|
message: `staged skill contains a symlink: ${action.desired.path}`,
|
|
1738
1788
|
});
|
|
1739
1789
|
}
|
|
@@ -1753,7 +1803,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1753
1803
|
: undefined;
|
|
1754
1804
|
|
|
1755
1805
|
if (observation.kind !== "file" || digest !== action.desired.digest) {
|
|
1756
|
-
return yield*
|
|
1806
|
+
return yield* InvalidProjectStateError.make({
|
|
1757
1807
|
message: `staged output digest mismatch for ${action.desired.path}`,
|
|
1758
1808
|
});
|
|
1759
1809
|
}
|
|
@@ -1761,7 +1811,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1761
1811
|
observation.kind !== action.desired.kind ||
|
|
1762
1812
|
observation.digest !== action.desired.digest
|
|
1763
1813
|
) {
|
|
1764
|
-
return yield*
|
|
1814
|
+
return yield* InvalidProjectStateError.make({
|
|
1765
1815
|
message: `staged output digest mismatch for ${action.desired.path}`,
|
|
1766
1816
|
});
|
|
1767
1817
|
}
|
|
@@ -1800,7 +1850,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
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
|
|