@code-insights/cli 1.1.0 → 2.0.0

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.
Files changed (173) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +101 -9
  3. package/dist/commands/config.d.ts +3 -0
  4. package/dist/commands/config.d.ts.map +1 -0
  5. package/dist/commands/config.js +130 -0
  6. package/dist/commands/config.js.map +1 -0
  7. package/dist/commands/connect.d.ts.map +1 -1
  8. package/dist/commands/connect.js +7 -1
  9. package/dist/commands/connect.js.map +1 -1
  10. package/dist/commands/init.d.ts.map +1 -1
  11. package/dist/commands/init.js +52 -0
  12. package/dist/commands/init.js.map +1 -1
  13. package/dist/commands/install-hook.d.ts.map +1 -1
  14. package/dist/commands/install-hook.js +16 -3
  15. package/dist/commands/install-hook.js.map +1 -1
  16. package/dist/commands/open.d.ts +9 -0
  17. package/dist/commands/open.d.ts.map +1 -0
  18. package/dist/commands/open.js +59 -0
  19. package/dist/commands/open.js.map +1 -0
  20. package/dist/commands/reset.d.ts.map +1 -1
  21. package/dist/commands/reset.js +16 -2
  22. package/dist/commands/reset.js.map +1 -1
  23. package/dist/commands/stats/actions/cost.d.ts +3 -0
  24. package/dist/commands/stats/actions/cost.d.ts.map +1 -0
  25. package/dist/commands/stats/actions/cost.js +139 -0
  26. package/dist/commands/stats/actions/cost.js.map +1 -0
  27. package/dist/commands/stats/actions/error-handler.d.ts +6 -0
  28. package/dist/commands/stats/actions/error-handler.d.ts.map +1 -0
  29. package/dist/commands/stats/actions/error-handler.js +42 -0
  30. package/dist/commands/stats/actions/error-handler.js.map +1 -0
  31. package/dist/commands/stats/actions/models.d.ts +3 -0
  32. package/dist/commands/stats/actions/models.d.ts.map +1 -0
  33. package/dist/commands/stats/actions/models.js +101 -0
  34. package/dist/commands/stats/actions/models.js.map +1 -0
  35. package/dist/commands/stats/actions/overview.d.ts +3 -0
  36. package/dist/commands/stats/actions/overview.d.ts.map +1 -0
  37. package/dist/commands/stats/actions/overview.js +147 -0
  38. package/dist/commands/stats/actions/overview.js.map +1 -0
  39. package/dist/commands/stats/actions/projects.d.ts +3 -0
  40. package/dist/commands/stats/actions/projects.d.ts.map +1 -0
  41. package/dist/commands/stats/actions/projects.js +90 -0
  42. package/dist/commands/stats/actions/projects.js.map +1 -0
  43. package/dist/commands/stats/actions/today.d.ts +3 -0
  44. package/dist/commands/stats/actions/today.d.ts.map +1 -0
  45. package/dist/commands/stats/actions/today.js +118 -0
  46. package/dist/commands/stats/actions/today.js.map +1 -0
  47. package/dist/commands/stats/data/aggregation.d.ts +60 -0
  48. package/dist/commands/stats/data/aggregation.d.ts.map +1 -0
  49. package/dist/commands/stats/data/aggregation.js +614 -0
  50. package/dist/commands/stats/data/aggregation.js.map +1 -0
  51. package/dist/commands/stats/data/cache.d.ts +29 -0
  52. package/dist/commands/stats/data/cache.d.ts.map +1 -0
  53. package/dist/commands/stats/data/cache.js +197 -0
  54. package/dist/commands/stats/data/cache.js.map +1 -0
  55. package/dist/commands/stats/data/firestore.d.ts +13 -0
  56. package/dist/commands/stats/data/firestore.d.ts.map +1 -0
  57. package/dist/commands/stats/data/firestore.js +155 -0
  58. package/dist/commands/stats/data/firestore.js.map +1 -0
  59. package/dist/commands/stats/data/fuzzy-match.d.ts +5 -0
  60. package/dist/commands/stats/data/fuzzy-match.d.ts.map +1 -0
  61. package/dist/commands/stats/data/fuzzy-match.js +36 -0
  62. package/dist/commands/stats/data/fuzzy-match.js.map +1 -0
  63. package/dist/commands/stats/data/local.d.ts +12 -0
  64. package/dist/commands/stats/data/local.d.ts.map +1 -0
  65. package/dist/commands/stats/data/local.js +77 -0
  66. package/dist/commands/stats/data/local.js.map +1 -0
  67. package/dist/commands/stats/data/source.d.ts +13 -0
  68. package/dist/commands/stats/data/source.d.ts.map +1 -0
  69. package/dist/commands/stats/data/source.js +41 -0
  70. package/dist/commands/stats/data/source.js.map +1 -0
  71. package/dist/commands/stats/data/types.d.ts +229 -0
  72. package/dist/commands/stats/data/types.d.ts.map +1 -0
  73. package/dist/commands/stats/data/types.js +50 -0
  74. package/dist/commands/stats/data/types.js.map +1 -0
  75. package/dist/commands/stats/index.d.ts +3 -0
  76. package/dist/commands/stats/index.d.ts.map +1 -0
  77. package/dist/commands/stats/index.js +41 -0
  78. package/dist/commands/stats/index.js.map +1 -0
  79. package/dist/commands/stats/render/charts.d.ts +9 -0
  80. package/dist/commands/stats/render/charts.d.ts.map +1 -0
  81. package/dist/commands/stats/render/charts.js +45 -0
  82. package/dist/commands/stats/render/charts.js.map +1 -0
  83. package/dist/commands/stats/render/colors.d.ts +21 -0
  84. package/dist/commands/stats/render/colors.d.ts.map +1 -0
  85. package/dist/commands/stats/render/colors.js +47 -0
  86. package/dist/commands/stats/render/colors.js.map +1 -0
  87. package/dist/commands/stats/render/format.d.ts +10 -0
  88. package/dist/commands/stats/render/format.d.ts.map +1 -0
  89. package/dist/commands/stats/render/format.js +57 -0
  90. package/dist/commands/stats/render/format.js.map +1 -0
  91. package/dist/commands/stats/render/layout.d.ts +10 -0
  92. package/dist/commands/stats/render/layout.d.ts.map +1 -0
  93. package/dist/commands/stats/render/layout.js +48 -0
  94. package/dist/commands/stats/render/layout.js.map +1 -0
  95. package/dist/commands/stats/shared.d.ts +6 -0
  96. package/dist/commands/stats/shared.d.ts.map +1 -0
  97. package/dist/commands/stats/shared.js +26 -0
  98. package/dist/commands/stats/shared.js.map +1 -0
  99. package/dist/commands/status.d.ts.map +1 -1
  100. package/dist/commands/status.js +50 -37
  101. package/dist/commands/status.js.map +1 -1
  102. package/dist/commands/sync.d.ts +16 -1
  103. package/dist/commands/sync.d.ts.map +1 -1
  104. package/dist/commands/sync.js +167 -117
  105. package/dist/commands/sync.js.map +1 -1
  106. package/dist/commands/telemetry.d.ts +3 -0
  107. package/dist/commands/telemetry.d.ts.map +1 -0
  108. package/dist/commands/telemetry.js +106 -0
  109. package/dist/commands/telemetry.js.map +1 -0
  110. package/dist/firebase/client.d.ts +5 -0
  111. package/dist/firebase/client.d.ts.map +1 -1
  112. package/dist/firebase/client.js +5 -2
  113. package/dist/firebase/client.js.map +1 -1
  114. package/dist/index.js +21 -4
  115. package/dist/index.js.map +1 -1
  116. package/dist/parser/jsonl.d.ts.map +1 -1
  117. package/dist/parser/jsonl.js +8 -4
  118. package/dist/parser/jsonl.js.map +1 -1
  119. package/dist/parser/titles.d.ts.map +1 -1
  120. package/dist/parser/titles.js +2 -1
  121. package/dist/parser/titles.js.map +1 -1
  122. package/dist/providers/claude-code.d.ts +14 -0
  123. package/dist/providers/claude-code.d.ts.map +1 -0
  124. package/dist/providers/claude-code.js +66 -0
  125. package/dist/providers/claude-code.js.map +1 -0
  126. package/dist/providers/codex.d.ts +14 -0
  127. package/dist/providers/codex.d.ts.map +1 -0
  128. package/dist/providers/codex.js +364 -0
  129. package/dist/providers/codex.js.map +1 -0
  130. package/dist/providers/copilot-cli.d.ts +14 -0
  131. package/dist/providers/copilot-cli.d.ts.map +1 -0
  132. package/dist/providers/copilot-cli.js +363 -0
  133. package/dist/providers/copilot-cli.js.map +1 -0
  134. package/dist/providers/cursor.d.ts +27 -0
  135. package/dist/providers/cursor.d.ts.map +1 -0
  136. package/dist/providers/cursor.js +356 -0
  137. package/dist/providers/cursor.js.map +1 -0
  138. package/dist/providers/registry.d.ts +10 -0
  139. package/dist/providers/registry.d.ts.map +1 -0
  140. package/dist/providers/registry.js +31 -0
  141. package/dist/providers/registry.js.map +1 -0
  142. package/dist/providers/types.d.ts +15 -0
  143. package/dist/providers/types.d.ts.map +1 -0
  144. package/dist/providers/types.js +2 -0
  145. package/dist/providers/types.js.map +1 -0
  146. package/dist/types.d.ts +5 -1
  147. package/dist/types.d.ts.map +1 -1
  148. package/dist/utils/config.d.ts +10 -1
  149. package/dist/utils/config.d.ts.map +1 -1
  150. package/dist/utils/config.js +21 -0
  151. package/dist/utils/config.js.map +1 -1
  152. package/dist/utils/device.js +2 -2
  153. package/dist/utils/device.js.map +1 -1
  154. package/dist/utils/firebase-json.d.ts.map +1 -1
  155. package/dist/utils/firebase-json.js +2 -1
  156. package/dist/utils/firebase-json.js.map +1 -1
  157. package/dist/utils/paths.d.ts +10 -0
  158. package/dist/utils/paths.d.ts.map +1 -0
  159. package/dist/utils/paths.js +16 -0
  160. package/dist/utils/paths.js.map +1 -0
  161. package/dist/utils/telemetry.d.ts +52 -0
  162. package/dist/utils/telemetry.d.ts.map +1 -0
  163. package/dist/utils/telemetry.js +304 -0
  164. package/dist/utils/telemetry.js.map +1 -0
  165. package/dist/utils/tips.d.ts +11 -0
  166. package/dist/utils/tips.d.ts.map +1 -0
  167. package/dist/utils/tips.js +106 -0
  168. package/dist/utils/tips.js.map +1 -0
  169. package/dist/utils/welcome.d.ts +11 -0
  170. package/dist/utils/welcome.d.ts.map +1 -0
  171. package/dist/utils/welcome.js +101 -0
  172. package/dist/utils/welcome.js.map +1 -0
  173. package/package.json +9 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../../src/commands/stats/render/layout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Command } from 'commander';
