ctxpkg 0.0.1

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 (61) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +282 -0
  3. package/bin/cli.js +8 -0
  4. package/bin/daemon.js +7 -0
  5. package/package.json +70 -0
  6. package/src/agent/AGENTS.md +249 -0
  7. package/src/agent/agent.prompts.ts +66 -0
  8. package/src/agent/agent.test-runner.schemas.ts +158 -0
  9. package/src/agent/agent.test-runner.ts +436 -0
  10. package/src/agent/agent.ts +371 -0
  11. package/src/agent/agent.types.ts +94 -0
  12. package/src/backend/AGENTS.md +112 -0
  13. package/src/backend/backend.protocol.ts +95 -0
  14. package/src/backend/backend.schemas.ts +123 -0
  15. package/src/backend/backend.services.ts +151 -0
  16. package/src/backend/backend.ts +111 -0
  17. package/src/backend/backend.types.ts +34 -0
  18. package/src/cli/AGENTS.md +213 -0
  19. package/src/cli/cli.agent.ts +197 -0
  20. package/src/cli/cli.chat.ts +369 -0
  21. package/src/cli/cli.client.ts +55 -0
  22. package/src/cli/cli.collections.ts +491 -0
  23. package/src/cli/cli.config.ts +252 -0
  24. package/src/cli/cli.daemon.ts +160 -0
  25. package/src/cli/cli.documents.ts +413 -0
  26. package/src/cli/cli.mcp.ts +177 -0
  27. package/src/cli/cli.ts +28 -0
  28. package/src/cli/cli.utils.ts +122 -0
  29. package/src/client/AGENTS.md +135 -0
  30. package/src/client/client.adapters.ts +279 -0
  31. package/src/client/client.ts +86 -0
  32. package/src/client/client.types.ts +17 -0
  33. package/src/collections/AGENTS.md +185 -0
  34. package/src/collections/collections.schemas.ts +195 -0
  35. package/src/collections/collections.ts +1160 -0
  36. package/src/config/config.ts +118 -0
  37. package/src/daemon/AGENTS.md +168 -0
  38. package/src/daemon/daemon.config.ts +23 -0
  39. package/src/daemon/daemon.manager.ts +215 -0
  40. package/src/daemon/daemon.schemas.ts +22 -0
  41. package/src/daemon/daemon.ts +205 -0
  42. package/src/database/AGENTS.md +211 -0
  43. package/src/database/database.ts +64 -0
  44. package/src/database/migrations/migrations.001-init.ts +56 -0
  45. package/src/database/migrations/migrations.002-fts5.ts +32 -0
  46. package/src/database/migrations/migrations.ts +20 -0
  47. package/src/database/migrations/migrations.types.ts +9 -0
  48. package/src/documents/AGENTS.md +301 -0
  49. package/src/documents/documents.schemas.ts +190 -0
  50. package/src/documents/documents.ts +734 -0
  51. package/src/embedder/embedder.ts +53 -0
  52. package/src/exports.ts +0 -0
  53. package/src/mcp/AGENTS.md +264 -0
  54. package/src/mcp/mcp.ts +105 -0
  55. package/src/tools/AGENTS.md +228 -0
  56. package/src/tools/agent/agent.ts +45 -0
  57. package/src/tools/documents/documents.ts +401 -0
  58. package/src/tools/tools.langchain.ts +37 -0
  59. package/src/tools/tools.mcp.ts +46 -0
  60. package/src/tools/tools.types.ts +35 -0
  61. package/src/utils/utils.services.ts +46 -0
