ai-foreman 1.3.0 → 1.4.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 +13 -1
- package/dist/branch/git.d.ts +2 -0
- package/dist/branch/git.js +18 -1
- package/dist/branch/github.d.ts +3 -1
- package/dist/branch/github.js +32 -0
- package/dist/branch/gitlab.d.ts +15 -0
- package/dist/branch/gitlab.js +290 -0
- package/dist/branch/planner.d.ts +1 -0
- package/dist/branch/planner.js +3 -1
- package/dist/branch/resume.d.ts +2 -0
- package/dist/branch/resume.js +7 -1
- package/dist/branch/runner.d.ts +6 -1
- package/dist/branch/runner.js +155 -20
- package/dist/branch/types.d.ts +10 -2
- package/dist/cli/start.js +135 -7
- package/dist/cli/tickets.d.ts +11 -0
- package/dist/cli/tickets.js +736 -92
- package/dist/foreman.js +2 -2
- package/dist/log.d.ts +1 -1
- package/dist/tickets/commands.d.ts +6 -0
- package/dist/tickets/commands.js +40 -3
- package/dist/tickets/config.d.ts +2 -1
- package/dist/tickets/config.js +20 -4
- package/dist/tickets/importer.d.ts +55 -0
- package/dist/tickets/importer.js +480 -0
- package/dist/tickets/recommendations.d.ts +32 -0
- package/dist/tickets/recommendations.js +183 -0
- package/dist/tickets/renderMarkdown.js +26 -2
- package/dist/tickets/setupConfig.d.ts +77 -0
- package/dist/tickets/setupConfig.js +369 -0
- package/dist/tickets/stateDb.d.ts +17 -0
- package/dist/tickets/stateDb.js +46 -0
- package/dist/tickets/ticketSchema.d.ts +55 -0
- package/dist/tickets/ticketSchema.js +14 -0
- package/dist/tickets/validate.js +2 -2
- package/package.json +2 -2
|
@@ -68,6 +68,19 @@ export interface ArchiveIndexEntry {
|
|
|
68
68
|
last_updated: string | null;
|
|
69
69
|
notes: string | null;
|
|
70
70
|
}
|
|
71
|
+
export type ReviewRecommendationStatus = "pending" | "accepted" | "deferred" | "dismissed";
|
|
72
|
+
export interface ReviewRecommendation {
|
|
73
|
+
id?: number;
|
|
74
|
+
created_at: string;
|
|
75
|
+
kind: string;
|
|
76
|
+
status: ReviewRecommendationStatus;
|
|
77
|
+
summary: string;
|
|
78
|
+
rationale: string | null;
|
|
79
|
+
ticket_ids_json: string;
|
|
80
|
+
patch_json: string;
|
|
81
|
+
source_json: string;
|
|
82
|
+
updated_at: string | null;
|
|
83
|
+
}
|
|
71
84
|
export declare class StateDb {
|
|
72
85
|
private db;
|
|
73
86
|
constructor(dbPath: string);
|
|
@@ -86,5 +99,9 @@ export declare class StateDb {
|
|
|
86
99
|
getRecentCompleted(): RecentCompletedContext[];
|
|
87
100
|
upsertRecentCompleted(row: RecentCompletedContext): void;
|
|
88
101
|
getArchiveIndex(): ArchiveIndexEntry[];
|
|
102
|
+
insertReviewRecommendation(item: Omit<ReviewRecommendation, "id">): number;
|
|
103
|
+
getReviewRecommendations(status?: ReviewRecommendationStatus): ReviewRecommendation[];
|
|
104
|
+
getReviewRecommendation(id: number): ReviewRecommendation | undefined;
|
|
105
|
+
updateReviewRecommendationStatus(id: number, status: ReviewRecommendationStatus, updatedAt: string): void;
|
|
89
106
|
transaction<T>(fn: () => T): T;
|
|
90
107
|
}
|
package/dist/tickets/stateDb.js
CHANGED
|
@@ -82,11 +82,26 @@ CREATE TABLE IF NOT EXISTS archive_index (
|
|
|
82
82
|
notes TEXT
|
|
83
83
|
);
|
|
84
84
|
|
|
85
|
+
CREATE TABLE IF NOT EXISTS review_recommendations (
|
|
86
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
87
|
+
created_at TEXT NOT NULL,
|
|
88
|
+
kind TEXT NOT NULL,
|
|
89
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
90
|
+
summary TEXT NOT NULL,
|
|
91
|
+
rationale TEXT,
|
|
92
|
+
ticket_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
93
|
+
patch_json TEXT NOT NULL DEFAULT '{}',
|
|
94
|
+
source_json TEXT NOT NULL DEFAULT '{}',
|
|
95
|
+
updated_at TEXT,
|
|
96
|
+
CHECK (status IN ('pending','accepted','deferred','dismissed'))
|
|
97
|
+
);
|
|
98
|
+
|
|
85
99
|
CREATE INDEX IF NOT EXISTS idx_ticket_state_status ON ticket_state(status);
|
|
86
100
|
CREATE INDEX IF NOT EXISTS idx_ticket_events_ticket_id ON ticket_events(ticket_id);
|
|
87
101
|
CREATE INDEX IF NOT EXISTS idx_ticket_events_timestamp ON ticket_events(timestamp);
|
|
88
102
|
CREATE INDEX IF NOT EXISTS idx_validation_snapshots_timestamp ON validation_snapshots(timestamp);
|
|
89
103
|
CREATE INDEX IF NOT EXISTS idx_future_work_disposition ON future_work(disposition);
|
|
104
|
+
CREATE INDEX IF NOT EXISTS idx_review_recommendations_status ON review_recommendations(status);
|
|
90
105
|
`;
|
|
91
106
|
export class StateDb {
|
|
92
107
|
db;
|
|
@@ -245,6 +260,37 @@ export class StateDb {
|
|
|
245
260
|
.prepare("SELECT * FROM archive_index ORDER BY last_updated DESC")
|
|
246
261
|
.all();
|
|
247
262
|
}
|
|
263
|
+
// ── review_recommendations ────────────────────────────────────────────────
|
|
264
|
+
insertReviewRecommendation(item) {
|
|
265
|
+
const r = this.db.prepare(`
|
|
266
|
+
INSERT INTO review_recommendations (
|
|
267
|
+
created_at,kind,status,summary,rationale,ticket_ids_json,patch_json,source_json,updated_at
|
|
268
|
+
) VALUES (
|
|
269
|
+
@created_at,@kind,@status,@summary,@rationale,@ticket_ids_json,@patch_json,@source_json,@updated_at
|
|
270
|
+
)
|
|
271
|
+
`).run(item);
|
|
272
|
+
return r.lastInsertRowid;
|
|
273
|
+
}
|
|
274
|
+
getReviewRecommendations(status) {
|
|
275
|
+
if (status) {
|
|
276
|
+
return this.db
|
|
277
|
+
.prepare("SELECT * FROM review_recommendations WHERE status = ? ORDER BY id")
|
|
278
|
+
.all(status);
|
|
279
|
+
}
|
|
280
|
+
return this.db
|
|
281
|
+
.prepare("SELECT * FROM review_recommendations ORDER BY id")
|
|
282
|
+
.all();
|
|
283
|
+
}
|
|
284
|
+
getReviewRecommendation(id) {
|
|
285
|
+
return this.db
|
|
286
|
+
.prepare("SELECT * FROM review_recommendations WHERE id = ?")
|
|
287
|
+
.get(id);
|
|
288
|
+
}
|
|
289
|
+
updateReviewRecommendationStatus(id, status, updatedAt) {
|
|
290
|
+
this.db
|
|
291
|
+
.prepare("UPDATE review_recommendations SET status = ?, updated_at = ? WHERE id = ?")
|
|
292
|
+
.run(status, updatedAt, id);
|
|
293
|
+
}
|
|
248
294
|
// ── transactions ──────────────────────────────────────────────────────────
|
|
249
295
|
transaction(fn) {
|
|
250
296
|
return this.db.transaction(fn)();
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export type TicketPriority = "P0" | "P1" | "P2" | "P3";
|
|
2
2
|
export type TicketSize = "XS" | "S" | "M" | "L" | "XL";
|
|
3
3
|
export type TicketRisk = "Low" | "Medium" | "High";
|
|
4
|
+
export interface TicketExternalRef {
|
|
5
|
+
provider: "linear" | "jira" | string;
|
|
6
|
+
id: string;
|
|
7
|
+
key?: string | null;
|
|
8
|
+
url?: string | null;
|
|
9
|
+
}
|
|
4
10
|
export interface TicketDef {
|
|
5
11
|
id: string;
|
|
6
12
|
order: number;
|
|
@@ -16,6 +22,7 @@ export interface TicketDef {
|
|
|
16
22
|
likely_files: string[];
|
|
17
23
|
rollback?: string | null;
|
|
18
24
|
notes?: string | null;
|
|
25
|
+
external_refs?: TicketExternalRef[];
|
|
19
26
|
}
|
|
20
27
|
export declare const TICKET_JSON_SCHEMA: {
|
|
21
28
|
readonly type: "object";
|
|
@@ -86,6 +93,30 @@ export declare const TICKET_JSON_SCHEMA: {
|
|
|
86
93
|
readonly notes: {
|
|
87
94
|
readonly type: readonly ["string", "null"];
|
|
88
95
|
};
|
|
96
|
+
readonly external_refs: {
|
|
97
|
+
readonly type: "array";
|
|
98
|
+
readonly items: {
|
|
99
|
+
readonly type: "object";
|
|
100
|
+
readonly required: readonly ["provider", "id"];
|
|
101
|
+
readonly additionalProperties: true;
|
|
102
|
+
readonly properties: {
|
|
103
|
+
readonly provider: {
|
|
104
|
+
readonly type: "string";
|
|
105
|
+
readonly minLength: 1;
|
|
106
|
+
};
|
|
107
|
+
readonly id: {
|
|
108
|
+
readonly type: "string";
|
|
109
|
+
readonly minLength: 1;
|
|
110
|
+
};
|
|
111
|
+
readonly key: {
|
|
112
|
+
readonly type: readonly ["string", "null"];
|
|
113
|
+
};
|
|
114
|
+
readonly url: {
|
|
115
|
+
readonly type: readonly ["string", "null"];
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
};
|
|
89
120
|
};
|
|
90
121
|
};
|
|
91
122
|
export declare const TICKETS_FILE_SCHEMA: {
|
|
@@ -163,6 +194,30 @@ export declare const TICKETS_FILE_SCHEMA: {
|
|
|
163
194
|
readonly notes: {
|
|
164
195
|
readonly type: readonly ["string", "null"];
|
|
165
196
|
};
|
|
197
|
+
readonly external_refs: {
|
|
198
|
+
readonly type: "array";
|
|
199
|
+
readonly items: {
|
|
200
|
+
readonly type: "object";
|
|
201
|
+
readonly required: readonly ["provider", "id"];
|
|
202
|
+
readonly additionalProperties: true;
|
|
203
|
+
readonly properties: {
|
|
204
|
+
readonly provider: {
|
|
205
|
+
readonly type: "string";
|
|
206
|
+
readonly minLength: 1;
|
|
207
|
+
};
|
|
208
|
+
readonly id: {
|
|
209
|
+
readonly type: "string";
|
|
210
|
+
readonly minLength: 1;
|
|
211
|
+
};
|
|
212
|
+
readonly key: {
|
|
213
|
+
readonly type: readonly ["string", "null"];
|
|
214
|
+
};
|
|
215
|
+
readonly url: {
|
|
216
|
+
readonly type: readonly ["string", "null"];
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
};
|
|
166
221
|
};
|
|
167
222
|
};
|
|
168
223
|
};
|
|
@@ -20,6 +20,20 @@ export const TICKET_JSON_SCHEMA = {
|
|
|
20
20
|
likely_files: { type: "array", items: { type: "string" } },
|
|
21
21
|
rollback: { type: ["string", "null"] },
|
|
22
22
|
notes: { type: ["string", "null"] },
|
|
23
|
+
external_refs: {
|
|
24
|
+
type: "array",
|
|
25
|
+
items: {
|
|
26
|
+
type: "object",
|
|
27
|
+
required: ["provider", "id"],
|
|
28
|
+
additionalProperties: true,
|
|
29
|
+
properties: {
|
|
30
|
+
provider: { type: "string", minLength: 1 },
|
|
31
|
+
id: { type: "string", minLength: 1 },
|
|
32
|
+
key: { type: ["string", "null"] },
|
|
33
|
+
url: { type: ["string", "null"] },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
23
37
|
},
|
|
24
38
|
};
|
|
25
39
|
export const TICKETS_FILE_SCHEMA = {
|
package/dist/tickets/validate.js
CHANGED
|
@@ -25,12 +25,12 @@ export function runAllValidation(config, projectDir, ticketDefs, states, db) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
// Pass 3: queue validation
|
|
28
|
-
const queueRows = buildNextQueue(ticketDefs, states, config.
|
|
28
|
+
const queueRows = buildNextQueue(ticketDefs, states, config.implementationLimit);
|
|
29
29
|
const remaining = ticketDefs.filter((t) => {
|
|
30
30
|
const s = states.get(t.id);
|
|
31
31
|
return s?.status !== "done" && s?.status !== "canceled";
|
|
32
32
|
});
|
|
33
|
-
const expectedLen = Math.min(config.
|
|
33
|
+
const expectedLen = Math.min(config.implementationLimit, remaining.length);
|
|
34
34
|
if (queueRows.length !== expectedLen) {
|
|
35
35
|
issues.push({ pass: 3, severity: "error", message: `[queue] expected ${expectedLen} rows, got ${queueRows.length}` });
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-foreman",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "An orchestrator that keeps Codex CLI / Claude Code builders moving through their step list unattended. Part of Rafi.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@anthropic-ai/claude-agent-sdk": "^0.3.148"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"special-agents": "0.
|
|
72
|
+
"special-agents": "0.5.0",
|
|
73
73
|
"@clack/prompts": "^1.4.0",
|
|
74
74
|
"ajv": "^8.20.0",
|
|
75
75
|
"ajv-formats": "^3.0.1",
|