@wardby/cli 0.3.0 → 0.4.1

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 (275) hide show
  1. package/.env.example +22 -3
  2. package/README.md +11 -9
  3. package/dist/cli-help.d.ts +1 -1
  4. package/dist/cli-help.js +1 -0
  5. package/dist/cli.js +68 -10
  6. package/dist/coding/base-commit.d.ts +6 -0
  7. package/dist/coding/base-commit.js +12 -0
  8. package/dist/coding/protocol.d.ts +17 -1
  9. package/dist/coding/protocol.js +17 -6
  10. package/dist/coding/provider.d.ts +6 -1
  11. package/dist/coding/provider.js +16 -9
  12. package/dist/config/providers.d.ts +25 -0
  13. package/dist/config/providers.js +71 -0
  14. package/dist/core/attribution.d.ts +101 -0
  15. package/dist/core/attribution.js +208 -0
  16. package/dist/core/budget-groups.d.ts +17 -10
  17. package/dist/core/budget-groups.js +15 -12
  18. package/dist/core/coding-queue.d.ts +3 -0
  19. package/dist/core/coding-queue.js +6 -2
  20. package/dist/core/coding-service-status.d.ts +11 -0
  21. package/dist/core/coding-service-status.js +17 -0
  22. package/dist/core/cost-report.d.ts +88 -0
  23. package/dist/core/cost-report.js +248 -0
  24. package/dist/core/dispatch.d.ts +42 -2
  25. package/dist/core/dispatch.js +144 -26
  26. package/dist/core/engine-native.js +17 -4
  27. package/dist/core/glob.d.ts +10 -0
  28. package/dist/core/glob.js +33 -0
  29. package/dist/core/host-events.d.ts +24 -1
  30. package/dist/core/host-events.js +341 -1
  31. package/dist/core/host-status.d.ts +19 -2
  32. package/dist/core/host-status.js +47 -30
  33. package/dist/core/issue-bridge.d.ts +60 -0
  34. package/dist/core/issue-bridge.js +189 -0
  35. package/dist/core/issue-dedupe.d.ts +70 -0
  36. package/dist/core/issue-dedupe.js +255 -0
  37. package/dist/core/issue-events.d.ts +42 -0
  38. package/dist/core/issue-events.js +155 -0
  39. package/dist/core/issue-status.d.ts +29 -0
  40. package/dist/core/issue-status.js +241 -0
  41. package/dist/core/issue-tracker-tools.d.ts +64 -0
  42. package/dist/core/issue-tracker-tools.js +850 -0
  43. package/dist/core/model-usage.d.ts +10 -0
  44. package/dist/core/model-usage.js +24 -0
  45. package/dist/core/reconciler.d.ts +8 -4
  46. package/dist/core/reconciler.js +15 -4
  47. package/dist/core/review-host-tools.js +10 -3
  48. package/dist/core/run-pricing.d.ts +61 -0
  49. package/dist/core/run-pricing.js +56 -0
  50. package/dist/core/runner.d.ts +5 -2
  51. package/dist/core/runner.js +178 -27
  52. package/dist/core/scheduler.d.ts +4 -1
  53. package/dist/core/scheduler.js +3 -2
  54. package/dist/core/self-defects.d.ts +80 -0
  55. package/dist/core/self-defects.js +180 -0
  56. package/dist/core/tool-names.js +3 -0
  57. package/dist/core/webhooks.d.ts +9 -1
  58. package/dist/core/webhooks.js +19 -1
  59. package/dist/env.js +6 -1
  60. package/dist/generated/prisma/browser.d.ts +66 -0
  61. package/dist/generated/prisma/client.d.ts +66 -0
  62. package/dist/generated/prisma/commonInputTypes.d.ts +122 -52
  63. package/dist/generated/prisma/enums.d.ts +7 -0
  64. package/dist/generated/prisma/enums.js +6 -0
  65. package/dist/generated/prisma/internal/class.d.ts +99 -0
  66. package/dist/generated/prisma/internal/class.js +4 -4
  67. package/dist/generated/prisma/internal/prismaNamespace.d.ts +826 -1
  68. package/dist/generated/prisma/internal/prismaNamespace.js +135 -2
  69. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +142 -0
  70. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +135 -2
  71. package/dist/generated/prisma/models/Agent.d.ts +389 -1
  72. package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
  73. package/dist/generated/prisma/models/AgentIssueProject.js +1 -0
  74. package/dist/generated/prisma/models/AgentRepository.d.ts +1 -1
  75. package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
  76. package/dist/generated/prisma/models/CodingProxySession.d.ts +73 -1
  77. package/dist/generated/prisma/models/CodingRun.d.ts +130 -1
  78. package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
  79. package/dist/generated/prisma/models/CodingRunServiceStatus.js +1 -0
  80. package/dist/generated/prisma/models/IssueFingerprint.d.ts +1183 -0
  81. package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
  82. package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
  83. package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
  84. package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
  85. package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
  86. package/dist/generated/prisma/models/Run.d.ts +933 -1
  87. package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
  88. package/dist/generated/prisma/models/RunAttribution.js +1 -0
  89. package/dist/generated/prisma/models/RunIssueStatus.d.ts +1199 -0
  90. package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
  91. package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
  92. package/dist/generated/prisma/models/RunModelUsage.js +1 -0
  93. package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
  94. package/dist/generated/prisma/models/WorkItem.js +1 -0
  95. package/dist/generated/prisma/models.d.ts +9 -0
  96. package/dist/help-index.json +355 -16
  97. package/dist/import/neutral-schema.d.ts +16 -16
  98. package/dist/knowledge/check.d.ts +13 -0
  99. package/dist/knowledge/check.js +69 -0
  100. package/dist/knowledge/cli.d.ts +14 -0
  101. package/dist/knowledge/cli.js +67 -0
  102. package/dist/knowledge/concept.d.ts +54 -0
  103. package/dist/knowledge/concept.js +78 -0
  104. package/dist/knowledge/note.d.ts +11 -0
  105. package/dist/knowledge/note.js +39 -0
  106. package/dist/knowledge/relevance.d.ts +11 -0
  107. package/dist/knowledge/relevance.js +14 -0
  108. package/dist/knowledge/span-hash.d.ts +3 -0
  109. package/dist/knowledge/span-hash.js +16 -0
  110. package/dist/mcp/auth/access.d.ts +4 -2
  111. package/dist/mcp/auth/ownership.d.ts +9 -9
  112. package/dist/mcp/auth/resource-server.d.ts +3 -1
  113. package/dist/mcp/auth/resource-server.js +18 -3
  114. package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
  115. package/dist/mcp/auth/self-hosted/session.d.ts +5 -5
  116. package/dist/mcp/context.d.ts +3 -0
  117. package/dist/mcp/host-events/deliveries.d.ts +9 -0
  118. package/dist/mcp/host-events/deliveries.js +17 -0
  119. package/dist/mcp/host-events/github-ingress.d.ts +4 -2
  120. package/dist/mcp/host-events/github-ingress.js +4 -13
  121. package/dist/mcp/host-events/jira-ingress.d.ts +29 -0
  122. package/dist/mcp/host-events/jira-ingress.js +92 -0
  123. package/dist/mcp/index.d.ts +2 -0
  124. package/dist/mcp/index.js +87 -9
  125. package/dist/mcp/server.js +5 -2
  126. package/dist/mcp/tools/agents.js +74 -3
  127. package/dist/mcp/tools/cost-report.d.ts +8 -0
  128. package/dist/mcp/tools/cost-report.js +60 -0
  129. package/dist/mcp/tools/issue-projects.d.ts +2 -0
  130. package/dist/mcp/tools/issue-projects.js +238 -0
  131. package/dist/mcp/tools/model-catalog.d.ts +22 -0
  132. package/dist/mcp/tools/model-catalog.js +423 -0
  133. package/dist/mcp/tools/repositories.js +2 -1
  134. package/dist/mcp/tools/tools.d.ts +2 -2
  135. package/dist/mcp/tools/trigger.js +33 -5
  136. package/dist/mcp/transport/streamable-http.d.ts +5 -0
  137. package/dist/mcp/transport/streamable-http.js +23 -1
  138. package/dist/mcp/webhooks/ingress.d.ts +2 -1
  139. package/dist/mcp/webhooks/ingress.js +9 -2
  140. package/dist/providers/auth/self-hosted.d.ts +8 -1
  141. package/dist/providers/auth/self-hosted.js +39 -2
  142. package/dist/providers/coding-proxy/memory-ledger.d.ts +1 -1
  143. package/dist/providers/coding-proxy/memory-ledger.js +10 -1
  144. package/dist/providers/coding-proxy/metering.d.ts +2 -1
  145. package/dist/providers/coding-proxy/metering.js +13 -2
  146. package/dist/providers/coding-proxy/prisma-ledger.js +59 -6
  147. package/dist/providers/coding-proxy/proxy.d.ts +12 -2
  148. package/dist/providers/coding-proxy/proxy.js +92 -30
  149. package/dist/providers/coding-proxy/types.d.ts +18 -1
  150. package/dist/providers/coding-proxy/types.js +12 -1
  151. package/dist/providers/engine/types.d.ts +19 -0
  152. package/dist/providers/executor/composition.js +9 -1
  153. package/dist/providers/executor/container.d.ts +30 -2
  154. package/dist/providers/executor/container.js +98 -17
  155. package/dist/providers/executor/dbos.d.ts +2 -0
  156. package/dist/providers/executor/dbos.js +7 -5
  157. package/dist/providers/executor/routing.d.ts +6 -0
  158. package/dist/providers/executor/routing.js +5 -0
  159. package/dist/providers/executor/types.d.ts +12 -0
  160. package/dist/providers/issue-tracker/adf.d.ts +31 -0
  161. package/dist/providers/issue-tracker/adf.js +181 -0
  162. package/dist/providers/issue-tracker/index.d.ts +5 -0
  163. package/dist/providers/issue-tracker/index.js +12 -0
  164. package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
  165. package/dist/providers/issue-tracker/jira-client.js +151 -0
  166. package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
  167. package/dist/providers/issue-tracker/jira-events.js +98 -0
  168. package/dist/providers/issue-tracker/jira.d.ts +116 -0
  169. package/dist/providers/issue-tracker/jira.js +502 -0
  170. package/dist/providers/issue-tracker/types.d.ts +269 -0
  171. package/dist/providers/issue-tracker/types.js +16 -0
  172. package/dist/providers/jobs/docker.d.ts +5 -1
  173. package/dist/providers/jobs/docker.js +61 -33
  174. package/dist/providers/jobs/kubernetes.d.ts +3 -0
  175. package/dist/providers/jobs/kubernetes.js +44 -4
  176. package/dist/providers/jobs/service-state.d.ts +22 -0
  177. package/dist/providers/jobs/service-state.js +17 -0
  178. package/dist/providers/llm/anthropic.d.ts +3 -3
  179. package/dist/providers/llm/anthropic.js +3 -9
  180. package/dist/providers/llm/bedrock.d.ts +3 -3
  181. package/dist/providers/llm/bedrock.js +3 -9
  182. package/dist/providers/llm/catalog-lookup.d.ts +10 -0
  183. package/dist/providers/llm/catalog-lookup.js +15 -0
  184. package/dist/providers/llm/catalog-shipped.d.ts +18 -0
  185. package/dist/providers/llm/catalog-shipped.js +197 -0
  186. package/dist/providers/llm/catalog-store.d.ts +58 -0
  187. package/dist/providers/llm/catalog-store.js +138 -0
  188. package/dist/providers/llm/catalog-types.d.ts +66 -0
  189. package/dist/providers/llm/catalog-types.js +64 -0
  190. package/dist/providers/llm/catalog.d.ts +61 -0
  191. package/dist/providers/llm/catalog.js +147 -0
  192. package/dist/providers/llm/claude-provider.d.ts +10 -14
  193. package/dist/providers/llm/claude-provider.js +11 -6
  194. package/dist/providers/llm/index.d.ts +9 -6
  195. package/dist/providers/llm/index.js +8 -5
  196. package/dist/providers/llm/openai.d.ts +14 -5
  197. package/dist/providers/llm/openai.js +24 -14
  198. package/dist/providers/llm/pricing-core.d.ts +5 -3
  199. package/dist/providers/llm/registration.js +8 -12
  200. package/dist/providers/llm/routing.d.ts +18 -17
  201. package/dist/providers/llm/routing.js +40 -24
  202. package/dist/providers/review-host/github-events.js +47 -1
  203. package/dist/providers/review-host/github.js +7 -6
  204. package/dist/providers/review-host/types.d.ts +25 -0
  205. package/dist/providers/vcs/git.js +2 -22
  206. package/dist/providers/vcs/github.d.ts +20 -0
  207. package/dist/providers/vcs/github.js +28 -2
  208. package/dist/providers/vcs/types.d.ts +6 -0
  209. package/dist/quickstart/index.d.ts +8 -0
  210. package/dist/quickstart/index.js +34 -34
  211. package/dist/serve.js +8 -2
  212. package/dist/viewer/api-schema.d.ts +2757 -0
  213. package/dist/viewer/api-schema.js +165 -0
  214. package/dist/viewer/build-schemas.d.ts +2 -0
  215. package/dist/viewer/build-schemas.js +18 -0
  216. package/dist/viewer/event-bus.d.ts +38 -0
  217. package/dist/viewer/event-bus.js +232 -0
  218. package/dist/viewer/graph.d.ts +40 -0
  219. package/dist/viewer/graph.js +243 -0
  220. package/dist/viewer/http.d.ts +30 -0
  221. package/dist/viewer/http.js +133 -0
  222. package/dist/viewer/run-detail.d.ts +4 -0
  223. package/dist/viewer/run-detail.js +61 -0
  224. package/dist/wardby-bin.js +5 -0
  225. package/docs/README.md +10 -0
  226. package/docs/agent-recipes.md +383 -0
  227. package/docs/code-review-agents.md +29 -2
  228. package/docs/coding-agent-setup.md +3 -0
  229. package/docs/coding-worker-isolation.md +39 -5
  230. package/docs/getting-started-gke.md +28 -11
  231. package/docs/getting-started-identity-provider.md +49 -38
  232. package/docs/getting-started.md +14 -0
  233. package/docs/jira-agents.md +649 -0
  234. package/docs/knowledge.md +387 -0
  235. package/docs/models.md +221 -0
  236. package/docs/security-deployment.md +19 -9
  237. package/docs/viewer-api.md +142 -0
  238. package/help/admin-viewer.md +39 -0
  239. package/help/agent-recipes.md +173 -0
  240. package/help/architecture-agent.md +189 -0
  241. package/help/builder-agent.md +80 -0
  242. package/help/code-review-agents.md +6 -0
  243. package/help/cost-attribution.md +67 -0
  244. package/help/creating-agents.md +22 -0
  245. package/help/deploy-gke.md +6 -0
  246. package/help/errors/model-unavailable.md +63 -0
  247. package/help/getting-started.md +1 -0
  248. package/help/github.md +18 -0
  249. package/help/identity-and-access.md +8 -3
  250. package/help/jira.md +135 -0
  251. package/help/knowledge.md +47 -0
  252. package/help/models.md +90 -0
  253. package/help/operating-agents.md +7 -1
  254. package/help/troubleshooting/budgets.md +6 -0
  255. package/package.json +5 -2
  256. package/prisma/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
  257. package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
  258. package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
  259. package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
  260. package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
  261. package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
  262. package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
  263. package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
  264. package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
  265. package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
  266. package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
  267. package/prisma/schema.prisma +258 -2
  268. package/dist/mcp/tools/models.d.ts +0 -8
  269. package/dist/mcp/tools/models.js +0 -15
  270. package/dist/providers/llm/pricing-anthropic.d.ts +0 -14
  271. package/dist/providers/llm/pricing-anthropic.js +0 -48
  272. package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -20
  273. package/dist/providers/llm/pricing-bedrock-claude.js +0 -46
  274. package/dist/providers/llm/pricing.d.ts +0 -30
  275. package/dist/providers/llm/pricing.js +0 -74
