jowork 0.2.3 → 0.2.5

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.
@@ -83,7 +83,10 @@ function createJoWorkMcpServer(opts) {
83
83
  return { isStale: true, lastSync: null, agoMinutes: null, hint: "cannot check freshness" };
84
84
  }
85
85
  }
86
+ let lastAutoSyncAt = 0;
87
+ const AUTO_SYNC_COOLDOWN_MS = 5 * 60 * 1e3;
86
88
  async function autoSyncIfStale(source) {
89
+ if (Date.now() - lastAutoSyncAt < AUTO_SYNC_COOLDOWN_MS) return false;
87
90
  const freshness = getDataFreshness(source);
88
91
  if (!freshness.isStale) return false;
89
92
  try {
@@ -110,6 +113,7 @@ function createJoWorkMcpServer(opts) {
110
113
  } catch {
111
114
  }
112
115
  }
116
+ lastAutoSyncAt = Date.now();
113
117
  return true;
114
118
  } catch {
115
119
  return false;
@@ -132,7 +136,8 @@ function createJoWorkMcpServer(opts) {
132
136
  if (path) {
133
137
  source = "local";
134
138
  }
135
- const pathFilter = path ? ` AND o.uri LIKE 'local://${escapeLike(path)}%'` : "";
139
+ const pathFilter = path ? ` AND o.uri LIKE ?` : "";
140
+ const pathParam = path ? `local://${escapeLike(path)}%` : null;
136
141
  const ftsMatchQuery = buildFtsQuery(query);
137
142
  if (ftsMatchQuery) {
138
143
  try {
@@ -141,7 +146,9 @@ function createJoWorkMcpServer(opts) {
141
146
  WHERE objects_fts MATCH ? AND o.source = ?${pathFilter} LIMIT ?` : `SELECT o.id, o.title, o.summary, o.source, o.source_type, o.uri, o.tags, o.file_path
142
147
  FROM objects_fts JOIN objects o ON o.rowid = objects_fts.rowid
143
148
  WHERE objects_fts MATCH ?${pathFilter} LIMIT ?`;
144
- const ftsArgs = source ? [ftsMatchQuery, source, limit] : [ftsMatchQuery, limit];
149
+ const ftsArgs = source ? [ftsMatchQuery, source] : [ftsMatchQuery];
150
+ if (pathParam) ftsArgs.push(pathParam);
151
+ ftsArgs.push(limit);
145
152
  const ftsResults = sqlite.prepare(ftsQuery).all(...ftsArgs);
146
153
  if (ftsResults.length > 0) {
147
154
  logInfo("mcp", `search_data: "${query}" (FTS)`, { source, path, resultCount: ftsResults.length, ms: Date.now() - t0 });
@@ -155,7 +162,7 @@ Showing ${ftsResults.length} results (summaries only). Use fetch_content with a
155
162
  }
156
163
  }
157
164
  const cleanedQuery = query.replace(/飞书|feishu|lark|github|gitlab|notion|slack/gi, "").replace(/群里|最近|在|讨论|什么|话题|有哪些|是什么|怎么样|帮我|告诉我|查一下/g, "").trim();
158
- const likePathFilter = path ? ` AND uri LIKE 'local://${escapeLike(path)}%'` : "";
165
+ const likePathFilter = path ? ` AND uri LIKE ?` : "";
159
166
  let rows = [];
160
167
  if (source && cleanedQuery.length >= 2) {
161
168
  const segments = cleanedQuery.split(/\s+/).filter((s) => s.length >= 2);
@@ -166,7 +173,9 @@ Showing ${ftsResults.length} results (summaries only). Use fetch_content with a
166
173
  const p = `%${escapeLike(seg)}%`;
167
174
  params.push(p, p, p);
168
175
  }
169
- params.push(source, limit);
176
+ params.push(source);
177
+ if (pathParam) params.push(pathParam);
178
+ params.push(limit);
170
179
  rows = sqlite.prepare(`
171
180
  SELECT id, title, summary, source, source_type, uri, tags, file_path FROM objects
172
181
  WHERE (${conditions}) AND source = ?${likePathFilter} ORDER BY last_synced_at DESC LIMIT ?
@@ -175,10 +184,13 @@ Showing ${ftsResults.length} results (summaries only). Use fetch_content with a
175
184
  rows = [];
176
185
  }
177
186
  } else if (source) {
187
+ const srcParams = [source];
188
+ if (pathParam) srcParams.push(pathParam);
189
+ srcParams.push(limit);
178
190
  rows = sqlite.prepare(`
179
191
  SELECT id, title, summary, source, source_type, uri, tags, file_path FROM objects
180
192
  WHERE source = ?${likePathFilter} ORDER BY last_synced_at DESC LIMIT ?
181
- `).all(source, limit);
193
+ `).all(...srcParams);
182
194
  } else if (cleanedQuery.length >= 2) {
183
195
  const pattern = `%${escapeLike(cleanedQuery)}%`;
184
196
  rows = sqlite.prepare(`
@@ -763,9 +775,37 @@ ${hot.summary}
763
775
  `SELECT source, COUNT(*) as count, MAX(last_synced_at) as last_sync FROM objects GROUP BY source`
764
776
  ).all();
765
777
  parts.push("## Data Sources\n");
778
+ const staleSources = [];
766
779
  for (const s of sources) {
767
780
  const ago = Math.round((now - s.last_sync) / 6e4);
768
- parts.push(`- ${s.source}: ${s.count} objects (last sync: ${ago} min ago)`);
781
+ const staleTag = ago > 60 ? " \u26A0 STALE" : "";
782
+ if (ago > 60) staleSources.push(s.source);
783
+ parts.push(`- ${s.source}: ${s.count} objects (last sync: ${ago} min ago${staleTag})`);
784
+ }
785
+ try {
786
+ const { listCredentials } = await import("./credential-store-ZRZCSRPC.js");
787
+ const connected = listCredentials();
788
+ const synced = new Set(sources.map((s) => s.source));
789
+ const neverSynced = connected.filter((c) => !synced.has(c));
790
+ for (const ns of neverSynced) {
791
+ parts.push(`- ${ns}: \u26A0 NEVER SYNCED`);
792
+ staleSources.push(ns);
793
+ }
794
+ } catch {
795
+ }
796
+ parts.push("\n## Recommended Actions\n");
797
+ if (staleSources.length > 0) {
798
+ parts.push(`- **SYNC NOW**: ${staleSources.join(", ")} data is stale. Call sync_now tool immediately.`);
799
+ }
800
+ if (goals.length === 0) {
801
+ parts.push("- **SET GOALS**: No goals configured. Ask user about their key objectives and create goals with update_goal.");
802
+ }
803
+ const memCount = sqlite.prepare("SELECT COUNT(*) as c FROM memories").get().c;
804
+ if (memCount === 0) {
805
+ parts.push("- **START MEMORY**: No memories saved yet. Save user preferences and decisions as you learn them via write_memory.");
806
+ }
807
+ if (staleSources.length === 0 && goals.length > 0 && memCount > 0) {
808
+ parts.push("- All systems healthy. No action needed.");
769
809
  }
770
810
  return { content: [{ type: "text", text: parts.join("\n") }] };
771
811
  }
@@ -773,7 +813,7 @@ ${hot.summary}
773
813
  server.tool(
774
814
  "sync_now",
775
815
  {
776
- source: z.string().optional().describe("Sync a specific source (feishu, github, etc.) or omit for all")
816
+ source: z.string().optional().describe("Sync a specific source (feishu, github, etc.) or omit for all. Call this directly when data is stale \u2014 do NOT ask the user for permission to sync.")
777
817
  },
778
818
  async ({ source }) => {
779
819
  const freshness = getDataFreshness(source);
@@ -322,8 +322,8 @@ var FileWriter = class {
322
322
  existingHeaders = [...existing.matchAll(/^## .+/gm)].map((m) => m[0]);
323
323
  }
324
324
  const newMessages = messages.filter((m) => {
325
- const header = `## ${m.time} \u2014 ${m.sender}`;
326
- return !existingHeaders.includes(header);
325
+ const header2 = `## ${m.time} \u2014 ${m.sender}`;
326
+ return !existingHeaders.includes(header2);
327
327
  });
328
328
  if (newMessages.length === 0) return filePath;
329
329
  if (!existsSync(absPath)) {
@@ -384,144 +384,181 @@ ${sanitizeContent(m.content)}`).join("\n");
384
384
  };
385
385
 
386
386
  // src/commands/sync.ts
387
+ var isTTY = process.stdout.isTTY;
388
+ var c = {
389
+ reset: isTTY ? "\x1B[0m" : "",
390
+ bold: isTTY ? "\x1B[1m" : "",
391
+ dim: isTTY ? "\x1B[2m" : "",
392
+ green: isTTY ? "\x1B[32m" : "",
393
+ yellow: isTTY ? "\x1B[33m" : "",
394
+ red: isTTY ? "\x1B[31m" : "",
395
+ cyan: isTTY ? "\x1B[36m" : "",
396
+ gray: isTTY ? "\x1B[90m" : "",
397
+ white: isTTY ? "\x1B[37m" : "",
398
+ bgGreen: isTTY ? "\x1B[42m" : "",
399
+ bgRed: isTTY ? "\x1B[41m" : "",
400
+ bgYellow: isTTY ? "\x1B[43m" : ""
401
+ };
402
+ var icon = {
403
+ ok: `${c.green}\u2713${c.reset}`,
404
+ warn: `${c.yellow}\u26A0${c.reset}`,
405
+ fail: `${c.red}\u2717${c.reset}`,
406
+ skip: `${c.gray}\u25CB${c.reset}`,
407
+ sync: `${c.cyan}\u21BB${c.reset}`,
408
+ link: `${c.cyan}\u27E1${c.reset}`,
409
+ git: `${c.gray}\u2387${c.reset}`
410
+ };
411
+ function header(text) {
412
+ console.log("");
413
+ console.log(` ${c.bold}${text}${c.reset}`);
414
+ console.log(` ${c.dim}${"\u2500".repeat(Math.min(text.length + 4, 50))}${c.reset}`);
415
+ }
416
+ function progressBar(current, total, width = 20) {
417
+ const pct = total > 0 ? current / total : 0;
418
+ const filled = Math.round(pct * width);
419
+ const empty = width - filled;
420
+ const bar = `${c.green}${"\u2588".repeat(filled)}${c.gray}${"\u2591".repeat(empty)}${c.reset}`;
421
+ return `${bar} ${c.dim}${current}/${total}${c.reset}`;
422
+ }
423
+ function sourceLabel(name) {
424
+ const colors = {
425
+ feishu: c.cyan,
426
+ github: c.white,
427
+ gitlab: c.yellow,
428
+ linear: c.cyan,
429
+ posthog: c.red,
430
+ firebase: c.yellow
431
+ };
432
+ return `${colors[name] ?? c.white}${c.bold}${name}${c.reset}`;
433
+ }
434
+ function resultLine(ok, msg) {
435
+ console.log(` ${ok ? icon.ok : icon.warn} ${msg}`);
436
+ }
437
+ function elapsed(start) {
438
+ const ms = Date.now() - start;
439
+ return ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
440
+ }
387
441
  async function runSync(sources) {
388
442
  const db = new DbManager(dbPath());
389
443
  db.ensureTables();
390
444
  const fileWriter = new FileWriter();
391
445
  const syncResults = [];
446
+ const t0 = Date.now();
447
+ let totalNew = 0;
392
448
  let gitManager = null;
393
449
  try {
394
450
  gitManager = new GitManager(fileRepoDir());
395
451
  await gitManager.init();
396
452
  } catch {
397
453
  }
398
- for (const source of sources) {
454
+ header(`Syncing ${sources.length} source${sources.length > 1 ? "s" : ""}`);
455
+ for (let i = 0; i < sources.length; i++) {
456
+ const source = sources[i];
399
457
  const cred = loadCredential(source);
400
458
  if (!cred) {
401
- console.log(`\u2298 ${source}: no credentials found, skipping`);
459
+ console.log(` ${icon.skip} ${sourceLabel(source)} ${c.dim}no credentials, skipping${c.reset}`);
402
460
  continue;
403
461
  }
404
- console.log(`Syncing ${source}...`);
462
+ const sourceStart = Date.now();
463
+ console.log(` ${icon.sync} ${sourceLabel(source)} ${c.dim}syncing...${c.reset}`);
464
+ const logger = {
465
+ info: (_msg) => {
466
+ },
467
+ warn: (msg) => resultLine(false, `${c.dim}${msg}${c.reset}`),
468
+ error: (msg) => console.error(` ${icon.fail} ${c.red}${msg}${c.reset}`)
469
+ };
405
470
  try {
406
471
  switch (source) {
407
472
  case "feishu": {
408
- const logger = {
409
- info: (msg) => console.log(` ${msg}`),
410
- warn: (msg) => console.log(` \u26A0 ${msg}`),
411
- error: (msg) => console.error(` \u2717 ${msg}`)
412
- };
413
473
  const result = await syncFeishu(db.getSqlite(), cred.data, logger, fileWriter);
414
- console.log(` \u2713 Synced ${result.totalMessages} messages (${result.newMessages} new) from ${result.chats} chats`);
474
+ resultLine(true, `${result.newMessages} new messages from ${result.chats} chats`);
475
+ totalNew += result.newMessages;
415
476
  syncResults.push({ source: "feishu", newObjects: result.newMessages, label: "messages" });
416
477
  try {
417
- const meetResult = await syncFeishuMeetings(db.getSqlite(), cred.data, logger, fileWriter);
418
- if (meetResult.meetings > 0) {
419
- console.log(` \u2713 Synced ${meetResult.meetings} calendar events (${meetResult.newObjects} new)`);
420
- syncResults.push({ source: "feishu/meetings", newObjects: meetResult.newObjects, label: "events" });
421
- }
422
- } catch (err) {
423
- console.log(` \u26A0 Meeting sync: ${err}`);
478
+ const mr = await syncFeishuMeetings(db.getSqlite(), cred.data, logger, fileWriter);
479
+ if (mr.newObjects > 0) resultLine(true, `${mr.newObjects} calendar events`);
480
+ syncResults.push({ source: "feishu/meetings", newObjects: mr.newObjects, label: "events" });
481
+ } catch {
424
482
  }
425
483
  try {
426
- const docResult = await syncFeishuDocs(db.getSqlite(), cred.data, logger, fileWriter);
427
- if (docResult.docs > 0) {
428
- console.log(` \u2713 Synced ${docResult.docs} documents (${docResult.newObjects} new)`);
429
- syncResults.push({ source: "feishu/docs", newObjects: docResult.newObjects, label: "docs" });
430
- }
431
- } catch (err) {
432
- console.log(` \u26A0 Document sync: ${err}`);
484
+ const dr = await syncFeishuDocs(db.getSqlite(), cred.data, logger, fileWriter);
485
+ if (dr.newObjects > 0) resultLine(true, `${dr.newObjects} documents`);
486
+ syncResults.push({ source: "feishu/docs", newObjects: dr.newObjects, label: "docs" });
487
+ } catch {
433
488
  }
434
489
  try {
435
- const approvalResult = await syncFeishuApprovals(db.getSqlite(), cred.data, logger, fileWriter);
436
- if (approvalResult.approvals > 0) {
437
- console.log(` \u2713 Synced ${approvalResult.approvals} approvals (${approvalResult.newObjects} new)`);
438
- syncResults.push({ source: "feishu/approvals", newObjects: approvalResult.newObjects, label: "approvals" });
439
- }
440
- } catch (err) {
441
- console.log(` \u26A0 Approval sync: ${err}`);
490
+ const ar = await syncFeishuApprovals(db.getSqlite(), cred.data, logger, fileWriter);
491
+ if (ar.newObjects > 0) resultLine(true, `${ar.newObjects} approvals`);
492
+ syncResults.push({ source: "feishu/approvals", newObjects: ar.newObjects, label: "approvals" });
493
+ } catch {
442
494
  }
443
495
  break;
444
496
  }
445
497
  case "github": {
446
- const ghLogger = {
447
- info: (msg) => console.log(` ${msg}`),
448
- warn: (msg) => console.log(` \u26A0 ${msg}`),
449
- error: (msg) => console.error(` \u2717 ${msg}`)
450
- };
451
- const result = await syncGitHub(db.getSqlite(), cred.data, ghLogger, fileWriter);
452
- console.log(` \u2713 Synced ${result.repos} repos: ${result.issues} issues, ${result.prs} PRs (${result.newObjects} new)`);
453
- syncResults.push({ source: "github", newObjects: result.newObjects });
498
+ const r = await syncGitHub(db.getSqlite(), cred.data, logger, fileWriter);
499
+ resultLine(true, `${r.repos} repos, ${r.prs} PRs, ${r.issues} issues ${c.dim}(${r.newObjects} new)${c.reset}`);
500
+ totalNew += r.newObjects;
501
+ syncResults.push({ source: "github", newObjects: r.newObjects });
454
502
  break;
455
503
  }
456
504
  case "gitlab": {
457
- const glLogger = {
458
- info: (msg) => console.log(` ${msg}`),
459
- warn: (msg) => console.log(` \u26A0 ${msg}`),
460
- error: (msg) => console.error(` \u2717 ${msg}`)
461
- };
462
- const glResult = await syncGitLab(db.getSqlite(), cred.data, glLogger, fileWriter);
463
- console.log(` \u2713 Synced ${glResult.projects} projects: ${glResult.issues} issues, ${glResult.mrs} MRs (${glResult.newObjects} new)`);
464
- syncResults.push({ source: "gitlab", newObjects: glResult.newObjects });
505
+ const r = await syncGitLab(db.getSqlite(), cred.data, logger, fileWriter);
506
+ resultLine(true, `${r.projects} projects, ${r.mrs} MRs, ${r.issues} issues ${c.dim}(${r.newObjects} new)${c.reset}`);
507
+ totalNew += r.newObjects;
508
+ syncResults.push({ source: "gitlab", newObjects: r.newObjects });
465
509
  break;
466
510
  }
467
511
  case "linear": {
468
- const linLogger = {
469
- info: (msg) => console.log(` ${msg}`),
470
- warn: (msg) => console.log(` \u26A0 ${msg}`),
471
- error: (msg) => console.error(` \u2717 ${msg}`)
472
- };
473
- const linResult = await syncLinear(db.getSqlite(), cred.data, linLogger, fileWriter);
474
- console.log(` \u2713 Synced ${linResult.issues} Linear issues (${linResult.newObjects} new)`);
475
- syncResults.push({ source: "linear", newObjects: linResult.newObjects, label: "issues" });
512
+ const r = await syncLinear(db.getSqlite(), cred.data, logger, fileWriter);
513
+ resultLine(true, `${r.issues} issues ${c.dim}(${r.newObjects} new)${c.reset}`);
514
+ totalNew += r.newObjects;
515
+ syncResults.push({ source: "linear", newObjects: r.newObjects, label: "issues" });
476
516
  break;
477
517
  }
478
518
  case "posthog": {
479
- const phLogger = {
480
- info: (msg) => console.log(` ${msg}`),
481
- warn: (msg) => console.log(` \u26A0 ${msg}`),
482
- error: (msg) => console.error(` \u2717 ${msg}`)
483
- };
484
- const phResult = await syncPostHog(db.getSqlite(), cred.data, phLogger, fileWriter);
485
- console.log(` \u2713 Synced ${phResult.insights} insights, ${phResult.events} events (${phResult.newObjects} new)`);
486
- syncResults.push({ source: "posthog", newObjects: phResult.newObjects });
519
+ const r = await syncPostHog(db.getSqlite(), cred.data, logger, fileWriter);
520
+ resultLine(true, `${r.insights} insights, ${r.events} events ${c.dim}(${r.newObjects} new)${c.reset}`);
521
+ totalNew += r.newObjects;
522
+ syncResults.push({ source: "posthog", newObjects: r.newObjects });
487
523
  break;
488
524
  }
489
525
  case "firebase": {
490
- const fbLogger = {
491
- info: (msg) => console.log(` ${msg}`),
492
- warn: (msg) => console.log(` \u26A0 ${msg}`),
493
- error: (msg) => console.error(` \u2717 ${msg}`)
494
- };
495
- const fbResult = await syncFirebase(db.getSqlite(), cred.data, fbLogger, fileWriter);
496
- console.log(` \u2713 Synced ${fbResult.events} Firebase events (${fbResult.newObjects} new)`);
497
- syncResults.push({ source: "firebase", newObjects: fbResult.newObjects, label: "events" });
526
+ const r = await syncFirebase(db.getSqlite(), cred.data, logger, fileWriter);
527
+ resultLine(true, `${r.events} events ${c.dim}(${r.newObjects} new)${c.reset}`);
528
+ totalNew += r.newObjects;
529
+ syncResults.push({ source: "firebase", newObjects: r.newObjects, label: "events" });
498
530
  break;
499
531
  }
500
532
  default:
501
- console.log(` Unknown source: ${source}`);
533
+ console.log(` ${icon.skip} ${c.dim}unknown source${c.reset}`);
502
534
  }
535
+ console.log(` ${c.dim}${elapsed(sourceStart)}${c.reset}`);
503
536
  } catch (err) {
504
537
  logError("sync", `Failed to sync ${source}`, { error: String(err) });
505
- console.error(` \u2717 ${source} sync failed: ${err}`);
538
+ console.log(` ${icon.fail} ${c.red}sync failed${c.reset} ${c.dim}${String(err).slice(0, 60)}${c.reset}`);
539
+ }
540
+ if (sources.length > 1) {
541
+ console.log(` ${progressBar(i + 1, sources.length)}`);
506
542
  }
507
543
  }
508
- console.log("Running entity extraction...");
544
+ console.log("");
545
+ console.log(` ${icon.link} ${c.dim}extracting links...${c.reset}`);
509
546
  const { processed, linksCreated } = linkAllUnprocessed(db.getSqlite());
510
- console.log(` \u2713 Extracted ${linksCreated} links from ${processed} objects`);
547
+ if (processed > 0) {
548
+ resultLine(true, `${linksCreated} links from ${processed} objects`);
549
+ }
511
550
  db.close();
512
551
  if (gitManager) {
513
552
  try {
514
- const sha = await gitManager.commitSync({
515
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
516
- sources: syncResults
517
- });
518
- if (sha) {
519
- console.log(` \u2713 Committed: ${sha.slice(0, 7)}`);
520
- }
521
- } catch (err) {
522
- logError("sync", "Git commit failed", { error: String(err) });
553
+ const sha = await gitManager.commitSync({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), sources: syncResults });
554
+ if (sha) console.log(` ${icon.git} ${c.dim}committed ${sha.slice(0, 7)}${c.reset}`);
555
+ } catch {
523
556
  }
524
557
  }
558
+ console.log("");
559
+ console.log(` ${c.bold}${c.green}Done${c.reset} ${c.dim}in ${elapsed(t0)}${c.reset}`);
560
+ console.log(` ${c.bold}${totalNew}${c.reset} new objects synced from ${c.bold}${syncResults.length}${c.reset} sources`);
561
+ console.log("");
525
562
  }
526
563
  function syncCommand(program) {
527
564
  program.command("sync").description("Sync data from connected sources").option("--source <source>", "Sync specific source only").action(async (opts) => {
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  syncCommand,
5
5
  syncFirebase,
6
6
  syncPostHog
7
- } from "./chunk-AIXKXEYS.js";
7
+ } from "./chunk-HUHDL7WV.js";
8
8
  import {
9
9
  linkAllUnprocessed,
10
10
  syncGitLab,
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-XAEGXSEO.js";
16
16
  import {
17
17
  createJoWorkMcpServer
18
- } from "./chunk-YVPWTH6F.js";
18
+ } from "./chunk-7U3SXINY.js";
19
19
  import {
20
20
  GoalManager
21
21
  } from "./chunk-TN327MDF.js";
@@ -91,7 +91,7 @@ function initCommand(program2) {
91
91
  default: true
92
92
  }]);
93
93
  if (continueSetup) {
94
- const { runSetupWizard } = await import("./setup-IDQDPCEJ.js");
94
+ const { runSetupWizard } = await import("./setup-SYBQIL2O.js");
95
95
  await runSetupWizard();
96
96
  } else {
97
97
  console.log("");
@@ -568,8 +568,11 @@ function serveCommand(program2) {
568
568
  program2.command("serve").description("Start MCP server (stdio mode for agents, or --daemon for background)").option("--daemon", "Run as background daemon with cron sync").action(async (opts) => {
569
569
  const resolvedDbPath = process.env["JOWORK_DB_PATH"] ?? dbPath();
570
570
  if (!existsSync2(resolvedDbPath)) {
571
- console.error("Error: JoWork not initialized. Run `jowork init` first.");
572
- process.exit(1);
571
+ const { writeConfig: writeConfig2 } = await import("./config-AI6UIJJN.js");
572
+ const db = new DbManager(dbPath());
573
+ db.ensureTables();
574
+ db.close();
575
+ writeConfig2({ version: "0.1.0", initialized: true, connectors: {} });
573
576
  }
574
577
  if (opts.daemon) {
575
578
  await startDaemon();
@@ -783,7 +786,16 @@ async function runSync() {
783
786
  // src/commands/register.ts
784
787
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
785
788
  import { join as join2 } from "path";
789
+ import { execSync } from "child_process";
786
790
  var HOME = process.env["HOME"] ?? "";
791
+ function getMcpCommand() {
792
+ try {
793
+ execSync("which jowork", { stdio: "ignore" });
794
+ return { command: "jowork", args: ["serve"] };
795
+ } catch {
796
+ return { command: "npx", args: ["-y", "jowork@latest", "serve"] };
797
+ }
798
+ }
787
799
  function registerCommand(program2) {
788
800
  program2.command("register").description("Register JoWork MCP server with an AI agent engine").argument("<engine>", "Engine to register with: claude-code, codex, openclaw").action(async (engine) => {
789
801
  switch (engine) {
@@ -820,9 +832,10 @@ function registerClaudeCode() {
820
832
  }
821
833
  }
822
834
  if (!config.mcpServers) config.mcpServers = {};
835
+ const mcp = getMcpCommand();
823
836
  config.mcpServers["jowork"] = {
824
- command: "jowork",
825
- args: ["serve"],
837
+ command: mcp.command,
838
+ args: mcp.args,
826
839
  env: { JOWORK_ENGINE: "claude-code" }
827
840
  };
828
841
  writeFileSync2(configPath2, JSON.stringify(config, null, 2));
@@ -849,9 +862,10 @@ function registerOpenClaw() {
849
862
  }
850
863
  }
851
864
  if (!config["mcpServers"]) config["mcpServers"] = {};
865
+ const mcp = getMcpCommand();
852
866
  config["mcpServers"]["jowork"] = {
853
- command: "jowork",
854
- args: ["serve"],
867
+ command: mcp.command,
868
+ args: mcp.args,
855
869
  env: { JOWORK_ENGINE: "openclaw" }
856
870
  };
857
871
  writeFileSync2(configPath2, JSON.stringify(config, null, 2));
@@ -876,10 +890,12 @@ function registerCodex() {
876
890
  console.log("\u2713 JoWork already registered with Codex");
877
891
  return;
878
892
  }
893
+ const mcp = getMcpCommand();
894
+ const argsToml = mcp.args.map((a) => `"${a}"`).join(", ");
879
895
  const mcpEntry = `
880
896
  [mcp_servers.jowork]
881
- command = "jowork"
882
- args = ["serve"]
897
+ command = "${mcp.command}"
898
+ args = [${argsToml}]
883
899
 
884
900
  [mcp_servers.jowork.env]
885
901
  JOWORK_ENGINE = "codex"
@@ -1950,7 +1966,7 @@ function dashboardCommand(program2) {
1950
1966
  console.error("Error: JoWork not initialized. Run `jowork init` first.");
1951
1967
  process.exit(1);
1952
1968
  }
1953
- const { startDashboard } = await import("./server-6WYDERK6.js");
1969
+ const { startDashboard } = await import("./server-5GVWN2NB.js");
1954
1970
  const port = opts.port ? parseInt(opts.port, 10) : void 0;
1955
1971
  const dashboard = await startDashboard({ port });
1956
1972
  const url = `http://127.0.0.1:${dashboard.port}`;
@@ -2386,7 +2402,7 @@ function configCommand(program2) {
2386
2402
  // src/commands/setup-skill.ts
2387
2403
  import { existsSync as existsSync15, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7, readFileSync as readFileSync7 } from "fs";
2388
2404
  import { join as join10 } from "path";
2389
- import { execSync } from "child_process";
2405
+ import { execSync as execSync2 } from "child_process";
2390
2406
  var HOME3 = process.env["HOME"] ?? process.env["USERPROFILE"] ?? "";
2391
2407
  var SKILL_CONTENT = `---
2392
2408
  name: jowork
@@ -2519,12 +2535,12 @@ function setupSkillCommand(program2) {
2519
2535
  console.log(" \u2713 MCP server registered with Claude Code");
2520
2536
  } else {
2521
2537
  try {
2522
- execSync("jowork register claude-code", { stdio: "pipe" });
2538
+ execSync2("jowork register claude-code", { stdio: "pipe" });
2523
2539
  console.log(" \u2713 MCP server registered with Claude Code");
2524
2540
  } catch {
2525
2541
  try {
2526
2542
  const cliPath = join10(__dirname, "..", "cli.js");
2527
- execSync(`node "${cliPath}" register claude-code`, { stdio: "pipe" });
2543
+ execSync2(`node "${cliPath}" register claude-code`, { stdio: "pipe" });
2528
2544
  console.log(" \u2713 MCP server registered with Claude Code");
2529
2545
  } catch {
2530
2546
  console.log(" \u26A0 Could not auto-register. Run: jowork register claude-code");
@@ -2532,9 +2548,9 @@ function setupSkillCommand(program2) {
2532
2548
  }
2533
2549
  }
2534
2550
  try {
2535
- execSync("which codex", { stdio: "pipe" });
2551
+ execSync2("which codex", { stdio: "pipe" });
2536
2552
  try {
2537
- execSync("jowork register codex", { stdio: "pipe" });
2553
+ execSync2("jowork register codex", { stdio: "pipe" });
2538
2554
  console.log(" \u2713 Registered with Codex");
2539
2555
  } catch {
2540
2556
  }
@@ -2573,13 +2589,13 @@ function setupSkillCommand(program2) {
2573
2589
  console.log(" Detected GITHUB_PERSONAL_ACCESS_TOKEN in environment.");
2574
2590
  console.log(" Connecting GitHub automatically...");
2575
2591
  try {
2576
- execSync('jowork connect github --token "$GITHUB_PERSONAL_ACCESS_TOKEN"', {
2592
+ execSync2('jowork connect github --token "$GITHUB_PERSONAL_ACCESS_TOKEN"', {
2577
2593
  stdio: "pipe",
2578
2594
  env: process.env
2579
2595
  });
2580
2596
  console.log(" \u2713 GitHub connected! Running initial sync...");
2581
2597
  try {
2582
- const output = execSync("jowork sync --source github", {
2598
+ const output = execSync2("jowork sync --source github", {
2583
2599
  encoding: "utf-8",
2584
2600
  timeout: 3e4,
2585
2601
  env: process.env
@@ -2624,7 +2640,7 @@ program.action(async () => {
2624
2640
  const config = readConfig2();
2625
2641
  if (!config.initialized || !existsSync16(dbPath2())) {
2626
2642
  if (process.stdin.isTTY) {
2627
- const { runSetupWizard } = await import("./setup-IDQDPCEJ.js");
2643
+ const { runSetupWizard } = await import("./setup-SYBQIL2O.js");
2628
2644
  await runSetupWizard();
2629
2645
  } else {
2630
2646
  console.log("JoWork is not initialized. Run `jowork init` in an interactive terminal.");
@@ -793,7 +793,7 @@ function focusMacOS(pid) {
793
793
  } catch {
794
794
  return { focused: false, method: "no-process" };
795
795
  }
796
- if (!tty || tty === "??") {
796
+ if (!tty || tty === "??" || !/^[a-zA-Z0-9/]+$/.test(tty)) {
797
797
  return { focused: false, method: "no-tty" };
798
798
  }
799
799
  let parentComm = "";
@@ -194,7 +194,7 @@ async function runSetupWizard() {
194
194
  console.log("");
195
195
  console.log(zh ? ` \u6B63\u5728\u540C\u6B65 ${connectedSources.length} \u4E2A\u6570\u636E\u6E90...` : ` Syncing ${connectedSources.length} source${connectedSources.length > 1 ? "s" : ""}...`);
196
196
  console.log("");
197
- const { runSync } = await import("./sync-7V54N62M.js");
197
+ const { runSync } = await import("./sync-KDSPGY4A.js");
198
198
  try {
199
199
  await runSync(connectedSources);
200
200
  } catch {
@@ -242,7 +242,15 @@ async function registerEngine(engine) {
242
242
  cursor: "Cursor",
243
243
  openclaw: "OpenClaw"
244
244
  };
245
- const mcpEntry = { command: "jowork", args: ["serve"] };
245
+ let mcpCommand = "jowork";
246
+ let mcpArgs = ["serve"];
247
+ try {
248
+ execSync("which jowork", { stdio: "ignore" });
249
+ } catch {
250
+ mcpCommand = "npx";
251
+ mcpArgs = ["-y", "jowork@latest", "serve"];
252
+ }
253
+ const mcpEntry = { command: mcpCommand, args: mcpArgs };
246
254
  switch (engine) {
247
255
  case "claude-code": {
248
256
  const p = join(HOME, ".claude.json");
@@ -266,7 +274,12 @@ async function registerEngine(engine) {
266
274
  const p = join(dir, "config.toml");
267
275
  let c = existsSync(p) ? readFileSync(p, "utf-8") : "";
268
276
  if (!c.includes("[mcp_servers.jowork]")) {
269
- c += '\n[mcp_servers.jowork]\ncommand = "jowork"\nargs = ["serve"]\n';
277
+ const argsToml = mcpArgs.map((a) => `"${a}"`).join(", ");
278
+ c += `
279
+ [mcp_servers.jowork]
280
+ command = "${mcpCommand}"
281
+ args = [${argsToml}]
282
+ `;
270
283
  writeFileSync(p, c);
271
284
  }
272
285
  break;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  runSync,
3
3
  syncCommand
4
- } from "./chunk-AIXKXEYS.js";
4
+ } from "./chunk-HUHDL7WV.js";
5
5
  import "./chunk-YJWTKFWX.js";
6
6
  import "./chunk-XAEGXSEO.js";
7
7
  import "./chunk-XLYRHKG6.js";
package/dist/transport.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createJoWorkMcpServer
3
- } from "./chunk-YVPWTH6F.js";
3
+ } from "./chunk-7U3SXINY.js";
4
4
  import "./chunk-TN327MDF.js";
5
5
  import {
6
6
  dbPath
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jowork",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "AI Agent Infrastructure — let AI agents truly understand your work. Connect data sources, give agents awareness and goals. Local-first, one command.",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",