@supaku/agentfactory-cli 0.7.13 → 0.7.15

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 +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,CAsBzB"}
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,CAuBzB"}
package/dist/src/index.js CHANGED
@@ -23,6 +23,7 @@ Commands:
23
23
  queue-admin Manage Redis work queue and sessions
24
24
  analyze-logs Analyze agent session logs for errors
25
25
  linear Linear issue tracker operations
26
+ sync-routes Generate missing route and page files from manifest
26
27
  help Show this help message
27
28
 
28
29
  Run 'agentfactory <command> --help' for command-specific options.
@@ -55,6 +56,9 @@ switch (command) {
55
56
  case 'linear':
56
57
  import('./linear');
57
58
  break;
59
+ case 'sync-routes':
60
+ import('./sync-routes');
61
+ break;
58
62
  case 'help':
59
63
  case '--help':
60
64
  case '-h':
@@ -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,MAAM,6BAA6B,CAAA;AACpE,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,sBAAsB,CAAA;AAgC7B,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,iBAAiB,CAAA;CAChC;AAmFD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,oBAAoB,CAsPtB"}
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,MAAM,6BAA6B,CAAA;AACpE,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,sBAAsB,CAAA;AAgC7B,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,iBAAiB,CAAA;CAChC;AA6BD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,oBAAoB,CAqQtB"}
@@ -40,43 +40,6 @@ function actionToWorkType(action) {
40
40
  return 'development';
41
41
  }
42
42
  }
43
- // ---------------------------------------------------------------------------
44
- // Factory
45
- // ---------------------------------------------------------------------------
46
- // ---------------------------------------------------------------------------
47
- // Terminal statuses (issues in these states are excluded from scans)
48
- // ---------------------------------------------------------------------------
49
- const TERMINAL_STATUSES = ['Accepted', 'Canceled', 'Duplicate'];
50
- // ---------------------------------------------------------------------------
51
- // Helpers
52
- // ---------------------------------------------------------------------------
53
- /**
54
- * Convert a Linear SDK Issue to a GovernorIssue.
55
- * Resolves lazy-loaded relations (state, labels, parent, project).
56
- *
57
- * Uses `unknown` + explicit casts to avoid importing `Issue` from
58
- * `@linear/sdk`, keeping the CLI package dependency graph clean.
59
- */
60
- async function sdkIssueToGovernorIssue(issue) {
61
- // The Linear SDK Issue type uses LinearFetch (thenable) for lazy-loaded relations.
62
- // We cast to `any` to access these properties without importing the SDK types.
63
- const i = issue;
64
- const state = await i.state;
65
- const labels = await i.labels();
66
- const parent = await i.parent;
67
- const project = await i.project;
68
- return {
69
- id: i.id,
70
- identifier: i.identifier,
71
- title: i.title,
72
- description: i.description ?? undefined,
73
- status: state?.name ?? 'Backlog',
74
- labels: labels.nodes.map((l) => l.name),
75
- createdAt: i.createdAt.getTime(),
76
- parentId: parent?.id,
77
- project: project?.name,
78
- };
79
- }
80
43
  /**
81
44
  * Create real GovernorDependencies backed by the Linear SDK and Redis.
82
45
  *
@@ -85,24 +48,34 @@ async function sdkIssueToGovernorIssue(issue) {
85
48
  */
