vouchington-tooling 0.29.0 → 0.31.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.
Files changed (174) hide show
  1. package/README.md +69 -19
  2. package/bin/node-guard.mjs +9 -0
  3. package/bin/vouchington-mcp.mjs +20 -0
  4. package/bin/vouchington.mjs +18 -0
  5. package/dist/agent-blackboard/feedback-outbox-counts.d.mts +5 -0
  6. package/dist/agent-blackboard/feedback-outbox-counts.mjs +11 -0
  7. package/dist/agent-blackboard/feedback-outbox-directory.d.mts +2 -0
  8. package/dist/agent-blackboard/feedback-outbox-directory.mjs +16 -0
  9. package/dist/agent-blackboard/feedback-outbox.mjs +5 -7
  10. package/dist/agent-blackboard/index.d.mts +1 -0
  11. package/dist/agent-blackboard/index.mjs +1 -0
  12. package/dist/api-contract-discovery/header-contract-registry.mjs +2 -0
  13. package/dist/api-contract-discovery/protocol-body-status.d.mts +2 -0
  14. package/dist/api-contract-discovery/protocol-body-status.mjs +4 -0
  15. package/dist/api-contract-discovery/protocol-callback-argument-bindings.d.mts +7 -0
  16. package/dist/api-contract-discovery/protocol-callback-argument-bindings.mjs +33 -0
  17. package/dist/api-contract-discovery/protocol-callback-invocation.d.mts +5 -0
  18. package/dist/api-contract-discovery/protocol-callback-invocation.mjs +103 -0
  19. package/dist/api-contract-discovery/protocol-callback-mutations.d.mts +5 -0
  20. package/dist/api-contract-discovery/protocol-callback-mutations.mjs +42 -0
  21. package/dist/api-contract-discovery/protocol-callback-registration.d.mts +3 -0
  22. package/dist/api-contract-discovery/protocol-callback-registration.mjs +27 -0
  23. package/dist/api-contract-discovery/protocol-callback-values.d.mts +17 -0
  24. package/dist/api-contract-discovery/protocol-callback-values.mjs +114 -0
  25. package/dist/api-contract-discovery/protocol-context-alias.d.mts +3 -0
  26. package/dist/api-contract-discovery/protocol-context-alias.mjs +20 -0
  27. package/dist/api-contract-discovery/protocol-contract-extraction.d.mts +9 -0
  28. package/dist/api-contract-discovery/protocol-contract-extraction.mjs +87 -0
  29. package/dist/api-contract-discovery/protocol-contract-keys.d.mts +1 -0
  30. package/dist/api-contract-discovery/protocol-contract-keys.mjs +8 -0
  31. package/dist/api-contract-discovery/protocol-contract-registry.d.mts +5 -0
  32. package/dist/api-contract-discovery/protocol-contract-registry.mjs +133 -0
  33. package/dist/api-contract-discovery/protocol-executable-path.d.mts +3 -0
  34. package/dist/api-contract-discovery/protocol-executable-path.mjs +64 -0
  35. package/dist/api-contract-discovery/protocol-execution-path.d.mts +3 -0
  36. package/dist/api-contract-discovery/protocol-execution-path.mjs +16 -0
  37. package/dist/api-contract-discovery/protocol-http-association.d.mts +7 -0
  38. package/dist/api-contract-discovery/protocol-http-association.mjs +97 -0
  39. package/dist/api-contract-discovery/protocol-http-body-kinds.d.mts +3 -0
  40. package/dist/api-contract-discovery/protocol-http-body-kinds.mjs +19 -0
  41. package/dist/api-contract-discovery/protocol-http-context-callers.d.mts +6 -0
  42. package/dist/api-contract-discovery/protocol-http-context-callers.mjs +34 -0
  43. package/dist/api-contract-discovery/protocol-http-context.d.mts +9 -0
  44. package/dist/api-contract-discovery/protocol-http-context.mjs +108 -0
  45. package/dist/api-contract-discovery/protocol-http-emission.d.mts +3 -0
  46. package/dist/api-contract-discovery/protocol-http-emission.mjs +76 -0
  47. package/dist/api-contract-discovery/protocol-http-implicit-taint.d.mts +7 -0
  48. package/dist/api-contract-discovery/protocol-http-implicit-taint.mjs +26 -0
  49. package/dist/api-contract-discovery/protocol-http-registration.d.mts +12 -0
  50. package/dist/api-contract-discovery/protocol-http-registration.mjs +53 -0
  51. package/dist/api-contract-discovery/protocol-marker-analysis.d.mts +7 -0
  52. package/dist/api-contract-discovery/protocol-marker-analysis.mjs +50 -0
  53. package/dist/api-contract-discovery/protocol-opaque-callback.d.mts +3 -0
  54. package/dist/api-contract-discovery/protocol-opaque-callback.mjs +107 -0
  55. package/dist/api-contract-discovery/protocol-platform-callbacks.d.mts +5 -0
  56. package/dist/api-contract-discovery/protocol-platform-callbacks.mjs +72 -0
  57. package/dist/api-contract-discovery/protocol-platform-mutation-targets.d.mts +5 -0
  58. package/dist/api-contract-discovery/protocol-platform-mutation-targets.mjs +89 -0
  59. package/dist/api-contract-discovery/protocol-platform-writes.d.mts +3 -0
  60. package/dist/api-contract-discovery/protocol-platform-writes.mjs +92 -0
  61. package/dist/api-contract-discovery/protocol-requested-keys.d.mts +5 -0
  62. package/dist/api-contract-discovery/protocol-requested-keys.mjs +45 -0
  63. package/dist/api-contract-discovery/protocol-sse-callers.d.mts +7 -0
  64. package/dist/api-contract-discovery/protocol-sse-callers.mjs +67 -0
  65. package/dist/api-contract-discovery/protocol-sse-emission.d.mts +11 -0
  66. package/dist/api-contract-discovery/protocol-sse-emission.mjs +61 -0
  67. package/dist/api-contract-discovery/protocol-sse-feasible-status.d.mts +3 -0
  68. package/dist/api-contract-discovery/protocol-sse-feasible-status.mjs +24 -0
  69. package/dist/api-contract-discovery/protocol-sse-raw-writes.d.mts +9 -0
  70. package/dist/api-contract-discovery/protocol-sse-raw-writes.mjs +70 -0
  71. package/dist/api-contract-discovery/protocol-sse-status.d.mts +3 -0
  72. package/dist/api-contract-discovery/protocol-sse-status.mjs +84 -0
  73. package/dist/api-contract-discovery/protocol-sse-write-access.d.mts +9 -0
  74. package/dist/api-contract-discovery/protocol-sse-write-access.mjs +42 -0
  75. package/dist/api-contract-discovery/protocol-sse-write-helpers.d.mts +6 -0
  76. package/dist/api-contract-discovery/protocol-sse-write-helpers.mjs +66 -0
  77. package/dist/api-contract-discovery/protocol-sse-write-mutations.d.mts +9 -0
  78. package/dist/api-contract-discovery/protocol-sse-write-mutations.mjs +36 -0
  79. package/dist/api-contract-discovery/protocol-sse-write-resolution.d.mts +14 -0
  80. package/dist/api-contract-discovery/protocol-sse-write-resolution.mjs +79 -0
  81. package/dist/api-contract-discovery/protocol-status-dominance.d.mts +5 -0
  82. package/dist/api-contract-discovery/protocol-status-dominance.mjs +35 -0
  83. package/dist/api-contract-discovery/protocol-write-receiver.d.mts +9 -0
  84. package/dist/api-contract-discovery/protocol-write-receiver.mjs +79 -0
  85. package/dist/api-contract-discovery/query-contract-registry.mjs +2 -0
  86. package/dist/api-contract-discovery/registered-route-binding-writes.d.mts +3 -0
  87. package/dist/api-contract-discovery/registered-route-binding-writes.mjs +30 -0
  88. package/dist/api-contract-discovery/registered-route-catalog.mjs +11 -7
  89. package/dist/api-contract-discovery/registered-route-factory-returns.d.mts +3 -0
  90. package/dist/api-contract-discovery/registered-route-factory-returns.mjs +29 -0
  91. package/dist/api-contract-discovery/registered-route-handler-analysis.d.mts +5 -1
  92. package/dist/api-contract-discovery/registered-route-handler-analysis.mjs +79 -35
  93. package/dist/api-contract-discovery/registered-route-runtime-parameters.d.mts +3 -0
  94. package/dist/api-contract-discovery/registered-route-runtime-parameters.mjs +5 -0
  95. package/dist/api-contract-discovery/registered-route-terminal-error.d.mts +3 -0
  96. package/dist/api-contract-discovery/registered-route-terminal-error.mjs +98 -0
  97. package/dist/api-contract-discovery/request-contract-registry.mjs +2 -0
  98. package/dist/api-contract-discovery/response-contract-implicit.mjs +28 -10
  99. package/dist/api-contract-discovery/response-contract-registry.mjs +14 -1
  100. package/dist/api-contract-discovery/response-contract-route-analysis.d.mts +1 -1
  101. package/dist/api-contract-discovery/response-contract-route-analysis.mjs +22 -4
  102. package/dist/api-contract-discovery/response-contract-status.d.mts +1 -1
  103. package/dist/api-contract-discovery/response-contract-status.mjs +10 -8
  104. package/dist/api-contract-discovery/response-contract-types.d.mts +7 -0
  105. package/dist/cli/commands/agent-blackboard-flags.d.mts +4 -0
  106. package/dist/cli/commands/agent-blackboard-flags.mjs +34 -0
  107. package/dist/cli/commands/agent-blackboard.mjs +27 -35
  108. package/dist/cli/index.d.mts +2 -0
  109. package/dist/cli/index.mjs +12 -15
  110. package/dist/cli/parse-forwarded-commands.d.mts +0 -3
  111. package/dist/cli/parse-forwarded-commands.mjs +0 -2
  112. package/dist/cli/parse.d.mts +0 -3
  113. package/dist/cli/usage.d.mts +1 -1
  114. package/dist/cli/usage.mjs +2 -6
  115. package/dist/contract-schema/contract-schema-bounded-array.d.mts +1 -3
  116. package/dist/contract-schema/contract-schema-bounded-array.mjs +5 -0
  117. package/dist/contract-schema/contract-schema-object-tuple.mjs +7 -5
  118. package/dist/contract-schema/contract-schema-type-extractor.mjs +13 -0
  119. package/dist/contract-schema/contract-schema-validator.mjs +2 -2
  120. package/dist/index.d.mts +0 -2
  121. package/dist/index.mjs +0 -1
  122. package/dist/mcp-server/dispatch.d.mts +4 -1
  123. package/dist/mcp-server/dispatch.mjs +6 -4
  124. package/dist/mcp-server/handlers-outbox.mjs +2 -7
  125. package/dist/mcp-server/harness.test-helpers.d.mts +1 -0
  126. package/dist/mcp-server/harness.test-helpers.mjs +1 -1
  127. package/dist/mcp-server/main.d.mts +5 -0
  128. package/dist/mcp-server/main.mjs +8 -0
  129. package/dist/mcp-server/run.d.mts +2 -0
  130. package/dist/mcp-server/run.mjs +4 -1
  131. package/dist/mcp-server/sdk.d.mts +11 -2
  132. package/dist/mcp-server/sdk.mjs +37 -5
  133. package/dist/mcp-server/server.mjs +3 -2
  134. package/dist/mcp-server/tools.mjs +3 -2
  135. package/dist/mcp-server/worktree.d.mts +17 -10
  136. package/dist/mcp-server/worktree.mjs +48 -31
  137. package/dist/openapi-document/build-openapi-catalog-sse.d.mts +4 -0
  138. package/dist/openapi-document/build-openapi-catalog-sse.mjs +27 -0
  139. package/dist/openapi-document/build-openapi-document.mjs +5 -2
  140. package/dist/openapi-document/build-openapi-response.d.mts +2 -2
  141. package/dist/openapi-document/build-openapi-response.mjs +33 -3
  142. package/dist/openapi-document/build-openapi-sse-events.d.mts +11 -0
  143. package/dist/openapi-document/build-openapi-sse-events.mjs +46 -0
  144. package/dist/openapi-document/contract-schema-intersection-merge.mjs +11 -11
  145. package/dist/openapi-document/contract-schema-to-openapi.mjs +3 -3
  146. package/dist/openapi-document/openapi-route-helpers.mjs +1 -3
  147. package/dist/openapi-document/openapi-types.d.mts +3 -0
  148. package/dist/openapi-document/operation-types.d.mts +7 -0
  149. package/docs/mcp-server.md +83 -25
  150. package/package.json +11 -9
  151. package/scripts/build.mjs +2 -0
  152. package/skills/blackboard/SKILL.md +66 -4
  153. package/skills/manifest.json +19 -19
  154. package/dist/agent-harness-config/apply.d.mts +0 -4
  155. package/dist/agent-harness-config/apply.mjs +0 -72
  156. package/dist/agent-harness-config/cli.d.mts +0 -10
  157. package/dist/agent-harness-config/cli.mjs +0 -100
  158. package/dist/agent-harness-config/index.d.mts +0 -4
  159. package/dist/agent-harness-config/index.mjs +0 -3
  160. package/dist/agent-harness-config/merge-json.d.mts +0 -5
  161. package/dist/agent-harness-config/merge-json.mjs +0 -62
  162. package/dist/agent-harness-config/merge-toml.d.mts +0 -6
  163. package/dist/agent-harness-config/merge-toml.mjs +0 -132
  164. package/dist/agent-harness-config/paths.d.mts +0 -3
  165. package/dist/agent-harness-config/paths.mjs +0 -117
  166. package/dist/agent-harness-config/policy.d.mts +0 -14
  167. package/dist/agent-harness-config/policy.mjs +0 -51
  168. package/dist/agent-harness-config/prerequisites.d.mts +0 -2
  169. package/dist/agent-harness-config/prerequisites.mjs +0 -67
  170. package/dist/agent-harness-config/toml-value.d.mts +0 -6
  171. package/dist/agent-harness-config/toml-value.mjs +0 -69
  172. package/dist/agent-harness-config/types.d.mts +0 -90
  173. package/dist/agent-harness-config/types.mjs +0 -2
  174. package/docs/agent-harness-config.md +0 -134
