kontexted 0.1.12 → 0.1.13

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 (45) hide show
  1. package/README.md +175 -26
  2. package/dist/commands/sync/conflicts/list.d.ts +16 -0
  3. package/dist/commands/sync/conflicts/list.js +131 -0
  4. package/dist/commands/sync/conflicts/resolve.d.ts +17 -0
  5. package/dist/commands/sync/conflicts/resolve.js +186 -0
  6. package/dist/commands/sync/conflicts/show.d.ts +16 -0
  7. package/dist/commands/sync/conflicts/show.js +128 -0
  8. package/dist/commands/sync/conflicts.d.ts +11 -0
  9. package/dist/commands/sync/conflicts.js +20 -0
  10. package/dist/commands/sync/force-pull.d.ts +21 -0
  11. package/dist/commands/sync/force-pull.js +173 -0
  12. package/dist/commands/sync/force-push.d.ts +21 -0
  13. package/dist/commands/sync/force-push.js +269 -0
  14. package/dist/commands/sync/index.d.ts +10 -0
  15. package/dist/commands/sync/index.js +187 -0
  16. package/dist/commands/sync/init.d.ts +13 -0
  17. package/dist/commands/sync/init.js +261 -0
  18. package/dist/commands/sync/reset.d.ts +21 -0
  19. package/dist/commands/sync/reset.js +134 -0
  20. package/dist/commands/sync/start.d.ts +23 -0
  21. package/dist/commands/sync/start.js +232 -0
  22. package/dist/commands/sync/status.d.ts +19 -0
  23. package/dist/commands/sync/status.js +203 -0
  24. package/dist/commands/sync/stop.d.ts +14 -0
  25. package/dist/commands/sync/stop.js +153 -0
  26. package/dist/index.js +2 -0
  27. package/dist/lib/api-client.d.ts +13 -0
  28. package/dist/lib/api-client.js +28 -2
  29. package/dist/lib/sync/command-utils.d.ts +77 -0
  30. package/dist/lib/sync/command-utils.js +281 -0
  31. package/dist/lib/sync/crypto.d.ts +8 -0
  32. package/dist/lib/sync/crypto.js +11 -0
  33. package/dist/lib/sync/file-watcher.d.ts +30 -0
  34. package/dist/lib/sync/file-watcher.js +117 -0
  35. package/dist/lib/sync/queue.d.ts +44 -0
  36. package/dist/lib/sync/queue.js +87 -0
  37. package/dist/lib/sync/remote-listener.d.ts +52 -0
  38. package/dist/lib/sync/remote-listener.js +228 -0
  39. package/dist/lib/sync/sync-engine.d.ts +175 -0
  40. package/dist/lib/sync/sync-engine.js +995 -0
  41. package/dist/lib/sync/types.d.ts +351 -0
  42. package/dist/lib/sync/types.js +5 -0
  43. package/dist/lib/sync/utils.d.ts +51 -0
  44. package/dist/lib/sync/utils.js +126 -0
  45. package/package.json +7 -4
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Kontexted CLI
2
2
 
3
- A command-line tool for Kontexted that provides MCP proxy functionality and workspace management.
3
+ The official CLI for Kontexted—provides **Disk Sync**, MCP proxy, server management, and workspace operations for AI-assisted development.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,68 +8,217 @@ A command-line tool for Kontexted that provides MCP proxy functionality and work
8
8
  npm install -g kontexted
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## Overview
12
12
 
13
- ### Authentication
13
+ Kontexted provides three ways for AI assistants to access your notes:
14
14
 
15
- Log in to a Kontexted server:
15
+ | Method | Best For | Setup Difficulty |
16
+ |--------|----------|------------------|
17
+ | **Disk Sync** | Direct file access, all AI tools | ★☆☆ Easy |
18
+ | **MCP Server** | Claude Desktop, MCP-compatible tools | ★★☆ Medium |
19
+ | **CLI Skills** | Scripted operations, custom workflows | ★★★ Advanced |
16
20
 
17
- ```bash
18
- kontexted login --url https://app.example.com --workspace my-workspace --alias prod
19
- ```
21
+ ---
20
22
 
21
- With an alias and write permissions:
23
+ ## Disk Sync (Recommended)
24
+
25
+ Sync your Kontexted workspace to disk as markdown files. This is the **recommended method** for AI coding agents to access your notes.
26
+
27
+ ### Quick Start
22
28
 
