@synctek/forgeos 2.0.0 → 2.1.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 (45) hide show
  1. package/README.md +177 -268
  2. package/dist/cli/commands/evidence.d.ts.map +1 -1
  3. package/dist/cli/commands/evidence.js +18 -0
  4. package/dist/cli/commands/evidence.js.map +1 -1
  5. package/dist/cli/commands/federation.d.ts +9 -4
  6. package/dist/cli/commands/federation.d.ts.map +1 -1
  7. package/dist/cli/commands/federation.js +73 -30
  8. package/dist/cli/commands/federation.js.map +1 -1
  9. package/dist/cli/commands/gate.d.ts.map +1 -1
  10. package/dist/cli/commands/gate.js +23 -0
  11. package/dist/cli/commands/gate.js.map +1 -1
  12. package/dist/cli/commands/review.d.ts.map +1 -1
  13. package/dist/cli/commands/review.js +25 -0
  14. package/dist/cli/commands/review.js.map +1 -1
  15. package/dist/mcp/handlers.js +17 -0
  16. package/dist/mcp/handlers.js.map +1 -1
  17. package/dist/shared/types.d.ts +15 -1
  18. package/dist/shared/types.d.ts.map +1 -1
  19. package/dist/trust/index.d.ts +1 -1
  20. package/dist/trust/index.js +1 -1
  21. package/package.json +11 -6
  22. package/dist/client.d.ts +0 -46
  23. package/dist/client.d.ts.map +0 -1
  24. package/dist/client.js +0 -146
  25. package/dist/client.js.map +0 -1
  26. package/dist/handlers.d.ts +0 -11
  27. package/dist/handlers.d.ts.map +0 -1
  28. package/dist/handlers.js +0 -424
  29. package/dist/handlers.js.map +0 -1
  30. package/dist/http-server.d.ts +0 -25
  31. package/dist/http-server.d.ts.map +0 -1
  32. package/dist/http-server.js +0 -246
  33. package/dist/http-server.js.map +0 -1
  34. package/dist/index.d.ts +0 -3
  35. package/dist/index.d.ts.map +0 -1
  36. package/dist/index.js +0 -40
  37. package/dist/index.js.map +0 -1
  38. package/dist/tools.d.ts +0 -944
  39. package/dist/tools.d.ts.map +0 -1
  40. package/dist/tools.js +0 -513
  41. package/dist/tools.js.map +0 -1
  42. package/dist/types.d.ts +0 -183
  43. package/dist/types.d.ts.map +0 -1
  44. package/dist/types.js +0 -3
  45. package/dist/types.js.map +0 -1