@@ -4,7 +4,7 @@
4
4
  {
5
5
  "name": "agent-workflow",
6
6
  "plugin": "vouchington-workflow",
7
- "pluginVersion": "0.11.0",
7
+ "pluginVersion": "0.12.0",
8
8
  "path": "agent-workflow/SKILL.md",
9
9
  "prerequisites": ["github-issue", "pr-description", "review-ci-logs"]
10
10
  },
@@ -18,7 +18,7 @@
18
18
  {
19
19
  "name": "blackboard",
20
20
  "plugin": "vouchington-workflow",
21
- "pluginVersion": "0.11.0",
21
+ "pluginVersion": "0.12.0",
22
22
  "path": "blackboard/SKILL.md"
23
23
  },
24
24
  {
@@ -30,7 +30,7 @@
30
30
  {
31
31
  "name": "dependabot",
32
32
  "plugin": "vouchington-workflow",
33
- "pluginVersion": "0.11.0",
33
+ "pluginVersion": "0.12.0",
34
34
  "path": "dependabot/SKILL.md",
35
35
  "prerequisites": ["github-actions-checklist"]
36
36
  },
@@ -44,26 +44,26 @@
44
44
  {
45
45
  "name": "git-commit-checklist",
46
46
  "plugin": "vouchington-workflow",
47
- "pluginVersion": "0.11.0",
47
+ "pluginVersion": "0.12.0",
48
48
  "path": "git-commit-checklist/SKILL.md"
49
49
  },
50
50
  {
51
51
  "name": "github-actions-authoring",
52
52
  "plugin": "vouchington-workflow",
53
- "pluginVersion": "0.11.0",
53
+ "pluginVersion": "0.12.0",
54
54
  "path": "github-actions-authoring/SKILL.md",
55
55
  "prerequisites": ["github-actions-checklist"]
56
56
  },
57
57
  {
58
58
  "name": "github-actions-checklist",
59
59
  "plugin": "vouchington-workflow",
60
- "pluginVersion": "0.11.0",
60
+ "pluginVersion": "0.12.0",
61
61
  "path": "github-actions-checklist/SKILL.md"
62
62
  },
63
63
  {
64
64
  "name": "github-issue",
65
65
  "plugin": "vouchington-workflow",
66
- "pluginVersion": "0.11.0",
66
+ "pluginVersion": "0.12.0",
67
67
  "path": "github-issue/SKILL.md"
68
68
  },
69
69
  {
@@ -76,26 +76,26 @@
76
76
  {
77
77
  "name": "npm-publishing",
78
78
  "plugin": "vouchington-workflow",
79
- "pluginVersion": "0.11.0",
79
+ "pluginVersion": "0.12.0",
80
80
  "path": "npm-publishing/SKILL.md"
81
81
  },
82
82
  {
83
83
  "name": "organize-github-issues",
84
84
  "plugin": "vouchington-workflow",
85
- "pluginVersion": "0.11.0",
85
+ "pluginVersion": "0.12.0",
86
86
  "path": "organize-github-issues/SKILL.md",
87
87
  "prerequisites": ["github-issue"]
88
88
  },
89
89
  {
90
90
  "name": "package-json-checklist",
91
91
  "plugin": "vouchington-workflow",
92
- "pluginVersion": "0.11.0",
92
+ "pluginVersion": "0.12.0",
93
93
  "path": "package-json-checklist/SKILL.md"
94
94
  },
95
95
  {
96
96
  "name": "planning",
97
97
  "plugin": "vouchington-workflow",
98
- "pluginVersion": "0.11.0",
98
+ "pluginVersion": "0.12.0",
99
99
  "path": "planning/SKILL.md",
100
100
  "prerequisites": ["github-issue"]
101
101
  },
@@ -127,53 +127,53 @@
127
127
  {
128
128
  "name": "pr-description",
129
129
  "plugin": "vouchington-workflow",
130
- "pluginVersion": "0.11.0",
130
+ "pluginVersion": "0.12.0",
131
131
  "path": "pr-description/SKILL.md"
132
132
  },
133
133
  {
134
134
  "name": "retrospective",
135
135
  "plugin": "vouchington-workflow",
136
- "pluginVersion": "0.11.0",
136
+ "pluginVersion": "0.12.0",
137
137
  "path": "retrospective/SKILL.md"
138
138
  },
139
139
  {
140
140
  "name": "retrospective-distill",
141
141
  "plugin": "vouchington-workflow",
142
- "pluginVersion": "0.11.0",
142
+ "pluginVersion": "0.12.0",
143
143
  "path": "retrospective-distill/SKILL.md",
144
144
  "prerequisites": ["github-issue"]
145
145
  },
146
146
  {
147
147
  "name": "review-ci-logs",
148
148
  "plugin": "vouchington-workflow",
149
- "pluginVersion": "0.11.0",
149
+ "pluginVersion": "0.12.0",
150
150
  "path": "review-ci-logs/SKILL.md",
151
151
  "prerequisites": ["github-actions-checklist", "pr-description"]
152
152
  },
153
153
  {
154
154
  "name": "review-github-issue-taxonomy",
155
155
  "plugin": "vouchington-workflow",
156
- "pluginVersion": "0.11.0",
156
+ "pluginVersion": "0.12.0",
157
157
  "path": "review-github-issue-taxonomy/SKILL.md",
158
158
  "prerequisites": ["github-issue"]
159
159
  },
160
160
  {
161
161
  "name": "revisit-followups",
162
162
  "plugin": "vouchington-workflow",
163
- "pluginVersion": "0.11.0",
163
+ "pluginVersion": "0.12.0",
164
164
  "path": "revisit-followups/SKILL.md",
165
165
  "prerequisites": ["github-issue"]
166
166
  },
167
167
  {
168
168
  "name": "stacked-prs",
169
169
  "plugin": "vouchington-workflow",
170
- "pluginVersion": "0.11.0",
170
+ "pluginVersion": "0.12.0",
171
171
  "path": "stacked-prs/SKILL.md"
172
172
  },
173
173
  {
174
174
  "name": "static-analysis-checklist",
175
175
  "plugin": "vouchington-workflow",
176
- "pluginVersion": "0.11.0",
176
+ "pluginVersion": "0.12.0",
177
177
  "path": "static-analysis-checklist/SKILL.md",
178
178
  "prerequisites": ["github-actions-checklist"]
179
179
  },
@@ -1,4 +0,0 @@
1
- import type { ApplyTarget, HarnessApplyResult, HarnessCheckResult, HarnessConfigOptions, HarnessPlan } from './types.mts';
2
- export declare function planHarnessConfig(target: ApplyTarget, options?: HarnessConfigOptions): HarnessPlan;
3
- export declare function checkHarnessConfig(target: ApplyTarget, options?: HarnessConfigOptions): Promise<HarnessCheckResult>;
4
- export declare function applyHarnessConfig(target: ApplyTarget, options?: HarnessConfigOptions): Promise<HarnessApplyResult>;
@@ -1,72 +0,0 @@
1
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
- import { dirname } from 'node:path';
3
- import { applyJsonPatches } from './merge-json.mjs';
4
- import { applyTomlPatches } from './merge-toml.mjs';
5
- import { planGlobalFiles, planRepoFiles } from './paths.mjs';
6
- import { checkHarnessPrerequisites } from './prerequisites.mjs';
7
- export function planHarnessConfig(target, options) {
8
- const files = target.kind === 'global' ? planGlobalFiles(options) : planRepoFiles(target.root, options);
9
- return { files, target };
10
- }
11
- async function readOptional(path) {
12
- try {
13
- return { exists: true, source: await readFile(path, 'utf8') };
14
- }
15
- catch (error) {
16
- if (error.code === 'ENOENT')
17
- return { exists: false, source: '' };
18
- throw error;
19
- }
20
- }
21
- function patched(plan, source) {
22
- return plan.format === 'json'
23
- ? applyJsonPatches(source, plan.patches, plan.path)
24
- : applyTomlPatches(source, plan.patches, plan.path);
25
- }
26
- function fileResult(plan, exists, drifts) {
27
- if (!exists)
28
- return { action: 'missing', drifts, path: plan.path };
29
- return { action: drifts.length === 0 ? 'ok' : 'update', drifts, path: plan.path };
30
- }
31
- export async function checkHarnessConfig(target, options) {
32
- const plan = planHarnessConfig(target, options);
33
- const files = [];
34
- for (const file of plan.files) {
35
- const { exists, source } = await readOptional(file.path);
36
- files.push(fileResult(file, exists, patched(file, source).drifts));
37
- }
38
- const prerequisites = await checkHarnessPrerequisites(target.kind === 'repo' ? target.root : undefined, options);
39
- return {
40
- compliant: files.every((file) => file.action === 'ok') &&
41
- prerequisites.every((prerequisite) => prerequisite.satisfied),
42
- files,
43
- prerequisites,
44
- target,
45
- };
46
- }
47
- export async function applyHarnessConfig(target, options) {
48
- const plan = planHarnessConfig(target, options);
49
- const files = [];
50
- const written = [];
51
- for (const file of plan.files) {
52
- const { exists, source } = await readOptional(file.path);
53
- const next = patched(file, source);
54
- const result = fileResult(file, exists, next.drifts);
55
- if (result.action === 'ok') {
56
- files.push(result);
57
- continue;
58
- }
59
- await mkdir(dirname(file.path), { recursive: true });
60
- await writeFile(file.path, next.text);
61
- files.push({ ...result, action: exists ? 'update' : 'create' });
62
- written.push(file.path);
63
- }
64
- const prerequisites = await checkHarnessPrerequisites(target.kind === 'repo' ? target.root : undefined, options);
65
- return {
66
- compliant: prerequisites.every((prerequisite) => prerequisite.satisfied),
67
- files,
68
- prerequisites,
69
- target,
70
- written,
71
- };
72
- }
@@ -1,10 +0,0 @@
1
- import { type HarnessId } from './types.mts';
2
- export interface ParsedAgentHarnessConfig {
3
- readonly action: 'dump' | 'check' | 'apply';
4
- readonly harnesses?: readonly HarnessId[];
5
- readonly home?: string;
6
- readonly repos: readonly string[];
7
- readonly global: boolean;
8
- }
9
- export declare function parseAgentHarnessConfigArguments(argv: readonly string[]): ParsedAgentHarnessConfig;
10
- export declare function runAgentHarnessConfigCli(argv: readonly string[]): Promise<number>;
@@ -1,100 +0,0 @@
1
- import { dumpHarnessPolicy } from './policy.mjs';
2
- import { applyHarnessConfig, checkHarnessConfig } from './apply.mjs';
3
- import { HARNESS_IDS } from './types.mjs';
4
- const AGENT_HARNESS_CONFIG_USAGE = 'agent-harness-config dump|check|apply [--global] [--repo PATH]... [--harness NAME]... [--home PATH]';
5
- function isHarness(value) {
6
- return HARNESS_IDS.includes(value);
7
- }
8
- export function parseAgentHarnessConfigArguments(argv) {
9
- const action = argv[0];
10
- if (action !== 'dump' && action !== 'check' && action !== 'apply') {
11
- throw new Error(`unknown agent-harness-config action: ${action ?? ''}\n${AGENT_HARNESS_CONFIG_USAGE}`);
12
- }
13
- let global = false;
14
- let home;
15
- const repos = [];
16
- const harnesses = [];
17
- for (let index = 1; index < argv.length; index += 1) {
18
- const flag = argv[index];
19
- if (flag === '--global') {
20
- global = true;
21
- continue;
22
- }
23
- if (flag === '--repo' || flag === '--harness' || flag === '--home') {
24
- const value = argv[index + 1];
25
- if (value === undefined)
26
- throw new Error(`${flag} requires a value\n${AGENT_HARNESS_CONFIG_USAGE}`);
27
- index += 1;
28
- if (flag === '--repo')
29
- repos.push(value);
30
- else if (flag === '--home')
31
- home = value;
32
- else if (!isHarness(value))
33
- throw new Error(`unknown harness: ${value}`);
34
- else
35
- harnesses.push(value);
36
- continue;
37
- }
38
- throw new Error(`unknown agent-harness-config option: ${flag}\n${AGENT_HARNESS_CONFIG_USAGE}`);
39
- }
40
- if (action !== 'dump' && !global && repos.length === 0) {
41
- throw new Error(`check and apply require --global and/or --repo\n${AGENT_HARNESS_CONFIG_USAGE}`);
42
- }
43
- return {
44
- action,
45
- global,
46
- repos,
47
- ...(harnesses.length > 0 ? { harnesses } : {}),
48
- ...(home === undefined ? {} : { home }),
49
- };
50
- }
51
- function formatDrift(drift) {
52
- return ` ${drift.path} ${drift.key}: ${JSON.stringify(drift.current)} -> ${JSON.stringify(drift.desired)}`;
53
- }
54
- function targets(parsed) {
55
- const list = [];
56
- if (parsed.global)
57
- list.push({ kind: 'global' });
58
- for (const root of parsed.repos)
59
- list.push({ kind: 'repo', root });
60
- return list;
61
- }
62
- export async function runAgentHarnessConfigCli(argv) {
63
- try {
64
- const parsed = parseAgentHarnessConfigArguments(argv);
65
- if (parsed.action === 'dump') {
66
- process.stdout.write(`${JSON.stringify(dumpHarnessPolicy(), null, 2)}\n`);
67
- return 0;
68
- }
69
- const options = {
70
- ...(parsed.harnesses === undefined ? {} : { harnesses: parsed.harnesses }),
71
- ...(parsed.home === undefined ? {} : { home: parsed.home }),
72
- };
73
- let compliant = true;
74
- for (const target of targets(parsed)) {
75
- const result = parsed.action === 'apply'
76
- ? await applyHarnessConfig(target, options)
77
- : await checkHarnessConfig(target, options);
78
- compliant &&=
79
- parsed.action === 'apply'
80
- ? result.prerequisites.every((prerequisite) => prerequisite.satisfied)
81
- : result.compliant;
82
- for (const file of result.files) {
83
- if (file.action === 'ok')
84
- process.stdout.write(`ok ${file.path}\n`);
85
- else
86
- process.stdout.write(`${file.action} ${file.path}\n`);
87
- for (const drift of file.drifts)
88
- process.stdout.write(`${formatDrift(drift)}\n`);
89
- }
90
- for (const prerequisite of result.prerequisites) {
91
- process.stdout.write(`prerequisite ${prerequisite.harness} ${prerequisite.key} satisfied=${prerequisite.satisfied}: ${prerequisite.message}\n`);
92
- }
93
- }
94
- return compliant ? 0 : 1;
95
- }
96
- catch (error) {
97
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
98
- return 2;
99
- }
100
- }
@@ -1,4 +0,0 @@
1
- export { applyHarnessConfig, checkHarnessConfig, planHarnessConfig } from './apply.mts';
2
- export { dumpHarnessPolicy, DEFAULT_EXTRA_WRITABLE_ROOTS } from './policy.mts';
3
- export { HARNESS_IDS } from './types.mts';
4
- export type { ApplyTarget, FilePlan, FileResult, HarnessApplyResult, HarnessCheckResult, HarnessConfigOptions, HarnessId, HarnessPlan, HarnessPolicyDump, JsonPatch, KeyDrift, TomlPatch, TomlValue, } from './types.mts';
@@ -1,3 +0,0 @@
1
- export { applyHarnessConfig, checkHarnessConfig, planHarnessConfig } from './apply.mjs';
2
- export { dumpHarnessPolicy, DEFAULT_EXTRA_WRITABLE_ROOTS } from './policy.mjs';
3
- export { HARNESS_IDS } from './types.mjs';
@@ -1,5 +0,0 @@
1
- import type { JsonPatch, KeyDrift } from './types.mts';
2
- export declare function applyJsonPatches(source: string, patches: readonly JsonPatch[], path: string): {
3
- drifts: KeyDrift[];
4
- text: string;
5
- };
@@ -1,62 +0,0 @@
1
- import { sameValue } from './policy.mjs';
2
- function getJsonPath(value, path) {
3
- let current = value;
4
- for (const key of path) {
5
- if (current === null || typeof current !== 'object' || Array.isArray(current))
6
- return undefined;
7
- current = current[key];
8
- }
9
- return current;
10
- }
11
- function setJsonPath(target, path, value) {
12
- let current = target;
13
- for (const key of path.slice(0, -1)) {
14
- const next = current[key];
15
- if (next === null || typeof next !== 'object' || Array.isArray(next)) {
16
- current[key] = {};
17
- }
18
- current = current[key];
19
- }
20
- const last = path.at(-1);
21
- if (last !== undefined)
22
- current[last] = value;
23
- }
24
- function parseJsonObject(source, path) {
25
- if (source.trim() === '')
26
- return {};
27
- const parsed = JSON.parse(source);
28
- if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
29
- throw new Error(`${path}: expected a JSON object`);
30
- }
31
- return parsed;
32
- }
33
- function unionMerge(current, values) {
34
- const base = Array.isArray(current) ? current : [];
35
- const merged = [...base];
36
- for (const value of values) {
37
- if (!merged.some((entry) => sameValue(entry, value)))
38
- merged.push(value);
39
- }
40
- return merged;
41
- }
42
- function desiredValue(document, patch) {
43
- if (patch.merge !== 'union')
44
- return patch.value;
45
- return unionMerge(getJsonPath(document, patch.path), patch.value);
46
- }
47
- export function applyJsonPatches(source, patches, path) {
48
- const document = parseJsonObject(source, path);
49
- const resolved = patches.map((patch) => ({ desired: desiredValue(document, patch), patch }));
50
- const drifts = [];
51
- for (const { desired, patch } of resolved) {
52
- const current = getJsonPath(document, patch.path);
53
- if (!sameValue(current, desired)) {
54
- drifts.push({ current, desired, key: patch.path.join('.'), path });
55
- }
56
- }
57
- if (drifts.length === 0)
58
- return { drifts, text: source === '' ? '{}\n' : source };
59
- for (const { desired, patch } of resolved)
60
- setJsonPath(document, patch.path, desired);
61
- return { drifts, text: `${JSON.stringify(document, null, 2)}\n` };
62
- }
@@ -1,6 +0,0 @@
1
- import type { KeyDrift, TomlPatch } from './types.mts';
2
- export declare function readTomlKey(text: string, table: string, key: string): unknown;
3
- export declare function applyTomlPatches(source: string, patches: readonly TomlPatch[], path: string): {
4
- drifts: KeyDrift[];
5
- text: string;
6
- };
@@ -1,132 +0,0 @@
1
- import { parse as parseToml } from 'smol-toml';
2
- import { sameValue } from './policy.mjs';
3
- import { formatTomlValue, parseTomlValue } from './toml-value.mjs';
4
- function lineStarts(text) {
5
- const starts = [0];
6
- for (let index = 0; index < text.length; index += 1) {
7
- if (text[index] === '\n')
8
- starts.push(index + 1);
9
- }
10
- return starts;
11
- }
12
- function lineAt(text, start) {
13
- const end = text.indexOf('\n', start);
14
- return text.slice(start, end === -1 ? text.length : end);
15
- }
16
- function isHeaderLine(line) {
17
- return line.trimStart().startsWith('[');
18
- }
19
- function headerName(line) {
20
- const trimmed = line.trim();
21
- if (trimmed.startsWith('[[') || !trimmed.startsWith('['))
22
- return undefined;
23
- const close = trimmed.indexOf(']');
24
- if (close <= 1)
25
- return undefined;
26
- return trimmed.slice(1, close);
27
- }
28
- function tableRange(text, table) {
29
- const starts = lineStarts(text);
30
- if (table === '') {
31
- const first = starts.find((start) => isHeaderLine(lineAt(text, start)));
32
- return { bodyEnd: first ?? text.length, bodyStart: 0 };
33
- }
34
- for (const start of starts) {
35
- const line = lineAt(text, start);
36
- if (headerName(line) !== table)
37
- continue;
38
- const bodyStart = start + line.length + (text[start + line.length] === '\n' ? 1 : 0);
39
- const next = starts.find((candidate) => candidate >= bodyStart && isHeaderLine(lineAt(text, candidate)));
40
- return { bodyEnd: next ?? text.length, bodyStart };
41
- }
42
- return { bodyEnd: -1, bodyStart: -1 };
43
- }
44
- function keyMatch(text, bodyStart, bodyEnd, key) {
45
- const pattern = new RegExp(`^[ \\t]*${key.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&')}[ \\t]*=[ \\t]*`, 'm');
46
- const slice = text.slice(bodyStart, bodyEnd);
47
- const match = pattern.exec(slice);
48
- if (!match || match.index === undefined)
49
- return undefined;
50
- const equals = bodyStart + match.index + match[0].length;
51
- return { equals, parsed: parseTomlValue(text, equals) };
52
- }
53
- export function readTomlKey(text, table, key) {
54
- const range = tableRange(text, table);
55
- if (range.bodyStart < 0)
56
- return undefined;
57
- return keyMatch(text, range.bodyStart, range.bodyEnd, key)?.parsed.value;
58
- }
59
- function appendTable(text, table, assignment) {
60
- const prefix = text === '' || text.endsWith('\n') ? text : `${text}\n`;
61
- const blank = prefix !== '' && !prefix.endsWith('\n\n') ? '\n' : '';
62
- return `${prefix}${blank}[${table}]\n${assignment}\n`;
63
- }
64
- function upsertTomlKey(text, table, key, value) {
65
- const assignment = `${key} = ${formatTomlValue(value)}`;
66
- const range = tableRange(text, table);
67
- if (range.bodyStart < 0)
68
- return appendTable(text, table, assignment);
69
- const existing = keyMatch(text, range.bodyStart, range.bodyEnd, key);
70
- if (existing) {
71
- return `${text.slice(0, existing.equals)}${formatTomlValue(value)}${text.slice(existing.parsed.end)}`;
72
- }
73
- const at = range.bodyEnd;
74
- const prefix = at > 0 && text[at - 1] !== '\n' ? '\n' : '';
75
- return `${text.slice(0, at)}${prefix}${assignment}\n${text.slice(at)}`;
76
- }
77
- function tomlPatchValue(parsed, table, key) {
78
- let current = parsed;
79
- if (table !== '') {
80
- for (const segment of table.split('.')) {
81
- /* v8 ignore next 2 -- if `text` parsed, every ancestor of a dotted table header is
82
- guaranteed to be a table too, so a real TOML parser can never hand back a non-object
83
- mid-path here. */
84
- if (current === null || typeof current !== 'object' || Array.isArray(current))
85
- return undefined;
86
- current = current[segment];
87
- }
88
- }
89
- /* v8 ignore next 2 -- assertValidToml only reaches a patch's table here once readTomlKey has
90
- already proven (as a match, or by forcing the write that creates it) that the table is a
91
- real nested object in this parsed document. */
92
- if (current === null || typeof current !== 'object' || Array.isArray(current))
93
- return undefined;
94
- return current[key];
95
- }
96
- function assertValidToml(text, patches, path) {
97
- let parsed;
98
- try {
99
- parsed = parseToml(text);
100
- }
101
- catch (error) {
102
- /* v8 ignore next -- smol-toml's parse() only ever throws TomlError, which extends Error, so
103
- the non-Error fallback here is unreachable in practice. */
104
- const message = error instanceof Error ? error.message : String(error);
105
- throw new Error(`${path}: produced invalid TOML (${message})`);
106
- }
107
- for (const patch of patches) {
108
- const value = tomlPatchValue(parsed, patch.table, patch.key);
109
- /* v8 ignore next 6 -- backstop for a formatTomlValue/parseToml encoding mismatch; formatTomlValue's
110
- JSON.stringify-based escaping is a valid TOML basic string in every case this module writes,
111
- so a real parser always reads back what we just wrote. */
112
- if (!sameValue(value, patch.value)) {
113
- const key = patch.table === '' ? patch.key : `${patch.table}.${patch.key}`;
114
- throw new Error(`${path}: wrote ${key} but a real TOML parser reads back ${JSON.stringify(value)}`);
115
- }
116
- }
117
- }
118
- export function applyTomlPatches(source, patches, path) {
119
- const drifts = [];
120
- let text = source.replaceAll('\r\n', '\n');
121
- for (const patch of patches) {
122
- const current = readTomlKey(text, patch.table, patch.key);
123
- if (sameValue(current, patch.value))
124
- continue;
125
- const key = patch.table === '' ? patch.key : `${patch.table}.${patch.key}`;
126
- drifts.push({ current, desired: patch.value, key, path });
127
- text = upsertTomlKey(text, patch.table, patch.key, patch.value);
128
- }
129
- if (drifts.length > 0)
130
- assertValidToml(text, patches, path);
131
- return { drifts, text };
132
- }
@@ -1,3 +0,0 @@
1
- import { type FilePlan, type HarnessConfigOptions } from './types.mts';
2
- export declare function planGlobalFiles(options?: HarnessConfigOptions): FilePlan[];
3
- export declare function planRepoFiles(root: string, options?: HarnessConfigOptions): FilePlan[];