@crewx/cli 0.8.3-rc.2 → 0.8.3-rc.4

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,18 +1,14 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.createCliCrewx = createCliCrewx;
7
4
  const fs_1 = require("fs");
8
5
  const path_1 = require("path");
9
6
  const os_1 = require("os");
10
- const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
11
7
  const sdk_1 = require("@crewx/sdk");
12
8
  const plugins_1 = require("@crewx/sdk/plugins");
9
+ const repository_1 = require("@crewx/sdk/repository");
13
10
  const register_builtin_tools_1 = require("../register-builtin-tools");
14
11
  const version_1 = require("../utils/version");
15
- const tasks_1 = require("../schema/tasks");
16
12
  /**
17
13
  * Build a Crewx instance with CLI-standard plugins (FileLogger + SqliteTracing)
18
14
  * and built-in tools registered. Use this from any CLI command that needs a
@@ -38,22 +34,22 @@ async function createCliCrewx(configPath = process.env.CREWX_CONFIG ?? 'crewx.ya
38
34
  yamlPath = absConfigPath;
39
35
  }
40
36
  else if (isExplicitConfig) {
41
- // Explicit config was set but file doesn't exist — throw
42
37
  throw new Error(`[crewx] Config file not found: ${absConfigPath} (set via CREWX_CONFIG)`);
43
38
  }
44
39
  else {
45
- // No explicit config and file doesn't exist — built-in-only mode
46
40
  yamlPath = undefined;
47
41
  }
48
- // Ensure ~/.crewx/crewx.db schema exists before SqliteTracingPlugin opens it.
42
+ // Run drizzle migrations once at bootstrap plugin relies on this guarantee.
49
43
  const dbDir = (0, path_1.join)((0, os_1.homedir)(), '.crewx');
50
44
  (0, fs_1.mkdirSync)(dbDir, { recursive: true });
51
- const schemaDb = new better_sqlite3_1.default((0, path_1.join)(dbDir, 'crewx.db'));
52
- (0, tasks_1.ensureTasksSchema)(schemaDb);
53
- schemaDb.close();
54
- // Pass ourselves back to the SDK: when this Crewx encounters a file:// remote
55
- // agent, the SDK uses this factory to bootstrap the target Crewx instance
56
- // with the same plugin set (FileLogger + SqliteTracing + built-in tools).
45
+ const dbPath = (0, path_1.join)(dbDir, 'crewx.db');
46
+ const handle = (0, repository_1.openDrizzleDb)(dbPath);
47
+ try {
48
+ (0, repository_1.runMigrations)(handle.db);
49
+ }
50
+ finally {
51
+ handle.close();
52
+ }
57
53
  const crewx = await sdk_1.Crewx.loadYaml(yamlPath, {
58
54
  remoteFactory: createCliCrewx,
59
55
  });
@@ -11,11 +11,15 @@ export interface InitResult {
11
11
  hookInstalled: boolean;
12
12
  errors: string[];
13
13
  skippedReason?: 'yaml-exists' | 'nested-project';
14
+ workspaceId?: string;
15
+ slug?: string;
14
16
  }
15
17
  /**
16
18
  * Programmatic init — creates crewx.yaml, support dirs, and optionally installs hooks.
17
19
  * Throws on unrecoverable errors (PATH_NOT_FOUND, NESTED_CREWX_PROJECT, YAML_CREATE_FAILED).
18
- * Partial failures (hook install, mkdir) are collected in InitResult.errors without rollback.
20
+ * Partial failures (hook install, mkdir, workspace registration) are collected in InitResult.errors.
21
+ * Workspace registration is always attempted (even when yaml already exists) so that CLI and
22
+ * server paths both produce a row in ~/.crewx/crewx.db.
19
23
  */