2
+ import type { StatsFlags } from './data/types.js';
3
+ export type { StatsFlags };
4
+ export declare function applySharedFlags(cmd: Command): Command;
5
+ export declare function parseFlags(options: Record<string, unknown>): StatsFlags;
6
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/commands/stats/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAK1D,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAQtD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAavE"}
@@ -0,0 +1,26 @@
1
+ import { InvalidPeriodError } from './data/types.js';
2
+ const VALID_PERIODS = ['7d', '30d', '90d', 'all'];
3
+ export function applySharedFlags(cmd) {
4
+ return cmd
5
+ .option('-p, --period <period>', 'Time range: 7d, 30d, 90d, all', '7d')
6
+ .option('--project <name>', 'Scope to a single project')
7
+ .option('--source <tool>', 'Filter by source tool (claude-code, cursor, etc.)')
8
+ .option('--no-sync', 'Skip auto-sync before displaying stats')
9
+ .option('--local', 'Use local session files (no Firebase required)')
10
+ .option('--remote', 'Force Firestore data source (requires Firebase config)');
11
+ }
12
+ export function parseFlags(options) {
13
+ const period = options.period || '7d';
14
+ if (!VALID_PERIODS.includes(period)) {
15
+ throw new InvalidPeriodError(`Invalid period "${period}". Expected: ${VALID_PERIODS.join(', ')}`);
16
+ }
17
+ return {
18
+ period: period,
19
+ project: options.project,
20
+ source: options.source,
21
+ noSync: !options.sync, // Commander's --no-sync sets sync=false
22
+ local: !!options.local,
23
+ remote: !!options.remote,
24
+ };
25
+ }
26
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/commands/stats/shared.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAIlD,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,OAAO,GAAG;SACP,MAAM,CAAC,uBAAuB,EAAE,+BAA+B,EAAE,IAAI,CAAC;SACtE,MAAM,CAAC,kBAAkB,EAAE,2BAA2B,CAAC;SACvD,MAAM,CAAC,iBAAiB,EAAE,mDAAmD,CAAC;SAC9E,MAAM,CAAC,WAAW,EAAE,wCAAwC,CAAC;SAC7D,MAAM,CAAC,SAAS,EAAE,gDAAgD,CAAC;SACnE,MAAM,CAAC,UAAU,EAAE,wDAAwD,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAgC;IACzD,MAAM,MAAM,GAAI,OAAO,CAAC,MAAiB,IAAI,IAAI,CAAC;IAClD,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,kBAAkB,CAAC,mBAAmB,MAAM,gBAAgB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpG,CAAC;IACD,OAAO;QACL,MAAM,EAAE,MAAgB;QACxB,OAAO,EAAE,OAAO,CAAC,OAA6B;QAC9C,MAAM,EAAE,OAAO,CAAC,MAA4B;QAC5C,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,EAAG,wCAAwC;QAChE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK;QACtB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM;KACzB,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAkFnD"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA6FnD"}
@@ -1,6 +1,7 @@
1
1
  import chalk from 'chalk';
