goalforge-claude 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +343 -0
  2. package/dist/components/claude-cli.d.ts +14 -0
  3. package/dist/components/claude-cli.d.ts.map +1 -0
  4. package/dist/components/claude-cli.js +50 -0
  5. package/dist/components/claude-cli.js.map +1 -0
  6. package/dist/components/cost-optimizer.d.ts +43 -0
  7. package/dist/components/cost-optimizer.d.ts.map +1 -0
  8. package/dist/components/cost-optimizer.js +140 -0
  9. package/dist/components/cost-optimizer.js.map +1 -0
  10. package/dist/components/executor.d.ts +18 -0
  11. package/dist/components/executor.d.ts.map +1 -0
  12. package/dist/components/executor.js +154 -0
  13. package/dist/components/executor.js.map +1 -0
  14. package/dist/components/memory-store.d.ts +47 -0
  15. package/dist/components/memory-store.d.ts.map +1 -0
  16. package/dist/components/memory-store.js +168 -0
  17. package/dist/components/memory-store.js.map +1 -0
  18. package/dist/components/planner.d.ts +22 -0
  19. package/dist/components/planner.d.ts.map +1 -0
  20. package/dist/components/planner.js +164 -0
  21. package/dist/components/planner.js.map +1 -0
  22. package/dist/components/reviewer.d.ts +19 -0
  23. package/dist/components/reviewer.d.ts.map +1 -0
  24. package/dist/components/reviewer.js +162 -0
  25. package/dist/components/reviewer.js.map +1 -0
  26. package/dist/components/task-queue.d.ts +36 -0
  27. package/dist/components/task-queue.d.ts.map +1 -0
  28. package/dist/components/task-queue.js +156 -0
  29. package/dist/components/task-queue.js.map +1 -0
  30. package/dist/components/test-runner.d.ts +20 -0
  31. package/dist/components/test-runner.d.ts.map +1 -0
  32. package/dist/components/test-runner.js +201 -0
  33. package/dist/components/test-runner.js.map +1 -0
  34. package/dist/core/config.d.ts +5 -0
  35. package/dist/core/config.d.ts.map +1 -0
  36. package/dist/core/config.js +38 -0
  37. package/dist/core/config.js.map +1 -0
  38. package/dist/core/logger.d.ts +16 -0
  39. package/dist/core/logger.d.ts.map +1 -0
  40. package/dist/core/logger.js +78 -0
  41. package/dist/core/logger.js.map +1 -0
  42. package/dist/core/types.d.ts +122 -0
  43. package/dist/core/types.d.ts.map +1 -0
  44. package/dist/core/types.js +4 -0
  45. package/dist/core/types.js.map +1 -0
  46. package/dist/index.d.ts +3 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +137 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/loop-controller.d.ts +39 -0
  51. package/dist/loop-controller.d.ts.map +1 -0
  52. package/dist/loop-controller.js +272 -0
  53. package/dist/loop-controller.js.map +1 -0
  54. package/package.json +50 -0
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Reviewer = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const logger_1 = require("../core/logger");
6
+ const claude_cli_1 = require("./claude-cli");
7
+ const SYSTEM_PROMPT = `You are a critical code reviewer in an autonomous development loop.
8
+ Your job is to find real problems — not style preferences. Be specific and actionable.
9
+
10
+ Return ONLY valid JSON (no markdown):
11
+ {
12
+ "score": 0-100,
13
+ "passed": true|false,
14
+ "critiques": [
15
+ {
16
+ "severity": "low|medium|high|critical",
17
+ "category": "missing-feature|complexity|correctness|performance|security",
18
+ "description": "what is wrong",
19
+ "suggestion": "how to fix it specifically"
20
+ }
21
+ ],
22
+ "suggestions": ["string — broader improvement ideas"]
23
+ }
24
+
25
+ Score guide:
26
+ 90-100 : excellent, ship it
27
+ 70-89 : good, minor issues
28
+ 50-69 : mediocre, should fix before proceeding
29
+ 0-49 : significant problems, must rework
30
+
31
+ "passed" = score >= 70 AND no "critical" severity critiques.`;
32
+ class Reviewer {
33
+ constructor(optimizer, memory, dryRun = false) {
34
+ this.optimizer = optimizer;
35
+ this.memory = memory;
36
+ this.dryRun = dryRun;
37
+ this.log = (0, logger_1.createLogger)('Reviewer');
38
+ }
39
+ async review(task) {
40
+ this.log.info('Reviewing task', { taskId: task.id });
41
+ if (!task.result) {
42
+ this.log.warn('Task has no result to review', { taskId: task.id });
43
+ return this.emptyReview(task.id, 'No result to review');
44
+ }
45
+ const prompt = this.buildPrompt(task);
46
+ const cacheKey = this.optimizer.buildCacheKey('review', task.id, prompt);
47
+ const estimate = this.optimizer.estimate(prompt, 1500, cacheKey);
48
+ if (estimate.recommendedAction === 'skip') {
49
+ this.log.warn('Skipping review — budget exhausted');
50
+ return this.emptyReview(task.id, 'Budget exhausted');
51
+ }
52
+ let raw;
53
+ const cached = this.optimizer.getCachedResponse(cacheKey);
54
+ if (cached) {
55
+ raw = cached;
56
+ }
57
+ else if (this.dryRun) {
58
+ raw = this.dryRunResponse(task);
59
+ }
60
+ else {
61
+ raw = await this.callApi(prompt);
62
+ this.optimizer.putCachedResponse(cacheKey, raw);
63
+ }
64
+ const result = this.parse(task.id, raw);
65
+ // Persist critiques to memory
66
+ for (const c of result.critiques) {
67
+ this.memory.saveCritique(c);
68
+ }
69
+ this.log.info('Review complete', {
70
+ taskId: task.id,
71
+ score: result.score,
72
+ passed: result.passed,
73
+ criticalIssues: result.critiques.filter((c) => c.severity === 'critical').length,
74
+ });
75
+ return result;
76
+ }
77
+ /** Count critical issues across all stored critiques. */
78
+ countCriticalIssues() {
79
+ return this.memory
80
+ .loadAllCritiques()
81
+ .filter((c) => c.severity === 'critical').length;
82
+ }
83
+ // ── Private ────────────────────────────────────────────────────────────────
84
+ buildPrompt(task) {
85
+ const priorCritiques = this.memory
86
+ .loadCritiquesForTask(task.id)
87
+ .map((c) => `• [${c.severity}] ${c.description}`)
88
+ .join('\n');
89
+ const filesCreated = task.result.filesCreated
90
+ .map((f) => `• ${f}`)
91
+ .join('\n');
92
+ return [
93
+ `TASK: ${task.objective}`,
94
+ `\nOUTPUT SUMMARY:\n${task.result.output}`,
95
+ filesCreated ? `\nFILES CREATED:\n${filesCreated}` : '',
96
+ priorCritiques ? `\nPRIOR CRITIQUES (avoid duplicates):\n${priorCritiques}` : '',
97
+ '\nAnalyse this work. Find real problems only — not stylistic preferences.',
98
+ ]
99
+ .filter(Boolean)
100
+ .join('\n');
101
+ }
102
+ async callApi(prompt) {
103
+ const { text, costUsd } = await (0, claude_cli_1.callClaude)(SYSTEM_PROMPT, prompt);
104
+ this.optimizer.recordCost(costUsd);
105
+ return text;
106
+ }
107
+ parse(taskId, raw) {
108
+ let parsed;
109
+ try {
110
+ const json = raw.replace(/^```json?\n?/m, '').replace(/```$/m, '').trim();
111
+ parsed = JSON.parse(json);
112
+ }
113
+ catch (err) {
114
+ this.log.error('Failed to parse review response', { err: String(err) });
115
+ return this.emptyReview(taskId, 'Parse error');
116
+ }
117
+ const critiques = (parsed.critiques ?? []).map((c) => ({
118
+ id: (0, crypto_1.randomUUID)(),
119
+ taskId,
120
+ severity: c.severity,
121
+ category: c.category,
122
+ description: c.description,
123
+ suggestion: c.suggestion,
124
+ createdAt: new Date().toISOString(),
125
+ }));
126
+ return {
127
+ taskId,
128
+ passed: parsed.passed ?? parsed.score >= 70,
129
+ score: parsed.score,
130
+ critiques,
131
+ suggestions: parsed.suggestions ?? [],
132
+ reviewedAt: new Date().toISOString(),
133
+ };
134
+ }
135
+ emptyReview(taskId, reason) {
136
+ return {
137
+ taskId,
138
+ passed: true, // assume pass when review cannot run
139
+ score: 100,
140
+ critiques: [],
141
+ suggestions: [reason],
142
+ reviewedAt: new Date().toISOString(),
143
+ };
144
+ }
145
+ dryRunResponse(task) {
146
+ return JSON.stringify({
147
+ score: 85,
148
+ passed: true,
149
+ critiques: [
150
+ {
151
+ severity: 'low',
152
+ category: 'missing-feature',
153
+ description: '[DRY-RUN] Placeholder critique',
154
+ suggestion: 'This is a dry-run — no real review performed',
155
+ },
156
+ ],
157
+ suggestions: ['Dry-run mode active — enable real review for production use'],
158
+ });
159
+ }
160
+ }
161
+ exports.Reviewer = Reviewer;
162
+ //# sourceMappingURL=reviewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewer.js","sourceRoot":"","sources":["../../src/components/reviewer.ts"],"names":[],"mappings":";;;AAAA,mCAAoC;AAIpC,2CAA8C;AAC9C,6CAA0C;AAE1C,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;6DAwBuC,CAAC;AAgB9D,MAAa,QAAQ;IAGnB,YACmB,SAAwB,EACxB,MAAmB,EACnB,SAAS,KAAK;QAFd,cAAS,GAAT,SAAS,CAAe;QACxB,WAAM,GAAN,MAAM,CAAa;QACnB,WAAM,GAAN,MAAM,CAAQ;QALhB,QAAG,GAAG,IAAA,qBAAY,EAAC,UAAU,CAAC,CAAC;IAM7C,CAAC;IAEJ,KAAK,CAAC,MAAM,CAAC,IAAU;QACrB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAErD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACnE,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACzE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAEjE,IAAI,QAAQ,CAAC,iBAAiB,KAAK,MAAM,EAAE,CAAC;YAC1C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YACpD,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,kBAAkB,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,GAAW,CAAC;QAChB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAC1D,IAAI,MAAM,EAAE,CAAC;YACX,GAAG,GAAG,MAAM,CAAC;QACf,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACvB,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAExC,8BAA8B;QAC9B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAC/B,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,cAAc,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;SACjF,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,yDAAyD;IACzD,mBAAmB;QACjB,OAAO,IAAI,CAAC,MAAM;aACf,gBAAgB,EAAE;aAClB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM,CAAC;IACrD,CAAC;IAED,8EAA8E;IAEtE,WAAW,CAAC,IAAU;QAC5B,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM;aAC/B,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;aAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;aAChD,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,MAAM,YAAY,GAAG,IAAI,CAAC,MAAO,CAAC,YAAY;aAC3C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;aACpB,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,OAAO;YACL,SAAS,IAAI,CAAC,SAAS,EAAE;YACzB,sBAAsB,IAAI,CAAC,MAAO,CAAC,MAAM,EAAE;YAC3C,YAAY,CAAC,CAAC,CAAC,qBAAqB,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE;YACvD,cAAc,CAAC,CAAC,CAAC,0CAA0C,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE;YAChF,2EAA2E;SAC5E;aACE,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,MAAc;QAClC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,uBAAU,EAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAClE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,MAAc,EAAE,GAAW;QACvC,IAAI,MAAwB,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC1E,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,iCAAiC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACxE,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,SAAS,GAAe,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjE,EAAE,EAAE,IAAA,mBAAU,GAAE;YAChB,MAAM;YACN,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC,CAAC;QAEJ,OAAO;YACL,MAAM;YACN,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE;YAC3C,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,SAAS;YACT,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;YACrC,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACrC,CAAC;IACJ,CAAC;IAEO,WAAW,CAAC,MAAc,EAAE,MAAc;QAChD,OAAO;YACL,MAAM;YACN,MAAM,EAAE,IAAI,EAAE,qCAAqC;YACnD,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,EAAE;YACb,WAAW,EAAE,CAAC,MAAM,CAAC;YACrB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACrC,CAAC;IACJ,CAAC;IAEO,cAAc,CAAC,IAAU;QAC/B,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE;gBACT;oBACE,QAAQ,EAAE,KAAK;oBACf,QAAQ,EAAE,iBAAiB;oBAC3B,WAAW,EAAE,gCAAgC;oBAC7C,UAAU,EAAE,8CAA8C;iBAC3D;aACF;YACD,WAAW,EAAE,CAAC,6DAA6D,CAAC;SAClD,CAAC,CAAC;IAChC,CAAC;CACF;AAlJD,4BAkJC"}
@@ -0,0 +1,36 @@
1
+ import { PlannerOutput, Task, TaskStatus } from '../core/types';
2
+ import { MemoryStore } from './memory-store';
3
+ /**
4
+ * In-memory task queue backed by MemoryStore for persistence.
5
+ * Respects dependency ordering: a task is only eligible when all its
6
+ * dependency task IDs are in COMPLETE status.
7
+ */
8
+ export declare class TaskQueue {
9
+ private readonly memory;
10
+ private tasks;
11
+ private readonly log;
12
+ constructor(memory: MemoryStore);
13
+ enqueue(plan: PlannerOutput): Task;
14
+ enqueueBatch(plans: PlannerOutput[]): Task[];
15
+ /** Move a task to RUNNING. Throws if it cannot run yet. */
16
+ start(taskId: string): Task;
17
+ complete(taskId: string): Task;
18
+ fail(taskId: string, reason?: string): Task;
19
+ block(taskId: string, reason: string): Task;
20
+ retry(taskId: string): Task;
21
+ update(taskId: string, updates: Partial<Task>): Task;
22
+ /** Returns the highest-priority PENDING task whose dependencies are all COMPLETE. */
23
+ nextEligible(): Task | null;
24
+ byStatus(status: TaskStatus): Task[];
25
+ getById(taskId: string): Task | undefined;
26
+ size(): number;
27
+ isComplete(): boolean;
28
+ stats(): Record<TaskStatus, number>;
29
+ private canRun;
30
+ private transition;
31
+ private getOrThrow;
32
+ private persist;
33
+ /** Load tasks that were persisted in a previous run. */
34
+ private hydrate;
35
+ }
36
+ //# sourceMappingURL=task-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-queue.d.ts","sourceRoot":"","sources":["../../src/components/task-queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C;;;;GAIG;AACH,qBAAa,SAAS;IAIR,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;gBAEpB,MAAM,EAAE,WAAW;IAMhD,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAgBlC,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE;IAI5C,2DAA2D;IAC3D,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAa3B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI9B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAO3C,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAO3C,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAW3B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IASpD,qFAAqF;IACrF,YAAY,IAAI,IAAI,GAAG,IAAI;IAQ3B,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE;IAIpC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAIzC,IAAI,IAAI,MAAM;IAId,UAAU,IAAI,OAAO;IASrB,KAAK,IAAI,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC;IAgBnC,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,OAAO;IAKf,wDAAwD;IACxD,OAAO,CAAC,OAAO;CAchB"}
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TaskQueue = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const logger_1 = require("../core/logger");
6
+ /**
7
+ * In-memory task queue backed by MemoryStore for persistence.
8
+ * Respects dependency ordering: a task is only eligible when all its
9
+ * dependency task IDs are in COMPLETE status.
10
+ */
11
+ class TaskQueue {
12
+ constructor(memory) {
13
+ this.memory = memory;
14
+ this.tasks = new Map();
15
+ this.log = (0, logger_1.createLogger)('TaskQueue');
16
+ this.hydrate();
17
+ }
18
+ // ── Mutation ───────────────────────────────────────────────────────────────
19
+ enqueue(plan) {
20
+ const task = {
21
+ ...plan,
22
+ id: (0, crypto_1.randomUUID)(),
23
+ status: 'PENDING',
24
+ retryCount: 0,
25
+ createdAt: new Date().toISOString(),
26
+ updatedAt: new Date().toISOString(),
27
+ };
28
+ this.tasks.set(task.id, task);
29
+ this.memory.saveTask(task);
30
+ this.log.info('Task enqueued', { taskId: task.id, objective: task.objective });
31
+ return task;
32
+ }
33
+ enqueueBatch(plans) {
34
+ return plans.map((p) => this.enqueue(p));
35
+ }
36
+ /** Move a task to RUNNING. Throws if it cannot run yet. */
37
+ start(taskId) {
38
+ const task = this.getOrThrow(taskId);
39
+ if (!this.canRun(task)) {
40
+ throw new Error(`Task ${taskId} has unresolved dependencies or is not PENDING`);
41
+ }
42
+ task.status = 'RUNNING';
43
+ task.updatedAt = new Date().toISOString();
44
+ this.persist(task);
45
+ return task;
46
+ }
47
+ complete(taskId) {
48
+ return this.transition(taskId, 'COMPLETE');
49
+ }
50
+ fail(taskId, reason) {
51
+ const task = this.transition(taskId, 'FAILED');
52
+ task.blockedReason = reason;
53
+ this.persist(task);
54
+ return task;
55
+ }
56
+ block(taskId, reason) {
57
+ const task = this.transition(taskId, 'BLOCKED');
58
+ task.blockedReason = reason;
59
+ this.persist(task);
60
+ return task;
61
+ }
62
+ retry(taskId) {
63
+ const task = this.getOrThrow(taskId);
64
+ task.status = 'PENDING';
65
+ task.retryCount += 1;
66
+ task.blockedReason = undefined;
67
+ task.updatedAt = new Date().toISOString();
68
+ this.persist(task);
69
+ this.log.info('Task queued for retry', { taskId, retryCount: task.retryCount });
70
+ return task;
71
+ }
72
+ update(taskId, updates) {
73
+ const task = this.getOrThrow(taskId);
74
+ Object.assign(task, updates, { updatedAt: new Date().toISOString() });
75
+ this.persist(task);
76
+ return task;
77
+ }
78
+ // ── Queries ────────────────────────────────────────────────────────────────
79
+ /** Returns the highest-priority PENDING task whose dependencies are all COMPLETE. */
80
+ nextEligible() {
81
+ const eligible = [...this.tasks.values()]
82
+ .filter((t) => t.status === 'PENDING' && this.canRun(t))
83
+ .sort((a, b) => a.priority - b.priority);
84
+ return eligible[0] ?? null;
85
+ }
86
+ byStatus(status) {
87
+ return [...this.tasks.values()].filter((t) => t.status === status);
88
+ }
89
+ getById(taskId) {
90
+ return this.tasks.get(taskId);
91
+ }
92
+ size() {
93
+ return this.tasks.size;
94
+ }
95
+ isComplete() {
96
+ return (this.tasks.size > 0 &&
97
+ [...this.tasks.values()].every((t) => t.status === 'COMPLETE' || t.status === 'FAILED'));
98
+ }
99
+ stats() {
100
+ const counts = {
101
+ PENDING: 0,
102
+ RUNNING: 0,
103
+ BLOCKED: 0,
104
+ COMPLETE: 0,
105
+ FAILED: 0,
106
+ };
107
+ for (const t of this.tasks.values()) {
108
+ counts[t.status]++;
109
+ }
110
+ return counts;
111
+ }
112
+ // ── Internals ──────────────────────────────────────────────────────────────
113
+ canRun(task) {
114
+ if (task.status !== 'PENDING')
115
+ return false;
116
+ return task.dependencies.every((depId) => {
117
+ const dep = this.tasks.get(depId);
118
+ return dep?.status === 'COMPLETE';
119
+ });
120
+ }
121
+ transition(taskId, status) {
122
+ const task = this.getOrThrow(taskId);
123
+ task.status = status;
124
+ task.updatedAt = new Date().toISOString();
125
+ this.persist(task);
126
+ this.log.debug(`Task → ${status}`, { taskId });
127
+ return task;
128
+ }
129
+ getOrThrow(taskId) {
130
+ const task = this.tasks.get(taskId);
131
+ if (!task)
132
+ throw new Error(`Task not found: ${taskId}`);
133
+ return task;
134
+ }
135
+ persist(task) {
136
+ this.tasks.set(task.id, task);
137
+ this.memory.saveTask(task);
138
+ }
139
+ /** Load tasks that were persisted in a previous run. */
140
+ hydrate() {
141
+ const persisted = this.memory.loadAllTasks();
142
+ for (const task of persisted) {
143
+ // Tasks that were RUNNING when the process died become PENDING again
144
+ if (task.status === 'RUNNING') {
145
+ task.status = 'PENDING';
146
+ task.updatedAt = new Date().toISOString();
147
+ }
148
+ this.tasks.set(task.id, task);
149
+ }
150
+ if (persisted.length > 0) {
151
+ this.log.info('Hydrated task queue from disk', { count: persisted.length });
152
+ }
153
+ }
154
+ }
155
+ exports.TaskQueue = TaskQueue;
156
+ //# sourceMappingURL=task-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-queue.js","sourceRoot":"","sources":["../../src/components/task-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { TestReport } from '../core/types';
2
+ export declare class TestRunner {
3
+ private readonly workspaceDir;
4
+ private readonly log;
5
+ constructor(workspaceDir: string);
6
+ /**
7
+ * Runs the test suite in workspaceDir and returns a structured report.
8
+ * Supports Jest (detected via jest.config.* or package.json scripts).
9
+ * Falls back to "npm test" if no Jest config is found.
10
+ */
11
+ run(): Promise<TestReport>;
12
+ private detectRunner;
13
+ private execTests;
14
+ private parseOutput;
15
+ private parseJestJson;
16
+ private parseJestText;
17
+ private extractCoverage;
18
+ private emptyReport;
19
+ }
20
+ //# sourceMappingURL=test-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-runner.d.ts","sourceRoot":"","sources":["../../src/components/test-runner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAe,UAAU,EAAE,MAAM,eAAe,CAAC;AAKxD,qBAAa,UAAU;IAGT,OAAO,CAAC,QAAQ,CAAC,YAAY;IAFzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA8B;gBAErB,YAAY,EAAE,MAAM;IAEjD;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;IAiDhC,OAAO,CAAC,YAAY;IAsBpB,OAAO,CAAC,SAAS;IAkBjB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,aAAa;IAwCrB,OAAO,CAAC,aAAa;IAmCrB,OAAO,CAAC,eAAe;IAoBvB,OAAO,CAAC,WAAW;CAYpB"}
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestRunner = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const logger_1 = require("../core/logger");
8
+ const JSON_REPORTER_SENTINEL = /^{/m;
9
+ class TestRunner {
10
+ constructor(workspaceDir) {
11
+ this.workspaceDir = workspaceDir;
12
+ this.log = (0, logger_1.createLogger)('TestRunner');
13
+ }
14
+ /**
15
+ * Runs the test suite in workspaceDir and returns a structured report.
16
+ * Supports Jest (detected via jest.config.* or package.json scripts).
17
+ * Falls back to "npm test" if no Jest config is found.
18
+ */
19
+ async run() {
20
+ this.log.info('Running tests', { workspaceDir: this.workspaceDir });
21
+ if (!(0, fs_1.existsSync)(this.workspaceDir)) {
22
+ return this.emptyReport('Workspace directory does not exist');
23
+ }
24
+ const hasJestConfig = (0, fs_1.existsSync)((0, path_1.join)(this.workspaceDir, 'jest.config.ts')) ||
25
+ (0, fs_1.existsSync)((0, path_1.join)(this.workspaceDir, 'jest.config.js')) ||
26
+ (0, fs_1.existsSync)((0, path_1.join)(this.workspaceDir, 'jest.config.json'));
27
+ const hasPkgJson = (0, fs_1.existsSync)((0, path_1.join)(this.workspaceDir, 'package.json'));
28
+ if (!hasJestConfig && !hasPkgJson) {
29
+ return this.emptyReport('No test setup found in workspace');
30
+ }
31
+ const runner = this.detectRunner();
32
+ this.log.debug('Detected test runner', { runner });
33
+ try {
34
+ const raw = this.execTests(runner);
35
+ const report = this.parseOutput(raw, runner);
36
+ this.log.info('Tests complete', {
37
+ total: report.totalTests,
38
+ passed: report.passed,
39
+ failed: report.failed,
40
+ coverage: report.coveragePercent,
41
+ });
42
+ return report;
43
+ }
44
+ catch (err) {
45
+ // execSync throws on non-zero exit; capture the output anyway
46
+ const stderr = err instanceof Error && 'stderr' in err
47
+ ? String(err.stderr ?? '')
48
+ : String(err);
49
+ const stdout = err instanceof Error && 'stdout' in err
50
+ ? String(err.stdout ?? '')
51
+ : '';
52
+ this.log.warn('Test command exited with non-zero code', {
53
+ message: (err instanceof Error ? err.message : String(err)).slice(0, 200),
54
+ });
55
+ return this.parseOutput(stdout + '\n' + stderr, runner);
56
+ }
57
+ }
58
+ // ── Private ────────────────────────────────────────────────────────────────
59
+ detectRunner() {
60
+ const hasJestConfig = (0, fs_1.existsSync)((0, path_1.join)(this.workspaceDir, 'jest.config.ts')) ||
61
+ (0, fs_1.existsSync)((0, path_1.join)(this.workspaceDir, 'jest.config.js')) ||
62
+ (0, fs_1.existsSync)((0, path_1.join)(this.workspaceDir, 'jest.config.json'));
63
+ if (hasJestConfig)
64
+ return 'jest-json';
65
+ const pkgPath = (0, path_1.join)(this.workspaceDir, 'package.json');
66
+ if ((0, fs_1.existsSync)(pkgPath)) {
67
+ try {
68
+ const pkg = JSON.parse(require('fs').readFileSync(pkgPath, 'utf-8'));
69
+ if (pkg.devDependencies?.jest || pkg.dependencies?.jest)
70
+ return 'jest';
71
+ if (pkg.scripts?.test)
72
+ return 'npm-test';
73
+ }
74
+ catch {
75
+ // ignore
76
+ }
77
+ }
78
+ return 'npm-test';
79
+ }
80
+ execTests(runner) {
81
+ const opts = {
82
+ cwd: this.workspaceDir,
83
+ encoding: 'utf-8',
84
+ stdio: 'pipe',
85
+ timeout: 120000,
86
+ };
87
+ const cmd = runner === 'jest-json'
88
+ ? 'npx jest --json --coverage --passWithNoTests 2>&1 || true'
89
+ : runner === 'jest'
90
+ ? 'npx jest --coverage --passWithNoTests 2>&1 || true'
91
+ : 'npm test 2>&1 || true';
92
+ return (0, child_process_1.execSync)(cmd, opts);
93
+ }
94
+ parseOutput(raw, runner) {
95
+ if (runner === 'jest-json') {
96
+ return this.parseJestJson(raw);
97
+ }
98
+ return this.parseJestText(raw);
99
+ }
100
+ parseJestJson(raw) {
101
+ // Jest --json outputs a JSON object as the last line
102
+ const lines = raw.split('\n').filter(Boolean);
103
+ const jsonLine = lines.reverse().find((l) => JSON_REPORTER_SENTINEL.test(l));
104
+ if (!jsonLine)
105
+ return this.parseJestText(raw);
106
+ try {
107
+ const result = JSON.parse(jsonLine);
108
+ const failures = [];
109
+ for (const suite of result.testResults ?? []) {
110
+ for (const t of suite.testResults ?? []) {
111
+ if (t.status === 'failed') {
112
+ failures.push({
113
+ testName: t.fullName,
114
+ file: suite.testFilePath,
115
+ error: t.failureMessages?.[0] ?? 'Unknown failure',
116
+ });
117
+ }
118
+ }
119
+ }
120
+ const coverage = this.extractCoverage(result.coverageMap);
121
+ return {
122
+ runAt: new Date().toISOString(),
123
+ totalTests: result.numTotalTests ?? 0,
124
+ passed: result.numPassedTests ?? 0,
125
+ failed: result.numFailedTests ?? 0,
126
+ skipped: result.numPendingTests ?? 0,
127
+ coveragePercent: coverage,
128
+ failures,
129
+ rawOutput: raw.slice(0, 2000),
130
+ };
131
+ }
132
+ catch {
133
+ return this.parseJestText(raw);
134
+ }
135
+ }
136
+ parseJestText(raw) {
137
+ // Heuristic parsing for jest text output or arbitrary test runners
138
+ const testMatch = raw.match(/Tests:\s+(\d+)\s+passed.*?(\d+)\s+total/);
139
+ const failMatch = raw.match(/(\d+)\s+failed/);
140
+ const covMatch = raw.match(/All files\s*\|\s*([\d.]+)/);
141
+ const total = testMatch ? parseInt(testMatch[2]) : 0;
142
+ const failed = failMatch ? parseInt(failMatch[1]) : 0;
143
+ const passed = total - failed;
144
+ const coverage = covMatch ? parseFloat(covMatch[1]) : 0;
145
+ // Extract failure blocks
146
+ const failures = [];
147
+ const failBlocks = raw.match(/● .+\n[\s\S]*?(?=\n●|\n\n[A-Z]|$)/g) ?? [];
148
+ for (const block of failBlocks.slice(0, 20)) {
149
+ const name = block.match(/● (.+)/)?.[1] ?? 'Unknown';
150
+ failures.push({
151
+ testName: name.trim(),
152
+ file: 'unknown',
153
+ error: block.slice(0, 500).trim(),
154
+ });
155
+ }
156
+ return {
157
+ runAt: new Date().toISOString(),
158
+ totalTests: total,
159
+ passed,
160
+ failed,
161
+ skipped: 0,
162
+ coveragePercent: coverage,
163
+ failures,
164
+ rawOutput: raw.slice(0, 2000),
165
+ };
166
+ }
167
+ extractCoverage(coverageMap) {
168
+ if (!coverageMap)
169
+ return 0;
170
+ const totals = [];
171
+ for (const file of Object.values(coverageMap)) {
172
+ const f = file;
173
+ const statements = f?.s;
174
+ if (!statements)
175
+ continue;
176
+ const vals = Object.values(statements);
177
+ // istanbul coverage format: s = { "0": 1, "1": 0 } (1 = covered)
178
+ const covered = Object.values(f.s ?? {}).filter((v) => Number(v) > 0).length;
179
+ const total = Object.keys(f.s ?? {}).length;
180
+ if (total > 0)
181
+ totals.push((covered / total) * 100);
182
+ }
183
+ return totals.length > 0
184
+ ? Math.round(totals.reduce((a, b) => a + b, 0) / totals.length)
185
+ : 0;
186
+ }
187
+ emptyReport(reason) {
188
+ return {
189
+ runAt: new Date().toISOString(),
190
+ totalTests: 0,
191
+ passed: 0,
192
+ failed: 0,
193
+ skipped: 0,
194
+ coveragePercent: 0,
195
+ failures: [],
196
+ rawOutput: reason,
197
+ };
198
+ }
199
+ }
200
+ exports.TestRunner = TestRunner;
201
+ //# sourceMappingURL=test-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-runner.js","sourceRoot":"","sources":["../../src/components/test-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { CostBudget, LoopConfig } from './types';
2
+ export declare const DEFAULT_BUDGET: CostBudget;
3
+ export declare function calcCost(inputTokens: number, outputTokens: number): number;
4
+ export declare function defaultLoopConfig(overrides?: Partial<LoopConfig>): LoopConfig;
5
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEjD,eAAO,MAAM,cAAc,EAAE,UAK5B,CAAC;AASF,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAK1E;AAED,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,OAAO,CAAC,UAAU,CAAM,GAAG,UAAU,CAejF"}