23
29
  ```bash
24
- kontexted login --url https://app.example.com --workspace my-workspace --alias prod --write
30
+ # In your project directory
31
+ kontexted sync init --alias my-workspace --dir .
32
+ kontexted sync start --daemon
25
33
  ```
26
34
 
27
- ### MCP Proxy Mode
35
+ Your notes are now available at `.kontexted/folder/note.md`
36
+
37
+ ### How It Works
38
+
39
+ - Notes sync to `.kontexted/` as markdown files
40
+ - Real-time bidirectional sync with file watching
41
+ - Directory is gitignored; `.ignore` file allows AI tools to reference files
42
+ - Works with opencode, Claude Code, Cursor, Windsurf, and any AI that reads files
43
+
44
+ ### Commands
45
+
46
+ | Command | Description |
47
+ |---------|-------------|
48
+ | `sync init --alias <name> --dir .` | Initialize sync in current directory |
49
+ | `sync start --daemon` | Start background sync with file watching |
50
+ | `sync start --foreground` | Start sync in foreground |
51
+ | `sync stop` | Stop sync daemon |
52
+ | `sync status` | Check sync status |
53
+ | `sync force-pull` | Pull all notes from server |
54
+ | `sync force-push` | Push all local changes to server |
55
+ | `sync conflicts list` | List sync conflicts |
56
+ | `sync conflicts show <id>` | Show conflict details |
57
+ | `sync conflicts resolve <id> --strategy <local\|remote>` | Resolve conflict |
58
+ | `sync reset` | Reset sync state |
59
+
60
+ ---
61
+
62
+ ## MCP Proxy
28
63
 
29
- Start the MCP proxy server (for use with Claude Desktop or other MCP clients):
64
+ Start the MCP proxy server for Claude Desktop or other MCP clients:
30
65
 
31
66
  ```bash
32
- # Using alias
33
- kontexted --alias prod
67
+ # Read-only mode
68
+ kontexted mcp --alias <name>
34
69
 
35
- # Using workspace name
36
- kontexted --workspace my-workspace
70
+ # With write access
71
+ kontexted mcp --alias <name> --write
72
+ ```
73
+
74
+ ### Configuring Claude Desktop
37
75
 
38
- # Enable write mode for this session
39
- kontexted --alias prod --write
76
+ **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
40
77
 
41
- # Disable write mode for this session
42
- kontexted --alias prod --write-off
78
+ ```json
79
+ {
80
+ "mcpServers": {
81
+ "kontexted": {
82
+ "command": "kontexted",
83
+ "args": ["mcp", "--alias", "my-workspace", "--write"]
84
+ }
85
+ }
86
+ }
43
87
  ```
44
88
 
45
- ### Manage Profiles
89
+ ---
90
+
91
+ ## Server Management
46
92
 
47
- Show stored profiles:
93
+ Run a local Kontexted server:
48
94
 
49
95
  ```bash
50
- kontexted show-config
96
+ # Initialize local server (first time)
97
+ kontexted server init
98
+
99
+ # Start server in background
100
+ kontexted server start
101
+
102
+ # Start server in foreground
103
+ kontexted server start --foreground
104
+
105
+ # Stop server
106
+ kontexted server stop
107
+
108
+ # Check status
109
+ kontexted server status
110
+
111
+ # View logs
112
+ kontexted server logs -f
113
+
114
+ # Display signup invite code
115
+ kontexted server show-invite
116
+
117
+ # Diagnose issues
118
+ kontexted server doctor
51
119
  ```
52
120
 
53
- Remove a profile:
121
+ ### Default Configuration
122
+
123
+ | Setting | Default |
124
+ |---------|---------|
125
+ | Database | SQLite (`~/.kontexted/data/kontexted.db`) |
126
+ | Port | `4729` |
127
+ | Config | `~/.kontexted/config.json` |
128
+
129
+ ---
130
+
131
+ ## CLI Skills
132
+
133
+ Direct CLI access for workspace operations:
54
134
 
