@nseng-ai/areg 0.1.1 → 0.1.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.
Files changed (43) hide show
  1. package/package.json +7 -6
  2. package/src/cli.ts +0 -33
  3. package/src/context.ts +3 -29
  4. package/src/fake-gateways.ts +108 -380
  5. package/src/gateways/errors.ts +2 -2
  6. package/src/gateways/fs-utils.ts +2 -2
  7. package/src/gateways/project-fs.ts +29 -41
  8. package/src/gateways/project-gateway.ts +98 -31
  9. package/src/gateways.ts +34 -150
  10. package/src/index.ts +2 -28
  11. package/src/operations/check.ts +33 -20
  12. package/src/operations/doctor-skills.ts +71 -7
  13. package/src/operations/file-state.ts +4 -4
  14. package/src/operations/manifest-source-findings.ts +45 -0
  15. package/src/operations/manifest-sources.ts +119 -0
  16. package/src/operations/pi-settings.ts +2 -5
  17. package/src/operations/project-inspection.ts +32 -28
  18. package/src/operations/project-mutations.ts +5 -21
  19. package/src/operations/project-resolution.ts +2 -2
  20. package/src/operations/skill-find.ts +23 -3
  21. package/src/operations/skill-kind-apply-plan.ts +2 -2
  22. package/src/operations/skill-kind-frontmatter.ts +1 -1
  23. package/src/operations/skill-kind-inference.ts +30 -14
  24. package/src/operations/skill-kind.ts +34 -12
  25. package/src/sort.ts +3 -3
  26. package/src/gateways/command-constants.ts +0 -1
  27. package/src/gateways/github-gateway.ts +0 -111
  28. package/src/gateways/host-gateway.ts +0 -35
  29. package/src/gateways/mutation-policy.ts +0 -94
  30. package/src/gateways/npx-skills-gateway.ts +0 -53
  31. package/src/gateways/prompt-gateway.ts +0 -21
  32. package/src/gateways/skillx-workspace-gateway.ts +0 -220
  33. package/src/operations/frontmatter.ts +0 -151
  34. package/src/operations/init.ts +0 -548
  35. package/src/operations/lockfile.ts +0 -107
  36. package/src/operations/managed-markdown-block.ts +0 -47
  37. package/src/operations/project-agents.ts +0 -115
  38. package/src/operations/skill-mirror-conventions.ts +0 -126
  39. package/src/operations/skillx.ts +0 -305
  40. package/src/operations/toml-section.ts +0 -53
  41. package/src/operations/update-skills.ts +0 -325
  42. package/src/real-gateways.ts +0 -6
  43. package/src/skill-lookup.ts +0 -254
@@ -1,28 +1,17 @@
1
1
  import { optionalEntry } from "@nseng-ai/foundation/primitives";
2
- import { resultErr, resultOk } from "@nseng-ai/foundation/result";
3
2
  import {
4
3
  skillLookupBaseRelativePath,
5
4
  skillLookupDescriptorForRoot,
6
5
  skillLookupDescriptorForSourceType,
7
6
  type SkillLookupRoot,
8
7
  type SkillLookupSourceType,
9
- } from "./skill-lookup.ts";
8
+ } from "@nseng-ai/foundation/skill-lookup";
10
9
 
11
10
  import { missingCheckSkillInspection } from "./gateways.ts";
