specguard-mcp 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -11,7 +11,7 @@ its prompt.
11
11
  SpecGuard is built [primarily for AI coding agents](https://github.com/yatfa-ai/specguard); this
12
12
  bridge is how an agent reaches it without scraping a web UI.
13
13
 
14
- > **Status: bootstrap.** Two tools ship today, each wrapping a capability that already exists. The
14
+ > **Status: bootstrap.** Three tools ship today, each wrapping a capability that already exists. The
15
15
  > toolset **grows gradually** — see [Adding a tool](#adding-a-tool). It is not published to npm yet;
16
16
  > install from a checkout.
17
17
 
@@ -32,14 +32,17 @@ refuses to boot and takes the tools that needed no configuration down with it.
32
32
 
33
33
  | Variable | Needed by | Default | What it is |
34
34
  | --- | --- | --- | --- |
35
- | `SPECGUARD_ENDPOINT` | `get_repository_overview` | — | your SpecGuard instance's root URL, **including the scheme** — e.g. `https://specguard.example.com`, or `http://localhost:3000`. A value with no scheme is refused by name (`SPECGUARD_ENDPOINT is not a usable URL: "sg.example.com"`) rather than surfacing later as an opaque failure. `SPECGUARD_URL` is accepted as an alias, and is the name every message uses when it is the one you set. A blank value counts as unset, so leaving `SPECGUARD_ENDPOINT` empty in a templated config falls through to `SPECGUARD_URL` instead of suppressing it |
35
+ | `SPECGUARD_ENDPOINT` | `get_repository_overview`, `list_repositories` | — | your SpecGuard instance's root URL, **including the scheme** — e.g. `https://specguard.example.com`, or `http://localhost:3000`. A value with no scheme is refused by name (`SPECGUARD_ENDPOINT is not a usable URL: "sg.example.com"`) rather than surfacing later as an opaque failure. `SPECGUARD_URL` is accepted as an alias, and is the name every message uses when it is the one you set. A blank value counts as unset, so leaving `SPECGUARD_ENDPOINT` empty in a templated config falls through to `SPECGUARD_URL` instead of suppressing it |
36
36
  | `SPECGUARD_API_KEY` | `get_repository_overview` | — | an agent/CI API key (`sgk_…`) issued by that deployment |
37
+ | `SPECGUARD_USER_API_KEY` | `list_repositories` | — | a **user** API key (`sgu_…`), minted from that deployment's account page. A different credential from the one above, not a second place to put the same value: SpecGuard decides which of them a request may use from the token's prefix, before it reads anything, and answers `401` for the other one. Set whichever your tools need — both, if you use both |
37
38
  | `SPECGUARD_LINT_COMMAND` | `lint_intent_annotations` | `specguard-lint` | the command that runs the linter. Most Ruby projects need `bundle exec specguard-lint` |
38
39
  | `SPECGUARD_TIMEOUT_MS` | HTTP tools | `30000` | how long a call to SpecGuard may take |
39
40
 
40
41
  `SPECGUARD_ENDPOINT` and `SPECGUARD_API_KEY` are the same variables
41
42
  [`specguard-rspec`](https://github.com/yatfa-ai/specguard-rspec) uses to ship a run, so a repository
42
- that already posts telemetry to SpecGuard already has them.
43
+ that already posts telemetry to SpecGuard already has them. `SPECGUARD_USER_API_KEY` is **not** one
44
+ of them — the gem has no notion of a user key — so that one is minted and set here for the first
45
+ time.
43
46
 
44
47
  Register it with your MCP client — for Claude Code:
45
48
 
@@ -52,6 +55,7 @@ Register it with your MCP client — for Claude Code:
52
55
  "env": {
53
56
  "SPECGUARD_ENDPOINT": "https://specguard.example.com",
54
57
  "SPECGUARD_API_KEY": "sgk_…",
58
+ "SPECGUARD_USER_API_KEY": "sgu_…",
55
59
  "SPECGUARD_LINT_COMMAND": "bundle exec specguard-lint"
56
60
  }
57
61
  }
@@ -297,6 +301,37 @@ a finding and not a disclosure someone forgot.
297
301
  Figures are `null` where CI did not report them. A `null` means *not measured*; it is never a zero,
298
302
  because a zero would read as a measurement that was taken.
299
303
 
304
+ ### `list_repositories`
305
+
306
+ Lists the SpecGuard repositories the person behind `SPECGUARD_USER_API_KEY` may open — *what can I
307
+ ask about*, which is the one question no other tool here can answer. `get_repository_overview` takes
308
+ no repository because its `sgk_…` key **is** the repository, so without this an agent can only report
309
+ on a repository somebody already named for it.
310
+
311
+ **This tool takes no arguments** — and not as an omission. The credential is the whole of the scope:
312
+ the endpoint takes no parameters, and which repositories are in the answer is decided by SpecGuard
313
+ from the person the key speaks for (owned, plus shared with them through a membership). A repository
314
+ they neither own nor were given access to never enters the response, so it cannot be filtered *in*
315
+ from this side either.
316
+
317
+ The body comes back as SpecGuard serves it — `{"repositories": […]}`, each entry carrying `id`,
318
+ `full_name`, `name`, `registered_at` and `role`, ordered by `full_name` ascending. The first four are
319
+ deliberately the same four fields, under the same names, that `get_repository_overview` serves in its
320
+ own `repository` block, so a client that reads one reads the other. `role` is `owner` or `member`:
321
+ the list mixes repositories this person owns with repositories somebody shared with them, and nothing
322
+ else tells them apart — read it before assuming a repository is one you may administer. An empty list
323
+ means no access, not an error.
324
+
325
+ **It reads a different key from `get_repository_overview`.** `SPECGUARD_USER_API_KEY` (`sgu_…`), not
326
+ `SPECGUARD_API_KEY` (`sgk_…`). SpecGuard refuses each credential in the other's place — the prefix
327
+ decides which table is consulted before any of them is read — so the two are not interchangeable and
328
+ setting one does not stand in for the other. Every message this tool produces names the variable
329
+ *it* reads, so a `401` here never sends you to check the key `get_repository_overview` uses.
330
+
331
+ Registering a repository, revoking keys and the rest of the user-scoped surface are not here yet.
332
+ `POST /api/v1/repositories` exists on the platform; what this bridge does not yet have is a way to
333
+ send a request body, and that arrives with the first tool that writes.
334
+
300
335
  ## How it works
301
336
 
302
337
  ```
@@ -306,11 +341,15 @@ agent ⇄ specguard-mcp ⇄ SpecGuard (HTTP, the same API the dashbo
306
341
 
307
342
  The bridge is a **thin client**: it shells out and it calls the API, and it re-implements neither.
308
343
  It carries no knowledge of the OpenTestIntent schema, holds no copy of the linter's rules, and
309
- reshapes no response — both tools return the shape of the capability they wrap, so a field added
344
+ reshapes no response — every tool returns the shape of the capability it wraps, so a field added
310
345
  upstream reaches the agent without a release here.
311
346
 
312
- Authorization and project scoping are enforced by SpecGuard, never by this bridge, using the same
313
- `sgk_…` keys CI uses to ingest runs. The bridge adds no credentials of its own and stores nothing.
347
+ Authorization and project scoping are enforced by SpecGuard, never by this bridge, using keys you
348
+ issue there — the same `sgk_…` keys CI uses to ingest runs, and, for the tools that answer to a
349
+ person rather than to a repository, an `sgu_…` user key. Which of the two a request may carry is
350
+ SpecGuard's decision and it is taken from the token's prefix, before any credential is looked up, so
351
+ the bridge cannot widen either one's reach: it forwards the key the tool's own variable holds and
352
+ reports what came back. It adds no credentials of its own and stores nothing.
314
353
 
315
354
  No argument ever reaches a shell: subprocesses are spawned with an argument list, so a path from a
316
355
  model is a path that does not exist rather than a command.
@@ -322,11 +361,13 @@ The toolset fills in as more of SpecGuard lands. Adding one is two mechanical ed
322
361
  1. a new file under `src/tools/` that default-exports a `ToolDefinition`;
323
362
  2. one entry appended to the array in `src/tools/index.ts`.
324
363
 
325
- There is no third — no third *wiring* edit, at least: every tool also earns a `### ` section with an
326
- argument table above, and every argument earns a row in that table, since this README ships as the
327
- package's published documentation, and `test/readme.test.ts` derives that obligation from the
328
- registry so a missing section or an undocumented parameter fails the suite. `src/server.ts` iterates
329
- that array and contains no per-tool code no `switch`, no hard-coded name and everything a tool
364
+ There is no third — no third *wiring* edit, at least: every tool also earns a `### ` section above,
365
+ and every argument earns a row in that section's table, since this README ships as the package's
366
+ published documentation, and `test/readme.test.ts` derives that obligation from the registry so a
367
+ missing section or an undocumented parameter fails the suite. A tool that genuinely takes no
368
+ arguments (`list_repositories` is the first) still earns the section, and is named in that file's
369
+ `ARGUMENT_LESS_TOOLS` — a deliberate line to add, rather than a floor relaxed for everyone.
370
+ `src/server.ts` iterates that array and contains no per-tool code — no `switch`, no hard-coded name — and everything a tool
330
371
  touches the world with (config, subprocesses, `fetch`) is injected, so a new tool is testable
331
372
  without a live deployment for free. The property tests in `test/tools/registry.test.ts` run over
332
373
  whatever the registry holds, so a tool added later is checked by tests written today.
@@ -26,6 +26,28 @@
26
26
  * too rather than left as a silent no-op for anyone who follows the ticket.
27
27
  * `SPECGUARD_ENDPOINT` wins when both are set and disagree, because it is the
28
28
  * one the rest of the toolchain is already reading.
29
+ *
30
+ * == TWO KEY VARIABLES, because SpecGuard has two credentials that refuse each
31
+ * == other
32
+ *
33
+ * `Api::BaseController` discriminates on the token's PREFIX *before any table is
34
+ * read*, and answers 401 on a mismatch without a lookup: `sgk_` names ONE
35
+ * repository (`GET /api/v1/repository`, `POST /api/v1/ingest`), `sgu_` names a
36
+ * PERSON (`GET /api/v1/repositories`). `UserApiKey::TOKEN_PREFIX` says the two
37
+ * prefixes are deliberately the same length so neither can be a prefix of the
38
+ * other — the mutual refusal is designed, not incidental.
39
+ *
40
+ * One variable therefore cannot serve both: whichever kind it holds, the tools
41
+ * needing the other kind 401. So there are two, read independently, and neither
42
+ * is required — an operator who only ever calls the repository tool sets only
43
+ * `SPECGUARD_API_KEY`, exactly as before this existed. Prefix-dispatching over a
44
+ * single variable was the alternative and it cannot work: an operator wanting
45
+ * both kinds of tool needs both keys present at once.
46
+ *
47
+ * Which variable a tool reads is then carried onto `ApiConfig` alongside the
48
+ * value — see `Credential` — for the same reason `endpointVariable` is: a
49
+ * diagnostic that names the wrong variable sends an operator to fix something
50
+ * they never set.
29
51
  */
30
52
  export interface Config {
31
53
  /** SpecGuard deployment root, trailing slash stripped. `undefined` when unset. */
@@ -40,8 +62,18 @@ export interface Config {
40
62
  * speak the one that was used.
41
63
  */
42
64
  readonly endpointVariable: EndpointVariable | undefined;
43
- /** An `sgk_…` API key. `undefined` when unset. */
65
+ /** An `sgk_…` repository API key, from `SPECGUARD_API_KEY`. `undefined` when unset. */
44
66
  readonly apiKey: string | undefined;
67
+ /**
68
+ * An `sgu_…` user API key, from `SPECGUARD_USER_API_KEY`. `undefined` when unset.
69
+ *
70
+ * A SECOND slot rather than a second meaning for the first one — see the note
71
+ * at the top of this file. It is minted from the account page
72
+ * (`/account`) and speaks for a person; the repository key is minted per
73
+ * repository and speaks for one repository. Neither deployment endpoint will
74
+ * accept the other's token, so the two values live in two places here too.
75
+ */
76
+ readonly userApiKey: string | undefined;
45
77
  /**
46
78
  * The command that runs the `@intent` linter, already tokenised.
47
79
  *
@@ -76,27 +108,85 @@ export interface ApiConfig {
76
108
  */
77
109
  readonly endpointVariable: EndpointVariable;
78
110
  readonly apiKey: string;
111
+ /**
112
+ * WHICH credential `apiKey` is — the variable it was read from, the prefix
113
+ * that variable is expected to hold, and how to say both in a sentence.
114
+ *
115
+ * Exactly the treatment `endpointVariable` above gets, applied to the other
116
+ * half, and for the same reason. `describeFailure` in
117
+ * `support/specguard-api.ts` used to hardcode "SPECGUARD_API_KEY must be an
118
+ * sgk_… key … keys are per-repository" into every 401, so the first
119
+ * user-scoped tool routed through `getJson` would have inherited three
120
+ * sentences that are all false of it — naming a variable its operator may
121
+ * never have set. Carrying the answer rather than re-deriving it per tool is
122
+ * what makes the next credential-scoped tool inherit correct naming the same
123
+ * way it inherits the URL check.
124
+ */
125
+ readonly credential: Credential;
79
126
  readonly requestTimeoutMs: number;
80
127
  }
81
128
  export type EndpointVariable = "SPECGUARD_ENDPOINT" | "SPECGUARD_URL";
129
+ export type ApiKeyVariable = "SPECGUARD_API_KEY" | "SPECGUARD_USER_API_KEY";
130
+ /**
131
+ * One of SpecGuard's two credential kinds, described well enough that a message
132
+ * about it can be written without knowing which one it is.
133
+ *
134
+ * The two prose fields are sentence FRAGMENTS rather than whole messages on
135
+ * purpose: the surrounding wording — "is not set in the MCP server's
136
+ * environment", "SpecGuard rejected the API key (401)" — is the same for both
137
+ * kinds and is written once, at the site that knows the situation. Only the
138
+ * parts that genuinely differ between an `sgk_` key and an `sgu_` key live
139
+ * here.
140
+ */
141
+ export interface Credential {
142
+ /** The environment variable this kind of key is read from. */
143
+ readonly variable: ApiKeyVariable;
144
+ /** The prefix SpecGuard requires of it, checked before any table is read. */
145
+ readonly prefix: "sgk_" | "sgu_";
146
+ /** Completes "… issued from ___" in a message about the variable being unset. */
147
+ readonly issuedFrom: string;
148
+ /** Completes "… issued by <deployment> ___" in a message about a 401. */
149
+ readonly rejection: string;
150
+ }
82
151
  /**
83
- * Both halves or a legible failure never one half and a surprise later.
152
+ * The `sgk_` key: one repository, and the same variable CI already sets.
84
153
  *
85
- * Reported together rather than one at a time: an operator who set neither
86
- * should learn that in one round trip instead of fixing a variable, re-calling,
87
- * and being told about the next one.
154
+ * The 401 wording is unchanged from when this was the only credential — it is
155
+ * accurate about this kind, and the reason a second kind exists is precisely
156
+ * that it was never accurate about the other.
157
+ */
158
+ export declare const REPOSITORY_CREDENTIAL: Credential;
159
+ /**
160
+ * The `sgu_` key: a person, and a variable nothing else in the toolchain reads.
88
161
  *
89
- * The endpoint is also PARSED here, not merely counted as present. It is spent
90
- * later inside `new URL(...)` in the HTTP client, where a malformed value throws
91
- * a bare `TypeError` — which is not a `SpecGuardMcpError`, so the server's error
92
- * boundary reads it as a defect and tells the agent "this is a bug in the
93
- * bridge, not in your project or configuration". For the commonest config typo
94
- * there is (omitting `https://`) that sentence is the exact opposite of the
95
- * truth, and it sends an agent looking in the one place the problem is not.
96
- * Validating here rather than at the call site is deliberate: every HTTP-backed
97
- * tool added later comes through this function and inherits the check.
162
+ * `specguard-rspec` ships runs with an `sgk_` key and has no notion of this one,
163
+ * so an operator who already has CI reporting to SpecGuard does NOT already
164
+ * have this variable — which is why the message says where to mint one rather
165
+ * than assuming it is lying around.
166
+ */
167
+ export declare const USER_CREDENTIAL: Credential;
168
+ /**
169
+ * What a tool needing the `sgk_` REPOSITORY key requires the endpoint and
170
+ * `SPECGUARD_API_KEY`.
171
+ *
172
+ * Unchanged in name and in behaviour: every tool that reached the deployment
173
+ * before this file grew a second credential calls exactly this, and gets exactly
174
+ * what it got.
98
175
  */
99
176
  export declare function requireApiConfig(config: Config): ApiConfig;
177
+ /**
178
+ * What a tool needing the `sgu_` USER key requires — the endpoint and
179
+ * `SPECGUARD_USER_API_KEY`.
180
+ *
181
+ * A sibling rather than a flag on `requireApiConfig`, which is the shape
182
+ * `loadConfig`'s note at the top of this file already sanctioned: a tool asks
183
+ * for what IT needs, so a tool needing neither key is unaffected and startup
184
+ * still validates nothing. The two differ only in which value and which
185
+ * `Credential` they pass to the one implementation below, so a fix to the
186
+ * diagnostics reaches both and they cannot drift into describing the same
187
+ * situation differently.
188
+ */
189
+ export declare function requireUserApiConfig(config: Config): ApiConfig;
100
190
  /**
101
191
  * Splits a configured command into argv WITHOUT a shell, honouring single and
102
192
  * double quotes so a path with a space survives.
@@ -24,21 +24,81 @@ export function loadConfig(env = process.env) {
24
24
  endpoint: endpointVariable === undefined ? undefined : normaliseEndpoint(env[endpointVariable]),
25
25
  endpointVariable,
26
26
  apiKey: presence(env["SPECGUARD_API_KEY"]),
27
+ userApiKey: presence(env["SPECGUARD_USER_API_KEY"]),
27
28
  lintCommand: lintCommand.length > 0 ? lintCommand : DEFAULT_LINT_COMMAND,
28
29
  requestTimeoutMs: positiveInteger(env["SPECGUARD_TIMEOUT_MS"]) ?? DEFAULT_REQUEST_TIMEOUT_MS,
29
30
  };
30
31
  }
32
+ /**
33
+ * The `sgk_` key: one repository, and the same variable CI already sets.
34
+ *
35
+ * The 401 wording is unchanged from when this was the only credential — it is
36
+ * accurate about this kind, and the reason a second kind exists is precisely
37
+ * that it was never accurate about the other.
38
+ */
39
+ export const REPOSITORY_CREDENTIAL = {
40
+ variable: "SPECGUARD_API_KEY",
41
+ prefix: "sgk_",
42
+ issuedFrom: "issued from its API keys page",
43
+ rejection: "for the repository you are asking about — keys are per-repository, and a " +
44
+ "revoked key reads the same as a wrong one",
45
+ };
46
+ /**
47
+ * The `sgu_` key: a person, and a variable nothing else in the toolchain reads.
48
+ *
49
+ * `specguard-rspec` ships runs with an `sgk_` key and has no notion of this one,
50
+ * so an operator who already has CI reporting to SpecGuard does NOT already
51
+ * have this variable — which is why the message says where to mint one rather
52
+ * than assuming it is lying around.
53
+ */
54
+ export const USER_CREDENTIAL = {
55
+ variable: "SPECGUARD_USER_API_KEY",
56
+ prefix: "sgu_",
57
+ issuedFrom: "issued from your account page",
58
+ rejection: "for your own SpecGuard account — a user key speaks for a person and lists what " +
59
+ "that person may open, an sgk_… repository key is refused here without a lookup, " +
60
+ "and a revoked key reads the same as a wrong one",
61
+ };
31
62
  /**
32
63
  * The name to speak when no variable was set at all — the message is telling
33
64
  * someone to set one, and this is the spelling the rest of the toolchain reads.
34
65
  */
35
66
  const DEFAULT_ENDPOINT_VARIABLE = "SPECGUARD_ENDPOINT";
67
+ /**
68
+ * What a tool needing the `sgk_` REPOSITORY key requires — the endpoint and
69
+ * `SPECGUARD_API_KEY`.
70
+ *
71
+ * Unchanged in name and in behaviour: every tool that reached the deployment
72
+ * before this file grew a second credential calls exactly this, and gets exactly
73
+ * what it got.
74
+ */
75
+ export function requireApiConfig(config) {
76
+ return requireCredentialledApiConfig(config, config.apiKey, REPOSITORY_CREDENTIAL);
77
+ }
78
+ /**
79
+ * What a tool needing the `sgu_` USER key requires — the endpoint and
80
+ * `SPECGUARD_USER_API_KEY`.
81
+ *
82
+ * A sibling rather than a flag on `requireApiConfig`, which is the shape
83
+ * `loadConfig`'s note at the top of this file already sanctioned: a tool asks
84
+ * for what IT needs, so a tool needing neither key is unaffected and startup
85
+ * still validates nothing. The two differ only in which value and which
86
+ * `Credential` they pass to the one implementation below, so a fix to the
87
+ * diagnostics reaches both and they cannot drift into describing the same
88
+ * situation differently.
89
+ */
90
+ export function requireUserApiConfig(config) {
91
+ return requireCredentialledApiConfig(config, config.userApiKey, USER_CREDENTIAL);
92
+ }
36
93
  /**
37
94
  * Both halves or a legible failure — never one half and a surprise later.
38
95
  *
39
96
  * Reported together rather than one at a time: an operator who set neither
40
97
  * should learn that in one round trip instead of fixing a variable, re-calling,
41
- * and being told about the next one.
98
+ * and being told about the next one. That property is why the two `require*`
99
+ * entry points share this body rather than each doing their own presence check:
100
+ * "together" has to mean the endpoint AND whichever key the calling tool needs,
101
+ * and a per-tool check would report them one at a time again.
42
102
  *
43
103
  * The endpoint is also PARSED here, not merely counted as present. It is spent
44
104
  * later inside `new URL(...)` in the HTTP client, where a malformed value throws
@@ -49,25 +109,36 @@ const DEFAULT_ENDPOINT_VARIABLE = "SPECGUARD_ENDPOINT";
49
109
  * truth, and it sends an agent looking in the one place the problem is not.
50
110
  * Validating here rather than at the call site is deliberate: every HTTP-backed
51
111
  * tool added later comes through this function and inherits the check.
112
+ *
113
+ * The KEY is deliberately NOT validated against `credential.prefix` here. The
114
+ * deployment is the authority on whether a token is acceptable — it checks the
115
+ * prefix, then the digest, then whether the key is revoked — and a second,
116
+ * weaker copy of the first third of that rule on this side would refuse a token
117
+ * SpecGuard would have accepted the moment the platform mints a third prefix.
118
+ * The prefix is carried so a MESSAGE can name it, not so this file can enforce
119
+ * it; the 401 branch of `describeFailure` is where a wrong-kind key is
120
+ * diagnosed, with the deployment's own verdict in hand.
52
121
  */
53
- export function requireApiConfig(config) {
122
+ function requireCredentialledApiConfig(config, apiKey, credential) {
54
123
  const endpointVariable = config.endpointVariable ?? DEFAULT_ENDPOINT_VARIABLE;
55
124
  const missing = [];
56
125
  if (config.endpoint === undefined)
57
126
  missing.push(endpointVariable);
58
- if (config.apiKey === undefined)
59
- missing.push("SPECGUARD_API_KEY");
127
+ if (apiKey === undefined)
128
+ missing.push(credential.variable);
60
129
  if (missing.length > 0) {
61
130
  throw new ConfigError(`This tool talks to a SpecGuard deployment, and ${missing.join(" and ")} ` +
62
131
  `${missing.length === 1 ? "is" : "are"} not set in the MCP server's environment. ` +
63
132
  "Set them in your MCP client's server config " +
64
- `(${endpointVariable} is your deployment's root URL, SPECGUARD_API_KEY an sgk_… key ` +
65
- "issued from its API keys page). Tools that do not reach the deployment are unaffected.");
133
+ `(${endpointVariable} is your deployment's root URL, ${credential.variable} ` +
134
+ `an ${credential.prefix}… key ${credential.issuedFrom}). ` +
135
+ "Tools that do not reach the deployment are unaffected.");
66
136
  }
67
137
  return {
68
138
  endpoint: requireHttpUrl(config.endpoint, endpointVariable),
69
139
  endpointVariable,
70
- apiKey: config.apiKey,
140
+ apiKey: apiKey,
141
+ credential,
71
142
  requestTimeoutMs: config.requestTimeoutMs,
72
143
  };
73
144
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA4F1C,MAAM,CAAC,MAAM,oBAAoB,GAAsB,CAAC,gBAAgB,CAAC,CAAC;AAC1E,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAEjD,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAC5D,6EAA6E;IAC7E,qEAAqE;IACrE,4EAA4E;IAC5E,8EAA8E;IAC9E,0EAA0E;IAC1E,6EAA6E;IAC7E,8EAA8E;IAC9E,8EAA8E;IAC9E,6EAA6E;IAC7E,mEAAmE;IACnE,8EAA8E;IAC9E,MAAM,gBAAgB,GACpB,QAAQ,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,KAAK,SAAS;QAC/C,CAAC,CAAC,oBAAoB;QACtB,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,KAAK,SAAS;YAC5C,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,SAAS,CAAC;IAElB,OAAO;QACL,QAAQ,EAAE,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC/F,gBAAgB;QAChB,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC1C,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QACnD,WAAW,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,oBAAoB;QACxE,gBAAgB,EAAE,eAAe,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,IAAI,0BAA0B;KAC7F,CAAC;AACJ,CAAC;AA+DD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAe;IAC/C,QAAQ,EAAE,mBAAmB;IAC7B,MAAM,EAAE,MAAM;IACd,UAAU,EAAE,+BAA+B;IAC3C,SAAS,EACP,2EAA2E;QAC3E,2CAA2C;CAC9C,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAe;IACzC,QAAQ,EAAE,wBAAwB;IAClC,MAAM,EAAE,MAAM;IACd,UAAU,EAAE,+BAA+B;IAC3C,SAAS,EACP,iFAAiF;QACjF,kFAAkF;QAClF,iDAAiD;CACpD,CAAC;AAEF;;;GAGG;AACH,MAAM,yBAAyB,GAAqB,oBAAoB,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,OAAO,6BAA6B,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;AACrF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,OAAO,6BAA6B,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AACnF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,SAAS,6BAA6B,CACpC,MAAc,EACd,MAA0B,EAC1B,UAAsB;IAEtB,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,yBAAyB,CAAC;IAE9E,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAClE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE5D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,WAAW,CACnB,kDAAkD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG;YACxE,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,4CAA4C;YAClF,8CAA8C;YAC9C,IAAI,gBAAgB,mCAAmC,UAAU,CAAC,QAAQ,GAAG;YAC7E,MAAM,UAAU,CAAC,MAAM,SAAS,UAAU,CAAC,UAAU,KAAK;YAC1D,wDAAwD,CAC3D,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,cAAc,CAAC,MAAM,CAAC,QAAkB,EAAE,gBAAgB,CAAC;QACrE,gBAAgB;QAChB,MAAM,EAAE,MAAgB;QACxB,UAAU;QACV,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;KAC1C,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,cAAc,CAAC,QAAgB,EAAE,IAAsB;IAC9D,IAAI,MAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,SAAS,CAAC;IACrB,CAAC;IAED,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,EAAE,CAAC;QAC1F,MAAM,IAAI,WAAW,CACnB,GAAG,IAAI,yBAAyB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,8BAA8B;YACpF,2DAA2D;YAC3D,kFAAkF;YAClF,iDAAiD,IAAI,+BAA+B;YACpF,gEAAgE,CACnE,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,GAAuB;IAChD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACrE,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,QAAQ,CAAC,GAAuB;IACvC,MAAM,OAAO,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC;IAC5B,OAAO,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;AACvE,CAAC;AAED,SAAS,eAAe,CAAC,GAAuB;IAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,OAAO,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACtE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAuB;IAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAEnC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,KAA4B,CAAC;IACjC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,IAAI,KAAK,KAAK;gBAAE,KAAK,GAAG,SAAS,CAAC;;gBACjC,OAAO,IAAI,IAAI,CAAC;YACrB,SAAS;QACX,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC;YACb,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpB,IAAI,OAAO;gBAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,GAAG,KAAK,CAAC;YAChB,SAAS;QACX,CAAC;QAED,OAAO,IAAI,IAAI,CAAC;QAChB,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IAED,IAAI,OAAO;QAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAElC,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  export { createServer, SERVER_NAME, SERVER_VERSION, type CreateServerOptions } from "./server.js";
2
- export { loadConfig, requireApiConfig, tokenise, type ApiConfig, type Config } from "./config.js";
2
+ export { loadConfig, requireApiConfig, requireUserApiConfig, tokenise, type ApiConfig, type ApiKeyVariable, type Config, type Credential, } from "./config.js";
3
3
  export { ApiError, ArgumentError, CommandError, ConfigError, SpecGuardMcpError } from "./errors.js";
4
4
  export { TOOLS } from "./tools/index.js";
5
5
  export type { ToolContext, ToolDefinition, ToolResult } from "./tools/types.js";
package/dist/src/index.js CHANGED
@@ -1,5 +1,10 @@
1
1
  export { createServer, SERVER_NAME, SERVER_VERSION } from "./server.js";
2
- export { loadConfig, requireApiConfig, tokenise } from "./config.js";
2
+ // `requireUserApiConfig` is re-exported beside `requireApiConfig` rather than
3
+ // left behind it: the two are one seam with two credentials, and a consumer that
4
+ // can name only half of it would have to deep-import past this entrypoint to
5
+ // reach the other — the same unnameable-but-typed state `test/index.test.ts`
6
+ // exists to keep out of the error taxonomy.
7
+ export { loadConfig, requireApiConfig, requireUserApiConfig, tokenise, } from "./config.js";
3
8
  export { ApiError, ArgumentError, CommandError, ConfigError, SpecGuardMcpError } from "./errors.js";
4
9
  export { TOOLS } from "./tools/index.js";
5
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAA4B,MAAM,aAAa,CAAC;AAClG,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAA+B,MAAM,aAAa,CAAC;AAClG,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAA4B,MAAM,aAAa,CAAC;AAClG,8EAA8E;AAC9E,iFAAiF;AACjF,6EAA6E;AAC7E,6EAA6E;AAC7E,4CAA4C;AAC5C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,oBAAoB,EACpB,QAAQ,GAKT,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { requireApiConfig, type ApiConfig } from "../config.js";
1
+ import { requireApiConfig, requireUserApiConfig, type ApiConfig } from "../config.js";
2
2
  /**
3
3
  * The SpecGuard HTTP client — a Bearer key and a path, and nothing else.
4
4
  *
@@ -8,4 +8,22 @@ import { requireApiConfig, type ApiConfig } from "../config.js";
8
8
  * second place for the permission model to be got wrong.
9
9
  */
10
10
  export declare function getJson(api: ApiConfig, path: string, query: Record<string, string | undefined>, fetchImpl: typeof globalThis.fetch): Promise<unknown>;
11
- export { requireApiConfig };
11
+ /**
12
+ * `getJson`, narrowed to the object every tool here actually asks it for.
13
+ *
14
+ * MCP hands a tool result back as an object, so an array or a bare JSON scalar
15
+ * is not something a tool can pass through: it surfaces as a protocol error
16
+ * rather than as something the agent can read. Every HTTP tool therefore
17
+ * needed the same three-clause guard, and the same sentence, immediately after
18
+ * its own `getJson` call — which made both the check and its wording the one
19
+ * thing each new tool had to remember to write for itself, and get identical.
20
+ *
21
+ * It belongs here for the reason `requireApiConfig` parses the endpoint rather
22
+ * than leaving that to callers: every HTTP-backed tool added later comes
23
+ * through this function and inherits the check, the same way it inherits the
24
+ * URL check and the 401 wording. `getJson` stays exported un-narrowed for an
25
+ * endpoint that legitimately serves an array — the point is not that objects
26
+ * are the only legal body, it is that no tool re-types this guard.
27
+ */
28
+ export declare function getJsonObject(api: ApiConfig, path: string, query: Record<string, string | undefined>, fetchImpl: typeof globalThis.fetch): Promise<Record<string, unknown>>;
29
+ export { requireApiConfig, requireUserApiConfig };
@@ -1,4 +1,4 @@
1
- import { requireApiConfig } from "../config.js";
1
+ import { requireApiConfig, requireUserApiConfig } from "../config.js";
2
2
  import { ApiError } from "../errors.js";
3
3
  /**
4
4
  * The SpecGuard HTTP client — a Bearer key and a path, and nothing else.
@@ -26,6 +26,30 @@ export async function getJson(api, path, query, fetchImpl) {
26
26
  "or login page.", response.status);
27
27
  }
28
28
  }
29
+ /**
30
+ * `getJson`, narrowed to the object every tool here actually asks it for.
31
+ *
32
+ * MCP hands a tool result back as an object, so an array or a bare JSON scalar
33
+ * is not something a tool can pass through: it surfaces as a protocol error
34
+ * rather than as something the agent can read. Every HTTP tool therefore
35
+ * needed the same three-clause guard, and the same sentence, immediately after
36
+ * its own `getJson` call — which made both the check and its wording the one
37
+ * thing each new tool had to remember to write for itself, and get identical.
38
+ *
39
+ * It belongs here for the reason `requireApiConfig` parses the endpoint rather
40
+ * than leaving that to callers: every HTTP-backed tool added later comes
41
+ * through this function and inherits the check, the same way it inherits the
42
+ * URL check and the 401 wording. `getJson` stays exported un-narrowed for an
43
+ * endpoint that legitimately serves an array — the point is not that objects
44
+ * are the only legal body, it is that no tool re-types this guard.
45
+ */
46
+ export async function getJsonObject(api, path, query, fetchImpl) {
47
+ const body = await getJson(api, path, query, fetchImpl);
48
+ if (typeof body !== "object" || body === null || Array.isArray(body)) {
49
+ throw new ApiError("SpecGuard returned a JSON value that was not an object.");
50
+ }
51
+ return body;
52
+ }
29
53
  /**
30
54
  * Tells "the deadline won the race" apart from any value a phase could produce.
31
55
  *
@@ -140,12 +164,22 @@ function timedOut(api) {
140
164
  * hit, and because SpecGuard answers it deliberately flat — "a valid Bearer API
141
165
  * key is required", with no detail about why — so the useful half of the
142
166
  * diagnosis has to be supplied from this side.
167
+ *
168
+ * WHICH VARIABLE AND WHICH PREFIX ARE READ OFF `api.credential`, never spelled
169
+ * out here. SpecGuard has two credential kinds that refuse each other's tokens
170
+ * before any table is read, so this one branch is reached by tools reading two
171
+ * different variables — and the sentence it used to hardcode ("SPECGUARD_API_KEY
172
+ * must be an sgk_… key … keys are per-repository") is false in all three of its
173
+ * claims for a user-scoped tool, naming a variable its operator may never have
174
+ * touched. That is the same defect `endpointVariable` fixes one branch down, and
175
+ * it gets the same remedy rather than a second hardcoded string: a tool added
176
+ * later inherits correct naming from the `require*` helper it already calls.
143
177
  */
144
178
  function describeFailure(status, body, api) {
145
179
  if (status === 401) {
146
- return new ApiError("SpecGuard rejected the API key (401). SPECGUARD_API_KEY must be an sgk_… key issued by " +
147
- `${api.endpoint} for the repository you are asking about keys are per-repository, and a ` +
148
- "revoked key reads the same as a wrong one.", status);
180
+ const { variable, prefix, rejection } = api.credential;
181
+ return new ApiError(`SpecGuard rejected the API key (401). ${variable} must be an ${prefix}… key issued by ` +
182
+ `${api.endpoint} ${rejection}.`, status);
149
183
  }
150
184
  if (status === 404) {
151
185
  return new ApiError(`${api.endpoint} has no such endpoint (404). Check that ${api.endpointVariable} is the ` +
@@ -153,5 +187,5 @@ function describeFailure(status, body, api) {
153
187
  }
154
188
  return new ApiError(`SpecGuard answered ${status}${body.trim() === "" ? "" : `: ${body.trim().slice(0, 500)}`}`, status);
155
189
  }
156
- export { requireApiConfig };
190
+ export { requireApiConfig, requireUserApiConfig };
157
191
  //# sourceMappingURL=specguard-api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"specguard-api.js","sourceRoot":"","sources":["../../../src/support/specguard-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"specguard-api.js","sourceRoot":"","sources":["../../../src/support/specguard-api.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,18 @@ import type { ToolDefinition } from "./types.js";
20
20
  * the two halves of the surface — one local subprocess, one authenticated HTTP
21
21
  * call — so the shape is proven on both kinds of capability rather than on one.
22
22
  *
23
+ * == The third: the first tool that reads the OTHER credential
24
+ *
25
+ * - `list_repositories` wraps `GET /api/v1/repositories` (shipped:
26
+ * `specguard/config/routes.rb`, `Api::V1::UserRepositoriesController`).
27
+ *
28
+ * It is the only tool here that answers to an `sgu_` USER key rather than an
29
+ * `sgk_` repository key, and SpecGuard refuses each credential in the other's
30
+ * place before it reads a table. So this entry is also what proves the second
31
+ * variable, the second `require*` helper and the credential-aware diagnostics
32
+ * work end to end — one tool, over a real endpoint, rather than a seam nothing
33
+ * exercises.
34
+ *
23
35
  * == What is deliberately absent
24
36
  *
25
37
  * `/check-intent` and duplicate clustering are NOT here and must not be added
@@ -28,6 +40,14 @@ import type { ToolDefinition } from "./types.js";
28
40
  * endpoint that does not exist would produce a server that discovers cleanly
29
41
  * and fails on use, which is worse than not offering the tool, because the
30
42
  * agent has already committed to a plan by the time it finds out.
43
+ *
44
+ * The user-scoped WRITE endpoints are absent for a different reason, and it is
45
+ * worth stating so nobody re-derives the wrong one: `POST /api/v1/repositories`
46
+ * exists on the platform today. What this bridge does not have is a way to call
47
+ * it — `support/specguard-api.ts` offers `getJson`, which hardcodes
48
+ * `method: "GET"` and takes no body. That transport lands with the first write
49
+ * tool, designed against a real request body and a real 4xx surface, rather
50
+ * than being invented here for a tool that does not yet exist.
31
51
  */
32
52
  export declare const TOOLS: readonly ToolDefinition[];
33
53
  export type { ToolContext, ToolDefinition, ToolResult } from "./types.js";
@@ -1,4 +1,5 @@
1
1
  import lintIntentAnnotations from "./lint-intent-annotations.js";
2
+ import listRepositories from "./list-repositories.js";
2
3
  import getRepositoryOverview from "./repository-overview.js";
3
4
  /**
4
5
  * THE REGISTRY — the one file that changes when the toolset grows.
@@ -21,6 +22,18 @@ import getRepositoryOverview from "./repository-overview.js";
21
22
  * the two halves of the surface — one local subprocess, one authenticated HTTP
22
23
  * call — so the shape is proven on both kinds of capability rather than on one.
23
24
  *
25
+ * == The third: the first tool that reads the OTHER credential
26
+ *
27
+ * - `list_repositories` wraps `GET /api/v1/repositories` (shipped:
28
+ * `specguard/config/routes.rb`, `Api::V1::UserRepositoriesController`).
29
+ *
30
+ * It is the only tool here that answers to an `sgu_` USER key rather than an
31
+ * `sgk_` repository key, and SpecGuard refuses each credential in the other's
32
+ * place before it reads a table. So this entry is also what proves the second
33
+ * variable, the second `require*` helper and the credential-aware diagnostics
34
+ * work end to end — one tool, over a real endpoint, rather than a seam nothing
35
+ * exercises.
36
+ *
24
37
  * == What is deliberately absent
25
38
  *
26
39
  * `/check-intent` and duplicate clustering are NOT here and must not be added
@@ -29,6 +42,18 @@ import getRepositoryOverview from "./repository-overview.js";
29
42
  * endpoint that does not exist would produce a server that discovers cleanly
30
43
  * and fails on use, which is worse than not offering the tool, because the
31
44
  * agent has already committed to a plan by the time it finds out.
45
+ *
46
+ * The user-scoped WRITE endpoints are absent for a different reason, and it is
47
+ * worth stating so nobody re-derives the wrong one: `POST /api/v1/repositories`
48
+ * exists on the platform today. What this bridge does not have is a way to call
49
+ * it — `support/specguard-api.ts` offers `getJson`, which hardcodes
50
+ * `method: "GET"` and takes no body. That transport lands with the first write
51
+ * tool, designed against a real request body and a real 4xx surface, rather
52
+ * than being invented here for a tool that does not yet exist.
32
53
  */
33
- export const TOOLS = [lintIntentAnnotations, getRepositoryOverview];
54
+ export const TOOLS = [
55
+ lintIntentAnnotations,
56
+ getRepositoryOverview,
57
+ listRepositories,
58
+ ];
34
59
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,MAAM,8BAA8B,CAAC;AACjE,OAAO,qBAAqB,MAAM,0BAA0B,CAAC;AAG7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,MAAM,KAAK,GAA8B,CAAC,qBAAqB,EAAE,qBAAqB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,MAAM,8BAA8B,CAAC;AACjE,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AACtD,OAAO,qBAAqB,MAAM,0BAA0B,CAAC;AAG7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,CAAC,MAAM,KAAK,GAA8B;IAC9C,qBAAqB;IACrB,qBAAqB;IACrB,gBAAgB;CACjB,CAAC"}
@@ -0,0 +1,68 @@
1
+ import type { ToolDefinition } from "./types.js";
2
+ /**
3
+ * `GET /api/v1/repositories` as a tool — shipped today in the platform
4
+ * (`specguard/config/routes.rb`, `Api::V1::UserRepositoriesController#index`).
5
+ *
6
+ * == Why this is the first user-scoped tool, and for now the only one
7
+ *
8
+ * Every other tool here answers about ONE repository the caller has already
9
+ * named — and a bridge that can only answer about a repository you can already
10
+ * name cannot tell an agent which repositories there ARE. `get_repository_overview`
11
+ * does not take a repository: its `sgk_` key IS the repository, so the question
12
+ * "what may I ask about" has no answer anywhere in this server. This is that
13
+ * answer, and it is the whole of what this tool does.
14
+ *
15
+ * It is also the tool that proves the second credential slot works end to end,
16
+ * which is why it ships alone. The registry's standing rule (`tools/index.ts`)
17
+ * is that a tool in `tools/list` is a promise an agent acts on, so the other
18
+ * user-scoped endpoints wait for the transport they need: `POST /api/v1/repositories`
19
+ * EXISTS on the platform today, and `getJson` hardcodes `method: "GET"` and
20
+ * takes no body, so registering a repository is blocked on a write transport
21
+ * rather than on a missing endpoint. That transport belongs with the first write
22
+ * tool, where it can be designed against a real body and a real 4xx surface.
23
+ *
24
+ * == It reads the OTHER key, and that is the point
25
+ *
26
+ * `Api::BaseController` decides which credential table to consult from the
27
+ * token's PREFIX, before any table is read, and answers 401 on a mismatch
28
+ * without a lookup — so this endpoint refuses the `sgk_` key
29
+ * `get_repository_overview` uses, and that one refuses this key. Hence
30
+ * `requireUserApiConfig` rather than `requireApiConfig`: the two are the same
31
+ * function over different variables, and the `Credential` each carries is what
32
+ * makes a 401 or an unset variable name the one the OPERATOR of this tool has
33
+ * to go and fix. See `config.ts`.
34
+ *
35
+ * == No arguments, because the credential is the whole question
36
+ *
37
+ * The endpoint takes no parameters: the person the `sgu_` key speaks for is the
38
+ * entire scope of the answer, and `Repository.accessible_by` — owned UNION
39
+ * shared-through-a-membership — is the platform's read-side boundary rather
40
+ * than a filter this bridge could widen or narrow. A repository the person
41
+ * neither owns nor is a member of never enters the response, so there is
42
+ * nothing here for a parameter to select and nothing an argument could reach.
43
+ *
44
+ * == The response is passed through, not re-modelled
45
+ *
46
+ * Same rule as every other tool here (`types.ts`: "A thin client that reshapes
47
+ * its upstream is not thin"), and it has real content on this body. The
48
+ * controller serves each entry as `id`, `full_name`, `name`, `registered_at`
49
+ * and `role`, and says why: the first four are DELIBERATELY the same four
50
+ * fields, under the same names, that `GET /api/v1/repository` serves in its own
51
+ * `repository` block, so a client that has read one knows how to read the other.
52
+ * Renaming or flattening anything here would spend that parity on the last hop.
53
+ *
54
+ * `role` is the field this surface adds — `"owner"` or `"member"` — because the
55
+ * list MIXES repositories the person owns with repositories somebody shared
56
+ * with them and no other field separates the two. An agent that will later
57
+ * register keys or change settings needs to know which of these it may expect
58
+ * to administer, so the value is named in the description rather than left to be
59
+ * discovered from the data.
60
+ *
61
+ * The order is `full_name` ascending, which the controller picks as the only
62
+ * column a client can page or diff against without SpecGuard promising an id
63
+ * ordering it has not designed. It is stated here for the same reason the other
64
+ * tool states its orders: a list whose order is a coincidence and a list whose
65
+ * order is a contract look identical in a response body.
66
+ */
67
+ declare const listRepositories: ToolDefinition;
68
+ export default listRepositories;
@@ -0,0 +1,105 @@
1
+ import { getJsonObject, requireUserApiConfig } from "../support/specguard-api.js";
2
+ /**
3
+ * `GET /api/v1/repositories` as a tool — shipped today in the platform
4
+ * (`specguard/config/routes.rb`, `Api::V1::UserRepositoriesController#index`).
5
+ *
6
+ * == Why this is the first user-scoped tool, and for now the only one
7
+ *
8
+ * Every other tool here answers about ONE repository the caller has already
9
+ * named — and a bridge that can only answer about a repository you can already
10
+ * name cannot tell an agent which repositories there ARE. `get_repository_overview`
11
+ * does not take a repository: its `sgk_` key IS the repository, so the question
12
+ * "what may I ask about" has no answer anywhere in this server. This is that
13
+ * answer, and it is the whole of what this tool does.
14
+ *
15
+ * It is also the tool that proves the second credential slot works end to end,
16
+ * which is why it ships alone. The registry's standing rule (`tools/index.ts`)
17
+ * is that a tool in `tools/list` is a promise an agent acts on, so the other
18
+ * user-scoped endpoints wait for the transport they need: `POST /api/v1/repositories`
19
+ * EXISTS on the platform today, and `getJson` hardcodes `method: "GET"` and
20
+ * takes no body, so registering a repository is blocked on a write transport
21
+ * rather than on a missing endpoint. That transport belongs with the first write
22
+ * tool, where it can be designed against a real body and a real 4xx surface.
23
+ *
24
+ * == It reads the OTHER key, and that is the point
25
+ *
26
+ * `Api::BaseController` decides which credential table to consult from the
27
+ * token's PREFIX, before any table is read, and answers 401 on a mismatch
28
+ * without a lookup — so this endpoint refuses the `sgk_` key
29
+ * `get_repository_overview` uses, and that one refuses this key. Hence
30
+ * `requireUserApiConfig` rather than `requireApiConfig`: the two are the same
31
+ * function over different variables, and the `Credential` each carries is what
32
+ * makes a 401 or an unset variable name the one the OPERATOR of this tool has
33
+ * to go and fix. See `config.ts`.
34
+ *
35
+ * == No arguments, because the credential is the whole question
36
+ *
37
+ * The endpoint takes no parameters: the person the `sgu_` key speaks for is the
38
+ * entire scope of the answer, and `Repository.accessible_by` — owned UNION
39
+ * shared-through-a-membership — is the platform's read-side boundary rather
40
+ * than a filter this bridge could widen or narrow. A repository the person
41
+ * neither owns nor is a member of never enters the response, so there is
42
+ * nothing here for a parameter to select and nothing an argument could reach.
43
+ *
44
+ * == The response is passed through, not re-modelled
45
+ *
46
+ * Same rule as every other tool here (`types.ts`: "A thin client that reshapes
47
+ * its upstream is not thin"), and it has real content on this body. The
48
+ * controller serves each entry as `id`, `full_name`, `name`, `registered_at`
49
+ * and `role`, and says why: the first four are DELIBERATELY the same four
50
+ * fields, under the same names, that `GET /api/v1/repository` serves in its own
51
+ * `repository` block, so a client that has read one knows how to read the other.
52
+ * Renaming or flattening anything here would spend that parity on the last hop.
53
+ *
54
+ * `role` is the field this surface adds — `"owner"` or `"member"` — because the
55
+ * list MIXES repositories the person owns with repositories somebody shared
56
+ * with them and no other field separates the two. An agent that will later
57
+ * register keys or change settings needs to know which of these it may expect
58
+ * to administer, so the value is named in the description rather than left to be
59
+ * discovered from the data.
60
+ *
61
+ * The order is `full_name` ascending, which the controller picks as the only
62
+ * column a client can page or diff against without SpecGuard promising an id
63
+ * ordering it has not designed. It is stated here for the same reason the other
64
+ * tool states its orders: a list whose order is a coincidence and a list whose
65
+ * order is a contract look identical in a response body.
66
+ */
67
+ const listRepositories = {
68
+ name: "list_repositories",
69
+ title: "List repositories",
70
+ description: "Lists the SpecGuard repositories the person behind this server's user API key may open — " +
71
+ "the answer to \"what can I ask about\", which no other tool here can give, because every " +
72
+ "other tool is already scoped to one repository by its key. " +
73
+ "Each entry carries `id`, `full_name` (`org/repo`, and the handle every other surface names " +
74
+ "a repository by), `name`, `registered_at` and `role`. " +
75
+ "`role` is `owner` or `member`: the list mixes repositories this person owns with " +
76
+ "repositories somebody shared with them, and nothing else distinguishes the two — read it " +
77
+ "before assuming a repository is yours to administer. " +
78
+ "Ordered by `full_name` ascending, which is stable across calls. " +
79
+ "The set is exactly what this person may see — a repository they neither own nor were given " +
80
+ "access to is absent rather than filtered, so an empty list means no access, never an error. " +
81
+ "Needs SPECGUARD_USER_API_KEY (an sgu_… key), which is a DIFFERENT credential from the " +
82
+ "sgk_… repository key get_repository_overview reads; SpecGuard refuses each in the other's " +
83
+ "place.",
84
+ inputSchema: {
85
+ type: "object",
86
+ // No properties, deliberately — see this file's header. Still CLOSED rather
87
+ // than merely empty: `additionalProperties: false` is advertised in
88
+ // `tools/list`, so a client that honours the schema REJECTS an invented
89
+ // argument before the call is made. Nothing on this side refuses it —
90
+ // `server.ts` forwards `arguments` unvalidated and `run` ignores them — so
91
+ // an open schema would have the argument silently dropped and the call
92
+ // answered as if it had been honoured.
93
+ additionalProperties: false,
94
+ },
95
+ async run(_args, context) {
96
+ const api = requireUserApiConfig(context.config);
97
+ const listing = await getJsonObject(api, "/api/v1/repositories", {}, context.fetch);
98
+ return {
99
+ text: JSON.stringify(listing, null, 2),
100
+ structured: listing,
101
+ };
102
+ },
103
+ };
104
+ export default listRepositories;
105
+ //# sourceMappingURL=list-repositories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-repositories.js","sourceRoot":"","sources":["../../../src/tools/list-repositories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGlF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,MAAM,gBAAgB,GAAmB;IACvC,IAAI,EAAE,mBAAmB;IACzB,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EACT,2FAA2F;QAC3F,2FAA2F;QAC3F,6DAA6D;QAC7D,6FAA6F;QAC7F,wDAAwD;QACxD,mFAAmF;QACnF,2FAA2F;QAC3F,uDAAuD;QACvD,kEAAkE;QAClE,6FAA6F;QAC7F,8FAA8F;QAC9F,wFAAwF;QACxF,4FAA4F;QAC5F,QAAQ;IACV,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,4EAA4E;QAC5E,oEAAoE;QACpE,wEAAwE;QACxE,sEAAsE;QACtE,2EAA2E;QAC3E,uEAAuE;QACvE,uCAAuC;QACvC,oBAAoB,EAAE,KAAK;KAC5B;IAED,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO;QACtB,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,sBAAsB,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAEpF,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACtC,UAAU,EAAE,OAAO;SACpB,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1,6 +1,5 @@
1
1
  import { requireApiConfig } from "../config.js";
2
- import { ApiError } from "../errors.js";
3
- import { getJson } from "../support/specguard-api.js";
2
+ import { getJsonObject } from "../support/specguard-api.js";
4
3
  import { optionalBoolean, optionalString } from "./args.js";
5
4
  /**
6
5
  * `GET /api/v1/repository` as a tool — shipped today in the platform
@@ -759,7 +758,7 @@ const getRepositoryOverview = {
759
758
  // asked explicitly for it not to be. See this file's header.
760
759
  const unannotatedExamples = optionalBoolean(args["unannotated_examples"], "unannotated_examples");
761
760
  const api = requireApiConfig(context.config);
762
- const body = await getJson(api, "/api/v1/repository", {
761
+ const overview = await getJsonObject(api, "/api/v1/repository", {
763
762
  branch,
764
763
  spec_directory: specDirectory,
765
764
  spec_file: specFile,
@@ -768,10 +767,6 @@ const getRepositoryOverview = {
768
767
  commit_sha: commitSha,
769
768
  unannotated_examples: unannotatedExamples === true ? "true" : undefined,
770
769
  }, context.fetch);
771
- if (typeof body !== "object" || body === null || Array.isArray(body)) {
772
- throw new ApiError("SpecGuard returned a JSON value that was not an object.");
773
- }
774
- const overview = body;
775
770
  return {
776
771
  text: renderText(overview),
777
772
  structured: overview,
@@ -1 +1 @@
1
- {"version":3,"file":"repository-overview.js","sourceRoot":"","sources":["../../../src/tools/repository-overview.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"repository-overview.js","sourceRoot":"","sources":["../../../src/tools/repository-overview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoaG;AACH,MAAM,qBAAqB,GAAmB;IAC5C,IAAI,EAAE,yBAAyB;IAE/B,KAAK,EAAE,mCAAmC;IAE1C,WAAW,EACT,2FAA2F;QAC3F,6FAA6F;QAC7F,yFAAyF;QACzF,+FAA+F;QAC/F,8FAA8F;QAC9F,gEAAgE;QAChE,gGAAgG;QAChG,wFAAwF;QACxF,6FAA6F;QAC7F,4BAA4B;QAC5B,gFAAgF;QAChF,4FAA4F;QAC5F,6FAA6F;QAC7F,kCAAkC;QAClC,6FAA6F;QAC7F,0FAA0F;QAC1F,wFAAwF;QACxF,yFAAyF;QACzF,4FAA4F;QAC5F,4DAA4D;QAC5D,+FAA+F;QAC/F,2FAA2F;QAC3F,gGAAgG;QAChG,wEAAwE;QACxE,4FAA4F;QAC5F,gGAAgG;QAChG,4EAA4E;QAC5E,2FAA2F;QAC3F,kFAAkF;QAClF,iGAAiG;QACjG,gGAAgG;QAChG,yFAAyF;QACzF,gGAAgG;QAChG,8FAA8F;QAC9F,+FAA+F;QAC/F,uDAAuD;QACvD,yFAAyF;QACzF,8FAA8F;QAC9F,uFAAuF;QACvF,iEAAiE;QACjE,iGAAiG;QACjG,8FAA8F;QAC9F,eAAe;QACf,8FAA8F;QAC9F,8FAA8F;QAC9F,+FAA+F;QAC/F,YAAY;QACZ,wFAAwF;QACxF,6FAA6F;QAC7F,sFAAsF;QACtF,8FAA8F;QAC9F,2FAA2F;QAC3F,4FAA4F;QAC5F,4FAA4F;QAC5F,0DAA0D;QAC1D,wFAAwF;QACxF,iFAAiF;QACjF,8FAA8F;QAC9F,yFAAyF;QACzF,6FAA6F;QAC7F,6FAA6F;QAC7F,qBAAqB;QACrB,mFAAmF;QACnF,+FAA+F;QAC/F,8FAA8F;QAC9F,4FAA4F;QAC5F,8FAA8F;QAC9F,8FAA8F;QAC9F,8FAA8F;QAC9F,yFAAyF;QACzF,+FAA+F;QAC/F,sDAAsD;QACtD,2FAA2F;QAC3F,oFAAoF;QACpF,4FAA4F;QAC5F,sEAAsE;QACtE,kDAAkD;QAClD,6FAA6F;QAC7F,qFAAqF;QACrF,oFAAoF;QACpF,wFAAwF;QACxF,4FAA4F;IAE9F,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,mFAAmF;oBACnF,uFAAuF;oBACvF,sFAAsF;oBACtF,qFAAqF;oBACrF,iFAAiF;oBACjF,kFAAkF;oBAClF,oFAAoF;oBACpF,yFAAyF;oBACzF,2FAA2F;oBAC3F,0FAA0F;oBAC1F,yEAAyE;aAC5E;YACD,cAAc,EAAE;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,wFAAwF;oBACxF,yFAAyF;oBACzF,iEAAiE;oBACjE,sFAAsF;oBACtF,oFAAoF;oBACpF,sFAAsF;oBACtF,yFAAyF;oBACzF,0FAA0F;oBAC1F,2FAA2F;oBAC3F,qFAAqF;oBACrF,uFAAuF;oBACvF,qFAAqF;oBACrF,oBAAoB;oBACpB,0FAA0F;oBAC1F,0FAA0F;oBAC1F,wFAAwF;oBACxF,0FAA0F;oBAC1F,uFAAuF;oBACvF,qDAAqD;oBACrD,uFAAuF;oBACvF,0FAA0F;oBAC1F,sFAAsF;oBACtF,mDAAmD;aACtD;YACD,SAAS,EAAE;gBACT,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,wFAAwF;oBACxF,oFAAoF;oBACpF,wDAAwD;oBACxD,uFAAuF;oBACvF,4EAA4E;oBAC5E,6EAA6E;oBAC7E,uFAAuF;oBACvF,sEAAsE;oBACtE,0FAA0F;oBAC1F,0FAA0F;oBAC1F,wCAAwC;oBACxC,uFAAuF;oBACvF,kFAAkF;oBAClF,uCAAuC;aAC1C;YACD,oBAAoB,EAAE;gBACpB,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,sFAAsF;oBACtF,wFAAwF;oBACxF,yDAAyD;oBACzD,mEAAmE;oBACnE,sFAAsF;oBACtF,oFAAoF;oBACpF,qFAAqF;oBACrF,uFAAuF;oBACvF,iFAAiF;oBACjF,oFAAoF;oBACpF,gFAAgF;oBAChF,yCAAyC;oBACzC,sFAAsF;oBACtF,kFAAkF;oBAClF,0CAA0C;aAC7C;YACD,aAAa,EAAE;gBACb,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,wFAAwF;oBACxF,mFAAmF;oBACnF,sEAAsE;oBACtE,qFAAqF;oBACrF,uFAAuF;oBACvF,oFAAoF;oBACpF,yEAAyE;oBACzE,wFAAwF;oBACxF,iFAAiF;oBACjF,+BAA+B;oBAC/B,qFAAqF;oBACrF,qFAAqF;oBACrF,sFAAsF;oBACtF,mFAAmF;oBACnF,4EAA4E;oBAC5E,sFAAsF;oBACtF,YAAY;oBACZ,wFAAwF;oBACxF,oFAAoF;oBACpF,qFAAqF;oBACrF,uFAAuF;oBACvF,qFAAqF;oBACrF,qFAAqF;oBACrF,sFAAsF;oBACtF,2DAA2D;oBAC3D,sFAAsF;oBACtF,oFAAoF;oBACpF,kFAAkF;oBAClF,+BAA+B;oBAC/B,oFAAoF;oBACpF,uFAAuF;oBACvF,oFAAoF;oBACpF,SAAS;aACZ;YACD,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,wFAAwF;oBACxF,0FAA0F;oBAC1F,wFAAwF;oBACxF,8DAA8D;oBAC9D,qDAAqD;oBACrD,yDAAyD;oBACzD,sFAAsF;oBACtF,0EAA0E;oBAC1E,uFAAuF;oBACvF,0CAA0C;oBAC1C,mEAAmE;oBACnE,qFAAqF;oBACrF,+EAA+E;oBAC/E,mFAAmF;oBACnF,0FAA0F;oBAC1F,wFAAwF;oBACxF,yFAAyF;oBACzF,+BAA+B;oBAC/B,wFAAwF;oBACxF,yFAAyF;oBACzF,0FAA0F;oBAC1F,yFAAyF;oBACzF,yEAAyE;oBACzE,yFAAyF;oBACzF,yFAAyF;oBACzF,uFAAuF;oBACvF,sFAAsF;oBACtF,yFAAyF;oBACzF,wFAAwF;oBACxF,qFAAqF;oBACrF,uFAAuF;oBACvF,yFAAyF;oBACzF,sFAAsF;oBACtF,sFAAsF;oBACtF,yEAAyE;oBACzE,8EAA8E;oBAC9E,sFAAsF;oBACtF,uFAAuF;oBACvF,uFAAuF;oBACvF,SAAS;aACZ;YACD,oBAAoB,EAAE;gBACpB,IAAI,EAAE,SAAS;gBACf,WAAW,EACT,iFAAiF;oBACjF,kFAAkF;oBAClF,2FAA2F;oBAC3F,0FAA0F;oBAC1F,iFAAiF;oBACjF,0FAA0F;oBAC1F,2FAA2F;oBAC3F,sFAAsF;oBACtF,0FAA0F;oBAC1F,0FAA0F;oBAC1F,wFAAwF;oBACxF,wFAAwF;oBACxF,kCAAkC;oBAClC,oFAAoF;oBACpF,uFAAuF;oBACvF,oFAAoF;oBACpF,sFAAsF;oBACtF,uFAAuF;oBACvF,2DAA2D;oBAC3D,uFAAuF;oBACvF,uFAAuF;oBACvF,uFAAuF;oBACvF,+EAA+E;oBAC/E,wFAAwF;oBACxF,+DAA+D;oBAC/D,kFAAkF;oBAClF,wFAAwF;oBACxF,qFAAqF;oBACrF,uCAAuC;oBACvC,yFAAyF;oBACzF,yFAAyF;oBACzF,yFAAyF;oBACzF,sFAAsF;oBACtF,6CAA6C;oBAC7C,0FAA0F;oBAC1F,wFAAwF;oBACxF,wFAAwF;oBACxF,wFAAwF;oBACxF,yFAAyF;oBACzF,qFAAqF;oBACrF,yFAAyF;oBACzF,qFAAqF;oBACrF,sFAAsF;oBACtF,uFAAuF;oBACvF,wEAAwE;oBACxE,+EAA+E;oBAC/E,cAAc;oBACd,wFAAwF;oBACxF,+DAA+D;oBAC/D,0DAA0D;oBAC1D,uFAAuF;oBACvF,qFAAqF;oBACrF,uFAAuF;oBACvF,uFAAuF;oBACvF,2DAA2D;oBAC3D,0FAA0F;oBAC1F,0FAA0F;oBAC1F,sFAAsF;oBACtF,oFAAoF;oBACpF,0FAA0F;oBAC1F,0FAA0F;oBAC1F,yBAAyB;oBACzB,qFAAqF;oBACrF,6DAA6D;oBAC7D,wFAAwF;oBACxF,qFAAqF;oBACrF,uFAAuF;oBACvF,wFAAwF;oBACxF,0FAA0F;oBAC1F,yFAAyF;oBACzF,mCAAmC;oBACnC,qFAAqF;oBACrF,uFAAuF;oBACvF,gFAAgF;aACnF;SACF;QACD,oBAAoB,EAAE,KAAK;KAC5B;IAED,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO;QACrB,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;QACxD,MAAM,aAAa,GAAG,cAAc,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC,CAAC;QAC/E,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC;QAChE,MAAM,mBAAmB,GAAG,cAAc,CACxC,IAAI,CAAC,sBAAsB,CAAC,EAC5B,sBAAsB,CACvB,CAAC;QACF,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC,CAAC;QAC5E,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,CAAC;QACnE,2EAA2E;QAC3E,6EAA6E;QAC7E,6EAA6E;QAC7E,wEAAwE;QACxE,6DAA6D;QAC7D,MAAM,mBAAmB,GAAG,eAAe,CACzC,IAAI,CAAC,sBAAsB,CAAC,EAC5B,sBAAsB,CACvB,CAAC;QACF,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE7C,MAAM,QAAQ,GAAG,MAAM,aAAa,CAClC,GAAG,EACH,oBAAoB,EACpB;YACE,MAAM;YACN,cAAc,EAAE,aAAa;YAC7B,SAAS,EAAE,QAAQ;YACnB,oBAAoB,EAAE,mBAAmB;YACzC,aAAa,EAAE,YAAY;YAC3B,UAAU,EAAE,SAAS;YACrB,oBAAoB,EAAE,mBAAmB,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;SACxE,EACD,OAAO,CAAC,KAAK,CACd,CAAC;QAEF,OAAO;YACL,IAAI,EAAE,UAAU,CAAC,QAAQ,CAAC;YAC1B,UAAU,EAAE,QAAQ;SACrB,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,qBAAqB,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,SAAS,UAAU,CAAC,QAAiC;IACnD,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC3C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specguard-mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "MCP server exposing SpecGuard suite intelligence to AI coding agents",
5
5
  "license": "ISC",
6
6
  "type": "module",