ctx-sync 1.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.
- package/dist/commands/audit.d.ts +76 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +367 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/config.d.ts +58 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +114 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/dir.d.ts +56 -0
- package/dist/commands/dir.d.ts.map +1 -0
- package/dist/commands/dir.js +172 -0
- package/dist/commands/dir.js.map +1 -0
- package/dist/commands/docker.d.ts +140 -0
- package/dist/commands/docker.d.ts.map +1 -0
- package/dist/commands/docker.js +380 -0
- package/dist/commands/docker.js.map +1 -0
- package/dist/commands/env.d.ts +96 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +352 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/init.d.ts +89 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +272 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/key.d.ts +92 -0
- package/dist/commands/key.d.ts.map +1 -0
- package/dist/commands/key.js +274 -0
- package/dist/commands/key.js.map +1 -0
- package/dist/commands/list.d.ts +38 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +84 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/note.d.ts +151 -0
- package/dist/commands/note.d.ts.map +1 -0
- package/dist/commands/note.js +411 -0
- package/dist/commands/note.js.map +1 -0
- package/dist/commands/pull.d.ts +47 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +94 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +40 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +94 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/restore.d.ts +116 -0
- package/dist/commands/restore.d.ts.map +1 -0
- package/dist/commands/restore.js +336 -0
- package/dist/commands/restore.js.map +1 -0
- package/dist/commands/service.d.ts +83 -0
- package/dist/commands/service.d.ts.map +1 -0
- package/dist/commands/service.js +259 -0
- package/dist/commands/service.js.map +1 -0
- package/dist/commands/show.d.ts +63 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +243 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/status.d.ts +53 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +150 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +105 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +243 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/team.d.ts +79 -0
- package/dist/commands/team.d.ts.map +1 -0
- package/dist/commands/team.js +233 -0
- package/dist/commands/team.js.map +1 -0
- package/dist/commands/track.d.ts +109 -0
- package/dist/commands/track.d.ts.map +1 -0
- package/dist/commands/track.js +406 -0
- package/dist/commands/track.js.map +1 -0
- package/dist/core/command-validator.d.ts +100 -0
- package/dist/core/command-validator.d.ts.map +1 -0
- package/dist/core/command-validator.js +299 -0
- package/dist/core/command-validator.js.map +1 -0
- package/dist/core/config-store.d.ts +76 -0
- package/dist/core/config-store.d.ts.map +1 -0
- package/dist/core/config-store.js +148 -0
- package/dist/core/config-store.js.map +1 -0
- package/dist/core/directories-handler.d.ts +116 -0
- package/dist/core/directories-handler.d.ts.map +1 -0
- package/dist/core/directories-handler.js +199 -0
- package/dist/core/directories-handler.js.map +1 -0
- package/dist/core/docker-handler.d.ts +183 -0
- package/dist/core/docker-handler.d.ts.map +1 -0
- package/dist/core/docker-handler.js +515 -0
- package/dist/core/docker-handler.js.map +1 -0
- package/dist/core/encryption.d.ts +79 -0
- package/dist/core/encryption.d.ts.map +1 -0
- package/dist/core/encryption.js +111 -0
- package/dist/core/encryption.js.map +1 -0
- package/dist/core/env-handler.d.ts +128 -0
- package/dist/core/env-handler.d.ts.map +1 -0
- package/dist/core/env-handler.js +272 -0
- package/dist/core/env-handler.js.map +1 -0
- package/dist/core/git-sync.d.ts +88 -0
- package/dist/core/git-sync.d.ts.map +1 -0
- package/dist/core/git-sync.js +143 -0
- package/dist/core/git-sync.js.map +1 -0
- package/dist/core/key-store.d.ts +51 -0
- package/dist/core/key-store.d.ts.map +1 -0
- package/dist/core/key-store.js +108 -0
- package/dist/core/key-store.js.map +1 -0
- package/dist/core/log-sanitizer.d.ts +72 -0
- package/dist/core/log-sanitizer.d.ts.map +1 -0
- package/dist/core/log-sanitizer.js +202 -0
- package/dist/core/log-sanitizer.js.map +1 -0
- package/dist/core/path-validator.d.ts +37 -0
- package/dist/core/path-validator.d.ts.map +1 -0
- package/dist/core/path-validator.js +127 -0
- package/dist/core/path-validator.js.map +1 -0
- package/dist/core/recipients.d.ts +99 -0
- package/dist/core/recipients.d.ts.map +1 -0
- package/dist/core/recipients.js +206 -0
- package/dist/core/recipients.js.map +1 -0
- package/dist/core/services-handler.d.ts +113 -0
- package/dist/core/services-handler.d.ts.map +1 -0
- package/dist/core/services-handler.js +176 -0
- package/dist/core/services-handler.js.map +1 -0
- package/dist/core/state-manager.d.ts +96 -0
- package/dist/core/state-manager.d.ts.map +1 -0
- package/dist/core/state-manager.js +165 -0
- package/dist/core/state-manager.js.map +1 -0
- package/dist/core/transport.d.ts +28 -0
- package/dist/core/transport.d.ts.map +1 -0
- package/dist/core/transport.js +79 -0
- package/dist/core/transport.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/errors.d.ts +81 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +191 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/secure-memory.d.ts +65 -0
- package/dist/utils/secure-memory.d.ts.map +1 -0
- package/dist/utils/secure-memory.js +86 -0
- package/dist/utils/secure-memory.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctx-sync note <project>` command.
|
|
3
|
+
*
|
|
4
|
+
* Records and updates mental context for a project — the "23-minute
|
|
5
|
+
* problem" solution. Prompts the user for current task, blockers,
|
|
6
|
+
* next steps, related links, and breadcrumbs, then encrypts and
|
|
7
|
+
* stores them in `mental-context.age`.
|
|
8
|
+
*
|
|
9
|
+
* Supports both interactive (prompt-based) and non-interactive
|
|
10
|
+
* (flag-based) input modes. When updating, existing context is
|
|
11
|
+
* merged — not overwritten — so incremental notes accumulate.
|
|
12
|
+
*
|
|
13
|
+
* @module commands/note
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from 'node:fs';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
import { withErrorHandler } from '../utils/errors.js';
|
|
18
|
+
import { STATE_FILES } from '@ctx-sync/shared';
|
|
19
|
+
import { identityToRecipient } from 'age-encryption';
|
|
20
|
+
import { loadKey } from '../core/key-store.js';
|
|
21
|
+
import { readState, writeState } from '../core/state-manager.js';
|
|
22
|
+
import { commitState } from '../core/git-sync.js';
|
|
23
|
+
import { getConfigDir, getSyncDir } from './init.js';
|
|
24
|
+
/**
|
|
25
|
+
* Parse a file reference string into structured location data.
|
|
26
|
+
*
|
|
27
|
+
* Accepts formats:
|
|
28
|
+
* - `file.ts` → { file: 'file.ts', line: 0 }
|
|
29
|
+
* - `file.ts:45` → { file: 'file.ts', line: 45 }
|
|
30
|
+
* - `file.ts:45:12` → { file: 'file.ts', line: 45, column: 12 }
|
|
31
|
+
*
|
|
32
|
+
* @param fileRef - The file reference string.
|
|
33
|
+
* @returns Parsed location, or null if the string is empty.
|
|
34
|
+
*/
|
|
35
|
+
export function parseFileReference(fileRef) {
|
|
36
|
+
const trimmed = fileRef.trim();
|
|
37
|
+
if (!trimmed) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
// Match file:line:col or file:line or just file
|
|
41
|
+
const match = /^(.+?)(?::(\d+))?(?::(\d+))?$/.exec(trimmed);
|
|
42
|
+
if (!match?.[1]) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const file = match[1];
|
|
46
|
+
const line = match[2] ? parseInt(match[2], 10) : 0;
|
|
47
|
+
const column = match[3] ? parseInt(match[3], 10) : undefined;
|
|
48
|
+
return { file, line, column };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parse a link string into title and URL.
|
|
52
|
+
*
|
|
53
|
+
* Accepts formats:
|
|
54
|
+
* - `https://example.com` → { title: 'https://example.com', url: 'https://example.com' }
|
|
55
|
+
* - `Title: https://example.com` → { title: 'Title', url: 'https://example.com' }
|
|
56
|
+
* - `Title - https://example.com` → { title: 'Title', url: 'https://example.com' }
|
|
57
|
+
*
|
|
58
|
+
* @param linkStr - The link string.
|
|
59
|
+
* @returns Parsed link with title and URL.
|
|
60
|
+
*/
|
|
61
|
+
export function parseLink(linkStr) {
|
|
62
|
+
const trimmed = linkStr.trim();
|
|
63
|
+
// Try "Title: URL" or "Title - URL" format
|
|
64
|
+
const separatorMatch = /^(.+?)\s*[-:]\s*(https?:\/\/.+)$/i.exec(trimmed);
|
|
65
|
+
if (separatorMatch?.[1] && separatorMatch[2]) {
|
|
66
|
+
return { title: separatorMatch[1].trim(), url: separatorMatch[2].trim() };
|
|
67
|
+
}
|
|
68
|
+
// Just a URL — use it as both title and URL
|
|
69
|
+
return { title: trimmed, url: trimmed };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create a default empty mental context structure.
|
|
73
|
+
*
|
|
74
|
+
* @returns A blank ProjectMentalContext.
|
|
75
|
+
*/
|
|
76
|
+
export function createEmptyContext() {
|
|
77
|
+
return {
|
|
78
|
+
currentTask: '',
|
|
79
|
+
blockers: [],
|
|
80
|
+
nextSteps: [],
|
|
81
|
+
relatedLinks: [],
|
|
82
|
+
breadcrumbs: [],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Merge new input into an existing mental context.
|
|
87
|
+
*
|
|
88
|
+
* Follows merge-not-overwrite semantics:
|
|
89
|
+
* - `currentTask` is replaced if new input provides one.
|
|
90
|
+
* - `lastWorkingOn` is replaced if new input provides one.
|
|
91
|
+
* - Blockers, next steps, links, and breadcrumbs are appended.
|
|
92
|
+
* - Duplicate blockers (by description) are skipped.
|
|
93
|
+
* - Duplicate links (by URL) are skipped.
|
|
94
|
+
*
|
|
95
|
+
* @param existing - The existing mental context (or null for new).
|
|
96
|
+
* @param input - The new input to merge.
|
|
97
|
+
* @returns The merged context.
|
|
98
|
+
*/
|
|
99
|
+
export function mergeContext(existing, input) {
|
|
100
|
+
const base = existing ?? createEmptyContext();
|
|
101
|
+
const now = new Date().toISOString();
|
|
102
|
+
const merged = {
|
|
103
|
+
currentTask: input.currentTask?.trim() || base.currentTask,
|
|
104
|
+
lastWorkingOn: base.lastWorkingOn,
|
|
105
|
+
blockers: [...base.blockers],
|
|
106
|
+
nextSteps: [...base.nextSteps],
|
|
107
|
+
relatedLinks: [...base.relatedLinks],
|
|
108
|
+
breadcrumbs: [...base.breadcrumbs],
|
|
109
|
+
};
|
|
110
|
+
// Update lastWorkingOn if provided
|
|
111
|
+
if (input.lastWorkingOn) {
|
|
112
|
+
merged.lastWorkingOn = {
|
|
113
|
+
file: input.lastWorkingOn.file,
|
|
114
|
+
line: input.lastWorkingOn.line,
|
|
115
|
+
column: input.lastWorkingOn.column,
|
|
116
|
+
description: input.lastWorkingOn.description || '',
|
|
117
|
+
timestamp: now,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// Append new blockers (skip duplicates by description)
|
|
121
|
+
if (input.blockers) {
|
|
122
|
+
const existingDescriptions = new Set(merged.blockers.map((b) => b.description.toLowerCase()));
|
|
123
|
+
for (const desc of input.blockers) {
|
|
124
|
+
const trimmed = desc.trim();
|
|
125
|
+
if (trimmed && !existingDescriptions.has(trimmed.toLowerCase())) {
|
|
126
|
+
merged.blockers.push({
|
|
127
|
+
description: trimmed,
|
|
128
|
+
addedAt: now,
|
|
129
|
+
priority: 'medium',
|
|
130
|
+
});
|
|
131
|
+
existingDescriptions.add(trimmed.toLowerCase());
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Append new next steps (skip exact duplicates)
|
|
136
|
+
if (input.nextSteps) {
|
|
137
|
+
const existingSteps = new Set(merged.nextSteps.map((s) => s.toLowerCase()));
|
|
138
|
+
for (const step of input.nextSteps) {
|
|
139
|
+
const trimmed = step.trim();
|
|
140
|
+
if (trimmed && !existingSteps.has(trimmed.toLowerCase())) {
|
|
141
|
+
merged.nextSteps.push(trimmed);
|
|
142
|
+
existingSteps.add(trimmed.toLowerCase());
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Append new related links (skip duplicates by URL)
|
|
147
|
+
if (input.relatedLinks) {
|
|
148
|
+
const existingUrls = new Set(merged.relatedLinks.map((l) => l.url));
|
|
149
|
+
for (const link of input.relatedLinks) {
|
|
150
|
+
if (link.url.trim() && !existingUrls.has(link.url.trim())) {
|
|
151
|
+
merged.relatedLinks.push({
|
|
152
|
+
title: link.title.trim(),
|
|
153
|
+
url: link.url.trim(),
|
|
154
|
+
});
|
|
155
|
+
existingUrls.add(link.url.trim());
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Append breadcrumb if provided
|
|
160
|
+
if (input.breadcrumb?.trim()) {
|
|
161
|
+
merged.breadcrumbs.push({
|
|
162
|
+
note: input.breadcrumb.trim(),
|
|
163
|
+
timestamp: now,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return merged;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Collect note input from interactive prompts.
|
|
170
|
+
*
|
|
171
|
+
* Shows the user their existing mental context (if any) and prompts
|
|
172
|
+
* for updates. Empty responses keep the existing values.
|
|
173
|
+
*
|
|
174
|
+
* @param existing - The existing mental context (or null).
|
|
175
|
+
* @returns The collected input.
|
|
176
|
+
*/
|
|
177
|
+
export async function collectNoteInput(existing) {
|
|
178
|
+
const Enquirer = (await import('enquirer')).default;
|
|
179
|
+
const enquirer = new Enquirer();
|
|
180
|
+
const input = {};
|
|
181
|
+
// Show existing context summary if available
|
|
182
|
+
if (existing?.currentTask) {
|
|
183
|
+
const chalk = (await import('chalk')).default;
|
|
184
|
+
console.log(chalk.dim(`\nCurrent task: "${existing.currentTask}"`));
|
|
185
|
+
if (existing.blockers.length > 0) {
|
|
186
|
+
console.log(chalk.dim(`Blockers: ${existing.blockers.map((b) => b.description).join(', ')}`));
|
|
187
|
+
}
|
|
188
|
+
if (existing.nextSteps.length > 0) {
|
|
189
|
+
console.log(chalk.dim(`Next steps: ${existing.nextSteps.join(', ')}`));
|
|
190
|
+
}
|
|
191
|
+
console.log('');
|
|
192
|
+
}
|
|
193
|
+
// Current task
|
|
194
|
+
const taskResponse = await enquirer.prompt({
|
|
195
|
+
type: 'input',
|
|
196
|
+
name: 'currentTask',
|
|
197
|
+
message: 'Current task:',
|
|
198
|
+
initial: existing?.currentTask ?? '',
|
|
199
|
+
});
|
|
200
|
+
if (taskResponse.currentTask?.trim()) {
|
|
201
|
+
input.currentTask = taskResponse.currentTask;
|
|
202
|
+
}
|
|
203
|
+
// Blockers
|
|
204
|
+
const blockerResponse = await enquirer.prompt({
|
|
205
|
+
type: 'input',
|
|
206
|
+
name: 'blockers',
|
|
207
|
+
message: 'Blockers (comma-separated, or press Enter to skip):',
|
|
208
|
+
});
|
|
209
|
+
if (blockerResponse.blockers?.trim()) {
|
|
210
|
+
input.blockers = blockerResponse.blockers
|
|
211
|
+
.split(',')
|
|
212
|
+
.map((b) => b.trim())
|
|
213
|
+
.filter(Boolean);
|
|
214
|
+
}
|
|
215
|
+
// Next steps
|
|
216
|
+
const stepsResponse = await enquirer.prompt({
|
|
217
|
+
type: 'input',
|
|
218
|
+
name: 'nextSteps',
|
|
219
|
+
message: 'Next steps (comma-separated, or press Enter to skip):',
|
|
220
|
+
});
|
|
221
|
+
if (stepsResponse.nextSteps?.trim()) {
|
|
222
|
+
input.nextSteps = stepsResponse.nextSteps
|
|
223
|
+
.split(',')
|
|
224
|
+
.map((s) => s.trim())
|
|
225
|
+
.filter(Boolean);
|
|
226
|
+
}
|
|
227
|
+
// Related links
|
|
228
|
+
const linksResponse = await enquirer.prompt({
|
|
229
|
+
type: 'input',
|
|
230
|
+
name: 'links',
|
|
231
|
+
message: 'Related links (comma-separated, or press Enter to skip):',
|
|
232
|
+
});
|
|
233
|
+
if (linksResponse.links?.trim()) {
|
|
234
|
+
input.relatedLinks = linksResponse.links
|
|
235
|
+
.split(',')
|
|
236
|
+
.map((l) => parseLink(l.trim()))
|
|
237
|
+
.filter((l) => l.url);
|
|
238
|
+
}
|
|
239
|
+
// Breadcrumb
|
|
240
|
+
const breadcrumbResponse = await enquirer.prompt({
|
|
241
|
+
type: 'input',
|
|
242
|
+
name: 'breadcrumb',
|
|
243
|
+
message: 'Breadcrumb note (or press Enter to skip):',
|
|
244
|
+
});
|
|
245
|
+
if (breadcrumbResponse.breadcrumb?.trim()) {
|
|
246
|
+
input.breadcrumb = breadcrumbResponse.breadcrumb;
|
|
247
|
+
}
|
|
248
|
+
return input;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Build NoteInput from CLI flags (non-interactive mode).
|
|
252
|
+
*
|
|
253
|
+
* @param options - The command-line options.
|
|
254
|
+
* @returns The constructed note input.
|
|
255
|
+
*/
|
|
256
|
+
export function buildInputFromFlags(options) {
|
|
257
|
+
const input = {};
|
|
258
|
+
if (options.task) {
|
|
259
|
+
input.currentTask = options.task;
|
|
260
|
+
}
|
|
261
|
+
if (options.blockers && options.blockers.length > 0) {
|
|
262
|
+
input.blockers = options.blockers;
|
|
263
|
+
}
|
|
264
|
+
if (options.nextSteps && options.nextSteps.length > 0) {
|
|
265
|
+
input.nextSteps = options.nextSteps;
|
|
266
|
+
}
|
|
267
|
+
if (options.links && options.links.length > 0) {
|
|
268
|
+
input.relatedLinks = options.links.map((l) => parseLink(l));
|
|
269
|
+
}
|
|
270
|
+
if (options.breadcrumb) {
|
|
271
|
+
input.breadcrumb = options.breadcrumb;
|
|
272
|
+
}
|
|
273
|
+
if (options.file) {
|
|
274
|
+
const parsed = parseFileReference(options.file);
|
|
275
|
+
if (parsed) {
|
|
276
|
+
input.lastWorkingOn = {
|
|
277
|
+
file: parsed.file,
|
|
278
|
+
line: parsed.line,
|
|
279
|
+
column: parsed.column,
|
|
280
|
+
description: options.fileDescription ?? '',
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return input;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Execute the note command logic.
|
|
288
|
+
*
|
|
289
|
+
* 1. Load key and decrypt existing state to find the project.
|
|
290
|
+
* 2. Load existing mental context (if any).
|
|
291
|
+
* 3. Collect new input (interactive prompts or flags).
|
|
292
|
+
* 4. Merge new input into existing context.
|
|
293
|
+
* 5. Encrypt and write `mental-context.age`.
|
|
294
|
+
* 6. Optionally commit to the sync repo.
|
|
295
|
+
*
|
|
296
|
+
* @param projectName - The name of the project.
|
|
297
|
+
* @param options - Note command options.
|
|
298
|
+
* @returns The note result.
|
|
299
|
+
*/
|
|
300
|
+
export async function executeNote(projectName, options = {}) {
|
|
301
|
+
const configDir = getConfigDir();
|
|
302
|
+
const syncDir = getSyncDir();
|
|
303
|
+
// Verify sync dir exists
|
|
304
|
+
if (!fs.existsSync(syncDir) || !fs.existsSync(path.join(syncDir, '.git'))) {
|
|
305
|
+
throw new Error('No sync repository found. Run `ctx-sync init` first.');
|
|
306
|
+
}
|
|
307
|
+
// Load key
|
|
308
|
+
const privateKey = loadKey(configDir);
|
|
309
|
+
const publicKey = await identityToRecipient(privateKey);
|
|
310
|
+
// 1. Verify the project exists in state
|
|
311
|
+
const state = await readState(syncDir, privateKey, 'state');
|
|
312
|
+
if (!state) {
|
|
313
|
+
throw new Error('No state file found. Track a project first with `ctx-sync track`.');
|
|
314
|
+
}
|
|
315
|
+
const project = state.projects.find((p) => p.name === projectName || p.id === projectName);
|
|
316
|
+
if (!project) {
|
|
317
|
+
const availableNames = state.projects.map((p) => p.name).join(', ');
|
|
318
|
+
throw new Error(`Project "${projectName}" not found.\n` +
|
|
319
|
+
(availableNames
|
|
320
|
+
? `Available projects: ${availableNames}`
|
|
321
|
+
: 'No projects tracked yet. Run `ctx-sync track` first.'));
|
|
322
|
+
}
|
|
323
|
+
// 2. Load existing mental context
|
|
324
|
+
const mentalContextData = await readState(syncDir, privateKey, 'mental-context');
|
|
325
|
+
const existingContext = mentalContextData?.[project.name] ?? null;
|
|
326
|
+
const isNew = existingContext === null;
|
|
327
|
+
// 3. Collect input
|
|
328
|
+
let noteInput;
|
|
329
|
+
if (options.promptFn) {
|
|
330
|
+
// Testing override
|
|
331
|
+
noteInput = await options.promptFn(existingContext);
|
|
332
|
+
}
|
|
333
|
+
else if (options.noInteractive) {
|
|
334
|
+
// Non-interactive: use flags only
|
|
335
|
+
noteInput = buildInputFromFlags(options);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
// Interactive: prompt user
|
|
339
|
+
noteInput = await collectNoteInput(existingContext);
|
|
340
|
+
}
|
|
341
|
+
// 4. Merge input into existing context
|
|
342
|
+
const mergedContext = mergeContext(existingContext, noteInput);
|
|
343
|
+
// 5. Write encrypted mental-context.age
|
|
344
|
+
const allMentalContext = mentalContextData ?? {};
|
|
345
|
+
allMentalContext[project.name] = mergedContext;
|
|
346
|
+
await writeState(syncDir, allMentalContext, publicKey, 'mental-context');
|
|
347
|
+
// 6. Optionally commit
|
|
348
|
+
if (!options.noSync) {
|
|
349
|
+
await commitState(syncDir, [STATE_FILES.MENTAL_CONTEXT, STATE_FILES.MANIFEST], `feat: update mental context for ${project.name}`);
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
projectName: project.name,
|
|
353
|
+
isNew,
|
|
354
|
+
context: mergedContext,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Register the `note` command on the given Commander program.
|
|
359
|
+
*/
|
|
360
|
+
export function registerNoteCommand(program) {
|
|
361
|
+
program
|
|
362
|
+
.command('note <project>')
|
|
363
|
+
.description('Record mental context for a project (tasks, blockers, next steps)')
|
|
364
|
+
.option('-t, --task <task>', 'Current task description')
|
|
365
|
+
.option('-b, --blocker <blocker...>', 'Add blocker(s)')
|
|
366
|
+
.option('-s, --next-step <step...>', 'Add next step(s)')
|
|
367
|
+
.option('-l, --link <link...>', 'Add related link(s)')
|
|
368
|
+
.option('-c, --breadcrumb <note>', 'Add a breadcrumb note')
|
|
369
|
+
.option('-f, --file <file>', 'File being worked on (file:line:col)')
|
|
370
|
+
.option('--file-description <desc>', 'Description of file work')
|
|
371
|
+
.option('--no-interactive', 'Use flags only, skip prompts')
|
|
372
|
+
.option('--no-sync', 'Skip syncing to Git')
|
|
373
|
+
.action(withErrorHandler(async (projectName, opts) => {
|
|
374
|
+
const options = {
|
|
375
|
+
task: opts['task'],
|
|
376
|
+
blockers: opts['blocker'],
|
|
377
|
+
nextSteps: opts['nextStep'],
|
|
378
|
+
links: opts['link'],
|
|
379
|
+
breadcrumb: opts['breadcrumb'],
|
|
380
|
+
file: opts['file'],
|
|
381
|
+
fileDescription: opts['fileDescription'],
|
|
382
|
+
noInteractive: opts['interactive'] === false,
|
|
383
|
+
noSync: opts['sync'] === false,
|
|
384
|
+
};
|
|
385
|
+
const chalk = (await import('chalk')).default;
|
|
386
|
+
const result = await executeNote(projectName, options);
|
|
387
|
+
if (result.isNew) {
|
|
388
|
+
console.log(chalk.green(`✅ Mental context created for: ${result.projectName}`));
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
console.log(chalk.green(`✅ Mental context updated for: ${result.projectName}`));
|
|
392
|
+
}
|
|
393
|
+
if (result.context.currentTask) {
|
|
394
|
+
console.log(` Task: ${result.context.currentTask}`);
|
|
395
|
+
}
|
|
396
|
+
if (result.context.blockers.length > 0) {
|
|
397
|
+
console.log(` Blockers: ${result.context.blockers.length}`);
|
|
398
|
+
}
|
|
399
|
+
if (result.context.nextSteps.length > 0) {
|
|
400
|
+
console.log(` Next steps: ${result.context.nextSteps.length}`);
|
|
401
|
+
}
|
|
402
|
+
if (result.context.relatedLinks.length > 0) {
|
|
403
|
+
console.log(` Links: ${result.context.relatedLinks.length}`);
|
|
404
|
+
}
|
|
405
|
+
if (result.context.breadcrumbs.length > 0) {
|
|
406
|
+
console.log(` Breadcrumbs: ${result.context.breadcrumbs.length}`);
|
|
407
|
+
}
|
|
408
|
+
console.log(chalk.dim('\n Context encrypted and saved to mental-context.age'));
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
411
|
+
//# sourceMappingURL=note.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"note.js","sourceRoot":"","sources":["../../src/commands/note.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctx-sync pull` command.
|
|
3
|
+
*
|
|
4
|
+
* Pulls the latest encrypted state from the remote. Does NOT commit or
|
|
5
|
+
* push — this is a one-way pull operation.
|
|
6
|
+
*
|
|
7
|
+
* Validates remote URL (transport security) before every pull.
|
|
8
|
+
* Handles merge conflicts on .age files (never auto-merges).
|
|
9
|
+
*
|
|
10
|
+
* @module commands/pull
|
|
11
|
+
*/
|
|
12
|
+
import type { Command } from 'commander';
|
|
13
|
+
/** Options for the pull command */
|
|
14
|
+
export interface PullOptions {
|
|
15
|
+
/** Non-interactive mode — keep local version on conflict */
|
|
16
|
+
noInteractive?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Result of a pull operation */
|
|
19
|
+
export interface PullResult {
|
|
20
|
+
/** Whether a pull was performed */
|
|
21
|
+
pulled: boolean;
|
|
22
|
+
/** Whether there were merge conflicts */
|
|
23
|
+
hadConflicts: boolean;
|
|
24
|
+
/** Files that had merge conflicts */
|
|
25
|
+
conflictFiles: string[];
|
|
26
|
+
/** Number of state files available after pull */
|
|
27
|
+
stateFileCount: number;
|
|
28
|
+
/** Whether the repo has a remote configured */
|
|
29
|
+
hasRemote: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Execute the pull command logic.
|
|
33
|
+
*
|
|
34
|
+
* 1. Validate remote URL.
|
|
35
|
+
* 2. Pull latest from remote (with conflict detection).
|
|
36
|
+
* 3. Handle merge conflicts (keep local in non-interactive mode).
|
|
37
|
+
* 4. Report available state files.
|
|
38
|
+
*
|
|
39
|
+
* @param options - Pull command options.
|
|
40
|
+
* @returns Pull result with operation details.
|
|
41
|
+
*/
|
|
42
|
+
export declare function executePull(options?: PullOptions): Promise<PullResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Register the `pull` command on the given Commander program.
|
|
45
|
+
*/
|
|
46
|
+
export declare function registerPullCommand(program: Command): void;
|
|
47
|
+
//# sourceMappingURL=pull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,mCAAmC;AACnC,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,iCAAiC;AACjC,MAAM,WAAW,UAAU;IACzB,mCAAmC;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,YAAY,EAAE,OAAO,CAAC;IACtB,qCAAqC;IACrC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CA+ChF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqC1D"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctx-sync pull` command.
|
|
3
|
+
*
|
|
4
|
+
* Pulls the latest encrypted state from the remote. Does NOT commit or
|
|
5
|
+
* push — this is a one-way pull operation.
|
|
6
|
+
*
|
|
7
|
+
* Validates remote URL (transport security) before every pull.
|
|
8
|
+
* Handles merge conflicts on .age files (never auto-merges).
|
|
9
|
+
*
|
|
10
|
+
* @module commands/pull
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import { withErrorHandler } from '../utils/errors.js';
|
|
15
|
+
import { listStateFiles } from '../core/state-manager.js';
|
|
16
|
+
import { validateSyncRemote, pullWithConflictDetection, resolveConflicts, } from './sync.js';
|
|
17
|
+
import { getSyncDir } from './init.js';
|
|
18
|
+
/**
|
|
19
|
+
* Execute the pull command logic.
|
|
20
|
+
*
|
|
21
|
+
* 1. Validate remote URL.
|
|
22
|
+
* 2. Pull latest from remote (with conflict detection).
|
|
23
|
+
* 3. Handle merge conflicts (keep local in non-interactive mode).
|
|
24
|
+
* 4. Report available state files.
|
|
25
|
+
*
|
|
26
|
+
* @param options - Pull command options.
|
|
27
|
+
* @returns Pull result with operation details.
|
|
28
|
+
*/
|
|
29
|
+
export async function executePull(options = {}) {
|
|
30
|
+
const syncDir = getSyncDir();
|
|
31
|
+
// Verify sync dir exists
|
|
32
|
+
if (!fs.existsSync(syncDir) || !fs.existsSync(path.join(syncDir, '.git'))) {
|
|
33
|
+
throw new Error('No sync repository found. Run `ctx-sync init` first.');
|
|
34
|
+
}
|
|
35
|
+
const result = {
|
|
36
|
+
pulled: false,
|
|
37
|
+
hadConflicts: false,
|
|
38
|
+
conflictFiles: [],
|
|
39
|
+
stateFileCount: 0,
|
|
40
|
+
hasRemote: false,
|
|
41
|
+
};
|
|
42
|
+
// 1. Validate remote
|
|
43
|
+
const remoteUrl = await validateSyncRemote(syncDir);
|
|
44
|
+
result.hasRemote = remoteUrl !== null;
|
|
45
|
+
if (!result.hasRemote) {
|
|
46
|
+
throw new Error('No remote configured. Nothing to pull.\n' +
|
|
47
|
+
'Add a remote with: ctx-sync init --remote <url>');
|
|
48
|
+
}
|
|
49
|
+
// 2. Pull latest with conflict detection
|
|
50
|
+
const pullResult = await pullWithConflictDetection(syncDir);
|
|
51
|
+
result.pulled = pullResult.pulled;
|
|
52
|
+
// 3. Handle conflicts
|
|
53
|
+
if (pullResult.conflictFiles.length > 0) {
|
|
54
|
+
result.hadConflicts = true;
|
|
55
|
+
result.conflictFiles = pullResult.conflictFiles;
|
|
56
|
+
// In non-interactive mode, keep local version (safest default)
|
|
57
|
+
const useLocal = options.noInteractive !== false;
|
|
58
|
+
await resolveConflicts(syncDir, pullResult.conflictFiles, useLocal);
|
|
59
|
+
}
|
|
60
|
+
// 4. Count available state files
|
|
61
|
+
result.stateFileCount = listStateFiles(syncDir).length;
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Register the `pull` command on the given Commander program.
|
|
66
|
+
*/
|
|
67
|
+
export function registerPullCommand(program) {
|
|
68
|
+
program
|
|
69
|
+
.command('pull')
|
|
70
|
+
.description('Pull latest encrypted state from remote')
|
|
71
|
+
.option('--no-interactive', 'Non-interactive mode (keep local on conflict)')
|
|
72
|
+
.action(withErrorHandler(async (opts) => {
|
|
73
|
+
const options = {
|
|
74
|
+
noInteractive: opts['interactive'] === false,
|
|
75
|
+
};
|
|
76
|
+
const chalk = (await import('chalk')).default;
|
|
77
|
+
const { default: ora } = await import('ora');
|
|
78
|
+
const spinner = ora('Pulling from remote...').start();
|
|
79
|
+
const result = await executePull(options);
|
|
80
|
+
spinner.stop();
|
|
81
|
+
if (result.hadConflicts) {
|
|
82
|
+
console.log(chalk.yellow(`⚠ Merge conflicts resolved on ${result.conflictFiles.length} file(s):`));
|
|
83
|
+
for (const file of result.conflictFiles) {
|
|
84
|
+
console.log(chalk.yellow(` - ${file}`));
|
|
85
|
+
}
|
|
86
|
+
console.log(chalk.dim(' Local version kept (encrypted files cannot be merged).'));
|
|
87
|
+
}
|
|
88
|
+
if (result.pulled) {
|
|
89
|
+
console.log(chalk.green('✅ Pulled latest from remote'));
|
|
90
|
+
}
|
|
91
|
+
console.log(chalk.dim(` ${result.stateFileCount} encrypted state file(s) available`));
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctx-sync push` command.
|
|
3
|
+
*
|
|
4
|
+
* Commits all encrypted state files (.age + manifest.json) and pushes to
|
|
5
|
+
* the remote. Does NOT pull first — this is a one-way push operation.
|
|
6
|
+
*
|
|
7
|
+
* Validates remote URL (transport security) before every push.
|
|
8
|
+
*
|
|
9
|
+
* @module commands/push
|
|
10
|
+
*/
|
|
11
|
+
import type { Command } from 'commander';
|
|
12
|
+
/** Result of a push operation */
|
|
13
|
+
export interface PushResult {
|
|
14
|
+
/** Whether a commit was created */
|
|
15
|
+
committed: boolean;
|
|
16
|
+
/** Whether a push was performed */
|
|
17
|
+
pushed: boolean;
|
|
18
|
+
/** Commit hash, if a commit was created */
|
|
19
|
+
commitHash: string | null;
|
|
20
|
+
/** Number of files committed */
|
|
21
|
+
fileCount: number;
|
|
22
|
+
/** Whether the repo has a remote configured */
|
|
23
|
+
hasRemote: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Execute the push command logic.
|
|
27
|
+
*
|
|
28
|
+
* 1. Validate remote URL.
|
|
29
|
+
* 2. Update manifest timestamp.
|
|
30
|
+
* 3. Commit all .age files + manifest.json.
|
|
31
|
+
* 4. Push to remote.
|
|
32
|
+
*
|
|
33
|
+
* @returns Push result with operation details.
|
|
34
|
+
*/
|
|
35
|
+
export declare function executePush(): Promise<PushResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Register the `push` command on the given Commander program.
|
|
38
|
+
*/
|
|
39
|
+
export declare function registerPushCommand(program: Command): void;
|
|
40
|
+
//# sourceMappingURL=push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,iCAAiC;AACjC,MAAM,WAAW,UAAU;IACzB,mCAAmC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,mCAAmC;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,UAAU,CAAC,CAgDvD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4B1D"}
|