@renseiai/agentfactory-cli 0.8.6 → 0.8.8

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.
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * AgentFactory Worktree Cleanup CLI
3
+ * AgentFactory Cleanup CLI
4
4
  *
5
- * Thin wrapper around the cleanup runner.
5
+ * Cleans up orphaned git worktrees and stale local branches.
6
6
  *
7
7
  * Usage:
8
8
  * af-cleanup [options]
9
9
  *
10
10
  * Options:
11
- * --dry-run Show what would be cleaned up without removing anything
12
- * --force Force removal even if worktree appears active
13
- * --path <dir> Custom worktrees directory (default: .worktrees)
14
- * --help, -h Show this help message
11
+ * --dry-run Show what would be cleaned up without removing anything
12
+ * --force Force removal / include branches with gone remotes
13
+ * --path <dir> Custom worktrees directory (default: .worktrees)
14
+ * --skip-worktrees Skip worktree cleanup
15
+ * --skip-branches Skip branch cleanup
16
+ * --help, -h Show this help message
15
17
  */
16
18
  export {};
17
19
  //# sourceMappingURL=cleanup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/cleanup.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG"}
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/cleanup.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG"}
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * AgentFactory Worktree Cleanup CLI
3
+ * AgentFactory Cleanup CLI
4
4
  *
5
- * Thin wrapper around the cleanup runner.
5
+ * Cleans up orphaned git worktrees and stale local branches.
6
6
  *
7
7
  * Usage:
8
8
  * af-cleanup [options]
9
9
  *
10
10
  * Options:
11
- * --dry-run Show what would be cleaned up without removing anything
12
- * --force Force removal even if worktree appears active
13
- * --path <dir> Custom worktrees directory (default: .worktrees)
14
- * --help, -h Show this help message
11
+ * --dry-run Show what would be cleaned up without removing anything
12
+ * --force Force removal / include branches with gone remotes
13
+ * --path <dir> Custom worktrees directory (default: .worktrees)
14
+ * --skip-worktrees Skip worktree cleanup
15
+ * --skip-branches Skip branch cleanup
16
+ * --help, -h Show this help message
15
17
  */
16
18
  import { basename } from 'path';
17
19
  import { runCleanup, getGitRoot } from './lib/cleanup-runner.js';
@@ -21,6 +23,8 @@ function parseArgs() {
21
23
  dryRun: false,
22
24
  force: false,
23
25
  worktreePath: undefined,
26
+ skipWorktrees: false,
27
+ skipBranches: false,
24
28
  };