@@ -0,0 +1,252 @@
1
+ import type { Command } from 'commander';
2
+ import { input, confirm, select } from '@inquirer/prompts';
3
+
4
+ import {
5
+ formatHeader,
6
+ formatSuccess,
7
+ formatInfo,
8
+ formatWarning,
9
+ formatTableHeader,
10
+ formatTableRow,
11
+ flattenObject,
12
+ withErrorHandling,
13
+ chalk,
14
+ } from './cli.utils.ts';
15
+
16
+ import { config, configPath, saveConfig } from '#root/config/config.ts';
17
+
18
+ /**
19
+ * Parse a string value to the appropriate type
20
+ */
21
+ const parseValue = (key: string, value: string): unknown => {
22
+ if (value === 'true') return true;
23
+ if (value === 'false') return false;
24
+
25
+ // Don't convert API keys to numbers
26
+ if (!key.toLowerCase().includes('key') && !isNaN(Number(value)) && value.trim() !== '') {
27
+ return Number(value);
28
+ }
29
+
30
+ return value;
31
+ };
32
+
33
+ /**
34
+ * Get the raw value from config, handling sensitive values
35
+ */
36
+ const getRawValue = (key: string): unknown => {
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ const value = (config as any).get(key);
39
+
40
+ if (value === '[Sensitive]') {
41
+ // Try to get the raw value for display
42
+ const parts = key.split('.');
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ let current: Record<string, unknown> = (config as any)._properties;
45
+
46
+ for (const part of parts) {
47
+ if (current && typeof current === 'object' && part in current) {
48
+ current = current[part] as Record<string, unknown>;
49
+ } else {
50
+ return value;
51
+ }
52
+ }
53
+
54
+ return current;
55
+ }
56
+
57
+ return value;
58
+ };
59
+
60
+ const createConfigCli = (command: Command) => {
61
+ command.description('Manage configuration settings');
62
+
63
+ // Set a configuration value
64
+ command
65
+ .command('set')
66
+ .argument('<key>', 'Configuration key (e.g., "openai.apiKey")')
67
+ .argument('<value>', 'Value to set')
68
+ .description('Set a configuration value')
69
+ .action(
70
+ withErrorHandling(async (key: string, value: string) => {
71
+ const parsedValue = parseValue(key, value);
72
+
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ (config as any).set(key, parsedValue);
75
+ saveConfig();
76
+
77
+ formatSuccess(`Configuration updated`);
78
+ console.log(chalk.dim(' Key: ') + chalk.cyan(key));
79
+ console.log(chalk.dim(' Value: ') + chalk.yellow(String(parsedValue)));
80
+ }),
81
+ );
82
+
83
+ // Get a configuration value
84
+ command
85
+ .command('get')
86
+ .argument('<key>', 'Configuration key to retrieve')
87
+ .description('Get a configuration value')
88
+ .action(
89
+ withErrorHandling(async (key: string) => {
90
+ const value = getRawValue(key);
91
+
92
+ if (value === undefined) {
93
+ formatWarning(`Key "${key}" is not set`);
94
+ return;
95
+ }
96
+
97
+ console.log(chalk.cyan(String(value)));
98
+ }),
99
+ );
100
+
101
+ // Show config file path
102
+ command
103
+ .command('path')
104
+ .description('Show configuration file path')
105
+ .action(() => {
106
+ formatInfo(`Configuration file: ${chalk.cyan(configPath)}`);
107
+ });
108
+
109
+ // List all configuration values
110
+ command
111
+ .command('list')
112
+ .alias('ls')
113
+ .description('List all configuration values')
114
+ .option('-a, --all', 'Show all values including sensitive ones')
115
+ .action(
116
+ withErrorHandling(async (options: { all?: boolean }) => {
117
+ const props = config.getProperties();
118
+ const flat = flattenObject(props as Record<string, unknown>);
119
+ const keys = Object.keys(flat).sort();
120
+
121
+ if (keys.length === 0) {
122
+ formatInfo('No configuration values set.');
123
+ return;
124
+ }
125
+
126
+ formatHeader('Configuration');
127
+
128
+ const maxKeyLen = Math.max(...keys.map((k) => k.length), 3);
129
+
130
+ formatTableHeader([
131
+ { name: 'Key', width: maxKeyLen },
132
+ { name: 'Value', width: 40 },
133
+ ]);
134
+
135
+ for (const key of keys) {
136
+ let value = flat[key];
137
+ let valueColor = chalk.white;
138
+
139
+ // Handle sensitive values
140
+ if (value === '[Sensitive]') {
141
+ if (options.all) {
142
+ value = getRawValue(key);
143
+ valueColor = chalk.yellow;
144
+ } else {
145
+ valueColor = chalk.dim;
146
+ }
147
+ }
148
+
149
+ formatTableRow([
150
+ { value: key, width: maxKeyLen, color: chalk.white },
151
+ { value: String(value), width: 40, color: valueColor },
152
+ ]);
153
+ }
154
+
155
+ console.log();
156
+
157
+ if (!options.all) {
158
+ formatInfo('Use --all to show sensitive values');
159
+ }
160
+ }),
161
+ );
162
+
163
+ // Interactive set command
164
+ command
165
+ .command('edit')
166
+ .argument('[key]', 'Configuration key to edit')
167
+ .description('Interactively edit a configuration value')
168
+ .action(
169
+ withErrorHandling(async (key?: string) => {
170
+ const props = config.getProperties();
171
+ const flat = flattenObject(props as Record<string, unknown>);
172
+ const keys = Object.keys(flat).sort();
173
+
174
+ let selectedKey = key;
175
+
176
+ if (!selectedKey) {
177
+ selectedKey = await select({
178
+ message: 'Select a configuration key to edit:',
179
+ choices: keys.map((k) => ({
180
+ name: `${k} = ${flat[k]}`,
181
+ value: k,
182
+ })),
183
+ });
184
+ }
185
+
186
+ if (!keys.includes(selectedKey)) {
187
+ const createNew = await confirm({
188
+ message: `Key "${selectedKey}" doesn't exist. Create it?`,
189
+ default: false,
190
+ });
191
+
192
+ if (!createNew) {
193
+ formatInfo('Operation cancelled.');
194
+ return;
195
+ }
196
+ }
197
+
198
+ const currentValue = flat[selectedKey];
199
+ const newValue = await input({
200
+ message: `Enter new value for "${selectedKey}":`,
201
+ default: currentValue !== '[Sensitive]' ? String(currentValue ?? '') : '',
202
+ });
203
+
204
+ const parsedValue = parseValue(selectedKey, newValue);
205
+
206
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
207
+ (config as any).set(selectedKey, parsedValue);
208
+ saveConfig();
209
+
210
+ formatSuccess(`Configuration updated`);
211
+ console.log(chalk.dim(' Key: ') + chalk.cyan(selectedKey));
212
+ console.log(chalk.dim(' Value: ') + chalk.yellow(String(parsedValue)));
213
+ }),
214
+ );
215
+
216
+ // Reset/delete a configuration value
217
+ command
218
+ .command('reset')
219
+ .argument('<key>', 'Configuration key to reset')
220
+ .description('Reset a configuration value to its default')
221
+ .option('-f, --force', 'Skip confirmation prompt')
222
+ .action(
223
+ withErrorHandling(async (key: string, options: { force?: boolean }) => {
224
+ const currentValue = getRawValue(key);
225
+
226
+ if (currentValue === undefined) {
227
+ formatWarning(`Key "${key}" is not set`);
228
+ return;
229
+ }
230
+
231
+ if (!options.force) {
232
+ const confirmed = await confirm({
233
+ message: chalk.yellow(`Reset "${key}" to default value?`),
234
+ default: false,
235
+ });
236
+
237
+ if (!confirmed) {
238
+ formatInfo('Operation cancelled.');
239
+ return;
240
+ }
241
+ }
242
+
243
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
244
+ (config as any).reset(key);
245
+ saveConfig();
246
+
247
+ formatSuccess(`Configuration key "${key}" reset to default`);
248
+ }),
249
+ );
250
+ };
251
+
252
+ export { createConfigCli };
@@ -0,0 +1,160 @@
1
+ import type { Command } from 'commander';
2
+
3
+ import {
4
+ formatHeader,
5
+ formatSuccess,
6
+ formatInfo,
7
+ formatTableHeader,
8
+ formatTableRow,
9
+ withErrorHandling,
10
+ chalk,
11
+ } from './cli.utils.ts';
12
+
13
+ import { DaemonManager } from '#root/daemon/daemon.manager.ts';
14
+
15
+ const createDaemonCli = (command: Command) => {
16
+ command.description('Manage the background daemon');
17
+
18
+ // Start command
19
+ command
20
+ .command('start')
21
+ .description('Start the daemon')
22
+ .action(
23
+ withErrorHandling(async () => {
24
+ const manager = new DaemonManager();
25
+
26
+ if (await manager.isRunning()) {
27
+ formatInfo('Daemon is already running.');
28
+ return;
29
+ }
30
+
31
+ formatInfo('Starting daemon...');
32
+ await manager.start();
33
+ formatSuccess('Daemon started.');
34
+
35
+ const status = await manager.getStatus();
36
+ if (status) {
37
+ formatInfo(`Socket: ${chalk.cyan(status.socketPath)}`);
38
+ formatInfo(`PID: ${chalk.cyan(status.pid)}`);
39
+ }
40
+ }),
41
+ );
42
+
43
+ // Stop command
44
+ command
45
+ .command('stop')
46
+ .description('Stop the daemon')
47
+ .action(
48
+ withErrorHandling(async () => {
49
+ const manager = new DaemonManager();
50
+
51
+ if (!(await manager.isRunning())) {
52
+ formatInfo('Daemon is not running.');
53
+ return;
54
+ }
55
+
56
+ formatInfo('Stopping daemon...');
57
+ await manager.stop();
58
+ formatSuccess('Daemon stopped.');
59
+ }),
60
+ );
61
+
62
+ // Status command
63
+ command
64
+ .command('status')
65
+ .description('Show daemon status')
66
+ .action(
67
+ withErrorHandling(async () => {
68
+ const manager = new DaemonManager();
69
+ const status = await manager.getStatus();
70
+
71
+ formatHeader('Daemon Status');
72
+
73
+ if (!status) {
74
+ formatInfo('Daemon is not running.');
75
+ return;
76
+ }
77
+
78
+ const uptime = formatUptime(status.uptime);
79
+
80
+ formatTableHeader([
81
+ { name: 'Property', width: 15 },
82
+ { name: 'Value', width: 40 },
83
+ ]);
84
+
85
+ formatTableRow([
86
+ { value: 'Status', width: 15, color: chalk.white },
87
+ { value: 'Running', width: 40, color: chalk.green },
88
+ ]);
89
+
90
+ formatTableRow([
91
+ { value: 'PID', width: 15, color: chalk.white },
92
+ { value: String(status.pid), width: 40, color: chalk.cyan },
93
+ ]);
94
+
95
+ formatTableRow([
96
+ { value: 'Socket', width: 15, color: chalk.white },
97
+ { value: status.socketPath, width: 40, color: chalk.cyan },
98
+ ]);
99
+
100
+ formatTableRow([
101
+ { value: 'Uptime', width: 15, color: chalk.white },
102
+ { value: uptime, width: 40, color: chalk.yellow },
103
+ ]);
104
+
105
+ formatTableRow([
106
+ { value: 'Connections', width: 15, color: chalk.white },
107
+ { value: String(status.connections), width: 40, color: chalk.magenta },
108
+ ]);
109
+
110
+ console.log();
111
+ }),
112
+ );
113
+
114
+ // Restart command
115
+ command
116
+ .command('restart')
117
+ .description('Restart the daemon')
118
+ .action(
119
+ withErrorHandling(async () => {
120
+ const manager = new DaemonManager();
121
+
122
+ if (await manager.isRunning()) {
123
+ formatInfo('Stopping daemon...');
124
+ await manager.stop();
125
+ // Wait a bit for cleanup
126
+ await new Promise((resolve) => setTimeout(resolve, 500));
127
+ }
128
+
129
+ formatInfo('Starting daemon...');
130
+ await manager.start();
131
+ formatSuccess('Daemon restarted.');
132
+
133
+ const status = await manager.getStatus();
134
+ if (status) {
135
+ formatInfo(`Socket: ${chalk.cyan(status.socketPath)}`);
136
+ formatInfo(`PID: ${chalk.cyan(status.pid)}`);
137
+ }
138
+ }),
139
+ );
140
+ };
141
+
142
+ const formatUptime = (ms: number): string => {
143
+ const seconds = Math.floor(ms / 1000);
144
+ const minutes = Math.floor(seconds / 60);
145
+ const hours = Math.floor(minutes / 60);
146
+ const days = Math.floor(hours / 24);
147
+
148
+ if (days > 0) {
149
+ return `${days}d ${hours % 24}h ${minutes % 60}m`;
150
+ }
151
+ if (hours > 0) {
152
+ return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
153
+ }
154
+ if (minutes > 0) {
155
+ return `${minutes}m ${seconds % 60}s`;
156
+ }
157
+ return `${seconds}s`;
158
+ };
159
+
160
+ export { createDaemonCli };