20
24
  export declare function handleInit(opts: {
21
25
  path: string;
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.handleInit = handleInit;
12
12
  const fs_1 = require("fs");
13
13
  const path_1 = require("path");
14
+ const repository_1 = require("@crewx/sdk/repository");
14
15
  const install_1 = require("./hook/install");
15
16
  const paths_1 = require("./hook/paths");
16
17
  const DEFAULT_AGENTS = [
@@ -82,13 +83,16 @@ ${agentBlocks}
82
83
  /**
83
84
  * Programmatic init — creates crewx.yaml, support dirs, and optionally installs hooks.
84
85
  * Throws on unrecoverable errors (PATH_NOT_FOUND, NESTED_CREWX_PROJECT, YAML_CREATE_FAILED).
85
- * Partial failures (hook install, mkdir) are collected in InitResult.errors without rollback.
86
+ * Partial failures (hook install, mkdir, workspace registration) are collected in InitResult.errors.
87
+ * Workspace registration is always attempted (even when yaml already exists) so that CLI and
88
+ * server paths both produce a row in ~/.crewx/crewx.db.
86
89
  */
87
90
  async function handleInit(opts) {
88
91
  const { path: target, skipHook = false, force = false } = opts;
89
92
  const errors = [];
90
93
  let yamlCreated = false;
91
94
  let hookInstalled = false;
95
+ let skippedReason;
92
96
  if (!(0, fs_1.existsSync)(target)) {
93
97
  throw new Error(`PATH_NOT_FOUND: ${target}`);
94
98
  }
@@ -101,33 +105,47 @@ async function handleInit(opts) {
101
105
  const ymlPath = (0, path_1.join)(target, 'crewx.yml');
102
106
  if ((0, fs_1.existsSync)(yamlPath) || (0, fs_1.existsSync)(ymlPath)) {
103
107
  if (!force) {
104
- return { yamlCreated: false, hookInstalled: false, errors: [], skippedReason: 'yaml-exists' };
108
+ // yaml already exists: skip creation but still register workspace below
109
+ skippedReason = 'yaml-exists';
105
110
  }
106
111
  }
107
- try {
108
- const content = generateDefaultYaml(DEFAULT_AGENTS);
109
- (0, fs_1.writeFileSync)(yamlPath, content, 'utf-8');
110
- yamlCreated = true;
111
- }
112
- catch (e) {
113
- throw new Error(`YAML_CREATE_FAILED: ${e.message}`);
114
- }
115
- for (const dir of ['.crewx/logs', '.claude/commands']) {
112
+ if (!skippedReason) {
116
113
  try {
117
- (0, fs_1.mkdirSync)((0, path_1.join)(target, dir), { recursive: true });
114
+ const content = generateDefaultYaml(DEFAULT_AGENTS);
115
+ (0, fs_1.writeFileSync)(yamlPath, content, 'utf-8');
116
+ yamlCreated = true;
118
117
  }
119
118
  catch (e) {
120
- errors.push(`MKDIR_FAILED:${dir}:${e.message}`);
119
+ throw new Error(`YAML_CREATE_FAILED: ${e.message}`);
121
120
  }
122
- }
123
- if (!skipHook) {
124
- try {
125
- await (0, install_1.handleHookInstall)({ projectRoot: target, yes: true });
126
- hookInstalled = true;
121
+ for (const dir of ['.crewx/logs', '.claude/commands']) {
122
+ try {
123
+ (0, fs_1.mkdirSync)((0, path_1.join)(target, dir), { recursive: true });
124
+ }
125
+ catch (e) {
126
+ errors.push(`MKDIR_FAILED:${dir}:${e.message}`);
127
+ }
127
128
  }
128
- catch (e) {
129
- errors.push(`HOOK_INSTALL_FAILED: ${e.message}`);
129
+ if (!skipHook) {
130
+ try {
131
+ await (0, install_1.handleHookInstall)({ projectRoot: target, yes: true });
132
+ hookInstalled = true;
133
+ }
134
+ catch (e) {
135
+ errors.push(`HOOK_INSTALL_FAILED: ${e.message}`);
136
+ }
130
137
  }
131
138
  }
132
- return { yamlCreated, hookInstalled, errors };
139
+ // Always register workspace in ~/.crewx/crewx.db (best-effort, idempotent)
140
+ let workspaceId;
141
+ let slug;
142
+ try {
143
+ const ws = new repository_1.WorkspaceRepository().registerWorkspace(target);
144
+ workspaceId = ws.id;
145
+ slug = ws.slug;
146
+ }
147
+ catch (e) {
148
+ errors.push(`WORKSPACE_REGISTER_FAILED: ${e.message}`);
149
+ }
150
+ return { yamlCreated, hookInstalled, errors, skippedReason, workspaceId, slug };
133
151
  }
@@ -6,7 +6,4 @@
6
6
  * crewx kill <task-id> Kill a specific running task
7
7
  * crewx kill --all Kill all running tasks
8
8
  */
9
- /**
10
- * Handle `crewx kill` command.
11
- */
12
9
  export declare function handleKill(args: string[]): Promise<void>;
@@ -9,10 +9,7 @@
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.handleKill = handleKill;
12
- const task_db_1 = require("./task-db");
13
- /**
14
- * Handle `crewx kill` command.
15
- */
12
+ const repository_1 = require("@crewx/sdk/repository");
16
13
  async function handleKill(args) {
17
14
  const killAll = args.includes('--all');
18
15
  const taskId = args.find(a => !a.startsWith('--'));
@@ -25,25 +22,31 @@ async function handleKill(args) {
25
22
  process.exit(1);
26
23
  return;
27
24
  }
25
+ const repo = new repository_1.TaskRepository();
28
26
  if (killAll) {
29
- const running = (0, task_db_1.getRunningTasks)();
27
+ const running = repo.getRunningTasks();
30
28
  if (running.length === 0) {
31
29
  console.log('No running tasks found.');
32
30
  return;
33
31
  }
34
32
  console.log(`Found ${running.length} running task(s). Killing...`);
35
33
  for (const task of running) {
36
- const r = (0, task_db_1.killTask)(task.id);
37
- console.log(r.message);
34
+ const r = repo.killTask(task.id);
35
+ if (r.killed) {
36
+ console.log(`Killed task ${task.id}${r.pid ? ` (PID: ${r.pid})` : ''}`);
37
+ }
38
+ else {
39
+ console.log(`Task ${task.id} could not be killed (may have already finished)`);
40
+ }
38
41
  }
39
42
  return;
40
43
  }
41
- const r = (0, task_db_1.killTask)(taskId);
42
- if (r.ok) {
43
- console.log(r.message);
44
+ const r = repo.killTask(taskId);
45
+ if (r.killed) {
46
+ console.log(`Killed task ${taskId}${r.pid ? ` (PID: ${r.pid})` : ''}`);
44
47
  }
45
48
  else {
46
- console.error(`Error: ${r.message}`);
49
+ console.error(`Error: Task not found or not running: ${taskId}`);
47
50
  process.exit(1);
48
51
  }
49
52
  }
@@ -7,7 +7,4 @@
7
7
  * crewx log ls Same as above
8
8
  * crewx log <task_id> Show specific task details
9
9
  */
10
- /**
11
- * Handle `crewx log` command.
12
- */
13
10
  export declare function handleLog(args: string[]): Promise<void>;
@@ -10,24 +10,22 @@
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.handleLog = handleLog;
13
- const task_db_1 = require("./task-db");
13
+ const repository_1 = require("@crewx/sdk/repository");
14
14
  function statusIcon(status) {
15
15
  switch (status) {
16
16
  case 'running': return '⏳';
17
17
  case 'success': return '✅';
18
18
  case 'failed': return '❌';
19
+ default: return '❓';
19
20
  }
20
21
  }
21
- /**
22
- * Handle `crewx log` command.
23
- */
24
22
  async function handleLog(args) {
25
23
  const action = args[0];
26
24
  const isTaskId = action && (action.startsWith('tsk_') || action.startsWith('task_'));
27
25
  const isListCommand = !action || action === 'ls' || action === 'list';
26
+ const repo = new repository_1.TaskRepository();
28
27
  if (isTaskId) {
29
- // Show specific task
30
- const task = (0, task_db_1.getTask)(action);
28
+ const task = repo.getTask(action);
31
29
  if (!task) {
32
30
  console.error(`Task not found: ${action}`);
33
31
  process.exit(1);
@@ -36,8 +34,8 @@ async function handleLog(args) {
36
34
  console.log(`\nTask Log\n${'='.repeat(60)}`);
37
35
  console.log(`Task ID: ${task.id}`);
38
36
  console.log(`Status: ${statusIcon(task.status)} ${task.status}`);
39
- console.log(`Agent: ${task.agent_id}`);
40
- console.log(`Mode: ${task.mode}`);
37
+ console.log(`Agent: ${task.agent_id ?? '—'}`);
38
+ console.log(`Mode: ${task.mode ?? '—'}`);
41
39
  console.log(`Started: ${new Date(task.started_at).toLocaleString()}`);
42
40
  if (task.completed_at) {
43
41
  const duration = new Date(task.completed_at).getTime() - new Date(task.started_at).getTime();
@@ -58,13 +56,13 @@ async function handleLog(args) {
58
56
  return;
59
57
  }
60
58
  if (isListCommand) {
61
- const allTasks = (0, task_db_1.getAllTasks)();
59
+ const allTasks = repo.getAllTasks();
62
60
  console.log('\nAvailable Task Logs');
63
61
  console.log('='.repeat(60));
64
62
  const total = allTasks.length;
65
- const completed = allTasks.filter(t => t.status === 'success').length;
66
- const failed = allTasks.filter(t => t.status === 'failed').length;
67
- const running = allTasks.filter(t => t.status === 'running').length;
63
+ const completed = allTasks.filter((t) => t.status === 'success').length;
64
+ const failed = allTasks.filter((t) => t.status === 'failed').length;
65
+ const running = allTasks.filter((t) => t.status === 'running').length;
68
66
  console.log(`Total: ${total} | ✅ Completed: ${completed} | ❌ Failed: ${failed} | ⏳ Running: ${running}`);
69
67
  console.log('='.repeat(60));
70
68
  console.log('');
@@ -79,7 +77,7 @@ async function handleLog(args) {
79
77
  ? `${new Date(task.completed_at).getTime() - new Date(task.started_at).getTime()}ms`
80
78
  : 'running...';
81
79
  console.log(`${idx + 1}. ${icon} ${task.id}`);
82
- console.log(` Agent: ${task.agent_id} Mode: ${task.mode}`);
80
+ console.log(` Agent: ${task.agent_id ?? '—'} Mode: ${task.mode ?? '—'}`);
83
81
  console.log(` Started: ${new Date(task.started_at).toLocaleString()}`);
84
82
  console.log(` Duration: ${duration}`);
85
83
  console.log('');
@@ -6,7 +6,4 @@
6
6
  * crewx ps List all running tasks
7
7
  * crewx ps --json Output as JSON array
8
8
  */
9
- /**
10
- * Handle `crewx ps` command.
11
- */
12
9
  export declare function handlePs(args: string[]): Promise<void>;
@@ -9,10 +9,7 @@
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.handlePs = handlePs;
12
- const task_db_1 = require("./task-db");
13
- /**
14
- * Format elapsed milliseconds into a human-readable string.
15
- */
12
+ const repository_1 = require("@crewx/sdk/repository");
16
13
  function formatElapsed(ms) {
17
14
  const s = Math.floor(ms / 1000);
18
15
  if (s < 60)
@@ -24,22 +21,16 @@ function formatElapsed(ms) {
24
21
  const h = Math.floor(m / 60);
25
22
  return `${h}h ${m % 60}m`;
26
23
  }
27
- /**
28
- * Format a task row into a table row.
29
- */
30
24
  function taskToRow(task) {
31
25
  const elapsed = formatElapsed(Date.now() - new Date(task.started_at).getTime());
32
26
  return [
33
27
  task.id,
34
- task.agent_id,
35
- task.pid !== null ? String(task.pid) : '—',
28
+ task.agent_id ?? '—',
29
+ task.pid !== null && task.pid !== undefined ? String(task.pid) : '—',
36
30
  elapsed,
37
- task.mode,
31
+ task.mode ?? '—',
38
32
  ];
39
33
  }
40
- /**
41
- * Render a table with padded columns.
42
- */
43
34
  function renderTable(headers, rows) {
44
35
  const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map(r => (r[i] ?? '').length)));
45
36
  const separator = colWidths.map(w => '-'.repeat(w)).join(' ');
@@ -50,16 +41,13 @@ function renderTable(headers, rows) {
50
41
  console.log(row.map((cell, i) => cell.padEnd(colWidths[i] ?? cell.length)).join(' '));
51
42
  }
52
43
  }
53
- /**
54
- * Handle `crewx ps` command.
55
- */
56
44
  async function handlePs(args) {
57
- const tasks = (0, task_db_1.getRunningTasks)();
45
+ const repo = new repository_1.TaskRepository();
46
+ const tasks = repo.getRunningTasks();
58
47
  if (tasks.length === 0) {
59
48
  console.log('No running tasks.');
60
49
  return;
61
50
  }
62
- // --json output
63
51
  if (args.includes('--json')) {
64
52
  console.log(JSON.stringify(tasks, null, 2));
65
53
  return;
@@ -7,7 +7,4 @@
7
7
  * crewx result <task-id> --json Print full task record as JSON
8
8
  * crewx result List recent tasks (latest 10)
9
9
  */
10
- /**
11
- * Handle `crewx result` command.
12
- */
13
10
  export declare function handleResult(args: string[]): Promise<void>;
@@ -10,23 +10,21 @@
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.handleResult = handleResult;
13
- const task_db_1 = require("./task-db");
13
+ const repository_1 = require("@crewx/sdk/repository");
14
14
  function statusIcon(status) {
15
15
  switch (status) {
16
16
  case 'running': return '⏳';
17
17
  case 'success': return '✅';
18
18
  case 'failed': return '❌';
19
+ default: return '❓';
19
20
  }
20
21
  }
21
- /**
22
- * Handle `crewx result` command.
23
- */
24
22
  async function handleResult(args) {
25
23
  const jsonMode = args.includes('--json');
26
24
  const taskId = args.find(a => !a.startsWith('--'));
25
+ const repo = new repository_1.TaskRepository();
27
26
  if (!taskId) {
28
- // Show recent tasks
29
- const all = (0, task_db_1.getAllTasks)();
27
+ const all = repo.getAllTasks();
30
28
  const recent = all.slice(0, 10);
31
29
  if (recent.length === 0) {
32
30
  console.log('No tasks found.');
@@ -37,7 +35,7 @@ async function handleResult(args) {
37
35
  recent.forEach((task, idx) => {
38
36
  const icon = statusIcon(task.status);
39
37
  console.log(`${idx + 1}. ${icon} ${task.id}`);
40
- console.log(` Agent: ${task.agent_id} Mode: ${task.mode}`);
38
+ console.log(` Agent: ${task.agent_id ?? '—'} Mode: ${task.mode ?? '—'}`);
41
39
  console.log(` Started: ${new Date(task.started_at).toLocaleString()}`);
42
40
  if (task.completed_at) {
43
41
  console.log(` Completed: ${new Date(task.completed_at).toLocaleString()}`);
@@ -47,7 +45,7 @@ async function handleResult(args) {
47
45
  console.log('Tip: Run `crewx result <task-id>` to see full output.');
48
46
  return;
49
47
  }
50
- const task = (0, task_db_1.getTask)(taskId);
48
+ const task = repo.getTask(taskId);
51
49
  if (!task) {
52
50
  console.error(`Error: Task not found: ${taskId}`);
53
51
  process.exit(1);
@@ -68,6 +66,5 @@ async function handleResult(args) {
68
66
  process.exit(1);
69
67
  return;
70
68
  }
71
- // Completed (success) — print raw result
72
69
  console.log(task.result ?? '');
73
70
  }
package/dist/main.js CHANGED
@@ -58,7 +58,6 @@ const result_1 = require("./commands/result");
58
58
  const log_1 = require("./commands/log");
59
59
  const doctor_1 = require("./commands/doctor");
60
60
  const init_1 = require("./commands/init");
61
- const workspace_repository_1 = require("./repository/workspace.repository");
62
61
  const builtin_1 = require("./builtin");
63
62
  const slack_1 = require("./commands/slack");
64
63
  const install_1 = require("./commands/hook/install");
@@ -146,7 +145,6 @@ async function main() {
146
145
  const force = initArgs.includes('--force') || initArgs.includes('-f');
147
146
  try {
148
147
  const r = await (0, init_1.handleInit)({ path: process.cwd(), force });
149
- new workspace_repository_1.WorkspaceRepository().registerWorkspace(process.cwd());
150
148
  if (r.skippedReason === 'yaml-exists') {
151
149
  console.log('✓ crewx.yaml already exists (use --force to overwrite)');
152
150
  }
@@ -156,6 +154,8 @@ async function main() {
156
154
  if (r.errors.length > 0)
157
155
  console.warn('Warnings:', r.errors.join('; '));
158
156
  }
157
+ if (r.workspaceId)
158
+ console.log(`✓ workspace registered (id=${r.workspaceId}, slug=${r.slug})`);
159
159
  console.log('✓ done');
160
160
  }
161
161
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewx/cli",
3
- "version": "0.8.3-rc.2",
3
+ "version": "0.8.3-rc.4",
4
4
  "license": "UNLICENSED",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"
@@ -27,14 +27,14 @@
27
27
  "dependencies": {
28
28
  "@crewx/adapter-slack": "^0.1.4",
29
29
  "better-sqlite3": "*",
30
- "@crewx/sdk": "0.8.2",
30
+ "@crewx/sdk": "0.8.3-rc.1",
31
31
  "@crewx/memory": "0.1.9",
32
32
  "@crewx/search": "0.1.8",
33
- "@crewx/doc": "0.1.7",
34
33
  "@crewx/wbs": "0.1.8",
34
+ "@crewx/doc": "0.1.7",
35
35
  "@crewx/skill": "0.1.7",
36
- "@crewx/workflow": "0.3.13",
37
36
  "@crewx/cron": "0.1.7",
37
+ "@crewx/workflow": "0.3.13",
38
38
  "@crewx/shared": "0.0.4"
39
39
  },
40
40
  "devDependencies": {