12
11
  import type {
13
12
  AregCheckPairingDirectory,
14
13
  AregCheckSkillInspection,
15
14
  AregErrorInfo,
16
- AregGithubGateway,
17
- AregGithubSkillFileResult,
18
- AregGithubSkillListResult,
19
- AregHostGateway,
20
- AregHostToolName,
21
- AregNpxSkillsAddRequest,
22
- AregNpxSkillsAddResult,
23
- AregNpxSkillsGateway,
24
- AregOperationResult,
25
- AregPathState,
26
15
  AregPiSkillInventoryInspection,
27
16
  AregProjectBaseInspection,
28
17
  AregProjectDirRequest,
@@ -34,7 +23,6 @@ import type {
34
23
  AregProjectRemoveEmptyDirResult,
35
24
  AregProjectSymlinkDeleteRequest,
36
25
  AregProjectTextWriteRequest,
37
- AregPromptGateway,
38
26
  AregSkillFindRootsInspection,
39
27
  AregSkillFindSkillInspection,
40
28
  AregSkillInspectionRequest,
@@ -42,26 +30,25 @@ import type {
42
30
  AregSkillKindResolveResult,
43
31
  AregSkillKindSkillInspection,
44
32
  AregSkillKindSourceType,
45
- AregSkillxInstallRequest,
46
- AregSkillxInstallResult,
47
- AregSkillxInstalledSkill,
48
- AregSkillxWorkspaceCleanupRequest,
49
- AregSkillxWorkspaceGateway,
50
- AregTextFileState,
51
- AregToolCheckResult,
52
33
  } from "./gateways.ts";
34
+ import type {
35
+ AregManifestSkillSourceInspection,
36
+ AregManifestSkillSourcesInspection,
37
+ } from "./operations/manifest-sources.ts";
53
38
  import { classifyResolvedSkillKindInspection } from "./gateways/skill-kind-classification.ts";
54
39
  import {
55
40
  classifySkillMirrorSymlinkState,
56
41
  parseSkillMirrorRelativePath,
57
- } from "./operations/skill-mirror-conventions.ts";
42
+ type PathState,
43
+ type TextFileState,
44
+ } from "@nseng-ai/harness-artifacts/api";
58
45
 
59
46
  export type FakeAregProjectOperation =
60
47
  | { type: "inspect-project-base"; cwd: string; projectPath: string }
61
- | { type: "inspect-instruction-files"; projectDir: string }
62
48
  | { type: "inspect-pi-artifacts"; projectDir: string }
63
49
  | { type: "inspect-pi-skill-inventory"; projectDir: string }
64
50
  | { type: "inspect-skill-name-inventory"; projectDir: string }
51
+ | { type: "inspect-manifest-skill-sources"; projectDir: string }
65
52
  | { type: "inspect-skill-find-roots"; projectDir: string }
66
53
  | { type: "inspect-check-skill"; projectDir: string; skillName: string }
67
54
  | { type: "inspect-skill-kind-skill"; projectDir: string; skillName: string }
@@ -79,23 +66,36 @@ export type FakeAregProjectOperation =
79
66
 
80
67
  export interface FakeAregCheckSkillOptions {
81
68
  name: string;
82
- skillsPath?: AregPathState;
83
- agentsPath?: AregPathState;
84
- claudePath?: AregPathState;
85
- localSkillMd?: AregTextFileState | string;
86
- remoteSkillMd?: AregTextFileState | string;
87
- openaiPolicy?: AregTextFileState | string;
69
+ skillsPath?: PathState;
70
+ agentsPath?: PathState;
71
+ claudePath?: PathState;
72
+ localSkillMd?: TextFileState | string;
73
+ remoteSkillMd?: TextFileState | string;
74
+ openaiPolicy?: TextFileState | string;
88
75
  }
89
76
 
90
77
  export interface FakeAregSkillKindSkillOptions {
91
78
  name: string;
92
79
  sourceType?: AregSkillKindSourceType;
93
80
  baseRelativePath?: string;
94
- skillDir?: AregPathState;
95
- skillMd?: AregTextFileState | string;
96
- openaiPolicy?: AregTextFileState | string;
97
- agentsPath?: AregPathState;
98
- claudePath?: AregPathState;
81
+ skillDir?: PathState;
82
+ skillMd?: TextFileState | string;
83
+ openaiPolicy?: TextFileState | string;
84
+ agentsPath?: PathState;
85
+ claudePath?: PathState;
86
+ }
87
+
88
+ export interface FakeAregManifestSkillSourceOptions {
89
+ skillName: string;
90
+ harness?: AregManifestSkillSourceInspection["harness"];
91
+ scope?: AregManifestSkillSourceInspection["scope"];
92
+ manifestPath?: string;
93
+ manifestKey?: string;
94
+ provenance?: Partial<AregManifestSkillSourceInspection["provenance"]>;
95
+ targetRootRelativePath?: string;
96
+ targetSkillRelativePath?: string;
97
+ skillDir?: PathState;
98
+ skillMd?: TextFileState | string;
99
99
  }
100
100
 
101
101
  export interface FakeAregSkillFindSkillOptions {
@@ -103,23 +103,19 @@ export interface FakeAregSkillFindSkillOptions {
103
103
  root?: SkillLookupRoot;
104
104
  sourceType?: SkillLookupSourceType;
105
105
  baseRelativePath?: string;
106
- skillDir?: AregPathState;
107
- skillMd?: AregTextFileState | string;
106
+ skillDir?: PathState;
107
+ skillMd?: TextFileState | string;
108
108
  }
109
109
 
110
110
  export interface FakeAregProjectGatewayOptions {
111
111
  projectDir?: string;
112
- projectPathState?: AregPathState;
113
- targetPathState?: AregPathState;
114
- lockfile?: AregTextFileState | object | string;
115
- nsToml?: AregTextFileState | string;
116
- aregJson?: AregTextFileState | object | string;
117
- agentsMd?: AregTextFileState | string;
118
- claudeMd?: AregTextFileState | string;
119
- claudeDir?: AregPathState;
120
- claudeSettings?: AregTextFileState | string;
121
- piDir?: AregPathState;
122
- piSettings?: AregTextFileState | object | string;
112
+ projectPathState?: PathState;
113
+ targetPathState?: PathState;
114
+ lockfile?: TextFileState | object | string;
115
+ nsToml?: TextFileState | string;
116
+ aregJson?: TextFileState | object | string;
117
+ piDir?: PathState;
118
+ piSettings?: TextFileState | object | string;
123
119
  replacementSurfaces?: readonly string[];
124
120
  piSkillInventory?: Partial<AregPiSkillInventoryInspection>;
125
121
  skillsDirectoryNames?: readonly string[];
@@ -129,6 +125,8 @@ export interface FakeAregProjectGatewayOptions {
129
125
  checkSkills?: readonly FakeAregCheckSkillOptions[];
130
126
  localSkills?: readonly FakeAregSkillKindSkillOptions[];
131
127
  findSkills?: readonly FakeAregSkillFindSkillOptions[];
128
+ manifestSkillSources?: readonly FakeAregManifestSkillSourceOptions[];
129
+ manifestErrors?: readonly AregManifestSkillSourcesInspection["errors"][number][];
132
130
  pairingDirectories?: readonly AregCheckPairingDirectory[];
133
131
  resolveFailures?: Readonly<Record<string, AregErrorInfo>>;
134
132
  preflightFailures?: Readonly<Record<string, AregErrorInfo>>;
@@ -138,10 +136,9 @@ export interface FakeAregProjectGatewayOptions {
138
136
 
139
137
  export class FakeAregProjectGateway implements AregProjectGateway {
140
138
  private readonly projectDir: string;
141
- private readonly projectPathState: AregPathState;
142
- private readonly files: Map<string, AregTextFileState>;
143
- private readonly claudeDir: AregPathState;
144
- private readonly piDir: AregPathState;
139
+ private readonly projectPathState: PathState;
140
+ private readonly files: Map<string, TextFileState>;
141
+ private readonly piDir: PathState;
145
142
  private readonly replacementSurfaces: readonly string[];
146
143
  private readonly piSkillInventory: AregPiSkillInventoryInspection | undefined;
147
144
  private readonly skillsDirectoryNames: readonly string[];
@@ -151,6 +148,7 @@ export class FakeAregProjectGateway implements AregProjectGateway {
151
148
  private readonly checkSkills: AregCheckSkillInspection[];
152
149
  private readonly localSkills: AregSkillKindSkillInspection[];
153
150
  private readonly explicitFindSkills: AregSkillFindSkillInspection[] | undefined;
151
+ private readonly manifestSkillSources: AregManifestSkillSourcesInspection;
154
152
  private readonly pairingDirectories: readonly AregCheckPairingDirectory[];
155
153
  private readonly resolveFailures: ReadonlyMap<string, AregErrorInfo>;
156
154
  private readonly preflightFailures: ReadonlyMap<string, AregErrorInfo>;
@@ -166,15 +164,8 @@ export class FakeAregProjectGateway implements AregProjectGateway {
166
164
  ["skills-lock.json", normalizeTextFileState(options.lockfile ?? { version: 1, skills: {} })],
167
165
  ["ns.toml", normalizeTextFileState(options.nsToml ?? { type: "missing" })],
168
166
  ["areg.json", normalizeTextFileState(options.aregJson ?? { type: "missing" })],
169
- ["AGENTS.md", normalizeTextFileState(options.agentsMd ?? { type: "missing" })],
170
- ["CLAUDE.md", normalizeTextFileState(options.claudeMd ?? { type: "missing" })],
171
- [
172
- ".claude/settings.local.json",
173
- normalizeTextFileState(options.claudeSettings ?? { type: "missing" }),
174
- ],
175
167
  [".pi/settings.json", normalizeTextFileState(options.piSettings ?? { type: "missing" })],
176
168
  ]);
177
- this.claudeDir = copyPathState(options.claudeDir ?? { type: "missing" });
178
169
  this.piDir = copyPathState(options.piDir ?? { type: "missing" });
179
170
  this.replacementSurfaces = [...(options.replacementSurfaces ?? [])];
180
171
  this.piSkillInventory =
@@ -189,6 +180,10 @@ export class FakeAregProjectGateway implements AregProjectGateway {
189
180
  this.localSkills = (options.localSkills ?? []).map(copyFakeSkillKindSkill);
190
181
  this.explicitFindSkills =
191
182
  options.findSkills === undefined ? undefined : options.findSkills.map(copyFakeSkillFindSkill);
183
+ this.manifestSkillSources = {
184
+ sources: (options.manifestSkillSources ?? []).map(copyFakeManifestSkillSource),
185
+ errors: (options.manifestErrors ?? []).map((error) => ({ ...error })),
186
+ };
192
187
  this.pairingDirectories = (options.pairingDirectories ?? []).map(copyPairingDirectory);
193
188
  this.resolveFailures = new Map(
194
189
  Object.entries(options.resolveFailures ?? {}).map(([key, value]) => [
@@ -228,16 +223,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
228
223
  };
229
224
  }
230
225
 
231
- async inspectInstructionFiles(request: AregProjectDirRequest) {
232
- this.log.push({ type: "inspect-instruction-files", projectDir: request.projectDir });
233
- return {
234
- agentsMd: this.fileState("AGENTS.md"),
235
- claudeMd: this.fileState("CLAUDE.md"),
236
- claudeDir: copyPathState(this.claudeDir),
237
- claudeSettings: this.fileState(".claude/settings.local.json"),
238
- };
239
- }
240
-
241
226
  async inspectPiArtifacts(request: AregProjectDirRequest) {
242
227
  this.log.push({ type: "inspect-pi-artifacts", projectDir: request.projectDir });
243
228
  return {
@@ -271,6 +256,16 @@ export class FakeAregProjectGateway implements AregProjectGateway {
271
256
  };
272
257
  }
273
258
 
259
+ async inspectManifestSkillSources(
260
+ request: AregProjectDirRequest,
261
+ ): Promise<AregManifestSkillSourcesInspection> {
262
+ this.log.push({ type: "inspect-manifest-skill-sources", projectDir: request.projectDir });
263
+ return {
264
+ sources: this.manifestSkillSources.sources.map(copyManifestSkillSource),
265
+ errors: this.manifestSkillSources.errors.map((error) => ({ ...error })),
266
+ };
267
+ }
268
+
274
269
  async inspectSkillFindRoots(
275
270
  request: AregProjectDirRequest,
276
271
  ): Promise<AregSkillFindRootsInspection> {
@@ -348,7 +343,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
348
343
  content: request.content,
349
344
  description: request.description,
350
345
  createParent: request.createParent,
351
- policy: request.policy,
352
346
  });
353
347
  const failure = this.preflightFailure(request.relativePath);
354
348
  return failure === undefined ? { ok: true } : { ok: false, error: failure };
@@ -362,7 +356,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
362
356
  projectDir: request.projectDir,
363
357
  relativePath: request.relativePath,
364
358
  description: request.description,
365
- policy: request.policy,
366
359
  });
367
360
  const failure = this.preflightFailure(request.relativePath);
368
361
  return failure === undefined ? { ok: true } : { ok: false, error: failure };
@@ -376,7 +369,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
376
369
  projectDir: request.projectDir,
377
370
  relativePath: request.relativePath,
378
371
  description: request.description,
379
- policy: request.policy,
380
372
  });
381
373
  const failure =
382
374
  this.preflightFailure(request.relativePath) ??
@@ -392,7 +384,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
392
384
  projectDir: request.projectDir,
393
385
  relativePath: request.relativePath,
394
386
  description: request.description,
395
- policy: request.policy,
396
387
  });
397
388
  const failure = this.preflightFailure(request.relativePath);
398
389
  return failure === undefined ? { ok: true } : { ok: false, error: failure };
@@ -406,7 +397,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
406
397
  content: request.content,
407
398
  description: request.description,
408
399
  createParent: request.createParent,
409
- policy: request.policy,
410
400
  });