55
135
  ```bash
56
- kontexted logout --alias prod
136
+ # Query workspace tree
137
+ kontexted skill workspace-tree --alias <name>
138
+
139
+ # Search notes
140
+ kontexted skill search-notes --alias <name> --query "search text" --limit 10
141
+
142
+ # Get note by ID
143
+ kontexted skill note-by-id --alias <name> --note-id <id>
144
+
145
+ # Create folder (requires --write login)
146
+ kontexted skill create-folder --alias <name> --name <slug> --display-name "Name"
147
+
148
+ # Create note (requires --write login)
149
+ kontexted skill create-note --alias <name> --name <slug> --title "Title"
150
+
151
+ # Update note content (requires --write login)
152
+ kontexted skill update-note-content --alias <name> --note-id <id> --content "Content"
57
153
  ```
58
154
 
59
- Remove all profiles:
155
+ ---
156
+
157
+ ## Authentication
60
158
 
61
159
  ```bash
160
+ # Login to a server
161
+ kontexted login --url https://app.example.com --workspace my-workspace --alias prod
162
+
163
+ # Login with write permissions
164
+ kontexted login --url https://app.example.com --workspace my-workspace --alias prod --write
165
+
166
+ # Show stored profiles
167
+ kontexted show-config
168
+
169
+ # Remove a profile
170
+ kontexted logout --alias prod
171
+
172
+ # Remove all profiles
62
173
  kontexted logout
63
174
  ```
64
175
 
176
+ ---
177
+
178
+ ## Command Reference
179
+
180
+ | Category | Command | Description |
181
+ |----------|---------|-------------|
182
+ | **Sync** | `sync init` | Initialize disk sync |
183
+ | | `sync start` | Start sync daemon |
184
+ | | `sync stop` | Stop sync daemon |
185
+ | | `sync status` | Check status |
186
+ | | `sync force-pull` | Force pull from server |
187
+ | | `sync force-push` | Force push to server |
188
+ | | `sync conflicts list` | List conflicts |
189
+ | | `sync reset` | Reset sync state |
190
+ | **Server** | `server init` | Initialize local server |
191
+ | | `server start` | Start server |
192
+ | | `server stop` | Stop server |
193
+ | | `server status` | Check server status |
194
+ | | `server logs` | View logs |
195
+ | | `server show-invite` | Display invite code |
196
+ | | `server doctor` | Diagnose issues |
197
+ | **Auth** | `login` | Authenticate to server |
198
+ | | `logout` | Remove stored profile |
199
+ | | `show-config` | Display configuration |
200
+ | **MCP** | `mcp` | Start MCP proxy |
201
+ | **Skills** | `skill workspace-tree` | Get folder structure |
202
+ | | `skill search-notes` | Search notes |
203
+ | | `skill note-by-id` | Get note by ID |
204
+ | | `skill create-folder` | Create folder |
205
+ | | `skill create-note` | Create note |
206
+ | | `skill update-note-content` | Update note |
207
+
208
+ ---
209
+
65
210
  ## Configuration
66
211
 
67
- Profiles are stored in `~/.kontexted/profile.json` with OAuth tokens.
212
+ Profiles are stored in `~/.kontexted/config.json` with OAuth tokens.
213
+
214
+ ---
68
215
 
69
216
  ## Requirements
70
217
 
71
218
  - Node.js 18 or higher
72
219
 
220
+ ---
221
+
73
222
  ## License
74
223
 
75
224
  MIT