86
49
  export function createRealDependencies(config) {
87
50
  const processingState = new RedisProcessingStateStorage();
51
+ // Cache of parent issue IDs populated by listIssues() single GraphQL query.
52
+ // Eliminates per-issue isParentIssue() API calls during governor scans.
53
+ const parentIssueIds = new Set();
88
54
  return {
89
55
  // -----------------------------------------------------------------------
90
- // 1. listIssues -- scan Linear project for non-terminal issues
56
+ // 1. listIssues -- scan Linear project using single GraphQL query
91
57
  // -----------------------------------------------------------------------
92
58
  listIssues: async (project) => {
93
59
  try {
94
- const linearClient = config.linearClient.linearClient;
95
- const issueConnection = await linearClient.issues({
96
- filter: {
97
- project: { name: { eq: project } },
98
- state: { name: { nin: [...TERMINAL_STATUSES] } },
99
- },
100
- });
101
- const results = [];
102
- for (const issue of issueConnection.nodes) {
103
- results.push(await sdkIssueToGovernorIssue(issue));
60
+ const rawIssues = await config.linearClient.listProjectIssues(project);
61
+ // Cache parent issue IDs for isParentIssue() lookups
62
+ parentIssueIds.clear();
63
+ for (const issue of rawIssues) {
64
+ if (issue.childCount > 0) {
65
+ parentIssueIds.add(issue.id);
66
+ }
104
67
  }
105
- return results;
68
+ return rawIssues.map((issue) => ({
69
+ id: issue.id,
70
+ identifier: issue.identifier,
71
+ title: issue.title,
72
+ description: issue.description,
73
+ status: issue.status,
74
+ labels: issue.labels,
75
+ createdAt: issue.createdAt,
76
+ parentId: issue.parentId,
77
+ project: issue.project,
78
+ }));
106
79
  }
107
80
  catch (err) {
108
81
  log.error('listIssues failed', {
@@ -147,9 +120,13 @@ export function createRealDependencies(config) {
147
120
  }
148
121
  },
149
122
  // -----------------------------------------------------------------------
150
- // 4. isParentIssue -- check via Linear API
123
+ // 4. isParentIssue -- check cache first, fall back to API
151
124
  // -----------------------------------------------------------------------
152
125
  isParentIssue: async (issueId) => {
126
+ // Check cached parent IDs from listIssues (populated by single GraphQL query)
127
+ if (parentIssueIds.has(issueId))
128
+ return true;
129
+ // Fall back to API for issues not in the last scan (e.g., webhook-driven)
153
130
  try {
154
131
  return await config.linearClient.isParentIssue(issueId);
155
132
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Sync Routes Runner -- Programmatic API for the af-sync-routes CLI.
3
+ *
4
+ * Exports `runSyncRoutes()` so route syncing can be invoked from code
5
+ * without going through process.argv / process.env / process.exit.
6
+ */
7
+ export interface SyncRoutesConfig {
8
+ /** Preview what would be created without writing (default: false) */
9
+ dryRun?: boolean;
10
+ /** Also sync dashboard page.tsx files (default: false) */
11
+ pages?: boolean;
12
+ /** Custom app directory (default: "src/app") */
13
+ appDir?: string;
14
+ /** Project root directory (default: process.cwd()) */
15
+ projectRoot?: string;
16
+ }
17
+ export interface SyncRoutesResult {
18
+ checked: number;
19
+ created: number;
20
+ skipped: number;
21
+ errors: Array<{
22
+ path: string;
23
+ error: string;
24
+ }>;
25
+ warnings: string[];
26
+ }
27
+ export declare function runSyncRoutes(config?: SyncRoutesConfig): SyncRoutesResult;
28
+ //# sourceMappingURL=sync-routes-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-routes-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/sync-routes-runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,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;IAC9C,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAMD,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,CA6GzE"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Sync Routes Runner -- Programmatic API for the af-sync-routes CLI.
3
+ *
4
+ * Exports `runSyncRoutes()` so route syncing can be invoked from code
5
+ * without going through process.argv / process.env / process.exit.
6
+ */
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
8
+ import { dirname, resolve } from 'path';
9
+ import { ROUTE_MANIFEST, generateRouteContent, generatePageContent, } from '@supaku/agentfactory';
10
+ // ---------------------------------------------------------------------------
11
+ // Runner
12
+ // ---------------------------------------------------------------------------
13
+ export function runSyncRoutes(config) {
14
+ const projectRoot = config?.projectRoot ?? process.cwd();
15
+ const appDir = config?.appDir ?? 'src/app';
16
+ const dryRun = config?.dryRun ?? false;
17
+ const syncPages = config?.pages ?? false;
18
+ const result = {
19
+ checked: 0,
20
+ created: 0,
21
+ skipped: 0,
22
+ errors: [],
23
+ warnings: [],
24
+ };
25
+ // Validate project structure
26
+ const srcDir = resolve(projectRoot, 'src');
27
+ if (!existsSync(srcDir)) {
28
+ result.errors.push({ path: srcDir, error: 'src/ directory not found — is this a Next.js project?' });
29
+ return result;
30
+ }
31
+ const configFile = resolve(projectRoot, 'src/lib/config.ts');
32
+ if (!existsSync(configFile)) {
33
+ result.warnings.push('src/lib/config.ts not found — route files import { routes } from this file');
34
+ }
35
+ // Check for dashboard dependency when syncing pages
36
+ if (syncPages) {
37
+ const pkgJsonPath = resolve(projectRoot, 'package.json');
38
+ if (existsSync(pkgJsonPath)) {
39
+ try {
40
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
41
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
42
+ if (!allDeps['@supaku/agentfactory-dashboard']) {
43
+ result.warnings.push('@supaku/agentfactory-dashboard not found in dependencies — page files require this package');
44
+ }
45
+ }
46
+ catch {
47
+ // Ignore JSON parse errors
48
+ }
49
+ }
50
+ }
51
+ // Sync route files
52
+ for (const entry of ROUTE_MANIFEST.routes) {
53
+ result.checked++;
54
+ const filePath = resolve(projectRoot, entry.path);
55
+ if (existsSync(filePath)) {
56
+ result.skipped++;
57
+ if (dryRun) {
58
+ console.log(` exists ${entry.path}`);
59
+ }
60
+ continue;
61
+ }
62
+ const content = generateRouteContent(entry);
63
+ if (dryRun) {
64
+ console.log(` create ${entry.path}`);
65
+ result.created++;
66
+ continue;
67
+ }
68
+ try {
69
+ mkdirSync(dirname(filePath), { recursive: true });
70
+ writeFileSync(filePath, content, 'utf-8');
71
+ console.log(` created ${entry.path}`);
72
+ result.created++;
73
+ }
74
+ catch (err) {
75
+ const message = err instanceof Error ? err.message : String(err);
76
+ result.errors.push({ path: entry.path, error: message });
77
+ }
78
+ }
79
+ // Sync page files (opt-in)
80
+ if (syncPages) {
81
+ for (const entry of ROUTE_MANIFEST.pages) {
82
+ result.checked++;
83
+ const filePath = resolve(projectRoot, entry.path);
84
+ if (existsSync(filePath)) {
85
+ result.skipped++;
86
+ if (dryRun) {
87
+ console.log(` exists ${entry.path}`);
88
+ }
89
+ continue;
90
+ }
91
+ const content = generatePageContent(entry);
92
+ if (dryRun) {
93
+ console.log(` create ${entry.path}`);
94
+ result.created++;
95
+ continue;
96
+ }
97
+ try {
98
+ mkdirSync(dirname(filePath), { recursive: true });
99
+ writeFileSync(filePath, content, 'utf-8');
100
+ console.log(` created ${entry.path}`);
101
+ result.created++;
102
+ }
103
+ catch (err) {
104
+ const message = err instanceof Error ? err.message : String(err);
105
+ result.errors.push({ path: entry.path, error: message });
106
+ }
107
+ }
108
+ }
109
+ return result;
110
+ }
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AgentFactory Route Sync CLI
4
+ *
5
+ * Generates missing route.ts and page.tsx files from the route manifest.
6
+ *
7
+ * Usage:
8
+ * af-sync-routes [options]
9
+ *
10
+ * Options:
11
+ * --dry-run Preview what would be created
12
+ * --pages Also sync dashboard page.tsx files
13
+ * --app-dir <p> Custom app directory (default: src/app)
14
+ * --help, -h Show this help message
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=sync-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-routes.d.ts","sourceRoot":"","sources":["../../src/sync-routes.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG"}
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AgentFactory Route Sync CLI
4
+ *
5
+ * Generates missing route.ts and page.tsx files from the route manifest.
6
+ *
7
+ * Usage:
8
+ * af-sync-routes [options]
9
+ *
10
+ * Options:
11
+ * --dry-run Preview what would be created
12
+ * --pages Also sync dashboard page.tsx files
13
+ * --app-dir <p> Custom app directory (default: src/app)
14
+ * --help, -h Show this help message
15
+ */
16
+ import { runSyncRoutes } from './lib/sync-routes-runner.js';
17
+ function parseArgs() {
18
+ const args = process.argv.slice(2);
19
+ const result = {
20
+ dryRun: false,
21
+ pages: false,
22
+ help: false,
23
+ };
24
+ for (let i = 0; i < args.length; i++) {
25
+ const arg = args[i];
26
+ switch (arg) {
27
+ case '--dry-run':
28
+ result.dryRun = true;
29
+ break;
30
+ case '--pages':
31
+ result.pages = true;
32
+ break;
33
+ case '--app-dir':
34
+ result.appDir = args[++i];
35
+ break;
36
+ case '--help':
37
+ case '-h':
38
+ result.help = true;
39
+ break;
40
+ }
41
+ }
42
+ return result;
43
+ }
44
+ function printHelp() {
45
+ console.log(`
46
+ AgentFactory Route Sync — Generate missing route and page files
47
+
48
+ Usage:
49
+ af-sync-routes [options]
50
+
51
+ Options:
52
+ --dry-run Preview what would be created without writing files
53
+ --pages Also sync dashboard page.tsx files (requires @supaku/agentfactory-dashboard)
54
+ --app-dir <p> Custom app directory (default: src/app)
55
+ --help, -h Show this help message
56
+
57
+ Examples:
58
+ # Preview missing routes
59
+ af-sync-routes --dry-run
60
+
61
+ # Create missing route files
62
+ af-sync-routes
63
+
64
+ # Also sync dashboard pages
65
+ af-sync-routes --pages
66
+
67
+ # After upgrading @supaku packages
68
+ pnpm bump:af && af-sync-routes --pages
69
+ `);
70
+ }
71
+ function main() {
72
+ const args = parseArgs();
73
+ if (args.help) {
74
+ printHelp();
75
+ return;
76
+ }
77
+ console.log('\n=== AgentFactory Route Sync ===\n');
78
+ if (args.dryRun) {
79
+ console.log('[DRY RUN MODE - No files will be written]\n');
80
+ }
81
+ const result = runSyncRoutes({
82
+ dryRun: args.dryRun,
83
+ pages: args.pages,
84
+ appDir: args.appDir,
85
+ });
86
+ // Print warnings
87
+ for (const warning of result.warnings) {
88
+ console.log(`\n warning: ${warning}`);
89
+ }
90
+ // Print summary
91
+ console.log(`\nSummary: checked=${result.checked} created=${result.created} skipped=${result.skipped} errors=${result.errors.length}`);
92
+ if (result.errors.length > 0) {
93
+ console.error('\nErrors:');
94
+ for (const err of result.errors) {
95
+ console.error(` ${err.path}: ${err.error}`);
96
+ }
97
+ process.exit(1);
98
+ }
99
+ }
100
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supaku/agentfactory-cli",
3
- "version": "0.7.13",
3
+ "version": "0.7.15",
4
4
  "type": "module",
5
5
  "description": "CLI tools for AgentFactory — local orchestrator, remote worker, queue admin",
6
6
  "author": "Supaku (https://supaku.com)",
@@ -35,7 +35,8 @@
35
35
  "af-queue-admin": "./dist/src/queue-admin.js",
36
36
  "af-analyze-logs": "./dist/src/analyze-logs.js",
37
37
  "af-linear": "./dist/src/linear.js",
38
- "af-governor": "./dist/src/governor.js"
38
+ "af-governor": "./dist/src/governor.js",
39
+ "af-sync-routes": "./dist/src/sync-routes.js"
39
40
  },
40
41
  "main": "./dist/src/index.js",
41
42
  "module": "./dist/src/index.js",
@@ -80,6 +81,11 @@
80
81
  "types": "./dist/src/lib/linear-runner.d.ts",
81
82
  "import": "./dist/src/lib/linear-runner.js",
82
83
  "default": "./dist/src/lib/linear-runner.js"
84
+ },
85
+ "./sync-routes": {
86
+ "types": "./dist/src/lib/sync-routes-runner.d.ts",
87
+ "import": "./dist/src/lib/sync-routes-runner.js",
88
+ "default": "./dist/src/lib/sync-routes-runner.js"
83
89
  }
84
90
  },
85
91
  "files": [
@@ -89,9 +95,9 @@
89
95
  ],
90
96
  "dependencies": {
91
97
  "dotenv": "^17.2.3",
92
- "@supaku/agentfactory-server": "0.7.13",
93
- "@supaku/agentfactory-linear": "0.7.13",
94
- "@supaku/agentfactory": "0.7.13"
98
+ "@supaku/agentfactory": "0.7.15",
99
+ "@supaku/agentfactory-server": "0.7.15",
100
+ "@supaku/agentfactory-linear": "0.7.15"
95
101
  },
96
102
  "devDependencies": {
97
103
  "@types/node": "^22.5.4",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=subpath-exports.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"subpath-exports.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/subpath-exports.test.ts"],"names":[],"mappings":""}
@@ -1,52 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- /**
3
- * Subpath export resolution tests.
4
- *
5
- * Verifies that every subpath export defined in package.json resolves
6
- * to a module that exports the expected function. These tests catch:
7
- * - Missing `default` condition in exports map (breaks tsx/CJS loaders)
8
- * - Mismatched file paths between exports map and built output
9
- * - Missing or renamed function exports
10
- */
11
- describe('@supaku/agentfactory-cli subpath exports', () => {
12
- it('exports runOrchestrator from ./orchestrator', async () => {
13
- const mod = await import('../lib/orchestrator-runner.js');
14
- expect(mod.runOrchestrator).toBeDefined();
15
- expect(typeof mod.runOrchestrator).toBe('function');
16
- });
17
- it('exports runWorker from ./worker', async () => {
18
- const mod = await import('../lib/worker-runner.js');
19
- expect(mod.runWorker).toBeDefined();
20
- expect(typeof mod.runWorker).toBe('function');
21
- });
22
- it('exports runWorkerFleet from ./worker-fleet', async () => {
23
- const mod = await import('../lib/worker-fleet-runner.js');
24
- expect(mod.runWorkerFleet).toBeDefined();
25
- expect(typeof mod.runWorkerFleet).toBe('function');
26
- });
27
- it('exports runCleanup from ./cleanup', async () => {
28
- const mod = await import('../lib/cleanup-runner.js');
29
- expect(mod.runCleanup).toBeDefined();
30
- expect(typeof mod.runCleanup).toBe('function');
31
- });
32
- it('exports runQueueAdmin from ./queue-admin', async () => {
33
- const mod = await import('../lib/queue-admin-runner.js');
34
- expect(mod.runQueueAdmin).toBeDefined();
35
- expect(typeof mod.runQueueAdmin).toBe('function');
36
- });
37
- it('exports runLogAnalyzer from ./analyze-logs', async () => {
38
- const mod = await import('../lib/analyze-logs-runner.js');
39
- expect(mod.runLogAnalyzer).toBeDefined();
40
- expect(typeof mod.runLogAnalyzer).toBe('function');
41
- });
42
- it('exports runLinear from ./linear', async () => {
43
- const mod = await import('../lib/linear-runner.js');
44
- expect(mod.runLinear).toBeDefined();
45
- expect(typeof mod.runLinear).toBe('function');
46
- });
47
- it('exports parseLinearArgs from ./linear', async () => {
48
- const mod = await import('../lib/linear-runner.js');
49
- expect(mod.parseLinearArgs).toBeDefined();
50
- expect(typeof mod.parseLinearArgs).toBe('function');
51
- });
52
- });