@akagilnc/pi-workflow-roles 0.1.5037 → 0.1.5063

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 (82) hide show
  1. package/README.md +2 -2
  2. package/README.zh-CN.md +2 -2
  3. package/THIRD_PARTY_NOTICES.md +8 -8
  4. package/dist/acp-host/production-host.js +410 -878
  5. package/dist/headless-host/description.js +0 -2
  6. package/dist/headless-host/production-host.js +413 -914
  7. package/dist/host-descriptions.js +2 -3
  8. package/dist/migrate-book-topology.js +4 -10
  9. package/dist/packaged-role-registry.js +17 -16
  10. package/dist/pi/role-turn-host.js +3 -27
  11. package/dist/public-cli/cli.js +6 -0
  12. package/dist/public-cli/instruction-seat-run.js +12 -40
  13. package/dist/public-cli/machine-method-skills.js +150 -0
  14. package/dist/public-cli/main.js +378 -579
  15. package/dist/public-cli/post-admission.js +2 -0
  16. package/dist/public-cli/registry.js +2 -1
  17. package/dist/public-cli/settlement.js +12 -99
  18. package/dist/public-cli/turn-request.js +16 -8
  19. package/package.json +5 -1
  20. package/scripts/build-package.mjs +1 -6
  21. package/src/headless-host/description.ts +0 -3
  22. package/src/headless-host/role-turn-host.ts +2 -23
  23. package/src/host-contracts.ts +2 -23
  24. package/src/host-descriptions.ts +2 -3
  25. package/src/packaged-role-registry.ts +14 -14
  26. package/src/pi/adapter.ts +0 -27
  27. package/src/pi/role-turn-host.ts +3 -31
  28. package/src/public-cli/cli.ts +6 -0
  29. package/src/public-cli/instruction-seat-run.ts +9 -42
  30. package/src/public-cli/machine-method-skills.ts +153 -0
  31. package/src/public-cli/post-admission.ts +2 -0
  32. package/src/public-cli/registry.ts +1 -0
  33. package/src/public-cli/settlement.ts +6 -138
  34. package/src/public-cli/turn-request.ts +24 -16
  35. package/src/reviewer-role.ts +1 -12
  36. package/src/role-envelope.ts +0 -13
  37. package/src/role-runtime-dependencies.ts +0 -13
  38. package/src/role-runtime.ts +4 -56
  39. package/src/worker-role.ts +1 -81
  40. package/dist/method-host-plugin/.claude-plugin/plugin.json +0 -5
  41. package/dist/method-host-plugin/skills/ak-cross-m-review/CONTEXT.md +0 -48
  42. package/dist/method-host-plugin/skills/ak-cross-m-review/LICENSE +0 -21
  43. package/dist/method-host-plugin/skills/ak-cross-m-review/SKILL.md +0 -170
  44. package/dist/method-host-plugin/skills/ak-cross-m-review/prompts/cmr-completeness.md +0 -118
  45. package/dist/method-host-plugin/skills/ak-cross-m-review/prompts/cmr-reviewer.md +0 -128
  46. package/dist/method-host-plugin/skills/ak-cross-m-review/provenance.json +0 -41
  47. package/dist/method-host-plugin/skills/diagnosing-bugs/SKILL.md +0 -134
  48. package/dist/method-host-plugin/skills/diagnosing-bugs/agents/openai.yaml +0 -3
  49. package/dist/method-host-plugin/skills/diagnosing-bugs/provenance.json +0 -31
  50. package/dist/method-host-plugin/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -41
  51. package/dist/method-host-plugin/skills/resolving-merge-conflicts/SKILL.md +0 -14
  52. package/dist/method-host-plugin/skills/resolving-merge-conflicts/agents/openai.yaml +0 -3
  53. package/dist/method-host-plugin/skills/resolving-merge-conflicts/provenance.json +0 -26
  54. package/dist/method-host-plugin/skills/tdd/SKILL.md +0 -38
  55. package/dist/method-host-plugin/skills/tdd/agents/openai.yaml +0 -3
  56. package/dist/method-host-plugin/skills/tdd/mocking.md +0 -59
  57. package/dist/method-host-plugin/skills/tdd/provenance.json +0 -36
  58. package/dist/method-host-plugin/skills/tdd/tests.md +0 -77
  59. package/dist/package-resources/method-skill.js +0 -259
  60. package/resources/method-host-plugin/.claude-plugin/plugin.json +0 -5
  61. package/resources/methods/ak-cross-m-review/CONTEXT.md +0 -48
  62. package/resources/methods/ak-cross-m-review/LICENSE +0 -21
  63. package/resources/methods/ak-cross-m-review/SKILL.md +0 -170
  64. package/resources/methods/ak-cross-m-review/prompts/cmr-completeness.md +0 -118
  65. package/resources/methods/ak-cross-m-review/prompts/cmr-reviewer.md +0 -128
  66. package/resources/methods/ak-cross-m-review/provenance.json +0 -41
  67. package/resources/methods/diagnosing-bugs/SKILL.md +0 -134
  68. package/resources/methods/diagnosing-bugs/agents/openai.yaml +0 -3
  69. package/resources/methods/diagnosing-bugs/provenance.json +0 -31
  70. package/resources/methods/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -41
  71. package/resources/methods/resolving-merge-conflicts/SKILL.md +0 -14
  72. package/resources/methods/resolving-merge-conflicts/agents/openai.yaml +0 -3
  73. package/resources/methods/resolving-merge-conflicts/provenance.json +0 -26
  74. package/resources/methods/tdd/SKILL.md +0 -38
  75. package/resources/methods/tdd/agents/openai.yaml +0 -3
  76. package/resources/methods/tdd/mocking.md +0 -59
  77. package/resources/methods/tdd/provenance.json +0 -36
  78. package/resources/methods/tdd/tests.md +0 -77
  79. package/src/canonical-skill-binding.ts +0 -118
  80. package/src/host-native-method.ts +0 -185
  81. package/src/package-resources/method-skill-binding.ts +0 -60
  82. package/src/package-resources/method-skill.ts +0 -363