411
401
  const failure = this.mutationFailure(request.relativePath);
412
402
  if (failure !== undefined) return { ok: false, error: failure };
@@ -425,7 +415,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
425
415
  projectDir: request.projectDir,
426
416
  relativePath: request.relativePath,
427
417
  description: request.description,
428
- policy: request.policy,
429
418
  });
430
419
  const failure = this.mutationFailure(request.relativePath);
431
420
  if (failure !== undefined) return { ok: false, error: failure };
@@ -444,7 +433,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
444
433
  projectDir: request.projectDir,
445
434
  relativePath: request.relativePath,
446
435
  description: request.description,
447
- policy: request.policy,
448
436
  });
449
437
  const failure =
450
438
  this.mutationFailure(request.relativePath) ??
@@ -467,7 +455,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
467
455
  projectDir: request.projectDir,
468
456
  relativePath: request.relativePath,
469
457
  description: request.description,
470
- policy: request.policy,
471
458
  });
472
459
  const failure = this.mutationFailure(request.relativePath);
473
460
  if (failure !== undefined) return { ok: false, error: failure };
@@ -483,7 +470,7 @@ export class FakeAregProjectGateway implements AregProjectGateway {
483
470
  return this.log.map(copyProjectOperation);
484
471
  }
485
472
 
