liferewind 0.1.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 (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +219 -0
  3. package/dist/api/client.d.ts +31 -0
  4. package/dist/api/client.d.ts.map +1 -0
  5. package/dist/api/client.js +115 -0
  6. package/dist/cli/commands/collect.d.ts +3 -0
  7. package/dist/cli/commands/collect.d.ts.map +1 -0
  8. package/dist/cli/commands/collect.js +62 -0
  9. package/dist/cli/commands/config.d.ts +3 -0
  10. package/dist/cli/commands/config.d.ts.map +1 -0
  11. package/dist/cli/commands/config.js +112 -0
  12. package/dist/cli/commands/doctor.d.ts +3 -0
  13. package/dist/cli/commands/doctor.d.ts.map +1 -0
  14. package/dist/cli/commands/doctor.js +150 -0
  15. package/dist/cli/commands/init.d.ts +3 -0
  16. package/dist/cli/commands/init.d.ts.map +1 -0
  17. package/dist/cli/commands/init.js +244 -0
  18. package/dist/cli/commands/start.d.ts +3 -0
  19. package/dist/cli/commands/start.d.ts.map +1 -0
  20. package/dist/cli/commands/start.js +59 -0
  21. package/dist/cli/commands/status.d.ts +3 -0
  22. package/dist/cli/commands/status.d.ts.map +1 -0
  23. package/dist/cli/commands/status.js +49 -0
  24. package/dist/cli/detect/browsers.d.ts +3 -0
  25. package/dist/cli/detect/browsers.d.ts.map +1 -0
  26. package/dist/cli/detect/browsers.js +19 -0
  27. package/dist/cli/detect/chatbot.d.ts +3 -0
  28. package/dist/cli/detect/chatbot.d.ts.map +1 -0
  29. package/dist/cli/detect/chatbot.js +15 -0
  30. package/dist/cli/detect/git.d.ts +2 -0
  31. package/dist/cli/detect/git.d.ts.map +1 -0
  32. package/dist/cli/detect/git.js +10 -0
  33. package/dist/cli/detect/index.d.ts +4 -0
  34. package/dist/cli/detect/index.d.ts.map +1 -0
  35. package/dist/cli/detect/index.js +3 -0
  36. package/dist/cli/index.d.ts +3 -0
  37. package/dist/cli/index.d.ts.map +1 -0
  38. package/dist/cli/index.js +30 -0
  39. package/dist/cli/utils/output.d.ts +8 -0
  40. package/dist/cli/utils/output.d.ts.map +1 -0
  41. package/dist/cli/utils/output.js +28 -0
  42. package/dist/cli/utils/path.d.ts +9 -0
  43. package/dist/cli/utils/path.d.ts.map +1 -0
  44. package/dist/cli/utils/path.js +23 -0
  45. package/dist/config/loader.d.ts +7 -0
  46. package/dist/config/loader.d.ts.map +1 -0
  47. package/dist/config/loader.js +64 -0
  48. package/dist/config/paths.d.ts +8 -0
  49. package/dist/config/paths.d.ts.map +1 -0
  50. package/dist/config/paths.js +21 -0
  51. package/dist/config/schema.d.ts +95 -0
  52. package/dist/config/schema.d.ts.map +1 -0
  53. package/dist/config/schema.js +110 -0
  54. package/dist/config/writer.d.ts +3 -0
  55. package/dist/config/writer.d.ts.map +1 -0
  56. package/dist/config/writer.js +19 -0
  57. package/dist/core/collector.d.ts +19 -0
  58. package/dist/core/collector.d.ts.map +1 -0
  59. package/dist/core/collector.js +83 -0
  60. package/dist/core/scheduler.d.ts +12 -0
  61. package/dist/core/scheduler.d.ts.map +1 -0
  62. package/dist/core/scheduler.js +48 -0
  63. package/dist/core/types.d.ts +29 -0
  64. package/dist/core/types.d.ts.map +1 -0
  65. package/dist/core/types.js +2 -0
  66. package/dist/index.d.ts +9 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +7 -0
  69. package/dist/sources/base.d.ts +27 -0
  70. package/dist/sources/base.d.ts.map +1 -0
  71. package/dist/sources/base.js +23 -0
  72. package/dist/sources/browser/index.d.ts +14 -0
  73. package/dist/sources/browser/index.d.ts.map +1 -0
  74. package/dist/sources/browser/index.js +116 -0
  75. package/dist/sources/browser/readers/base.d.ts +28 -0
  76. package/dist/sources/browser/readers/base.d.ts.map +1 -0
  77. package/dist/sources/browser/readers/base.js +110 -0
  78. package/dist/sources/browser/readers/chromium.d.ts +13 -0
  79. package/dist/sources/browser/readers/chromium.d.ts.map +1 -0
  80. package/dist/sources/browser/readers/chromium.js +64 -0
  81. package/dist/sources/browser/readers/safari.d.ts +9 -0
  82. package/dist/sources/browser/readers/safari.d.ts.map +1 -0
  83. package/dist/sources/browser/readers/safari.js +34 -0
  84. package/dist/sources/browser/types.d.ts +35 -0
  85. package/dist/sources/browser/types.d.ts.map +1 -0
  86. package/dist/sources/browser/types.js +1 -0
  87. package/dist/sources/chatbot/index.d.ts +12 -0
  88. package/dist/sources/chatbot/index.d.ts.map +1 -0
  89. package/dist/sources/chatbot/index.js +67 -0
  90. package/dist/sources/chatbot/readers/base.d.ts +25 -0
  91. package/dist/sources/chatbot/readers/base.d.ts.map +1 -0
  92. package/dist/sources/chatbot/readers/base.js +109 -0
  93. package/dist/sources/chatbot/readers/chatwise.d.ts +14 -0
  94. package/dist/sources/chatbot/readers/chatwise.d.ts.map +1 -0
  95. package/dist/sources/chatbot/readers/chatwise.js +117 -0
  96. package/dist/sources/chatbot/types.d.ts +33 -0
  97. package/dist/sources/chatbot/types.d.ts.map +1 -0
  98. package/dist/sources/chatbot/types.js +1 -0
  99. package/dist/sources/filesystem/index.d.ts +10 -0
  100. package/dist/sources/filesystem/index.d.ts.map +1 -0
  101. package/dist/sources/filesystem/index.js +58 -0
  102. package/dist/sources/filesystem/scanner.d.ts +24 -0
  103. package/dist/sources/filesystem/scanner.d.ts.map +1 -0
  104. package/dist/sources/filesystem/scanner.js +264 -0
  105. package/dist/sources/filesystem/types.d.ts +39 -0
  106. package/dist/sources/filesystem/types.d.ts.map +1 -0
  107. package/dist/sources/filesystem/types.js +1 -0
  108. package/dist/sources/git/index.d.ts +16 -0
  109. package/dist/sources/git/index.d.ts.map +1 -0
  110. package/dist/sources/git/index.js +169 -0
  111. package/dist/sources/git/types.d.ts +25 -0
  112. package/dist/sources/git/types.d.ts.map +1 -0
  113. package/dist/sources/git/types.js +1 -0
  114. package/dist/sources/index.d.ts +7 -0
  115. package/dist/sources/index.d.ts.map +1 -0
  116. package/dist/sources/index.js +16 -0
  117. package/dist/sources/registry.d.ts +13 -0
  118. package/dist/sources/registry.d.ts.map +1 -0
  119. package/dist/sources/registry.js +19 -0
  120. package/dist/utils/logger.d.ts +12 -0
  121. package/dist/utils/logger.d.ts.map +1 -0
  122. package/dist/utils/logger.js +34 -0
  123. package/dist/utils/path.d.ts +9 -0
  124. package/dist/utils/path.d.ts.map +1 -0
  125. package/dist/utils/path.js +22 -0
  126. package/dist/utils/retry.d.ts +8 -0
  127. package/dist/utils/retry.d.ts.map +1 -0
  128. package/dist/utils/retry.js +22 -0
  129. package/package.json +81 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Richard Wang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,219 @@
1
+ # liferewind
2
+
3
+ [![npm version](https://img.shields.io/npm/v/liferewind.svg)](https://www.npmjs.com/package/liferewind)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ AI-powered personal life review tool - collect your digital footprints from git, browser history, documents, and AI chatbots.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install -g liferewind
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ```bash
17
+ # Interactive configuration wizard
18
+ liferewind init
19
+
20
+ # Start collecting
21
+ liferewind start
22
+
23
+ # Manual collection
24
+ liferewind collect
25
+ ```
26
+
27
+ ## Data Sources
28
+
29
+ | Type | Status | Description |
30
+ |------|--------|-------------|
31
+ | Git | ✅ Implemented | Git commit history |
32
+ | Browser | ✅ Implemented | Browser history (Chrome, Safari, Arc, Dia, Comet) |
33
+ | Filesystem | ✅ Implemented | Document change tracking (.md, .txt, .docx, .pdf, etc.) |
34
+ | Chatbot | ✅ Implemented | Local AI chatbot history (ChatWise) |
35
+
36
+ ## Configuration
37
+
38
+ Run `liferewind init` for interactive setup, or create config manually.
39
+
40
+ Config file lookup order:
41
+ 1. `~/.liferewind/config.json` (primary user config)
42
+ 2. `~/.config/liferewind/collector.json`
43
+ 3. `~/.liferewind-collector.json`
44
+ 4. `./collector.config.json`
45
+
46
+ Example configuration (see `collector.config.example.json`):
47
+
48
+ ```json
49
+ {
50
+ "api": {
51
+ "baseUrl": "http://localhost:3000",
52
+ "apiKey": "your-api-key"
53
+ },
54
+ "sources": {
55
+ "git": {
56
+ "enabled": true,
57
+ "schedule": "daily",
58
+ "options": {
59
+ "scanPaths": ["~/Documents", "~/Projects"],
60
+ "sinceDays": 30
61
+ }
62
+ },
63
+ "browser": {
64
+ "enabled": true,
65
+ "schedule": "daily",
66
+ "options": {
67
+ "browsers": ["chrome", "safari", "arc"],
68
+ "excludeDomains": ["localhost", "127.0.0.1"],
69
+ "sinceDays": 7
70
+ }
71
+ },
72
+ "filesystem": {
73
+ "enabled": false,
74
+ "schedule": "daily",
75
+ "options": {
76
+ "watchPaths": ["~/Documents"],
77
+ "excludePatterns": ["**/node_modules/**", "**/.git/**"],
78
+ "fileTypes": [".md", ".txt", ".docx", ".pdf"],
79
+ "sinceDays": 7,
80
+ "includeContent": true
81
+ }
82
+ },
83
+ "chatbot": {
84
+ "enabled": false,
85
+ "schedule": "daily",
86
+ "options": {
87
+ "clients": ["chatwise"],
88
+ "sinceDays": 30,
89
+ "includeContent": true
90
+ }
91
+ }
92
+ },
93
+ "logging": {
94
+ "level": "info"
95
+ }
96
+ }
97
+ ```
98
+
99
+ **Note:** Git repositories are automatically excluded from filesystem scanning.
100
+
101
+ Environment variables (fallback):
102
+ ```bash
103
+ export LIFEREWIND_API_URL="http://localhost:3000"
104
+ export LIFEREWIND_API_KEY="your-api-key"
105
+ ```
106
+
107
+ ## CLI Commands
108
+
109
+ ```bash
110
+ # Configuration
111
+ liferewind init # Interactive setup wizard
112
+ liferewind config show # Display current config
113
+ liferewind config edit # Open config in editor
114
+ liferewind config validate # Validate config file
115
+ liferewind config path # Show config file locations
116
+
117
+ # Collection
118
+ liferewind start # Start collector service
119
+ liferewind start --run-once # Collect once and exit
120
+ liferewind collect # Manual trigger (all sources)
121
+ liferewind collect git # Manual trigger (specific source)
122
+
123
+ # Diagnostics
124
+ liferewind status # Show service status
125
+ liferewind doctor # Check environment issues
126
+ ```
127
+
128
+ ## Development
129
+
130
+ ```bash
131
+ # Development with watch
132
+ pnpm dev
133
+
134
+ # Build
135
+ pnpm build
136
+
137
+ # Production
138
+ pnpm start
139
+
140
+ # PM2 daemon
141
+ pnpm pm2:start
142
+ pnpm pm2:stop
143
+ pnpm pm2:logs
144
+ ```
145
+
146
+ ## Schedule Frequencies
147
+
148
+ | Value | Cron | Description |
149
+ |-------|------|-------------|
150
+ | `hourly` | `0 * * * *` | Every hour |
151
+ | `daily` | `0 9 * * *` | Daily at 9:00 |
152
+ | `weekly` | `0 9 * * 1` | Monday at 9:00 |
153
+ | `monthly` | `0 9 1 * *` | 1st of month at 9:00 |
154
+ | `manual` | - | Manual trigger only |
155
+
156
+ ## Adding a Data Source
157
+
158
+ 1. Create a new directory under `src/sources/`
159
+ 2. Extend the `DataSource<TOptions>` base class
160
+ 3. Register in `src/sources/index.ts`
161
+
162
+ ```typescript
163
+ // src/sources/my-source/index.ts
164
+ import { DataSource } from '../base.js';
165
+
166
+ interface MySourceOptions {
167
+ // ...
168
+ }
169
+
170
+ export class MySource extends DataSource<MySourceOptions> {
171
+ readonly type = 'my-source' as const;
172
+ readonly name = 'My Source';
173
+
174
+ async validate(): Promise<boolean> {
175
+ // Validate environment requirements
176
+ }
177
+
178
+ async collect(): Promise<CollectionResult> {
179
+ // Collect data
180
+ }
181
+ }
182
+
183
+ // src/sources/index.ts
184
+ sourceRegistry.register('my-source', (config, ctx) => new MySource(config, ctx));
185
+ ```
186
+
187
+ ## Project Structure
188
+
189
+ ```
190
+ src/
191
+ ├── index.ts # Library exports
192
+ ├── cli/ # CLI commands
193
+ │ ├── index.ts # CLI entry point
194
+ │ ├── commands/ # Command implementations
195
+ │ ├── detect/ # Environment detection
196
+ │ └── utils/ # CLI utilities
197
+ ├── core/
198
+ │ ├── types.ts # Type definitions
199
+ │ ├── collector.ts # Main orchestrator
200
+ │ └── scheduler.ts # Cron scheduler
201
+ ├── sources/
202
+ │ ├── base.ts # DataSource base class
203
+ │ ├── registry.ts # Source registry
204
+ │ ├── index.ts # Source registration
205
+ │ ├── git/ # Git data source
206
+ │ ├── browser/ # Browser history (multi-browser)
207
+ │ ├── filesystem/ # Filesystem changes
208
+ │ └── chatbot/ # Chatbot history (multi-client)
209
+ ├── api/
210
+ │ └── client.ts # API client
211
+ ├── config/
212
+ │ ├── schema.ts # Config schema (Zod)
213
+ │ ├── loader.ts # Config loader
214
+ │ ├── writer.ts # Config writer
215
+ │ └── paths.ts # Config path constants
216
+ └── utils/
217
+ ├── logger.ts # Logger
218
+ └── retry.ts # Retry with backoff
219
+ ```
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ import type { CollectionResult } from '../core/types.js';
3
+ import type { Logger } from '../utils/logger.js';
4
+ export interface ApiClientConfig {
5
+ baseUrl: string;
6
+ apiKey: string;
7
+ timeout: number;
8
+ retryAttempts: number;
9
+ }
10
+ declare const pushResponseSchema: z.ZodObject<{
11
+ success: z.ZodLiteral<true>;
12
+ data: z.ZodObject<{
13
+ itemsReceived: z.ZodNumber;
14
+ itemsInserted: z.ZodNumber;
15
+ requestId: z.ZodString;
16
+ }, z.core.$strip>;
17
+ }, z.core.$strip>;
18
+ export type PushResponse = z.infer<typeof pushResponseSchema>;
19
+ export declare class ApiClient {
20
+ private static readonly BATCH_SIZE;
21
+ private config;
22
+ private logger;
23
+ constructor(config: ApiClientConfig, logger: Logger);
24
+ pushData(result: CollectionResult): Promise<PushResponse>;
25
+ healthCheck(): Promise<boolean>;
26
+ private pushInBatches;
27
+ private pushBatch;
28
+ private splitIntoBatches;
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGjD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,QAAA,MAAM,kBAAkB;;;;;;;iBAOtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAO9D,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAQ;IAE1C,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM;IAK7C,QAAQ,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAQzD,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;YAWvB,aAAa;YA4Db,SAAS;IAuCvB,OAAO,CAAC,gBAAgB;CAOzB"}
@@ -0,0 +1,115 @@
1
+ import { z } from 'zod';
2
+ import { retry } from '../utils/retry.js';
3
+ const pushResponseSchema = z.object({
4
+ success: z.literal(true),
5
+ data: z.object({
6
+ itemsReceived: z.number(),
7
+ itemsInserted: z.number(),
8
+ requestId: z.string(),
9
+ }),
10
+ });
11
+ export class ApiClient {
12
+ static BATCH_SIZE = 1000;
13
+ config;
14
+ logger;
15
+ constructor(config, logger) {
16
+ this.config = config;
17
+ this.logger = logger;
18
+ }
19
+ async pushData(result) {
20
+ if (result.items.length <= ApiClient.BATCH_SIZE) {
21
+ return this.pushBatch(result);
22
+ }
23
+ return this.pushInBatches(result);
24
+ }
25
+ async healthCheck() {
26
+ try {
27
+ const response = await fetch(`${this.config.baseUrl}/api/health`, {
28
+ signal: AbortSignal.timeout(5000),
29
+ });
30
+ return response.ok;
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ async pushInBatches(result) {
37
+ const batches = this.splitIntoBatches(result.items, ApiClient.BATCH_SIZE);
38
+ this.logger.info(`Splitting ${result.items.length} items into ${batches.length} batches (${ApiClient.BATCH_SIZE} items/batch)`);
39
+ const batchResults = [];
40
+ for (const [index, batch] of batches.entries()) {
41
+ const batchNumber = index + 1;
42
+ this.logger.info(`Pushing batch ${batchNumber}/${batches.length} (${batch.length} items)`);
43
+ try {
44
+ const response = await this.pushBatch({
45
+ ...result,
46
+ items: batch,
47
+ itemsCollected: batch.length,
48
+ });
49
+ batchResults.push({
50
+ requestId: response.data.requestId,
51
+ itemsInserted: response.data.itemsInserted,
52
+ });
53
+ this.logger.info(`Batch ${batchNumber}/${batches.length} completed: ` +
54
+ `requestId=${response.data.requestId}, inserted=${response.data.itemsInserted}/${batch.length}`);
55
+ }
56
+ catch (error) {
57
+ const successfulBatches = batchResults.length;
58
+ const totalInserted = batchResults.reduce((sum, r) => sum + r.itemsInserted, 0);
59
+ this.logger.error(`Batch ${batchNumber}/${batches.length} failed. ` +
60
+ `${successfulBatches} batches succeeded (${totalInserted} items inserted).`, error);
61
+ throw new Error(`Batch ${batchNumber}/${batches.length} failed after ${successfulBatches} successful batches ` +
62
+ `(${totalInserted} items inserted). ${error instanceof Error ? error.message : 'Unknown error'}`);
63
+ }
64
+ }
65
+ const totalInserted = batchResults.reduce((sum, r) => sum + r.itemsInserted, 0);
66
+ const lastRequestId = batchResults[batchResults.length - 1]?.requestId ?? 'unknown';
67
+ return {
68
+ success: true,
69
+ data: {
70
+ itemsReceived: result.items.length,
71
+ itemsInserted: totalInserted,
72
+ requestId: lastRequestId,
73
+ },
74
+ };
75
+ }
76
+ async pushBatch(result) {
77
+ const url = `${this.config.baseUrl}/api/collector/ingest`;
78
+ return retry(async () => {
79
+ const response = await fetch(url, {
80
+ method: 'POST',
81
+ headers: {
82
+ 'Content-Type': 'application/json',
83
+ Authorization: `Bearer ${this.config.apiKey}`,
84
+ 'X-Source-Type': result.sourceType,
85
+ },
86
+ body: JSON.stringify({
87
+ sourceType: result.sourceType,
88
+ collectedAt: result.collectedAt.toISOString(),
89
+ items: result.items,
90
+ }),
91
+ signal: AbortSignal.timeout(this.config.timeout),
92
+ });
93
+ if (!response.ok) {
94
+ const errorText = await response.text();
95
+ throw new Error(`API error ${response.status}: ${errorText}`);
96
+ }
97
+ const json = await response.json();
98
+ return pushResponseSchema.parse(json);
99
+ }, {
100
+ attempts: this.config.retryAttempts,
101
+ delay: 1000,
102
+ backoff: 2,
103
+ onRetry: (error, attempt) => {
104
+ this.logger.warn(`API push retry ${attempt}/${this.config.retryAttempts}`, error);
105
+ },
106
+ });
107
+ }
108
+ splitIntoBatches(items, batchSize) {
109
+ const batches = [];
110
+ for (let i = 0; i < items.length; i += batchSize) {
111
+ batches.push(items.slice(i, i + batchSize));
112
+ }
113
+ return batches;
114
+ }
115
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const collectCommand: Command;
3
+ //# sourceMappingURL=collect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/collect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,cAAc,SA0DvB,CAAC"}
@@ -0,0 +1,62 @@
1
+ import { Command } from 'commander';
2
+ import ora from 'ora';
3
+ import { Collector } from '../../core/collector.js';
4
+ import { loadConfig } from '../../config/loader.js';
5
+ import { createLogger } from '../../utils/logger.js';
6
+ import { registerBuiltinSources } from '../../sources/index.js';
7
+ import { printError, printSuccess } from '../utils/output.js';
8
+ import { SOURCE_TYPES } from '../../core/types.js';
9
+ export const collectCommand = new Command('collect')
10
+ .description('Manually trigger data collection')
11
+ .argument('[source]', 'specific source to collect (git, browser, filesystem, chatbot)')
12
+ .action(async (source, _options, cmd) => {
13
+ const globalOpts = cmd.optsWithGlobals();
14
+ // Validate source argument
15
+ if (source && !SOURCE_TYPES.includes(source)) {
16
+ printError(`Invalid source: ${source}`);
17
+ console.log(`Valid sources: ${SOURCE_TYPES.join(', ')}`);
18
+ process.exit(1);
19
+ }
20
+ try {
21
+ const config = loadConfig(globalOpts.config);
22
+ // Apply global log level overrides
23
+ let logLevel = config.logging.level;
24
+ if (globalOpts.verbose)
25
+ logLevel = 'debug';
26
+ if (globalOpts.quiet)
27
+ logLevel = 'error';
28
+ const logger = createLogger({ level: logLevel });
29
+ registerBuiltinSources();
30
+ const collector = new Collector(config, logger);
31
+ await collector.validateSources();
32
+ const enabledSources = collector.getEnabledSources();
33
+ if (enabledSources.length === 0) {
34
+ printError('No data sources enabled or validated.');
35
+ process.exit(1);
36
+ }
37
+ const spinner = ora();
38
+ if (source) {
39
+ // Collect specific source
40
+ if (!enabledSources.includes(source)) {
41
+ printError(`Source '${source}' is not enabled or not validated.`);
42
+ process.exit(1);
43
+ }
44
+ spinner.start(`Collecting from ${source}...`);
45
+ await collector.triggerCollection(source);
46
+ spinner.succeed(`Collection from ${source} complete`);
47
+ }
48
+ else {
49
+ // Collect all enabled sources
50
+ for (const src of enabledSources) {
51
+ spinner.start(`Collecting from ${src}...`);
52
+ await collector.triggerCollection(src);
53
+ spinner.succeed(`Collection from ${src} complete`);
54
+ }
55
+ }
56
+ printSuccess('All collections complete.');
57
+ }
58
+ catch (error) {
59
+ printError(`Collection failed: ${error instanceof Error ? error.message : String(error)}`);
60
+ process.exit(1);
61
+ }
62
+ });
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const configCommand: Command;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,aAAa,SAA4D,CAAC"}
@@ -0,0 +1,112 @@
1
+ import { Command } from 'commander';
2
+ import { execSync } from 'node:child_process';
3
+ import { existsSync } from 'node:fs';
4
+ import pc from 'picocolors';
5
+ import { loadConfig, findConfigPath } from '../../config/loader.js';
6
+ import { getUserConfigPath, getAllConfigPaths } from '../../config/paths.js';
7
+ import { printError, printSuccess, printInfo, printDim } from '../utils/output.js';
8
+ export const configCommand = new Command('config').description('Manage configuration');
9
+ function printConfigSummary(config, revealSecrets = false) {
10
+ console.log(pc.bold('\nAPI Configuration'));
11
+ console.log(` Base URL: ${config.api.baseUrl}`);
12
+ const apiKey = revealSecrets
13
+ ? config.api.apiKey
14
+ : config.api.apiKey.length > 12
15
+ ? config.api.apiKey.slice(0, 8) + '...' + config.api.apiKey.slice(-4)
16
+ : '***';
17
+ console.log(` API Key: ${apiKey}`);
18
+ console.log(` Timeout: ${config.api.timeout}ms`);
19
+ console.log(` Retries: ${config.api.retryAttempts}`);
20
+ console.log(pc.bold('\nData Sources'));
21
+ const sources = ['git', 'browser', 'filesystem', 'chatbot'];
22
+ for (const name of sources) {
23
+ const src = config.sources[name];
24
+ const status = src.enabled ? pc.green('enabled') : pc.dim('disabled');
25
+ const schedule = src.enabled ? ` (${src.schedule})` : '';
26
+ console.log(` ${name}: ${status}${schedule}`);
27
+ }
28
+ console.log(pc.bold('\nLogging'));
29
+ console.log(` Level: ${config.logging.level}`);
30
+ }
31
+ configCommand
32
+ .command('show')
33
+ .description('Display current configuration')
34
+ .option('--json', 'output as JSON')
35
+ .option('--reveal-secrets', 'show API keys (hidden by default)')
36
+ .action((options, cmd) => {
37
+ const globalOpts = cmd.optsWithGlobals();
38
+ try {
39
+ const config = loadConfig(globalOpts.config);
40
+ const configPath = findConfigPath(globalOpts.config);
41
+ if (configPath) {
42
+ printDim(`Loaded from: ${configPath}`);
43
+ }
44
+ // Helper to mask API key
45
+ const maskApiKey = (key) => key.length > 12 ? key.slice(0, 8) + '...' + key.slice(-4) : '***';
46
+ if (options.json) {
47
+ const output = {
48
+ ...config,
49
+ api: {
50
+ ...config.api,
51
+ apiKey: options.revealSecrets ? config.api.apiKey : maskApiKey(config.api.apiKey),
52
+ },
53
+ };
54
+ console.log(JSON.stringify(output, null, 2));
55
+ }
56
+ else {
57
+ printConfigSummary(config, options.revealSecrets);
58
+ }
59
+ }
60
+ catch (error) {
61
+ printError(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
62
+ process.exit(1);
63
+ }
64
+ });
65
+ configCommand
66
+ .command('path')
67
+ .description('Show configuration file paths')
68
+ .action(() => {
69
+ const paths = getAllConfigPaths();
70
+ console.log('Configuration file search order:');
71
+ paths.forEach((p, i) => {
72
+ const exists = existsSync(p);
73
+ const status = exists ? '(exists)' : '';
74
+ console.log(` ${i + 1}. ${p} ${status}`);
75
+ });
76
+ console.log(`\nPrimary user config: ${getUserConfigPath()}`);
77
+ });
78
+ configCommand
79
+ .command('edit')
80
+ .description('Open configuration in default editor')
81
+ .action((_options, cmd) => {
82
+ const globalOpts = cmd.optsWithGlobals();
83
+ const configPath = findConfigPath(globalOpts.config);
84
+ if (!configPath) {
85
+ printError('No config file found.');
86
+ printInfo("Run 'liferewind init' to create one.");
87
+ process.exit(1);
88
+ }
89
+ const editor = process.env['EDITOR'] || process.env['VISUAL'] || (process.platform === 'darwin' ? 'open' : 'vi');
90
+ try {
91
+ execSync(`${editor} "${configPath}"`, { stdio: 'inherit' });
92
+ }
93
+ catch (error) {
94
+ printError(`Failed to open editor: ${error instanceof Error ? error.message : String(error)}`);
95
+ process.exit(1);
96
+ }
97
+ });
98
+ configCommand
99
+ .command('validate')
100
+ .description('Validate configuration file')
101
+ .action((_options, cmd) => {
102
+ const globalOpts = cmd.optsWithGlobals();
103
+ try {
104
+ loadConfig(globalOpts.config);
105
+ const configPath = findConfigPath(globalOpts.config);
106
+ printSuccess(`Configuration is valid: ${configPath}`);
107
+ }
108
+ catch (error) {
109
+ printError(`Configuration invalid: ${error instanceof Error ? error.message : String(error)}`);
110
+ process.exit(1);
111
+ }
112
+ });
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const doctorCommand: Command;
3
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoHpC,eAAO,MAAM,aAAa,SAiDtB,CAAC"}