@@ -1,36 +0,0 @@
1
- {
2
- "name": "tdd",
3
- "kind": "role-method-skill",
4
- "upstream": {
5
- "repository": "https://github.com/mattpocock/skills",
6
- "path": "skills/engineering/tdd",
7
- "commit": "8b36d4fb2635b3c21998dcd8144439c9e5ba7302",
8
- "tag": "v1.2.2",
9
- "license": "MIT",
10
- "copyright": "Copyright (c) 2026 Matt Pocock",
11
- "attribution": "mattpocock/skills"
12
- },
13
- "packageAdaptation": "red-green-advisory-no-historical-compliance-gate",
14
- "files": {
15
- "SKILL.md": {
16
- "sha256": "26c74168236078b8e43510b49f8c4b30784ec3cc7d4b1356792cbc8500d5cae6",
17
- "byteLength": 3798,
18
- "gitBlob": "d6b6bebaa1d1fed58812f8809b9ebc1ff9a5d1e4"
19
- },
20
- "tests.md": {
21
- "sha256": "859f9e592c188fda4fc7277dd180e4ce9c7a2e13f6efe1f6f29eccc9d28c106a",
22
- "byteLength": 2214,
23
- "gitBlob": "7ab86479f925a1f9e8ba680af33cb3b12e015381"
24
- },
25
- "mocking.md": {
26
- "sha256": "3ceb807fdf4a47d6a93d4d9a891e5ba6d362a6247bd08adc451feebfc17361ef",
27
- "byteLength": 1481,
28
- "gitBlob": "71cbfee674d93244ce81d1830b930ca9a69200bd"
29
- },
30
- "agents/openai.yaml": {
31
- "sha256": "ea6f01cf1b8c06a4b0f5b649d74b1b8ce8685e72af1b38d70d877693e092af0b",
32
- "byteLength": 87,
33
- "gitBlob": "651b838a7663e027b1b8884491e867f26bb9a021"
34
- }
35
- }
36
- }
@@ -1,77 +0,0 @@
1
- # Good and Bad Tests
2
-
3
- ## Good Tests
4
-
5
- **Integration-style**: Test through real interfaces, not mocks of internal parts.
6
-
7
- ```typescript
8
- // GOOD: Tests observable behavior
9
- test("user can checkout with valid cart", async () => {
10
- const cart = createCart();
11
- cart.add(product);
12
- const result = await checkout(cart, paymentMethod);
13
- expect(result.status).toBe("confirmed");
14
- });
15
- ```
16
-
17
- Characteristics:
18
-
19
- - Tests behavior users/callers care about
20
- - Uses public API only
21
- - Survives internal refactors
22
- - Describes WHAT, not HOW
23
- - One logical assertion per test
24
-
25
- ## Bad Tests
26
-
27
- **Implementation-detail tests**: Coupled to internal structure.
28
-
29
- ```typescript
30
- // BAD: Tests implementation details
31
- test("checkout calls paymentService.process", async () => {
32
- const mockPayment = jest.mock(paymentService);
33
- await checkout(cart, payment);
34
- expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
35
- });
36
- ```
37
-
38
- Red flags:
39
-
40
- - Mocking internal collaborators
41
- - Testing private methods
42
- - Asserting on call counts/order
43
- - Test breaks when refactoring without behavior change
44
- - Test name describes HOW not WHAT
45
- - Verifying through external means instead of interface
46
-
47
- ```typescript
48
- // BAD: Bypasses interface to verify
49
- test("createUser saves to database", async () => {
50
- await createUser({ name: "Alice" });
51
- const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
52
- expect(row).toBeDefined();
53
- });
54
-
55
- // GOOD: Verifies through interface
56
- test("createUser makes user retrievable", async () => {
57
- const user = await createUser({ name: "Alice" });
58
- const retrieved = await getUser(user.id);
59
- expect(retrieved.name).toBe("Alice");
60
- });
61
- ```
62
-
63
- **Tautological tests**: Expected value restates the implementation, so the test passes by construction.
64
-
65
- ```typescript
66
- // BAD: Expected value is recomputed the way the code computes it
67
- test("calculateTotal sums line items", () => {
68
- const items = [{ price: 10 }, { price: 5 }];
69
- const expected = items.reduce((sum, i) => sum + i.price, 0);
70
- expect(calculateTotal(items)).toBe(expected);
71
- });
72
-
73
- // GOOD: Expected value is an independent, known literal
74
- test("calculateTotal sums line items", () => {
75
- expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
76
- });
77
- ```
@@ -1,118 +0,0 @@
1
- import { readFile, realpath } from "node:fs/promises";
2
- import { homedir } from "node:os";
3
- import { dirname, resolve } from "node:path";
4
-
5
- import { stripFrontmatter } from "@earendil-works/pi-coding-agent";
6
- import type { HostSkillExpansionEvidence } from "./host-contracts.ts";
7
- export type CanonicalSkillName = "tdd" | "ak-cross-m-review";
8
-
9
- export type CanonicalSkillSnapshot = Readonly<{
10
- raw: string;
11
- path: string;
12
- baseDir: string;
13
- body: string;
14
- /** Plain skill text — no length/digest identity shell (ADR 0031). */
15
- snapshotIdentity: Readonly<{ text: string }>;
16
- }>;
17
-
18
- export type CanonicalSkillEvidence<Name extends CanonicalSkillName = CanonicalSkillName> = Readonly<{
19
- name: Name;
20
- location: string;
21
- content: string;
22
- userMessage: string;
23
- }>;
24
-
25
- export type CanonicalSkillBinding<Name extends CanonicalSkillName = CanonicalSkillName> = Readonly<{
26
- name: Name;
27
- snapshot: CanonicalSkillSnapshot;
28
- /**
29
- * Accept host expansion evidence when name/location/content match the binding
30
- * and userMessage equals the captured original request (ADR 0032).
31
- */
32
- captureExpansion(
33
- evidence: HostSkillExpansionEvidence | undefined,
34
- originalRequest: string,
35
- ): CanonicalSkillEvidence<Name> | undefined;
36
- }>;
37
-
38
- export type AnyCanonicalSkillBinding =
39
- | CanonicalSkillBinding<"tdd">
40
- | CanonicalSkillBinding<"ak-cross-m-review">;
41
-
42
- export function captureCanonicalSkillExpansion<Name extends CanonicalSkillName>(
43
- name: Name,
44
- snapshot: CanonicalSkillSnapshot,
45
- configuredPath: string,
46
- evidence: HostSkillExpansionEvidence | undefined,
47
- originalRequest: string,
48
- ): CanonicalSkillEvidence<Name> | undefined {
49
- const matchedPath =
50
- evidence?.location === configuredPath
51
- ? configuredPath
52
- : evidence?.location === snapshot.path
53
- ? snapshot.path
54
- : undefined;
55
- const expectedContent = matchedPath === undefined ? undefined : snapshot.body;
56
- const prefixedContent = matchedPath === undefined
57
- ? undefined
58
- : `References are relative to ${dirname(matchedPath)}.\n\n${snapshot.body}`;
59
- if (
60
- evidence?.name !== name
61
- || matchedPath === undefined
62
- || (evidence.content !== expectedContent && evidence.content !== prefixedContent)
63
- || evidence.userMessage !== originalRequest
64
- ) {
65
- return undefined;
66
- }
67
- return Object.freeze({ ...evidence, name });
68
- }
69
-
70
- export class CanonicalSkillUnavailableError extends Error {
71
- readonly code = "canonical-skill-unavailable" as const;
72
- constructor(readonly skillName: CanonicalSkillName, path: string, cause: unknown) {
73
- super(`Canonical ${skillName} Skill is unavailable at ${path}`, { cause });
74
- this.name = "CanonicalSkillUnavailableError";
75
- }
76
- }
77
-
78
- export async function loadCanonicalSkillBinding(
79
- name: CanonicalSkillName,
80
- ): Promise<AnyCanonicalSkillBinding> {
81
- const configuredPath = resolve(
82
- homedir(),
83
- `.agents/skills/${name}/SKILL.md`,
84
- );
85
- let path: string;
86
- let raw: string;
87
- try {
88
- path = await realpath(configuredPath);
89
- raw = await readFile(path, "utf8");
90
- } catch (error) {
91
- throw new CanonicalSkillUnavailableError(name, configuredPath, error);
92
- }
93
- const body = stripFrontmatter(raw).trim();
94
- if (body.length === 0) {
95
- throw new Error(`Canonical ${name} Skill is empty at ${path}`);
96
- }
97
- const snapshot: CanonicalSkillSnapshot = Object.freeze({
98
- raw,
99
- path,
100
- baseDir: dirname(path),
101
- body,
102
- snapshotIdentity: Object.freeze({ text: raw }),
103
- });
104
- const binding: CanonicalSkillBinding<typeof name> = {
105
- name,
106
- snapshot,
107
- captureExpansion(evidence, originalRequest) {
108
- return captureCanonicalSkillExpansion(
109
- name,
110
- snapshot,
111
- configuredPath,
112
- evidence,
113
- originalRequest,
114
- );
115
- },
116
- };
117
- return Object.freeze(binding) as AnyCanonicalSkillBinding;
118
- }
@@ -1,185 +0,0 @@
1
- /** #922 / #980 host-native packaged method delivery. */
2
- import { access, lstat, mkdir, readdir, readFile, readlink, realpath, symlink } from "node:fs/promises";
3
- import { basename, dirname, join } from "node:path";
4
- import type { MethodBinding } from "./host-contracts.ts";
5
-
6
- type HostMethodSkill = Readonly<{ name: string }>;
7
- export const HOST_METHOD_PLUGIN_NAME = "ak-methods" as const;
8
- export const packagedMethodsDir = (root: string) => join(root, "resources", "methods");
9
- export const packagedMethodPluginDir = (root: string) => join(root, "dist", "method-host-plugin");
10
-
11
- export function hostMethodSkills(methods: readonly MethodBinding[]): readonly HostMethodSkill[] {
12
- return Object.freeze(methods.flatMap((method) => {
13
- if (method.kind !== "skill") return [];
14
- const name = basename(dirname(method.path));
15
- return name ? [Object.freeze({ name })] : [];
16
- }));
17
- }
18
-
19
- const alreadyPrefixed = (prompt: string, token: string) => {
20
- const text = prompt.trimStart();
21
- return text === token || text.startsWith(`${token} `) || text.startsWith(`${token}\n`);
22
- };
23
-
24
- function prefixMethodInvocation(token: string, prompt: string): string {
25
- if (alreadyPrefixed(prompt, token)) return prompt;
26
- return prompt ? `${token} ${prompt}` : token;
27
- }
28
-
29
- /** Claude's documented plugin Skill invocation syntax. */
30
- export function applyClaudeSkillInvocation(methods: readonly MethodBinding[], prompt: string): string {
31
- const skills = hostMethodSkills(methods);
32
- if (skills.length !== 1) return prompt;
33
- return prefixMethodInvocation(`/${HOST_METHOD_PLUGIN_NAME}:${skills[0]!.name}`, prompt);
34
- }
35
-
36
- /** Codex's documented explicit Skill invocation syntax. */
37
- export function applyCodexSkillInvocation(methods: readonly MethodBinding[], prompt: string): string {
38
- return hostMethodSkills(methods).reduceRight(
39
- (invocation, skill) => prefixMethodInvocation(`$${skill.name}`, invocation),
40
- prompt,
41
- );
42
- }
43
-
44
- const isEnoent = (error: unknown): boolean =>
45
- (error as NodeJS.ErrnoException).code === "ENOENT";
46
-
47
- /** Packaged method skill directory names (child has SKILL.md). I/O errors propagate. */
48
- async function packagedMethodSkillNames(packagedMethodsRealpath: string): Promise<readonly string[]> {
49
- const entries = await readdir(packagedMethodsRealpath, { withFileTypes: true });
50
- const names: string[] = [];
51
- for (const entry of entries) {
52
- if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
53
- try {
54
- await access(join(packagedMethodsRealpath, entry.name, "SKILL.md"));
55
- } catch (error) {
56
- if (isEnoent(error)) continue;
57
- throw error;
58
- }
59
- names.push(entry.name);
60
- }
61
- return names;
62
- }
63
-
64
- /**
65
- * Byte map of every regular file under a packaged skill directory
66
- * (relative path → bytes). Undefined when SKILL.md is missing. Other I/O
67
- * errors propagate. Integrity only — no free-text parsing of Skill prose.
68
- */
69
- async function packagedSkillFileBytes(
70
- skillDir: string,
71
- ): Promise<ReadonlyMap<string, Buffer> | undefined> {
72
- try {
73
- await access(join(skillDir, "SKILL.md"));
74
- } catch (error) {
75
- if (isEnoent(error)) return undefined;
76
- throw error;
77
- }
78
- const files = new Map<string, Buffer>();
79
- async function walk(dir: string, prefix: string): Promise<void> {
80
- const entries = await readdir(dir, { withFileTypes: true });
81
- for (const entry of entries) {
82
- const rel = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
83
- const full = join(dir, entry.name);
84
- if (entry.isDirectory()) {
85
- await walk(full, rel);
86
- continue;
87
- }
88
- if (entry.isFile()) {
89
- files.set(rel, await readFile(full));
90
- }
91
- }
92
- }
93
- await walk(skillDir, "");
94
- return files;
95
- }
96
-
97
- /**
98
- * True when catalog skill already carries identical bytes for every required
99
- * packaged relative path. Only those paths are read — extra catalog files are
100
- * out of scope for the compatibility proof.
101
- */
102
- async function catalogPublishesPackagedSkill(
103
- catalogSkillDir: string,
104
- required: ReadonlyMap<string, Buffer>,
105
- ): Promise<boolean> {
106
- for (const [rel, bytes] of required) {
107
- let other: Buffer;
108
- try {
109
- other = await readFile(join(catalogSkillDir, rel));
110
- } catch (error) {
111
- if (isEnoent(error)) return false;
112
- throw error;
113
- }
114
- if (!bytes.equals(other)) return false;
115
- }
116
- return true;
117
- }
118
-
119
- /**
120
- * Existing project catalog is usable when it already publishes every packaged
121
- * method skill with identical file bytes. Path identity is sufficient but not
122
- * required (#980): a worktree catalog at another realpath that still carries
123
- * the packaged method bytes must stay put. Name-only / foreign / stale content
124
- * remains a true conflict. Incomplete, broken, or non-symlink entries too.
125
- *
126
- * Caller must only invoke this when `link` is known present; link-missing
127
- * ENOENT is classified solely by the existence probe in
128
- * `installWorkspaceMethodSkills`, not here.
129
- */
130
- async function isCompatibleMethodCatalog(
131
- link: string,
132
- packagedMethodsRealpath: string,
133
- ): Promise<boolean> {
134
- const stat = await lstat(link);
135
- if (!stat.isSymbolicLink()) return false;
136
- let resolved: string;
137
- try {
138
- resolved = await realpath(link);
139
- } catch (error) {
140
- if (isEnoent(error)) return false;
141
- throw error;
142
- }
143
- if (resolved === packagedMethodsRealpath) return true;
144
- const names = await packagedMethodSkillNames(packagedMethodsRealpath);
145
- if (names.length === 0) return false;
146
- for (const name of names) {
147
- const required = await packagedSkillFileBytes(join(packagedMethodsRealpath, name));
148
- if (required === undefined) return false;
149
- if (!(await catalogPublishesPackagedSkill(join(resolved, name), required))) return false;
150
- }
151
- return true;
152
- }
153
-
154
- /** Install the documented project Skill catalog once and leave it in place. */
155
- export async function installWorkspaceMethodSkills(cwd: string, packageRoot: string): Promise<void> {
156
- const target = await realpath(packagedMethodsDir(packageRoot));
157
- const link = join(cwd, ".agents", "skills");
158
-
159
- // Existence probe only — this seam alone classifies link-missing ENOENT.
160
- let present: boolean;
161
- try {
162
- await lstat(link);
163
- present = true;
164
- } catch (error) {
165
- if (!isEnoent(error)) throw error;
166
- present = false;
167
- }
168
-
169
- if (present) {
170
- if (await isCompatibleMethodCatalog(link, target)) return;
171
- const stat = await lstat(link);
172
- const detail = stat.isSymbolicLink() ? `symlink to ${await readlink(link)}` : "non-symlink entry";
173
- throw new Error(`workspace method catalog conflict at ${link}: ${detail}`);
174
- }
175
-
176
- await mkdir(dirname(link), { recursive: true });
177
- try {
178
- await symlink(target, link);
179
- } catch (error) {
180
- if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
181
- if (!(await isCompatibleMethodCatalog(link, target))) {
182
- throw new Error(`workspace method catalog conflict at ${link}`);
183
- }
184
- }
185
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * Canonical Skill binding built from package-owned method material.
3
- * Lives beside the method seam — used by the Internal role runtime, not the
4
- * public ak-role bin bundle.
5
- */
6
- import { dirname } from "node:path";
7
-
8
- import {
9
- captureCanonicalSkillExpansion,
10
- type CanonicalSkillBinding,
11
- type CanonicalSkillName,
12
- } from "../canonical-skill-binding.ts";
13
- import {
14
- loadPackagedMethodSkillMaterial,
15
- resolvePackagedMethodSkillPath,
16
- type PackagedMethodSkillName,
17
- } from "./method-skill.ts";
18
-
19
- type PackagedCanonicalSkillName = Extract<
20
- PackagedMethodSkillName,
21
- CanonicalSkillName
22
- >;
23
-
24
- /**
25
- * Build a CanonicalSkillBinding from package-owned material.
26
- * captureExpansion accepts the package skill path (configured or realpath).
27
- */
28
- export async function loadPackagedCanonicalSkillBinding<
29
- Name extends PackagedCanonicalSkillName,
30
- >(
31
- packageRoot: string,
32
- name: Name,
33
- ): Promise<CanonicalSkillBinding<Name>> {
34
- // Forced expansion bindings are only defined for canonical completion-gated skills.
35
- // Optional Fixer diagnosing-bugs stays available via --skill without this binding.
36
- const material = await loadPackagedMethodSkillMaterial(packageRoot, name);
37
- const configuredPath = resolvePackagedMethodSkillPath(packageRoot, name);
38
- const snapshot = Object.freeze({
39
- raw: material.raw,
40
- path: material.skillPath,
41
- baseDir: dirname(material.skillPath),
42
- body: material.body,
43
- snapshotIdentity: Object.freeze({ text: material.raw }),
44
- });
45
-
46
- const binding: CanonicalSkillBinding<Name> = {
47
- name,
48
- snapshot,
49
- captureExpansion(evidence, originalRequest) {
50
- return captureCanonicalSkillExpansion(
51
- name,
52
- snapshot,
53
- configuredPath,
54
- evidence,
55
- originalRequest,
56
- );
57
- },
58
- };
59
- return Object.freeze(binding);
60
- }