@@ -8,15 +8,15 @@ export declare const CiphertextSchema: z.ZodObject<{
8
8
  tag: z.ZodString;
9
9
  }, "strip", z.ZodTypeAny, {
10
10
  tag: string;
11
- v: number;
12
11
  alg: string;
12
+ v: number;
13
13
  epk: string;
14
14
  nonce: string;
15
15
  ct: string;
16
16
  }, {
17
17
  tag: string;
18
- v: number;
19
18
  alg: string;
19
+ v: number;
20
20
  epk: string;
21
21
  nonce: string;
22
22
  ct: string;
@@ -98,20 +98,20 @@ export declare const NeutralAgentSchema: z.ZodObject<{
98
98
  slug: z.ZodOptional<z.ZodDefault<z.ZodNullable<z.ZodString>>>;
99
99
  emailAllowlist: z.ZodOptional<z.ZodDefault<z.ZodNullable<z.ZodUnknown>>>;
100
100
  }, "strip", z.ZodTypeAny, {
101
- slug?: string | null | undefined;
102
101
  description?: string | null | undefined;
102
+ slug?: string | null | undefined;
103
103
  emailAllowlist?: unknown;
104
104
  }, {
105
- slug?: string | null | undefined;
106
105
  description?: string | null | undefined;
106
+ slug?: string | null | undefined;
107
107
  emailAllowlist?: unknown;
108
108
  }>>;
109
109
  }, "strip", z.ZodTypeAny, {
110
110
  name: string;
111
111
  model: string;
112
112
  budgetUsd: string;
113
- kind: "native";
114
113
  provider: "openai" | "bedrock";
114
+ kind: "native";
115
115
  systemPrompt: string;
116
116
  schedule: string | null;
117
117
  timezone: string;
@@ -121,8 +121,8 @@ export declare const NeutralAgentSchema: z.ZodObject<{
121
121
  region: string | null;
122
122
  ownerEmail: string | null;
123
123
  unmodeled: {
124
- slug?: string | null | undefined;
125
124
  description?: string | null | undefined;
125
+ slug?: string | null | undefined;
126
126
  emailAllowlist?: unknown;
127
127
  };
128
128
  }, {
@@ -140,8 +140,8 @@ export declare const NeutralAgentSchema: z.ZodObject<{
140
140
  region?: string | null | undefined;
141
141
  ownerEmail?: string | null | undefined;
142
142
  unmodeled?: {
143
- slug?: string | null | undefined;
144
143
  description?: string | null | undefined;
144
+ slug?: string | null | undefined;
145
145
  emailAllowlist?: unknown;
146
146
  } | undefined;
147
147
  }>;
@@ -208,15 +208,15 @@ export declare const NeutralSecretSchema: z.ZodObject<{
208
208
  tag: z.ZodString;
209
209
  }, "strip", z.ZodTypeAny, {
210
210
  tag: string;
211
- v: number;
212
211
  alg: string;
212
+ v: number;
213
213
  epk: string;
214
214
  nonce: string;
215
215
  ct: string;
216
216
  }, {
217
217
  tag: string;
218
- v: number;
219
218
  alg: string;
219
+ v: number;
220
220
  epk: string;
221
221
  nonce: string;
222
222
  ct: string;
@@ -227,23 +227,23 @@ export declare const NeutralSecretSchema: z.ZodObject<{
227
227
  ownerEmail: string | null;
228
228
  ciphertext?: {
229
229
  tag: string;
230
- v: number;
231
230
  alg: string;
231
+ v: number;
232
232
  epk: string;
233
233
  nonce: string;
234
234
  ct: string;
235
235
  } | undefined;
236
236
  }, {
237
237
  name: string;
238
+ description?: string | null | undefined;
238
239
  ciphertext?: {
239
240
  tag: string;
240
- v: number;
241
241
  alg: string;
242
+ v: number;
242
243
  epk: string;
243
244
  nonce: string;
244
245
  ct: string;
245
246
  } | undefined;
246
- description?: string | null | undefined;
247
247
  ownerEmail?: string | null | undefined;
248
248
  }>;
249
249
  export type NeutralSecret = z.infer<typeof NeutralSecretSchema>;
@@ -331,11 +331,11 @@ export declare const NeutralWebhookSchema: z.ZodObject<{
331
331
  agentName: z.ZodString;
332
332
  enabled: z.ZodBoolean;
333
333
  }, "strip", z.ZodTypeAny, {
334
- agentName: string;
335
334
  enabled: boolean;
336
- }, {
337
335
  agentName: string;
336
+ }, {
338
337
  enabled: boolean;
338
+ agentName: string;
339
339
  }>;
340
340
  export type NeutralWebhook = z.infer<typeof NeutralWebhookSchema>;
341
341
  export declare const NeutralBudgetSchema: z.ZodObject<{
@@ -349,8 +349,8 @@ export declare const NeutralBudgetSchema: z.ZodObject<{
349
349
  attachedAgentNames: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
350
350
  }, "strip", z.ZodTypeAny, {
351
351
  name: string;
352
- period: "daily" | "weekly" | "monthly";
353
352
  enabled: boolean;
353
+ period: "daily" | "weekly" | "monthly";
354
354
  ownerEmail: string | null;
355
355
  attachedAgentNames: string[];
356
356
  alertThreshold: string;
@@ -358,8 +358,8 @@ export declare const NeutralBudgetSchema: z.ZodObject<{
358
358
  alertEmails: string[];
359
359
  }, {
360
360
  name: string;
361
- period: "daily" | "weekly" | "monthly";
362
361
  enabled: boolean;
362
+ period: "daily" | "weekly" | "monthly";
363
363
  alertThreshold: string;
364
364
  blockThreshold: string;
365
365
  ownerEmail?: string | null | undefined;
@@ -0,0 +1,13 @@
1
+ export type CheckSeverity = "error" | "warning";
2
+ export type CheckCode = "concept_invalid" | "concept_secret" | "index_missing" | "index_link_broken" | "concept_not_indexed" | "citation_unverifiable" | "citation_stale";
3
+ export interface CheckIssue {
4
+ severity: CheckSeverity;
5
+ code: CheckCode;
6
+ file: string;
7
+ message: string;
8
+ }
9
+ export interface BundleFiles {
10
+ files: Map<string, string>;
11
+ }
12
+ export type RepoFileReader = (repoRelativePath: string) => string | null;
13
+ export declare function checkBundle(bundle: BundleFiles, readRepoFile: RepoFileReader): CheckIssue[];
@@ -0,0 +1,69 @@
1
+ import { redactTokenShapedValues } from "../coding/protocol.js";
2
+ import { parseConcept, RESERVED_BUNDLE_FILES } from "./concept.js";
3
+ import { spanHash } from "./span-hash.js";
4
+ const LINK = /\]\(([^)#\s]+\.md)(?:#[^)\s]*)?\)/g;
5
+ const URL_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
6
+ const baseName = (path) => path.split("/").pop() ?? path;
7
+ function dirOf(path) {
8
+ const cut = path.lastIndexOf("/");
9
+ return cut < 0 ? "" : path.slice(0, cut + 1);
10
+ }
11
+ function normalize(path) {
12
+ const parts = [];
13
+ for (const part of path.split("/")) {
14
+ if (part === "..")
15
+ parts.pop();
16
+ else if (part !== "." && part !== "")
17
+ parts.push(part);
18
+ }
19
+ return parts.join("/");
20
+ }
21
+ export function checkBundle(bundle, readRepoFile) {
22
+ const issues = [];
23
+ const add = (severity, code, file, message) => issues.push({ severity, code, file, message });
24
+ if (!bundle.files.has("index.md"))
25
+ add("error", "index_missing", "index.md", "the bundle has no root index.md");
26
+ const linked = new Set();
27
+ for (const [path, text] of bundle.files) {
28
+ if (redactTokenShapedValues(text) !== text)
29
+ add("error", "concept_secret", path, "contains a secret-shaped value");
30
+ if (baseName(path) !== "index.md")
31
+ continue;
32
+ for (const match of text.matchAll(LINK)) {
33
+ if (URL_SCHEME.test(match[1]))
34
+ continue;
35
+ const target = normalize(match[1].startsWith("/") ? match[1].slice(1) : dirOf(path) + match[1]);
36
+ linked.add(target);
37
+ if (!bundle.files.has(target))
38
+ add("error", "index_link_broken", path, `links to missing ${target}`);
39
+ }
40
+ }
41
+ const fileCache = new Map();
42
+ const read = (p) => {
43
+ if (!fileCache.has(p))
44
+ fileCache.set(p, readRepoFile(p));
45
+ return fileCache.get(p) ?? null;
46
+ };
47
+ for (const [path, text] of bundle.files) {
48
+ const base = path.split("/").pop() ?? path;
49
+ if (!path.endsWith(".md") || RESERVED_BUNDLE_FILES.has(base))
50
+ continue;
51
+ const parsed = parseConcept(path, text);
52
+ if (!parsed.ok) {
53
+ add("error", "concept_invalid", path, parsed.error);
54
+ continue;
55
+ }
56
+ if (!linked.has(path))
57
+ add("warning", "concept_not_indexed", path, "not linked from an index.md");
58
+ for (const citation of parsed.concept.citations) {
59
+ const where = `${citation.path}${citation.lines ? `#L${citation.lines[0]}-L${citation.lines[1]}` : ""}`;
60
+ const source = read(citation.path);
61
+ const actual = source === null ? null : spanHash(source, citation.lines);
62
+ if (actual === null)
63
+ add("warning", "citation_unverifiable", path, `${where} is missing or out of range`);
64
+ else if (actual !== citation.spanHash)
65
+ add("warning", "citation_stale", path, `${where} changed since it was cited`);
66
+ }
67
+ }
68
+ return issues;
69
+ }
@@ -0,0 +1,14 @@
1
+ export declare function parseKnowledgeArgs(args: string[]): {
2
+ dir: string;
3
+ root: string;
4
+ strict: boolean;
5
+ json: boolean;
6
+ };
7
+ /**
8
+ * Every `.md` file under `dir`, keyed by bundle-relative POSIX path. Entry
9
+ * types come from the directory listing itself (no separate stat, so nothing
10
+ * can change between the check and the read), and symbolic links are skipped:
11
+ * a bundle is plain files, and following links could loop or leave the bundle.
12
+ */
13
+ export declare function loadBundle(dir: string): Map<string, string>;
14
+ export declare function knowledgeCommand(args: string[]): Promise<void>;
@@ -0,0 +1,67 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import { join, relative, resolve, sep } from "node:path";
3
+ import { parseArgs } from "node:util";
4
+ import { checkBundle } from "./check.js";
5
+ import { DEFAULT_KNOWLEDGE_BUNDLE_PATH } from "./concept.js";
6
+ const USAGE = "usage: wardby knowledge check [dir] [--root <repo root>] [--strict] [--json]";
7
+ export function parseKnowledgeArgs(args) {
8
+ const [sub, ...rest] = args;
9
+ if (sub !== "check")
10
+ throw new Error(USAGE);
11
+ const { values, positionals } = parseArgs({
12
+ args: rest,
13
+ allowPositionals: true,
14
+ options: { root: { type: "string" }, strict: { type: "boolean" }, json: { type: "boolean" } },
15
+ });
16
+ if (positionals.length > 1)
17
+ throw new Error(USAGE);
18
+ return {
19
+ dir: positionals[0] ?? DEFAULT_KNOWLEDGE_BUNDLE_PATH,
20
+ root: values.root ?? ".",
21
+ strict: values.strict ?? false,
22
+ json: values.json ?? false,
23
+ };
24
+ }
25
+ /**
26
+ * Every `.md` file under `dir`, keyed by bundle-relative POSIX path. Entry
27
+ * types come from the directory listing itself (no separate stat, so nothing
28
+ * can change between the check and the read), and symbolic links are skipped:
29
+ * a bundle is plain files, and following links could loop or leave the bundle.
30
+ */
31
+ export function loadBundle(dir) {
32
+ const files = new Map();
33
+ const walk = (current) => {
34
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
35
+ const full = join(current, entry.name);
36
+ if (entry.isDirectory())
37
+ walk(full);
38
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
39
+ files.set(relative(dir, full).split(sep).join("/"), readFileSync(full, "utf8"));
40
+ }
41
+ }
42
+ };
43
+ walk(dir);
44
+ return files;
45
+ }
46
+ export async function knowledgeCommand(args) {
47
+ const options = parseKnowledgeArgs(args);
48
+ const root = resolve(options.root);
49
+ const files = loadBundle(resolve(root, options.dir));
50
+ const issues = checkBundle({ files }, (path) => {
51
+ try {
52
+ return readFileSync(join(root, path), "utf8");
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ });
58
+ if (options.json)
59
+ process.stdout.write(`${JSON.stringify({ issues }, null, 2)}\n`);
60
+ else {
61
+ for (const issue of issues)
62
+ process.stdout.write(`${issue.severity} ${issue.code} ${options.dir}/${issue.file}: ${issue.message}\n`);
63
+ process.stdout.write(`${files.size} files checked, ${issues.filter((i) => i.severity === "error").length} errors, ${issues.filter((i) => i.severity === "warning").length} warnings\n`);
64
+ }
65
+ if (issues.some((i) => i.severity === "error" || (options.strict && i.severity === "warning")))
66
+ process.exitCode = 1;
67
+ }
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ export declare const DEFAULT_KNOWLEDGE_BUNDLE_PATH = "docs/knowledge";
3
+ export declare const RESERVED_BUNDLE_FILES: Set<string>;
4
+ declare const CitationSchema: z.ZodObject<{
5
+ id: z.ZodOptional<z.ZodString>;
6
+ repo: z.ZodString;
7
+ path: z.ZodEffects<z.ZodString, string, string>;
8
+ lines: z.ZodOptional<z.ZodEffects<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, [number, number], [number, number]>>;
9
+ symbol: z.ZodOptional<z.ZodString>;
10
+ sha: z.ZodString;
11
+ spanHash: z.ZodString;
12
+ }, "strip", z.ZodTypeAny, {
13
+ path: string;
14
+ repo: string;
15
+ sha: string;
16
+ spanHash: string;
17
+ symbol?: string | undefined;
18
+ id?: string | undefined;
19
+ lines?: [number, number] | undefined;
20
+ }, {
21
+ path: string;
22
+ repo: string;
23
+ sha: string;
24
+ spanHash: string;
25
+ symbol?: string | undefined;
26
+ id?: string | undefined;
27
+ lines?: [number, number] | undefined;
28
+ }>;
29
+ export type KnowledgeCitation = z.infer<typeof CitationSchema>;
30
+ export interface ParsedConcept {
31
+ path: string;
32
+ type: string;
33
+ title?: string;
34
+ description?: string;
35
+ status: "draft" | "stable" | "deprecated";
36
+ roles: string[];
37
+ affects: string[];
38
+ citations: KnowledgeCitation[];
39
+ body: string;
40
+ frontMatter: Record<string, unknown>;
41
+ }
42
+ export type ParseResult = {
43
+ ok: true;
44
+ concept: ParsedConcept;
45
+ } | {
46
+ ok: false;
47
+ error: string;
48
+ };
49
+ export declare function splitFrontMatter(text: string): {
50
+ yaml: string;
51
+ body: string;
52
+ } | null;
53
+ export declare function parseConcept(path: string, text: string): ParseResult;
54
+ export {};
@@ -0,0 +1,78 @@
1
+ import { parse as parseYaml } from "yaml";
2
+ import { z } from "zod";
3
+ export const DEFAULT_KNOWLEDGE_BUNDLE_PATH = "docs/knowledge";
4
+ export const RESERVED_BUNDLE_FILES = new Set(["index.md", "log.md"]);
5
+ const CitationSchema = z.object({
6
+ id: z.string().min(1).optional(),
7
+ repo: z.string().min(1),
8
+ path: z
9
+ .string()
10
+ .min(1)
11
+ .refine((p) => !p.startsWith("/") && !p.split("/").includes(".."), "path must be repo-relative"),
12
+ lines: z
13
+ .tuple([z.number().int().min(1), z.number().int().min(1)])
14
+ .refine(([a, b]) => b >= a, "lines must be [start, end] with end >= start")
15
+ .optional(),
16
+ symbol: z.string().min(1).optional(),
17
+ sha: z.string().regex(/^[0-9a-f]{40}$/, "sha must be a full 40-hex commit"),
18
+ spanHash: z.string().regex(/^sha256:[0-9a-f]{64}$/, "spanHash must be sha256:<64 hex>"),
19
+ });
20
+ const WardbyBlockSchema = z
21
+ .object({
22
+ schema: z.literal(1),
23
+ roles: z.array(z.enum(["builder", "reviewer", "planner"])).default([]),
24
+ affects: z.array(z.string().min(1)).default([]),
25
+ citations: z.array(CitationSchema).default([]),
26
+ supersedes: z.string().nullable().optional(),
27
+ confidence: z.enum(["low", "medium", "high"]).optional(),
28
+ })
29
+ .passthrough();
30
+ const FrontMatterSchema = z
31
+ .object({
32
+ type: z.string().trim().min(1),
33
+ title: z.string().optional(),
34
+ description: z.string().optional(),
35
+ status: z.enum(["draft", "stable", "deprecated"]).default("stable"),
36
+ wardby: WardbyBlockSchema.optional(),
37
+ })
38
+ .passthrough();
39
+ export function splitFrontMatter(text) {
40
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(text);
41
+ return match ? { yaml: match[1], body: match[2] } : null;
42
+ }
43
+ export function parseConcept(path, text) {
44
+ const base = path.split("/").pop() ?? path;
45
+ if (RESERVED_BUNDLE_FILES.has(base))
46
+ return { ok: false, error: `${base} is reserved and cannot be a concept` };
47
+ const split = splitFrontMatter(text);
48
+ if (!split)
49
+ return { ok: false, error: "missing YAML front-matter (--- ... ---)" };
50
+ let raw;
51
+ try {
52
+ raw = parseYaml(split.yaml);
53
+ }
54
+ catch (error) {
55
+ return { ok: false, error: `invalid YAML: ${error instanceof Error ? error.message : String(error)}` };
56
+ }
57
+ const parsed = FrontMatterSchema.safeParse(raw);
58
+ if (!parsed.success) {
59
+ const issue = parsed.error.issues[0];
60
+ return { ok: false, error: `${issue.path.join(".") || "front-matter"}: ${issue.message}` };
61
+ }
62
+ const fm = parsed.data;
63
+ return {
64
+ ok: true,
65
+ concept: {
66
+ path,
67
+ type: fm.type,
68
+ title: fm.title,
69
+ description: fm.description,
70
+ status: fm.status,
71
+ roles: fm.wardby?.roles ?? [],
72
+ affects: fm.wardby?.affects ?? [],
73
+ citations: fm.wardby?.citations ?? [],
74
+ body: split.body,
75
+ frontMatter: raw,
76
+ },
77
+ };
78
+ }
@@ -0,0 +1,11 @@
1
+ import type { FittedSection } from "../coding/protocol.js";
2
+ export declare const KNOWLEDGE_NOTE_MAX_BYTES: number;
3
+ export declare const KNOWLEDGE_INDEX_READ_MAX_BYTES: number;
4
+ export interface KnowledgeNoteInput {
5
+ bundlePath: string;
6
+ indexText: string;
7
+ }
8
+ /** The note, cut at a line boundary to fit maxBytes, or undefined when not even the header fits. */
9
+ export declare function renderKnowledgeNote(input: KnowledgeNoteInput, maxBytes: number): string | undefined;
10
+ /** The knowledge note as a composeCodingTask section, capped at KNOWLEDGE_NOTE_MAX_BYTES. */
11
+ export declare function knowledgeSection(input: KnowledgeNoteInput): FittedSection;
@@ -0,0 +1,39 @@
1
+ import { splitFrontMatter } from "./concept.js";
2
+ export const KNOWLEDGE_NOTE_MAX_BYTES = 8 * 1024;
3
+ export const KNOWLEDGE_INDEX_READ_MAX_BYTES = 64 * 1024;
4
+ const END_MARKER = "[end of architecture knowledge]";
5
+ const bytes = (text) => Buffer.byteLength(text, "utf8");
6
+ function header(bundlePath) {
7
+ return [
8
+ "Architecture knowledge for this repository (recalled context, not authority;",
9
+ "the repository's own instructions such as AGENTS.md win on conflict). Concept",
10
+ `files live in ${bundlePath}/. Before designing or changing an area, read the`,
11
+ "concepts that cover the files you will touch. If your change alters behavior a",
12
+ "concept describes, update that concept's prose in the same change; leave its",
13
+ "`wardby:` block for the architecture agent to re-anchor.",
14
+ "",
15
+ `${bundlePath}/index.md:`,
16
+ ].join("\n");
17
+ }
18
+ /** The note, cut at a line boundary to fit maxBytes, or undefined when not even the header fits. */
19
+ export function renderKnowledgeNote(input, maxBytes) {
20
+ const top = header(input.bundlePath);
21
+ const body = (splitFrontMatter(input.indexText)?.body ?? input.indexText).trim();
22
+ const full = `${top}\n${body}\n${END_MARKER}`;
23
+ if (bytes(full) <= maxBytes)
24
+ return full;
25
+ const marker = `[index truncated — list ${input.bundlePath}/ for the rest]`;
26
+ if (bytes(`${top}\n${marker}\n${END_MARKER}`) > maxBytes)
27
+ return undefined;
28
+ const kept = [];
29
+ for (const line of body.split("\n")) {
30
+ if (bytes(`${top}\n${[...kept, line, marker, END_MARKER].join("\n")}`) > maxBytes)
31
+ break;
32
+ kept.push(line);
33
+ }
34
+ return `${top}\n${[...kept, marker, END_MARKER].join("\n")}`;
35
+ }
36
+ /** The knowledge note as a composeCodingTask section, capped at KNOWLEDGE_NOTE_MAX_BYTES. */
37
+ export function knowledgeSection(input) {
38
+ return { render: (maxBytes) => renderKnowledgeNote(input, Math.min(maxBytes, KNOWLEDGE_NOTE_MAX_BYTES)) };
39
+ }
@@ -0,0 +1,11 @@
1
+ import type { ParsedConcept } from "./concept.js";
2
+ export interface DriftScope {
3
+ concepts: string[];
4
+ reason: "changed_files" | "bundle_edited" | "unknown_changes";
5
+ }
6
+ export declare function driftScope(input: {
7
+ changedPaths: string[];
8
+ changedPathsComplete: boolean;
9
+ bundlePath: string;
10
+ concepts: ParsedConcept[];
11
+ }): DriftScope | null;
@@ -0,0 +1,14 @@
1
+ import { matchesGlob } from "../core/glob.js";
2
+ export function driftScope(input) {
3
+ const all = input.concepts.map((c) => c.path).sort();
4
+ if (!input.changedPathsComplete)
5
+ return all.length ? { concepts: all, reason: "unknown_changes" } : null;
6
+ const prefix = `${input.bundlePath}/`;
7
+ const bundleEdited = input.changedPaths.some((p) => p.startsWith(prefix));
8
+ const selected = input.concepts.filter((concept) => input.changedPaths.some((changed) => changed === prefix + concept.path ||
9
+ concept.citations.some((c) => c.path === changed) ||
10
+ concept.affects.some((glob) => matchesGlob(changed, glob))));
11
+ if (!selected.length)
12
+ return null;
13
+ return { concepts: selected.map((c) => c.path).sort(), reason: bundleEdited ? "bundle_edited" : "changed_files" };
14
+ }
@@ -0,0 +1,3 @@
1
+ /** sha256 of lines A..B (1-based, inclusive), each followed by "\n"
2
+ * (equals `sed -n 'A,Bp' FILE | sha256sum` when the last cited line ends with a newline). */
3
+ export declare function spanHash(fileText: string, lines?: [number, number]): string | null;
@@ -0,0 +1,16 @@
1
+ import { createHash } from "node:crypto";
2
+ /** sha256 of lines A..B (1-based, inclusive), each followed by "\n"
3
+ * (equals `sed -n 'A,Bp' FILE | sha256sum` when the last cited line ends with a newline). */
4
+ export function spanHash(fileText, lines) {
5
+ const digest = (text) => `sha256:${createHash("sha256").update(text, "utf8").digest("hex")}`;
6
+ if (!lines)
7
+ return digest(fileText);
8
+ const [start, end] = lines;
9
+ const all = fileText.endsWith("\n") ? fileText.slice(0, -1).split("\n") : fileText.split("\n");
10
+ if (start < 1 || end < start || end > all.length)
11
+ return null;
12
+ return digest(all
13
+ .slice(start - 1, end)
14
+ .map((line) => `${line}\n`)
15
+ .join(""));
16
+ }
@@ -40,17 +40,19 @@ export declare function requireAgentAccess(ctx: McpRequestContext, id: string, r
40
40
  name: string;
41
41
  model: string;
42
42
  budgetUsd: import("@prisma/client-runtime-utils").Decimal;
43
+ createdAt: Date;
44
+ updatedAt: Date;
43
45
  kind: import("#prisma").AgentKind;
44
46
  id: string;
45
47
  ownerId: string | null;
46
- createdAt: Date;
47
- updatedAt: Date;
48
48
  systemPrompt: string;
49
49
  schedule: string | null;
50
50
  timezone: string;
51
51
  scheduleEnabled: boolean;
52
52
  lastScheduledAt: Date | null;
53
53
  maxTurns: number;
54
+ defectProjectKey: string | null;
55
+ defectIssueType: string | null;
54
56
  budgetGroupId: string | null;
55
57
  memoryEnabled: boolean;
56
58
  effort: string | null;
@@ -26,34 +26,34 @@ export declare function visibleToPrincipal(principalId: string): {
26
26
  };
27
27
  export declare function requireOwnedBudgetGroup(db: PrismaClient, id: string, principalId: string): Promise<{
28
28
  name: string;
29
+ createdAt: Date;
30
+ updatedAt: Date;
29
31
  id: string;
30
32
  ownerId: string | null;
31
33
  dailyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
32
34
  weeklyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
33
35
  monthlyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
34
36
  warnThresholdRatio: import("@prisma/client-runtime-utils").Decimal;
35
- createdAt: Date;
36
- updatedAt: Date;
37
37
  }>;
38
38
  export declare function requireReadableBudgetGroup(db: Pick<PrismaClient, "budgetGroup">, id: string, principalId: string): Promise<{
39
39
  name: string;
40
+ createdAt: Date;
41
+ updatedAt: Date;
40
42
  id: string;
41
43
  ownerId: string | null;
42
44
  dailyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
43
45
  weeklyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
44
46
  monthlyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
45
47
  warnThresholdRatio: import("@prisma/client-runtime-utils").Decimal;
46
- createdAt: Date;
47
- updatedAt: Date;
48
48
  }>;
49
49
  export declare function requireOwnedSecret(db: PrismaClient, id: string, principalId: string): Promise<void>;
50
50
  export declare function requireOwnedWebhook(db: PrismaClient, id: string, principalId: string): Promise<void>;
51
51
  export declare function requireOwnedDatastore(db: PrismaClient, id: string, principalId: string): Promise<{
52
52
  name: string;
53
- id: string;
54
- ownerId: string | null;
55
53
  createdAt: Date;
56
54
  updatedAt: Date;
55
+ id: string;
56
+ ownerId: string | null;
57
57
  }>;
58
58
  export declare function requireOwnedTool(db: Pick<PrismaClient, "tool">, id: string, principalId: string): Promise<Tool>;
59
59
  /**
@@ -74,12 +74,12 @@ export declare function assertStrictlyOwnedTool(tool: Tool | null, id: string, p
74
74
  export declare function requireOwnedTask(db: PrismaClient, taskId: string, principalId: string): Promise<{
75
75
  status: import("#prisma").TaskStatus;
76
76
  runId: string | null;
77
- kind: import("#prisma").TaskKind;
78
77
  error: import("@prisma/client/runtime/client").JsonValue | null;
79
- result: import("@prisma/client/runtime/client").JsonValue | null;
80
- id: string;
81
78
  createdAt: Date;
82
79
  updatedAt: Date;
80
+ kind: import("#prisma").TaskKind;
81
+ result: import("@prisma/client/runtime/client").JsonValue | null;
82
+ id: string;
83
83
  principalId: string | null;
84
84
  inputRequests: import("@prisma/client/runtime/client").JsonValue | null;
85
85
  inputResponses: import("@prisma/client/runtime/client").JsonValue | null;
@@ -16,7 +16,9 @@ export declare const SCOPES_SUPPORTED: string[];
16
16
  * agents:admin reassigns ANY agent's owner (make_owner) and sets a BYO
17
17
  * workerImageRef; packages:approve widens coding agents' package allowlists;
18
18
  * services:manage changes the coding-run service catalog, which decides what
19
- * runs next to every coding run that names an entry.
19
+ * runs next to every coding run that names an entry; admin:view reads every
20
+ * owner's runs (the admin viewer API); models:admin changes the model catalog,
21
+ * which prices every principal's runs.
20
22
  * A token scope only DELEGATES — it never authorizes on its own:
21
23
  * requireScope/requireAnyScope honour one only when one of the caller's
22
24
  * roles (McpRequestContext.roles, resolved live per request) grants it.