25
29
  for (let i = 0; i < args.length; i++) {
26
30
  const arg = args[i];
@@ -34,6 +38,12 @@ function parseArgs() {
34
38
  case '--path':
35
39
  result.worktreePath = args[++i];
36
40
  break;
41
+ case '--skip-worktrees':
42
+ result.skipWorktrees = true;
43
+ break;
44
+ case '--skip-branches':
45
+ result.skipBranches = true;
46
+ break;
37
47
  case '--help':
38
48
  case '-h':
39
49
  printHelp();
@@ -44,67 +54,101 @@ function parseArgs() {
44
54
  }
45
55
  function printHelp() {
46
56
  console.log(`
47
- AgentFactory Worktree Cleanup - Remove orphaned git worktrees
57
+ AgentFactory Cleanup - Remove orphaned worktrees and stale branches
48
58
 
49
59
  Usage:
50
60
  af-cleanup [options]
51
61
 
52
62
  Options:
53
- --dry-run Show what would be cleaned up without removing
54
- --force Force removal even if worktree appears active
55
- --path <dir> Custom worktrees directory (default: .worktrees)
56
- --help, -h Show this help message
63
+ --dry-run Show what would be cleaned up without removing
64
+ --force Force worktree removal + delete branches with gone remotes
65
+ --path <dir> Custom worktrees directory (default: .worktrees)
66
+ --skip-worktrees Skip worktree cleanup
67
+ --skip-branches Skip branch cleanup
68
+ --help, -h Show this help message
57
69
 
58
- Orphaned worktrees are identified by:
59
- - Branch no longer exists (merged/deleted)
60
- - Not listed in 'git worktree list' (stale directory)
61
- - Lock file exists but is stale
70
+ Worktree cleanup:
71
+ Orphaned worktrees are identified by:
72
+ - Branch no longer exists (merged/deleted)
73
+ - Not listed in 'git worktree list' (stale directory)
74
+
75
+ Branch cleanup:
76
+ By default, deletes local branches already merged into main.
77
+ With --force, also deletes branches whose remote tracking branch is gone.
62
78
 
63
79
  Examples:
64
80
  # Preview what would be cleaned up
65
81
  af-cleanup --dry-run
66
82
 
67
- # Clean up orphaned worktrees
83
+ # Clean up everything (merged branches + orphaned worktrees)
68
84
  af-cleanup
69
85
 
70
- # Force cleanup all worktrees (use with caution)
86
+ # Aggressive cleanup (includes branches with gone remotes)
71
87
  af-cleanup --force
88
+
89
+ # Only clean up branches
90
+ af-cleanup --skip-worktrees
91
+
92
+ # Only clean up worktrees
93
+ af-cleanup --skip-branches
72
94
  `);
73
95
  }
74
96
  function printSummary(result) {
75
97
  console.log('=== Summary ===\n');
76
- console.log(` Scanned: ${result.scanned} worktree(s)`);
77
- console.log(` Orphaned: ${result.orphaned}`);
78
- console.log(` Cleaned: ${result.cleaned}`);
79
- if (result.skipped > 0) {
80
- console.log(` Skipped: ${result.skipped} (IDE/process still open — use --force)`);
98
+ if (result.scanned > 0 || result.orphaned > 0) {
99
+ console.log(' Worktrees:');
100
+ console.log(` Scanned: ${result.scanned}`);
101
+ console.log(` Orphaned: ${result.orphaned}`);
102
+ console.log(` Cleaned: ${result.cleaned}`);
103
+ if (result.skipped > 0) {
104
+ console.log(` Skipped: ${result.skipped} (IDE/process still open — use --force)`);
105
+ }
106
+ if (result.errors.length > 0) {
107
+ console.log(` Errors: ${result.errors.length}`);
108
+ for (const err of result.errors) {
109
+ console.log(` - ${basename(err.path)}: ${err.error}`);
110
+ }
111
+ }
112
+ console.log('');
81
113
  }
82
- if (result.errors.length > 0) {
83
- console.log(` Errors: ${result.errors.length}`);
84
- for (const err of result.errors) {
85
- console.log(` - ${basename(err.path)}: ${err.error}`);
114
+ if (result.branches.scanned > 0 || result.branches.deleted > 0) {
115
+ console.log(' Branches:');
116
+ console.log(` Scanned: ${result.branches.scanned}`);
117
+ console.log(` Deleted: ${result.branches.deleted}`);
118
+ if (result.branches.errors.length > 0) {
119
+ console.log(` Errors: ${result.branches.errors.length}`);
120
+ for (const err of result.branches.errors) {
121
+ console.log(` - ${err.branch}: ${err.error}`);
122
+ }
86
123
  }
124
+ console.log('');
125
+ }
126
+ const totalErrors = result.errors.length + result.branches.errors.length;
127
+ if (totalErrors === 0 && result.cleaned === 0 && result.branches.deleted === 0) {
128
+ console.log(' Nothing to clean up.\n');
87
129
  }
88
- console.log('');
89
130
  }
90
131
  // Main execution
91
132
  function main() {
92
133
  const args = parseArgs();
93
- console.log('\n=== AgentFactory Worktree Cleanup ===\n');
134
+ console.log('\n=== AgentFactory Cleanup ===\n');
94
135
  if (args.dryRun) {
95
136
  console.log('[DRY RUN MODE - No changes will be made]\n');
96
137
  }
97
138
  if (args.force) {
98
- console.log('[FORCE MODE - All worktrees will be removed]\n');
139
+ console.log('[FORCE MODE - Aggressive cleanup enabled]\n');
99
140
  }
100
141
  const result = runCleanup({
101
142
  dryRun: args.dryRun,
102
143
  force: args.force,
103
144
  worktreePath: args.worktreePath,
104
145
  gitRoot: getGitRoot(),
146
+ skipWorktrees: args.skipWorktrees,
147
+ skipBranches: args.skipBranches,
105
148
  });
106
149
  printSummary(result);
107
- if (result.errors.length > 0) {
150
+ const totalErrors = result.errors.length + result.branches.errors.length;
151
+ if (totalErrors > 0) {
108
152
  process.exit(1);
109
153
  }
110
154
  }
@@ -31,7 +31,7 @@ import { createRealDependencies } from './lib/governor-dependencies.js';
31
31
  import { printStartupBanner, printScanSummary, printCircuitBreakerWarning, } from './lib/governor-logger.js';
32
32
  import { getVersion, checkForUpdate, printUpdateNotification } from './lib/version.js';
33
33
  import { maybeAutoUpdate } from './lib/auto-updater.js';
34
- import { createLinearAgentClient } from '@renseiai/agentfactory-linear';
34
+ import { createLinearAgentClient } from '@renseiai/plugin-linear';
35
35
  import { createLogger, initTouchpointStorage } from '@renseiai/agentfactory';
36
36
  import { RedisOverrideStorage, listStoredWorkspaces, getAccessToken, createRedisTokenBucket, createRedisCircuitBreaker, } from '@renseiai/agentfactory-server';
37
37
  // ---------------------------------------------------------------------------
@@ -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,CAwBzB"}
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,CAyBzB"}
package/dist/src/index.js CHANGED
@@ -25,6 +25,7 @@ Commands:
25
25
  analyze-logs Analyze agent session logs for errors
26
26
  linear Linear issue tracker operations
27
27
  sync-routes Generate missing route and page files from manifest
28
+ status Show fleet status (inline one-line summary)
28
29
  help Show this help message
29
30
 
30
31
  Run 'agentfactory <command> --help' for command-specific options.
@@ -63,6 +64,9 @@ switch (command) {
63
64
  case 'sync-routes':
64
65
  import('./sync-routes');
65
66
  break;
67
+ case 'status':
68
+ import('./status');
69
+ break;
66
70
  case 'help':
67
71
  case '--help':
68
72
  case '-h':
@@ -1 +1 @@
1
- {"version":3,"file":"agent-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/agent-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAiBH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAA;AAE5E,MAAM,WAAW,iBAAiB;IAChC,yBAAyB;IACzB,OAAO,EAAE,YAAY,CAAA;IACrB,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wEAAwE;IACxE,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAMD,eAAO,MAAM,CAAC;;;;;;;CAOJ,CAAA;AAuRV,wBAAsB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBvE"}
1
+ {"version":3,"file":"agent-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/agent-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAiBH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAA;AAE5E,MAAM,WAAW,iBAAiB;IAChC,yBAAyB;IACzB,OAAO,EAAE,YAAY,CAAA;IACrB,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wEAAwE;IACxE,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAMD,eAAO,MAAM,CAAC;;;;;;;CAOJ,CAAA;AA8RV,wBAAsB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBvE"}
@@ -5,8 +5,8 @@
5
5
  * agent sessions. Works by updating Redis state directly — workers poll for
6
6
  * status changes and pending prompts every 5 seconds.
7
7
  */
8
- import { getRedisClient, getAllSessions, updateSessionStatus, storeSessionState, storePendingPrompt, disconnectRedis, } from '@renseiai/agentfactory-server';
9
- import { createLinearAgentClient } from '@renseiai/agentfactory-linear';
8
+ import { getRedisClient, getAllSessions, updateSessionStatus, storeSessionState, publishUrgent, disconnectRedis, } from '@renseiai/agentfactory-server';
9
+ import { createLinearAgentClient } from '@renseiai/plugin-linear';
10
10
  // ---------------------------------------------------------------------------
11
11
  // ANSI colors
12
12
  // ---------------------------------------------------------------------------
@@ -114,12 +114,23 @@ async function chatWithAgent(issueId, message) {
114
114
  await disconnectRedis();
115
115
  return;
116
116
  }
117
- const prompt = await storePendingPrompt(session.linearSessionId, session.issueId, message);
118
- if (prompt) {
119
- console.log(`${C.green}Message queued${C.reset} (id: ${prompt.id})worker will pick up within ~5 seconds`);
117
+ const agentId = session.agentId;
118
+ if (!agentId) {
119
+ console.error(`${C.red}Session has no agentIdcannot publish to inbox${C.reset}`);
120
+ await disconnectRedis();
121
+ return;
120
122
  }
121
- else {
122
- console.error(`${C.red}Failed to store pending prompt${C.reset}`);
123
+ try {
124
+ const streamId = await publishUrgent(agentId, {
125
+ type: 'directive',
126
+ sessionId: session.linearSessionId,
127
+ payload: message,
128
+ createdAt: Date.now(),
129
+ });
130
+ console.log(`${C.green}Message queued${C.reset} (id: ${streamId}) — worker will pick up within ~5 seconds`);
131
+ }
132
+ catch (err) {
133
+ console.error(`${C.red}Failed to publish to inbox: ${err instanceof Error ? err.message : String(err)}${C.reset}`);
123
134
  }
124
135
  await disconnectRedis();
125
136
  }
@@ -13,8 +13,12 @@ export interface CleanupRunnerConfig {
13
13
  worktreePath?: string;
14
14
  /** Git root for default worktree path (default: auto-detect) */
15
15
  gitRoot?: string;
16
+ /** Skip worktree cleanup (default: false) */
17
+ skipWorktrees?: boolean;
18
+ /** Skip branch cleanup (default: false) */
19
+ skipBranches?: boolean;
16
20
  }
17
- export interface CleanupResult {
21
+ export interface WorktreeCleanupResult {
18
22
  scanned: number;
19
23
  orphaned: number;
20
24
  cleaned: number;
@@ -24,6 +28,17 @@ export interface CleanupResult {
24
28
  error: string;
25
29
  }>;
26
30
  }
31
+ export interface CleanupResult extends WorktreeCleanupResult {
32
+ branches: BranchCleanupResult;
33
+ }
34
+ export interface BranchCleanupResult {
35
+ scanned: number;
36
+ deleted: number;
37
+ errors: Array<{
38
+ branch: string;
39
+ error: string;
40
+ }>;
41
+ }
27
42
  export declare function getGitRoot(): string;
28
43
  export declare function runCleanup(config?: CleanupRunnerConfig): CleanupResult;
29
44
  //# sourceMappingURL=cleanup-runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/cleanup-runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/C;AAuBD,wBAAgB,UAAU,IAAI,MAAM,CASnC;AAgSD,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,aAAa,CAUtE"}
1
+ {"version":3,"file":"cleanup-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/cleanup-runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6CAA6C;IAC7C,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/C;AAED,MAAM,WAAW,aAAc,SAAQ,qBAAqB;IAC1D,QAAQ,EAAE,mBAAmB,CAAA;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACjD;AAuBD,wBAAgB,UAAU,IAAI,MAAM,CASnC;AA8bD,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,aAAa,CAkBtE"}
@@ -282,6 +282,150 @@ function cleanup(options) {
282
282
  return result;
283
283
  }
284
284
  // ---------------------------------------------------------------------------
285
+ // Branch cleanup
286
+ // ---------------------------------------------------------------------------
287
+ /**
288
+ * Get the current branch name (to avoid deleting it).
289
+ */
290
+ function getCurrentBranch() {
291
+ try {
292
+ return execSync('git rev-parse --abbrev-ref HEAD', {
293
+ encoding: 'utf-8',
294
+ stdio: ['pipe', 'pipe', 'pipe'],
295
+ }).trim();
296
+ }
297
+ catch {
298
+ return '';
299
+ }
300
+ }
301
+ /**
302
+ * Get branches that have been merged into the given base branch.
303
+ */
304
+ function getMergedBranches(baseBranch) {
305
+ try {
306
+ const output = execSync(`git branch --merged ${baseBranch}`, {
307
+ encoding: 'utf-8',
308
+ stdio: ['pipe', 'pipe', 'pipe'],
309
+ });
310
+ return output
311
+ .split('\n')
312
+ .map((line) => line.replace(/^[*+]?\s+/, '').trim())
313
+ .filter((b) => b && b !== baseBranch);
314
+ }
315
+ catch {
316
+ return [];
317
+ }
318
+ }
319
+ /**
320
+ * Get local branches whose remote tracking branch is gone.
321
+ */
322
+ function getGoneBranches() {
323
+ try {
324
+ // Fetch prune first so we have up-to-date remote state
325
+ execSync('git fetch --prune 2>/dev/null || true', {
326
+ encoding: 'utf-8',
327
+ stdio: ['pipe', 'pipe', 'pipe'],
328
+ timeout: 15000,
329
+ });
330
+ const output = execSync('git branch -vv', {
331
+ encoding: 'utf-8',
332
+ stdio: ['pipe', 'pipe', 'pipe'],
333
+ });
334
+ const gone = [];
335
+ for (const line of output.split('\n')) {
336
+ // Match lines like " SUP-123 abc1234 [origin/SUP-123: gone] commit msg"
337
+ if (/\[.*: gone\]/.test(line)) {
338
+ const branch = line.replace(/^\*?\s+/, '').split(/\s+/)[0];
339
+ if (branch)
340
+ gone.push(branch);
341
+ }
342
+ }
343
+ return gone;
344
+ }
345
+ catch {
346
+ return [];
347
+ }
348
+ }
349
+ /**
350
+ * Clean up stale local branches.
351
+ *
352
+ * Default: deletes branches merged into main.
353
+ * With force: also deletes branches whose remote tracking branch is gone.
354
+ */
355
+ function cleanupBranches(options) {
356
+ const result = {
357
+ scanned: 0,
358
+ deleted: 0,
359
+ errors: [],
360
+ };
361
+ // Prune stale worktree metadata first so that branches from prunable
362
+ // worktrees (e.g. research agents) are no longer locked.
363
+ try {
364
+ execSync('git worktree prune', {
365
+ encoding: 'utf-8',
366
+ stdio: ['pipe', 'pipe', 'pipe'],
367
+ });
368
+ }
369
+ catch {
370
+ // Ignore prune errors
371
+ }
372
+ console.log('Scanning local branches...\n');
373
+ const currentBranch = getCurrentBranch();
374
+ // Determine the base branch (main or master)
375
+ const baseBranch = branchExists('main') ? 'main' : branchExists('master') ? 'master' : '';
376
+ if (!baseBranch) {
377
+ console.log('Could not determine base branch (main/master). Skipping branch cleanup.\n');
378
+ return result;
379
+ }
380
+ // Collect branches to delete
381
+ const toDelete = new Set();
382
+ const mergedBranches = getMergedBranches(baseBranch);
383
+ for (const b of mergedBranches) {
384
+ toDelete.add(b);
385
+ }
386
+ if (options.force) {
387
+ const goneBranches = getGoneBranches();
388
+ for (const b of goneBranches) {
389
+ toDelete.add(b);
390
+ }
391
+ }
392
+ // Never delete base branch or current branch
393
+ toDelete.delete(baseBranch);
394
+ toDelete.delete(currentBranch);
395
+ result.scanned = toDelete.size;
396
+ if (toDelete.size === 0) {
397
+ console.log('No stale branches found.\n');
398
+ return result;
399
+ }
400
+ const merged = new Set(mergedBranches);
401
+ const sorted = [...toDelete].sort();
402
+ for (const branch of sorted) {
403
+ const isMerged = merged.has(branch);
404
+ const reason = isMerged ? 'merged' : 'remote gone';
405
+ if (options.dryRun) {
406
+ console.log(` Would delete: ${branch} (${reason})`);
407
+ continue;
408
+ }
409
+ // Use -d for merged, -D for gone (unmerged)
410
+ const flag = isMerged ? '-d' : '-D';
411
+ try {
412
+ execSync(`git branch ${flag} "${branch}"`, {
413
+ encoding: 'utf-8',
414
+ stdio: ['pipe', 'pipe', 'pipe'],
415
+ });
416
+ console.log(` deleted: ${branch} (${reason})`);
417
+ result.deleted++;
418
+ }
419
+ catch (err) {
420
+ const msg = err instanceof Error ? err.message : String(err);
421
+ console.log(` FAILED: ${branch} — ${msg}`);
422
+ result.errors.push({ branch, error: msg });
423
+ }
424
+ }
425
+ console.log('');
426
+ return result;
427
+ }
428
+ // ---------------------------------------------------------------------------
285
429
  // Runner
286
430
  // ---------------------------------------------------------------------------
287
431
  export function runCleanup(config) {
@@ -291,5 +435,11 @@ export function runCleanup(config) {
291
435
  force: config?.force ?? false,
292
436
  worktreePath: config?.worktreePath ?? resolve(gitRoot, '.worktrees'),
293
437
  };
294
- return cleanup(options);
438
+ const worktreeResult = config?.skipWorktrees
439
+ ? { scanned: 0, orphaned: 0, cleaned: 0, skipped: 0, errors: [] }
440
+ : cleanup(options);
441
+ const branchResult = config?.skipBranches
442
+ ? { scanned: 0, deleted: 0, errors: [] }
443
+ : cleanupBranches(options);
444
+ return { ...worktreeResult, branches: branchResult };
295
445
  }
@@ -5,7 +5,7 @@
5
5
  * using the Linear SDK (via LinearAgentClient) and Redis storage
6
6
  * (from @renseiai/agentfactory-server).
7
7
  */
8
- import type { LinearAgentClient, WorkflowContext } from '@renseiai/agentfactory-linear';
8
+ import type { LinearAgentClient, WorkflowContext } from '@renseiai/plugin-linear';
9
9
  import type { GovernorDependencies } from '@renseiai/agentfactory';
10
10
  export interface RealDependenciesConfig {
11
11
  linearClient: LinearAgentClient;
@@ -1 +1 @@
1
- {"version":3,"file":"governor-dependencies.d.ts","sourceRoot":"","sources":["../../../src/lib/governor-dependencies.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AACvF,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,wBAAwB,CAAA;AAiC/B,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,iBAAiB,CAAA;IAC/B,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAA;IACjE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,eAAe,KAAK,MAAM,CAAA;CAC9H;AA6BD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,oBAAoB,CA6TtB"}
1
+ {"version":3,"file":"governor-dependencies.d.ts","sourceRoot":"","sources":["../../../src/lib/governor-dependencies.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACjF,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,wBAAwB,CAAA;AAiC/B,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,iBAAiB,CAAA;IAC/B,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAA;IACjE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,eAAe,KAAK,MAAM,CAAA;CAC9H;AA6BD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,oBAAoB,CA8TtB"}
@@ -252,6 +252,8 @@ export function createRealDependencies(config) {
252
252
  if (parentIssueIds.has(issueId)) {
253
253
  if (workType === 'development')
254
254
  workType = 'coordination';
255
+ else if (workType === 'inflight')
256
+ workType = 'inflight-coordination';
255
257
  else if (workType === 'qa')
256
258
  workType = 'qa-coordination';
257
259
  else if (workType === 'acceptance')
@@ -5,7 +5,7 @@
5
5
  * to avoid external dependencies like chalk.
6
6
  */
7
7
  import type { ScanResult } from '@renseiai/agentfactory';
8
- import type { LinearApiQuota } from '@renseiai/agentfactory-linear';
8
+ import type { LinearApiQuota } from '@renseiai/plugin-linear';
9
9
  export interface StartupBannerConfig {
10
10
  version: string;
11
11
  projects: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"governor-logger.d.ts","sourceRoot":"","sources":["../../../src/lib/governor-logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAkCnE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE;QACR,YAAY,EAAE,OAAO,CAAA;QACrB,mBAAmB,EAAE,OAAO,CAAA;QAC5B,eAAe,EAAE,OAAO,CAAA;QACxB,MAAM,EAAE,OAAO,CAAA;QACf,cAAc,EAAE,OAAO,CAAA;KACxB,CAAA;IACD,cAAc,EAAE,OAAO,CAAA;IACvB,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAqFpE;AAMD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,UAAU,EAAE,EACrB,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,cAAc,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,CA6CN;AAMD,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAuC5E;AAMD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAa/D"}
1
+ {"version":3,"file":"governor-logger.d.ts","sourceRoot":"","sources":["../../../src/lib/governor-logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAkC7D,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE;QACR,YAAY,EAAE,OAAO,CAAA;QACrB,mBAAmB,EAAE,OAAO,CAAA;QAC5B,eAAe,EAAE,OAAO,CAAA;QACxB,MAAM,EAAE,OAAO,CAAA;QACf,cAAc,EAAE,OAAO,CAAA;KACxB,CAAA;IACD,cAAc,EAAE,OAAO,CAAA;IACvB,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAqFpE;AAMD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,UAAU,EAAE,EACrB,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,cAAc,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,CA6CN;AAMD,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAuC5E;AAMD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAa/D"}
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Linear CLI Runner — re-exports from @renseiai/agentfactory (core).
2
+ * Linear CLI Runner — re-exports from @renseiai/plugin-linear.
3
3
  *
4
- * The canonical implementation lives in packages/core/src/tools/linear-runner.ts
4
+ * The canonical implementation lives in packages/linear/src/tools/linear-runner.ts
5
5
  * so both the CLI and the in-process tool plugin share a single source of truth.
6
6
  */
7
- export { runLinear, parseLinearArgs, type LinearRunnerConfig, type LinearRunnerResult, } from '@renseiai/agentfactory';
7
+ export { runLinear, parseLinearArgs, type LinearRunnerConfig, type LinearRunnerResult, } from '@renseiai/plugin-linear';
8
8
  //# sourceMappingURL=linear-runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"linear-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/linear-runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,SAAS,EACT,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAA"}
1
+ {"version":3,"file":"linear-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/linear-runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,SAAS,EACT,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,yBAAyB,CAAA"}
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Linear CLI Runner — re-exports from @renseiai/agentfactory (core).
2
+ * Linear CLI Runner — re-exports from @renseiai/plugin-linear.
3
3
  *
4
- * The canonical implementation lives in packages/core/src/tools/linear-runner.ts
4
+ * The canonical implementation lives in packages/linear/src/tools/linear-runner.ts
5
5
  * so both the CLI and the in-process tool plugin share a single source of truth.
6
6
  */
7
- export { runLinear, parseLinearArgs, } from '@renseiai/agentfactory';
7
+ export { runLinear, parseLinearArgs, } from '@renseiai/plugin-linear';
@@ -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,EACjB,KAAK,iBAAiB,EACvB,MAAM,wBAAwB,CAAA;AAM/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,CA6FnC"}
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,EACvB,MAAM,wBAAwB,CAAA;AAY/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"}
@@ -8,6 +8,8 @@
8
8
  import path from 'path';
9
9
  import { execSync } from 'child_process';
10
10
  import { createOrchestrator, } from '@renseiai/agentfactory';
11
+ import { LinearIssueTrackerClient, createLinearStatusMappings, linearPlugin, } from '@renseiai/plugin-linear';
12
+ import { codeIntelligencePlugin } from '@renseiai/agentfactory-code-intelligence';
11
13
  // ---------------------------------------------------------------------------
12
14
  // Helpers
13
15
  // ---------------------------------------------------------------------------
@@ -77,11 +79,16 @@ export async function runOrchestrator(config) {
77
79
  const dryRun = config.dryRun ?? false;
78
80
  const gitRoot = config.gitRoot ?? getGitRoot();
79
81
  const cb = config.callbacks ?? defaultCallbacks();
82
+ const issueTrackerClient = new LinearIssueTrackerClient({ apiKey: config.linearApiKey });
83
+ const statusMappings = createLinearStatusMappings();
80
84
  const orchestratorConfig = {
81
85
  project: config.project,
82
86
  maxConcurrent,
83
87
  worktreePath: path.resolve(gitRoot, '.worktrees'),
84
88
  linearApiKey: config.linearApiKey,
89
+ issueTrackerClient,
90
+ statusMappings,
91
+ toolPlugins: [linearPlugin, codeIntelligencePlugin],
85
92
  };
86
93
  if (config.templateDir) {
87
94
  orchestratorConfig.templateDir = config.templateDir;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Status Runner -- Programmatic API for the af-status CLI.
3
+ *
4
+ * Provides fleet status via the AgentFactory public API.
5
+ * In TTY mode, spawns the Go `af-status` binary for rich inline output.
6
+ * In piped/JSON mode, fetches stats directly from Node.js.
7
+ */
8
+ export interface StatusRunnerConfig {
9
+ /** Output raw JSON instead of human-readable format */
10
+ json?: boolean;
11
+ /** Enable auto-refresh watch mode */
12
+ watch?: boolean;
13
+ /** Watch interval (e.g., "1s", "5s") — only used with watch */
14
+ interval?: string;
15
+ /** API base URL override */
16
+ url?: string;
17
+ }
18
+ export declare const C: {
19
+ readonly reset: "\u001B[0m";
20
+ readonly red: "\u001B[31m";
21
+ readonly green: "\u001B[32m";
22
+ readonly yellow: "\u001B[33m";
23
+ readonly cyan: "\u001B[36m";
24
+ readonly gray: "\u001B[90m";
25
+ };
26
+ export declare function runStatus(config: StatusRunnerConfig): Promise<void>;
27
+ //# sourceMappingURL=status-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/status-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAMD,eAAO,MAAM,CAAC;;;;;;;CAOJ,CAAA;AAuEV,wBAAsB,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBzE"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Status Runner -- Programmatic API for the af-status CLI.
3
+ *
4
+ * Provides fleet status via the AgentFactory public API.
5
+ * In TTY mode, spawns the Go `af-status` binary for rich inline output.
6
+ * In piped/JSON mode, fetches stats directly from Node.js.
7
+ */
8
+ import { spawn } from 'child_process';
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+ // ---------------------------------------------------------------------------
14
+ // ANSI colors (same as agent-runner)
15
+ // ---------------------------------------------------------------------------
16
+ export const C = {
17
+ reset: '\x1b[0m',
18
+ red: '\x1b[31m',
19
+ green: '\x1b[32m',
20
+ yellow: '\x1b[33m',
21
+ cyan: '\x1b[36m',
22
+ gray: '\x1b[90m',
23
+ };
24
+ // ---------------------------------------------------------------------------
25
+ // Helpers
26
+ // ---------------------------------------------------------------------------
27
+ function getBaseURL(override) {
28
+ return override ?? process.env.WORKER_API_URL ?? 'http://localhost:3000';
29
+ }
30
+ async function fetchStats(baseURL) {
31
+ const url = `${baseURL.replace(/\/+$/, '')}/api/public/stats`;
32
+ const resp = await fetch(url);
33
+ if (!resp.ok) {
34
+ throw new Error(`API request failed: ${resp.status} ${resp.statusText}`);
35
+ }
36
+ return resp.json();
37
+ }
38
+ function resolveGoBinary() {
39
+ // Go binary lives in packages/tui/bin/af-status relative to the CLI package
40
+ return path.resolve(__dirname, '../../tui/bin/af-status');
41
+ }
42
+ // ---------------------------------------------------------------------------
43
+ // Command handlers
44
+ // ---------------------------------------------------------------------------
45
+ async function showJSON(baseURL) {
46
+ const stats = await fetchStats(baseURL);
47
+ console.log(JSON.stringify(stats, null, 2));
48
+ }
49
+ function spawnGoBinary(config, baseURL) {
50
+ return new Promise((resolve, reject) => {
51
+ const binPath = resolveGoBinary();
52
+ const args = ['--url', baseURL];
53
+ if (config.json)
54
+ args.push('--json');
55
+ if (config.watch)
56
+ args.push('--watch');
57
+ if (config.interval)
58
+ args.push('--interval', config.interval);
59
+ const child = spawn(binPath, args, {
60
+ stdio: 'inherit',
61
+ });
62
+ child.on('error', (err) => {
63
+ if (err.code === 'ENOENT') {
64
+ // Go binary not built yet — fall back to Node.js JSON output
65
+ console.error(`${C.yellow}Go binary not found at ${binPath}${C.reset}`);
66
+ console.error(`${C.gray}Falling back to JSON output. Build with: cd packages/tui && make build-status${C.reset}`);
67
+ showJSON(baseURL).then(resolve, reject);
68
+ }
69
+ else {
70
+ reject(err);
71
+ }
72
+ });
73
+ child.on('close', (code) => {
74
+ if (code === 0) {
75
+ resolve();
76
+ }
77
+ else {
78
+ reject(new Error(`af-status exited with code ${code}`));
79
+ }
80
+ });
81
+ });
82
+ }
83
+ // ---------------------------------------------------------------------------
84
+ // Public entry point
85
+ // ---------------------------------------------------------------------------
86
+ export async function runStatus(config) {
87
+ const baseURL = getBaseURL(config.url);
88
+ const isTTY = process.stdout.isTTY ?? false;
89
+ // If piped (non-TTY) and not explicitly requesting watch, default to JSON
90
+ if (!isTTY && !config.watch) {
91
+ await showJSON(baseURL);
92
+ return;
93
+ }
94
+ // If --json flag without watch, just fetch and print
95
+ if (config.json && !config.watch) {
96
+ await showJSON(baseURL);
97
+ return;
98
+ }
99
+ // TTY mode or watch mode — delegate to Go binary
100
+ await spawnGoBinary(config, baseURL);
101
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"worker-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/worker-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAmBH,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;AAwED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAm7Bf"}
1
+ {"version":3,"file":"worker-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/worker-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAyBH,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;AAyED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA89Bf"}
@@ -9,6 +9,8 @@ import path from 'path';
9
9
  import { execSync } from 'child_process';
10
10
  import os from 'os';
11
11
  import { createOrchestrator, createLogger, } from '@renseiai/agentfactory';
12
+ import { LinearIssueTrackerClient, createLinearStatusMappings, linearPlugin, } from '@renseiai/plugin-linear';
13
+ import { codeIntelligencePlugin } from '@renseiai/agentfactory-code-intelligence';
12
14
  // ---------------------------------------------------------------------------
13
15
  // Helpers (stateless)
14
16
  // ---------------------------------------------------------------------------
@@ -275,25 +277,26 @@ export async function runWorker(config, signal) {
275
277
  // -----------------------------------------------------------------------
276
278
  async function pollForWork() {
277
279
  if (!workerId)
278
- return { work: [], pendingPrompts: {}, hasPendingPrompts: false };
280
+ return { work: [], inboxMessages: {}, hasInboxMessages: false };
279
281
  const result = await apiRequestWithError(`/api/workers/${workerId}/poll`);
280
282
  if (result.error?.type === 'worker_not_found') {
281
283
  await attemptReregistration();
282
- return { work: [], pendingPrompts: {}, hasPendingPrompts: false };
284
+ return { work: [], inboxMessages: {}, hasInboxMessages: false };
283
285
  }
284
286
  if (!result.data) {
285
- return { work: [], pendingPrompts: {}, hasPendingPrompts: false };
287
+ return { work: [], inboxMessages: {}, hasInboxMessages: false };
286
288
  }
287
289
  const pollData = result.data;
288
- if (pollData.hasPendingPrompts) {
289
- const totalPrompts = Object.values(pollData.pendingPrompts).reduce((sum, prompts) => sum + prompts.length, 0);
290
- log.info('Received pending prompts', {
291
- sessionCount: Object.keys(pollData.pendingPrompts).length,
292
- totalPrompts,
293
- sessions: Object.entries(pollData.pendingPrompts).map(([sessionId, prompts]) => ({
290
+ if (pollData.hasInboxMessages) {
291
+ const totalMessages = Object.values(pollData.inboxMessages).reduce((sum, messages) => sum + messages.length, 0);
292
+ log.info('Received inbox messages', {
293
+ sessionCount: Object.keys(pollData.inboxMessages).length,
294
+ totalMessages,
295
+ sessions: Object.entries(pollData.inboxMessages).map(([sessionId, messages]) => ({
294
296
  sessionId: sessionId.substring(0, 8),
295
- promptCount: prompts.length,
296
- promptIds: prompts.map((p) => p.id),
297
+ messageCount: messages.length,
298
+ messageIds: messages.map((m) => m.id),
299
+ types: messages.map((m) => m.type),
297
300
  })),
298
301
  });
299
302
  }
@@ -307,6 +310,15 @@ export async function runWorker(config, signal) {
307
310
  body: JSON.stringify({ workerId }),
308
311
  });
309
312
  }
313
+ async function ackInboxMessage(sessionId, message) {
314
+ await apiRequest(`/api/sessions/${sessionId}/inbox/ack`, {
315
+ method: 'POST',
316
+ body: JSON.stringify({
317
+ messageId: message.id,
318
+ lane: message.lane,
319
+ }),
320
+ });
321
+ }
310
322
  async function reportStatus(sessionId, status, extra) {
311
323
  if (!workerId)
312
324
  return;
@@ -405,9 +417,14 @@ export async function runWorker(config, signal) {
405
417
  ? `Resuming work on ${work.issueIdentifier}`
406
418
  : `Worker claimed ${work.issueIdentifier}. Setting up environment...`);
407
419
  // Create orchestrator with API activity proxy
420
+ const issueTrackerClient = new LinearIssueTrackerClient({ apiKey: linearApiKey });
421
+ const statusMappings = createLinearStatusMappings();
408
422
  const orchestrator = createOrchestrator({
409
423
  maxConcurrent: 1,
410
424
  worktreePath: path.resolve(gitRoot, '.worktrees'),
425
+ issueTrackerClient,
426
+ statusMappings,
427
+ toolPlugins: [linearPlugin, codeIntelligencePlugin],
411
428
  apiActivityConfig: {
412
429
  baseUrl: workerConfig.apiUrl,
413
430
  apiKey: workerConfig.apiKey,
@@ -689,68 +706,94 @@ export async function runWorker(config, signal) {
689
706
  }
690
707
  }
691
708
  }
692
- // Handle pending prompts for active sessions
693
- if (pollResult.hasPendingPrompts) {
694
- for (const [sessionId, prompts] of Object.entries(pollResult.pendingPrompts)) {
695
- for (const prompt of prompts) {
696
- log.info('Processing pending prompt', {
709
+ // Handle inbox messages for active sessions (urgent-first)
710
+ if (pollResult.hasInboxMessages) {
711
+ for (const [sessionId, messages] of Object.entries(pollResult.inboxMessages)) {
712
+ for (const message of messages) {
713
+ // Handle nudge: no-op wake signal, just discard
714
+ if (message.type === 'nudge') {
715
+ log.debug('Nudge received (no-op)', {
716
+ sessionId: sessionId.substring(0, 8),
717
+ messageId: message.id,
718
+ });
719
+ await ackInboxMessage(sessionId, message);
720
+ continue;
721
+ }
722
+ const orchestrator = activeOrchestrators.get(sessionId);
723
+ // Handle stop signal: trigger graceful agent shutdown
724
+ if (message.type === 'stop') {
725
+ log.info('Stop signal received', {
726
+ sessionId: sessionId.substring(0, 8),
727
+ messageId: message.id,
728
+ });
729
+ if (orchestrator) {
730
+ const agent = orchestrator.getAgentBySession(sessionId);
731
+ if (agent) {
732
+ try {
733
+ await orchestrator.stopAgent(agent.issueId, false);
734
+ log.success('Agent stopped via inbox signal', {
735
+ sessionId: sessionId.substring(0, 8),
736
+ });
737
+ }
738
+ catch (error) {
739
+ log.error('Failed to stop agent', {
740
+ sessionId: sessionId.substring(0, 8),
741
+ error: error instanceof Error ? error.message : String(error),
742
+ });
743
+ }
744
+ }
745
+ }
746
+ await ackInboxMessage(sessionId, message);
747
+ continue;
748
+ }
749
+ // Handle directive and hook-result: inject as follow-up prompt
750
+ log.info('Processing inbox message', {
697
751
  sessionId: sessionId.substring(0, 8),
698
- promptId: prompt.id,
699
- promptLength: prompt.prompt.length,
700
- userName: prompt.userName,
752
+ messageId: message.id,
753
+ type: message.type,
754
+ lane: message.lane,
755
+ payloadLength: message.payload.length,
756
+ userName: message.userName,
701
757
  });
702
- const orchestrator = activeOrchestrators.get(sessionId);
703
758
  if (!orchestrator) {
704
759
  log.warn('No active orchestrator found for session', {
705
760
  sessionId: sessionId.substring(0, 8),
706
- promptId: prompt.id,
761
+ messageId: message.id,
707
762
  });
708
763
  continue;
709
764
  }
710
765
  const agent = orchestrator.getAgentBySession(sessionId);
711
766
  const providerSessionId = agent?.providerSessionId;
712
- log.info('Forwarding prompt to provider session', {
713
- sessionId: sessionId.substring(0, 8),
714
- promptId: prompt.id,
715
- hasProviderSession: !!providerSessionId,
716
- agentStatus: agent?.status,
717
- });
718
767
  try {
719
- const result = await orchestrator.forwardPrompt(prompt.issueId, sessionId, prompt.prompt, providerSessionId, agent?.workType);
768
+ const result = await orchestrator.forwardPrompt(agent?.issueId ?? '', sessionId, message.payload, providerSessionId, agent?.workType);
720
769
  if (result.forwarded) {
721
770
  log.success(result.injected
722
771
  ? 'Message injected into running session'
723
- : 'Prompt forwarded successfully', {
772
+ : 'Inbox message forwarded successfully', {
724
773
  sessionId: sessionId.substring(0, 8),
725
- promptId: prompt.id,
774
+ messageId: message.id,
775
+ type: message.type,
726
776
  injected: result.injected ?? false,
727
777
  resumed: result.resumed,
728
778
  newAgentPid: result.agent?.pid,
729
779
  });
730
- const claimResult = await apiRequest(`/api/sessions/${sessionId}/prompts`, {
731
- method: 'POST',
732
- body: JSON.stringify({ promptId: prompt.id }),
733
- });
734
- if (claimResult?.claimed) {
735
- log.debug('Prompt claimed', { promptId: prompt.id });
736
- }
737
- else {
738
- log.warn('Failed to claim prompt', { promptId: prompt.id });
739
- }
780
+ // ACK after successful delivery
781
+ await ackInboxMessage(sessionId, message);
740
782
  }
741
783
  else {
742
- log.error('Failed to forward prompt', {
784
+ log.error('Failed to forward inbox message', {
743
785
  sessionId: sessionId.substring(0, 8),
744
- promptId: prompt.id,
786
+ messageId: message.id,
787
+ type: message.type,
745
788
  reason: result.reason,
746
789
  error: result.error?.message,
747
790
  });
748
791
  }
749
792
  }
750
793
  catch (error) {
751
- log.error('Error forwarding prompt', {
794
+ log.error('Error forwarding inbox message', {
752
795
  sessionId: sessionId.substring(0, 8),
753
- promptId: prompt.id,
796
+ messageId: message.id,
754
797
  error: error instanceof Error ? error.message : String(error),
755
798
  });
756
799
  }
@@ -13,6 +13,7 @@
13
13
  * update-issue <id> Update an existing issue
14
14
  * list-comments <issueId> List comments on an issue
15
15
  * create-comment <issueId> Create a comment on an issue
16
+ * list-issues List issues with flexible filters
16
17
  * list-backlog-issues List backlog issues for a project
17
18
  * list-unblocked-backlog List unblocked backlog issues
18
19
  * check-blocked <id> Check if an issue is blocked
@@ -1 +1 @@
1
- {"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/linear.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG"}
1
+ {"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/linear.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG"}
@@ -13,6 +13,7 @@
13
13
  * update-issue <id> Update an existing issue
14
14
  * list-comments <issueId> List comments on an issue
15
15
  * create-comment <issueId> Create a comment on an issue
16
+ * list-issues List issues with flexible filters
16
17
  * list-backlog-issues List backlog issues for a project
17
18
  * list-unblocked-backlog List unblocked backlog issues
18
19
  * check-blocked <id> Check if an issue is blocked
@@ -51,6 +52,7 @@ Commands:
51
52
  update-issue <id> Update an existing issue
52
53
  list-comments <issueId> List comments on an issue
53
54
  create-comment <issueId> Create a comment on an issue
55
+ list-issues List issues with flexible filters
54
56
  list-backlog-issues List backlog issues for a project
55
57
  list-unblocked-backlog List unblocked backlog issues
56
58
  check-blocked <id> Check if an issue is blocked
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AgentFactory Status CLI
4
+ *
5
+ * Quick fleet status checks for terminal and scripting use.
6
+ *
7
+ * Usage:
8
+ * af-status One-line fleet summary (via Go binary in TTY)
9
+ * af-status --json JSON stats to stdout
10
+ * af-status --watch Auto-refresh every 3 seconds
11
+ * af-status --watch --interval 5s Custom refresh interval
12
+ * af-status | jq Pipe-friendly (auto-detects non-TTY)
13
+ *
14
+ * Environment (loaded from .env.local in CWD):
15
+ * WORKER_API_URL API base URL (default: http://localhost:3000)
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/status.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG"}
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AgentFactory Status CLI
4
+ *
5
+ * Quick fleet status checks for terminal and scripting use.
6
+ *
7
+ * Usage:
8
+ * af-status One-line fleet summary (via Go binary in TTY)
9
+ * af-status --json JSON stats to stdout
10
+ * af-status --watch Auto-refresh every 3 seconds
11
+ * af-status --watch --interval 5s Custom refresh interval
12
+ * af-status | jq Pipe-friendly (auto-detects non-TTY)
13
+ *
14
+ * Environment (loaded from .env.local in CWD):
15
+ * WORKER_API_URL API base URL (default: http://localhost:3000)
16
+ */
17
+ import path from 'path';
18
+ import { config } from 'dotenv';
19
+ // Load environment variables from .env.local in CWD
20
+ config({ path: path.resolve(process.cwd(), '.env.local') });
21
+ import { runStatus, C } from './lib/status-runner.js';
22
+ // ---------------------------------------------------------------------------
23
+ // Usage
24
+ // ---------------------------------------------------------------------------
25
+ function printUsage() {
26
+ console.log(`
27
+ ${C.cyan}AgentFactory Status${C.reset} - Quick fleet status checks
28
+
29
+ ${C.yellow}Usage:${C.reset}
30
+ af-status [options]
31
+
32
+ ${C.yellow}Options:${C.reset}
33
+ --json Output raw JSON stats to stdout
34
+ --watch Auto-refresh status every 3 seconds
35
+ --interval <duration> Custom refresh interval (e.g., 1s, 5s) [default: 3s]
36
+ --url <url> API base URL [default: WORKER_API_URL or http://localhost:3000]
37
+ --help, -h Show this help message
38
+
39
+ ${C.yellow}Examples:${C.reset}
40
+ af-status One-line fleet summary
41
+ af-status --json JSON output
42
+ af-status --json | jq .agentsWorking Extract a field
43
+ af-status --watch Auto-refresh mode
44
+ af-status --watch --interval 1s Refresh every second
45
+
46
+ ${C.yellow}Environment:${C.reset}
47
+ WORKER_API_URL API base URL (default: http://localhost:3000)
48
+ `);
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // Main
52
+ // ---------------------------------------------------------------------------
53
+ async function main() {
54
+ const args = process.argv.slice(2);
55
+ if (args.includes('--help') || args.includes('-h') || args.includes('help')) {
56
+ printUsage();
57
+ return;
58
+ }
59
+ const jsonMode = args.includes('--json');
60
+ const watchMode = args.includes('--watch');
61
+ // Parse --interval value
62
+ let interval;
63
+ const intervalIdx = args.indexOf('--interval');
64
+ if (intervalIdx !== -1 && intervalIdx + 1 < args.length) {
65
+ interval = args[intervalIdx + 1];
66
+ }
67
+ // Parse --url value
68
+ let url;
69
+ const urlIdx = args.indexOf('--url');
70
+ if (urlIdx !== -1 && urlIdx + 1 < args.length) {
71
+ url = args[urlIdx + 1];
72
+ }
73
+ await runStatus({ json: jsonMode, watch: watchMode, interval, url });
74
+ }
75
+ main().catch((error) => {
76
+ console.error('Error:', error instanceof Error ? error.message : error);
77
+ process.exit(1);
78
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@renseiai/agentfactory-cli",
3
- "version": "0.8.6",
3
+ "version": "0.8.8",
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)",
@@ -37,7 +37,8 @@
37
37
  "af-analyze-logs": "dist/src/analyze-logs.js",
38
38
  "af-linear": "dist/src/linear.js",
39
39
  "af-governor": "dist/src/governor.js",
40
- "af-sync-routes": "dist/src/sync-routes.js"
40
+ "af-sync-routes": "dist/src/sync-routes.js",
41
+ "af-status": "dist/src/status.js"
41
42
  },
42
43
  "main": "./dist/src/index.js",
43
44
  "module": "./dist/src/index.js",
@@ -92,6 +93,11 @@
92
93
  "types": "./dist/src/lib/sync-routes-runner.d.ts",
93
94
  "import": "./dist/src/lib/sync-routes-runner.js",
94
95
  "default": "./dist/src/lib/sync-routes-runner.js"
96
+ },
97
+ "./status": {
98
+ "types": "./dist/src/lib/status-runner.d.ts",
99
+ "import": "./dist/src/lib/status-runner.js",
100
+ "default": "./dist/src/lib/status-runner.js"
95
101
  }
96
102
  },
97
103
  "files": [
@@ -101,9 +107,10 @@
101
107
  ],
102
108
  "dependencies": {
103
109
  "dotenv": "^17.2.3",
104
- "@renseiai/agentfactory": "0.8.6",
105
- "@renseiai/agentfactory-linear": "0.8.6",
106
- "@renseiai/agentfactory-server": "0.8.6"
110
+ "@renseiai/agentfactory-code-intelligence": "0.8.8",
111
+ "@renseiai/plugin-linear": "0.8.8",
112
+ "@renseiai/agentfactory": "0.8.8",
113
+ "@renseiai/agentfactory-server": "0.8.8"
107
114
  },
108
115
  "devDependencies": {
109
116
  "@types/node": "^22.5.4",