486
- private fileState(relativePath: string): AregTextFileState {
473
+ private fileState(relativePath: string): TextFileState {
487
474
  return copyTextFileState(this.files.get(relativePath) ?? { type: "missing" });
488
475
  }
489
476
 
@@ -522,267 +509,6 @@ export class FakeAregProjectGateway implements AregProjectGateway {
522
509
  }
523
510
  }
524
511
 
525
- export type FakeAregHostOperation = { type: "check-tool"; tool: AregHostToolName; cwd: string };
526
-
527
- export interface FakeAregHostGatewayOptions {
528
- tools?: Partial<Record<AregHostToolName, string | null>>;
529
- }
530
-
531
- export class FakeAregHostGateway implements AregHostGateway {
532
- private readonly tools: ReadonlyMap<AregHostToolName, string | null>;
533
- private readonly log: FakeAregHostOperation[] = [];
534
-
535
- constructor(options: FakeAregHostGatewayOptions = {}) {
536
- this.tools = new Map(
537
- Object.entries(options.tools ?? {}) as Array<[AregHostToolName, string | null]>,
538
- );
539
- }
540
-
541
- async checkTool(options: {
542
- tool: AregHostToolName;
543
- cwd: string;
544
- env: NodeJS.ProcessEnv;
545
- }): Promise<AregToolCheckResult> {
546
- this.log.push({ type: "check-tool", tool: options.tool, cwd: options.cwd });
547
- const path = this.tools.get(options.tool);
548
- if (path === null)
549
- return {
550
- type: "missing",
551
- tool: options.tool,
552
- message: `Required host tool is missing: ${options.tool}`,
553
- };
554
- return { type: "found", tool: options.tool, path: path ?? `/fake/bin/${options.tool}` };
555
- }
556
-
557
- operations(): readonly FakeAregHostOperation[] {
558
- return this.log.map((operation) => ({ ...operation }));
559
- }
560
- }
561
-
562
- export type FakeAregGithubOperation =
563
- | {
564
- type: "list-skill-directory-names";
565
- repo: string;
566
- ref?: string;
567
- }
568
- | {
569
- type: "check-skill-file";
570
- repo: string;
571
- path: string;
572
- ref?: string;
573
- };
574
-
575
- export interface FakeAregGithubGatewayOptions {
576
- repos?: Record<string, readonly string[] | "missing" | "auth-error" | AregErrorInfo>;
577
- files?: Record<string, "found" | "missing" | "auth-error" | AregErrorInfo>;
578
- }
579
-
580
- export class FakeAregGithubGateway implements AregGithubGateway {
581
- private readonly repos: ReadonlyMap<
582
- string,
583
- readonly string[] | "missing" | "auth-error" | AregErrorInfo
584
- >;
585
- private readonly files: ReadonlyMap<string, "found" | "missing" | "auth-error" | AregErrorInfo>;
586
- private readonly log: FakeAregGithubOperation[] = [];
587
-
588
- constructor(options: FakeAregGithubGatewayOptions = {}) {
589
- this.repos = new Map(
590
- Object.entries(options.repos ?? {}).map(([repo, value]) => [repo, copyGithubState(value)]),
591
- );
592
- this.files = new Map(
593
- Object.entries(options.files ?? {}).map(([path, value]) => [
594
- path,
595
- copyGithubFileState(value),
596
- ]),
597
- );
598
- }
599
-
600
- async listSkillDirectoryNames(options: {
601
- repo: string;
602
- ref?: string;
603
- env: NodeJS.ProcessEnv;
604
- }): Promise<AregGithubSkillListResult> {
605
- this.log.push({
606
- type: "list-skill-directory-names",
607
- repo: options.repo,
608
- ...optionalEntry("ref", options.ref),
609
- });
610
- const state = this.repos.get(options.repo);
611
- if (state === undefined || state === "missing")
612
- return { type: "missing", message: `Skill source not found: ${options.repo}` };
613
- if (state === "auth-error")
614
- return { type: "auth-error", message: `GitHub authentication failed for ${options.repo}` };
615
- if (isReadonlyStringArray(state)) return { type: "ok", skillNames: [...state] };
616
- return { type: "error", error: copyErrorInfo(state) };
617
- }
618
-
619
- async checkSkillFile(options: {
620
- repo: string;
621
- path: string;
622
- ref?: string;
623
- env: NodeJS.ProcessEnv;
624
- }): Promise<AregGithubSkillFileResult> {
625
- this.log.push({
626
- type: "check-skill-file",
627
- repo: options.repo,
628
- path: options.path,
629
- ...optionalEntry("ref", options.ref),
630
- });
631
- const state = this.files.get(githubFileKey(options.repo, options.path, options.ref));
632
- if (state === undefined || state === "found") return { type: "found" };
633
- if (state === "missing")
634
- return { type: "missing", message: `Skill file not found: ${options.repo}/${options.path}` };
635
- if (state === "auth-error")
636
- return { type: "auth-error", message: `GitHub authentication failed for ${options.repo}` };
637
- return { type: "error", error: copyErrorInfo(state) };
638
- }
639
-
640
- operations(): readonly FakeAregGithubOperation[] {
641
- return this.log.map((operation) => ({ ...operation }));
642
- }
643
- }
644
-
645
- export type FakeAregNpxSkillsOperation = { type: "add-skills" } & Omit<
646
- AregNpxSkillsAddRequest,
647
- "env"
648
- >;
649
-
650
- export interface FakeAregNpxSkillsGatewayOptions {
651
- failure?: AregErrorInfo;
652
- failures?: Readonly<Record<string, AregErrorInfo>>;
653
- }
654
-
655
- export class FakeAregNpxSkillsGateway implements AregNpxSkillsGateway {
656
- private readonly failure: AregErrorInfo | undefined;
657
- private readonly failures: ReadonlyMap<string, AregErrorInfo>;
658
- private readonly log: FakeAregNpxSkillsOperation[] = [];
659
-
660
- constructor(options: FakeAregNpxSkillsGatewayOptions = {}) {
661
- this.failure = options.failure === undefined ? undefined : copyErrorInfo(options.failure);
662
- this.failures = new Map(
663
- Object.entries(options.failures ?? {}).map(([key, value]) => [key, copyErrorInfo(value)]),
664
- );
665
- }
666
-
667
- async addSkills(request: AregNpxSkillsAddRequest): Promise<AregNpxSkillsAddResult> {
668
- this.log.push({
669
- type: "add-skills",
670
- sourceRepo: request.sourceRepo,
671
- skillNames: [...request.skillNames],
672
- targetAgents: [...request.targetAgents],
673
- cwd: request.cwd,
674
- });
675
- if (this.failure !== undefined) return { type: "error", error: copyErrorInfo(this.failure) };
676
- const keyedFailure = this.failures.get(failureKey(request.sourceRepo, request.skillNames));
677
- if (keyedFailure !== undefined) return { type: "error", error: copyErrorInfo(keyedFailure) };
678
- return { type: "ok" };
679
- }
680
-
681
- operations(): readonly FakeAregNpxSkillsOperation[] {
682
- return this.log.map((operation) => ({
683
- ...operation,
684
- skillNames: [...operation.skillNames],
685
- targetAgents: [...operation.targetAgents],
686
- }));
687
- }
688
- }
689
-
690
- export type FakeAregPromptOperation = {
691
- type: "confirm";
692
- message: string;
693
- defaultValue: boolean;
694
- response: boolean;
695
- };
696
-
697
- export interface FakeAregPromptGatewayOptions {
698
- responses?: readonly boolean[];
699
- shouldConfirmByDefault?: boolean;
700
- }
701
-
702
- export class FakeAregPromptGateway implements AregPromptGateway {
703
- private readonly responses: boolean[];
704
- private readonly shouldConfirmByDefault: boolean;
705
- private readonly log: FakeAregPromptOperation[] = [];
706
-
707
- constructor(options: FakeAregPromptGatewayOptions = {}) {
708
- this.responses = [...(options.responses ?? [])];
709
- this.shouldConfirmByDefault = options.shouldConfirmByDefault ?? false;
710
- }
711
-
712
- async confirm(request: { message: string; defaultValue: boolean }): Promise<boolean> {
713
- const response = this.responses.shift() ?? this.shouldConfirmByDefault;
714
- this.log.push({
715
- type: "confirm",
716
- message: request.message,
717
- defaultValue: request.defaultValue,
718
- response,
719
- });
720
- return response;
721
- }
722
-
723
- operations(): readonly FakeAregPromptOperation[] {
724
- return this.log.map((operation) => ({ ...operation }));
725
- }
726
- }
727
-
728
- export type FakeAregSkillxOperation =
729
- | ({ type: "install-into-workspace" } & Omit<AregSkillxInstallRequest, "env">)
730
- | ({ type: "cleanup-workspace" } & AregSkillxWorkspaceCleanupRequest);
731
-
732
- export interface FakeAregSkillxWorkspaceGatewayOptions {
733
- workspaceRoot?: string;
734
- installedSkills?: readonly AregSkillxInstalledSkill[];
735
- failure?: AregErrorInfo;
736
- cleanupFailure?: AregErrorInfo;
737
- }
738
-
739
- export class FakeAregSkillxWorkspaceGateway implements AregSkillxWorkspaceGateway {
740
- private readonly workspaceRoot: string;
741
- private readonly installedSkills: readonly AregSkillxInstalledSkill[];
742
- private readonly failure: AregErrorInfo | undefined;
743
- private readonly cleanupFailure: AregErrorInfo | undefined;
744
- private readonly log: FakeAregSkillxOperation[] = [];
745
-
746
- constructor(options: FakeAregSkillxWorkspaceGatewayOptions = {}) {
747
- this.workspaceRoot = options.workspaceRoot ?? "/tmp/areg-skillx";
748
- this.installedSkills = (options.installedSkills ?? []).map(copyInstalledSkill);
749
- this.failure = options.failure === undefined ? undefined : copyErrorInfo(options.failure);
750
- this.cleanupFailure =
751
- options.cleanupFailure === undefined ? undefined : copyErrorInfo(options.cleanupFailure);
752
- }
753
-
754
- async installIntoWorkspace(request: AregSkillxInstallRequest): Promise<AregSkillxInstallResult> {
755
- this.log.push({
756
- type: "install-into-workspace",
757
- sourceRepo: request.sourceRepo,
758
- ...optionalEntry("skillName", request.skillName),
759
- cwd: request.cwd,
760
- });
761
- if (this.failure !== undefined) return { type: "error", error: copyErrorInfo(this.failure) };
762
- return {
763
- type: "ok",
764
- workspace: {
765
- workspaceRoot: this.workspaceRoot,
766
- installedSkills: this.installedSkills.map(copyInstalledSkill),
767
- },
768
- };
769
- }
770
-
771
- async cleanupWorkspace(request: AregSkillxWorkspaceCleanupRequest): Promise<AregOperationResult> {
772
- this.log.push({ type: "cleanup-workspace", workspaceRoot: request.workspaceRoot });
773
- if (this.cleanupFailure !== undefined) return resultErr(copyErrorInfo(this.cleanupFailure));
774
- return resultOk(undefined);
775
- }
776
-
777
- operations(): readonly FakeAregSkillxOperation[] {
778
- return this.log.map((operation) => ({ ...operation }));
779
- }
780
- }
781
-
782
- function failureKey(sourceRepo: string, skillNames: readonly string[]): string {
783
- return `${sourceRepo}:${skillNames.join(",")}`;
784
- }
785
-
786
512
  function copyProjectOperation(operation: FakeAregProjectOperation): FakeAregProjectOperation {
787
513
  switch (operation.type) {
788
514
  case "write-text-file":
@@ -825,6 +551,33 @@ function copyFakeSkillFindSkill(
825
551
  };
826
552
  }
827
553
 
554
+ function copyFakeManifestSkillSource(
555
+ source: FakeAregManifestSkillSourceOptions,
556
+ ): AregManifestSkillSourceInspection {
557
+ const manifestKey = source.manifestKey ?? `skill:${source.skillName}:pi:project`;
558
+ const targetSkillRelativePath =
559
+ source.targetSkillRelativePath ?? `.pi/skills/${source.skillName}`;
560
+ return {
561
+ skillName: source.skillName,
562
+ harness: source.harness ?? "pi",
563
+ scope: source.scope ?? "project",
564
+ manifestPath: source.manifestPath ?? "/repo/.pi/skills/.ns-harness-artifacts-manifest.json",
565
+ manifestKey,
566
+ provenance: {
567
+ type: source.provenance?.type ?? "npm-module",
568
+ packageName: source.provenance?.packageName ?? "@example/skills",
569
+ relativePath: source.provenance?.relativePath ?? `skills/${source.skillName}`,
570
+ version: source.provenance?.version ?? "1.0.0",
571
+ },
572
+ targetRootRelativePath: source.targetRootRelativePath ?? ".pi/skills",
573
+ targetSkillRelativePath,
574
+ skillDir: copyPathState(source.skillDir ?? { type: "directory" }),
575
+ skillMd: normalizeTextFileState(
576
+ source.skillMd ?? `---\nname: ${source.skillName}\ndescription: ${source.skillName}\n---\n`,
577
+ ),
578
+ };
579
+ }
580
+
828
581
  function copyFakeSkillKindSkill(
829
582
  skill: FakeAregSkillKindSkillOptions,
830
583
  ): AregSkillKindSkillInspection {
@@ -860,8 +613,8 @@ function copyCheckSkill(skill: AregCheckSkillInspection): AregCheckSkillInspecti
860
613
  interface SkillInspectionCore {
861
614
  name: string;
862
615
  baseRelativePath: string;
863
- skillDir: AregPathState;
864
- skillMd: AregTextFileState;
616
+ skillDir: PathState;
617
+ skillMd: TextFileState;
865
618
  }
866
619
 
867
620
  function copySkillInspectionCore(skill: SkillInspectionCore): SkillInspectionCore {
@@ -891,6 +644,17 @@ function copySkillFindSkill(skill: AregSkillFindSkillInspection): AregSkillFindS
891
644
  };
892
645
  }
893
646
 
647
+ function copyManifestSkillSource(
648
+ source: AregManifestSkillSourceInspection,
649
+ ): AregManifestSkillSourceInspection {
650
+ return {
651
+ ...source,
652
+ provenance: { ...source.provenance },
653
+ skillDir: copyPathState(source.skillDir),
654
+ skillMd: copyTextFileState(source.skillMd),
655
+ };
656
+ }
657
+
894
658
  function skillKindSkillToFindSkill(
895
659
  skill: AregSkillKindSkillInspection,
896
660
  ): AregSkillFindSkillInspection {
@@ -935,13 +699,13 @@ function skillForRelativePath(
935
699
  return skills.find((skill) => relativePath.startsWith(`${skill.baseRelativePath}/`));
936
700
  }
937
701
 
938
- function normalizeTextFileState(value: AregTextFileState | object | string): AregTextFileState {
702
+ function normalizeTextFileState(value: TextFileState | object | string): TextFileState {
939
703
  if (typeof value === "string") return { type: "file", text: value };
940
- if ("type" in value) return copyTextFileState(value as AregTextFileState);
704
+ if ("type" in value) return copyTextFileState(value as TextFileState);
941
705
  return { type: "file", text: `${JSON.stringify(value, null, 2)}\n` };
942
706
  }
943
707
 
944
- function copyTextFileState(state: AregTextFileState): AregTextFileState {
708
+ function copyTextFileState(state: TextFileState): TextFileState {
945
709
  return { ...state };
946
710
  }
947
711
 
@@ -955,7 +719,7 @@ function copyPiSkillInventory(
955
719
  };
956
720
  }
957
721
 
958
- function copyPathState(state: AregPathState): AregPathState {
722
+ function copyPathState(state: PathState): PathState {
959
723
  return { ...state };
960
724
  }
961
725
 
@@ -968,42 +732,6 @@ function copyPairingDirectory(directory: AregCheckPairingDirectory): AregCheckPa
968
732
  };
969
733
  }
970
734
 
971
- function copyGithubState(
972
- value: readonly string[] | "missing" | "auth-error" | AregErrorInfo,
973
- ): readonly string[] | "missing" | "auth-error" | AregErrorInfo {
974
- if (isReadonlyStringArray(value)) return [...value];
975
- if (value === "missing" || value === "auth-error") return value;
976
- return copyErrorInfo(value);
977
- }
978
-
979
- function copyGithubFileState(
980
- value: "found" | "missing" | "auth-error" | AregErrorInfo,
981
- ): "found" | "missing" | "auth-error" | AregErrorInfo {
982
- if (value === "found" || value === "missing" || value === "auth-error") return value;
983
- return copyErrorInfo(value);
984
- }
985
-
986
- function githubFileKey(repo: string, path: string, ref: string | undefined): string {
987
- return ref === undefined ? `${repo}:${path}` : `${repo}:${path}@${ref}`;
988
- }
989
-
990
- function isReadonlyStringArray(
991
- value: readonly string[] | "missing" | "auth-error" | AregErrorInfo,
992
- ): value is readonly string[] {
993
- return Array.isArray(value);
994
- }
995
-
996
- function copyInstalledSkill(skill: AregSkillxInstalledSkill): AregSkillxInstalledSkill {
997
- return {
998
- name: skill.name,
999
- directory: skill.directory,
1000
- skillFile: skill.skillFile,
1001
- relativeFiles: [...skill.relativeFiles],
1002
- };
1003
- }
1004
-
1005
735
  function copyErrorInfo(error: AregErrorInfo): AregErrorInfo {
1006
- return error.displayCommand === undefined
1007
- ? { code: error.code, message: error.message }
1008
- : { code: error.code, message: error.message, displayCommand: error.displayCommand };
736
+ return { code: error.code, message: error.message };
1009
737
  }