package/dist/client.d.ts DELETED
@@ -1,46 +0,0 @@
1
- /**
2
- * ForgeOS Engine HTTP client.
3
- * Thin wrapper over fetch() that calls the ForgeOS Engine REST API.
4
- *
5
- * Board Conditions (Engineering Director):
6
- * - 10-second timeout per request (AbortController)
7
- * - One automatic retry on network errors (fetch failures, not HTTP errors)
8
- * - X-ForgeOS-API-Key header when apiKey is provided
9
- *
10
- * Design note: API key is accepted only as a constructor parameter.
11
- * The module-level singleton `client` uses env vars for backward-compat
12
- * with the stdio transport. The HTTP transport creates per-request instances
13
- * with the key threaded from the Authorization header — no env mutation.
14
- */
15
- export declare class ForgeOSClient {
16
- private baseUrl;
17
- private apiKey;
18
- /**
19
- * @param baseUrl Engine base URL. Defaults to FORGEOS_ENGINE_URL env var or http://localhost:8400.
20
- * @param apiKey API key to send as X-ForgeOS-API-Key. Pass explicitly; do NOT read from env here.
21
- */
22
- constructor(baseUrl?: string, apiKey?: string);
23
- /** Build common headers for every request. */
24
- private headers;
25
- /**
26
- * Core fetch wrapper with timeout and retry.
27
- * - Aborts after REQUEST_TIMEOUT_MS via AbortController.
28
- * - Retries once on network errors (TypeError from fetch).
29
- * - Does NOT retry on HTTP error responses (4xx/5xx) or timeouts.
30
- */
31
- private fetchWithRetry;
32
- /** Parse a non-ok response into a descriptive error. */
33
- private throwHttpError;
34
- get(path: string, params?: Record<string, string>): Promise<unknown>;
35
- post(path: string, body?: unknown): Promise<unknown>;
36
- patch(path: string, body?: unknown): Promise<unknown>;
37
- put(path: string, body?: unknown): Promise<unknown>;
38
- }
39
- /**
40
- * Module-level singleton for the stdio transport path.
41
- * Reads FORGEOS_API_KEY from env at startup — safe because stdio is
42
- * single-tenant (one process = one user). The HTTP transport MUST NOT
43
- * use this singleton; it must construct per-request ForgeOSClient instances.
44
- */
45
- export declare const client: ForgeOSClient;
46
- //# sourceMappingURL=client.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAkBH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IAEvB;;;OAGG;gBACS,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAK7C,8CAA8C;IAC9C,OAAO,CAAC,OAAO;IAQf;;;;;OAKG;YACW,cAAc;IA6C5B,wDAAwD;YAC1C,cAAc;IAKtB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAcpE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAWpD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAWrD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CAU1D;AAED;;;;;GAKG;AACH,eAAO,MAAM,MAAM,eAGlB,CAAC"}
package/dist/client.js DELETED
@@ -1,146 +0,0 @@
1
- /**
2
- * ForgeOS Engine HTTP client.
3
- * Thin wrapper over fetch() that calls the ForgeOS Engine REST API.
4
- *
5
- * Board Conditions (Engineering Director):
6
- * - 10-second timeout per request (AbortController)
7
- * - One automatic retry on network errors (fetch failures, not HTTP errors)
8
- * - X-ForgeOS-API-Key header when apiKey is provided
9
- *
10
- * Design note: API key is accepted only as a constructor parameter.
11
- * The module-level singleton `client` uses env vars for backward-compat
12
- * with the stdio transport. The HTTP transport creates per-request instances
13
- * with the key threaded from the Authorization header — no env mutation.
14
- */
15
- const ENGINE_URL = process.env.FORGEOS_ENGINE_URL || "http://localhost:8400";
16
- const REQUEST_TIMEOUT_MS = 10_000;
17
- const MAX_RETRIES = 1;
18
- /** Check if an error is a network-level failure (not an HTTP status error). */
19
- function isNetworkError(err) {
20
- if (err instanceof TypeError)
21
- return true; // fetch throws TypeError for network issues
22
- if (err instanceof DOMException && err.name === "AbortError")
23
- return false; // timeout — do not retry
24
- return false;
25
- }
26
- /** Small delay for retry backoff. */
27
- function delay(ms) {
28
- return new Promise((resolve) => setTimeout(resolve, ms));
29
- }
30
- export class ForgeOSClient {
31
- baseUrl;
32
- apiKey;
33
- /**
34
- * @param baseUrl Engine base URL. Defaults to FORGEOS_ENGINE_URL env var or http://localhost:8400.
35
- * @param apiKey API key to send as X-ForgeOS-API-Key. Pass explicitly; do NOT read from env here.
36
- */
37
- constructor(baseUrl, apiKey) {
38
- this.baseUrl = (baseUrl || ENGINE_URL).replace(/\/+$/, "");
39
- this.apiKey = apiKey ?? "";
40
- }
41
- /** Build common headers for every request. */
42
- headers(extra) {
43
- const h = { ...extra };
44
- if (this.apiKey) {
45
- h["X-ForgeOS-API-Key"] = this.apiKey;
46
- }
47
- return h;
48
- }
49
- /**
50
- * Core fetch wrapper with timeout and retry.
51
- * - Aborts after REQUEST_TIMEOUT_MS via AbortController.
52
- * - Retries once on network errors (TypeError from fetch).
53
- * - Does NOT retry on HTTP error responses (4xx/5xx) or timeouts.
54
- */
55
- async fetchWithRetry(url, init) {
56
- let lastError;
57
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
58
- const controller = new AbortController();
59
- const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
60
- try {
61
- const res = await fetch(url, {
62
- ...init,
63
- signal: controller.signal,
64
- });
65
- clearTimeout(timer);
66
- return res;
67
- }
68
- catch (err) {
69
- clearTimeout(timer);
70
- lastError = err;
71
- // Only retry on network errors, not on timeouts or other issues
72
- if (isNetworkError(err) && attempt < MAX_RETRIES) {
73
- await delay(500);
74
- continue;
75
- }
76
- // Timeout — throw a clearer message
77
- if (err instanceof DOMException && err.name === "AbortError") {
78
- throw new Error(`Request timed out after ${REQUEST_TIMEOUT_MS}ms: ${init.method ?? "GET"} ${url}`);
79
- }
80
- throw err;
81
- }
82
- }
83
- // Should not reach here, but safety net
84
- throw lastError;
85
- }
86
- /** Parse a non-ok response into a descriptive error. */
87
- async throwHttpError(res) {
88
- const text = await res.text().catch(() => "(unreadable body)");
89
- throw new Error(`${res.status} ${res.statusText}: ${text}`);
90
- }
91
- async get(path, params) {
92
- let url = `${this.baseUrl}${path}`;
93
- if (params) {
94
- const searchParams = new URLSearchParams(params);
95
- url += `?${searchParams.toString()}`;
96
- }
97
- const res = await this.fetchWithRetry(url, {
98
- method: "GET",
99
- headers: this.headers(),
100
- });
101
- if (!res.ok)
102
- await this.throwHttpError(res);
103
- return res.json();
104
- }
105
- async post(path, body) {
106
- const url = `${this.baseUrl}${path}`;
107
- const res = await this.fetchWithRetry(url, {
108
- method: "POST",
109
- headers: this.headers({ "Content-Type": "application/json" }),
110
- body: body !== undefined ? JSON.stringify(body) : undefined,
111
- });
112
- if (!res.ok)
113
- await this.throwHttpError(res);
114
- return res.json();
115
- }
116
- async patch(path, body) {
117
- const url = `${this.baseUrl}${path}`;
118
- const res = await this.fetchWithRetry(url, {
119
- method: "PATCH",
120
- headers: this.headers({ "Content-Type": "application/json" }),
121
- body: body !== undefined ? JSON.stringify(body) : undefined,
122
- });
123
- if (!res.ok)
124
- await this.throwHttpError(res);
125
- return res.json();
126
- }
127
- async put(path, body) {
128
- const url = `${this.baseUrl}${path}`;
129
- const res = await this.fetchWithRetry(url, {
130
- method: "PUT",
131
- headers: this.headers({ "Content-Type": "application/json" }),
132
- body: body !== undefined ? JSON.stringify(body) : undefined,
133
- });
134
- if (!res.ok)
135
- await this.throwHttpError(res);
136
- return res.json();
137
- }
138
- }
139
- /**
140
- * Module-level singleton for the stdio transport path.
141
- * Reads FORGEOS_API_KEY from env at startup — safe because stdio is
142
- * single-tenant (one process = one user). The HTTP transport MUST NOT
143
- * use this singleton; it must construct per-request ForgeOSClient instances.
144
- */
145
- export const client = new ForgeOSClient(process.env.FORGEOS_ENGINE_URL, process.env.FORGEOS_API_KEY);
146
- //# sourceMappingURL=client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -1,11 +0,0 @@
1
- import { ForgeOSClient } from "./client.js";
2
- declare function ok(data: unknown): {
3
- content: {
4
- type: "text";
5
- text: string;
6
- }[];
7
- };
8
- export declare function createHandleTool(forgeClient: ForgeOSClient): (name: string, rawArgs: Record<string, unknown>) => Promise<ReturnType<typeof ok>>;
9
- export declare function handleTool(name: string, rawArgs: Record<string, unknown>): Promise<ReturnType<typeof ok>>;
10
- export {};
11
- //# sourceMappingURL=handlers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,aAAa,EAAE,MAAM,aAAa,CAAC;AAMrE,iBAAS,EAAE,CAAC,IAAI,EAAE,OAAO;;;;;EAMxB;AA2JD,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,aAAa,GACzB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAEpF;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAEhC"}
package/dist/handlers.js DELETED
@@ -1,424 +0,0 @@
1
- import { z } from "zod";
2
- import { client as defaultClient } from "./client.js";
3
- // ---------------------------------------------------------------------------
4
- // Response helpers
5
- // ---------------------------------------------------------------------------
6
- function ok(data) {
7
- return {
8
- content: [
9
- { type: "text", text: JSON.stringify(data, null, 2) },
10
- ],
11
- };
12
- }
13
- function fail(error) {
14
- return {
15
- content: [
16
- {
17
- type: "text",
18
- text: JSON.stringify({ ok: false, error }, null, 2),
19
- },
20
- ],
21
- isError: true,
22
- };
23
- }
24
- // ---------------------------------------------------------------------------
25
- // Input schemas (Zod validation)
26
- // ---------------------------------------------------------------------------
27
- const InitArgs = z.object({
28
- project_id: z.string(),
29
- developer_id: z.string().optional().default("local"),
30
- });
31
- const CreateInitiativeArgs = z.object({
32
- project_id: z.string(),
33
- title: z.string(),
34
- description: z.string(),
35
- priority: z
36
- .enum(["low", "medium", "high", "critical"])
37
- .optional()
38
- .default("medium"),
39
- });
40
- const ProposeChangesetArgs = z.object({
41
- project_id: z.string(),
42
- initiative_id: z.string(),
43
- description: z.string(),
44
- files_changed: z.array(z.string()),
45
- modules_affected: z.array(z.string()),
46
- branch: z.string().optional(),
47
- });
48
- const GetProfileArgs = z.object({
49
- role: z.string(),
50
- project_id: z.string().optional(),
51
- team_id: z.string().optional().default("default"),
52
- modules_affected: z.array(z.string()).optional(),
53
- files_changed: z.array(z.string()).optional(),
54
- });
55
- const SubmitEvidenceArgs = z.object({
56
- project_id: z.string(),
57
- changeset_id: z.string(),
58
- type: z.string(),
59
- summary: z.string(),
60
- file_refs: z.array(z.string()).optional().default([]),
61
- });
62
- const SubmitReviewArgs = z.object({
63
- project_id: z.string(),
64
- changeset_id: z.string(),
65
- role: z.string(),
66
- status: z.enum(["approved", "blocked", "pending"]),
67
- notes: z.string(),
68
- findings: z.array(z.object({
69
- category: z.string(),
70
- severity: z.string(),
71
- observation: z.string(),
72
- recommendation: z.string().optional(),
73
- })),
74
- });
75
- const CheckGatesArgs = z.object({
76
- project_id: z.string(),
77
- changeset_id: z.string(),
78
- });
79
- const PromoteGateArgs = z.object({
80
- project_id: z.string(),
81
- changeset_id: z.string(),
82
- gate_id: z.string(),
83
- promoted_by: z.string().optional().default("local_agent"),
84
- });
85
- const ReleaseCheckArgs = z.object({
86
- project_id: z.string(),
87
- changeset_id: z.string(),
88
- });
89
- const QueryMindArgs = z.object({
90
- team_id: z.string().optional().default("default"),
91
- context: z.string(),
92
- domain: z.string().optional(),
93
- });
94
- const ObserveArgs = z.object({
95
- team_id: z.string().optional().default("default"),
96
- domain: z.string(),
97
- observation_type: z.string(),
98
- content: z.string(),
99
- confidence: z.number().optional().default(0.8),
100
- tags: z.array(z.string()).optional().default([]),
101
- });
102
- const GetWorkflowArgs = z.object({
103
- initiative_id: z.string(),
104
- });
105
- const CreateProjectArgs = z.object({
106
- name: z.string().min(1),
107
- description: z.string().optional(),
108
- platform: z
109
- .enum(["web", "mobile", "api", "cli", "desktop", "other"])
110
- .optional(),
111
- });
112
- const QuickstartArgs = z.object({});
113
- const DiscoverArgs = z.object({
114
- languages: z.array(z.string()).optional().default([]),
115
- ci_tools: z.array(z.string()).optional().default([]),
116
- test_frameworks: z.array(z.string()).optional().default([]),
117
- team_size: z.number().optional(),
118
- repo_type: z.string().optional(),
119
- });
120
- const GetPresetsArgs = z.object({});
121
- const ConfigureProjectGatesArgs = z.object({
122
- project_id: z.string(),
123
- preset: z.string().optional(),
124
- custom_template: z.record(z.string(), z.unknown()).optional(),
125
- });
126
- const RecommendGatesArgs = z.object({
127
- project_id: z.string(),
128
- changeset_id: z.string(),
129
- });
130
- const ScorecardArgs = z.object({});
131
- const WorkflowTemplateArgs = z.object({
132
- template_id: z.string().optional(),
133
- });
134
- // ---------------------------------------------------------------------------
135
- // Handler factory — returns a bound handleTool for the given client.
136
- // Use createHandleTool(client) in the HTTP transport so each request uses
137
- // its own ForgeOSClient carrying the per-request API key.
138
- // The plain handleTool export exists for the stdio transport (uses the
139
- // module-level singleton).
140
- // ---------------------------------------------------------------------------
141
- export function createHandleTool(forgeClient) {
142
- return (name, rawArgs) => _handleTool(name, rawArgs, forgeClient);
143
- }
144
- export async function handleTool(name, rawArgs) {
145
- return _handleTool(name, rawArgs, defaultClient);
146
- }
147
- async function _handleTool(name, rawArgs, client) {
148
- switch (name) {
149
- // -----------------------------------------------------------------
150
- // forge_init — GET /api/bootstrap?project_id=...&developer_id=...
151
- // Also fetches GET /api/sandbox/scorecard to include setup status.
152
- // -----------------------------------------------------------------
153
- case "forge_init": {
154
- const a = InitArgs.parse(rawArgs);
155
- const [bootstrap, scorecard] = await Promise.allSettled([
156
- client.get("/api/bootstrap", {
157
- project_id: a.project_id,
158
- developer_id: a.developer_id,
159
- }),
160
- client.get("/api/sandbox/scorecard"),
161
- ]);
162
- const result = bootstrap.status === "fulfilled"
163
- ? bootstrap.value
164
- : { error: bootstrap.reason?.message };
165
- result.setup_scorecard =
166
- scorecard.status === "fulfilled"
167
- ? scorecard.value
168
- : { error: scorecard.reason?.message };
169
- return ok(result);
170
- }
171
- // -----------------------------------------------------------------
172
- // forge_create_initiative — POST /api/projects/{project_id}/initiatives
173
- // -----------------------------------------------------------------
174
- case "forge_create_initiative": {
175
- const a = CreateInitiativeArgs.parse(rawArgs);
176
- const data = await client.post(`/api/projects/${a.project_id}/initiatives`, { title: a.title, description: a.description, priority: a.priority });
177
- return ok(data);
178
- }
179
- // -----------------------------------------------------------------
180
- // forge_propose_changeset — POST /api/projects/{project_id}/changesets
181
- // After creating the changeset, also calls recommend-gates and merges
182
- // the result so the caller has the full risk/gate analysis in one shot.
183
- // -----------------------------------------------------------------
184
- case "forge_propose_changeset": {
185
- const a = ProposeChangesetArgs.parse(rawArgs);
186
- const body = {
187
- initiative_id: a.initiative_id,
188
- description: a.description,
189
- files_changed: a.files_changed,
190
- modules_affected: a.modules_affected,
191
- };
192
- if (a.branch)
193
- body.branch = a.branch;
194
- const changeset = (await client.post(`/api/projects/${a.project_id}/changesets`, body));
195
- // Extract changeset id — engine may return it at various keys
196
- const csId = changeset.id ??
197
- changeset.changeset_id;
198
- let recommendation = null;
199
- if (csId) {
200
- const rec = await (async () => {
201
- try {
202
- return await client.post(`/api/projects/${a.project_id}/changesets/${csId}/recommend-gates`);
203
- }
204
- catch {
205
- return null;
206
- }
207
- })();
208
- recommendation = rec;
209
- }
210
- return ok({ ...changeset, gate_recommendation: recommendation });
211
- }
212
- // -----------------------------------------------------------------
213
- // forge_get_profile
214
- // Base: GET /api/profiles/{role}
215
- // Contextualized: POST /api/profiles/contextualize
216
- // -----------------------------------------------------------------
217
- case "forge_get_profile": {
218
- const a = GetProfileArgs.parse(rawArgs);
219
- if (a.project_id) {
220
- // Contextualized profile with Shared Mind data
221
- const data = await client.post("/api/profiles/contextualize", {
222
- role: a.role,
223
- project_id: a.project_id,
224
- team_id: a.team_id,
225
- modules_affected: a.modules_affected || [],
226
- files_changed: a.files_changed || [],
227
- });
228
- return ok(data);
229
- }
230
- else {
231
- // Base profile (no project context)
232
- const data = await client.get(`/api/profiles/${a.role}`);
233
- return ok(data);
234
- }
235
- }
236
- // -----------------------------------------------------------------
237
- // forge_submit_evidence
238
- // POST /api/projects/{project_id}/changesets/{changeset_id}/evidence
239
- // -----------------------------------------------------------------
240
- case "forge_submit_evidence": {
241
- const a = SubmitEvidenceArgs.parse(rawArgs);
242
- const data = await client.post(`/api/projects/${a.project_id}/changesets/${a.changeset_id}/evidence`, {
243
- type: a.type,
244
- summary: a.summary,
245
- file_refs: a.file_refs,
246
- });
247
- return ok(data);
248
- }
249
- // -----------------------------------------------------------------
250
- // forge_submit_review
251
- // POST /api/projects/{project_id}/changesets/{changeset_id}/reviews/complete
252
- // -----------------------------------------------------------------
253
- case "forge_submit_review": {
254
- const a = SubmitReviewArgs.parse(rawArgs);
255
- const data = await client.post(`/api/projects/${a.project_id}/changesets/${a.changeset_id}/reviews/complete`, {
256
- role: a.role,
257
- status: a.status,
258
- notes: a.notes,
259
- findings: a.findings,
260
- reviewer_type: "ai_agent",
261
- reviewer_id: `local_agent_${a.role}`,
262
- });
263
- return ok(data);
264
- }
265
- // -----------------------------------------------------------------
266
- // forge_check_gates
267
- // GET /api/projects/{project_id}/changesets/{changeset_id}/gates
268
- // -----------------------------------------------------------------
269
- case "forge_check_gates": {
270
- const a = CheckGatesArgs.parse(rawArgs);
271
- const data = await client.get(`/api/projects/${a.project_id}/changesets/${a.changeset_id}/gates`);
272
- return ok(data);
273
- }
274
- // -----------------------------------------------------------------
275
- // forge_promote_gate
276
- // POST /api/projects/{project_id}/changesets/{changeset_id}/gates/{gate_id}/promote
277
- // -----------------------------------------------------------------
278
- case "forge_promote_gate": {
279
- const a = PromoteGateArgs.parse(rawArgs);
280
- const data = await client.post(`/api/projects/${a.project_id}/changesets/${a.changeset_id}/gates/${a.gate_id}/promote`, { promoted_by: a.promoted_by });
281
- return ok(data);
282
- }
283
- // -----------------------------------------------------------------
284
- // forge_release_check
285
- // GET /api/projects/{project_id}/changesets/{changeset_id}/release-check
286
- // -----------------------------------------------------------------
287
- case "forge_release_check": {
288
- const a = ReleaseCheckArgs.parse(rawArgs);
289
- const data = await client.get(`/api/projects/${a.project_id}/changesets/${a.changeset_id}/release-check`);
290
- return ok(data);
291
- }
292
- // -----------------------------------------------------------------
293
- // forge_query_mind
294
- // GET /api/shared-mind/{team_id}/query?context=...&domain=...
295
- // (shared_mind router has prefix="/api/shared-mind" in its own
296
- // APIRouter, registered without additional prefix at app level)
297
- // -----------------------------------------------------------------
298
- case "forge_query_mind": {
299
- const a = QueryMindArgs.parse(rawArgs);
300
- const params = { context: a.context };
301
- if (a.domain)
302
- params.domain = a.domain;
303
- const data = await client.get(`/api/shared-mind/${a.team_id}/query`, params);
304
- return ok(data);
305
- }
306
- // -----------------------------------------------------------------
307
- // forge_observe
308
- // POST /api/shared-mind/{team_id}/observe
309
- // -----------------------------------------------------------------
310
- case "forge_observe": {
311
- const a = ObserveArgs.parse(rawArgs);
312
- const data = await client.post(`/api/shared-mind/${a.team_id}/observe`, {
313
- domain: a.domain,
314
- source_agent: "forge_mcp_local",
315
- observation_type: a.observation_type,
316
- content: a.content,
317
- confidence: a.confidence,
318
- tags: a.tags,
319
- });
320
- return ok(data);
321
- }
322
- // -----------------------------------------------------------------
323
- // forge_get_workflow
324
- // GET /api/workflow/{initiative_id}
325
- // -----------------------------------------------------------------
326
- case "forge_get_workflow": {
327
- const a = GetWorkflowArgs.parse(rawArgs);
328
- const data = await client.get(`/api/workflow/${a.initiative_id}`);
329
- return ok(data);
330
- }
331
- // -----------------------------------------------------------------
332
- // forge_create_project — POST /api/projects
333
- // -----------------------------------------------------------------
334
- case "forge_create_project": {
335
- const a = CreateProjectArgs.parse(rawArgs);
336
- const body = { name: a.name };
337
- if (a.description)
338
- body.description = a.description;
339
- if (a.platform)
340
- body.platform = a.platform;
341
- const data = await client.post("/api/projects", body);
342
- return ok(data);
343
- }
344
- // -----------------------------------------------------------------
345
- // forge_quickstart — POST /api/sandbox/quickstart
346
- // -----------------------------------------------------------------
347
- case "forge_quickstart": {
348
- QuickstartArgs.parse(rawArgs);
349
- const data = await client.post("/api/sandbox/quickstart");
350
- return ok(data);
351
- }
352
- // -----------------------------------------------------------------
353
- // forge_discover — POST /api/sandbox/discover
354
- // -----------------------------------------------------------------
355
- case "forge_discover": {
356
- const a = DiscoverArgs.parse(rawArgs);
357
- const body = {
358
- languages: a.languages,
359
- ci_tools: a.ci_tools,
360
- test_frameworks: a.test_frameworks,
361
- };
362
- if (a.team_size !== undefined)
363
- body.team_size = a.team_size;
364
- if (a.repo_type !== undefined)
365
- body.repo_type = a.repo_type;
366
- const data = await client.post("/api/sandbox/discover", body);
367
- return ok(data);
368
- }
369
- // -----------------------------------------------------------------
370
- // forge_get_presets — GET /api/gate-presets
371
- // -----------------------------------------------------------------
372
- case "forge_get_presets": {
373
- GetPresetsArgs.parse(rawArgs);
374
- const data = await client.get("/api/gate-presets");
375
- return ok(data);
376
- }
377
- // -----------------------------------------------------------------
378
- // forge_configure_project_gates — PUT /api/projects/{id}/gate-template
379
- // -----------------------------------------------------------------
380
- case "forge_configure_project_gates": {
381
- const a = ConfigureProjectGatesArgs.parse(rawArgs);
382
- const body = {};
383
- if (a.preset !== undefined)
384
- body.preset = a.preset;
385
- if (a.custom_template !== undefined)
386
- body.custom_template = a.custom_template;
387
- const data = await client.put(`/api/projects/${a.project_id}/gate-template`, body);
388
- return ok(data);
389
- }
390
- // -----------------------------------------------------------------
391
- // forge_recommend_gates
392
- // POST /api/projects/{project_id}/changesets/{changeset_id}/recommend-gates
393
- // -----------------------------------------------------------------
394
- case "forge_recommend_gates": {
395
- const a = RecommendGatesArgs.parse(rawArgs);
396
- const data = await client.post(`/api/projects/${a.project_id}/changesets/${a.changeset_id}/recommend-gates`);
397
- return ok(data);
398
- }
399
- // -----------------------------------------------------------------
400
- // forge_scorecard — GET /api/sandbox/scorecard
401
- // -----------------------------------------------------------------
402
- case "forge_scorecard": {
403
- ScorecardArgs.parse(rawArgs);
404
- const data = await client.get("/api/sandbox/scorecard");
405
- return ok(data);
406
- }
407
- // -----------------------------------------------------------------
408
- // forge_workflow_template
409
- // GET /api/workflow-templates (all)
410
- // GET /api/workflow-templates/{id} (specific)
411
- // -----------------------------------------------------------------
412
- case "forge_workflow_template": {
413
- const a = WorkflowTemplateArgs.parse(rawArgs);
414
- const path = a.template_id
415
- ? `/api/workflow-templates/${a.template_id}`
416
- : "/api/workflow-templates";
417
- const data = await client.get(path);
418
- return ok(data);
419
- }
420
- default:
421
- return fail(`Unknown tool: ${name}`);
422
- }
423
- }
424
- //# sourceMappingURL=handlers.js.map