@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.
- package/.env.example +22 -3
- package/README.md +11 -9
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +1 -0
- package/dist/cli.js +68 -10
- package/dist/coding/base-commit.d.ts +6 -0
- package/dist/coding/base-commit.js +12 -0
- package/dist/coding/protocol.d.ts +17 -1
- package/dist/coding/protocol.js +17 -6
- package/dist/coding/provider.d.ts +6 -1
- package/dist/coding/provider.js +16 -9
- package/dist/config/providers.d.ts +25 -0
- package/dist/config/providers.js +71 -0
- package/dist/core/attribution.d.ts +101 -0
- package/dist/core/attribution.js +208 -0
- package/dist/core/budget-groups.d.ts +17 -10
- package/dist/core/budget-groups.js +15 -12
- package/dist/core/coding-queue.d.ts +3 -0
- package/dist/core/coding-queue.js +6 -2
- package/dist/core/coding-service-status.d.ts +11 -0
- package/dist/core/coding-service-status.js +17 -0
- package/dist/core/cost-report.d.ts +88 -0
- package/dist/core/cost-report.js +248 -0
- package/dist/core/dispatch.d.ts +42 -2
- package/dist/core/dispatch.js +144 -26
- package/dist/core/engine-native.js +17 -4
- package/dist/core/glob.d.ts +10 -0
- package/dist/core/glob.js +33 -0
- package/dist/core/host-events.d.ts +24 -1
- package/dist/core/host-events.js +341 -1
- package/dist/core/host-status.d.ts +19 -2
- package/dist/core/host-status.js +47 -30
- package/dist/core/issue-bridge.d.ts +60 -0
- package/dist/core/issue-bridge.js +189 -0
- package/dist/core/issue-dedupe.d.ts +70 -0
- package/dist/core/issue-dedupe.js +255 -0
- package/dist/core/issue-events.d.ts +42 -0
- package/dist/core/issue-events.js +155 -0
- package/dist/core/issue-status.d.ts +29 -0
- package/dist/core/issue-status.js +241 -0
- package/dist/core/issue-tracker-tools.d.ts +64 -0
- package/dist/core/issue-tracker-tools.js +850 -0
- package/dist/core/model-usage.d.ts +10 -0
- package/dist/core/model-usage.js +24 -0
- package/dist/core/reconciler.d.ts +8 -4
- package/dist/core/reconciler.js +15 -4
- package/dist/core/review-host-tools.js +10 -3
- package/dist/core/run-pricing.d.ts +61 -0
- package/dist/core/run-pricing.js +56 -0
- package/dist/core/runner.d.ts +5 -2
- package/dist/core/runner.js +178 -27
- package/dist/core/scheduler.d.ts +4 -1
- package/dist/core/scheduler.js +3 -2
- package/dist/core/self-defects.d.ts +80 -0
- package/dist/core/self-defects.js +180 -0
- package/dist/core/tool-names.js +3 -0
- package/dist/core/webhooks.d.ts +9 -1
- package/dist/core/webhooks.js +19 -1
- package/dist/env.js +6 -1
- package/dist/generated/prisma/browser.d.ts +66 -0
- package/dist/generated/prisma/client.d.ts +66 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +122 -52
- package/dist/generated/prisma/enums.d.ts +7 -0
- package/dist/generated/prisma/enums.js +6 -0
- package/dist/generated/prisma/internal/class.d.ts +99 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +826 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +135 -2
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +142 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +135 -2
- package/dist/generated/prisma/models/Agent.d.ts +389 -1
- package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
- package/dist/generated/prisma/models/AgentIssueProject.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +1 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingProxySession.d.ts +73 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +130 -1
- package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
- package/dist/generated/prisma/models/CodingRunServiceStatus.js +1 -0
- package/dist/generated/prisma/models/IssueFingerprint.d.ts +1183 -0
- package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
- package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +933 -1
- package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
- package/dist/generated/prisma/models/RunAttribution.js +1 -0
- package/dist/generated/prisma/models/RunIssueStatus.d.ts +1199 -0
- package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
- package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
- package/dist/generated/prisma/models/RunModelUsage.js +1 -0
- package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
- package/dist/generated/prisma/models/WorkItem.js +1 -0
- package/dist/generated/prisma/models.d.ts +9 -0
- package/dist/help-index.json +355 -16
- package/dist/import/neutral-schema.d.ts +16 -16
- package/dist/knowledge/check.d.ts +13 -0
- package/dist/knowledge/check.js +69 -0
- package/dist/knowledge/cli.d.ts +14 -0
- package/dist/knowledge/cli.js +67 -0
- package/dist/knowledge/concept.d.ts +54 -0
- package/dist/knowledge/concept.js +78 -0
- package/dist/knowledge/note.d.ts +11 -0
- package/dist/knowledge/note.js +39 -0
- package/dist/knowledge/relevance.d.ts +11 -0
- package/dist/knowledge/relevance.js +14 -0
- package/dist/knowledge/span-hash.d.ts +3 -0
- package/dist/knowledge/span-hash.js +16 -0
- package/dist/mcp/auth/access.d.ts +4 -2
- package/dist/mcp/auth/ownership.d.ts +9 -9
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +18 -3
- package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
- package/dist/mcp/auth/self-hosted/session.d.ts +5 -5
- package/dist/mcp/context.d.ts +3 -0
- package/dist/mcp/host-events/deliveries.d.ts +9 -0
- package/dist/mcp/host-events/deliveries.js +17 -0
- package/dist/mcp/host-events/github-ingress.d.ts +4 -2
- package/dist/mcp/host-events/github-ingress.js +4 -13
- package/dist/mcp/host-events/jira-ingress.d.ts +29 -0
- package/dist/mcp/host-events/jira-ingress.js +92 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +87 -9
- package/dist/mcp/server.js +5 -2
- package/dist/mcp/tools/agents.js +74 -3
- package/dist/mcp/tools/cost-report.d.ts +8 -0
- package/dist/mcp/tools/cost-report.js +60 -0
- package/dist/mcp/tools/issue-projects.d.ts +2 -0
- package/dist/mcp/tools/issue-projects.js +238 -0
- package/dist/mcp/tools/model-catalog.d.ts +22 -0
- package/dist/mcp/tools/model-catalog.js +423 -0
- package/dist/mcp/tools/repositories.js +2 -1
- package/dist/mcp/tools/tools.d.ts +2 -2
- package/dist/mcp/tools/trigger.js +33 -5
- package/dist/mcp/transport/streamable-http.d.ts +5 -0
- package/dist/mcp/transport/streamable-http.js +23 -1
- package/dist/mcp/webhooks/ingress.d.ts +2 -1
- package/dist/mcp/webhooks/ingress.js +9 -2
- package/dist/providers/auth/self-hosted.d.ts +8 -1
- package/dist/providers/auth/self-hosted.js +39 -2
- package/dist/providers/coding-proxy/memory-ledger.d.ts +1 -1
- package/dist/providers/coding-proxy/memory-ledger.js +10 -1
- package/dist/providers/coding-proxy/metering.d.ts +2 -1
- package/dist/providers/coding-proxy/metering.js +13 -2
- package/dist/providers/coding-proxy/prisma-ledger.js +59 -6
- package/dist/providers/coding-proxy/proxy.d.ts +12 -2
- package/dist/providers/coding-proxy/proxy.js +92 -30
- package/dist/providers/coding-proxy/types.d.ts +18 -1
- package/dist/providers/coding-proxy/types.js +12 -1
- package/dist/providers/engine/types.d.ts +19 -0
- package/dist/providers/executor/composition.js +9 -1
- package/dist/providers/executor/container.d.ts +30 -2
- package/dist/providers/executor/container.js +98 -17
- package/dist/providers/executor/dbos.d.ts +2 -0
- package/dist/providers/executor/dbos.js +7 -5
- package/dist/providers/executor/routing.d.ts +6 -0
- package/dist/providers/executor/routing.js +5 -0
- package/dist/providers/executor/types.d.ts +12 -0
- package/dist/providers/issue-tracker/adf.d.ts +31 -0
- package/dist/providers/issue-tracker/adf.js +181 -0
- package/dist/providers/issue-tracker/index.d.ts +5 -0
- package/dist/providers/issue-tracker/index.js +12 -0
- package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
- package/dist/providers/issue-tracker/jira-client.js +151 -0
- package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
- package/dist/providers/issue-tracker/jira-events.js +98 -0
- package/dist/providers/issue-tracker/jira.d.ts +116 -0
- package/dist/providers/issue-tracker/jira.js +502 -0
- package/dist/providers/issue-tracker/types.d.ts +269 -0
- package/dist/providers/issue-tracker/types.js +16 -0
- package/dist/providers/jobs/docker.d.ts +5 -1
- package/dist/providers/jobs/docker.js +61 -33
- package/dist/providers/jobs/kubernetes.d.ts +3 -0
- package/dist/providers/jobs/kubernetes.js +44 -4
- package/dist/providers/jobs/service-state.d.ts +22 -0
- package/dist/providers/jobs/service-state.js +17 -0
- package/dist/providers/llm/anthropic.d.ts +3 -3
- package/dist/providers/llm/anthropic.js +3 -9
- package/dist/providers/llm/bedrock.d.ts +3 -3
- package/dist/providers/llm/bedrock.js +3 -9
- package/dist/providers/llm/catalog-lookup.d.ts +10 -0
- package/dist/providers/llm/catalog-lookup.js +15 -0
- package/dist/providers/llm/catalog-shipped.d.ts +18 -0
- package/dist/providers/llm/catalog-shipped.js +197 -0
- package/dist/providers/llm/catalog-store.d.ts +58 -0
- package/dist/providers/llm/catalog-store.js +138 -0
- package/dist/providers/llm/catalog-types.d.ts +66 -0
- package/dist/providers/llm/catalog-types.js +64 -0
- package/dist/providers/llm/catalog.d.ts +61 -0
- package/dist/providers/llm/catalog.js +147 -0
- package/dist/providers/llm/claude-provider.d.ts +10 -14
- package/dist/providers/llm/claude-provider.js +11 -6
- package/dist/providers/llm/index.d.ts +9 -6
- package/dist/providers/llm/index.js +8 -5
- package/dist/providers/llm/openai.d.ts +14 -5
- package/dist/providers/llm/openai.js +24 -14
- package/dist/providers/llm/pricing-core.d.ts +5 -3
- package/dist/providers/llm/registration.js +8 -12
- package/dist/providers/llm/routing.d.ts +18 -17
- package/dist/providers/llm/routing.js +40 -24
- package/dist/providers/review-host/github-events.js +47 -1
- package/dist/providers/review-host/github.js +7 -6
- package/dist/providers/review-host/types.d.ts +25 -0
- package/dist/providers/vcs/git.js +2 -22
- package/dist/providers/vcs/github.d.ts +20 -0
- package/dist/providers/vcs/github.js +28 -2
- package/dist/providers/vcs/types.d.ts +6 -0
- package/dist/quickstart/index.d.ts +8 -0
- package/dist/quickstart/index.js +34 -34
- package/dist/serve.js +8 -2
- package/dist/viewer/api-schema.d.ts +2757 -0
- package/dist/viewer/api-schema.js +165 -0
- package/dist/viewer/build-schemas.d.ts +2 -0
- package/dist/viewer/build-schemas.js +18 -0
- package/dist/viewer/event-bus.d.ts +38 -0
- package/dist/viewer/event-bus.js +232 -0
- package/dist/viewer/graph.d.ts +40 -0
- package/dist/viewer/graph.js +243 -0
- package/dist/viewer/http.d.ts +30 -0
- package/dist/viewer/http.js +133 -0
- package/dist/viewer/run-detail.d.ts +4 -0
- package/dist/viewer/run-detail.js +61 -0
- package/dist/wardby-bin.js +5 -0
- package/docs/README.md +10 -0
- package/docs/agent-recipes.md +383 -0
- package/docs/code-review-agents.md +29 -2
- package/docs/coding-agent-setup.md +3 -0
- package/docs/coding-worker-isolation.md +39 -5
- package/docs/getting-started-gke.md +28 -11
- package/docs/getting-started-identity-provider.md +49 -38
- package/docs/getting-started.md +14 -0
- package/docs/jira-agents.md +649 -0
- package/docs/knowledge.md +387 -0
- package/docs/models.md +221 -0
- package/docs/security-deployment.md +19 -9
- package/docs/viewer-api.md +142 -0
- package/help/admin-viewer.md +39 -0
- package/help/agent-recipes.md +173 -0
- package/help/architecture-agent.md +189 -0
- package/help/builder-agent.md +80 -0
- package/help/code-review-agents.md +6 -0
- package/help/cost-attribution.md +67 -0
- package/help/creating-agents.md +22 -0
- package/help/deploy-gke.md +6 -0
- package/help/errors/model-unavailable.md +63 -0
- package/help/getting-started.md +1 -0
- package/help/github.md +18 -0
- package/help/identity-and-access.md +8 -3
- package/help/jira.md +135 -0
- package/help/knowledge.md +47 -0
- package/help/models.md +90 -0
- package/help/operating-agents.md +7 -1
- package/help/troubleshooting/budgets.md +6 -0
- package/package.json +5 -2
- package/prisma/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
- package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
- package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
- package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
- package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
- package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
- package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
- package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
- package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
- package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
- package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
- package/prisma/schema.prisma +258 -2
- package/dist/mcp/tools/models.d.ts +0 -8
- package/dist/mcp/tools/models.js +0 -15
- package/dist/providers/llm/pricing-anthropic.d.ts +0 -14
- package/dist/providers/llm/pricing-anthropic.js +0 -48
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -20
- package/dist/providers/llm/pricing-bedrock-claude.js +0 -46
- package/dist/providers/llm/pricing.d.ts +0 -30
- package/dist/providers/llm/pricing.js +0 -74
|
@@ -7,6 +7,10 @@ import { normalizeGitHubRepository } from "../../coding/protocol.js";
|
|
|
7
7
|
const PR_ACTIONS = new Set(["opened", "synchronize", "reopened", "ready_for_review"]);
|
|
8
8
|
const TRUSTED_ASSOCIATIONS = new Set(["OWNER", "MEMBER", "COLLABORATOR"]);
|
|
9
9
|
const SAFE_SHA = /^[0-9a-f]{40}$/;
|
|
10
|
+
// GitHub's push payload includes at most 2048 commits.
|
|
11
|
+
const PUSH_MAX_COMMITS = 2048;
|
|
12
|
+
const SAFE_BRANCH = /^[A-Za-z0-9._/-]{1,100}$/;
|
|
13
|
+
const PUSH_MAX_PATHS = 1000;
|
|
10
14
|
/** Mirrors the runId shape validated in providers/vcs (github.ts, git.ts). */
|
|
11
15
|
const SAFE_RUN_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
|
|
12
16
|
/** The hidden marker a coding run writes at the top of every PR it opens (providers/vcs/github.ts); legacy prefix too. */
|
|
@@ -75,10 +79,52 @@ export function normalizeGitHubEvent(eventName, payload, app) {
|
|
|
75
79
|
const repository = repositoryOf(p);
|
|
76
80
|
if (!repository)
|
|
77
81
|
return null;
|
|
82
|
+
if (eventName === "push") {
|
|
83
|
+
const defaultBranch = obj(p.repository)?.default_branch;
|
|
84
|
+
const { before, after } = p;
|
|
85
|
+
if (typeof defaultBranch !== "string" || !SAFE_BRANCH.test(defaultBranch))
|
|
86
|
+
return null;
|
|
87
|
+
if (p.ref !== `refs/heads/${defaultBranch}` || p.deleted === true)
|
|
88
|
+
return null;
|
|
89
|
+
if (typeof before !== "string" || typeof after !== "string")
|
|
90
|
+
return null;
|
|
91
|
+
if (!SAFE_SHA.test(before) || !SAFE_SHA.test(after) || /^0+$/.test(after))
|
|
92
|
+
return null;
|
|
93
|
+
const commits = Array.isArray(p.commits) ? p.commits : [];
|
|
94
|
+
const paths = new Set();
|
|
95
|
+
for (const commit of commits) {
|
|
96
|
+
const c = obj(commit);
|
|
97
|
+
for (const list of [c?.added, c?.modified, c?.removed]) {
|
|
98
|
+
if (Array.isArray(list))
|
|
99
|
+
for (const path of list)
|
|
100
|
+
if (typeof path === "string")
|
|
101
|
+
paths.add(path);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
kind: "push",
|
|
106
|
+
provider: "github",
|
|
107
|
+
repository,
|
|
108
|
+
branch: defaultBranch,
|
|
109
|
+
before,
|
|
110
|
+
after,
|
|
111
|
+
changedPaths: [...paths].sort().slice(0, PUSH_MAX_PATHS),
|
|
112
|
+
// GitHub includes at most 2048 commits, so a full list may be truncated.
|
|
113
|
+
changedPathsComplete: commits.length < PUSH_MAX_COMMITS && paths.size <= PUSH_MAX_PATHS,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
78
116
|
if (eventName === "pull_request") {
|
|
117
|
+
const pr = obj(p.pull_request);
|
|
118
|
+
if (p.action === "closed") {
|
|
119
|
+
// No author or fork gate: this only marks issue/PR pairs wardby itself
|
|
120
|
+
// recorded for this repository and number, never acting on PR text.
|
|
121
|
+
const prNumber = int(pr?.number);
|
|
122
|
+
if (!prNumber)
|
|
123
|
+
return null;
|
|
124
|
+
return { kind: "pr_closed", provider: "github", repository, prNumber, merged: pr?.merged === true };
|
|
125
|
+
}
|
|
79
126
|
if (typeof p.action !== "string" || !PR_ACTIONS.has(p.action))
|
|
80
127
|
return null;
|
|
81
|
-
const pr = obj(p.pull_request);
|
|
82
128
|
const head = obj(pr?.head);
|
|
83
129
|
const prNumber = int(pr?.number);
|
|
84
130
|
const headSha = head?.sha;
|
|
@@ -335,6 +335,7 @@ export class GitHubReviewHost {
|
|
|
335
335
|
endLine: window.startLine + slice.length - 1,
|
|
336
336
|
truncated: window.startLine - 1 + slice.length < lines.length,
|
|
337
337
|
content: slice.map((line, i) => `${window.startLine + i}: ${line}`).join("\n"),
|
|
338
|
+
text: slice.join("\n"),
|
|
338
339
|
};
|
|
339
340
|
});
|
|
340
341
|
}
|
|
@@ -343,15 +344,15 @@ export class GitHubReviewHost {
|
|
|
343
344
|
return this.withToken(repository, { contents: "read" }, async (get) => {
|
|
344
345
|
const resolvedRef = ref ?? str(record(await (await get(base)).json()).default_branch);
|
|
345
346
|
const tree = record(await (await get(`${base}/git/trees/${encodeURIComponent(resolvedRef)}?recursive=1`)).json());
|
|
346
|
-
const
|
|
347
|
+
const entries = list(tree.tree)
|
|
347
348
|
.filter((e) => e.type === "blob" && typeof e.path === "string" && e.path.startsWith(pathPrefix))
|
|
348
|
-
.filter((e) => !VENDORED.test(str(e.path)) && !str(e.path).endsWith("package-lock.json"))
|
|
349
|
-
.map((e) => `${str(e.path)} (${num(e.size)} bytes)`);
|
|
349
|
+
.filter((e) => !VENDORED.test(str(e.path)) && !str(e.path).endsWith("package-lock.json"));
|
|
350
350
|
return {
|
|
351
351
|
ref: resolvedRef,
|
|
352
|
-
count:
|
|
353
|
-
truncated: tree.truncated === true ||
|
|
354
|
-
files:
|
|
352
|
+
count: entries.length,
|
|
353
|
+
truncated: tree.truncated === true || entries.length > MAX_LISTED_FILES,
|
|
354
|
+
files: entries.slice(0, MAX_LISTED_FILES).map((e) => `${str(e.path)} (${num(e.size)} bytes)`),
|
|
355
|
+
paths: entries.slice(0, MAX_LISTED_FILES).map((e) => str(e.path)),
|
|
355
356
|
};
|
|
356
357
|
});
|
|
357
358
|
}
|
|
@@ -70,7 +70,10 @@ export type FileReadResult = {
|
|
|
70
70
|
startLine: number;
|
|
71
71
|
endLine: number;
|
|
72
72
|
truncated: boolean;
|
|
73
|
+
/** The window with `N: ` line-number prefixes: the display form shown to agents. */
|
|
73
74
|
content: string;
|
|
75
|
+
/** The same window without line-number prefixes, lines joined by "\n": for machine parsing. */
|
|
76
|
+
text: string;
|
|
74
77
|
} | {
|
|
75
78
|
kind: "directory";
|
|
76
79
|
path: string;
|
|
@@ -83,7 +86,10 @@ export interface FileListView {
|
|
|
83
86
|
ref: string;
|
|
84
87
|
count: number;
|
|
85
88
|
truncated: boolean;
|
|
89
|
+
/** Display strings (path plus size) shown to agents. */
|
|
86
90
|
files: string[];
|
|
91
|
+
/** Bare repository paths, same order and length as `files`. */
|
|
92
|
+
paths: string[];
|
|
87
93
|
}
|
|
88
94
|
export interface InlineComment {
|
|
89
95
|
path: string;
|
|
@@ -247,12 +253,31 @@ export type HostUserAuthorizerRegistry = Partial<Record<ReviewHostProvider, Host
|
|
|
247
253
|
* provider payload into, for src/core/host-events.ts to route.
|
|
248
254
|
*/
|
|
249
255
|
export type HostEvent = {
|
|
256
|
+
kind: "push";
|
|
257
|
+
provider: ReviewHostProvider;
|
|
258
|
+
repository: string;
|
|
259
|
+
/** The branch pushed (without refs/heads/), always the repository's default branch. */
|
|
260
|
+
branch: string;
|
|
261
|
+
before: string;
|
|
262
|
+
after: string;
|
|
263
|
+
/** Union of added/modified/removed paths over the payload's commits, deduplicated, at most 1000. */
|
|
264
|
+
changedPaths: string[];
|
|
265
|
+
/** False when GitHub truncated the commit list (> 20 commits) or paths were capped: treat every concept as in scope. */
|
|
266
|
+
changedPathsComplete: boolean;
|
|
267
|
+
} | {
|
|
250
268
|
kind: "pr_updated";
|
|
251
269
|
provider: ReviewHostProvider;
|
|
252
270
|
repository: string;
|
|
253
271
|
prNumber: number;
|
|
254
272
|
headSha: string;
|
|
255
273
|
isFork: boolean;
|
|
274
|
+
} | {
|
|
275
|
+
/** The PR was merged or closed; only drives bookkeeping on issue/PR pairs wardby recorded. */
|
|
276
|
+
kind: "pr_closed";
|
|
277
|
+
provider: ReviewHostProvider;
|
|
278
|
+
repository: string;
|
|
279
|
+
prNumber: number;
|
|
280
|
+
merged: boolean;
|
|
256
281
|
} | {
|
|
257
282
|
kind: "check_rerun";
|
|
258
283
|
provider: ReviewHostProvider;
|
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
import { gitExcludePathspecs, normalizeCollectExclusions } from "../../coding/collect-exclude.js";
|
|
6
6
|
import { WARDBY_PROTECTED_PATHS, isProtectedPathException, isWellFormedProtectedPath, protectedPathBody, protectsSomePath, } from "../../coding/protected-paths.js";
|
|
7
7
|
import { ensurePrivateDirectory } from "../../core/private-directory.js";
|
|
8
|
+
import { globRegex } from "../../core/glob.js";
|
|
8
9
|
import { MAX_REDACTED_SPAN, redactTokenShapedValues, normalizeGitHubRepository, normalizeGitRef, } from "../../coding/protocol.js";
|
|
9
10
|
import { isSafeGitHubInstallationToken } from "./github.js";
|
|
10
11
|
export const DEFAULT_MAX_CHANGED_FILES = 1_000;
|
|
@@ -149,28 +150,6 @@ function validateProtectedPath(value) {
|
|
|
149
150
|
}
|
|
150
151
|
return path;
|
|
151
152
|
}
|
|
152
|
-
function globRegex(pattern) {
|
|
153
|
-
let source = "^";
|
|
154
|
-
for (let index = 0; index < pattern.length; index += 1) {
|
|
155
|
-
const character = pattern[index];
|
|
156
|
-
if (character === "*") {
|
|
157
|
-
if (pattern[index + 1] === "*") {
|
|
158
|
-
source += ".*";
|
|
159
|
-
index += 1;
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
source += "[^/]*";
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
else if (character === "?") {
|
|
166
|
-
source += "[^/]";
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
source += character.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return new RegExp(`${source}$`);
|
|
173
|
-
}
|
|
174
153
|
/** The fixed baseline, split once: its literal exceptions and its protected globs. */
|
|
175
154
|
const WARDBY_UNPROTECTED = new Set(WARDBY_PROTECTED_PATHS.filter(isProtectedPathException).map(protectedPathBody));
|
|
176
155
|
const WARDBY_PROTECTED = WARDBY_PROTECTED_PATHS.filter((pattern) => !isProtectedPathException(pattern)).map((pattern) => globRegex(pattern));
|
|
@@ -458,6 +437,7 @@ export class GitVcsProvider {
|
|
|
458
437
|
tag: details?.tag,
|
|
459
438
|
packages: details?.packages,
|
|
460
439
|
packageRefusals: details?.packageRefusals,
|
|
440
|
+
...(details?.issue ? { issue: details.issue } : {}),
|
|
461
441
|
});
|
|
462
442
|
return {
|
|
463
443
|
outcome: prepared.continuation ? "pull_request_updated" : "pull_request_opened",
|
|
@@ -40,6 +40,19 @@ export interface PullRequestInput {
|
|
|
40
40
|
* draft either way; unset (a fresh run), only a draft PR is accepted.
|
|
41
41
|
*/
|
|
42
42
|
acceptReadyForReview?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* The originating issue-tracker issue (control-plane data, never model
|
|
45
|
+
* output). Provider-neutral: the title gets a `[KEY] ` prefix and the body a
|
|
46
|
+
* first line linking it ("Resolves <trackerName> issue …"). The key is
|
|
47
|
+
* re-validated here, a non-https `url` is dropped, and so is a `trackerName`
|
|
48
|
+
* that isn't a short plain name. The PR lookup keys on the hidden run
|
|
49
|
+
* marker, never the title.
|
|
50
|
+
*/
|
|
51
|
+
issue?: {
|
|
52
|
+
key: string;
|
|
53
|
+
url?: string;
|
|
54
|
+
trackerName?: string;
|
|
55
|
+
};
|
|
43
56
|
}
|
|
44
57
|
export interface PullRequestResult {
|
|
45
58
|
number: number;
|
|
@@ -95,6 +108,13 @@ export interface GitHubRepositoryAccess {
|
|
|
95
108
|
type Fetch = typeof globalThis.fetch;
|
|
96
109
|
/** Accepts bounded opaque installation tokens without allowing control characters. */
|
|
97
110
|
export declare function isSafeGitHubInstallationToken(value: unknown): value is string;
|
|
111
|
+
/**
|
|
112
|
+
* Same shape as ISSUE_KEY (issue-tracker/types.ts); duplicated so the VCS layer
|
|
113
|
+
* stays free of issue-tracker imports (a test keeps the two in step). The
|
|
114
|
+
* executor validates with the shared one.
|
|
115
|
+
*/
|
|
116
|
+
export declare const PR_ISSUE_KEY: RegExp;
|
|
117
|
+
export declare function pullRequestTitle(input: PullRequestInput): string;
|
|
98
118
|
/** At most this many packages, and separately this many refusals, are listed
|
|
99
119
|
* in a PR body; the rest are counted in a "…and N more" line pointing at
|
|
100
120
|
* get_run, which returns the full list. */
|
|
@@ -33,8 +33,30 @@ export function isSafeGitHubInstallationToken(value) {
|
|
|
33
33
|
Buffer.byteLength(value, "utf8") <= 512 &&
|
|
34
34
|
/^[A-Za-z0-9._-]+$/.test(value));
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Same shape as ISSUE_KEY (issue-tracker/types.ts); duplicated so the VCS layer
|
|
38
|
+
* stays free of issue-tracker imports (a test keeps the two in step). The
|
|
39
|
+
* executor validates with the shared one.
|
|
40
|
+
*/
|
|
41
|
+
export const PR_ISSUE_KEY = /^[A-Z][A-Z0-9_]{0,254}-[1-9]\d{0,9}$/;
|
|
42
|
+
/** The issue key when it is well-formed; a malformed one is dropped everywhere it would be rendered. */
|
|
43
|
+
function validIssueKey(input) {
|
|
44
|
+
const key = input.issue?.key;
|
|
45
|
+
return typeof key === "string" && PR_ISSUE_KEY.test(key) ? key : undefined;
|
|
46
|
+
}
|
|
47
|
+
export function pullRequestTitle(input) {
|
|
48
|
+
const base = input.tag ? `[${input.tag}] Wardby run ${input.runId}` : `Wardby run ${input.runId}`;
|
|
49
|
+
const key = validIssueKey(input);
|
|
50
|
+
return key ? `[${key}] ${base}` : base;
|
|
51
|
+
}
|
|
52
|
+
function issueLine(input) {
|
|
53
|
+
const key = validIssueKey(input);
|
|
54
|
+
const url = input.issue?.url;
|
|
55
|
+
if (!key || typeof url !== "string" || !url.startsWith("https://") || /[\s<>()]/.test(url))
|
|
56
|
+
return undefined;
|
|
57
|
+
const name = input.issue?.trackerName;
|
|
58
|
+
const tracker = typeof name === "string" && /^[A-Za-z][A-Za-z0-9 .-]{0,39}$/.test(name) ? `${name.trim()} ` : "";
|
|
59
|
+
return `Resolves ${tracker}issue [${key}](${url})`;
|
|
38
60
|
}
|
|
39
61
|
/**
|
|
40
62
|
* Package names/versions/reasons come from registry metadata, not from
|
|
@@ -103,6 +125,10 @@ function packagesSection(input) {
|
|
|
103
125
|
/** The hidden run marker stays first and unconditional: createOrFindDraftPullRequest's idempotent lookup depends on it. */
|
|
104
126
|
export function pullRequestBody(input) {
|
|
105
127
|
const sections = [`${RUN_MARKER_PREFIX}${input.runId} -->`];
|
|
128
|
+
// Hidden marker stays first; the issue link is the first visible line.
|
|
129
|
+
const issue = issueLine(input);
|
|
130
|
+
if (issue)
|
|
131
|
+
sections.push(issue);
|
|
106
132
|
if (input.summary)
|
|
107
133
|
sections.push(input.summary);
|
|
108
134
|
if (input.tests?.length) {
|
|
@@ -65,6 +65,12 @@ export interface FinalizeChangesDetails {
|
|
|
65
65
|
name: string;
|
|
66
66
|
reason: string;
|
|
67
67
|
}[];
|
|
68
|
+
/** Originating issue (control-plane data): named in the PR title and body. Provider-neutral. */
|
|
69
|
+
issue?: {
|
|
70
|
+
key: string;
|
|
71
|
+
url?: string;
|
|
72
|
+
trackerName?: string;
|
|
73
|
+
};
|
|
68
74
|
}
|
|
69
75
|
export type FinalizeChangesResult = {
|
|
70
76
|
outcome: "no_changes";
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arguments for `<client> mcp add`. The server name comes right after `add`:
|
|
3
|
+
* Claude Code's `-e/--env <env...>` is variadic and would otherwise read the
|
|
4
|
+
* name as another KEY=value ("Invalid environment variable format: wardby").
|
|
5
|
+
*/
|
|
6
|
+
export declare function mcpAddArgs(client: "codex" | "claude", projectDir: string, packageSpec: string): string[];
|
|
1
7
|
export declare function quickstartCommand(args: string[]): Promise<void>;
|
|
8
|
+
/** The closing "build your first agent" hint; the assistant prompts only make sense once an MCP client is configured. */
|
|
9
|
+
export declare function nextStepLines(mcpConfigured: boolean): string[];
|
|
2
10
|
export declare function doctorCommand(args: string[]): Promise<void>;
|
|
3
11
|
export declare function statusCommand(args: string[]): Promise<void>;
|
|
4
12
|
export declare function logsCommand(args: string[]): Promise<void>;
|
package/dist/quickstart/index.js
CHANGED
|
@@ -263,54 +263,41 @@ function runDemo(paths) {
|
|
|
263
263
|
function clientInstalled(command) {
|
|
264
264
|
return runCommand(command, ["--version"]).status === 0;
|
|
265
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Arguments for `<client> mcp add`. The server name comes right after `add`:
|
|
268
|
+
* Claude Code's `-e/--env <env...>` is variadic and would otherwise read the
|
|
269
|
+
* name as another KEY=value ("Invalid environment variable format: wardby").
|
|
270
|
+
*/
|
|
271
|
+
export function mcpAddArgs(client, projectDir, packageSpec) {
|
|
272
|
+
const env = `WARDBY_PROJECT_DIR=${projectDir}`;
|
|
273
|
+
const options = client === "codex" ? ["--env", env] : ["--scope", "local", "-e", env];
|
|
274
|
+
return ["mcp", "add", "wardby", ...options, "--", "npx", "--yes", packageSpec, "mcp"];
|
|
275
|
+
}
|
|
266
276
|
function configureOneMcpClient(client, paths) {
|
|
267
277
|
if (!clientInstalled(client)) {
|
|
268
278
|
console.warn(`! ${client} is not installed; skipped its MCP configuration.`);
|
|
269
|
-
return;
|
|
279
|
+
return false;
|
|
270
280
|
}
|
|
271
281
|
const existing = runCommand(client, ["mcp", "get", "wardby"], { cwd: paths.projectDir });
|
|
272
282
|
if (existing.status === 0) {
|
|
273
283
|
console.log(`✓ ${client} already has an MCP server named wardby.`);
|
|
274
|
-
return;
|
|
284
|
+
return true;
|
|
275
285
|
}
|
|
276
|
-
const
|
|
277
|
-
const args = client === "codex"
|
|
278
|
-
? [
|
|
279
|
-
"mcp",
|
|
280
|
-
"add",
|
|
281
|
-
"--env",
|
|
282
|
-
`WARDBY_PROJECT_DIR=${paths.projectDir}`,
|
|
283
|
-
"wardby",
|
|
284
|
-
"--",
|
|
285
|
-
"npx",
|
|
286
|
-
"--yes",
|
|
287
|
-
packageSpec,
|
|
288
|
-
"mcp",
|
|
289
|
-
]
|
|
290
|
-
: [
|
|
291
|
-
"mcp",
|
|
292
|
-
"add",
|
|
293
|
-
"--scope",
|
|
294
|
-
"local",
|
|
295
|
-
"-e",
|
|
296
|
-
`WARDBY_PROJECT_DIR=${paths.projectDir}`,
|
|
297
|
-
"wardby",
|
|
298
|
-
"--",
|
|
299
|
-
"npx",
|
|
300
|
-
"--yes",
|
|
301
|
-
packageSpec,
|
|
302
|
-
"mcp",
|
|
303
|
-
];
|
|
286
|
+
const args = mcpAddArgs(client, paths.projectDir, `@wardby/cli@${packageJson.version}`);
|
|
304
287
|
const result = runCommand(client, args, { cwd: paths.projectDir });
|
|
305
288
|
if (result.status !== 0)
|
|
306
289
|
commandFailure(`${client} MCP configuration`, result);
|
|
307
290
|
console.log(`✓ Configured Wardby MCP for ${client}.`);
|
|
291
|
+
return true;
|
|
308
292
|
}
|
|
293
|
+
/** True when at least one requested client ended up with the wardby MCP server. */
|
|
309
294
|
function configureMcpClients(client, paths) {
|
|
295
|
+
const results = [];
|
|
310
296
|
if (client === "codex" || client === "both")
|
|
311
|
-
configureOneMcpClient("codex", paths);
|
|
297
|
+
results.push(configureOneMcpClient("codex", paths));
|
|
312
298
|
if (client === "claude" || client === "both")
|
|
313
|
-
configureOneMcpClient("claude", paths);
|
|
299
|
+
results.push(configureOneMcpClient("claude", paths));
|
|
300
|
+
return results.some(Boolean);
|
|
314
301
|
}
|
|
315
302
|
async function chooseClient(nonInteractive) {
|
|
316
303
|
if (nonInteractive)
|
|
@@ -412,12 +399,25 @@ export async function quickstartCommand(args) {
|
|
|
412
399
|
console.log(`Skipped the billed run. Start it later with: npx @wardby/cli@latest run ${DEMO_AGENT}`);
|
|
413
400
|
}
|
|
414
401
|
const client = parseClient(values.client) ?? (await chooseClient(nonInteractive));
|
|
415
|
-
|
|
416
|
-
configureMcpClients(client, paths);
|
|
402
|
+
const mcpConfigured = client !== "none" && configureMcpClients(client, paths);
|
|
417
403
|
console.log("\nWardby is ready.");
|
|
418
404
|
console.log(" npx @wardby/cli@latest status");
|
|
419
405
|
console.log(" npx @wardby/cli@latest doctor");
|
|
420
406
|
console.log(" npx @wardby/cli@latest down");
|
|
407
|
+
for (const line of nextStepLines(mcpConfigured))
|
|
408
|
+
console.log(line);
|
|
409
|
+
}
|
|
410
|
+
/** The closing "build your first agent" hint; the assistant prompts only make sense once an MCP client is configured. */
|
|
411
|
+
export function nextStepLines(mcpConfigured) {
|
|
412
|
+
const guide = "the guide: npx @wardby/cli@latest help open agent-recipes";
|
|
413
|
+
const lines = ["", "Next: build your first agent."];
|
|
414
|
+
if (mcpConfigured) {
|
|
415
|
+
lines.push("Ask your assistant one of:", ' "Set up the Wardby architecture keeper for this repository"', ' "Set up a Wardby builder for this repository"', `Or read ${guide}`);
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
lines.push(`Read ${guide}`);
|
|
419
|
+
}
|
|
420
|
+
return lines;
|
|
421
421
|
}
|
|
422
422
|
async function databaseHealthy(paths) {
|
|
423
423
|
const env = readQuickstartEnv(paths);
|
package/dist/serve.js
CHANGED
|
@@ -33,12 +33,18 @@ export async function startServe(options = {}) {
|
|
|
33
33
|
const concurrency = loadCodingConcurrencyConfig();
|
|
34
34
|
const providers = options.providers ?? buildMcpProviders().providers;
|
|
35
35
|
const mcp = await startMcp({ providers, schedulerAttached: true });
|
|
36
|
-
const reconciler = startReconciler({
|
|
36
|
+
const reconciler = startReconciler({
|
|
37
|
+
executor: providers.executor,
|
|
38
|
+
reviewHosts: providers.reviewHosts,
|
|
39
|
+
issueTrackers: providers.issueTrackers,
|
|
40
|
+
});
|
|
41
|
+
const selfDefects = { db: prisma, issueTrackers: providers.issueTrackers };
|
|
37
42
|
const scheduler = startScheduler({
|
|
38
43
|
executor: providers.executor,
|
|
39
44
|
scope: options.scope,
|
|
45
|
+
selfDefects,
|
|
40
46
|
onLeaderTick: async () => {
|
|
41
|
-
await drainCodingQueue({ db: prisma, executor: providers.executor, ...concurrency });
|
|
47
|
+
await drainCodingQueue({ db: prisma, executor: providers.executor, ...concurrency, selfDefects });
|
|
42
48
|
},
|
|
43
49
|
});
|
|
44
50
|
return {
|