@amykit/plugin-claude 0.2.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.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ <!-- Generated by `npm run docs:generate` from the package itself. Do not edit by hand. -->
2
+
3
+ # @amykit/plugin-claude
4
+
5
+ The claude CLI as the agent, with git on the side.
6
+
7
+ A plugin for [amy](https://github.com/nicolasmelo1/amy). It provides `claude` in the `agent` collection and `claude` in the `harness` collection.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ npm install -g @amykit/plugin-claude
13
+ ```
14
+
15
+ Then name it in `~/.amy/config.yaml`:
16
+
17
+ ```yaml
18
+ workflows:
19
+ your-profile:
20
+ plugins:
21
+ - "@amykit/plugin-claude"
22
+ ```
23
+
24
+ ## Configuration
25
+
26
+ ```yaml
27
+ plugins:
28
+ "@amykit/plugin-claude":
29
+ defaultBranch: "main"
30
+ model: ""
31
+ models: []
32
+ reviewerHints: {}
33
+ timeoutMs: 1800000
34
+ ```
35
+
36
+ | Setting | Type | Required | Default | What it is |
37
+ | :-- | :-- | :-- | :-- | :-- |
38
+ | `defaultBranch` | `string` | no | `main` | the branch new work is cut from, which is not always `main` |
39
+ | `model` | `string` | no | `""` | passed to the CLI as --model, which is the flag it accepts |
40
+ | `models` | `string[]` | no | `[]` | the model tiers to offer the relay, cheapest first. One agent is contributed per tier, named `claude:<model>`. Empty means a single agent using `model` |
41
+ | `reviewerHints` | `record` | no | `{}` | guidance appended when answering a particular reviewer, by host login |
42
+ | `timeoutMs` | `number` | no | `1800000` | how long one agent call may run before it is given up on |
43
+
44
+ Every field is checked at boot against the schema this package declares. A key that is
45
+ not one of the above is a refusal naming the plugin and the key, not a setting that
46
+ silently never applied.
47
+
48
+ ## Where the reasoning is
49
+
50
+ - [Writing a plugin](https://github.com/nicolasmelo1/amy/tree/main/docs/build/write-a-plugin.md)
51
+ - [Reference: plugins](https://github.com/nicolasmelo1/amy/tree/main/docs/reference/plugins.md)
52
+ - [Ports and the registry](https://github.com/nicolasmelo1/amy/tree/main/docs/concepts/ports.md)
53
+
54
+ ## Licence
55
+
56
+ MIT
@@ -0,0 +1,30 @@
1
+ import { CommandRunner, Harness, HarnessReply } from "@amykit/core";
2
+ export interface ClaudeHarnessConfig {
3
+ /** Passed to the CLI as --model. Omit to use whatever the CLI defaults to. */
4
+ model?: string;
5
+ timeoutMs?: number;
6
+ }
7
+ /**
8
+ * The `claude` CLI, and everything knowable about what a call to it took.
9
+ *
10
+ * `--output-format json` is what makes the run measurable rather than
11
+ * guessed: it returns the answer, the token counts, the cost the harness
12
+ * itself worked out, and an error status to classify on.
13
+ */
14
+ export declare class ClaudeHarness implements Harness {
15
+ private readonly runner;
16
+ private readonly config;
17
+ readonly name = "claude";
18
+ constructor(runner: CommandRunner, config?: ClaudeHarnessConfig);
19
+ ask(prompt: string, cwd: string): Promise<HarnessReply>;
20
+ /**
21
+ * Turns a parsed run into the account the engine logs and the budget spends.
22
+ *
23
+ * A cost the harness reported wins, because it knows the plan and the
24
+ * discounts. Failing that, the vendored table computes one and says so. A
25
+ * model the table has never heard of leaves the cost absent rather than
26
+ * inventing a rate to spend against a ceiling.
27
+ */
28
+ private account;
29
+ }
30
+ //# sourceMappingURL=ClaudeHarness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClaudeHarness.d.ts","sourceRoot":"","sources":["../src/ClaudeHarness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAI9E,MAAM,WAAW,mBAAmB;IAClC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,OAAO;IAIzC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,QAAQ,CAAC,IAAI,YAAY;gBAGN,MAAM,EAAE,aAAa,EACrB,MAAM,GAAE,mBAAwB;IAG7C,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAoB7D;;;;;;;OAOG;IACH,OAAO,CAAC,OAAO;CAuBhB"}
@@ -0,0 +1,65 @@
1
+ import { costOf, specFor } from "@amykit/model-specs";
2
+ import { parseRun } from "./envelope.js";
3
+ /**
4
+ * The `claude` CLI, and everything knowable about what a call to it took.
5
+ *
6
+ * `--output-format json` is what makes the run measurable rather than
7
+ * guessed: it returns the answer, the token counts, the cost the harness
8
+ * itself worked out, and an error status to classify on.
9
+ */
10
+ export class ClaudeHarness {
11
+ runner;
12
+ config;
13
+ name = "claude";
14
+ constructor(runner, config = {}) {
15
+ this.runner = runner;
16
+ this.config = config;
17
+ }
18
+ async ask(prompt, cwd) {
19
+ const asked = this.config.model ?? "";
20
+ const args = [];
21
+ if (this.config.model) {
22
+ args.push("--model", this.config.model);
23
+ }
24
+ args.push("--output-format", "json", "-p");
25
+ const started = Date.now();
26
+ const result = await this.runner.run("claude", args, {
27
+ cwd,
28
+ stdin: prompt,
29
+ timeoutMs: this.config.timeoutMs,
30
+ });
31
+ const elapsed = Date.now() - started;
32
+ const parsed = parseRun(result.stdout, result.ok, asked);
33
+ return { text: parsed.text, run: this.account(parsed, elapsed, result.stderr) };
34
+ }
35
+ /**
36
+ * Turns a parsed run into the account the engine logs and the budget spends.
37
+ *
38
+ * A cost the harness reported wins, because it knows the plan and the
39
+ * discounts. Failing that, the vendored table computes one and says so. A
40
+ * model the table has never heard of leaves the cost absent rather than
41
+ * inventing a rate to spend against a ceiling.
42
+ */
43
+ account(parsed, elapsedMs, stderr) {
44
+ let costUsd = parsed.costUsd;
45
+ let costSource = parsed.costSource;
46
+ if (costUsd === undefined && parsed.tokens) {
47
+ const spec = specFor(parsed.model);
48
+ if (spec) {
49
+ costUsd = costOf(spec, parsed.tokens);
50
+ costSource = "computed";
51
+ }
52
+ }
53
+ return {
54
+ outcome: parsed.outcome,
55
+ harness: this.name,
56
+ model: parsed.model,
57
+ tokens: parsed.tokens,
58
+ costUsd,
59
+ costSource,
60
+ durationMs: parsed.durationMs ?? elapsedMs,
61
+ output: [parsed.text, stderr].filter(Boolean).join("\n").trim(),
62
+ };
63
+ }
64
+ }
65
+ //# sourceMappingURL=ClaudeHarness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClaudeHarness.js","sourceRoot":"","sources":["../src/ClaudeHarness.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAa,QAAQ,EAAE,MAAM,eAAe,CAAC;AAQpD;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IAIL;IACA;IAJV,IAAI,GAAG,QAAQ,CAAC;IAEzB,YACmB,MAAqB,EACrB,SAA8B,EAAE;QADhC,WAAM,GAAN,MAAM,CAAe;QACrB,WAAM,GAAN,MAAM,CAA0B;IAChD,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAC,MAAc,EAAE,GAAW;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;QACtC,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAE3C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE;YACnD,GAAG;YACH,KAAK,EAAE,MAAM;YACb,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;SACjC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;QAErC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACzD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAClF,CAAC;IAED;;;;;;;OAOG;IACK,OAAO,CAAC,MAAiB,EAAE,SAAiB,EAAE,MAAc;QAClE,IAAI,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC7B,IAAI,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QAEnC,IAAI,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnC,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;gBACtC,UAAU,GAAG,UAAU,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,IAAI,CAAC,IAAI;YAClB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO;YACP,UAAU;YACV,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,SAAS;YAC1C,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE;SAChE,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ import { ConfigSchema } from "@amykit/core";
2
+ /** What this plugin needs told to it, and nothing more. */
3
+ export declare const configSchema: ConfigSchema;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,2DAA2D;AAC3D,eAAO,MAAM,YAAY,EAAE,YA2B1B,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,29 @@
1
+ /** What this plugin needs told to it, and nothing more. */
2
+ export const configSchema = {
3
+ defaultBranch: {
4
+ type: "string",
5
+ description: "the branch new work is cut from, which is not always `main`",
6
+ default: "main",
7
+ },
8
+ model: {
9
+ type: "string",
10
+ description: "passed to the CLI as --model, which is the flag it accepts",
11
+ default: "",
12
+ },
13
+ models: {
14
+ type: "string[]",
15
+ description: "the model tiers to offer the relay, cheapest first. One agent is contributed per tier, named `claude:<model>`. Empty means a single agent using `model`",
16
+ default: [],
17
+ },
18
+ reviewerHints: {
19
+ type: "record",
20
+ description: "guidance appended when answering a particular reviewer, by host login",
21
+ default: {},
22
+ },
23
+ timeoutMs: {
24
+ type: "number",
25
+ description: "how long one agent call may run before it is given up on",
26
+ default: 30 * 60 * 1000,
27
+ },
28
+ };
29
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,2DAA2D;AAC3D,MAAM,CAAC,MAAM,YAAY,GAAiB;IACxC,aAAa,EAAE;QACb,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,6DAA6D;QAC1E,OAAO,EAAE,MAAM;KAChB;IACD,KAAK,EAAE;QACL,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,4DAA4D;QACzE,OAAO,EAAE,EAAE;KACZ;IACD,MAAM,EAAE;QACN,IAAI,EAAE,UAAU;QAChB,WAAW,EACT,yJAAyJ;QAC3J,OAAO,EAAE,EAAE;KACZ;IACD,aAAa,EAAE;QACb,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,uEAAuE;QACpF,OAAO,EAAE,EAAE;KACZ;IACD,SAAS,EAAE;QACT,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,0DAA0D;QACvE,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;KACxB;CACF,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { AgentOutcome, CostSource, TokenUsage } from "@amykit/core";
2
+ /** The shape `claude -p --output-format json` prints. */
3
+ export interface ClaudeEnvelope {
4
+ result?: string;
5
+ is_error?: boolean;
6
+ api_error_status?: number | string | null;
7
+ stop_reason?: string | null;
8
+ total_cost_usd?: number;
9
+ duration_ms?: number;
10
+ usage?: {
11
+ input_tokens?: number;
12
+ output_tokens?: number;
13
+ cache_read_input_tokens?: number;
14
+ cache_creation_input_tokens?: number;
15
+ /**
16
+ * How the cache writes split by time to live.
17
+ *
18
+ * This is present in real envelopes and matters: a one-hour write is
19
+ * billed at twice the input rate, so ignoring the split undercounts a
20
+ * cache-heavy run badly. One real run had all 10,232 of its cache writes
21
+ * at one hour, which is a 60% difference in the computed cost.
22
+ */
23
+ cache_creation?: {
24
+ ephemeral_1h_input_tokens?: number;
25
+ ephemeral_5m_input_tokens?: number;
26
+ };
27
+ };
28
+ modelUsage?: Record<string, unknown>;
29
+ }
30
+ export interface ParsedRun {
31
+ outcome: AgentOutcome;
32
+ text: string;
33
+ model: string;
34
+ tokens?: TokenUsage;
35
+ costUsd?: number;
36
+ costSource: CostSource;
37
+ durationMs?: number;
38
+ }
39
+ /**
40
+ * Reads the envelope, or says it could not.
41
+ *
42
+ * Returns null rather than throwing, because a harness that printed something
43
+ * unexpected is a run to classify, not an exception to unwind.
44
+ */
45
+ export declare function parseEnvelope(stdout: string): ClaudeEnvelope | null;
46
+ /**
47
+ * Which of the four outcomes this run was.
48
+ *
49
+ * Read off the envelope, not off stderr. A rate limit and a failure want
50
+ * opposite responses, and guessing between them from log text is how a relay
51
+ * ends up escalating to an expensive model over a quota problem.
52
+ *
53
+ * Known limit: a rate limit severe enough that no envelope is printed
54
+ * classifies as `failed`. That is honest rather than clever — when a real
55
+ * throttled envelope is in hand, its shape goes here.
56
+ */
57
+ export declare function classify(envelope: ClaudeEnvelope | null, exitOk: boolean): AgentOutcome;
58
+ /** The token counts, if the envelope carried them. */
59
+ export declare function tokensOf(envelope: ClaudeEnvelope | null): TokenUsage | undefined;
60
+ /**
61
+ * The model the run actually used.
62
+ *
63
+ * `modelUsage` is keyed by it, decorations and all, which is more truthful
64
+ * than what was asked for: a fallback would have changed it.
65
+ */
66
+ export declare function modelOf(envelope: ClaudeEnvelope | null, asked: string): string;
67
+ export declare function parseRun(stdout: string, exitOk: boolean, asked: string): ParsedRun;
68
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEpE,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE;QACN,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,uBAAuB,CAAC,EAAE,MAAM,CAAC;QACjC,2BAA2B,CAAC,EAAE,MAAM,CAAC;QACrC;;;;;;;WAOG;QACH,cAAc,CAAC,EAAE;YACf,yBAAyB,CAAC,EAAE,MAAM,CAAC;YACnC,yBAAyB,CAAC,EAAE,MAAM,CAAC;SACpC,CAAC;KACH,CAAC;IACF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CASnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,GAAG,YAAY,CAOvF;AAQD,sDAAsD;AACtD,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,GAAG,UAAU,GAAG,SAAS,CAahF;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9E;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAqBlF"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Reads the envelope, or says it could not.
3
+ *
4
+ * Returns null rather than throwing, because a harness that printed something
5
+ * unexpected is a run to classify, not an exception to unwind.
6
+ */
7
+ export function parseEnvelope(stdout) {
8
+ const trimmed = stdout.trim();
9
+ if (!trimmed.startsWith("{"))
10
+ return null;
11
+ try {
12
+ return JSON.parse(trimmed);
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ /**
19
+ * Which of the four outcomes this run was.
20
+ *
21
+ * Read off the envelope, not off stderr. A rate limit and a failure want
22
+ * opposite responses, and guessing between them from log text is how a relay
23
+ * ends up escalating to an expensive model over a quota problem.
24
+ *
25
+ * Known limit: a rate limit severe enough that no envelope is printed
26
+ * classifies as `failed`. That is honest rather than clever — when a real
27
+ * throttled envelope is in hand, its shape goes here.
28
+ */
29
+ export function classify(envelope, exitOk) {
30
+ if (!envelope)
31
+ return exitOk ? "completed" : "abandoned";
32
+ if (isRateLimit(envelope.api_error_status))
33
+ return "rate-limited";
34
+ if (envelope.is_error === true || !exitOk)
35
+ return "failed";
36
+ return "completed";
37
+ }
38
+ function isRateLimit(status) {
39
+ if (status === undefined || status === null)
40
+ return false;
41
+ if (typeof status === "number")
42
+ return status === 429;
43
+ return status.includes("429");
44
+ }
45
+ /** The token counts, if the envelope carried them. */
46
+ export function tokensOf(envelope) {
47
+ const usage = envelope?.usage;
48
+ if (!usage)
49
+ return undefined;
50
+ const oneHour = usage.cache_creation?.ephemeral_1h_input_tokens;
51
+ return {
52
+ input: usage.input_tokens ?? 0,
53
+ output: usage.output_tokens ?? 0,
54
+ cacheRead: usage.cache_read_input_tokens ?? 0,
55
+ cacheWrite: usage.cache_creation_input_tokens ?? 0,
56
+ ...(oneHour === undefined ? {} : { cacheWrite1h: oneHour }),
57
+ };
58
+ }
59
+ /**
60
+ * The model the run actually used.
61
+ *
62
+ * `modelUsage` is keyed by it, decorations and all, which is more truthful
63
+ * than what was asked for: a fallback would have changed it.
64
+ */
65
+ export function modelOf(envelope, asked) {
66
+ const keys = Object.keys(envelope?.modelUsage ?? {});
67
+ return keys[0] ?? asked;
68
+ }
69
+ export function parseRun(stdout, exitOk, asked) {
70
+ const envelope = parseEnvelope(stdout);
71
+ const tokens = tokensOf(envelope);
72
+ const reported = envelope?.total_cost_usd;
73
+ return {
74
+ outcome: classify(envelope, exitOk),
75
+ // The envelope puts the answer in `result`; without one, the raw output
76
+ // is all there is.
77
+ text: envelope?.result ?? stdout,
78
+ model: modelOf(envelope, asked),
79
+ tokens,
80
+ costUsd: typeof reported === "number" ? reported : undefined,
81
+ // The harness knows the plan and the discounts, so what it says beats
82
+ // anything worked out from a price table.
83
+ // Only ever `reported` or `unknown` here. Working a cost out from a
84
+ // price table is the caller's job, and claiming `computed` without
85
+ // computing would be the exact confusion `costSource` exists to prevent.
86
+ costSource: typeof reported === "number" ? "reported" : "unknown",
87
+ durationMs: envelope?.duration_ms,
88
+ };
89
+ }
90
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { ClaudeHarness } from "./ClaudeHarness.js";
2
+ export type { ClaudeHarnessConfig } from "./ClaudeHarness.js";
3
+ export { classify, modelOf, parseEnvelope, parseRun, tokensOf } from "./envelope.js";
4
+ export type { ClaudeEnvelope, ParsedRun } from "./envelope.js";
5
+ export { configSchema } from "./config.js";
6
+ export { plugin } from "./plugin.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrF,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { ClaudeHarness } from "./ClaudeHarness.js";
2
+ export { classify, modelOf, parseEnvelope, parseRun, tokensOf } from "./envelope.js";
3
+ export { configSchema } from "./config.js";
4
+ export { plugin } from "./plugin.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Plugin } from "@amykit/core";
2
+ export declare const plugin: Plugin;
3
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,MAAM,EAAE,MAAM,cAAc,CAAC;AAK3C,eAAO,MAAM,MAAM,EAAE,MA2BpB,CAAC"}
package/dist/plugin.js ADDED
@@ -0,0 +1,30 @@
1
+ import { Git } from "@amykit/core";
2
+ import { contributeTiers } from "@amykit/agent-kit";
3
+ import { ClaudeHarness } from "./ClaudeHarness.js";
4
+ import { configSchema } from "./config.js";
5
+ export const plugin = {
6
+ name: "@amykit/plugin-claude",
7
+ version: "0.1.0",
8
+ configSchema,
9
+ register(registry, ctx) {
10
+ const git = new Git(ctx.runner, {
11
+ workspaceRoot: ctx.paths.workspace,
12
+ defaultBranch: ctx.config.defaultBranch,
13
+ });
14
+ const tiers = ctx.config.models;
15
+ // Contributed rather than mounted as the `agent` port: only one plugin
16
+ // can own a port, and three harnesses that each wanted to be *the* agent
17
+ // would refuse to mount together. The relay composes what is contributed.
18
+ contributeTiers(registry, {
19
+ harness: "claude",
20
+ models: tiers.length > 0 ? tiers : [ctx.config.model || ""],
21
+ git,
22
+ agent: { reviewerHints: ctx.config.reviewerHints },
23
+ make: (model) => new ClaudeHarness(ctx.runner, {
24
+ model: model || undefined,
25
+ timeoutMs: ctx.config.timeoutMs,
26
+ }),
27
+ });
28
+ },
29
+ };
30
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,CAAC,MAAM,MAAM,GAAW;IAC5B,IAAI,EAAE,uBAAuB;IAC7B,OAAO,EAAE,OAAO;IAChB,YAAY;IACZ,QAAQ,CAAC,QAAQ,EAAE,GAAG;QACpB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE;YAC9B,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS;YAClC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,aAAuB;SAClD,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,MAAkB,CAAC;QAE5C,uEAAuE;QACvE,yEAAyE;QACzE,0EAA0E;QAC1E,eAAe,CAAC,QAAQ,EAAE;YACxB,OAAO,EAAE,QAAQ;YACjB,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,MAAM,CAAC,KAAgB,IAAI,EAAE,CAAC;YACvE,GAAG;YACH,KAAK,EAAE,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,aAAuC,EAAE;YAC5E,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CACd,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE;gBAC5B,KAAK,EAAE,KAAK,IAAI,SAAS;gBACzB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,SAAmB;aAC1C,CAAC;SACL,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@amykit/plugin-claude",
3
+ "version": "0.2.0",
4
+ "description": "The claude CLI as the agent, with git on the side.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Nicolas Melo",
8
+ "homepage": "https://github.com/nicolasmelo1/amy#readme",
9
+ "bugs": "https://github.com/nicolasmelo1/amy/issues",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/nicolasmelo1/amy.git",
13
+ "directory": "plugins/claude"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public",
17
+ "registry": "https://registry.npmjs.org"
18
+ },
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "dependencies": {
31
+ "@amykit/agent-kit": "^0.2.0",
32
+ "@amykit/core": "^0.2.0",
33
+ "@amykit/model-specs": "^0.2.0"
34
+ },
35
+ "devDependencies": {
36
+ "@amykit/test-fixtures": "^0.1.0"
37
+ }
38
+ }