@renseiai/agentfactory-cli 0.8.11 → 0.8.13
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/dist/src/code.d.ts +23 -0
- package/dist/src/code.d.ts.map +1 -0
- package/dist/src/code.js +88 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/lib/code-intelligence-runner.d.ts +16 -0
- package/dist/src/lib/code-intelligence-runner.d.ts.map +1 -0
- package/dist/src/lib/code-intelligence-runner.js +189 -0
- package/dist/src/lib/merge-queue-runner.d.ts +37 -0
- package/dist/src/lib/merge-queue-runner.d.ts.map +1 -0
- package/dist/src/lib/merge-queue-runner.js +185 -0
- package/dist/src/lib/orchestrator-runner.d.ts.map +1 -1
- package/dist/src/lib/orchestrator-runner.js +9 -2
- package/dist/src/lib/worker-runner.d.ts.map +1 -1
- package/dist/src/lib/worker-runner.js +9 -2
- package/dist/src/merge-queue.d.ts +24 -0
- package/dist/src/merge-queue.d.ts.map +1 -0
- package/dist/src/merge-queue.js +91 -0
- package/package.json +20 -6
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AgentFactory Code Intelligence CLI
|
|
4
|
+
*
|
|
5
|
+
* Exposes code-intelligence tools as CLI commands for use by Task sub-agents
|
|
6
|
+
* and other non-MCP contexts.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* af-code <command> [options]
|
|
10
|
+
*
|
|
11
|
+
* Commands:
|
|
12
|
+
* search-symbols <query> Search for code symbols by name
|
|
13
|
+
* get-repo-map Get PageRank-ranked repository file map
|
|
14
|
+
* search-code <query> BM25/hybrid code search
|
|
15
|
+
* check-duplicate Check content for duplicates
|
|
16
|
+
* help Show this help message
|
|
17
|
+
*
|
|
18
|
+
* Environment:
|
|
19
|
+
* VOYAGE_AI_API_KEY Optional — enables semantic vector embeddings
|
|
20
|
+
* COHERE_API_KEY Optional — enables cross-encoder reranking
|
|
21
|
+
*/
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../src/code.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;GAmBG"}
|
package/dist/src/code.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AgentFactory Code Intelligence CLI
|
|
4
|
+
*
|
|
5
|
+
* Exposes code-intelligence tools as CLI commands for use by Task sub-agents
|
|
6
|
+
* and other non-MCP contexts.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* af-code <command> [options]
|
|
10
|
+
*
|
|
11
|
+
* Commands:
|
|
12
|
+
* search-symbols <query> Search for code symbols by name
|
|
13
|
+
* get-repo-map Get PageRank-ranked repository file map
|
|
14
|
+
* search-code <query> BM25/hybrid code search
|
|
15
|
+
* check-duplicate Check content for duplicates
|
|
16
|
+
* help Show this help message
|
|
17
|
+
*
|
|
18
|
+
* Environment:
|
|
19
|
+
* VOYAGE_AI_API_KEY Optional — enables semantic vector embeddings
|
|
20
|
+
* COHERE_API_KEY Optional — enables cross-encoder reranking
|
|
21
|
+
*/
|
|
22
|
+
import path from 'path';
|
|
23
|
+
import { config } from 'dotenv';
|
|
24
|
+
// Load environment variables from .env.local
|
|
25
|
+
config({ path: path.resolve(process.cwd(), '.env.local'), quiet: true });
|
|
26
|
+
import { runCodeIntelligence, parseCodeArgs } from './lib/code-intelligence-runner.js';
|
|
27
|
+
function printHelp() {
|
|
28
|
+
console.log(`
|
|
29
|
+
AgentFactory Code Intelligence CLI — codebase search and analysis
|
|
30
|
+
|
|
31
|
+
Usage:
|
|
32
|
+
af-code <command> [options]
|
|
33
|
+
|
|
34
|
+
Commands:
|
|
35
|
+
search-symbols <query> Search for functions, classes, types by name
|
|
36
|
+
get-repo-map Get PageRank-ranked repository file map
|
|
37
|
+
search-code <query> BM25/hybrid keyword search with code-aware tokenization
|
|
38
|
+
check-duplicate Check content for exact/near duplicates
|
|
39
|
+
help Show this help message
|
|
40
|
+
|
|
41
|
+
Options (search-symbols):
|
|
42
|
+
--max-results <N> Maximum results (default: 20)
|
|
43
|
+
--kinds <kinds> Comma-separated symbol kinds: function,class,interface,type,etc.
|
|
44
|
+
--file-pattern <pattern> Filter by file pattern (e.g. "*.ts", "src/**")
|
|
45
|
+
|
|
46
|
+
Options (get-repo-map):
|
|
47
|
+
--max-files <N> Maximum files to include (default: 50)
|
|
48
|
+
--file-patterns <patterns> Comma-separated file pattern filters
|
|
49
|
+
|
|
50
|
+
Options (search-code):
|
|
51
|
+
--max-results <N> Maximum results (default: 20)
|
|
52
|
+
--language <lang> Filter by language (e.g. typescript, python)
|
|
53
|
+
|
|
54
|
+
Options (check-duplicate):
|
|
55
|
+
--content <string> Content to check (inline)
|
|
56
|
+
--content-file <path> Path to file containing content to check
|
|
57
|
+
|
|
58
|
+
Index:
|
|
59
|
+
First invocation builds the index from source files (~5-10s).
|
|
60
|
+
Subsequent calls reuse the persisted index from .agentfactory/code-index/.
|
|
61
|
+
|
|
62
|
+
Examples:
|
|
63
|
+
af-code search-symbols "SearchEngine"
|
|
64
|
+
af-code search-symbols "handleRequest" --kinds "function,method" --file-pattern "*.ts"
|
|
65
|
+
af-code get-repo-map --max-files 20
|
|
66
|
+
af-code search-code "incremental indexer" --language typescript
|
|
67
|
+
af-code check-duplicate --content "function hello() { return 'world' }"
|
|
68
|
+
af-code check-duplicate --content-file /tmp/snippet.ts
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
async function main() {
|
|
72
|
+
const { command, args, positionalArgs } = parseCodeArgs(process.argv.slice(2));
|
|
73
|
+
if (!command || command === 'help' || args['help'] || args['h']) {
|
|
74
|
+
printHelp();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const result = await runCodeIntelligence({
|
|
78
|
+
command,
|
|
79
|
+
args,
|
|
80
|
+
positionalArgs,
|
|
81
|
+
cwd: process.cwd(),
|
|
82
|
+
});
|
|
83
|
+
console.log(JSON.stringify(result.output, null, 2));
|
|
84
|
+
}
|
|
85
|
+
main().catch((error) => {
|
|
86
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
});
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,QAAA,MAAM,OAAO,QAAkB,CAAA;AAE/B,iBAAS,SAAS,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,QAAA,MAAM,OAAO,QAAkB,CAAA;AAE/B,iBAAS,SAAS,IAAI,IAAI,CA2BzB"}
|
package/dist/src/index.js
CHANGED
|
@@ -22,6 +22,7 @@ Commands:
|
|
|
22
22
|
agent Manage running agent sessions (stop, chat, status, reconnect)
|
|
23
23
|
cleanup Clean up orphaned git worktrees
|
|
24
24
|
queue-admin Manage Redis work queue and sessions
|
|
25
|
+
merge-queue Manage the Refinery merge queue
|
|
25
26
|
analyze-logs Analyze agent session logs for errors
|
|
26
27
|
linear Linear issue tracker operations
|
|
27
28
|
sync-routes Generate missing route and page files from manifest
|
|
@@ -56,6 +57,9 @@ switch (command) {
|
|
|
56
57
|
case 'queue-admin':
|
|
57
58
|
import('./queue-admin');
|
|
58
59
|
break;
|
|
60
|
+
case 'merge-queue':
|
|
61
|
+
import('./merge-queue');
|
|
62
|
+
break;
|
|
59
63
|
case 'analyze-logs':
|
|
60
64
|
import('./analyze-logs');
|
|
61
65
|
break;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface CodeIntelligenceRunnerConfig {
|
|
2
|
+
command: string;
|
|
3
|
+
args: Record<string, string | string[] | boolean>;
|
|
4
|
+
positionalArgs: string[];
|
|
5
|
+
cwd: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CodeIntelligenceRunnerResult {
|
|
8
|
+
output: unknown;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseCodeArgs(argv: string[]): {
|
|
11
|
+
command: string | undefined;
|
|
12
|
+
args: Record<string, string | string[] | boolean>;
|
|
13
|
+
positionalArgs: string[];
|
|
14
|
+
};
|
|
15
|
+
export declare function runCodeIntelligence(config: CodeIntelligenceRunnerConfig): Promise<CodeIntelligenceRunnerResult>;
|
|
16
|
+
//# sourceMappingURL=code-intelligence-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-intelligence-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/code-intelligence-runner.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,CAAA;IACjD,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,OAAO,CAAA;CAChB;AAID,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAC7C,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,CAAA;IACjD,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB,CAwBA;AAgLD,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,4BAA4B,GACnC,OAAO,CAAC,4BAA4B,CAAC,CAevC"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { join, extname, relative } from 'node:path';
|
|
3
|
+
// ── Supported extensions (mirrors SymbolExtractor's EXTENSION_MAP) ──────────
|
|
4
|
+
const SUPPORTED_EXTENSIONS = new Set([
|
|
5
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
6
|
+
'.py', '.go', '.rs',
|
|
7
|
+
]);
|
|
8
|
+
const IGNORE_DIRS = new Set([
|
|
9
|
+
'node_modules', 'dist', '.git', '.next', '.turbo',
|
|
10
|
+
'build', 'coverage', '__pycache__', '.agentfactory',
|
|
11
|
+
'.worktrees', 'vendor', 'target',
|
|
12
|
+
]);
|
|
13
|
+
// ── Arg parsing ─────────────────────────────────────────────────────────────
|
|
14
|
+
export function parseCodeArgs(argv) {
|
|
15
|
+
const args = {};
|
|
16
|
+
const positionalArgs = [];
|
|
17
|
+
let command;
|
|
18
|
+
for (let i = 0; i < argv.length; i++) {
|
|
19
|
+
const arg = argv[i];
|
|
20
|
+
if (arg.startsWith('--')) {
|
|
21
|
+
const key = arg.slice(2);
|
|
22
|
+
const next = argv[i + 1];
|
|
23
|
+
if (next && !next.startsWith('--')) {
|
|
24
|
+
args[key] = next;
|
|
25
|
+
i++;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
args[key] = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (!command) {
|
|
32
|
+
command = arg;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
positionalArgs.push(arg);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { command, args, positionalArgs };
|
|
39
|
+
}
|
|
40
|
+
// ── File discovery ──────────────────────────────────────────────────────────
|
|
41
|
+
async function discoverSourceFiles(cwd) {
|
|
42
|
+
const files = new Map();
|
|
43
|
+
async function walk(dir) {
|
|
44
|
+
let entries;
|
|
45
|
+
try {
|
|
46
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
if (entry.name.startsWith('.') && IGNORE_DIRS.has(entry.name))
|
|
53
|
+
continue;
|
|
54
|
+
if (IGNORE_DIRS.has(entry.name))
|
|
55
|
+
continue;
|
|
56
|
+
const fullPath = join(dir, entry.name);
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
await walk(fullPath);
|
|
59
|
+
}
|
|
60
|
+
else if (entry.isFile() && SUPPORTED_EXTENSIONS.has(extname(entry.name))) {
|
|
61
|
+
// Skip files larger than 512KB (likely generated)
|
|
62
|
+
try {
|
|
63
|
+
const s = await stat(fullPath);
|
|
64
|
+
if (s.size > 512 * 1024)
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
72
|
+
const relPath = relative(cwd, fullPath);
|
|
73
|
+
files.set(relPath, content);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Skip unreadable files
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
await walk(cwd);
|
|
82
|
+
return files;
|
|
83
|
+
}
|
|
84
|
+
// ── Index initialization ────────────────────────────────────────────────────
|
|
85
|
+
async function initializeIndex(cwd) {
|
|
86
|
+
const { SymbolExtractor, IncrementalIndexer, SearchEngine, HybridSearchEngine, RepoMapGenerator, DedupPipeline, InMemoryStore, } = await import('@renseiai/agentfactory-code-intelligence');
|
|
87
|
+
const extractor = new SymbolExtractor();
|
|
88
|
+
const indexer = new IncrementalIndexer(extractor, { indexDir: '.agentfactory/code-index' });
|
|
89
|
+
const searchEngine = new SearchEngine();
|
|
90
|
+
const hybridEngine = new HybridSearchEngine(searchEngine, null, null);
|
|
91
|
+
const repoMapGen = new RepoMapGenerator();
|
|
92
|
+
const dedupStore = new InMemoryStore();
|
|
93
|
+
const dedupPipeline = new DedupPipeline(dedupStore);
|
|
94
|
+
// Try loading persisted index
|
|
95
|
+
const loaded = await indexer.load(cwd);
|
|
96
|
+
if (loaded) {
|
|
97
|
+
const symbols = indexer.getAllSymbols();
|
|
98
|
+
searchEngine.buildIndex(symbols);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// No persisted index — discover files and build fresh
|
|
102
|
+
const files = await discoverSourceFiles(cwd);
|
|
103
|
+
await indexer.index(files);
|
|
104
|
+
await indexer.save(cwd);
|
|
105
|
+
const symbols = indexer.getAllSymbols();
|
|
106
|
+
searchEngine.buildIndex(symbols);
|
|
107
|
+
}
|
|
108
|
+
return { indexer, searchEngine, hybridEngine, repoMapGen, dedupPipeline, extractor };
|
|
109
|
+
}
|
|
110
|
+
// ── Command handlers ────────────────────────────────────────────────────────
|
|
111
|
+
async function searchSymbols(config, engines) {
|
|
112
|
+
const query = config.positionalArgs[0];
|
|
113
|
+
if (!query)
|
|
114
|
+
throw new Error('Usage: af-code search-symbols <query> [--max-results N] [--kinds "function,class"] [--file-pattern "*.ts"]');
|
|
115
|
+
const maxResults = config.args['max-results'] ? Number(config.args['max-results']) : 20;
|
|
116
|
+
const kinds = config.args['kinds']
|
|
117
|
+
? (typeof config.args['kinds'] === 'string' ? config.args['kinds'].split(',').map(s => s.trim()) : config.args['kinds'])
|
|
118
|
+
: undefined;
|
|
119
|
+
const filePattern = typeof config.args['file-pattern'] === 'string' ? config.args['file-pattern'] : undefined;
|
|
120
|
+
const results = engines.searchEngine.search({
|
|
121
|
+
query,
|
|
122
|
+
maxResults,
|
|
123
|
+
symbolKinds: kinds,
|
|
124
|
+
filePattern,
|
|
125
|
+
});
|
|
126
|
+
return results;
|
|
127
|
+
}
|
|
128
|
+
async function getRepoMap(config, engines) {
|
|
129
|
+
const maxFiles = config.args['max-files'] ? Number(config.args['max-files']) : 50;
|
|
130
|
+
const rawPatterns = config.args['file-patterns'];
|
|
131
|
+
const filePatterns = typeof rawPatterns === 'string'
|
|
132
|
+
? rawPatterns.split(',').map(s => s.trim())
|
|
133
|
+
: Array.isArray(rawPatterns) ? rawPatterns : undefined;
|
|
134
|
+
// Rebuild ASTs from the file index for PageRank computation
|
|
135
|
+
const fileIndex = engines.indexer.getFileIndex();
|
|
136
|
+
const asts = [...fileIndex.values()].map(fi => ({
|
|
137
|
+
filePath: fi.filePath,
|
|
138
|
+
language: fi.symbols[0]?.language ?? 'unknown',
|
|
139
|
+
symbols: fi.symbols,
|
|
140
|
+
imports: [],
|
|
141
|
+
exports: fi.symbols.filter(s => s.exported).map(s => s.name),
|
|
142
|
+
}));
|
|
143
|
+
const entries = engines.repoMapGen.generate(asts, { maxFiles, filePatterns });
|
|
144
|
+
const formatted = engines.repoMapGen.format(entries);
|
|
145
|
+
return { entries, formatted };
|
|
146
|
+
}
|
|
147
|
+
async function searchCode(config, engines) {
|
|
148
|
+
const query = config.positionalArgs[0];
|
|
149
|
+
if (!query)
|
|
150
|
+
throw new Error('Usage: af-code search-code <query> [--max-results N] [--language ts]');
|
|
151
|
+
const maxResults = config.args['max-results'] ? Number(config.args['max-results']) : 20;
|
|
152
|
+
const language = typeof config.args['language'] === 'string' ? config.args['language'] : undefined;
|
|
153
|
+
const results = await engines.hybridEngine.search({
|
|
154
|
+
query,
|
|
155
|
+
maxResults,
|
|
156
|
+
language,
|
|
157
|
+
});
|
|
158
|
+
return results;
|
|
159
|
+
}
|
|
160
|
+
async function checkDuplicate(config, engines) {
|
|
161
|
+
let content;
|
|
162
|
+
if (typeof config.args['content-file'] === 'string') {
|
|
163
|
+
content = await readFile(config.args['content-file'], 'utf-8');
|
|
164
|
+
}
|
|
165
|
+
else if (typeof config.args['content'] === 'string') {
|
|
166
|
+
content = config.args['content'];
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
throw new Error('Usage: af-code check-duplicate --content "<code>" or --content-file /path/to/file');
|
|
170
|
+
}
|
|
171
|
+
const result = await engines.dedupPipeline.check(content);
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
// ── Main runner ─────────────────────────────────────────────────────────────
|
|
175
|
+
export async function runCodeIntelligence(config) {
|
|
176
|
+
const engines = await initializeIndex(config.cwd);
|
|
177
|
+
switch (config.command) {
|
|
178
|
+
case 'search-symbols':
|
|
179
|
+
return { output: await searchSymbols(config, engines) };
|
|
180
|
+
case 'get-repo-map':
|
|
181
|
+
return { output: await getRepoMap(config, engines) };
|
|
182
|
+
case 'search-code':
|
|
183
|
+
return { output: await searchCode(config, engines) };
|
|
184
|
+
case 'check-duplicate':
|
|
185
|
+
return { output: await checkDuplicate(config, engines) };
|
|
186
|
+
default:
|
|
187
|
+
throw new Error(`Unknown command: ${config.command}. Available: search-symbols, get-repo-map, search-code, check-duplicate`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge Queue Runner -- Programmatic API for the merge-queue CLI.
|
|
3
|
+
*
|
|
4
|
+
* Extracts ALL command handlers from the merge-queue bin script so they can be
|
|
5
|
+
* invoked programmatically (e.g. from a Next.js route handler or test) without
|
|
6
|
+
* process.exit / dotenv / argv coupling.
|
|
7
|
+
*/
|
|
8
|
+
export type MergeQueueCommand = 'status' | 'list' | 'retry' | 'skip' | 'pause' | 'resume' | 'priority';
|
|
9
|
+
export interface MergeQueueRunnerConfig {
|
|
10
|
+
/** Command to execute */
|
|
11
|
+
command: MergeQueueCommand;
|
|
12
|
+
/** Remaining CLI arguments (flags, positional args) */
|
|
13
|
+
args: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare const C: {
|
|
16
|
+
readonly reset: "\u001B[0m";
|
|
17
|
+
readonly bold: "\u001B[1m";
|
|
18
|
+
readonly dim: "\u001B[2m";
|
|
19
|
+
readonly red: "\u001B[31m";
|
|
20
|
+
readonly green: "\u001B[32m";
|
|
21
|
+
readonly yellow: "\u001B[33m";
|
|
22
|
+
readonly blue: "\u001B[34m";
|
|
23
|
+
readonly cyan: "\u001B[36m";
|
|
24
|
+
};
|
|
25
|
+
export declare function parseArgs(args: string[]): {
|
|
26
|
+
repoId: string;
|
|
27
|
+
prNumber?: number;
|
|
28
|
+
priority?: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function formatAge(ms: number): string;
|
|
31
|
+
/**
|
|
32
|
+
* Run a merge-queue command programmatically.
|
|
33
|
+
*
|
|
34
|
+
* Throws if REDIS_URL is not set or if required arguments are missing.
|
|
35
|
+
*/
|
|
36
|
+
export declare function runMergeQueueCommand(config: MergeQueueRunnerConfig): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=merge-queue-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-queue-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/merge-queue-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,MAAM,GACN,OAAO,GACP,MAAM,GACN,OAAO,GACP,QAAQ,GACR,UAAU,CAAA;AAEd,MAAM,WAAW,sBAAsB;IACrC,yBAAyB;IACzB,OAAO,EAAE,iBAAiB,CAAA;IAC1B,uDAAuD;IACvD,IAAI,EAAE,MAAM,EAAE,CAAA;CACf;AAMD,eAAO,MAAM,CAAC;;;;;;;;;CASJ,CAAA;AAYV,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAiBlG;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAS5C;AAiHD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDxF"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge Queue Runner -- Programmatic API for the merge-queue CLI.
|
|
3
|
+
*
|
|
4
|
+
* Extracts ALL command handlers from the merge-queue bin script so they can be
|
|
5
|
+
* invoked programmatically (e.g. from a Next.js route handler or test) without
|
|
6
|
+
* process.exit / dotenv / argv coupling.
|
|
7
|
+
*/
|
|
8
|
+
import { MergeQueueStorage, isRedisConfigured, disconnectRedis, redisSet, redisDel, } from '@renseiai/agentfactory-server';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// ANSI colors
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
export const C = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
bold: '\x1b[1m',
|
|
15
|
+
dim: '\x1b[2m',
|
|
16
|
+
red: '\x1b[31m',
|
|
17
|
+
green: '\x1b[32m',
|
|
18
|
+
yellow: '\x1b[33m',
|
|
19
|
+
blue: '\x1b[34m',
|
|
20
|
+
cyan: '\x1b[36m',
|
|
21
|
+
};
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Helpers
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
function ensureRedis() {
|
|
26
|
+
if (!isRedisConfigured()) {
|
|
27
|
+
throw new Error('REDIS_URL environment variable is not set');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function parseArgs(args) {
|
|
31
|
+
let repoId = 'default';
|
|
32
|
+
let prNumber;
|
|
33
|
+
let priority;
|
|
34
|
+
for (let i = 0; i < args.length; i++) {
|
|
35
|
+
if (args[i] === '--repo' && args[i + 1]) {
|
|
36
|
+
repoId = args[i + 1];
|
|
37
|
+
i++;
|
|
38
|
+
}
|
|
39
|
+
else if (prNumber === undefined && /^\d+$/.test(args[i])) {
|
|
40
|
+
prNumber = parseInt(args[i], 10);
|
|
41
|
+
}
|
|
42
|
+
else if (prNumber !== undefined && priority === undefined && /^\d+$/.test(args[i])) {
|
|
43
|
+
priority = parseInt(args[i], 10);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { repoId, prNumber, priority };
|
|
47
|
+
}
|
|
48
|
+
export function formatAge(ms) {
|
|
49
|
+
const seconds = Math.floor(ms / 1000);
|
|
50
|
+
if (seconds < 60)
|
|
51
|
+
return `${seconds}s`;
|
|
52
|
+
const minutes = Math.floor(seconds / 60);
|
|
53
|
+
if (minutes < 60)
|
|
54
|
+
return `${minutes}m`;
|
|
55
|
+
const hours = Math.floor(minutes / 60);
|
|
56
|
+
if (hours < 24)
|
|
57
|
+
return `${hours}h ${minutes % 60}m`;
|
|
58
|
+
const days = Math.floor(hours / 24);
|
|
59
|
+
return `${days}d ${hours % 24}h`;
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Command handlers
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
async function handleStatus(storage, repoId) {
|
|
65
|
+
const status = await storage.getStatus(repoId);
|
|
66
|
+
console.log(`\n${C.bold}Merge Queue Status${C.reset} (repo: ${C.cyan}${repoId}${C.reset})`);
|
|
67
|
+
console.log('\u2500'.repeat(50));
|
|
68
|
+
console.log(` Queue depth: ${C.bold}${status.depth}${C.reset}`);
|
|
69
|
+
console.log(` Processing: ${status.processing
|
|
70
|
+
? `${C.green}PR #${status.processing.prNumber}${C.reset} (${status.processing.sourceBranch})`
|
|
71
|
+
: `${C.dim}none${C.reset}`}`);
|
|
72
|
+
console.log(` Failed: ${status.failedCount > 0 ? `${C.red}${status.failedCount}${C.reset}` : `${C.dim}0${C.reset}`}`);
|
|
73
|
+
console.log(` Blocked: ${status.blockedCount > 0 ? `${C.yellow}${status.blockedCount}${C.reset}` : `${C.dim}0${C.reset}`}`);
|
|
74
|
+
console.log();
|
|
75
|
+
}
|
|
76
|
+
async function handleList(storage, repoId) {
|
|
77
|
+
const entries = await storage.list(repoId);
|
|
78
|
+
if (entries.length === 0) {
|
|
79
|
+
console.log(`\n${C.dim}No PRs in merge queue for repo: ${repoId}${C.reset}\n`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
console.log(`\n${C.bold}Queued PRs${C.reset} (repo: ${C.cyan}${repoId}${C.reset})`);
|
|
83
|
+
console.log('\u2500'.repeat(70));
|
|
84
|
+
console.log(` ${C.dim}# PR Branch Priority Age${C.reset}`);
|
|
85
|
+
console.log('\u2500'.repeat(70));
|
|
86
|
+
entries.forEach((entry, index) => {
|
|
87
|
+
const age = formatAge(Date.now() - entry.enqueuedAt);
|
|
88
|
+
const branch = entry.sourceBranch.length > 30
|
|
89
|
+
? entry.sourceBranch.slice(0, 27) + '...'
|
|
90
|
+
: entry.sourceBranch.padEnd(30);
|
|
91
|
+
console.log(` ${String(index + 1).padStart(2)} ${C.bold}#${String(entry.prNumber).padEnd(4)}${C.reset} ${branch} ${entry.priority} ${age}`);
|
|
92
|
+
});
|
|
93
|
+
// Also show failed
|
|
94
|
+
const failed = await storage.listFailed(repoId);
|
|
95
|
+
if (failed.length > 0) {
|
|
96
|
+
console.log(`\n${C.red}${C.bold}Failed PRs${C.reset}`);
|
|
97
|
+
console.log('\u2500'.repeat(70));
|
|
98
|
+
failed.forEach((entry) => {
|
|
99
|
+
console.log(` ${C.red}#${entry.prNumber}${C.reset} ${entry.sourceBranch} \u2014 ${entry.failureReason}`);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// Show blocked
|
|
103
|
+
const blocked = await storage.listBlocked(repoId);
|
|
104
|
+
if (blocked.length > 0) {
|
|
105
|
+
console.log(`\n${C.yellow}${C.bold}Blocked PRs${C.reset}`);
|
|
106
|
+
console.log('\u2500'.repeat(70));
|
|
107
|
+
blocked.forEach((entry) => {
|
|
108
|
+
console.log(` ${C.yellow}#${entry.prNumber}${C.reset} ${entry.sourceBranch} \u2014 ${entry.blockReason}`);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
console.log();
|
|
112
|
+
}
|
|
113
|
+
async function handleRetry(storage, repoId, prNumber) {
|
|
114
|
+
await storage.retry(repoId, prNumber);
|
|
115
|
+
console.log(`${C.green}PR #${prNumber} moved back to queue${C.reset}`);
|
|
116
|
+
}
|
|
117
|
+
async function handleSkip(storage, repoId, prNumber) {
|
|
118
|
+
await storage.skip(repoId, prNumber);
|
|
119
|
+
console.log(`${C.yellow}PR #${prNumber} removed from queue${C.reset}`);
|
|
120
|
+
}
|
|
121
|
+
async function handlePause(repoId) {
|
|
122
|
+
await redisSet(`merge:paused:${repoId}`, 'true');
|
|
123
|
+
console.log(`${C.yellow}Merge queue paused for repo: ${repoId}${C.reset}`);
|
|
124
|
+
}
|
|
125
|
+
async function handleResume(repoId) {
|
|
126
|
+
await redisDel(`merge:paused:${repoId}`);
|
|
127
|
+
console.log(`${C.green}Merge queue resumed for repo: ${repoId}${C.reset}`);
|
|
128
|
+
}
|
|
129
|
+
async function handlePriority(storage, repoId, prNumber, priority) {
|
|
130
|
+
await storage.reorder(repoId, prNumber, priority);
|
|
131
|
+
console.log(`${C.green}PR #${prNumber} priority updated to ${priority}${C.reset}`);
|
|
132
|
+
}
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Public entry point
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
/**
|
|
137
|
+
* Run a merge-queue command programmatically.
|
|
138
|
+
*
|
|
139
|
+
* Throws if REDIS_URL is not set or if required arguments are missing.
|
|
140
|
+
*/
|
|
141
|
+
export async function runMergeQueueCommand(config) {
|
|
142
|
+
ensureRedis();
|
|
143
|
+
const storage = new MergeQueueStorage();
|
|
144
|
+
const { repoId, prNumber, priority } = parseArgs(config.args);
|
|
145
|
+
try {
|
|
146
|
+
switch (config.command) {
|
|
147
|
+
case 'status':
|
|
148
|
+
await handleStatus(storage, repoId);
|
|
149
|
+
break;
|
|
150
|
+
case 'list':
|
|
151
|
+
await handleList(storage, repoId);
|
|
152
|
+
break;
|
|
153
|
+
case 'retry':
|
|
154
|
+
if (!prNumber) {
|
|
155
|
+
console.error(`${C.red}Error: PR number required. Usage: af merge-queue retry <prNumber>${C.reset}`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
await handleRetry(storage, repoId, prNumber);
|
|
159
|
+
break;
|
|
160
|
+
case 'skip':
|
|
161
|
+
if (!prNumber) {
|
|
162
|
+
console.error(`${C.red}Error: PR number required. Usage: af merge-queue skip <prNumber>${C.reset}`);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
await handleSkip(storage, repoId, prNumber);
|
|
166
|
+
break;
|
|
167
|
+
case 'pause':
|
|
168
|
+
await handlePause(repoId);
|
|
169
|
+
break;
|
|
170
|
+
case 'resume':
|
|
171
|
+
await handleResume(repoId);
|
|
172
|
+
break;
|
|
173
|
+
case 'priority':
|
|
174
|
+
if (!prNumber || priority === undefined) {
|
|
175
|
+
console.error(`${C.red}Error: PR number and priority required. Usage: af merge-queue priority <prNumber> <priority>${C.reset}`);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
await handlePriority(storage, repoId, prNumber, priority);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
await disconnectRedis();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/orchestrator-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,iBAAiB,
|
|
1
|
+
{"version":3,"file":"orchestrator-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/orchestrator-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,iBAAiB,EAEvB,MAAM,wBAAwB,CAAA;AAkB/B,MAAM,WAAW,wBAAwB;IACvC,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAA;IACpB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kDAAkD;IAClD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,qBAAqB,CAAA;IACjC,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACpD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;IAC5C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;IAC/C,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAC1D,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;CAClD;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC,CAAA;IAChD,SAAS,EAAE,YAAY,EAAE,CAAA;CAC1B;AAMD,wBAAgB,UAAU,IAAI,MAAM,CASnC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAYjD;AA0CD,wBAAsB,eAAe,CACnC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,wBAAwB,CAAC,CAmGnC"}
|
|
@@ -9,7 +9,14 @@ import path from 'path';
|
|
|
9
9
|
import { execSync } from 'child_process';
|
|
10
10
|
import { createOrchestrator, } from '@renseiai/agentfactory';
|
|
11
11
|
import { LinearIssueTrackerClient, createLinearStatusMappings, linearPlugin, } from '@renseiai/plugin-linear';
|
|
12
|
-
|
|
12
|
+
let codeIntelligencePlugin;
|
|
13
|
+
try {
|
|
14
|
+
;
|
|
15
|
+
({ codeIntelligencePlugin } = await import('@renseiai/agentfactory-code-intelligence'));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// code-intelligence is optional — agents run without af_code_* tools
|
|
19
|
+
}
|
|
13
20
|
// ---------------------------------------------------------------------------
|
|
14
21
|
// Helpers
|
|
15
22
|
// ---------------------------------------------------------------------------
|
|
@@ -88,7 +95,7 @@ export async function runOrchestrator(config) {
|
|
|
88
95
|
linearApiKey: config.linearApiKey,
|
|
89
96
|
issueTrackerClient,
|
|
90
97
|
statusMappings,
|
|
91
|
-
toolPlugins: [linearPlugin, codeIntelligencePlugin],
|
|
98
|
+
toolPlugins: [linearPlugin, codeIntelligencePlugin].filter(Boolean),
|
|
92
99
|
};
|
|
93
100
|
if (config.templateDir) {
|
|
94
101
|
orchestratorConfig.templateDir = config.templateDir;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/worker-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"worker-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/worker-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgCH,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mDAAmD;IACnD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AA4ED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAi/Bf"}
|
|
@@ -10,7 +10,14 @@ import { execSync } from 'child_process';
|
|
|
10
10
|
import os from 'os';
|
|
11
11
|
import { createOrchestrator, createLogger, } from '@renseiai/agentfactory';
|
|
12
12
|
import { LinearIssueTrackerClient, createLinearStatusMappings, linearPlugin, } from '@renseiai/plugin-linear';
|
|
13
|
-
|
|
13
|
+
let codeIntelligencePlugin;
|
|
14
|
+
try {
|
|
15
|
+
;
|
|
16
|
+
({ codeIntelligencePlugin } = await import('@renseiai/agentfactory-code-intelligence'));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// code-intelligence is optional — agents run without af_code_* tools
|
|
20
|
+
}
|
|
14
21
|
// ---------------------------------------------------------------------------
|
|
15
22
|
// Helpers (stateless)
|
|
16
23
|
// ---------------------------------------------------------------------------
|
|
@@ -424,7 +431,7 @@ export async function runWorker(config, signal) {
|
|
|
424
431
|
worktreePath: path.resolve(gitRoot, '..', path.basename(gitRoot) + '.wt'),
|
|
425
432
|
issueTrackerClient,
|
|
426
433
|
statusMappings,
|
|
427
|
-
toolPlugins: [linearPlugin, codeIntelligencePlugin],
|
|
434
|
+
toolPlugins: [linearPlugin, codeIntelligencePlugin].filter(Boolean),
|
|
428
435
|
apiActivityConfig: {
|
|
429
436
|
baseUrl: workerConfig.apiUrl,
|
|
430
437
|
apiKey: workerConfig.apiKey,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AgentFactory Merge Queue CLI
|
|
4
|
+
*
|
|
5
|
+
* Thin wrapper around the merge-queue runner. Handles dotenv, arg parsing,
|
|
6
|
+
* and process.exit so the runner stays process-agnostic.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* af merge-queue <command> [options]
|
|
10
|
+
*
|
|
11
|
+
* Commands:
|
|
12
|
+
* status [--repo <repoId>] Show queue overview
|
|
13
|
+
* list [--repo <repoId>] List all queued PRs
|
|
14
|
+
* retry <prNumber> [--repo <repoId>] Move failed/blocked PR back to queue
|
|
15
|
+
* skip <prNumber> [--repo <repoId>] Remove PR from queue
|
|
16
|
+
* pause [--repo <repoId>] Pause queue processing
|
|
17
|
+
* resume [--repo <repoId>] Resume queue processing
|
|
18
|
+
* priority <prNumber> <priority> [--repo <repoId>] Change PR priority
|
|
19
|
+
*
|
|
20
|
+
* Environment (loaded from .env.local in CWD):
|
|
21
|
+
* REDIS_URL Required for Redis connection
|
|
22
|
+
*/
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=merge-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-queue.d.ts","sourceRoot":"","sources":["../../src/merge-queue.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;GAoBG"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AgentFactory Merge Queue CLI
|
|
4
|
+
*
|
|
5
|
+
* Thin wrapper around the merge-queue runner. Handles dotenv, arg parsing,
|
|
6
|
+
* and process.exit so the runner stays process-agnostic.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* af merge-queue <command> [options]
|
|
10
|
+
*
|
|
11
|
+
* Commands:
|
|
12
|
+
* status [--repo <repoId>] Show queue overview
|
|
13
|
+
* list [--repo <repoId>] List all queued PRs
|
|
14
|
+
* retry <prNumber> [--repo <repoId>] Move failed/blocked PR back to queue
|
|
15
|
+
* skip <prNumber> [--repo <repoId>] Remove PR from queue
|
|
16
|
+
* pause [--repo <repoId>] Pause queue processing
|
|
17
|
+
* resume [--repo <repoId>] Resume queue processing
|
|
18
|
+
* priority <prNumber> <priority> [--repo <repoId>] Change PR priority
|
|
19
|
+
*
|
|
20
|
+
* Environment (loaded from .env.local in CWD):
|
|
21
|
+
* REDIS_URL Required for Redis connection
|
|
22
|
+
*/
|
|
23
|
+
import path from 'path';
|
|
24
|
+
import { config } from 'dotenv';
|
|
25
|
+
// Load environment variables from .env.local in CWD
|
|
26
|
+
config({ path: path.resolve(process.cwd(), '.env.local') });
|
|
27
|
+
import { runMergeQueueCommand, C } from './lib/merge-queue-runner.js';
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Usage
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
function printUsage() {
|
|
32
|
+
console.log(`
|
|
33
|
+
${C.cyan}AgentFactory Merge Queue${C.reset} - Manage the Refinery merge queue
|
|
34
|
+
|
|
35
|
+
${C.yellow}Usage:${C.reset}
|
|
36
|
+
af merge-queue <command> [options]
|
|
37
|
+
|
|
38
|
+
${C.yellow}Commands:${C.reset}
|
|
39
|
+
status [--repo <repoId>] Show queue overview
|
|
40
|
+
list [--repo <repoId>] List all queued PRs
|
|
41
|
+
retry <prNumber> [--repo <repoId>] Move failed/blocked PR back to queue
|
|
42
|
+
skip <prNumber> [--repo <repoId>] Remove PR from queue
|
|
43
|
+
pause [--repo <repoId>] Pause queue processing
|
|
44
|
+
resume [--repo <repoId>] Resume queue processing
|
|
45
|
+
priority <prNumber> <priority> [--repo <repoId>] Change PR priority
|
|
46
|
+
|
|
47
|
+
${C.yellow}Examples:${C.reset}
|
|
48
|
+
af merge-queue status
|
|
49
|
+
af merge-queue list --repo my-org/my-repo
|
|
50
|
+
af merge-queue retry 42
|
|
51
|
+
af merge-queue skip 42 --repo my-org/my-repo
|
|
52
|
+
af merge-queue pause
|
|
53
|
+
af merge-queue resume
|
|
54
|
+
af merge-queue priority 42 1
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Valid commands
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
const VALID_COMMANDS = new Set([
|
|
61
|
+
'status',
|
|
62
|
+
'list',
|
|
63
|
+
'retry',
|
|
64
|
+
'skip',
|
|
65
|
+
'pause',
|
|
66
|
+
'resume',
|
|
67
|
+
'priority',
|
|
68
|
+
]);
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Main
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
async function main() {
|
|
73
|
+
const [command, ...args] = process.argv.slice(3); // argv[2] is 'merge-queue'
|
|
74
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
75
|
+
printUsage();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!VALID_COMMANDS.has(command)) {
|
|
79
|
+
console.error(`Unknown command: ${command}`);
|
|
80
|
+
printUsage();
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
await runMergeQueueCommand({
|
|
84
|
+
command: command,
|
|
85
|
+
args,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
main().catch((error) => {
|
|
89
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@renseiai/agentfactory-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI tools for AgentFactory — local orchestrator, remote worker, queue admin",
|
|
6
6
|
"author": "Rensei AI (https://rensei.ai)",
|
|
@@ -40,7 +40,9 @@
|
|
|
40
40
|
"af-sync-routes": "dist/src/sync-routes.js",
|
|
41
41
|
"af-setup": "dist/src/setup.js",
|
|
42
42
|
"af-status": "dist/src/status.js",
|
|
43
|
-
"af-migrate-worktrees": "dist/src/migrate-worktrees.js"
|
|
43
|
+
"af-migrate-worktrees": "dist/src/migrate-worktrees.js",
|
|
44
|
+
"af-merge-queue": "dist/src/merge-queue.js",
|
|
45
|
+
"af-code": "dist/src/code.js"
|
|
44
46
|
},
|
|
45
47
|
"main": "./dist/src/index.js",
|
|
46
48
|
"module": "./dist/src/index.js",
|
|
@@ -105,6 +107,16 @@
|
|
|
105
107
|
"types": "./dist/src/lib/status-runner.d.ts",
|
|
106
108
|
"import": "./dist/src/lib/status-runner.js",
|
|
107
109
|
"default": "./dist/src/lib/status-runner.js"
|
|
110
|
+
},
|
|
111
|
+
"./merge-queue": {
|
|
112
|
+
"types": "./dist/src/lib/merge-queue-runner.d.ts",
|
|
113
|
+
"import": "./dist/src/lib/merge-queue-runner.js",
|
|
114
|
+
"default": "./dist/src/lib/merge-queue-runner.js"
|
|
115
|
+
},
|
|
116
|
+
"./code-intelligence": {
|
|
117
|
+
"types": "./dist/src/lib/code-intelligence-runner.d.ts",
|
|
118
|
+
"import": "./dist/src/lib/code-intelligence-runner.js",
|
|
119
|
+
"default": "./dist/src/lib/code-intelligence-runner.js"
|
|
108
120
|
}
|
|
109
121
|
},
|
|
110
122
|
"files": [
|
|
@@ -114,10 +126,12 @@
|
|
|
114
126
|
],
|
|
115
127
|
"dependencies": {
|
|
116
128
|
"dotenv": "^17.2.3",
|
|
117
|
-
"@renseiai/agentfactory
|
|
118
|
-
"@renseiai/agentfactory-
|
|
119
|
-
"@renseiai/plugin-linear": "0.8.
|
|
120
|
-
|
|
129
|
+
"@renseiai/agentfactory": "0.8.13",
|
|
130
|
+
"@renseiai/agentfactory-server": "0.8.13",
|
|
131
|
+
"@renseiai/plugin-linear": "0.8.13"
|
|
132
|
+
},
|
|
133
|
+
"optionalDependencies": {
|
|
134
|
+
"@renseiai/agentfactory-code-intelligence": "0.8.13"
|
|
121
135
|
},
|
|
122
136
|
"devDependencies": {
|
|
123
137
|
"@types/node": "^22.5.4",
|