2
- import { loadConfig, loadSyncState, isConfigured, getConfigDir, getClaudeDir, hasWebConfig, loadWebConfig } from '../utils/config.js';
2
+ import { loadConfig, loadSyncState, isConfigured, getConfigDir, getClaudeDir, hasWebConfig, loadWebConfig, resolveDataSourcePreference } from '../utils/config.js';
3
3
  import { initializeFirebase, getProjects } from '../firebase/client.js';
4
+ import { trackEvent } from '../utils/telemetry.js';
4
5
  import * as fs from 'fs';
5
6
  /**
6
7
  * Show Code Insights status
@@ -9,17 +10,19 @@ export async function statusCommand() {
9
10
  console.log(chalk.cyan('\nšŸ“Š Code Insights Status\n'));
10
11
  // Check configuration
11
12
  console.log(chalk.white('Configuration:'));
13
+ const preference = resolveDataSourcePreference();
12
14
  if (isConfigured()) {
13
15
  console.log(chalk.green(` āœ“ Configured at ${getConfigDir()}`));
14
16
  const config = loadConfig();
15
17
  if (config) {
16
- console.log(chalk.gray(` Project: ${config.firebase.projectId}`));
18
+ console.log(chalk.gray(` Project: ${config.firebase?.projectId ?? '(local)'}`));
17
19
  }
20
+ console.log(chalk.gray(` Data source: ${preference}`));
18
21
  }
19
22
  else {
20
- console.log(chalk.red(' āœ— Not configured'));
21
- console.log(chalk.gray(' Run `code-insights init` to set up'));
22
- return;
23
+ console.log(chalk.yellow(' ā—‹ Not configured (running in zero-config mode)'));
24
+ console.log(chalk.gray(' Stats work without config: code-insights stats'));
25
+ console.log(chalk.gray(' To configure Firebase: code-insights init'));
23
26
  }
24
27
  // Check Claude directory
25
28
  console.log(chalk.white('\nClaude Code:'));
@@ -46,45 +49,55 @@ export async function statusCommand() {
46
49
  console.log(chalk.yellow(' ⚠ Never synced'));
47
50
  console.log(chalk.gray(' Run `code-insights sync` to sync'));
48
51
  }
49
- // Check Firebase connection
50
- console.log(chalk.white('\nFirebase:'));
51
- const config = loadConfig();
52
- if (config) {
53
- try {
54
- initializeFirebase(config);
55
- const projects = await getProjects();
56
- console.log(chalk.green(' āœ“ Connected'));
57
- console.log(chalk.gray(` ${projects.length} projects in Firestore`));
58
- if (projects.length > 0) {
59
- console.log(chalk.white('\nSynced Projects:'));
60
- for (const project of projects.slice(0, 5)) {
61
- console.log(chalk.gray(` ${project.name} (${project.sessionCount} sessions)`));
62
- }
63
- if (projects.length > 5) {
64
- console.log(chalk.gray(` ... and ${projects.length - 5} more`));
52
+ if (preference === 'local') {
53
+ // Local mode — skip Firebase connection check
54
+ console.log(chalk.white('\nFirebase:'));
55
+ console.log(chalk.gray(' ā—‹ Not applicable (data source is local)'));
56
+ console.log(chalk.gray(' Use `code-insights stats --local` for session analytics'));
57
+ console.log(chalk.gray(' To switch: code-insights config set-source firebase'));
58
+ }
59
+ else {
60
+ // Check Firebase connection
61
+ console.log(chalk.white('\nFirebase:'));
62
+ const config = loadConfig();
63
+ if (config) {
64
+ try {
65
+ initializeFirebase(config);
66
+ const projects = await getProjects();
67
+ console.log(chalk.green(' āœ“ Connected'));
68
+ console.log(chalk.gray(` ${projects.length} projects in Firestore`));
69
+ if (projects.length > 0) {
70
+ console.log(chalk.white('\nSynced Projects:'));
71
+ for (const project of projects.slice(0, 5)) {
72
+ console.log(chalk.gray(` ${project.name} (${project.sessionCount} sessions)`));
73
+ }
74
+ if (projects.length > 5) {
75
+ console.log(chalk.gray(` ... and ${projects.length - 5} more`));
76
+ }
65
77
  }
66
78
  }
79
+ catch (error) {
80
+ console.log(chalk.red(' āœ— Connection failed'));
81
+ console.log(chalk.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
82
+ }
67
83
  }
68
- catch (error) {
69
- console.log(chalk.red(' āœ— Connection failed'));
70
- console.log(chalk.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
84
+ // Check web dashboard config
85
+ console.log(chalk.white('\nWeb Dashboard:'));
86
+ if (hasWebConfig()) {
87
+ const webConfig = loadWebConfig();
88
+ console.log(chalk.green(' āœ“ Configured'));
89
+ if (webConfig && typeof webConfig.projectId === 'string') {
90
+ console.log(chalk.gray(` Project: ${webConfig.projectId}`));
91
+ }
92
+ console.log(chalk.gray(' Run "code-insights connect" to get dashboard URL'));
71
93
  }
72
- }
73
- // Check web dashboard config
74
- console.log(chalk.white('\nWeb Dashboard:'));
75
- if (hasWebConfig()) {
76
- const webConfig = loadWebConfig();
77
- console.log(chalk.green(' āœ“ Configured'));
78
- if (webConfig && typeof webConfig.projectId === 'string') {
79
- console.log(chalk.gray(` Project: ${webConfig.projectId}`));
94
+ else {
95
+ console.log(chalk.yellow(' ā—‹ Not configured'));
96
+ console.log(chalk.gray(' Run "code-insights init" to configure'));
80
97
  }
81
- console.log(chalk.gray(' Run "code-insights connect" to get dashboard URL'));
82
- }
83
- else {
84
- console.log(chalk.yellow(' ā—‹ Not configured'));
85
- console.log(chalk.gray(' Run "code-insights init" to configure'));
86
98
  }
87
99
  console.log('');
100
+ trackEvent('status', true);
88
101
  }
89
102
  /**
90
103
  * Count JSONL files in Claude directory
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACtI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;IAEvD,sBAAsB;IACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC3C,IAAI,YAAY,EAAE,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,qBAAqB,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC,CAAC;QAClE,OAAO;IACT,CAAC;IAED,yBAAyB;IACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QAChF,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,SAAS,EAAE,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,WAAW,CAAC,MAAM,cAAc,YAAY,WAAW,CAAC,CAAC,CAAC;IAC1F,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,oBAAoB,SAAS,EAAE,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,mBAAmB;IACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,aAAa,EAAE,CAAC;IAClC,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,kBAAkB,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,WAAW,gBAAgB,CAAC,CAAC,CAAC;IAC9D,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,4BAA4B;IAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,kBAAkB,CAAC,MAAM,CAAC,CAAC;YAC3B,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;YAC1C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC,MAAM,wBAAwB,CAAC,CAAC,CAAC;YAExE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;gBAC/C,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;oBAC3C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,YAAY,YAAY,CAAC,CAAC,CAAC;gBACpF,CAAC;gBACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACxB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,QAAQ,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC7C,IAAI,YAAY,EAAE,EAAE,CAAC;QACnB,MAAM,SAAS,GAAG,aAAa,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC3C,IAAI,SAAS,IAAI,OAAO,SAAS,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC,CAAC;IAClF,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,OAAe;IACtC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAErC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAClC,MAAM,WAAW,GAAG,GAAG,OAAO,IAAI,GAAG,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YAAE,SAAS;QAElC,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QAC1C,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5D,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,27 @@
1
1
  interface SyncOptions {
2
2
  force?: boolean;
3
+ forceRemote?: boolean;
3
4
  project?: string;
4
5
  dryRun?: boolean;
5
6
  quiet?: boolean;
6
7
  regenerateTitles?: boolean;
8
+ source?: string;
7
9
  }
10
+ export interface SyncResult {
11
+ syncedCount: number;
12
+ messageCount: number;
13
+ errorCount: number;
14
+ }
15
+ /**
16
+ * Core sync logic — reusable from stats commands and other callers.
17
+ *
18
+ * Throws on fatal errors (missing config, Firebase connection failure,
19
+ * unknown provider) instead of calling process.exit().
20
+ * Returns a SyncResult summary instead of printing one.
21
+ */
22
+ export declare function runSync(options?: SyncOptions): Promise<SyncResult>;
8
23
  /**
9
- * Sync Claude Code sessions to Firestore
24
+ * Sync AI coding sessions to Firestore
10
25
  */