@@ -0,0 +1,16 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * Handler for the sync conflicts list command
4
+ */
5
+ export declare function handler(argv: {
6
+ json?: boolean;
7
+ dir?: string;
8
+ }): Promise<void>;
9
+ export declare const command = "list";
10
+ export declare const desc = "List unresolved conflicts";
11
+ export declare const builder: () => void;
12
+ export declare function handlerYargs(argv: any): Promise<void>;
13
+ /**
14
+ * Register the sync conflicts list command.
15
+ */
16
+ export declare function registerConflictsListCommand(conflictsCommand: Command): void;
@@ -0,0 +1,131 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ /**
4
+ * Default sync directory name
5
+ */
6
+ const DEFAULT_SYNC_DIR = ".kontexted";
7
+ /**
8
+ * Find the sync directory by looking for .kontexted/ or using --dir option
9
+ */
10
+ async function findSyncDir(cwd, dirArg) {
11
+ // If --dir was provided, use it
12
+ if (dirArg) {
13
+ const syncDir = path.resolve(cwd, dirArg);
14
+ try {
15
+ await fs.access(syncDir);
16
+ return syncDir;
17
+ }
18
+ catch {
19
+ console.error(`Error: Directory not found: ${syncDir}`);
20
+ process.exit(1);
21
+ }
22
+ }
23
+ // Otherwise, look for .kontexted/ in current directory
24
+ const defaultSyncDir = path.join(cwd, DEFAULT_SYNC_DIR);
25
+ try {
26
+ await fs.access(defaultSyncDir);
27
+ return defaultSyncDir;
28
+ }
29
+ catch {
30
+ console.error(`Error: Sync directory not found.`);
31
+ console.error(`Expected to find '${DEFAULT_SYNC_DIR}/' in current directory or specify --dir option.`);
32
+ console.error(`Run 'kontexted sync init' first to initialize sync.`);
33
+ process.exit(1);
34
+ }
35
+ }
36
+ /**
37
+ * Read and parse conflicts.log to get all conflict entries
38
+ */
39
+ async function getConflicts(syncDir) {
40
+ const logPath = path.join(syncDir, ".sync", "conflicts.log");
41
+ try {
42
+ const content = await fs.readFile(logPath, "utf-8");
43
+ const lines = content.trim().split("\n").filter(Boolean);
44
+ return lines.map((line) => JSON.parse(line));
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ }
50
+ /**
51
+ * Format timestamp for display
52
+ */
53
+ function formatTimestamp(isoString) {
54
+ const date = new Date(isoString);
55
+ return date.toISOString().replace("T", " ").slice(0, 19) + " UTC";
56
+ }
57
+ /**
58
+ * Display conflicts in human-readable format
59
+ */
60
+ function displayConflicts(conflicts) {
61
+ if (conflicts.length === 0) {
62
+ console.log("No conflicts found.");
63
+ return;
64
+ }
65
+ console.log("Conflicts:");
66
+ for (let i = 0; i < conflicts.length; i++) {
67
+ const conflict = conflicts[i];
68
+ const winnerText = conflict.winner === "local" ? "local wins" : "remote wins";
69
+ const timestamp = formatTimestamp(conflict.timestamp);
70
+ console.log(` ${i + 1}. ${conflict.filePath} (${winnerText}, ${timestamp})`);
71
+ }
72
+ }
73
+ /**
74
+ * Display conflicts in JSON format
75
+ */
76
+ function displayConflictsJson(conflicts) {
77
+ const output = conflicts.map((conflict, index) => ({
78
+ id: index + 1,
79
+ filePath: conflict.filePath,
80
+ winner: conflict.winner,
81
+ timestamp: conflict.timestamp,
82
+ localMtime: conflict.localMtime,
83
+ remoteMtime: conflict.remoteMtime,
84
+ }));
85
+ console.log(JSON.stringify(output, null, 2));
86
+ }
87
+ /**
88
+ * Handler for the sync conflicts list command
89
+ */
90
+ export async function handler(argv) {
91
+ const cwd = process.cwd();
92
+ // Find the sync directory
93
+ const syncDir = await findSyncDir(cwd, argv.dir);
94
+ // Get conflicts
95
+ const conflicts = await getConflicts(syncDir);
96
+ // Display output
97
+ if (argv.json) {
98
+ displayConflictsJson(conflicts);
99
+ }
100
+ else {
101
+ displayConflicts(conflicts);
102
+ }
103
+ }
104
+ // ============ Yargs Command Module ============
105
+ export const command = "list";
106
+ export const desc = "List unresolved conflicts";
107
+ export const builder = () => { };
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ export async function handlerYargs(argv) {
110
+ await handler({
111
+ json: argv.json,
112
+ dir: argv.dir,
113
+ });
114
+ }
115
+ // ============ Register with Commander ============
116
+ /**
117
+ * Register the sync conflicts list command.
118
+ */
119
+ export function registerConflictsListCommand(conflictsCommand) {
120
+ conflictsCommand
121
+ .command("list")
122
+ .description(desc)
123
+ .option("--dir <directory>", "Sync directory")
124
+ .option("--json", "Output as JSON")
125
+ .action(async (opts) => {
126
+ await handlerYargs({
127
+ json: opts.json,
128
+ dir: opts.dir,
129
+ });
130
+ });
131
+ }
@@ -0,0 +1,17 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * Handler for the sync conflicts resolve command
4
+ */
5
+ export declare function handler(argv: {
6
+ id: string;
7
+ keep: "local" | "remote";
8
+ dir?: string;
9
+ }): Promise<void>;
10
+ export declare const command = "resolve";
11
+ export declare const desc = "Manually resolve conflict";
12
+ export declare const builder: () => void;
13
+ export declare function handlerYargs(argv: any): Promise<void>;
14
+ /**
15
+ * Register the sync conflicts resolve command.
16
+ */
17
+ export declare function registerConflictsResolveCommand(conflictsCommand: Command): void;
@@ -0,0 +1,186 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ /**
4
+ * Default sync directory name
5
+ */
6
+ const DEFAULT_SYNC_DIR = ".kontexted";
7
+ /**
8
+ * Find the sync directory by looking for .kontexted/ or using --dir option
9
+ */
10
+ async function findSyncDir(cwd, dirArg) {
11
+ // If --dir was provided, use it
12
+ if (dirArg) {
13
+ const syncDir = path.resolve(cwd, dirArg);
14
+ try {
15
+ await fs.access(syncDir);
16
+ return syncDir;
17
+ }
18
+ catch {
19
+ console.error(`Error: Directory not found: ${syncDir}`);
20
+ process.exit(1);
21
+ }
22
+ }
23
+ // Otherwise, look for .kontexted/ in current directory
24
+ const defaultSyncDir = path.join(cwd, DEFAULT_SYNC_DIR);
25
+ try {
26
+ await fs.access(defaultSyncDir);
27
+ return defaultSyncDir;
28
+ }
29
+ catch {
30
+ console.error(`Error: Sync directory not found.`);
31
+ console.error(`Expected to find '${DEFAULT_SYNC_DIR}/' in current directory or specify --dir option.`);
32
+ console.error(`Run 'kontexted sync init' first to initialize sync.`);
33
+ process.exit(1);
34
+ }
35
+ }
36
+ /**
37
+ * Read and parse conflicts.log to get all conflict entries
38
+ */
39
+ async function getConflicts(syncDir) {
40
+ const logPath = path.join(syncDir, ".sync", "conflicts.log");
41
+ try {
42
+ const content = await fs.readFile(logPath, "utf-8");
43
+ const lines = content.trim().split("\n").filter(Boolean);
44
+ return lines.map((line) => JSON.parse(line));
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ }
50
+ /**
51
+ * Write conflicts back to the log file
52
+ */
53
+ async function writeConflicts(syncDir, conflicts) {
54
+ const logPath = path.join(syncDir, ".sync", "conflicts.log");
55
+ const content = conflicts
56
+ .map((entry) => JSON.stringify(entry))
57
+ .join("\n");
58
+ await fs.writeFile(logPath, content, "utf-8");
59
+ }
60
+ /**
61
+ * Load sync state from .sync/state.json
62
+ */
63
+ async function loadSyncState(syncDir) {
64
+ const statePath = path.join(syncDir, ".sync", "state.json");
65
+ try {
66
+ const stateRaw = await fs.readFile(statePath, "utf-8");
67
+ return JSON.parse(stateRaw);
68
+ }
69
+ catch (error) {
70
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
71
+ return null;
72
+ }
73
+ throw error;
74
+ }
75
+ }
76
+ /**
77
+ * Save sync state to .sync/state.json
78
+ */
79
+ async function saveSyncState(syncDir, state) {
80
+ const statePath = path.join(syncDir, ".sync", "state.json");
81
+ await fs.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
82
+ }
83
+ /**
84
+ * Handler for the sync conflicts resolve command
85
+ */
86
+ export async function handler(argv) {
87
+ const cwd = process.cwd();
88
+ // Find the sync directory
89
+ const syncDir = await findSyncDir(cwd, argv.dir);
90
+ // Parse the conflict ID (1-based index)
91
+ const conflictId = parseInt(argv.id, 10);
92
+ if (isNaN(conflictId) || conflictId < 1) {
93
+ console.error(`Error: Invalid conflict ID: ${argv.id}`);
94
+ process.exit(1);
95
+ }
96
+ // Get conflicts
97
+ const conflicts = await getConflicts(syncDir);
98
+ // Find the conflict by ID (convert to 0-based index)
99
+ const conflictIndex = conflictId - 1;
100
+ if (conflictIndex < 0 || conflictIndex >= conflicts.length) {
101
+ console.error(`Error: Conflict not found: ${argv.id}`);
102
+ console.error(`Total conflicts: ${conflicts.length}`);
103
+ process.exit(1);
104
+ }
105
+ const conflict = conflicts[conflictIndex];
106
+ // Resolve the conflict
107
+ const localFilePath = path.join(syncDir, conflict.filePath);
108
+ if (argv.keep === "local") {
109
+ // Keep local version - already in place, just remove conflict entry
110
+ console.log(`Resolved conflict #${conflictId}: keeping local version`);
111
+ console.log(` File: ${conflict.filePath}`);
112
+ }
113
+ else {
114
+ // Keep remote version - copy loser (shadow) to main file
115
+ const loserPath = path.join(syncDir, conflict.loserPath);
116
+ const loserContent = await fs.readFile(loserPath, "utf-8");
117
+ await fs.writeFile(localFilePath, loserContent, "utf-8");
118
+ console.log(`Resolved conflict #${conflictId}: keeping remote version`);
119
+ console.log(` File: ${conflict.filePath}`);
120
+ console.log(` Copied from: ${conflict.loserPath}`);
121
+ }
122
+ // Update sync state - mark file as synced with the new hash
123
+ let state = await loadSyncState(syncDir);
124
+ if (state) {
125
+ // Get the new content hash
126
+ const newContent = await fs.readFile(localFilePath, "utf-8");
127
+ const newHash = await hashContent(newContent);
128
+ // Update the file state
129
+ if (state.files[conflict.filePath]) {
130
+ state.files[conflict.filePath].localHash = newHash;
131
+ state.files[conflict.filePath].remoteHash = newHash;
132
+ await saveSyncState(syncDir, state);
133
+ console.log(` Updated sync state for ${conflict.filePath}`);
134
+ }
135
+ }
136
+ // Remove the conflict from the log
137
+ conflicts.splice(conflictIndex, 1);
138
+ await writeConflicts(syncDir, conflicts);
139
+ console.log(` Conflict removed from conflicts.log`);
140
+ }
141
+ /**
142
+ * Simple hash function for content (SHA-256 via crypto)
143
+ */
144
+ async function hashContent(content) {
145
+ const crypto = await import("node:crypto");
146
+ return crypto.createHash("sha256").update(content).digest("hex");
147
+ }
148
+ // ============ Yargs Command Module ============
149
+ export const command = "resolve";
150
+ export const desc = "Manually resolve conflict";
151
+ export const builder = () => { };
152
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
153
+ export async function handlerYargs(argv) {
154
+ await handler({
155
+ id: argv.id,
156
+ keep: argv.keep,
157
+ dir: argv.dir,
158
+ });
159
+ }
160
+ // ============ Register with Commander ============
161
+ /**
162
+ * Register the sync conflicts resolve command.
163
+ */
164
+ export function registerConflictsResolveCommand(conflictsCommand) {
165
+ conflictsCommand
166
+ .command("resolve <id>")
167
+ .description(desc)
168
+ .requiredOption("--keep <local|remote>", "Which version to keep: 'local' or 'remote'")
169
+ .option("--dir <directory>", "Sync directory")
170
+ .action(async (id, opts) => {
171
+ if (!opts.keep) {
172
+ console.error("Error: --keep option is required");
173
+ console.error("Usage: kontexted sync conflicts resolve <id> --keep <local|remote>");
174
+ process.exit(1);
175
+ }
176
+ if (opts.keep !== "local" && opts.keep !== "remote") {
177
+ console.error("Error: --keep must be 'local' or 'remote'");
178
+ process.exit(1);
179
+ }
180
+ await handlerYargs({
181
+ id,
182
+ keep: opts.keep,
183
+ dir: opts.dir,
184
+ });
185
+ });
186
+ }
@@ -0,0 +1,16 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * Handler for the sync conflicts show command
4
+ */
5
+ export declare function handler(argv: {
6
+ id: string;
7
+ dir?: string;
8
+ }): Promise<void>;
9
+ export declare const command = "show";
10
+ export declare const desc = "Show details of a conflict";
11
+ export declare const builder: () => void;
12
+ export declare function handlerYargs(argv: any): Promise<void>;
13
+ /**
14
+ * Register the sync conflicts show command.
15
+ */
16
+ export declare function registerConflictsShowCommand(conflictsCommand: Command): void;