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,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctx-sync team` command group.
|
|
3
|
+
*
|
|
4
|
+
* Manages team members (multi-recipient encryption):
|
|
5
|
+
* - `team add --name <n> --key <pubkey>` — add a team member with fingerprint verification.
|
|
6
|
+
* - `team remove <name>` — remove a team member and re-encrypt all state.
|
|
7
|
+
* - `team list` — list all team members and their public keys.
|
|
8
|
+
* - `team revoke <pubkey>` — immediately revoke a key and re-encrypt all state.
|
|
9
|
+
*
|
|
10
|
+
* **Security:**
|
|
11
|
+
* - Adding a member prompts for out-of-band fingerprint verification.
|
|
12
|
+
* - Removing/revoking a member triggers full re-encryption of all state files
|
|
13
|
+
* so the revoked key can no longer decrypt current or future data.
|
|
14
|
+
* - Recipients config is stored locally and NEVER synced to Git.
|
|
15
|
+
*
|
|
16
|
+
* @module commands/team
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from 'node:fs';
|
|
19
|
+
import * as path from 'node:path';
|
|
20
|
+
import { withErrorHandler } from '../utils/errors.js';
|
|
21
|
+
import { identityToRecipient } from 'age-encryption';
|
|
22
|
+
import { decryptState, encryptStateForRecipients } from '../core/encryption.js';
|
|
23
|
+
import { loadKey, } from '../core/key-store.js';
|
|
24
|
+
import { listStateFiles, readManifest, writeManifest, } from '../core/state-manager.js';
|
|
25
|
+
import { addRecipient, removeRecipientByName, removeRecipientByKey, getRecipients, initRecipients, getAllRecipientKeys, computeFingerprint, } from '../core/recipients.js';
|
|
26
|
+
import { getConfigDir, getSyncDir } from './init.js';
|
|
27
|
+
// ─── Core Logic ───────────────────────────────────────────────────────────
|
|
28
|
+
/**
|
|
29
|
+
* Ensure the recipients config is initialised.
|
|
30
|
+
*
|
|
31
|
+
* If no recipients file exists, creates one with the owner's public key.
|
|
32
|
+
*/
|
|
33
|
+
async function ensureRecipientsInit() {
|
|
34
|
+
const configDir = getConfigDir();
|
|
35
|
+
const config = getRecipients(configDir);
|
|
36
|
+
if (!config) {
|
|
37
|
+
const privateKey = loadKey(configDir);
|
|
38
|
+
const publicKey = await identityToRecipient(privateKey);
|
|
39
|
+
initRecipients(configDir, publicKey);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Re-encrypt all state files for the current set of recipients.
|
|
44
|
+
*
|
|
45
|
+
* Used after removing/revoking a team member to ensure they can no
|
|
46
|
+
* longer decrypt any state files.
|
|
47
|
+
*
|
|
48
|
+
* @returns Array of filenames that were re-encrypted.
|
|
49
|
+
*/
|
|
50
|
+
async function reEncryptAllState() {
|
|
51
|
+
const configDir = getConfigDir();
|
|
52
|
+
const syncDir = getSyncDir();
|
|
53
|
+
const privateKey = loadKey(configDir);
|
|
54
|
+
const ownerPublicKey = await identityToRecipient(privateKey);
|
|
55
|
+
const allKeys = getAllRecipientKeys(configDir, ownerPublicKey);
|
|
56
|
+
const ageFiles = listStateFiles(syncDir);
|
|
57
|
+
const filesReEncrypted = [];
|
|
58
|
+
for (const filename of ageFiles) {
|
|
59
|
+
const filePath = path.join(syncDir, filename);
|
|
60
|
+
const ciphertext = fs.readFileSync(filePath, 'utf-8');
|
|
61
|
+
if (!ciphertext.trim()) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Decrypt with owner's key, re-encrypt for all current recipients
|
|
65
|
+
const plainData = await decryptState(ciphertext, privateKey);
|
|
66
|
+
const newCiphertext = await encryptStateForRecipients(plainData, allKeys);
|
|
67
|
+
fs.writeFileSync(filePath, newCiphertext, 'utf-8');
|
|
68
|
+
filesReEncrypted.push(filename);
|
|
69
|
+
}
|
|
70
|
+
// Update manifest
|
|
71
|
+
const manifest = readManifest(syncDir);
|
|
72
|
+
if (manifest) {
|
|
73
|
+
manifest.lastSync = new Date().toISOString();
|
|
74
|
+
writeManifest(syncDir, manifest);
|
|
75
|
+
}
|
|
76
|
+
return filesReEncrypted;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Execute `ctx-sync team add`.
|
|
80
|
+
*
|
|
81
|
+
* Adds a new team member to the recipients list and re-encrypts
|
|
82
|
+
* all state files so the new member can decrypt them.
|
|
83
|
+
*/
|
|
84
|
+
export async function executeTeamAdd(options) {
|
|
85
|
+
await ensureRecipientsInit();
|
|
86
|
+
const configDir = getConfigDir();
|
|
87
|
+
const member = addRecipient(configDir, options.name, options.key);
|
|
88
|
+
// Re-encrypt all state for all recipients (including the new member)
|
|
89
|
+
await reEncryptAllState();
|
|
90
|
+
return {
|
|
91
|
+
name: member.name,
|
|
92
|
+
publicKey: member.publicKey,
|
|
93
|
+
fingerprint: member.fingerprint,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Execute `ctx-sync team remove`.
|
|
98
|
+
*
|
|
99
|
+
* Removes a team member by name and re-encrypts all state files
|
|
100
|
+
* so the removed member can no longer decrypt them.
|
|
101
|
+
*/
|
|
102
|
+
export async function executeTeamRemove(name) {
|
|
103
|
+
await ensureRecipientsInit();
|
|
104
|
+
const configDir = getConfigDir();
|
|
105
|
+
const removed = removeRecipientByName(configDir, name);
|
|
106
|
+
// Re-encrypt ALL state without the removed member
|
|
107
|
+
const filesReEncrypted = await reEncryptAllState();
|
|
108
|
+
return {
|
|
109
|
+
name: removed.name,
|
|
110
|
+
publicKey: removed.publicKey,
|
|
111
|
+
filesReEncrypted,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Execute `ctx-sync team revoke`.
|
|
116
|
+
*
|
|
117
|
+
* Immediately revokes a team member's key and re-encrypts all
|
|
118
|
+
* state files. Similar to remove but uses the public key directly.
|
|
119
|
+
*/
|
|
120
|
+
export async function executeTeamRevoke(publicKey) {
|
|
121
|
+
await ensureRecipientsInit();
|
|
122
|
+
const configDir = getConfigDir();
|
|
123
|
+
const removed = removeRecipientByKey(configDir, publicKey);
|
|
124
|
+
// Re-encrypt ALL state without the revoked key
|
|
125
|
+
const filesReEncrypted = await reEncryptAllState();
|
|
126
|
+
return {
|
|
127
|
+
name: removed.name,
|
|
128
|
+
publicKey: removed.publicKey,
|
|
129
|
+
filesReEncrypted,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Execute `ctx-sync team list`.
|
|
134
|
+
*
|
|
135
|
+
* Returns the owner's public key and all team members.
|
|
136
|
+
*/
|
|
137
|
+
export async function executeTeamList() {
|
|
138
|
+
await ensureRecipientsInit();
|
|
139
|
+
const configDir = getConfigDir();
|
|
140
|
+
const config = getRecipients(configDir);
|
|
141
|
+
if (!config) {
|
|
142
|
+
throw new Error('Recipients configuration not initialised. Run `ctx-sync init` first.');
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
ownerPublicKey: config.ownerPublicKey,
|
|
146
|
+
members: config.members.map((m) => ({
|
|
147
|
+
name: m.name,
|
|
148
|
+
publicKey: m.publicKey,
|
|
149
|
+
fingerprint: m.fingerprint,
|
|
150
|
+
addedAt: m.addedAt,
|
|
151
|
+
})),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// ─── Commander Registration ───────────────────────────────────────────────
|
|
155
|
+
/**
|
|
156
|
+
* Register the `ctx-sync team` command group on the given program.
|
|
157
|
+
*/
|
|
158
|
+
export function registerTeamCommand(program) {
|
|
159
|
+
const teamCmd = program
|
|
160
|
+
.command('team')
|
|
161
|
+
.description('Manage team members (multi-recipient encryption)');
|
|
162
|
+
// ── team add ──────────────────────────────────────────────────────
|
|
163
|
+
teamCmd
|
|
164
|
+
.command('add')
|
|
165
|
+
.description('Add a team member as an encryption recipient')
|
|
166
|
+
.requiredOption('--name <name>', 'Human-readable name for the team member')
|
|
167
|
+
.requiredOption('--key <pubkey>', 'Age public key (age1...)')
|
|
168
|
+
.option('--no-verify', 'Skip fingerprint verification prompt')
|
|
169
|
+
.action(withErrorHandler(async (opts) => {
|
|
170
|
+
// Show fingerprint for verification
|
|
171
|
+
if (opts.verify) {
|
|
172
|
+
const fingerprint = computeFingerprint(opts.key);
|
|
173
|
+
console.log(`\n⚠ Verify this key fingerprint with ${opts.name}:`);
|
|
174
|
+
console.log(` Fingerprint: ${fingerprint}`);
|
|
175
|
+
console.log(` Key: ${opts.key}`);
|
|
176
|
+
console.log('');
|
|
177
|
+
}
|
|
178
|
+
const result = await executeTeamAdd({
|
|
179
|
+
name: opts.name,
|
|
180
|
+
key: opts.key,
|
|
181
|
+
noVerify: !opts.verify,
|
|
182
|
+
});
|
|
183
|
+
console.log(`✓ Added team member: ${result.name}`);
|
|
184
|
+
console.log(` Public key: ${result.publicKey}`);
|
|
185
|
+
console.log(` Fingerprint: ${result.fingerprint}`);
|
|
186
|
+
console.log(' All state re-encrypted for new recipient set.');
|
|
187
|
+
}));
|
|
188
|
+
// ── team remove ───────────────────────────────────────────────────
|
|
189
|
+
teamCmd
|
|
190
|
+
.command('remove <name>')
|
|
191
|
+
.description('Remove a team member and re-encrypt all state')
|
|
192
|
+
.action(withErrorHandler(async (name) => {
|
|
193
|
+
const result = await executeTeamRemove(name);
|
|
194
|
+
console.log(`✓ Removed team member: ${result.name}`);
|
|
195
|
+
console.log(` Public key: ${result.publicKey}`);
|
|
196
|
+
console.log(` Files re-encrypted: ${String(result.filesReEncrypted.length)}`);
|
|
197
|
+
console.log(` ${result.name} can no longer decrypt any state files.`);
|
|
198
|
+
}));
|
|
199
|
+
// ── team list ─────────────────────────────────────────────────────
|
|
200
|
+
teamCmd
|
|
201
|
+
.command('list')
|
|
202
|
+
.description('List all team members and their public keys')
|
|
203
|
+
.action(withErrorHandler(async () => {
|
|
204
|
+
const result = await executeTeamList();
|
|
205
|
+
console.log(`Owner key: ${result.ownerPublicKey}`);
|
|
206
|
+
console.log('');
|
|
207
|
+
if (result.members.length === 0) {
|
|
208
|
+
console.log('No team members added yet.');
|
|
209
|
+
console.log('Use `ctx-sync team add --name <name> --key <pubkey>` to add one.');
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
console.log(`Team members (${String(result.members.length)}):`);
|
|
213
|
+
for (const member of result.members) {
|
|
214
|
+
console.log(` ${member.name}`);
|
|
215
|
+
console.log(` Key: ${member.publicKey}`);
|
|
216
|
+
console.log(` Fingerprint: ${member.fingerprint}`);
|
|
217
|
+
console.log(` Added: ${member.addedAt}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}));
|
|
221
|
+
// ── team revoke ───────────────────────────────────────────────────
|
|
222
|
+
teamCmd
|
|
223
|
+
.command('revoke <pubkey>')
|
|
224
|
+
.description('Immediately revoke a key and re-encrypt all state')
|
|
225
|
+
.action(withErrorHandler(async (pubkey) => {
|
|
226
|
+
const result = await executeTeamRevoke(pubkey);
|
|
227
|
+
console.log(`✓ Revoked key for: ${result.name}`);
|
|
228
|
+
console.log(` Public key: ${result.publicKey}`);
|
|
229
|
+
console.log(` Files re-encrypted: ${String(result.filesReEncrypted.length)}`);
|
|
230
|
+
console.log(` ${result.name} can no longer decrypt new or existing state.`);
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=team.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../src/commands/team.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctx-sync track` command.
|
|
3
|
+
*
|
|
4
|
+
* Detects the current project's Git state, validates the path,
|
|
5
|
+
* encrypts the project entry, and writes it to `state.age`.
|
|
6
|
+
*
|
|
7
|
+
* Phase 16 enhancements: Step-by-step wizard with auto-detection,
|
|
8
|
+
* .env import, Docker tracking, mental context, and summary.
|
|
9
|
+
* `--yes` flag skips interactive confirmations (but NOT restore commands).
|
|
10
|
+
*
|
|
11
|
+
* @module commands/track
|
|
12
|
+
*/
|
|
13
|
+
import type { Command } from 'commander';
|
|
14
|
+
import type { Project } from '@ctx-sync/shared';
|
|
15
|
+
/** Options for the track command */
|
|
16
|
+
export interface TrackOptions {
|
|
17
|
+
/** Override the auto-detected project name */
|
|
18
|
+
name?: string;
|
|
19
|
+
/** Path to the project directory (default: CWD) */
|
|
20
|
+
path?: string;
|
|
21
|
+
/** Skip committing/pushing to sync repo */
|
|
22
|
+
noSync?: boolean;
|
|
23
|
+
/** Skip interactive prompts — accept defaults */
|
|
24
|
+
yes?: boolean;
|
|
25
|
+
/** Non-interactive mode — skip all prompts */
|
|
26
|
+
noInteractive?: boolean;
|
|
27
|
+
/** Override wizard prompt function (for testing) */
|
|
28
|
+
wizardPromptFn?: (context: WizardContext) => Promise<WizardAnswers>;
|
|
29
|
+
}
|
|
30
|
+
/** Context provided to the wizard prompt function */
|
|
31
|
+
export interface WizardContext {
|
|
32
|
+
projectName: string;
|
|
33
|
+
projectPath: string;
|
|
34
|
+
gitBranch: string;
|
|
35
|
+
envFileFound: boolean;
|
|
36
|
+
dockerComposeFound: boolean;
|
|
37
|
+
isNew: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** Answers collected from the wizard */
|
|
40
|
+
export interface WizardAnswers {
|
|
41
|
+
/** Whether to import .env file */
|
|
42
|
+
importEnv: boolean;
|
|
43
|
+
/** Whether to track Docker services */
|
|
44
|
+
trackDocker: boolean;
|
|
45
|
+
/** Mental context — current task (empty string = skip) */
|
|
46
|
+
currentTask: string;
|
|
47
|
+
/** Mental context — next steps (empty array = skip) */
|
|
48
|
+
nextSteps: string[];
|
|
49
|
+
/** Whether to proceed with commit */
|
|
50
|
+
confirmCommit: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** Result returned by executeTrack */
|
|
53
|
+
export interface TrackResult {
|
|
54
|
+
/** The project entry that was created or updated */
|
|
55
|
+
project: Project;
|
|
56
|
+
/** Whether this is a newly tracked project (vs. update) */
|
|
57
|
+
isNew: boolean;
|
|
58
|
+
/** Whether a .env file was found in the project */
|
|
59
|
+
envFileFound: boolean;
|
|
60
|
+
/** Whether a docker-compose file was found in the project */
|
|
61
|
+
dockerComposeFound: boolean;
|
|
62
|
+
/** Number of env vars imported (0 if skipped) */
|
|
63
|
+
envVarsImported: number;
|
|
64
|
+
/** Number of Docker services tracked (0 if skipped) */
|
|
65
|
+
dockerServicesTracked: number;
|
|
66
|
+
/** Whether mental context was set */
|
|
67
|
+
mentalContextSet: boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Detect Git information for a project directory.
|
|
71
|
+
*
|
|
72
|
+
* @param projectPath - Absolute path to the project directory.
|
|
73
|
+
* @returns Git metadata (branch, remote, uncommitted, stash count).
|
|
74
|
+
*/
|
|
75
|
+
export declare function detectGitInfo(projectPath: string): Promise<Project['git']>;
|
|
76
|
+
/**
|
|
77
|
+
* Auto-detect project name from Git remote URL or directory name.
|
|
78
|
+
*
|
|
79
|
+
* Priority:
|
|
80
|
+
* 1. Explicit `--name` flag.
|
|
81
|
+
* 2. Repository name from Git remote URL (e.g., `origin`).
|
|
82
|
+
* 3. Directory name.
|
|
83
|
+
*
|
|
84
|
+
* @param projectPath - Absolute path to the project directory.
|
|
85
|
+
* @param gitRemote - The Git remote fetch URL (empty if none).
|
|
86
|
+
* @returns The detected project name.
|
|
87
|
+
*/
|
|
88
|
+
export declare function detectProjectName(projectPath: string, gitRemote: string): string;
|
|
89
|
+
/**
|
|
90
|
+
* Execute the track command logic.
|
|
91
|
+
*
|
|
92
|
+
* 1. Resolve and validate the project path.
|
|
93
|
+
* 2. Auto-detect project name from Git remote / directory.
|
|
94
|
+
* 3. Detect Git info, .env, docker-compose.
|
|
95
|
+
* 4. Run interactive wizard (if not --yes or --no-interactive).
|
|
96
|
+
* 5. Build or update the Project entry.
|
|
97
|
+
* 6. Optionally import .env, track Docker, set mental context.
|
|
98
|
+
* 7. Encrypt and write state.age.
|
|
99
|
+
* 8. Optionally commit to the sync repo.
|
|
100
|
+
*
|
|
101
|
+
* @param options - Track command options.
|
|
102
|
+
* @returns Track result including the project entry.
|
|
103
|
+
*/
|
|
104
|
+
export declare function executeTrack(options: TrackOptions): Promise<TrackResult>;
|
|
105
|
+
/**
|
|
106
|
+
* Register the `track` command on the given Commander program.
|
|
107
|
+
*/
|
|
108
|
+
export declare function registerTrackCommand(program: Command): void;
|
|
109
|
+
//# sourceMappingURL=track.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"track.d.ts","sourceRoot":"","sources":["../../src/commands/track.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAA4B,MAAM,kBAAkB,CAAC;AAU1E,oCAAoC;AACpC,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iDAAiD;IACjD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,8CAA8C;IAC9C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oDAAoD;IACpD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CACrE;AAED,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,kCAAkC;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,WAAW,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,qCAAqC;IACrC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,KAAK,EAAE,OAAO,CAAC;IACf,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAC;IACtB,6DAA6D;IAC7D,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iDAAiD;IACjD,eAAe,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qCAAqC;IACrC,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CA2CzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAkBhF;AA4FD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA6L9E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6E3D"}
|