11
26
  export declare function syncCommand(options?: SyncOptions): Promise<void>;
12
27
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAUA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmJ1E"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAYA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiK5E;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkC1E"}
@@ -2,13 +2,19 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import ora from 'ora';
5
- import { loadConfig, loadSyncState, saveSyncState, getClaudeDir } from '../utils/config.js';
6
- import { parseJsonlFile } from '../parser/jsonl.js';
5
+ import { loadConfig, loadSyncState, saveSyncState, resolveDataSourcePreference } from '../utils/config.js';
6
+ import { trackEvent } from '../utils/telemetry.js';
7
7
  import { initializeFirebase, uploadSession, uploadMessages, sessionExists, recalculateUsageStats } from '../firebase/client.js';
8
+ import { getAllProviders, getProvider } from '../providers/registry.js';
9
+ import { splitVirtualPath } from '../utils/paths.js';
8
10
  /**
9
- * Sync Claude Code sessions to Firestore
11
+ * Core sync logic — reusable from stats commands and other callers.
12
+ *
13
+ * Throws on fatal errors (missing config, Firebase connection failure,
14
+ * unknown provider) instead of calling process.exit().
15
+ * Returns a SyncResult summary instead of printing one.
10
16
  */
11
- export async function syncCommand(options = {}) {
17
+ export async function runSync(options = {}) {
12
18
  const log = options.quiet ? () => { } : console.log.bind(console);
13
19
  const noopSpinner = {
14
20
  start: function () { return this; },
@@ -20,12 +26,12 @@ export async function syncCommand(options = {}) {
20
26
  const createSpinner = options.quiet
21
27
  ? () => noopSpinner
22
28
  : ora;
23
- log(chalk.cyan('\nšŸ“¤ Code Insights Sync\n'));
29
+ log(chalk.cyan('\n\uD83D\uDCE4 Code Insights Sync\n'));
24
30
  // Load config
25
31
  const config = loadConfig();
26
32
  if (!config) {
27
- log(chalk.red('Not configured. Run `code-insights init` first.'));
28
- process.exit(1);
33
+ throw new Error('Sync requires Firebase. Run `code-insights init` to set up.\n' +
34
+ ' For local-only analytics: code-insights stats');
29
35
  }
30
36
  // Initialize Firebase
31
37
  const spinner = createSpinner('Connecting to Firebase...').start();
@@ -35,86 +41,104 @@ export async function syncCommand(options = {}) {
35
41
  }
36
42
  catch (error) {
37
43
  spinner.fail('Failed to connect to Firebase');
38
- if (!options.quiet) {
39
- console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
40
- }
41
- process.exit(1);
44
+ throw new Error(`Failed to connect to Firebase: ${error instanceof Error ? error.message : 'Unknown error'}`);
42
45
  }
43
- // Find JSONL files
44
- const claudeDir = getClaudeDir();
45
- if (!fs.existsSync(claudeDir)) {
46
- log(chalk.yellow(`Claude directory not found: ${claudeDir}`));
47
- log(chalk.gray('Make sure you have Claude Code installed and have run at least one session.'));
48
- process.exit(1);
46
+ // Dry-run banner
47
+ if (options.dryRun) {
48
+ log(chalk.yellow('\n\uD83D\uDD0D Dry run \u2014 no changes will be made'));
49
49
  }
50
- spinner.start('Discovering sessions...');
51
- const jsonlFiles = discoverJsonlFiles(claudeDir, options.project);
52
- spinner.succeed(`Found ${jsonlFiles.length} session files`);
53
- if (jsonlFiles.length === 0) {
54
- log(chalk.yellow('No sessions to sync.'));
55
- return;
50
+ // Get providers to sync
51
+ let providers;
52
+ if (options.source) {
53
+ try {
54
+ providers = [getProvider(options.source)];
55
+ }
56
+ catch {
57
+ throw new Error(`Unknown source: ${options.source}. Available: ${getAllProviders().map(p => p.getProviderName()).join(', ')}`);
58
+ }
59
+ }
60
+ else {
61
+ providers = getAllProviders();
56
62
  }
57
63
  // Load sync state
58
64
  const syncState = options.force ? { lastSync: '', files: {} } : loadSyncState();
59
- // Filter to only new/modified files
60
- const filesToSync = filterFilesToSync(jsonlFiles, syncState, options.force);
61
- log(chalk.gray(` ${filesToSync.length} files need syncing (${jsonlFiles.length - filesToSync.length} already synced)`));
62
- if (filesToSync.length === 0) {
63
- log(chalk.green('\nāœ… Already up to date!'));
64
- return;
65
- }
66
- if (options.dryRun) {
67
- log(chalk.yellow('\nšŸ” Dry run - no changes will be made'));
68
- for (const file of filesToSync) {
69
- log(chalk.gray(` Would sync: ${path.basename(file)}`));
70
- }
71
- return;
72
- }
73
- // Process files
74
- let syncedCount = 0;
75
- let messageCount = 0;
76
- let errorCount = 0;
77
- for (const filePath of filesToSync) {
78
- const fileName = path.basename(filePath);
79
- spinner.start(`Processing ${fileName}...`);
65
+ let totalSyncedCount = 0;
66
+ let totalMessageCount = 0;
67
+ let totalErrorCount = 0;
68
+ for (const provider of providers) {
69
+ const providerName = provider.getProviderName();
80
70
  try {
81
- // Parse session
82
- const session = await parseJsonlFile(filePath);
83
- if (!session) {
84
- spinner.warn(`Skipped ${fileName} (no valid data)`);
71
+ if (providers.length > 1) {
72
+ log(chalk.cyan(`\n\uD83D\uDCE6 Syncing ${providerName}...`));
73
+ }
74
+ // Discovery
75
+ spinner.start(`Discovering ${providerName} sessions...`);
76
+ const sessionFiles = await provider.discover({ projectFilter: options.project });
77
+ spinner.succeed(`Found ${sessionFiles.length} ${providerName} session files`);
78
+ if (sessionFiles.length === 0)
79
+ continue;
80
+ // Filter to only new/modified files
81
+ const filesToSync = filterFilesToSync(sessionFiles, syncState, options.force);
82
+ log(chalk.gray(` ${filesToSync.length} files need syncing (${sessionFiles.length - filesToSync.length} already synced)`));
83
+ if (filesToSync.length === 0)
84
+ continue;
85
+ if (options.dryRun) {
86
+ for (const file of filesToSync) {
87
+ log(chalk.gray(` Would sync: ${path.basename(file)}`));
88
+ }
85
89
  continue;
86
90
  }
87
- // Check if already exists (unless force)
88
- if (!options.force) {
89
- const exists = await sessionExists(session.id);
90
- if (exists) {
91
- spinner.info(`Skipped ${fileName} (already synced)`);
91
+ // Process files
92
+ for (const filePath of filesToSync) {
93
+ const fileName = path.basename(filePath);
94
+ spinner.start(`Processing ${fileName}...`);
95
+ try {
96
+ // Parse session
97
+ const session = await provider.parse(filePath);
98
+ if (!session) {
99
+ spinner.warn(`Skipped ${fileName} (no valid data)`);
100
+ continue;
101
+ }
102
+ // Check if already exists (unless force)
103
+ if (!options.force) {
104
+ const exists = await sessionExists(session.id);
105
+ if (exists) {
106
+ spinner.info(`Skipped ${fileName} (already synced)`);
107
+ updateSyncState(syncState, filePath, session.id);
108
+ saveSyncState(syncState);
109
+ continue;
110
+ }
111
+ }
112
+ // Upload session and messages to Firestore
113
+ await uploadSession(session, !!options.force);
114
+ await uploadMessages(session);
115
+ // Update and persist sync state after each file
116
+ // so progress survives crashes (e.g., Firebase quota exceeded)
92
117
  updateSyncState(syncState, filePath, session.id);
93
118
  saveSyncState(syncState);
94
- continue;
119
+ totalSyncedCount++;
120
+ totalMessageCount += session.messages.length;
121
+ spinner.succeed(`Synced ${fileName} (${session.messages.length} messages)`);
122
+ }
123
+ catch (error) {
124
+ totalErrorCount++;
125
+ spinner.fail(`Failed to sync ${fileName}`);
126
+ if (!options.quiet) {
127
+ console.error(chalk.red(` ${error instanceof Error ? error.message : 'Unknown error'}`));
128
+ }
95
129
  }
96
130
  }
97
- // Upload session and messages to Firestore
98
- await uploadSession(session, !!options.force);
99
- await uploadMessages(session);
100
- // Update and persist sync state after each file
101
- // so progress survives crashes (e.g., Firebase quota exceeded)
102
- updateSyncState(syncState, filePath, session.id);
103
- saveSyncState(syncState);
104
- syncedCount++;
105
- messageCount += session.messages.length;
106
- spinner.succeed(`Synced ${fileName} (${session.messages.length} messages)`);
107
131
  }
108
132
  catch (error) {
109
- errorCount++;
110
- spinner.fail(`Failed to sync ${fileName}`);
133
+ totalErrorCount++;
134
+ spinner.fail(`Failed to sync ${providerName}`);
111
135
  if (!options.quiet) {
112
136
  console.error(chalk.red(` ${error instanceof Error ? error.message : 'Unknown error'}`));
113
137
  }
114
138
  }
115
139
  }
116
- // Reconcile usage stats after force sync
117
- if (options.force) {
140
+ // Reconcile usage stats after force sync (skip if nothing changed)
141
+ if (options.force && (totalSyncedCount > 0 || totalErrorCount > 0)) {
118
142
  spinner.start('Recalculating usage stats...');
119
143
  try {
120
144
  const result = await recalculateUsageStats();
@@ -130,52 +154,48 @@ export async function syncCommand(options = {}) {
130
154
  // Save sync state
131
155
  syncState.lastSync = new Date().toISOString();
132
156
  saveSyncState(syncState);
133
- // Summary
134
- log(chalk.cyan('\nšŸ“Š Sync Summary'));
135
- log(chalk.white(` Sessions synced: ${syncedCount}`));
136
- log(chalk.white(` Messages uploaded: ${messageCount}`));
137
- if (errorCount > 0) {
138
- log(chalk.red(` Errors: ${errorCount}`));
139
- }
140
- log(chalk.green('\nāœ… Sync complete!'));
157
+ return {
158
+ syncedCount: totalSyncedCount,
159
+ messageCount: totalMessageCount,
160
+ errorCount: totalErrorCount,
161
+ };
141
162
  }
142
163
  /**
143
- * Discover all JSONL files in Claude directory
164
+ * Sync AI coding sessions to Firestore
144
165
  */
145
- function discoverJsonlFiles(baseDir, projectFilter) {
146
- const files = [];
147
- const projectDirs = fs.readdirSync(baseDir);
148
- for (const projectDir of projectDirs) {
149
- // Skip hidden files and non-directories
150
- if (projectDir.startsWith('.'))
151
- continue;
152
- const projectPath = path.join(baseDir, projectDir);
153
- const stat = fs.statSync(projectPath);
154
- if (!stat.isDirectory())
155
- continue;
156
- // Apply project filter if specified
157
- if (projectFilter && !projectDir.toLowerCase().includes(projectFilter.toLowerCase())) {
158
- continue;
166
+ export async function syncCommand(options = {}) {
167
+ const log = options.quiet ? () => { } : console.log.bind(console);
168
+ const preference = resolveDataSourcePreference();
169
+ if (preference === 'local' && !options.forceRemote) {
170
+ log(chalk.yellow('\n ⚠ Data source is set to local. Sync is only used with Firebase.\n'));
171
+ log(chalk.gray(' To switch to Firebase: code-insights config set-source firebase'));
172
+ log(chalk.gray(' To sync anyway (one-time): code-insights sync --force-remote\n'));
173
+ return;
174
+ }
175
+ try {
176
+ const result = await runSync(options);
177
+ // Summary (only if not quiet)
178
+ if (result.syncedCount === 0 && result.errorCount === 0) {
179
+ log(chalk.green('\n\u2705 Already up to date!'));
180
+ trackEvent('sync', true);
181
+ return;
159
182
  }
160
- // Find JSONL files in project directory
161
- const projectFiles = fs.readdirSync(projectPath);
162
- for (const file of projectFiles) {
163
- if (file.endsWith('.jsonl')) {
164
- files.push(path.join(projectPath, file));
165
- }
183
+ log(chalk.cyan('\n\uD83D\uDCCA Sync Summary'));
184
+ log(chalk.white(` Sessions synced: ${result.syncedCount}`));
185
+ log(chalk.white(` Messages uploaded: ${result.messageCount}`));
186
+ if (result.errorCount > 0) {
187
+ log(chalk.red(` Errors: ${result.errorCount}`));
166
188
  }
167
- // Also check subagents directory
168
- const subagentsDir = path.join(projectPath, 'subagents');
169
- if (fs.existsSync(subagentsDir)) {
170
- const subagentFiles = fs.readdirSync(subagentsDir);
171
- for (const file of subagentFiles) {
172
- if (file.endsWith('.jsonl')) {
173
- files.push(path.join(subagentsDir, file));
174
- }
175
- }
189
+ log(chalk.green('\n\u2705 Sync complete!'));
190
+ trackEvent('sync', true);
191
+ }
192
+ catch (error) {
193
+ trackEvent('sync', false);
194
+ if (!options.quiet) {
195
+ console.error(chalk.red(error instanceof Error ? error.message : 'Sync failed'));
176
196
  }
197
+ process.exit(1);
177
198
  }
178
- return files;
179
199
  }
180
200
  /**
181
201
  * Filter files to only those that need syncing
@@ -184,22 +204,52 @@ function filterFilesToSync(files, syncState, force) {
184
204
  if (force)
185
205
  return files;
186
206
  return files.filter((filePath) => {
187
- const stat = fs.statSync(filePath);
207
+ const { realPath, sessionFragment } = splitVirtualPath(filePath);
208
+ const stat = fs.statSync(realPath);
188
209
  const lastModified = stat.mtime.toISOString();
189
- const fileState = syncState.files[filePath];
190
- // Sync if never synced or modified since last sync
191
- return !fileState || fileState.lastModified !== lastModified;
210
+ const fileState = syncState.files[realPath];
211
+ // If file was never synced, sync it
212
+ if (!fileState)
213
+ return true;
214
+ // For virtual paths (multi-session files), check if this specific session was synced
215
+ if (sessionFragment && fileState.syncedSessionIds) {
216
+ return !fileState.syncedSessionIds.includes(sessionFragment);
217
+ }
218
+ // For regular files, check if modified since last sync
219
+ if (sessionFragment) {
220
+ // Virtual path but no syncedSessionIds tracked yet — needs sync
221
+ return true;
222
+ }
223
+ return fileState.lastModified !== lastModified;
192
224
  });
193
225
  }
194
226
  /**
195
227
  * Update sync state for a file
196
228
  */
197
229
  function updateSyncState(state, filePath, sessionId) {
198
- const stat = fs.statSync(filePath);
199
- state.files[filePath] = {
200
- lastModified: stat.mtime.toISOString(),
201
- lastSyncedLine: 0, // Not tracking lines for now
202
- sessionId,
203
- };
230
+ const { realPath, sessionFragment } = splitVirtualPath(filePath);
231
+ const stat = fs.statSync(realPath);
232
+ if (sessionFragment) {
233
+ // Virtual path: track the session fragment in syncedSessionIds
234
+ const existing = state.files[realPath];
235
+ const syncedIds = existing?.syncedSessionIds || [];
236
+ if (!syncedIds.includes(sessionFragment)) {
237
+ syncedIds.push(sessionFragment);
238
+ }
239
+ state.files[realPath] = {
240
+ lastModified: stat.mtime.toISOString(),
241
+ lastSyncedLine: 0,
242
+ sessionId,
243
+ syncedSessionIds: syncedIds,
244
+ };
245
+ }
246
+ else {
247
+ // Regular file path
248
+ state.files[realPath] = {
249
+ lastModified: stat.mtime.toISOString(),
250
+ lastSyncedLine: 0,
251
+ sessionId,
252
+ };
253
+ }
204
254
  }
205
255
  //# sourceMappingURL=sync.js.map