@gethmy/agent 1.7.1 → 1.7.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.
Files changed (77) hide show
  1. package/dist/cli.js +6376 -141
  2. package/dist/index.js +6206 -333
  3. package/package.json +2 -2
  4. package/dist/board-helpers.d.ts +0 -31
  5. package/dist/board-helpers.js +0 -150
  6. package/dist/budget.d.ts +0 -39
  7. package/dist/budget.js +0 -73
  8. package/dist/cli.d.ts +0 -14
  9. package/dist/completion.d.ts +0 -36
  10. package/dist/completion.js +0 -322
  11. package/dist/config-validation.d.ts +0 -23
  12. package/dist/config-validation.js +0 -77
  13. package/dist/config.d.ts +0 -23
  14. package/dist/config.js +0 -103
  15. package/dist/episode-writer.d.ts +0 -116
  16. package/dist/episode-writer.js +0 -349
  17. package/dist/git-diff-stat.d.ts +0 -24
  18. package/dist/git-diff-stat.js +0 -56
  19. package/dist/git-pr.d.ts +0 -38
  20. package/dist/git-pr.js +0 -399
  21. package/dist/http-server.d.ts +0 -66
  22. package/dist/http-server.js +0 -96
  23. package/dist/index.d.ts +0 -5
  24. package/dist/log.d.ts +0 -34
  25. package/dist/log.js +0 -100
  26. package/dist/merge-monitor.d.ts +0 -23
  27. package/dist/merge-monitor.js +0 -169
  28. package/dist/pm.d.ts +0 -14
  29. package/dist/pm.js +0 -63
  30. package/dist/pool.d.ts +0 -71
  31. package/dist/pool.js +0 -259
  32. package/dist/process-group.d.ts +0 -26
  33. package/dist/process-group.js +0 -72
  34. package/dist/progress-tracker.d.ts +0 -82
  35. package/dist/progress-tracker.js +0 -457
  36. package/dist/prompt.d.ts +0 -23
  37. package/dist/prompt.js +0 -160
  38. package/dist/queue.d.ts +0 -39
  39. package/dist/queue.js +0 -100
  40. package/dist/reconcile.d.ts +0 -35
  41. package/dist/reconcile.js +0 -174
  42. package/dist/recovery.d.ts +0 -30
  43. package/dist/recovery.js +0 -141
  44. package/dist/review-completion.d.ts +0 -35
  45. package/dist/review-completion.js +0 -475
  46. package/dist/review-knowledge.d.ts +0 -14
  47. package/dist/review-knowledge.js +0 -89
  48. package/dist/review-prompt.d.ts +0 -12
  49. package/dist/review-prompt.js +0 -103
  50. package/dist/review-worker.d.ts +0 -56
  51. package/dist/review-worker.js +0 -638
  52. package/dist/review-worktree.d.ts +0 -12
  53. package/dist/review-worktree.js +0 -95
  54. package/dist/run-log.d.ts +0 -6
  55. package/dist/run-log.js +0 -19
  56. package/dist/startup-banner.d.ts +0 -29
  57. package/dist/startup-banner.js +0 -143
  58. package/dist/state-store.d.ts +0 -89
  59. package/dist/state-store.js +0 -230
  60. package/dist/stream-parser-selftest.d.ts +0 -9
  61. package/dist/stream-parser-selftest.js +0 -97
  62. package/dist/stream-parser.d.ts +0 -43
  63. package/dist/stream-parser.js +0 -174
  64. package/dist/transitions.d.ts +0 -57
  65. package/dist/transitions.js +0 -131
  66. package/dist/types.d.ts +0 -167
  67. package/dist/types.js +0 -76
  68. package/dist/verification.d.ts +0 -39
  69. package/dist/verification.js +0 -317
  70. package/dist/watcher.d.ts +0 -53
  71. package/dist/watcher.js +0 -153
  72. package/dist/worker.d.ts +0 -54
  73. package/dist/worker.js +0 -507
  74. package/dist/worktree-gc.d.ts +0 -67
  75. package/dist/worktree-gc.js +0 -245
  76. package/dist/worktree.d.ts +0 -18
  77. package/dist/worktree.js +0 -177
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/agent",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "Push-based agent daemon for Harmony — watches board assignments and spawns Claude CLI workers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -38,7 +38,7 @@
38
38
  "scripts": {
39
39
  "start": "node dist/index.js",
40
40
  "prebuild": "cd ../harmony-shared && bun run build && cd ../memory && bun run build",
41
- "build": "rm -rf dist && tsc -p tsconfig.build.json",
41
+ "build": "rm -rf dist && bun build src/index.ts src/cli.ts --outdir dist --target node --external @supabase/supabase-js --external @gethmy/mcp --external \"@gethmy/mcp/*\"",
42
42
  "typecheck": "tsc --noEmit",
43
43
  "prepublishOnly": "npm run build",
44
44
  "test": "vitest run"
@@ -1,31 +0,0 @@
1
- import type { HarmonyApiClient } from "@gethmy/mcp/src/api-client.js";
2
- import type { Card, Label } from "@harmony/shared";
3
- /**
4
- * Build a label lookup map from board-level label definitions.
5
- */
6
- export declare function buildLabelMap(boardLabels: Label[]): Map<string, Label>;
7
- /**
8
- * Resolve a card's `labelIds` to full Label objects using a label map.
9
- */
10
- export declare function resolveCardLabels(card: Card, labelMap: Map<string, Label>): Label[];
11
- /**
12
- * Check if a card has a label by name (case-insensitive).
13
- */
14
- export declare function hasLabel(cardLabels: Label[], labelName: string): boolean;
15
- /**
16
- * Move a card to a column by name. Fetches the board to resolve column ID.
17
- */
18
- export declare function moveCardToColumn(client: HarmonyApiClient, card: Card, targetColumnName: string): Promise<void>;
19
- /**
20
- * Find a label by name (case-insensitive) or create it if missing.
21
- */
22
- export declare function findOrCreateLabel(client: HarmonyApiClient, projectId: string, labelName: string, color?: string): Promise<string | null>;
23
- /**
24
- * Add a label to a card by name, creating the label if it doesn't exist.
25
- */
26
- export declare function addLabelByName(client: HarmonyApiClient, card: Card, labelName: string, color?: string): Promise<void>;
27
- /**
28
- * Move a card to a column and add a label in one pass, fetching the board only once.
29
- * Returns true if the move succeeded, false otherwise.
30
- */
31
- export declare function moveCardAndAddLabel(client: HarmonyApiClient, card: Card, targetColumnName: string, labelName: string, labelColor?: string): Promise<boolean>;
@@ -1,150 +0,0 @@
1
- import { log } from "./log.js";
2
- const TAG = "board";
3
- /**
4
- * Build a label lookup map from board-level label definitions.
5
- */
6
- export function buildLabelMap(boardLabels) {
7
- const map = new Map();
8
- for (const label of boardLabels) {
9
- map.set(label.id, label);
10
- }
11
- return map;
12
- }
13
- /**
14
- * Resolve a card's `labelIds` to full Label objects using a label map.
15
- */
16
- export function resolveCardLabels(card, labelMap) {
17
- const ids = card.labelIds ?? [];
18
- return ids
19
- .map((id) => labelMap.get(id))
20
- .filter((l) => l !== undefined);
21
- }
22
- /**
23
- * Check if a card has a label by name (case-insensitive).
24
- */
25
- export function hasLabel(cardLabels, labelName) {
26
- return cardLabels.some((l) => l.name.toLowerCase() === labelName.toLowerCase());
27
- }
28
- /**
29
- * Move a card to a column by name. Fetches the board to resolve column ID.
30
- */
31
- export async function moveCardToColumn(client, card, targetColumnName) {
32
- try {
33
- const board = await client.getBoard(card.project_id);
34
- const targetColumn = board.columns.find((c) => c.name.toLowerCase() === targetColumnName.toLowerCase());
35
- if (!targetColumn) {
36
- log.warn(TAG, `Column "${targetColumnName}" not found, skipping move`);
37
- return;
38
- }
39
- if (card.column_id === targetColumn.id) {
40
- log.debug(TAG, `Card already in "${targetColumnName}"`);
41
- return;
42
- }
43
- await client.moveCard(card.id, targetColumn.id);
44
- log.info(TAG, `Moved #${card.short_id} to "${targetColumnName}"`);
45
- }
46
- catch (err) {
47
- log.error(TAG, `Failed to move card: ${err instanceof Error ? err.message : err}`);
48
- }
49
- }
50
- /**
51
- * Find a label by name (case-insensitive) or create it if missing.
52
- */
53
- export async function findOrCreateLabel(client, projectId, labelName, color = "#22c55e") {
54
- try {
55
- const board = await client.getBoard(projectId);
56
- const labels = board.labels ?? [];
57
- const existing = labels.find((l) => l.name.toLowerCase() === labelName.toLowerCase());
58
- if (existing)
59
- return existing.id;
60
- const result = await client.createLabel(projectId, {
61
- name: labelName,
62
- color,
63
- });
64
- const labelId = result?.label?.id;
65
- if (labelId) {
66
- log.info(TAG, `Created label "${labelName}"`);
67
- return labelId;
68
- }
69
- log.warn(TAG, `createLabel succeeded but returned no id`);
70
- return null;
71
- }
72
- catch (err) {
73
- log.error(TAG, `Failed to find/create label "${labelName}": ${err instanceof Error ? err.message : err}`);
74
- return null;
75
- }
76
- }
77
- /**
78
- * Add a label to a card by name, creating the label if it doesn't exist.
79
- */
80
- export async function addLabelByName(client, card, labelName, color) {
81
- const labelId = await findOrCreateLabel(client, card.project_id, labelName, color);
82
- if (!labelId)
83
- return;
84
- try {
85
- await client.addLabelToCard(card.id, labelId);
86
- log.info(TAG, `Added label "${labelName}" to #${card.short_id}`);
87
- }
88
- catch (err) {
89
- log.error(TAG, `Failed to add label to card: ${err instanceof Error ? err.message : err}`);
90
- }
91
- }
92
- /**
93
- * Move a card to a column and add a label in one pass, fetching the board only once.
94
- * Returns true if the move succeeded, false otherwise.
95
- */
96
- export async function moveCardAndAddLabel(client, card, targetColumnName, labelName, labelColor = "#8b5cf6") {
97
- let moved = false;
98
- try {
99
- const board = await client.getBoard(card.project_id);
100
- const columns = board.columns;
101
- const labels = board.labels ?? [];
102
- // Move card
103
- const targetColumn = columns.find((c) => c.name.toLowerCase() === targetColumnName.toLowerCase());
104
- if (!targetColumn) {
105
- log.warn(TAG, `Column "${targetColumnName}" not found on board (available: ${columns.map((c) => c.name).join(", ")})`);
106
- }
107
- else if (card.column_id === targetColumn.id) {
108
- log.debug(TAG, `Card #${card.short_id} already in "${targetColumnName}"`);
109
- moved = true;
110
- }
111
- else {
112
- try {
113
- await client.moveCard(card.id, targetColumn.id);
114
- log.info(TAG, `Moved #${card.short_id} to "${targetColumnName}"`);
115
- moved = true;
116
- }
117
- catch (err) {
118
- log.error(TAG, `Failed to move #${card.short_id} to "${targetColumnName}": ${err instanceof Error ? err.message : err}`);
119
- }
120
- }
121
- // Resolve label ID (find or create)
122
- let labelId;
123
- const existing = labels.find((l) => l.name.toLowerCase() === labelName.toLowerCase());
124
- if (existing) {
125
- labelId = existing.id;
126
- }
127
- else {
128
- const result = await client.createLabel(card.project_id, {
129
- name: labelName,
130
- color: labelColor,
131
- });
132
- labelId = result?.label?.id;
133
- if (labelId)
134
- log.info(TAG, `Created label "${labelName}"`);
135
- }
136
- if (labelId) {
137
- try {
138
- await client.addLabelToCard(card.id, labelId);
139
- log.info(TAG, `Added label "${labelName}" to #${card.short_id}`);
140
- }
141
- catch (err) {
142
- log.error(TAG, `Failed to add label "${labelName}" to #${card.short_id}: ${err instanceof Error ? err.message : err}`);
143
- }
144
- }
145
- }
146
- catch (err) {
147
- log.error(TAG, `Failed to prepare move/label for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
148
- }
149
- return moved;
150
- }
package/dist/budget.d.ts DELETED
@@ -1,39 +0,0 @@
1
- import type { FailureSummaryRecord, StateStore } from "./state-store.js";
2
- import type { AgentConfig } from "./types.js";
3
- export type GuardDecision = {
4
- allow: true;
5
- } | {
6
- allow: false;
7
- reason: GuardReason;
8
- detail: string;
9
- };
10
- export type GuardReason = "max_attempts" | "daily_budget";
11
- /**
12
- * BudgetGuard is consulted on every implement pickup. It protects the
13
- * daemon from two failure modes:
14
- * 1. Cards that can never succeed — after N failed attempts the daemon
15
- * gives up quietly (`max_attempts`) and pings once via a comment.
16
- * 2. Runaway daily spend across the entire daemon (`daily_budget`).
17
- *
18
- * Both are recoverable: `max_attempts` resets when the card is reassigned
19
- * (see `StateStore.resetAttempts`), and `daily_budget` resets at UTC
20
- * midnight. There is no per-card cost cap and no permanent dead-letter
21
- * quarantine — the guard never blocks a card forever.
22
- */
23
- export declare class BudgetGuard {
24
- private config;
25
- private store;
26
- constructor(config: AgentConfig["budget"], store: StateStore);
27
- /**
28
- * Inspect a card before we commit to picking it up. `max_attempts` means
29
- * the daemon has given up (the worker has already posted a comment);
30
- * `daily_budget` is a soft pause until the UTC day rolls over.
31
- */
32
- check(cardId: string): GuardDecision;
33
- }
34
- /**
35
- * Build the one-shot "agent gave up" comment posted when a card exhausts
36
- * its attempt budget. Lists the recent failure summaries so a human has a
37
- * post-mortem trail and a recovery branch to check out.
38
- */
39
- export declare function buildGaveUpComment(maxAttempts: number, failures: FailureSummaryRecord[]): string;
package/dist/budget.js DELETED
@@ -1,73 +0,0 @@
1
- /**
2
- * BudgetGuard is consulted on every implement pickup. It protects the
3
- * daemon from two failure modes:
4
- * 1. Cards that can never succeed — after N failed attempts the daemon
5
- * gives up quietly (`max_attempts`) and pings once via a comment.
6
- * 2. Runaway daily spend across the entire daemon (`daily_budget`).
7
- *
8
- * Both are recoverable: `max_attempts` resets when the card is reassigned
9
- * (see `StateStore.resetAttempts`), and `daily_budget` resets at UTC
10
- * midnight. There is no per-card cost cap and no permanent dead-letter
11
- * quarantine — the guard never blocks a card forever.
12
- */
13
- export class BudgetGuard {
14
- config;
15
- store;
16
- constructor(config, store) {
17
- this.config = config;
18
- this.store = store;
19
- }
20
- /**
21
- * Inspect a card before we commit to picking it up. `max_attempts` means
22
- * the daemon has given up (the worker has already posted a comment);
23
- * `daily_budget` is a soft pause until the UTC day rolls over.
24
- */
25
- check(cardId) {
26
- const card = this.store.getCard(cardId);
27
- if (card && card.attempts >= this.config.maxAttemptsPerCard) {
28
- return {
29
- allow: false,
30
- reason: "max_attempts",
31
- detail: `${card.attempts} of ${this.config.maxAttemptsPerCard} attempts exhausted`,
32
- };
33
- }
34
- const dailySpent = this.store.getDailyCostCents();
35
- if (dailySpent >= this.config.dailyBudgetCents) {
36
- return {
37
- allow: false,
38
- reason: "daily_budget",
39
- detail: `daily budget ${formatCents(dailySpent)}/${formatCents(this.config.dailyBudgetCents)} exhausted`,
40
- };
41
- }
42
- return { allow: true };
43
- }
44
- }
45
- /**
46
- * Build the one-shot "agent gave up" comment posted when a card exhausts
47
- * its attempt budget. Lists the recent failure summaries so a human has a
48
- * post-mortem trail and a recovery branch to check out.
49
- */
50
- export function buildGaveUpComment(maxAttempts, failures) {
51
- const lines = [
52
- "**Agent gave up — needs a human.**",
53
- `Stopped after ${maxAttempts} failed attempt${maxAttempts === 1 ? "" : "s"}. Reassign the card to try again.`,
54
- ];
55
- if (failures.length > 0) {
56
- lines.push("", "Recent failures:");
57
- for (const f of failures) {
58
- const when = new Date(f.ts).toISOString().replace("T", " ").slice(0, 16);
59
- const tag = f.reason ? ` [${f.reason}]` : "";
60
- const branch = f.recoveryBranch
61
- ? `\n recover: \`git fetch && git checkout ${f.recoveryBranch}\``
62
- : "";
63
- lines.push(`- ${when} UTC${tag} — ${f.summary}${branch}`);
64
- }
65
- }
66
- else {
67
- lines.push("", "_No prior failure summaries recorded._");
68
- }
69
- return lines.join("\n");
70
- }
71
- function formatCents(cents) {
72
- return `$${(cents / 100).toFixed(2)}`;
73
- }
package/dist/cli.d.ts DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Harmony Agent CLI entry point.
4
- *
5
- * Subcommands:
6
- * (no args) — run the daemon (same as `run`)
7
- * run — run the daemon
8
- * status — GET /status from the running daemon, pretty-print
9
- * health — GET /health, exit 0 if healthy, 1 otherwise
10
- * doctor — run preflight checks without starting the daemon
11
- * gc — one-shot worktree garbage collection
12
- * help — show usage
13
- */
14
- export {};
@@ -1,36 +0,0 @@
1
- import type { HarmonyApiClient } from "@gethmy/mcp/src/api-client.js";
2
- import type { Card } from "@harmony/shared";
3
- import type { StateStore } from "./state-store.js";
4
- import type { CostUpdate } from "./stream-parser.js";
5
- import { type AgentConfig } from "./types.js";
6
- export interface SessionStats {
7
- filesEdited: number;
8
- /** Edited file paths tracked by the ProgressTracker (#272). */
9
- filesEditedPaths?: string[];
10
- filesRead: number;
11
- toolCalls: number;
12
- cost: CostUpdate | null;
13
- /** Trimmed last assistant text — feeds the episode write hook (Phase 1.5). */
14
- lastAssistantText?: string;
15
- /** All non-trivial assistant text blocks — richer summary source (#272). */
16
- assistantTextBlocks?: string[];
17
- }
18
- export declare function buildTokenPayload(stats?: SessionStats | null): {
19
- costCents?: undefined;
20
- inputTokens?: undefined;
21
- outputTokens?: undefined;
22
- cacheCreationInputTokens?: undefined;
23
- cacheReadInputTokens?: undefined;
24
- modelName?: undefined;
25
- } | {
26
- costCents: number;
27
- inputTokens: number;
28
- outputTokens: number;
29
- cacheCreationInputTokens: number;
30
- cacheReadInputTokens: number;
31
- modelName: string | undefined;
32
- };
33
- /**
34
- * Post-work pipeline: push branch, create PR, move card, post summary.
35
- */
36
- export declare function runCompletion(client: HarmonyApiClient, card: Card, branchName: string, worktreePath: string, config: AgentConfig, workerId: number, sessionStats: SessionStats | undefined, workspaceId: string | undefined, agentSessionId: string | null | undefined, stateStore: StateStore): Promise<boolean>;