@prnv/tuck 1.4.1 → 1.5.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/index.js +938 -568
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -9,85 +9,203 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/ui/
|
|
12
|
+
// src/ui/theme.ts
|
|
13
13
|
import chalk from "chalk";
|
|
14
|
+
import figures from "figures";
|
|
15
|
+
import logSymbols from "log-symbols";
|
|
16
|
+
var DIVIDER_WIDTH, INDENT, colors, c, icons, categoryStyles, divider, indent, formatCount, formatStatus, boxStyles;
|
|
17
|
+
var init_theme = __esm({
|
|
18
|
+
"src/ui/theme.ts"() {
|
|
19
|
+
"use strict";
|
|
20
|
+
DIVIDER_WIDTH = 60;
|
|
21
|
+
INDENT = " ";
|
|
22
|
+
colors = {
|
|
23
|
+
// Brand
|
|
24
|
+
brand: chalk.cyan,
|
|
25
|
+
brandBold: chalk.bold.cyan,
|
|
26
|
+
brandDim: chalk.dim.cyan,
|
|
27
|
+
brandBg: chalk.bgCyan.black,
|
|
28
|
+
// Status
|
|
29
|
+
success: chalk.green,
|
|
30
|
+
warning: chalk.yellow,
|
|
31
|
+
error: chalk.red,
|
|
32
|
+
info: chalk.blue,
|
|
33
|
+
// Text
|
|
34
|
+
muted: chalk.dim,
|
|
35
|
+
bold: chalk.bold,
|
|
36
|
+
highlight: chalk.bold.white,
|
|
37
|
+
// Direct color aliases (for compatibility)
|
|
38
|
+
cyan: chalk.cyan,
|
|
39
|
+
green: chalk.green,
|
|
40
|
+
yellow: chalk.yellow,
|
|
41
|
+
red: chalk.red,
|
|
42
|
+
blue: chalk.blue,
|
|
43
|
+
dim: chalk.dim,
|
|
44
|
+
white: chalk.white
|
|
45
|
+
};
|
|
46
|
+
c = colors;
|
|
47
|
+
icons = {
|
|
48
|
+
// Status icons (colored, from log-symbols)
|
|
49
|
+
success: logSymbols.success,
|
|
50
|
+
error: logSymbols.error,
|
|
51
|
+
warning: logSymbols.warning,
|
|
52
|
+
info: logSymbols.info,
|
|
53
|
+
// Action icons (from figures - auto fallback)
|
|
54
|
+
tick: figures.tick,
|
|
55
|
+
cross: figures.cross,
|
|
56
|
+
pointer: figures.pointer,
|
|
57
|
+
arrowRight: figures.arrowRight,
|
|
58
|
+
arrowDown: figures.arrowDown,
|
|
59
|
+
arrowUp: figures.arrowUp,
|
|
60
|
+
// Progress icons
|
|
61
|
+
circle: figures.circle,
|
|
62
|
+
circleFilled: figures.circleFilled,
|
|
63
|
+
bullet: figures.bullet,
|
|
64
|
+
ellipsis: figures.ellipsis,
|
|
65
|
+
// File operations
|
|
66
|
+
add: c.success(figures.tick),
|
|
67
|
+
remove: c.error(figures.cross),
|
|
68
|
+
modify: c.warning("~"),
|
|
69
|
+
sync: c.brand(figures.arrowRight),
|
|
70
|
+
// Tree/structure
|
|
71
|
+
line: figures.line,
|
|
72
|
+
corner: figures.lineDownRight,
|
|
73
|
+
tee: figures.lineDownRightArc,
|
|
74
|
+
// Category icons
|
|
75
|
+
shell: "$",
|
|
76
|
+
git: figures.star,
|
|
77
|
+
editors: figures.pointer,
|
|
78
|
+
terminal: "#",
|
|
79
|
+
ssh: figures.warning,
|
|
80
|
+
misc: figures.bullet
|
|
81
|
+
};
|
|
82
|
+
categoryStyles = {
|
|
83
|
+
shell: { icon: "$", color: c.success },
|
|
84
|
+
git: { icon: figures.star, color: c.warning },
|
|
85
|
+
editors: { icon: figures.pointer, color: c.brand },
|
|
86
|
+
terminal: { icon: "#", color: c.info },
|
|
87
|
+
ssh: { icon: figures.warning, color: c.error },
|
|
88
|
+
misc: { icon: figures.bullet, color: c.muted }
|
|
89
|
+
};
|
|
90
|
+
divider = (width = DIVIDER_WIDTH) => c.muted("\u2500".repeat(width));
|
|
91
|
+
indent = (level = 1) => INDENT.repeat(level);
|
|
92
|
+
formatCount = (n, singular, plural) => {
|
|
93
|
+
const word = n === 1 ? singular : plural || `${singular}s`;
|
|
94
|
+
return `${c.bold(n.toString())} ${word}`;
|
|
95
|
+
};
|
|
96
|
+
formatStatus = (status) => {
|
|
97
|
+
switch (status) {
|
|
98
|
+
case "added":
|
|
99
|
+
return c.success(status);
|
|
100
|
+
case "modified":
|
|
101
|
+
return c.warning(status);
|
|
102
|
+
case "deleted":
|
|
103
|
+
return c.error(status);
|
|
104
|
+
default:
|
|
105
|
+
return c.muted(status);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
boxStyles = {
|
|
109
|
+
/** Compact header box */
|
|
110
|
+
header: {
|
|
111
|
+
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
112
|
+
borderStyle: "round",
|
|
113
|
+
borderColor: "cyan"
|
|
114
|
+
},
|
|
115
|
+
/** Standard info box */
|
|
116
|
+
info: {
|
|
117
|
+
padding: 1,
|
|
118
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
119
|
+
borderStyle: "round",
|
|
120
|
+
borderColor: "cyan"
|
|
121
|
+
},
|
|
122
|
+
/** Success box */
|
|
123
|
+
success: {
|
|
124
|
+
padding: 1,
|
|
125
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
126
|
+
borderStyle: "round",
|
|
127
|
+
borderColor: "green"
|
|
128
|
+
},
|
|
129
|
+
/** Error box */
|
|
130
|
+
error: {
|
|
131
|
+
padding: 1,
|
|
132
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
133
|
+
borderStyle: "round",
|
|
134
|
+
borderColor: "red"
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// src/ui/banner.ts
|
|
14
141
|
import boxen from "boxen";
|
|
15
142
|
var banner, miniBanner, customHelp, nextSteps;
|
|
16
143
|
var init_banner = __esm({
|
|
17
144
|
"src/ui/banner.ts"() {
|
|
18
145
|
"use strict";
|
|
146
|
+
init_theme();
|
|
19
147
|
banner = () => {
|
|
20
148
|
const art = `
|
|
21
149
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557
|
|
22
150
|
\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D
|
|
23
|
-
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
24
|
-
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557
|
|
151
|
+
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
152
|
+
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557
|
|
25
153
|
\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2557
|
|
26
154
|
\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D`;
|
|
27
|
-
console.log(
|
|
28
|
-
console.log(
|
|
155
|
+
console.log(colors.brand(art));
|
|
156
|
+
console.log(colors.muted(" Modern Dotfiles Manager\n"));
|
|
29
157
|
};
|
|
30
158
|
miniBanner = () => {
|
|
31
|
-
console.log(
|
|
32
|
-
boxen(chalk.cyan.bold("tuck") + chalk.dim(" \xB7 Modern Dotfiles Manager"), {
|
|
33
|
-
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
34
|
-
borderStyle: "round",
|
|
35
|
-
borderColor: "cyan"
|
|
36
|
-
})
|
|
37
|
-
);
|
|
159
|
+
console.log(boxen(colors.brandBold("tuck") + colors.muted(" \xB7 Modern Dotfiles Manager"), boxStyles.header));
|
|
38
160
|
console.log();
|
|
39
161
|
};
|
|
40
162
|
customHelp = (version) => {
|
|
41
|
-
const title = boxen(
|
|
42
|
-
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
43
|
-
borderStyle: "round",
|
|
44
|
-
borderColor: "cyan"
|
|
45
|
-
});
|
|
163
|
+
const title = boxen(colors.brandBold("tuck") + colors.muted(` v${version}`), boxStyles.header);
|
|
46
164
|
const quickStart = `
|
|
47
|
-
${
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
165
|
+
${colors.brandBold("Quick Start:")}
|
|
166
|
+
${indent()}${colors.brand("tuck init")} Set up tuck
|
|
167
|
+
${indent()}${colors.brand("tuck add <file>")} Track a dotfile
|
|
168
|
+
${indent()}${colors.brand("tuck sync")} Commit changes
|
|
169
|
+
${indent()}${colors.brand("tuck push")} Push to remote
|
|
52
170
|
|
|
53
|
-
${
|
|
54
|
-
|
|
171
|
+
${colors.brandBold("New Machine:")}
|
|
172
|
+
${indent()}${colors.brand("tuck apply <user>")} Apply dotfiles from GitHub
|
|
55
173
|
`;
|
|
56
174
|
const commands = `
|
|
57
|
-
${
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
175
|
+
${colors.brandBold("Commands:")}
|
|
176
|
+
${indent()}${colors.brand("Getting Started")}
|
|
177
|
+
${indent()}${indent()}init Initialize tuck
|
|
178
|
+
${indent()}${indent()}scan Detect dotfiles
|
|
179
|
+
${indent()}${indent()}apply <source> Apply from repo
|
|
62
180
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
181
|
+
${indent()}${colors.brand("Managing Files")}
|
|
182
|
+
${indent()}${indent()}add <paths...> Track files
|
|
183
|
+
${indent()}${indent()}remove <paths...> Untrack files
|
|
184
|
+
${indent()}${indent()}list List tracked
|
|
185
|
+
${indent()}${indent()}status Show status
|
|
68
186
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
187
|
+
${indent()}${colors.brand("Syncing")}
|
|
188
|
+
${indent()}${indent()}sync Commit changes
|
|
189
|
+
${indent()}${indent()}push Push to remote
|
|
190
|
+
${indent()}${indent()}pull Pull from remote
|
|
191
|
+
${indent()}${indent()}diff Show changes
|
|
74
192
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
193
|
+
${indent()}${colors.brand("Restoring")}
|
|
194
|
+
${indent()}${indent()}restore Restore files
|
|
195
|
+
${indent()}${indent()}undo Undo last apply
|
|
78
196
|
|
|
79
|
-
|
|
80
|
-
|
|
197
|
+
${indent()}${colors.brand("Config")}
|
|
198
|
+
${indent()}${indent()}config Manage settings
|
|
81
199
|
`;
|
|
82
200
|
const footer = `
|
|
83
|
-
${
|
|
84
|
-
${
|
|
201
|
+
${colors.muted("Run")} ${colors.brand("tuck <command> --help")} ${colors.muted("for details")}
|
|
202
|
+
${colors.muted("Docs:")} ${colors.brand("https://github.com/Pranav-Karra-3301/tuck")}
|
|
85
203
|
`;
|
|
86
204
|
return `${title}
|
|
87
205
|
${quickStart}${commands}${footer}`;
|
|
88
206
|
};
|
|
89
207
|
nextSteps = (steps) => {
|
|
90
|
-
const content = steps.map((step, i) => `${
|
|
208
|
+
const content = steps.map((step, i) => `${colors.brand(`${i + 1}.`)} ${step}`).join("\n");
|
|
91
209
|
console.log(
|
|
92
210
|
boxen(content, {
|
|
93
211
|
padding: 1,
|
|
@@ -103,75 +221,65 @@ ${quickStart}${commands}${footer}`;
|
|
|
103
221
|
});
|
|
104
222
|
|
|
105
223
|
// src/ui/logger.ts
|
|
106
|
-
import
|
|
107
|
-
|
|
224
|
+
import logSymbols2 from "log-symbols";
|
|
225
|
+
import figures2 from "figures";
|
|
226
|
+
var fileIcons, logger;
|
|
108
227
|
var init_logger = __esm({
|
|
109
228
|
"src/ui/logger.ts"() {
|
|
110
229
|
"use strict";
|
|
230
|
+
init_theme();
|
|
231
|
+
init_theme();
|
|
232
|
+
fileIcons = {
|
|
233
|
+
add: colors.success(figures2.tick),
|
|
234
|
+
modify: colors.warning("~"),
|
|
235
|
+
delete: colors.error(figures2.cross),
|
|
236
|
+
sync: colors.brand(figures2.arrowRight),
|
|
237
|
+
merge: colors.info("+")
|
|
238
|
+
};
|
|
111
239
|
logger = {
|
|
112
240
|
info: (msg) => {
|
|
113
|
-
console.log(
|
|
241
|
+
console.log(logSymbols2.info, msg);
|
|
114
242
|
},
|
|
115
243
|
success: (msg) => {
|
|
116
|
-
console.log(
|
|
244
|
+
console.log(logSymbols2.success, msg);
|
|
117
245
|
},
|
|
118
246
|
warning: (msg) => {
|
|
119
|
-
console.log(
|
|
247
|
+
console.log(logSymbols2.warning, msg);
|
|
120
248
|
},
|
|
121
249
|
error: (msg) => {
|
|
122
|
-
console.log(
|
|
250
|
+
console.log(logSymbols2.error, msg);
|
|
123
251
|
},
|
|
124
252
|
debug: (msg) => {
|
|
125
253
|
if (process.env.DEBUG) {
|
|
126
|
-
console.log(
|
|
254
|
+
console.log(colors.muted(figures2.bullet), colors.muted(msg));
|
|
127
255
|
}
|
|
128
256
|
},
|
|
129
257
|
step: (current, total, msg) => {
|
|
130
|
-
|
|
258
|
+
const counter = colors.muted(`[${current}/${total}]`);
|
|
259
|
+
console.log(counter, msg);
|
|
131
260
|
},
|
|
132
261
|
file: (action, path) => {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
modify: chalk2.yellow("~"),
|
|
136
|
-
delete: chalk2.red("-"),
|
|
137
|
-
sync: chalk2.blue("<>"),
|
|
138
|
-
merge: chalk2.magenta("+")
|
|
139
|
-
};
|
|
140
|
-
console.log(` ${icons[action]} ${path}`);
|
|
262
|
+
const icon = fileIcons[action];
|
|
263
|
+
console.log(`${indent()}${icon} ${colors.brand(path)}`);
|
|
141
264
|
},
|
|
142
265
|
tree: (items) => {
|
|
143
|
-
items.forEach(({ name, isLast, indent = 0 }) => {
|
|
144
|
-
const indentation =
|
|
145
|
-
const prefix = isLast ?
|
|
146
|
-
console.log(
|
|
266
|
+
items.forEach(({ name, isLast, indent: indent2 = 0 }) => {
|
|
267
|
+
const indentation = indent(indent2);
|
|
268
|
+
const prefix = isLast ? figures2.lineUpRight : figures2.lineDownRightArc;
|
|
269
|
+
console.log(colors.muted(indentation + prefix + figures2.line), name);
|
|
147
270
|
});
|
|
148
271
|
},
|
|
149
272
|
blank: () => {
|
|
150
273
|
console.log();
|
|
151
274
|
},
|
|
152
275
|
dim: (msg) => {
|
|
153
|
-
console.log(
|
|
276
|
+
console.log(colors.muted(msg));
|
|
154
277
|
},
|
|
155
278
|
heading: (msg) => {
|
|
156
|
-
console.log(
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const word = count === 1 ? singular : plural || `${singular}s`;
|
|
161
|
-
return `${chalk2.bold(count.toString())} ${word}`;
|
|
162
|
-
};
|
|
163
|
-
formatStatus = (status) => {
|
|
164
|
-
switch (status) {
|
|
165
|
-
case "modified":
|
|
166
|
-
return chalk2.yellow("modified");
|
|
167
|
-
case "added":
|
|
168
|
-
return chalk2.green("added");
|
|
169
|
-
case "deleted":
|
|
170
|
-
return chalk2.red("deleted");
|
|
171
|
-
case "untracked":
|
|
172
|
-
return chalk2.gray("untracked");
|
|
173
|
-
default:
|
|
174
|
-
return status;
|
|
279
|
+
console.log(colors.brandBold(msg));
|
|
280
|
+
},
|
|
281
|
+
divider: () => {
|
|
282
|
+
console.log(divider(DIVIDER_WIDTH));
|
|
175
283
|
}
|
|
176
284
|
};
|
|
177
285
|
}
|
|
@@ -179,18 +287,27 @@ var init_logger = __esm({
|
|
|
179
287
|
|
|
180
288
|
// src/ui/prompts.ts
|
|
181
289
|
import * as p from "@clack/prompts";
|
|
182
|
-
import chalk3 from "chalk";
|
|
183
290
|
var prompts;
|
|
184
291
|
var init_prompts = __esm({
|
|
185
292
|
"src/ui/prompts.ts"() {
|
|
186
293
|
"use strict";
|
|
294
|
+
init_theme();
|
|
187
295
|
prompts = {
|
|
296
|
+
/**
|
|
297
|
+
* Display command intro header
|
|
298
|
+
*/
|
|
188
299
|
intro: (title) => {
|
|
189
|
-
p.intro(
|
|
300
|
+
p.intro(colors.brandBg(` ${title} `));
|
|
190
301
|
},
|
|
302
|
+
/**
|
|
303
|
+
* Display command outro/success message
|
|
304
|
+
*/
|
|
191
305
|
outro: (message) => {
|
|
192
|
-
p.outro(
|
|
306
|
+
p.outro(colors.success(message));
|
|
193
307
|
},
|
|
308
|
+
/**
|
|
309
|
+
* Confirm dialog (yes/no)
|
|
310
|
+
*/
|
|
194
311
|
confirm: async (message, initial = false) => {
|
|
195
312
|
const result = await p.confirm({ message, initialValue: initial });
|
|
196
313
|
if (p.isCancel(result)) {
|
|
@@ -198,6 +315,9 @@ var init_prompts = __esm({
|
|
|
198
315
|
}
|
|
199
316
|
return result;
|
|
200
317
|
},
|
|
318
|
+
/**
|
|
319
|
+
* Single select from options
|
|
320
|
+
*/
|
|
201
321
|
select: async (message, options) => {
|
|
202
322
|
const result = await p.select({
|
|
203
323
|
message,
|
|
@@ -212,6 +332,9 @@ var init_prompts = __esm({
|
|
|
212
332
|
}
|
|
213
333
|
return result;
|
|
214
334
|
},
|
|
335
|
+
/**
|
|
336
|
+
* Multi-select from options
|
|
337
|
+
*/
|
|
215
338
|
multiselect: async (message, options, config) => {
|
|
216
339
|
const mappedOptions = options.map((opt) => ({
|
|
217
340
|
value: opt.value,
|
|
@@ -220,8 +343,8 @@ var init_prompts = __esm({
|
|
|
220
343
|
}));
|
|
221
344
|
const result = await p.multiselect({
|
|
222
345
|
message,
|
|
346
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
223
347
|
options: mappedOptions,
|
|
224
|
-
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
225
348
|
required: config?.required ?? false,
|
|
226
349
|
initialValues: config?.initialValues
|
|
227
350
|
});
|
|
@@ -230,6 +353,9 @@ var init_prompts = __esm({
|
|
|
230
353
|
}
|
|
231
354
|
return result;
|
|
232
355
|
},
|
|
356
|
+
/**
|
|
357
|
+
* Text input
|
|
358
|
+
*/
|
|
233
359
|
text: async (message, options) => {
|
|
234
360
|
const result = await p.text({
|
|
235
361
|
message,
|
|
@@ -242,6 +368,9 @@ var init_prompts = __esm({
|
|
|
242
368
|
}
|
|
243
369
|
return result;
|
|
244
370
|
},
|
|
371
|
+
/**
|
|
372
|
+
* Password input (hidden)
|
|
373
|
+
*/
|
|
245
374
|
password: async (message) => {
|
|
246
375
|
const result = await p.password({ message });
|
|
247
376
|
if (p.isCancel(result)) {
|
|
@@ -249,14 +378,26 @@ var init_prompts = __esm({
|
|
|
249
378
|
}
|
|
250
379
|
return result;
|
|
251
380
|
},
|
|
381
|
+
/**
|
|
382
|
+
* Create a spinner for async operations
|
|
383
|
+
*/
|
|
252
384
|
spinner: () => p.spinner(),
|
|
385
|
+
/**
|
|
386
|
+
* Display a note/info box
|
|
387
|
+
*/
|
|
253
388
|
note: (message, title) => {
|
|
254
389
|
p.note(message, title);
|
|
255
390
|
},
|
|
391
|
+
/**
|
|
392
|
+
* Cancel operation and exit
|
|
393
|
+
*/
|
|
256
394
|
cancel: (message = "Operation cancelled") => {
|
|
257
395
|
p.cancel(message);
|
|
258
396
|
process.exit(0);
|
|
259
397
|
},
|
|
398
|
+
/**
|
|
399
|
+
* Logging helpers
|
|
400
|
+
*/
|
|
260
401
|
log: {
|
|
261
402
|
info: (message) => {
|
|
262
403
|
p.log.info(message);
|
|
@@ -277,6 +418,9 @@ var init_prompts = __esm({
|
|
|
277
418
|
p.log.message(message);
|
|
278
419
|
}
|
|
279
420
|
},
|
|
421
|
+
/**
|
|
422
|
+
* Group multiple prompts
|
|
423
|
+
*/
|
|
280
424
|
group: async (steps, options) => {
|
|
281
425
|
const results = await p.group(steps, {
|
|
282
426
|
onCancel: () => {
|
|
@@ -294,52 +438,78 @@ var init_prompts = __esm({
|
|
|
294
438
|
});
|
|
295
439
|
|
|
296
440
|
// src/ui/spinner.ts
|
|
297
|
-
import
|
|
298
|
-
import
|
|
441
|
+
import * as p2 from "@clack/prompts";
|
|
442
|
+
import logSymbols3 from "log-symbols";
|
|
299
443
|
var createSpinner, withSpinner;
|
|
300
444
|
var init_spinner = __esm({
|
|
301
445
|
"src/ui/spinner.ts"() {
|
|
302
446
|
"use strict";
|
|
447
|
+
init_theme();
|
|
303
448
|
createSpinner = (initialText) => {
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
spinner: "dots"
|
|
308
|
-
});
|
|
449
|
+
const spinner4 = p2.spinner();
|
|
450
|
+
let currentText = initialText || "";
|
|
451
|
+
let started = false;
|
|
309
452
|
return {
|
|
310
453
|
start: (text2) => {
|
|
311
|
-
|
|
312
|
-
|
|
454
|
+
currentText = text2 || currentText || "Loading...";
|
|
455
|
+
spinner4.start(currentText);
|
|
456
|
+
started = true;
|
|
313
457
|
},
|
|
314
458
|
stop: () => {
|
|
315
|
-
|
|
459
|
+
if (started) {
|
|
460
|
+
spinner4.stop(currentText);
|
|
461
|
+
started = false;
|
|
462
|
+
}
|
|
316
463
|
},
|
|
317
464
|
succeed: (text2) => {
|
|
318
|
-
|
|
465
|
+
if (started) {
|
|
466
|
+
spinner4.stop(colors.success(text2 || currentText));
|
|
467
|
+
started = false;
|
|
468
|
+
} else {
|
|
469
|
+
console.log(logSymbols3.success, colors.success(text2 || currentText));
|
|
470
|
+
}
|
|
319
471
|
},
|
|
320
472
|
fail: (text2) => {
|
|
321
|
-
|
|
473
|
+
if (started) {
|
|
474
|
+
spinner4.stop(colors.error(text2 || currentText));
|
|
475
|
+
started = false;
|
|
476
|
+
} else {
|
|
477
|
+
console.log(logSymbols3.error, colors.error(text2 || currentText));
|
|
478
|
+
}
|
|
322
479
|
},
|
|
323
480
|
warn: (text2) => {
|
|
324
|
-
|
|
481
|
+
if (started) {
|
|
482
|
+
spinner4.stop(colors.warning(text2 || currentText));
|
|
483
|
+
started = false;
|
|
484
|
+
} else {
|
|
485
|
+
console.log(logSymbols3.warning, colors.warning(text2 || currentText));
|
|
486
|
+
}
|
|
325
487
|
},
|
|
326
488
|
info: (text2) => {
|
|
327
|
-
|
|
489
|
+
if (started) {
|
|
490
|
+
spinner4.stop(colors.info(text2 || currentText));
|
|
491
|
+
started = false;
|
|
492
|
+
} else {
|
|
493
|
+
console.log(logSymbols3.info, colors.info(text2 || currentText));
|
|
494
|
+
}
|
|
328
495
|
},
|
|
329
496
|
text: (text2) => {
|
|
330
|
-
|
|
497
|
+
currentText = text2;
|
|
498
|
+
if (started) {
|
|
499
|
+
spinner4.message(text2);
|
|
500
|
+
}
|
|
331
501
|
}
|
|
332
502
|
};
|
|
333
503
|
};
|
|
334
504
|
withSpinner = async (text2, fn, options) => {
|
|
335
|
-
const
|
|
336
|
-
|
|
505
|
+
const spinner4 = createSpinner(text2);
|
|
506
|
+
spinner4.start();
|
|
337
507
|
try {
|
|
338
508
|
const result = await fn();
|
|
339
|
-
|
|
509
|
+
spinner4.succeed(options?.successText || text2);
|
|
340
510
|
return result;
|
|
341
511
|
} catch (error) {
|
|
342
|
-
|
|
512
|
+
spinner4.fail(options?.failText || text2);
|
|
343
513
|
throw error;
|
|
344
514
|
}
|
|
345
515
|
};
|
|
@@ -347,26 +517,21 @@ var init_spinner = __esm({
|
|
|
347
517
|
});
|
|
348
518
|
|
|
349
519
|
// src/ui/table.ts
|
|
350
|
-
import chalk5 from "chalk";
|
|
351
520
|
var init_table = __esm({
|
|
352
521
|
"src/ui/table.ts"() {
|
|
353
522
|
"use strict";
|
|
523
|
+
init_theme();
|
|
354
524
|
}
|
|
355
525
|
});
|
|
356
526
|
|
|
357
527
|
// src/ui/progress.ts
|
|
358
|
-
import
|
|
359
|
-
import
|
|
360
|
-
|
|
528
|
+
import * as p3 from "@clack/prompts";
|
|
529
|
+
import logSymbols4 from "log-symbols";
|
|
530
|
+
import figures3 from "figures";
|
|
361
531
|
var init_progress = __esm({
|
|
362
532
|
"src/ui/progress.ts"() {
|
|
363
533
|
"use strict";
|
|
364
|
-
|
|
365
|
-
pending: chalk6.dim("\u25CB"),
|
|
366
|
-
in_progress: chalk6.cyan("\u25CF"),
|
|
367
|
-
completed: chalk6.green("\u2713"),
|
|
368
|
-
error: chalk6.red("\u2717")
|
|
369
|
-
};
|
|
534
|
+
init_theme();
|
|
370
535
|
}
|
|
371
536
|
});
|
|
372
537
|
|
|
@@ -374,6 +539,7 @@ var init_progress = __esm({
|
|
|
374
539
|
var init_ui = __esm({
|
|
375
540
|
"src/ui/index.ts"() {
|
|
376
541
|
"use strict";
|
|
542
|
+
init_theme();
|
|
377
543
|
init_banner();
|
|
378
544
|
init_logger();
|
|
379
545
|
init_prompts();
|
|
@@ -388,6 +554,7 @@ import { homedir } from "os";
|
|
|
388
554
|
import { join, dirname } from "path";
|
|
389
555
|
import { readFileSync } from "fs";
|
|
390
556
|
import { fileURLToPath } from "url";
|
|
557
|
+
import figures4 from "figures";
|
|
391
558
|
var __dirname, packageJsonPath, VERSION_VALUE, VERSION, DESCRIPTION, HOME_DIR, DEFAULT_TUCK_DIR, MANIFEST_FILE, CONFIG_FILE, BACKUP_DIR, FILES_DIR, CATEGORIES;
|
|
392
559
|
var init_constants = __esm({
|
|
393
560
|
"src/constants.ts"() {
|
|
@@ -425,7 +592,7 @@ var init_constants = __esm({
|
|
|
425
592
|
},
|
|
426
593
|
git: {
|
|
427
594
|
patterns: [".gitconfig", ".gitignore_global", ".gitmessage", ".gitattributes"],
|
|
428
|
-
icon:
|
|
595
|
+
icon: figures4.star
|
|
429
596
|
},
|
|
430
597
|
editors: {
|
|
431
598
|
patterns: [
|
|
@@ -437,7 +604,7 @@ var init_constants = __esm({
|
|
|
437
604
|
".ideavimrc",
|
|
438
605
|
".nanorc"
|
|
439
606
|
],
|
|
440
|
-
icon:
|
|
607
|
+
icon: figures4.pointer
|
|
441
608
|
},
|
|
442
609
|
terminal: {
|
|
443
610
|
patterns: [
|
|
@@ -453,11 +620,11 @@ var init_constants = __esm({
|
|
|
453
620
|
},
|
|
454
621
|
ssh: {
|
|
455
622
|
patterns: [".ssh/config"],
|
|
456
|
-
icon:
|
|
623
|
+
icon: figures4.warning
|
|
457
624
|
},
|
|
458
625
|
misc: {
|
|
459
626
|
patterns: [],
|
|
460
|
-
icon:
|
|
627
|
+
icon: figures4.bullet
|
|
461
628
|
}
|
|
462
629
|
};
|
|
463
630
|
}
|
|
@@ -771,7 +938,7 @@ var init_config_schema = __esm({
|
|
|
771
938
|
});
|
|
772
939
|
|
|
773
940
|
// src/errors.ts
|
|
774
|
-
import
|
|
941
|
+
import chalk2 from "chalk";
|
|
775
942
|
var TuckError, NotInitializedError, AlreadyInitializedError, FileNotFoundError, FileNotTrackedError, FileAlreadyTrackedError, GitError, ConfigError, ManifestError, PermissionError, GitHubCliError, BackupError, SecretsDetectedError, handleError;
|
|
776
943
|
var init_errors = __esm({
|
|
777
944
|
"src/errors.ts"() {
|
|
@@ -886,22 +1053,22 @@ var init_errors = __esm({
|
|
|
886
1053
|
};
|
|
887
1054
|
handleError = (error) => {
|
|
888
1055
|
if (error instanceof TuckError) {
|
|
889
|
-
console.error(
|
|
1056
|
+
console.error(chalk2.red("x"), error.message);
|
|
890
1057
|
if (error.suggestions && error.suggestions.length > 0) {
|
|
891
1058
|
console.error();
|
|
892
|
-
console.error(
|
|
893
|
-
error.suggestions.forEach((s) => console.error(
|
|
1059
|
+
console.error(chalk2.dim("Suggestions:"));
|
|
1060
|
+
error.suggestions.forEach((s) => console.error(chalk2.dim(` \u2192 ${s}`)));
|
|
894
1061
|
}
|
|
895
1062
|
process.exit(1);
|
|
896
1063
|
}
|
|
897
1064
|
if (error instanceof Error) {
|
|
898
|
-
console.error(
|
|
1065
|
+
console.error(chalk2.red("x"), "An unexpected error occurred:", error.message);
|
|
899
1066
|
if (process.env.DEBUG) {
|
|
900
1067
|
console.error(error.stack);
|
|
901
1068
|
}
|
|
902
1069
|
process.exit(1);
|
|
903
1070
|
}
|
|
904
|
-
console.error(
|
|
1071
|
+
console.error(chalk2.red("x"), "An unknown error occurred");
|
|
905
1072
|
process.exit(1);
|
|
906
1073
|
};
|
|
907
1074
|
}
|
|
@@ -3191,8 +3358,8 @@ var init_files = __esm({
|
|
|
3191
3358
|
});
|
|
3192
3359
|
|
|
3193
3360
|
// src/lib/fileTracking.ts
|
|
3194
|
-
import
|
|
3195
|
-
import
|
|
3361
|
+
import chalk3 from "chalk";
|
|
3362
|
+
import ora from "ora";
|
|
3196
3363
|
import { ensureDir as ensureDir2 } from "fs-extra";
|
|
3197
3364
|
import { dirname as dirname4 } from "path";
|
|
3198
3365
|
var SENSITIVE_FILE_PATTERNS, isSensitiveFile, trackFilesWithProgress;
|
|
@@ -3249,19 +3416,19 @@ var init_fileTracking = __esm({
|
|
|
3249
3416
|
const sensitiveFiles = [];
|
|
3250
3417
|
let succeeded = 0;
|
|
3251
3418
|
console.log();
|
|
3252
|
-
console.log(
|
|
3253
|
-
console.log(
|
|
3419
|
+
console.log(chalk3.bold.cyan(`${actionVerb} ${total} ${total === 1 ? "file" : "files"}...`));
|
|
3420
|
+
console.log(chalk3.dim("\u2500".repeat(50)));
|
|
3254
3421
|
console.log();
|
|
3255
3422
|
for (let i = 0; i < files.length; i++) {
|
|
3256
3423
|
const file = files[i];
|
|
3257
3424
|
const expandedPath = expandPath(file.path);
|
|
3258
|
-
const indexStr =
|
|
3425
|
+
const indexStr = chalk3.dim(`[${i + 1}/${total}]`);
|
|
3259
3426
|
const category = file.category || detectCategory(expandedPath);
|
|
3260
3427
|
const filename = sanitizeFilename(expandedPath);
|
|
3261
3428
|
const categoryInfo = CATEGORIES[category];
|
|
3262
3429
|
const icon = categoryInfo?.icon || "\u25CB";
|
|
3263
|
-
const
|
|
3264
|
-
text: `${indexStr} ${actionVerb} ${
|
|
3430
|
+
const spinner4 = ora({
|
|
3431
|
+
text: `${indexStr} ${actionVerb} ${chalk3.cyan(collapsePath(file.path))}`,
|
|
3265
3432
|
color: "cyan",
|
|
3266
3433
|
spinner: "dots",
|
|
3267
3434
|
indent: 2
|
|
@@ -3291,9 +3458,9 @@ var init_fileTracking = __esm({
|
|
|
3291
3458
|
modified: now,
|
|
3292
3459
|
checksum
|
|
3293
3460
|
});
|
|
3294
|
-
|
|
3295
|
-
const categoryStr = showCategory ?
|
|
3296
|
-
console.log(` ${
|
|
3461
|
+
spinner4.stop();
|
|
3462
|
+
const categoryStr = showCategory ? chalk3.dim(` ${icon} ${category}`) : "";
|
|
3463
|
+
console.log(` ${chalk3.green("\u2713")} ${indexStr} ${collapsePath(file.path)}${categoryStr}`);
|
|
3297
3464
|
if (isSensitiveFile(collapsePath(file.path))) {
|
|
3298
3465
|
sensitiveFiles.push(file.path);
|
|
3299
3466
|
}
|
|
@@ -3305,30 +3472,30 @@ var init_fileTracking = __esm({
|
|
|
3305
3472
|
await new Promise((resolve2) => setTimeout(resolve2, delayBetween));
|
|
3306
3473
|
}
|
|
3307
3474
|
} catch (error) {
|
|
3308
|
-
|
|
3475
|
+
spinner4.stop();
|
|
3309
3476
|
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
3310
3477
|
errors.push({ path: file.path, error: errorObj });
|
|
3311
|
-
console.log(` ${
|
|
3478
|
+
console.log(` ${chalk3.red("\u2717")} ${indexStr} ${collapsePath(file.path)} ${chalk3.red("- failed")}`);
|
|
3312
3479
|
}
|
|
3313
3480
|
}
|
|
3314
3481
|
console.log();
|
|
3315
3482
|
if (succeeded > 0) {
|
|
3316
|
-
console.log(
|
|
3483
|
+
console.log(chalk3.green("\u2713"), chalk3.bold(`Tracked ${succeeded} ${succeeded === 1 ? "file" : "files"} successfully`));
|
|
3317
3484
|
}
|
|
3318
3485
|
if (errors.length > 0) {
|
|
3319
3486
|
console.log();
|
|
3320
|
-
console.log(
|
|
3487
|
+
console.log(chalk3.red("\u2717"), chalk3.bold(`Failed to track ${errors.length} ${errors.length === 1 ? "file" : "files"}:`));
|
|
3321
3488
|
for (const { path, error } of errors) {
|
|
3322
|
-
console.log(
|
|
3489
|
+
console.log(chalk3.dim(` \u2022 ${collapsePath(path)}: ${error.message}`));
|
|
3323
3490
|
}
|
|
3324
3491
|
}
|
|
3325
3492
|
if (sensitiveFiles.length > 0) {
|
|
3326
3493
|
console.log();
|
|
3327
|
-
console.log(
|
|
3494
|
+
console.log(chalk3.yellow("\u26A0"), chalk3.yellow("Warning: Some files may contain sensitive data:"));
|
|
3328
3495
|
for (const path of sensitiveFiles) {
|
|
3329
|
-
console.log(
|
|
3496
|
+
console.log(chalk3.dim(` \u2022 ${collapsePath(path)}`));
|
|
3330
3497
|
}
|
|
3331
|
-
console.log(
|
|
3498
|
+
console.log(chalk3.dim(" Make sure your repository is private!"));
|
|
3332
3499
|
}
|
|
3333
3500
|
return {
|
|
3334
3501
|
succeeded,
|
|
@@ -3385,7 +3552,7 @@ var init_backup = __esm({
|
|
|
3385
3552
|
// src/lib/hooks.ts
|
|
3386
3553
|
import { exec } from "child_process";
|
|
3387
3554
|
import { promisify as promisify2 } from "util";
|
|
3388
|
-
import
|
|
3555
|
+
import chalk4 from "chalk";
|
|
3389
3556
|
var execAsync, runHook, runPreSyncHook, runPostSyncHook, runPreRestoreHook, runPostRestoreHook;
|
|
3390
3557
|
var init_hooks = __esm({
|
|
3391
3558
|
"src/lib/hooks.ts"() {
|
|
@@ -3405,19 +3572,19 @@ var init_hooks = __esm({
|
|
|
3405
3572
|
}
|
|
3406
3573
|
if (!options?.trustHooks) {
|
|
3407
3574
|
console.log();
|
|
3408
|
-
console.log(
|
|
3409
|
-
console.log(
|
|
3410
|
-
console.log(
|
|
3411
|
-
console.log(
|
|
3412
|
-
console.log(
|
|
3413
|
-
console.log(
|
|
3575
|
+
console.log(chalk4.yellow.bold("WARNING: Hook Execution"));
|
|
3576
|
+
console.log(chalk4.dim("\u2500".repeat(50)));
|
|
3577
|
+
console.log(chalk4.white(`Hook type: ${chalk4.cyan(hookType)}`));
|
|
3578
|
+
console.log(chalk4.white("Command:"));
|
|
3579
|
+
console.log(chalk4.red(` ${command}`));
|
|
3580
|
+
console.log(chalk4.dim("\u2500".repeat(50)));
|
|
3414
3581
|
console.log(
|
|
3415
|
-
|
|
3582
|
+
chalk4.yellow(
|
|
3416
3583
|
"SECURITY: Hooks can execute arbitrary commands on your system."
|
|
3417
3584
|
)
|
|
3418
3585
|
);
|
|
3419
3586
|
console.log(
|
|
3420
|
-
|
|
3587
|
+
chalk4.yellow(
|
|
3421
3588
|
"Only proceed if you trust the source of this configuration."
|
|
3422
3589
|
)
|
|
3423
3590
|
);
|
|
@@ -3483,13 +3650,13 @@ __export(restore_exports, {
|
|
|
3483
3650
|
runRestore: () => runRestore
|
|
3484
3651
|
});
|
|
3485
3652
|
import { Command } from "commander";
|
|
3486
|
-
import chalk10 from "chalk";
|
|
3487
3653
|
import { join as join7 } from "path";
|
|
3488
3654
|
import { chmod, stat as stat4 } from "fs/promises";
|
|
3489
3655
|
var fixSSHPermissions, fixGPGPermissions, prepareFilesToRestore, restoreFiles, runInteractiveRestore, runRestore, runRestoreCommand, restoreCommand;
|
|
3490
3656
|
var init_restore = __esm({
|
|
3491
3657
|
"src/commands/restore.ts"() {
|
|
3492
3658
|
"use strict";
|
|
3659
|
+
init_theme();
|
|
3493
3660
|
init_ui();
|
|
3494
3661
|
init_paths();
|
|
3495
3662
|
init_manifest();
|
|
@@ -3615,14 +3782,16 @@ var init_restore = __esm({
|
|
|
3615
3782
|
}
|
|
3616
3783
|
const fileOptions = files.map((file) => {
|
|
3617
3784
|
const categoryConfig = CATEGORIES[file.category] || { icon: "\u{1F4C4}" };
|
|
3618
|
-
const status = file.existsAtTarget ?
|
|
3785
|
+
const status = file.existsAtTarget ? colors.yellow("(exists, will backup)") : "";
|
|
3619
3786
|
return {
|
|
3620
3787
|
value: file.id,
|
|
3621
3788
|
label: `${categoryConfig.icon} ${file.source} ${status}`,
|
|
3622
3789
|
hint: file.category
|
|
3623
3790
|
};
|
|
3624
3791
|
});
|
|
3625
|
-
const selectedIds = await prompts.multiselect("Select files to restore:", fileOptions, {
|
|
3792
|
+
const selectedIds = await prompts.multiselect("Select files to restore:", fileOptions, {
|
|
3793
|
+
required: true
|
|
3794
|
+
});
|
|
3626
3795
|
if (selectedIds.length === 0) {
|
|
3627
3796
|
prompts.cancel("No files selected");
|
|
3628
3797
|
return;
|
|
@@ -3634,7 +3803,7 @@ var init_restore = __esm({
|
|
|
3634
3803
|
prompts.log.warning(
|
|
3635
3804
|
`${existingFiles.length} file${existingFiles.length > 1 ? "s" : ""} will be backed up:`
|
|
3636
3805
|
);
|
|
3637
|
-
existingFiles.forEach((f) => console.log(
|
|
3806
|
+
existingFiles.forEach((f) => console.log(colors.dim(` ${f.source}`)));
|
|
3638
3807
|
console.log();
|
|
3639
3808
|
}
|
|
3640
3809
|
const useSymlink = await prompts.select("Restore method:", [
|
|
@@ -4389,7 +4558,7 @@ var init_patterns = __esm({
|
|
|
4389
4558
|
low: 3
|
|
4390
4559
|
};
|
|
4391
4560
|
const minLevel = severityOrder[minSeverity];
|
|
4392
|
-
return ALL_SECRET_PATTERNS.filter((
|
|
4561
|
+
return ALL_SECRET_PATTERNS.filter((p4) => severityOrder[p4.severity] <= minLevel);
|
|
4393
4562
|
};
|
|
4394
4563
|
createCustomPattern = (id, name, pattern, options) => {
|
|
4395
4564
|
return {
|
|
@@ -4561,7 +4730,7 @@ var init_scanner = __esm({
|
|
|
4561
4730
|
}
|
|
4562
4731
|
if (options.excludePatternIds && options.excludePatternIds.length > 0) {
|
|
4563
4732
|
const excludeSet = new Set(options.excludePatternIds);
|
|
4564
|
-
patterns = patterns.filter((
|
|
4733
|
+
patterns = patterns.filter((p4) => !excludeSet.has(p4.id));
|
|
4565
4734
|
}
|
|
4566
4735
|
for (const pattern of patterns) {
|
|
4567
4736
|
if (Date.now() - scanStartTime > SCAN_TIMEOUT_MS) {
|
|
@@ -5094,10 +5263,10 @@ var init_external = __esm({
|
|
|
5094
5263
|
"credential"
|
|
5095
5264
|
];
|
|
5096
5265
|
const ruleIdLower = ruleId.toLowerCase();
|
|
5097
|
-
if (criticalPatterns.some((
|
|
5266
|
+
if (criticalPatterns.some((p4) => ruleIdLower.includes(p4))) {
|
|
5098
5267
|
return "critical";
|
|
5099
5268
|
}
|
|
5100
|
-
if (highPatterns.some((
|
|
5269
|
+
if (highPatterns.some((p4) => ruleIdLower.includes(p4))) {
|
|
5101
5270
|
return "high";
|
|
5102
5271
|
}
|
|
5103
5272
|
return "medium";
|
|
@@ -5248,11 +5417,11 @@ var init_secrets = __esm({
|
|
|
5248
5417
|
}
|
|
5249
5418
|
}
|
|
5250
5419
|
const customPatterns = (security.customPatterns || []).map(
|
|
5251
|
-
(
|
|
5252
|
-
severity:
|
|
5253
|
-
description:
|
|
5254
|
-
placeholder:
|
|
5255
|
-
flags:
|
|
5420
|
+
(p4, i) => createCustomPattern(`config-${i}`, p4.name || `Custom Pattern ${i + 1}`, p4.pattern, {
|
|
5421
|
+
severity: p4.severity,
|
|
5422
|
+
description: p4.description,
|
|
5423
|
+
placeholder: p4.placeholder,
|
|
5424
|
+
flags: p4.flags
|
|
5256
5425
|
})
|
|
5257
5426
|
);
|
|
5258
5427
|
return scanFiles(filepaths, {
|
|
@@ -5317,7 +5486,6 @@ var init_secrets = __esm({
|
|
|
5317
5486
|
|
|
5318
5487
|
// src/commands/secrets.ts
|
|
5319
5488
|
import { Command as Command3 } from "commander";
|
|
5320
|
-
import chalk12 from "chalk";
|
|
5321
5489
|
var runSecretsList, runSecretsSet, runSecretsUnset, runSecretsPath, runScanHistory, runScanFiles, displayScanResults, secretsCommand;
|
|
5322
5490
|
var init_secrets2 = __esm({
|
|
5323
5491
|
"src/commands/secrets.ts"() {
|
|
@@ -5345,19 +5513,19 @@ var init_secrets2 = __esm({
|
|
|
5345
5513
|
return;
|
|
5346
5514
|
}
|
|
5347
5515
|
console.log();
|
|
5348
|
-
console.log(
|
|
5349
|
-
console.log(
|
|
5516
|
+
console.log(colors.bold.cyan(`Stored Secrets (${secrets.length})`));
|
|
5517
|
+
console.log(colors.dim("\u2500".repeat(50)));
|
|
5350
5518
|
console.log();
|
|
5351
5519
|
for (const secret of secrets) {
|
|
5352
|
-
console.log(` ${
|
|
5353
|
-
console.log(` ${
|
|
5520
|
+
console.log(` ${colors.green(secret.name)}`);
|
|
5521
|
+
console.log(` ${colors.dim("Placeholder:")} ${colors.cyan(secret.placeholder)}`);
|
|
5354
5522
|
if (secret.description) {
|
|
5355
|
-
console.log(` ${
|
|
5523
|
+
console.log(` ${colors.dim("Type:")} ${secret.description}`);
|
|
5356
5524
|
}
|
|
5357
5525
|
if (secret.source) {
|
|
5358
|
-
console.log(` ${
|
|
5526
|
+
console.log(` ${colors.dim("Source:")} ${secret.source}`);
|
|
5359
5527
|
}
|
|
5360
|
-
console.log(` ${
|
|
5528
|
+
console.log(` ${colors.dim("Added:")} ${new Date(secret.addedAt).toLocaleDateString()}`);
|
|
5361
5529
|
console.log();
|
|
5362
5530
|
}
|
|
5363
5531
|
logger.dim(`Secrets file: ${getSecretsPath(tuckDir)}`);
|
|
@@ -5418,22 +5586,22 @@ var init_secrets2 = __esm({
|
|
|
5418
5586
|
}
|
|
5419
5587
|
const limit = options.limit ? parseInt(options.limit, 10) : 50;
|
|
5420
5588
|
prompts.intro("tuck secrets scan-history");
|
|
5421
|
-
const
|
|
5422
|
-
|
|
5589
|
+
const spinner4 = prompts.spinner();
|
|
5590
|
+
spinner4.start("Scanning git history for secrets...");
|
|
5423
5591
|
try {
|
|
5424
5592
|
const logEntries = await getLog(tuckDir, {
|
|
5425
5593
|
maxCount: limit,
|
|
5426
5594
|
since: options.since
|
|
5427
5595
|
});
|
|
5428
5596
|
if (logEntries.length === 0) {
|
|
5429
|
-
|
|
5597
|
+
spinner4.stop("No commits found");
|
|
5430
5598
|
return;
|
|
5431
5599
|
}
|
|
5432
5600
|
let simpleGit2;
|
|
5433
5601
|
try {
|
|
5434
5602
|
simpleGit2 = (await import("simple-git")).default;
|
|
5435
5603
|
} catch (importError) {
|
|
5436
|
-
|
|
5604
|
+
spinner4.stop("Git integration is unavailable (simple-git module could not be loaded).");
|
|
5437
5605
|
const errorMsg = importError instanceof Error ? importError.message : String(importError);
|
|
5438
5606
|
logger.error(`Failed to load simple-git for scan-history: ${errorMsg}`);
|
|
5439
5607
|
return;
|
|
@@ -5443,7 +5611,7 @@ var init_secrets2 = __esm({
|
|
|
5443
5611
|
let scannedCommits = 0;
|
|
5444
5612
|
for (const entry of logEntries) {
|
|
5445
5613
|
scannedCommits++;
|
|
5446
|
-
|
|
5614
|
+
spinner4.message(`Scanning commit ${scannedCommits}/${logEntries.length}...`);
|
|
5447
5615
|
try {
|
|
5448
5616
|
const diff = await git.diff([`${entry.hash}^`, entry.hash]);
|
|
5449
5617
|
if (diff) {
|
|
@@ -5478,7 +5646,7 @@ var init_secrets2 = __esm({
|
|
|
5478
5646
|
continue;
|
|
5479
5647
|
}
|
|
5480
5648
|
}
|
|
5481
|
-
|
|
5649
|
+
spinner4.stop(`Scanned ${scannedCommits} commits`);
|
|
5482
5650
|
if (results.length === 0) {
|
|
5483
5651
|
console.log();
|
|
5484
5652
|
logger.success("No secrets found in git history");
|
|
@@ -5486,32 +5654,32 @@ var init_secrets2 = __esm({
|
|
|
5486
5654
|
return;
|
|
5487
5655
|
}
|
|
5488
5656
|
console.log();
|
|
5489
|
-
console.log(
|
|
5490
|
-
console.log(
|
|
5657
|
+
console.log(colors.bold.red(`Found potential secrets in ${results.length} commits`));
|
|
5658
|
+
console.log(colors.dim("\u2500".repeat(60)));
|
|
5491
5659
|
console.log();
|
|
5492
5660
|
for (const result of results) {
|
|
5493
|
-
console.log(
|
|
5494
|
-
console.log(
|
|
5495
|
-
console.log(
|
|
5496
|
-
console.log(
|
|
5661
|
+
console.log(colors.yellow(`Commit: ${result.commit}`));
|
|
5662
|
+
console.log(colors.dim(` Author: ${result.author}`));
|
|
5663
|
+
console.log(colors.dim(` Date: ${result.date}`));
|
|
5664
|
+
console.log(colors.dim(` Message: ${result.message}`));
|
|
5497
5665
|
console.log();
|
|
5498
5666
|
for (const secret of result.secrets) {
|
|
5499
|
-
const severityColor = secret.severity === "critical" ?
|
|
5667
|
+
const severityColor = secret.severity === "critical" ? colors.red : secret.severity === "high" ? colors.yellow : colors.dim;
|
|
5500
5668
|
console.log(` ${severityColor(`[${secret.severity}]`)} ${secret.pattern}`);
|
|
5501
|
-
console.log(
|
|
5669
|
+
console.log(colors.dim(` Value: ${secret.redactedValue}`));
|
|
5502
5670
|
}
|
|
5503
5671
|
console.log();
|
|
5504
5672
|
}
|
|
5505
|
-
console.log(
|
|
5673
|
+
console.log(colors.dim("\u2500".repeat(60)));
|
|
5506
5674
|
console.log();
|
|
5507
5675
|
logger.warning("If these secrets are still valid, rotate them immediately!");
|
|
5508
5676
|
console.log();
|
|
5509
5677
|
logger.dim("To remove secrets from git history, consider using:");
|
|
5510
5678
|
logger.dim(" - git filter-branch");
|
|
5511
5679
|
logger.dim(" - BFG Repo-Cleaner (https://rtyley.github.io/bfg-repo-cleaner/)");
|
|
5512
|
-
prompts.outro(
|
|
5680
|
+
prompts.outro(colors.red(`${results.length} commits with potential secrets`));
|
|
5513
5681
|
} catch (error) {
|
|
5514
|
-
|
|
5682
|
+
spinner4.stop("Scan failed");
|
|
5515
5683
|
throw error;
|
|
5516
5684
|
}
|
|
5517
5685
|
};
|
|
@@ -5527,7 +5695,7 @@ var init_secrets2 = __esm({
|
|
|
5527
5695
|
logger.dim("Usage: tuck secrets scan <file> [files...]");
|
|
5528
5696
|
return;
|
|
5529
5697
|
}
|
|
5530
|
-
const expandedPaths = paths.map((
|
|
5698
|
+
const expandedPaths = paths.map((p4) => expandPath(p4));
|
|
5531
5699
|
for (const path of expandedPaths) {
|
|
5532
5700
|
if (!await pathExists(path)) {
|
|
5533
5701
|
logger.warning(`File not found: ${path}`);
|
|
@@ -5543,10 +5711,10 @@ var init_secrets2 = __esm({
|
|
|
5543
5711
|
logger.error("No valid files to scan");
|
|
5544
5712
|
return;
|
|
5545
5713
|
}
|
|
5546
|
-
const
|
|
5547
|
-
|
|
5714
|
+
const spinner4 = prompts.spinner();
|
|
5715
|
+
spinner4.start(`Scanning ${existingPaths.length} file(s)...`);
|
|
5548
5716
|
const summary = await scanForSecrets(existingPaths, tuckDir);
|
|
5549
|
-
|
|
5717
|
+
spinner4.stop("Scan complete");
|
|
5550
5718
|
if (summary.filesWithSecrets === 0) {
|
|
5551
5719
|
console.log();
|
|
5552
5720
|
logger.success("No secrets detected");
|
|
@@ -5556,28 +5724,34 @@ var init_secrets2 = __esm({
|
|
|
5556
5724
|
};
|
|
5557
5725
|
displayScanResults = (summary) => {
|
|
5558
5726
|
console.log();
|
|
5559
|
-
console.log(
|
|
5560
|
-
|
|
5727
|
+
console.log(
|
|
5728
|
+
colors.bold.red(
|
|
5729
|
+
`Found ${summary.totalSecrets} potential secret(s) in ${summary.filesWithSecrets} file(s)`
|
|
5730
|
+
)
|
|
5731
|
+
);
|
|
5732
|
+
console.log(colors.dim("\u2500".repeat(60)));
|
|
5561
5733
|
console.log();
|
|
5562
5734
|
if (summary.bySeverity.critical > 0) {
|
|
5563
|
-
console.log(
|
|
5735
|
+
console.log(colors.red(` Critical: ${summary.bySeverity.critical}`));
|
|
5564
5736
|
}
|
|
5565
5737
|
if (summary.bySeverity.high > 0) {
|
|
5566
|
-
console.log(
|
|
5738
|
+
console.log(colors.yellow(` High: ${summary.bySeverity.high}`));
|
|
5567
5739
|
}
|
|
5568
5740
|
if (summary.bySeverity.medium > 0) {
|
|
5569
|
-
console.log(
|
|
5741
|
+
console.log(colors.blue(` Medium: ${summary.bySeverity.medium}`));
|
|
5570
5742
|
}
|
|
5571
5743
|
if (summary.bySeverity.low > 0) {
|
|
5572
|
-
console.log(
|
|
5744
|
+
console.log(colors.dim(` Low: ${summary.bySeverity.low}`));
|
|
5573
5745
|
}
|
|
5574
5746
|
console.log();
|
|
5575
5747
|
for (const result of summary.results) {
|
|
5576
|
-
console.log(
|
|
5748
|
+
console.log(colors.cyan(result.collapsedPath));
|
|
5577
5749
|
for (const match of result.matches) {
|
|
5578
|
-
const severityColor = match.severity === "critical" ?
|
|
5579
|
-
console.log(
|
|
5580
|
-
|
|
5750
|
+
const severityColor = match.severity === "critical" ? colors.red : match.severity === "high" ? colors.yellow : match.severity === "medium" ? colors.blue : colors.dim;
|
|
5751
|
+
console.log(
|
|
5752
|
+
` ${colors.dim(`Line ${match.line}:`)} ${severityColor(`[${match.severity}]`)} ${match.patternName}`
|
|
5753
|
+
);
|
|
5754
|
+
console.log(colors.dim(` ${match.context}`));
|
|
5581
5755
|
}
|
|
5582
5756
|
console.log();
|
|
5583
5757
|
}
|
|
@@ -5590,9 +5764,7 @@ var init_secrets2 = __esm({
|
|
|
5590
5764
|
new Command3("set").description("Set a secret value (prompts securely)").argument("<name>", "Secret name (e.g., GITHUB_TOKEN)").action(runSecretsSet)
|
|
5591
5765
|
).addCommand(
|
|
5592
5766
|
new Command3("unset").description("Remove a secret").argument("<name>", "Secret name to remove").action(runSecretsUnset)
|
|
5593
|
-
).addCommand(
|
|
5594
|
-
new Command3("path").description("Show path to secrets file").action(runSecretsPath)
|
|
5595
|
-
).addCommand(
|
|
5767
|
+
).addCommand(new Command3("path").description("Show path to secrets file").action(runSecretsPath)).addCommand(
|
|
5596
5768
|
new Command3("scan").description("Scan files for secrets").argument("[paths...]", "Files to scan").action(runScanFiles)
|
|
5597
5769
|
).addCommand(
|
|
5598
5770
|
new Command3("scan-history").description("Scan git history for leaked secrets").option("--since <date>", "Only scan commits after this date (e.g., 2024-01-01)").option("--limit <n>", "Maximum number of commits to scan", "50").action(runScanHistory)
|
|
@@ -5607,7 +5779,6 @@ __export(sync_exports, {
|
|
|
5607
5779
|
syncCommand: () => syncCommand
|
|
5608
5780
|
});
|
|
5609
5781
|
import { Command as Command4 } from "commander";
|
|
5610
|
-
import chalk13 from "chalk";
|
|
5611
5782
|
import { join as join12 } from "path";
|
|
5612
5783
|
var detectChanges, pullIfBehind, detectNewDotfiles, generateCommitMessage, syncFiles, scanAndHandleSecrets, runInteractiveSync, pushWithSpinner, runSync, runSyncCommand, syncCommand;
|
|
5613
5784
|
var init_sync = __esm({
|
|
@@ -5720,7 +5891,9 @@ var init_sync = __esm({
|
|
|
5720
5891
|
changes.push(`\u2022 ${file}`);
|
|
5721
5892
|
});
|
|
5722
5893
|
} else {
|
|
5723
|
-
changes.push(
|
|
5894
|
+
changes.push(
|
|
5895
|
+
`${result.modified.length > 0 ? "\n" : ""}Deleted: ${result.deleted.length} files`
|
|
5896
|
+
);
|
|
5724
5897
|
}
|
|
5725
5898
|
}
|
|
5726
5899
|
if (changes.length > 0) {
|
|
@@ -5794,14 +5967,14 @@ var init_sync = __esm({
|
|
|
5794
5967
|
if (!scanningEnabled) {
|
|
5795
5968
|
return true;
|
|
5796
5969
|
}
|
|
5797
|
-
const modifiedPaths = changes.filter((
|
|
5970
|
+
const modifiedPaths = changes.filter((c2) => c2.status === "modified").map((c2) => expandPath(c2.source));
|
|
5798
5971
|
if (modifiedPaths.length === 0) {
|
|
5799
5972
|
return true;
|
|
5800
5973
|
}
|
|
5801
|
-
const
|
|
5802
|
-
|
|
5974
|
+
const spinner4 = prompts.spinner();
|
|
5975
|
+
spinner4.start("Scanning for secrets...");
|
|
5803
5976
|
const summary = await scanForSecrets(modifiedPaths, tuckDir);
|
|
5804
|
-
|
|
5977
|
+
spinner4.stop("Scan complete");
|
|
5805
5978
|
if (summary.totalSecrets === 0) {
|
|
5806
5979
|
return true;
|
|
5807
5980
|
}
|
|
@@ -5817,14 +5990,18 @@ var init_sync = __esm({
|
|
|
5817
5990
|
}
|
|
5818
5991
|
if (action === "ignore") {
|
|
5819
5992
|
for (const result of summary.results) {
|
|
5820
|
-
const sourcePath = changes.find((
|
|
5993
|
+
const sourcePath = changes.find((c2) => expandPath(c2.source) === result.path)?.source;
|
|
5821
5994
|
if (sourcePath) {
|
|
5822
5995
|
await addToTuckignore(tuckDir, sourcePath);
|
|
5823
5996
|
logger.dim(`Added ${collapsePath(result.path)} to .tuckignore`);
|
|
5824
5997
|
}
|
|
5825
5998
|
}
|
|
5826
5999
|
const filesToRemove = new Set(summary.results.map((r) => r.path));
|
|
5827
|
-
changes.splice(
|
|
6000
|
+
changes.splice(
|
|
6001
|
+
0,
|
|
6002
|
+
changes.length,
|
|
6003
|
+
...changes.filter((c2) => !filesToRemove.has(expandPath(c2.source)))
|
|
6004
|
+
);
|
|
5828
6005
|
if (changes.length === 0) {
|
|
5829
6006
|
prompts.log.info("No remaining changes to sync");
|
|
5830
6007
|
return false;
|
|
@@ -5844,7 +6021,9 @@ var init_sync = __esm({
|
|
|
5844
6021
|
pullSpinner.stop(`Could not pull: ${pullResult.error}`);
|
|
5845
6022
|
prompts.log.warning("Continuing with local changes...");
|
|
5846
6023
|
} else if (pullResult.pulled) {
|
|
5847
|
-
pullSpinner.stop(
|
|
6024
|
+
pullSpinner.stop(
|
|
6025
|
+
`Pulled ${pullResult.behind} commit${pullResult.behind > 1 ? "s" : ""} from remote`
|
|
6026
|
+
);
|
|
5848
6027
|
} else {
|
|
5849
6028
|
pullSpinner.stop("Up to date with remote");
|
|
5850
6029
|
}
|
|
@@ -5889,19 +6068,19 @@ var init_sync = __esm({
|
|
|
5889
6068
|
}
|
|
5890
6069
|
if (changes.length > 0) {
|
|
5891
6070
|
console.log();
|
|
5892
|
-
console.log(
|
|
6071
|
+
console.log(colors.bold("Changes to tracked files:"));
|
|
5893
6072
|
for (const change of changes) {
|
|
5894
6073
|
if (change.status === "modified") {
|
|
5895
|
-
console.log(
|
|
6074
|
+
console.log(colors.yellow(` ~ ${change.path}`));
|
|
5896
6075
|
} else if (change.status === "deleted") {
|
|
5897
|
-
console.log(
|
|
6076
|
+
console.log(colors.red(` - ${change.path}`));
|
|
5898
6077
|
}
|
|
5899
6078
|
}
|
|
5900
6079
|
}
|
|
5901
6080
|
let filesToTrack = [];
|
|
5902
6081
|
if (newFiles.length > 0) {
|
|
5903
6082
|
console.log();
|
|
5904
|
-
console.log(
|
|
6083
|
+
console.log(colors.bold(`New dotfiles found (${newFiles.length}):`));
|
|
5905
6084
|
const grouped = {};
|
|
5906
6085
|
for (const file of newFiles) {
|
|
5907
6086
|
if (!grouped[file.category]) grouped[file.category] = [];
|
|
@@ -5909,23 +6088,28 @@ var init_sync = __esm({
|
|
|
5909
6088
|
}
|
|
5910
6089
|
for (const [category, files] of Object.entries(grouped)) {
|
|
5911
6090
|
const categoryInfo = DETECTION_CATEGORIES[category] || { icon: "-", name: category };
|
|
5912
|
-
console.log(
|
|
6091
|
+
console.log(
|
|
6092
|
+
colors.cyan(
|
|
6093
|
+
` ${categoryInfo.icon} ${categoryInfo.name}: ${files.length} file${files.length > 1 ? "s" : ""}`
|
|
6094
|
+
)
|
|
6095
|
+
);
|
|
5913
6096
|
}
|
|
5914
6097
|
console.log();
|
|
5915
|
-
const trackNewFiles = await prompts.confirm(
|
|
6098
|
+
const trackNewFiles = await prompts.confirm(
|
|
6099
|
+
"Would you like to track some of these new files?",
|
|
6100
|
+
true
|
|
6101
|
+
);
|
|
5916
6102
|
if (trackNewFiles) {
|
|
5917
6103
|
const selectOptions = newFiles.map((f) => ({
|
|
5918
6104
|
value: f.path,
|
|
5919
|
-
label: `${collapsePath(expandPath(f.path))}${f.sensitive ?
|
|
6105
|
+
label: `${collapsePath(expandPath(f.path))}${f.sensitive ? colors.yellow(" [sensitive]") : ""}`,
|
|
5920
6106
|
hint: f.category
|
|
5921
6107
|
}));
|
|
5922
6108
|
const nonSensitiveFiles = newFiles.filter((f) => !f.sensitive);
|
|
5923
6109
|
const initialValues = nonSensitiveFiles.map((f) => f.path);
|
|
5924
|
-
const selected = await prompts.multiselect(
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
{ initialValues }
|
|
5928
|
-
);
|
|
6110
|
+
const selected = await prompts.multiselect("Select files to track:", selectOptions, {
|
|
6111
|
+
initialValues
|
|
6112
|
+
});
|
|
5929
6113
|
filesToTrack = selected.map((path) => ({ path }));
|
|
5930
6114
|
}
|
|
5931
6115
|
}
|
|
@@ -5945,12 +6129,12 @@ var init_sync = __esm({
|
|
|
5945
6129
|
}
|
|
5946
6130
|
if (largeFiles.length > 0) {
|
|
5947
6131
|
console.log();
|
|
5948
|
-
console.log(
|
|
6132
|
+
console.log(colors.yellow("Large files detected:"));
|
|
5949
6133
|
for (const file of largeFiles) {
|
|
5950
|
-
console.log(
|
|
6134
|
+
console.log(colors.yellow(` ${file.path} (${file.size})`));
|
|
5951
6135
|
}
|
|
5952
6136
|
console.log();
|
|
5953
|
-
console.log(
|
|
6137
|
+
console.log(colors.dim("GitHub has a 50MB warning and 100MB hard limit."));
|
|
5954
6138
|
console.log();
|
|
5955
6139
|
const hasBlockers = largeFiles.some((f) => f.sizeBytes >= SIZE_BLOCK_THRESHOLD);
|
|
5956
6140
|
if (hasBlockers) {
|
|
@@ -5961,10 +6145,10 @@ var init_sync = __esm({
|
|
|
5961
6145
|
]);
|
|
5962
6146
|
if (action === "ignore") {
|
|
5963
6147
|
for (const file of largeFiles) {
|
|
5964
|
-
const fullPath = changes.find((
|
|
6148
|
+
const fullPath = changes.find((c2) => c2.path === file.path)?.source;
|
|
5965
6149
|
if (fullPath) {
|
|
5966
6150
|
await addToTuckignore(tuckDir, fullPath);
|
|
5967
|
-
const index = changes.findIndex((
|
|
6151
|
+
const index = changes.findIndex((c2) => c2.path === file.path);
|
|
5968
6152
|
if (index > -1) changes.splice(index, 1);
|
|
5969
6153
|
}
|
|
5970
6154
|
}
|
|
@@ -5985,10 +6169,10 @@ var init_sync = __esm({
|
|
|
5985
6169
|
]);
|
|
5986
6170
|
if (action === "ignore") {
|
|
5987
6171
|
for (const file of largeFiles) {
|
|
5988
|
-
const fullPath = changes.find((
|
|
6172
|
+
const fullPath = changes.find((c2) => c2.path === file.path)?.source;
|
|
5989
6173
|
if (fullPath) {
|
|
5990
6174
|
await addToTuckignore(tuckDir, fullPath);
|
|
5991
|
-
const index = changes.findIndex((
|
|
6175
|
+
const index = changes.findIndex((c2) => c2.path === file.path);
|
|
5992
6176
|
if (index > -1) changes.splice(index, 1);
|
|
5993
6177
|
}
|
|
5994
6178
|
}
|
|
@@ -6013,12 +6197,16 @@ var init_sync = __esm({
|
|
|
6013
6197
|
let result = { modified: [], deleted: [] };
|
|
6014
6198
|
if (changes.length > 0) {
|
|
6015
6199
|
const message = options.message || generateCommitMessage({
|
|
6016
|
-
modified: changes.filter((
|
|
6017
|
-
deleted: changes.filter((
|
|
6200
|
+
modified: changes.filter((c2) => c2.status === "modified").map((c2) => c2.path),
|
|
6201
|
+
deleted: changes.filter((c2) => c2.status === "deleted").map((c2) => c2.path)
|
|
6018
6202
|
});
|
|
6019
6203
|
console.log();
|
|
6020
|
-
console.log(
|
|
6021
|
-
console.log(
|
|
6204
|
+
console.log(colors.dim("Commit message:"));
|
|
6205
|
+
console.log(
|
|
6206
|
+
colors.cyan(
|
|
6207
|
+
message.split("\n").map((line) => ` ${line}`).join("\n")
|
|
6208
|
+
)
|
|
6209
|
+
);
|
|
6022
6210
|
console.log();
|
|
6023
6211
|
result = await syncFiles(tuckDir, changes, { ...options, message });
|
|
6024
6212
|
} else if (filesToTrack.length > 0) {
|
|
@@ -6043,21 +6231,21 @@ var init_sync = __esm({
|
|
|
6043
6231
|
}
|
|
6044
6232
|
};
|
|
6045
6233
|
pushWithSpinner = async (tuckDir, _options) => {
|
|
6046
|
-
const
|
|
6234
|
+
const spinner4 = prompts.spinner();
|
|
6047
6235
|
try {
|
|
6048
6236
|
const status = await getStatus(tuckDir);
|
|
6049
6237
|
const needsUpstream = !status.tracking;
|
|
6050
6238
|
const branch = status.branch;
|
|
6051
|
-
|
|
6239
|
+
spinner4.start("Pushing to remote...");
|
|
6052
6240
|
await push(tuckDir, {
|
|
6053
6241
|
setUpstream: needsUpstream,
|
|
6054
6242
|
branch: needsUpstream ? branch : void 0
|
|
6055
6243
|
});
|
|
6056
|
-
|
|
6244
|
+
spinner4.stop("Pushed to remote");
|
|
6057
6245
|
return true;
|
|
6058
6246
|
} catch (error) {
|
|
6059
6247
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
6060
|
-
|
|
6248
|
+
spinner4.stop(`Push failed: ${errorMsg}`);
|
|
6061
6249
|
prompts.log.warning("Run 'tuck push' to try again");
|
|
6062
6250
|
return false;
|
|
6063
6251
|
}
|
|
@@ -6090,7 +6278,7 @@ var init_sync = __esm({
|
|
|
6090
6278
|
if (!options.force) {
|
|
6091
6279
|
const scanningEnabled = await isSecretScanningEnabled(tuckDir);
|
|
6092
6280
|
if (scanningEnabled) {
|
|
6093
|
-
const modifiedPaths = changes.filter((
|
|
6281
|
+
const modifiedPaths = changes.filter((c2) => c2.status === "modified").map((c2) => expandPath(c2.source));
|
|
6094
6282
|
if (modifiedPaths.length > 0) {
|
|
6095
6283
|
const summary = await scanForSecrets(modifiedPaths, tuckDir);
|
|
6096
6284
|
if (summary.totalSecrets > 0) {
|
|
@@ -6124,7 +6312,9 @@ var init_sync = __esm({
|
|
|
6124
6312
|
}
|
|
6125
6313
|
}
|
|
6126
6314
|
};
|
|
6127
|
-
syncCommand = new Command4("sync").description(
|
|
6315
|
+
syncCommand = new Command4("sync").description(
|
|
6316
|
+
"Sync all dotfile changes (pull, detect changes, scan for new files, track, commit, push)"
|
|
6317
|
+
).argument("[message]", "Commit message").option("-m, --message <msg>", "Commit message").option("--no-commit", "Stage changes but don't commit").option("--no-push", "Commit but don't push to remote").option("--no-pull", "Don't pull from remote first").option("--no-scan", "Don't scan for new dotfiles").option("--no-hooks", "Skip execution of pre/post sync hooks").option("--trust-hooks", "Trust and run hooks without confirmation (use with caution)").option("-f, --force", "Skip secret scanning (not recommended)").action(async (messageArg, options) => {
|
|
6128
6318
|
await runSyncCommand(messageArg, options);
|
|
6129
6319
|
});
|
|
6130
6320
|
}
|
|
@@ -6132,7 +6322,7 @@ var init_sync = __esm({
|
|
|
6132
6322
|
|
|
6133
6323
|
// src/index.ts
|
|
6134
6324
|
import { Command as Command16 } from "commander";
|
|
6135
|
-
import
|
|
6325
|
+
import chalk5 from "chalk";
|
|
6136
6326
|
|
|
6137
6327
|
// src/commands/init.ts
|
|
6138
6328
|
init_ui();
|
|
@@ -6150,7 +6340,6 @@ import { Command as Command2 } from "commander";
|
|
|
6150
6340
|
import { join as join8 } from "path";
|
|
6151
6341
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
6152
6342
|
import { ensureDir as ensureDir4 } from "fs-extra";
|
|
6153
|
-
import chalk11 from "chalk";
|
|
6154
6343
|
import { copy as copy3 } from "fs-extra";
|
|
6155
6344
|
import { tmpdir } from "os";
|
|
6156
6345
|
import { readFile as readFile4, rm as rm2 } from "fs/promises";
|
|
@@ -6325,7 +6514,7 @@ var setupAlternativeAuth = async (tuckDir) => {
|
|
|
6325
6514
|
const diagnosis = await diagnoseAuthIssue();
|
|
6326
6515
|
prompts.log.warning(diagnosis.issue);
|
|
6327
6516
|
for (const suggestion of diagnosis.suggestions) {
|
|
6328
|
-
console.log(
|
|
6517
|
+
console.log(colors.muted(` ${suggestion}`));
|
|
6329
6518
|
}
|
|
6330
6519
|
console.log();
|
|
6331
6520
|
}
|
|
@@ -6395,7 +6584,7 @@ var setupAlternativeAuth = async (tuckDir) => {
|
|
|
6395
6584
|
if (sshInfo.publicKey) {
|
|
6396
6585
|
console.log();
|
|
6397
6586
|
prompts.log.info("Your public key (copy this to GitHub):");
|
|
6398
|
-
console.log(
|
|
6587
|
+
console.log(colors.brand(sshInfo.publicKey));
|
|
6399
6588
|
console.log();
|
|
6400
6589
|
}
|
|
6401
6590
|
}
|
|
@@ -6420,7 +6609,10 @@ var setupAlternativeAuth = async (tuckDir) => {
|
|
|
6420
6609
|
return { remoteUrl: null, pushed: false };
|
|
6421
6610
|
}
|
|
6422
6611
|
if (authMethod === "fine-grained" || authMethod === "classic") {
|
|
6423
|
-
return await setupTokenAuth(
|
|
6612
|
+
return await setupTokenAuth(
|
|
6613
|
+
tuckDir,
|
|
6614
|
+
authMethod === "fine-grained" ? "fine-grained" : "classic"
|
|
6615
|
+
);
|
|
6424
6616
|
}
|
|
6425
6617
|
return { remoteUrl: null, pushed: false };
|
|
6426
6618
|
};
|
|
@@ -6481,7 +6673,9 @@ var setupTokenAuth = async (tuckDir, preferredType) => {
|
|
|
6481
6673
|
const detectedType = detectTokenType(token);
|
|
6482
6674
|
const finalType = detectedType !== "unknown" ? detectedType : tokenType;
|
|
6483
6675
|
if (detectedType !== "unknown" && detectedType !== tokenType) {
|
|
6484
|
-
prompts.log.info(
|
|
6676
|
+
prompts.log.info(
|
|
6677
|
+
`Detected ${detectedType === "fine-grained" ? "fine-grained" : "classic"} token`
|
|
6678
|
+
);
|
|
6485
6679
|
}
|
|
6486
6680
|
const storeSpinner = prompts.spinner();
|
|
6487
6681
|
storeSpinner.start("Storing credentials securely...");
|
|
@@ -6491,7 +6685,9 @@ var setupTokenAuth = async (tuckDir, preferredType) => {
|
|
|
6491
6685
|
prompts.log.success("Authentication configured successfully");
|
|
6492
6686
|
} catch (error) {
|
|
6493
6687
|
storeSpinner.stop("Failed to store credentials");
|
|
6494
|
-
prompts.log.warning(
|
|
6688
|
+
prompts.log.warning(
|
|
6689
|
+
`Could not store credentials: ${error instanceof Error ? error.message : String(error)}`
|
|
6690
|
+
);
|
|
6495
6691
|
prompts.log.info("You may be prompted for credentials when pushing");
|
|
6496
6692
|
}
|
|
6497
6693
|
await configureGitCredentialHelper().catch((error) => {
|
|
@@ -6530,7 +6726,9 @@ var promptForManualRepoUrl = async (tuckDir, username, preferredProtocol = "http
|
|
|
6530
6726
|
prompts.log.success("Remote configured");
|
|
6531
6727
|
return { remoteUrl: repoUrl, pushed: false };
|
|
6532
6728
|
} catch (error) {
|
|
6533
|
-
prompts.log.error(
|
|
6729
|
+
prompts.log.error(
|
|
6730
|
+
`Failed to add remote: ${error instanceof Error ? error.message : String(error)}`
|
|
6731
|
+
);
|
|
6534
6732
|
return { remoteUrl: null, pushed: false };
|
|
6535
6733
|
}
|
|
6536
6734
|
};
|
|
@@ -6576,7 +6774,9 @@ var setupGitHubRepo = async (tuckDir) => {
|
|
|
6576
6774
|
return await setupAlternativeAuth(tuckDir);
|
|
6577
6775
|
}
|
|
6578
6776
|
} catch (error) {
|
|
6579
|
-
prompts.log.warning(
|
|
6777
|
+
prompts.log.warning(
|
|
6778
|
+
`Authentication failed: ${error instanceof Error ? error.message : String(error)}`
|
|
6779
|
+
);
|
|
6580
6780
|
const useAlt = await prompts.confirm("Try alternative authentication?", true);
|
|
6581
6781
|
if (useAlt) {
|
|
6582
6782
|
return await setupAlternativeAuth(tuckDir);
|
|
@@ -6607,16 +6807,18 @@ var setupGitHubRepo = async (tuckDir) => {
|
|
|
6607
6807
|
]);
|
|
6608
6808
|
let repo;
|
|
6609
6809
|
try {
|
|
6610
|
-
const
|
|
6611
|
-
|
|
6810
|
+
const spinner4 = prompts.spinner();
|
|
6811
|
+
spinner4.start(`Creating repository ${user.login}/${repoName}...`);
|
|
6612
6812
|
repo = await createRepo({
|
|
6613
6813
|
name: repoName,
|
|
6614
6814
|
description: "My dotfiles managed with tuck",
|
|
6615
6815
|
isPrivate: visibility === "private"
|
|
6616
6816
|
});
|
|
6617
|
-
|
|
6817
|
+
spinner4.stop(`Repository created: ${repo.fullName}`);
|
|
6618
6818
|
} catch (error) {
|
|
6619
|
-
prompts.log.error(
|
|
6819
|
+
prompts.log.error(
|
|
6820
|
+
`Failed to create repository: ${error instanceof Error ? error.message : String(error)}`
|
|
6821
|
+
);
|
|
6620
6822
|
return { remoteUrl: null, pushed: false };
|
|
6621
6823
|
}
|
|
6622
6824
|
const remoteUrl = await getPreferredRepoUrl(repo);
|
|
@@ -6625,14 +6827,14 @@ var setupGitHubRepo = async (tuckDir) => {
|
|
|
6625
6827
|
const shouldPush = await prompts.confirm("Push initial commit to GitHub?", true);
|
|
6626
6828
|
if (shouldPush) {
|
|
6627
6829
|
try {
|
|
6628
|
-
const
|
|
6629
|
-
|
|
6830
|
+
const spinner4 = prompts.spinner();
|
|
6831
|
+
spinner4.start("Creating initial commit...");
|
|
6630
6832
|
await stageAll(tuckDir);
|
|
6631
6833
|
await commit(tuckDir, "Initial commit: tuck dotfiles setup");
|
|
6632
|
-
|
|
6633
|
-
|
|
6834
|
+
spinner4.stop("Initial commit created");
|
|
6835
|
+
spinner4.start("Pushing to GitHub...");
|
|
6634
6836
|
await push(tuckDir, { remote: "origin", branch: "main", setUpstream: true });
|
|
6635
|
-
|
|
6837
|
+
spinner4.stop("Pushed to GitHub");
|
|
6636
6838
|
prompts.note(
|
|
6637
6839
|
`Your dotfiles are now at:
|
|
6638
6840
|
${repo.url}
|
|
@@ -6643,7 +6845,9 @@ tuck apply ${user.login}`,
|
|
|
6643
6845
|
);
|
|
6644
6846
|
return { remoteUrl, pushed: true };
|
|
6645
6847
|
} catch (error) {
|
|
6646
|
-
prompts.log.error(
|
|
6848
|
+
prompts.log.error(
|
|
6849
|
+
`Failed to push: ${error instanceof Error ? error.message : String(error)}`
|
|
6850
|
+
);
|
|
6647
6851
|
return { remoteUrl, pushed: false };
|
|
6648
6852
|
}
|
|
6649
6853
|
}
|
|
@@ -6685,7 +6889,9 @@ var analyzeRepository = async (repoDir) => {
|
|
|
6685
6889
|
const categories = await readdir5(filesDir);
|
|
6686
6890
|
for (const category of categories) {
|
|
6687
6891
|
const categoryPath = join8(filesDir, category);
|
|
6688
|
-
const categoryStats = await import("fs/promises").then(
|
|
6892
|
+
const categoryStats = await import("fs/promises").then(
|
|
6893
|
+
(fs) => fs.stat(categoryPath).catch(() => null)
|
|
6894
|
+
);
|
|
6689
6895
|
if (categoryStats?.isDirectory()) {
|
|
6690
6896
|
const files = await readdir5(categoryPath);
|
|
6691
6897
|
foundFiles.push(...files);
|
|
@@ -6698,7 +6904,7 @@ var analyzeRepository = async (repoDir) => {
|
|
|
6698
6904
|
try {
|
|
6699
6905
|
const rootFiles = await readdir4(repoDir);
|
|
6700
6906
|
for (const file of rootFiles) {
|
|
6701
|
-
if (commonPatterns.some((
|
|
6907
|
+
if (commonPatterns.some((p4) => file.includes(p4) || file.startsWith("."))) {
|
|
6702
6908
|
foundFiles.push(file);
|
|
6703
6909
|
}
|
|
6704
6910
|
}
|
|
@@ -6730,10 +6936,10 @@ var importExistingRepo = async (tuckDir, repoName, analysis, repoDir) => {
|
|
|
6730
6936
|
const remoteUrl = protocol === "ssh" ? `git@github.com:${repoName}.git` : `https://github.com/${repoName}.git`;
|
|
6731
6937
|
if (analysis.type === "valid-tuck") {
|
|
6732
6938
|
prompts.log.step("Importing tuck repository...");
|
|
6733
|
-
const
|
|
6734
|
-
|
|
6939
|
+
const spinner4 = prompts.spinner();
|
|
6940
|
+
spinner4.start("Copying repository...");
|
|
6735
6941
|
await copy3(repoDir, tuckDir, { overwrite: true });
|
|
6736
|
-
|
|
6942
|
+
spinner4.stop("Repository imported");
|
|
6737
6943
|
const fileCount = Object.keys(analysis.manifest.files).length;
|
|
6738
6944
|
const grouped = {};
|
|
6739
6945
|
for (const [_id, file] of Object.entries(analysis.manifest.files)) {
|
|
@@ -6746,7 +6952,11 @@ var importExistingRepo = async (tuckDir, repoName, analysis, repoDir) => {
|
|
|
6746
6952
|
const { DETECTION_CATEGORIES: DETECTION_CATEGORIES2 } = await Promise.resolve().then(() => (init_detect(), detect_exports));
|
|
6747
6953
|
for (const [category, files] of Object.entries(grouped)) {
|
|
6748
6954
|
const categoryInfo = DETECTION_CATEGORIES2[category] || { icon: "-", name: category };
|
|
6749
|
-
console.log(
|
|
6955
|
+
console.log(
|
|
6956
|
+
colors.brand(
|
|
6957
|
+
` ${categoryInfo.icon} ${categoryInfo.name}: ${files.length} file${files.length > 1 ? "s" : ""}`
|
|
6958
|
+
)
|
|
6959
|
+
);
|
|
6750
6960
|
}
|
|
6751
6961
|
console.log();
|
|
6752
6962
|
prompts.note(
|
|
@@ -6814,7 +7024,8 @@ var importExistingRepo = async (tuckDir, repoName, analysis, repoDir) => {
|
|
|
6814
7024
|
let count = 0;
|
|
6815
7025
|
const entries = await readdir4(dir);
|
|
6816
7026
|
for (const entry of entries) {
|
|
6817
|
-
if (entry === ".git" || entry === ".tuckmanifest.json" || entry === ".tuckrc.json")
|
|
7027
|
+
if (entry === ".git" || entry === ".tuckmanifest.json" || entry === ".tuckrc.json")
|
|
7028
|
+
continue;
|
|
6818
7029
|
const fullPath = join8(dir, entry);
|
|
6819
7030
|
const stats = await stat11(fullPath).catch(() => null);
|
|
6820
7031
|
if (stats?.isDirectory()) {
|
|
@@ -6868,7 +7079,9 @@ var importExistingRepo = async (tuckDir, repoName, analysis, repoDir) => {
|
|
|
6868
7079
|
await addRemote(tuckDir, "origin", remoteUrl);
|
|
6869
7080
|
if (action === "fresh") {
|
|
6870
7081
|
prompts.log.info("Tuck initialized. When you push, it will replace the repository contents.");
|
|
6871
|
-
prompts.log.info(
|
|
7082
|
+
prompts.log.info(
|
|
7083
|
+
"Run 'tuck add' to track files, then 'tuck sync && tuck push --force' to update remote"
|
|
7084
|
+
);
|
|
6872
7085
|
} else {
|
|
6873
7086
|
prompts.log.info("Tuck initialized with remote configured");
|
|
6874
7087
|
prompts.log.info("Run 'tuck add' to start tracking files");
|
|
@@ -6913,17 +7126,14 @@ var runInteractiveInit = async () => {
|
|
|
6913
7126
|
const ghInstalled = await isGhInstalled();
|
|
6914
7127
|
const ghAuth = ghInstalled && await isGhAuthenticated();
|
|
6915
7128
|
if (ghAuth) {
|
|
6916
|
-
const
|
|
6917
|
-
|
|
7129
|
+
const spinner4 = prompts.spinner();
|
|
7130
|
+
spinner4.start("Checking for existing dotfiles repository on GitHub...");
|
|
6918
7131
|
try {
|
|
6919
7132
|
const user = await getAuthenticatedUser();
|
|
6920
7133
|
const existingRepoName = await findDotfilesRepo(user.login);
|
|
6921
7134
|
if (existingRepoName) {
|
|
6922
|
-
|
|
6923
|
-
const importRepo = await prompts.confirm(
|
|
6924
|
-
`Import dotfiles from ${existingRepoName}?`,
|
|
6925
|
-
true
|
|
6926
|
-
);
|
|
7135
|
+
spinner4.stop(`Found repository: ${existingRepoName}`);
|
|
7136
|
+
const importRepo = await prompts.confirm(`Import dotfiles from ${existingRepoName}?`, true);
|
|
6927
7137
|
if (importRepo) {
|
|
6928
7138
|
const tempDir = join8(tmpdir(), `tuck-import-${Date.now()}`);
|
|
6929
7139
|
const cloneSpinner = prompts.spinner();
|
|
@@ -6954,7 +7164,9 @@ var runInteractiveInit = async () => {
|
|
|
6954
7164
|
if (result.filesApplied > 0) {
|
|
6955
7165
|
prompts.log.info(`Applied ${result.filesApplied} files to your system`);
|
|
6956
7166
|
} else if (result.filesInRepo > 0) {
|
|
6957
|
-
prompts.log.info(
|
|
7167
|
+
prompts.log.info(
|
|
7168
|
+
'Files are ready in ~/.tuck. Run "tuck restore" to apply them to your system'
|
|
7169
|
+
);
|
|
6958
7170
|
}
|
|
6959
7171
|
} else {
|
|
6960
7172
|
prompts.log.success(`Tuck initialized with ${existingRepoName} as remote`);
|
|
@@ -6978,9 +7190,7 @@ var runInteractiveInit = async () => {
|
|
|
6978
7190
|
} else if (phase === "importing") {
|
|
6979
7191
|
prompts.log.warning(errorMessage);
|
|
6980
7192
|
} else {
|
|
6981
|
-
prompts.log.warning(
|
|
6982
|
-
`Could not clone repository: ${errorMessage}`
|
|
6983
|
-
);
|
|
7193
|
+
prompts.log.warning(`Could not clone repository: ${errorMessage}`);
|
|
6984
7194
|
}
|
|
6985
7195
|
console.log();
|
|
6986
7196
|
const useAsRemoteAnyway = await prompts.confirm(
|
|
@@ -7014,10 +7224,10 @@ var runInteractiveInit = async () => {
|
|
|
7014
7224
|
skipExistingRepoQuestion = true;
|
|
7015
7225
|
}
|
|
7016
7226
|
} else {
|
|
7017
|
-
|
|
7227
|
+
spinner4.stop("No existing dotfiles repository found");
|
|
7018
7228
|
}
|
|
7019
7229
|
} catch {
|
|
7020
|
-
|
|
7230
|
+
spinner4.stop("Could not check for existing repositories");
|
|
7021
7231
|
}
|
|
7022
7232
|
}
|
|
7023
7233
|
if (!skipExistingRepoQuestion) {
|
|
@@ -7057,7 +7267,10 @@ var runInteractiveInit = async () => {
|
|
|
7057
7267
|
console.log();
|
|
7058
7268
|
}
|
|
7059
7269
|
if (!remoteUrl) {
|
|
7060
|
-
const wantsRemote = await prompts.confirm(
|
|
7270
|
+
const wantsRemote = await prompts.confirm(
|
|
7271
|
+
"Would you like to set up a remote repository?",
|
|
7272
|
+
true
|
|
7273
|
+
);
|
|
7061
7274
|
if (wantsRemote) {
|
|
7062
7275
|
const ghResult = await setupGitHubRepo(tuckDir);
|
|
7063
7276
|
remoteUrl = ghResult.remoteUrl;
|
|
@@ -7147,7 +7360,7 @@ Example URLs:
|
|
|
7147
7360
|
console.log();
|
|
7148
7361
|
prompts.log.warning(`Found ${sensitiveFiles.length} sensitive file(s):`);
|
|
7149
7362
|
for (const sf of sensitiveFiles) {
|
|
7150
|
-
console.log(
|
|
7363
|
+
console.log(colors.warning(` ! ${collapsePath(sf.path)} - ${sf.description || sf.category}`));
|
|
7151
7364
|
}
|
|
7152
7365
|
console.log();
|
|
7153
7366
|
const trackSensitive = await prompts.confirm(
|
|
@@ -7156,10 +7369,7 @@ Example URLs:
|
|
|
7156
7369
|
);
|
|
7157
7370
|
if (trackSensitive) {
|
|
7158
7371
|
for (const sf of sensitiveFiles) {
|
|
7159
|
-
const track = await prompts.confirm(
|
|
7160
|
-
`Track ${collapsePath(sf.path)}?`,
|
|
7161
|
-
false
|
|
7162
|
-
);
|
|
7372
|
+
const track = await prompts.confirm(`Track ${collapsePath(sf.path)}?`, false);
|
|
7163
7373
|
if (track) {
|
|
7164
7374
|
filesToTrack.push(sf.path);
|
|
7165
7375
|
}
|
|
@@ -7177,7 +7387,7 @@ Example URLs:
|
|
|
7177
7387
|
console.log();
|
|
7178
7388
|
prompts.log.warning(`Found ${sensitiveFiles.length} sensitive file(s):`);
|
|
7179
7389
|
for (const sf of sensitiveFiles) {
|
|
7180
|
-
console.log(
|
|
7390
|
+
console.log(colors.warning(` ! ${collapsePath(sf.path)} - ${sf.description || sf.category}`));
|
|
7181
7391
|
}
|
|
7182
7392
|
console.log();
|
|
7183
7393
|
const trackSensitive = await prompts.confirm(
|
|
@@ -7187,10 +7397,7 @@ Example URLs:
|
|
|
7187
7397
|
if (trackSensitive) {
|
|
7188
7398
|
const filesToTrack = [];
|
|
7189
7399
|
for (const sf of sensitiveFiles) {
|
|
7190
|
-
const track = await prompts.confirm(
|
|
7191
|
-
`Track ${collapsePath(sf.path)}?`,
|
|
7192
|
-
false
|
|
7193
|
-
);
|
|
7400
|
+
const track = await prompts.confirm(`Track ${collapsePath(sf.path)}?`, false);
|
|
7194
7401
|
if (track) {
|
|
7195
7402
|
filesToTrack.push(sf.path);
|
|
7196
7403
|
}
|
|
@@ -7320,7 +7527,6 @@ init_constants();
|
|
|
7320
7527
|
init_files();
|
|
7321
7528
|
import { Command as Command5 } from "commander";
|
|
7322
7529
|
import { basename as basename6 } from "path";
|
|
7323
|
-
import chalk14 from "chalk";
|
|
7324
7530
|
|
|
7325
7531
|
// src/lib/binary.ts
|
|
7326
7532
|
init_paths();
|
|
@@ -7533,13 +7739,10 @@ If you need to backup SSH keys, use a secure password manager.`
|
|
|
7533
7739
|
logger.warning(
|
|
7534
7740
|
`File ${path} is ${formatFileSize(sizeCheck.size)} (exceeds GitHub's 100MB limit)`
|
|
7535
7741
|
);
|
|
7536
|
-
const action = await prompts.select(
|
|
7537
|
-
"
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
{ value: "cancel", label: "Cancel operation" }
|
|
7541
|
-
]
|
|
7542
|
-
);
|
|
7742
|
+
const action = await prompts.select("How would you like to proceed?", [
|
|
7743
|
+
{ value: "ignore", label: "Add to .tuckignore and skip" },
|
|
7744
|
+
{ value: "cancel", label: "Cancel operation" }
|
|
7745
|
+
]);
|
|
7543
7746
|
if (action === "ignore") {
|
|
7544
7747
|
await addToTuckignore(tuckDir, collapsedPath);
|
|
7545
7748
|
logger.success(`Added ${path} to .tuckignore`);
|
|
@@ -7552,14 +7755,11 @@ If you need to backup SSH keys, use a secure password manager.`
|
|
|
7552
7755
|
logger.warning(
|
|
7553
7756
|
`File ${path} is ${formatFileSize(sizeCheck.size)}. GitHub recommends files under 50MB.`
|
|
7554
7757
|
);
|
|
7555
|
-
const action = await prompts.select(
|
|
7556
|
-
"
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
{ value: "cancel", label: "Cancel operation" }
|
|
7561
|
-
]
|
|
7562
|
-
);
|
|
7758
|
+
const action = await prompts.select("How would you like to proceed?", [
|
|
7759
|
+
{ value: "continue", label: "Track it anyway" },
|
|
7760
|
+
{ value: "ignore", label: "Add to .tuckignore and skip" },
|
|
7761
|
+
{ value: "cancel", label: "Cancel operation" }
|
|
7762
|
+
]);
|
|
7563
7763
|
if (action === "ignore") {
|
|
7564
7764
|
await addToTuckignore(tuckDir, collapsedPath);
|
|
7565
7765
|
logger.success(`Added ${path} to .tuckignore`);
|
|
@@ -7599,14 +7799,16 @@ var addFiles = async (filesToAdd, tuckDir, options) => {
|
|
|
7599
7799
|
};
|
|
7600
7800
|
var displaySecretWarning = (summary) => {
|
|
7601
7801
|
console.log();
|
|
7602
|
-
console.log(
|
|
7802
|
+
console.log(
|
|
7803
|
+
colors.error(colors.bold(` Security Warning: Found ${summary.totalSecrets} potential secret(s)`))
|
|
7804
|
+
);
|
|
7603
7805
|
console.log();
|
|
7604
7806
|
for (const result of summary.results) {
|
|
7605
|
-
console.log(` ${
|
|
7807
|
+
console.log(` ${colors.brand(result.collapsedPath)}`);
|
|
7606
7808
|
for (const match of result.matches) {
|
|
7607
|
-
const severityColor = match.severity === "critical" ?
|
|
7809
|
+
const severityColor = match.severity === "critical" ? colors.error : match.severity === "high" ? colors.warning : match.severity === "medium" ? colors.info : colors.muted;
|
|
7608
7810
|
console.log(
|
|
7609
|
-
` ${
|
|
7811
|
+
` ${colors.muted(`Line ${match.line}:`)} ${match.redactedValue} ${severityColor(`[${match.severity}]`)}`
|
|
7610
7812
|
);
|
|
7611
7813
|
}
|
|
7612
7814
|
console.log();
|
|
@@ -7678,7 +7880,7 @@ var handleSecretsDetected = async (summary, filesToAdd, tuckDir) => {
|
|
|
7678
7880
|
}
|
|
7679
7881
|
case "proceed": {
|
|
7680
7882
|
const confirmed = await prompts.confirm(
|
|
7681
|
-
|
|
7883
|
+
colors.error("Are you SURE you want to track files containing secrets?"),
|
|
7682
7884
|
false
|
|
7683
7885
|
);
|
|
7684
7886
|
if (!confirmed) {
|
|
@@ -7908,7 +8110,6 @@ init_manifest();
|
|
|
7908
8110
|
init_git();
|
|
7909
8111
|
init_errors();
|
|
7910
8112
|
import { Command as Command7 } from "commander";
|
|
7911
|
-
import chalk15 from "chalk";
|
|
7912
8113
|
var runInteractivePush = async (tuckDir) => {
|
|
7913
8114
|
prompts.intro("tuck push");
|
|
7914
8115
|
const hasRemoteRepo = await hasRemote(tuckDir);
|
|
@@ -7937,13 +8138,13 @@ var runInteractivePush = async (tuckDir) => {
|
|
|
7937
8138
|
return;
|
|
7938
8139
|
}
|
|
7939
8140
|
console.log();
|
|
7940
|
-
console.log(
|
|
7941
|
-
console.log(
|
|
8141
|
+
console.log(colors.dim("Remote:"), remoteUrl);
|
|
8142
|
+
console.log(colors.dim("Branch:"), branch);
|
|
7942
8143
|
if (status.ahead > 0) {
|
|
7943
|
-
console.log(
|
|
8144
|
+
console.log(colors.dim("Commits:"), colors.green(`\u2191 ${status.ahead} to push`));
|
|
7944
8145
|
}
|
|
7945
8146
|
if (status.behind > 0) {
|
|
7946
|
-
console.log(
|
|
8147
|
+
console.log(colors.dim("Warning:"), colors.yellow(`\u2193 ${status.behind} commits behind remote`));
|
|
7947
8148
|
const pullFirst = await prompts.confirm("Pull changes first?", true);
|
|
7948
8149
|
if (pullFirst) {
|
|
7949
8150
|
prompts.log.info("Run 'tuck pull' first, then push");
|
|
@@ -7989,7 +8190,7 @@ var runInteractivePush = async (tuckDir) => {
|
|
|
7989
8190
|
viewUrl = remoteUrl.replace("git@github.com:", "https://github.com/").replace(".git", "");
|
|
7990
8191
|
}
|
|
7991
8192
|
console.log();
|
|
7992
|
-
console.log(
|
|
8193
|
+
console.log(colors.dim("View at:"), colors.cyan(viewUrl));
|
|
7993
8194
|
}
|
|
7994
8195
|
prompts.outro("");
|
|
7995
8196
|
};
|
|
@@ -8042,7 +8243,6 @@ init_manifest();
|
|
|
8042
8243
|
init_git();
|
|
8043
8244
|
init_errors();
|
|
8044
8245
|
import { Command as Command8 } from "commander";
|
|
8045
|
-
import chalk16 from "chalk";
|
|
8046
8246
|
var runInteractivePull = async (tuckDir) => {
|
|
8047
8247
|
prompts.intro("tuck pull");
|
|
8048
8248
|
const hasRemoteRepo = await hasRemote(tuckDir);
|
|
@@ -8058,23 +8258,23 @@ var runInteractivePull = async (tuckDir) => {
|
|
|
8058
8258
|
const branch = await getCurrentBranch(tuckDir);
|
|
8059
8259
|
const remoteUrl = await getRemoteUrl(tuckDir);
|
|
8060
8260
|
console.log();
|
|
8061
|
-
console.log(
|
|
8062
|
-
console.log(
|
|
8261
|
+
console.log(colors.dim("Remote:"), remoteUrl);
|
|
8262
|
+
console.log(colors.dim("Branch:"), branch);
|
|
8063
8263
|
if (status.behind === 0) {
|
|
8064
8264
|
prompts.log.success("Already up to date");
|
|
8065
8265
|
return;
|
|
8066
8266
|
}
|
|
8067
|
-
console.log(
|
|
8267
|
+
console.log(colors.dim("Commits:"), colors.yellow(`\u2193 ${status.behind} to pull`));
|
|
8068
8268
|
if (status.ahead > 0) {
|
|
8069
8269
|
console.log(
|
|
8070
|
-
|
|
8071
|
-
|
|
8270
|
+
colors.dim("Note:"),
|
|
8271
|
+
colors.yellow(`You also have ${status.ahead} local commit${status.ahead > 1 ? "s" : ""} to push`)
|
|
8072
8272
|
);
|
|
8073
8273
|
}
|
|
8074
8274
|
if (status.modified.length > 0 || status.staged.length > 0) {
|
|
8075
8275
|
console.log();
|
|
8076
8276
|
prompts.log.warning("You have uncommitted changes");
|
|
8077
|
-
console.log(
|
|
8277
|
+
console.log(colors.dim("Modified:"), status.modified.join(", "));
|
|
8078
8278
|
const continueAnyway = await prompts.confirm("Pull anyway? (may cause merge conflicts)");
|
|
8079
8279
|
if (!continueAnyway) {
|
|
8080
8280
|
prompts.cancel("Commit or stash your changes first with 'tuck sync'");
|
|
@@ -8128,6 +8328,7 @@ init_restore();
|
|
|
8128
8328
|
|
|
8129
8329
|
// src/commands/status.ts
|
|
8130
8330
|
init_ui();
|
|
8331
|
+
init_prompts();
|
|
8131
8332
|
init_paths();
|
|
8132
8333
|
init_manifest();
|
|
8133
8334
|
init_git();
|
|
@@ -8136,8 +8337,9 @@ init_tuckignore();
|
|
|
8136
8337
|
init_errors();
|
|
8137
8338
|
init_constants();
|
|
8138
8339
|
import { Command as Command9 } from "commander";
|
|
8139
|
-
import chalk17 from "chalk";
|
|
8140
8340
|
import boxen2 from "boxen";
|
|
8341
|
+
import logSymbols5 from "log-symbols";
|
|
8342
|
+
import figures5 from "figures";
|
|
8141
8343
|
var detectFileChanges = async (tuckDir) => {
|
|
8142
8344
|
const files = await getAllTrackedFiles(tuckDir);
|
|
8143
8345
|
const ignoredPaths = await loadTuckignore(tuckDir);
|
|
@@ -8217,44 +8419,50 @@ var getFullStatus = async (tuckDir) => {
|
|
|
8217
8419
|
}
|
|
8218
8420
|
};
|
|
8219
8421
|
};
|
|
8422
|
+
var formatRemoteUrl = (url) => {
|
|
8423
|
+
return url.replace(/^https?:\/\//, "").replace(/\.git$/, "").replace(/^github\.com\//, "");
|
|
8424
|
+
};
|
|
8220
8425
|
var printStatus = (status) => {
|
|
8221
8426
|
const headerLines = [
|
|
8222
|
-
`${
|
|
8427
|
+
`${colors.brandBold("tuck")} ${colors.muted(`v${VERSION}`)}`,
|
|
8223
8428
|
"",
|
|
8224
|
-
`${
|
|
8225
|
-
`${
|
|
8429
|
+
`${colors.muted("Repository:")} ${collapsePath(status.tuckDir)}`,
|
|
8430
|
+
`${colors.muted("Branch:")} ${colors.brand(status.branch)}`
|
|
8226
8431
|
];
|
|
8227
8432
|
if (status.remote) {
|
|
8228
|
-
|
|
8229
|
-
headerLines.push(`${chalk17.dim("Remote:")} ${shortRemote}`);
|
|
8433
|
+
headerLines.push(`${colors.muted("Remote:")} ${formatRemoteUrl(status.remote)}`);
|
|
8230
8434
|
} else {
|
|
8231
|
-
headerLines.push(`${
|
|
8435
|
+
headerLines.push(`${colors.muted("Remote:")} ${colors.warning("not configured")}`);
|
|
8232
8436
|
}
|
|
8233
|
-
console.log(boxen2(headerLines.join("\n"),
|
|
8234
|
-
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
8235
|
-
borderColor: "cyan",
|
|
8236
|
-
borderStyle: "round"
|
|
8237
|
-
}));
|
|
8437
|
+
console.log(boxen2(headerLines.join("\n"), boxStyles.header));
|
|
8238
8438
|
if (status.remote) {
|
|
8239
|
-
|
|
8439
|
+
console.log();
|
|
8240
8440
|
switch (status.remoteStatus) {
|
|
8241
8441
|
case "up-to-date":
|
|
8242
|
-
|
|
8442
|
+
console.log(logSymbols5.success, colors.success("Up to date with remote"));
|
|
8243
8443
|
break;
|
|
8244
8444
|
case "ahead":
|
|
8245
|
-
|
|
8445
|
+
console.log(
|
|
8446
|
+
colors.warning(figures5.arrowUp),
|
|
8447
|
+
colors.warning(`${status.ahead} commit${status.ahead > 1 ? "s" : ""} ahead`)
|
|
8448
|
+
);
|
|
8246
8449
|
break;
|
|
8247
8450
|
case "behind":
|
|
8248
|
-
|
|
8451
|
+
console.log(
|
|
8452
|
+
colors.warning(figures5.arrowDown),
|
|
8453
|
+
colors.warning(`${status.behind} commit${status.behind > 1 ? "s" : ""} behind`)
|
|
8454
|
+
);
|
|
8249
8455
|
break;
|
|
8250
8456
|
case "diverged":
|
|
8251
|
-
|
|
8457
|
+
console.log(
|
|
8458
|
+
logSymbols5.warning,
|
|
8459
|
+
colors.error(`Diverged (${status.ahead} ahead, ${status.behind} behind)`)
|
|
8460
|
+
);
|
|
8252
8461
|
break;
|
|
8253
8462
|
}
|
|
8254
|
-
console.log("\n" + remoteInfo);
|
|
8255
8463
|
}
|
|
8256
8464
|
console.log();
|
|
8257
|
-
console.log(
|
|
8465
|
+
console.log(colors.bold(`${status.trackedCount} files tracked`));
|
|
8258
8466
|
const categoryOrder = ["shell", "git", "editors", "terminal", "ssh", "misc"];
|
|
8259
8467
|
const sortedCategories = Object.keys(status.categoryCounts).sort((a, b) => {
|
|
8260
8468
|
const aIdx = categoryOrder.indexOf(a);
|
|
@@ -8265,66 +8473,74 @@ var printStatus = (status) => {
|
|
|
8265
8473
|
return aIdx - bIdx;
|
|
8266
8474
|
});
|
|
8267
8475
|
if (sortedCategories.length > 0) {
|
|
8268
|
-
|
|
8269
|
-
const
|
|
8270
|
-
|
|
8271
|
-
|
|
8476
|
+
const categoryLine = sortedCategories.map((cat) => {
|
|
8477
|
+
const style = categoryStyles[cat] || categoryStyles.misc;
|
|
8478
|
+
const count = status.categoryCounts[cat];
|
|
8479
|
+
return `${style.color(style.icon)} ${cat}: ${count}`;
|
|
8480
|
+
}).join(" ");
|
|
8481
|
+
console.log(colors.muted(indent() + categoryLine));
|
|
8272
8482
|
}
|
|
8273
8483
|
if (status.changes.length > 0) {
|
|
8274
8484
|
console.log();
|
|
8275
|
-
console.log(
|
|
8485
|
+
console.log(colors.bold("Changes:"));
|
|
8276
8486
|
for (const change of status.changes) {
|
|
8277
8487
|
const statusText = formatStatus(change.status);
|
|
8278
|
-
console.log(
|
|
8488
|
+
console.log(`${indent()}${statusText} ${colors.brand(change.path)}`);
|
|
8279
8489
|
}
|
|
8280
8490
|
}
|
|
8281
8491
|
const hasGitChanges = status.gitChanges.staged.length > 0 || status.gitChanges.modified.length > 0 || status.gitChanges.untracked.length > 0;
|
|
8282
8492
|
if (hasGitChanges) {
|
|
8283
8493
|
console.log();
|
|
8284
|
-
console.log(
|
|
8494
|
+
console.log(colors.bold("Repository:"));
|
|
8285
8495
|
if (status.gitChanges.staged.length > 0) {
|
|
8286
|
-
console.log(
|
|
8287
|
-
status.gitChanges.staged.forEach(
|
|
8496
|
+
console.log(colors.success(`${indent()}Staged:`));
|
|
8497
|
+
status.gitChanges.staged.forEach(
|
|
8498
|
+
(f) => console.log(colors.success(`${indent()}${indent()}+ ${f}`))
|
|
8499
|
+
);
|
|
8288
8500
|
}
|
|
8289
8501
|
if (status.gitChanges.modified.length > 0) {
|
|
8290
|
-
console.log(
|
|
8291
|
-
status.gitChanges.modified.forEach(
|
|
8502
|
+
console.log(colors.warning(`${indent()}Modified:`));
|
|
8503
|
+
status.gitChanges.modified.forEach(
|
|
8504
|
+
(f) => console.log(colors.warning(`${indent()}${indent()}~ ${f}`))
|
|
8505
|
+
);
|
|
8292
8506
|
}
|
|
8293
8507
|
if (status.gitChanges.untracked.length > 0) {
|
|
8294
|
-
console.log(
|
|
8295
|
-
status.gitChanges.untracked.forEach(
|
|
8508
|
+
console.log(colors.muted(`${indent()}Untracked:`));
|
|
8509
|
+
status.gitChanges.untracked.forEach(
|
|
8510
|
+
(f) => console.log(colors.muted(`${indent()}${indent()}? ${f}`))
|
|
8511
|
+
);
|
|
8296
8512
|
}
|
|
8297
8513
|
}
|
|
8298
8514
|
console.log();
|
|
8299
8515
|
if (status.changes.length > 0) {
|
|
8300
|
-
prompts.note("Run 'tuck sync' to commit changes", "Next
|
|
8516
|
+
prompts.note("Run 'tuck sync' to commit changes", "Next");
|
|
8301
8517
|
} else if (status.remoteStatus === "ahead") {
|
|
8302
|
-
prompts.note("Run 'tuck push' to push changes
|
|
8518
|
+
prompts.note("Run 'tuck push' to push changes", "Next");
|
|
8303
8519
|
} else if (status.remoteStatus === "behind") {
|
|
8304
|
-
prompts.note("Run 'tuck pull' to pull changes
|
|
8520
|
+
prompts.note("Run 'tuck pull' to pull changes", "Next");
|
|
8305
8521
|
} else if (status.trackedCount === 0) {
|
|
8306
|
-
prompts.note("Run 'tuck add <path>' to start tracking
|
|
8522
|
+
prompts.note("Run 'tuck add <path>' to start tracking", "Next");
|
|
8307
8523
|
} else {
|
|
8308
|
-
prompts.outro("Everything
|
|
8524
|
+
prompts.outro("Everything up to date");
|
|
8309
8525
|
}
|
|
8310
8526
|
};
|
|
8311
8527
|
var printShortStatus = (status) => {
|
|
8312
8528
|
const parts = [];
|
|
8313
8529
|
parts.push(`[${status.branch}]`);
|
|
8314
8530
|
if (status.remoteStatus === "ahead") {
|
|
8315
|
-
parts.push(
|
|
8531
|
+
parts.push(colors.warning(`${figures5.arrowUp}${status.ahead}`));
|
|
8316
8532
|
} else if (status.remoteStatus === "behind") {
|
|
8317
|
-
parts.push(
|
|
8533
|
+
parts.push(colors.warning(`${figures5.arrowDown}${status.behind}`));
|
|
8318
8534
|
} else if (status.remoteStatus === "diverged") {
|
|
8319
|
-
parts.push(
|
|
8535
|
+
parts.push(colors.error(`${figures5.arrowUp}${status.ahead}${figures5.arrowDown}${status.behind}`));
|
|
8320
8536
|
}
|
|
8321
8537
|
if (status.changes.length > 0) {
|
|
8322
|
-
const modified = status.changes.filter((
|
|
8323
|
-
const deleted = status.changes.filter((
|
|
8324
|
-
if (modified > 0) parts.push(`~${modified}`);
|
|
8325
|
-
if (deleted > 0) parts.push(`-${deleted}`);
|
|
8538
|
+
const modified = status.changes.filter((ch) => ch.status === "modified").length;
|
|
8539
|
+
const deleted = status.changes.filter((ch) => ch.status === "deleted").length;
|
|
8540
|
+
if (modified > 0) parts.push(colors.warning(`~${modified}`));
|
|
8541
|
+
if (deleted > 0) parts.push(colors.error(`-${deleted}`));
|
|
8326
8542
|
}
|
|
8327
|
-
parts.push(`(${status.trackedCount} tracked)`);
|
|
8543
|
+
parts.push(colors.muted(`(${status.trackedCount} tracked)`));
|
|
8328
8544
|
console.log(parts.join(" "));
|
|
8329
8545
|
};
|
|
8330
8546
|
var printJsonStatus = (status) => {
|
|
@@ -8357,7 +8573,6 @@ init_manifest();
|
|
|
8357
8573
|
init_errors();
|
|
8358
8574
|
init_constants();
|
|
8359
8575
|
import { Command as Command10 } from "commander";
|
|
8360
|
-
import chalk18 from "chalk";
|
|
8361
8576
|
var groupByCategory2 = async (tuckDir) => {
|
|
8362
8577
|
const files = await getAllTrackedFiles(tuckDir);
|
|
8363
8578
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -8396,15 +8611,15 @@ var printList = (groups) => {
|
|
|
8396
8611
|
totalFiles += fileCount;
|
|
8397
8612
|
console.log();
|
|
8398
8613
|
console.log(
|
|
8399
|
-
|
|
8614
|
+
colors.bold(`${group2.icon} ${group2.name}`) + colors.dim(` (${formatCount(fileCount, "file")})`)
|
|
8400
8615
|
);
|
|
8401
8616
|
group2.files.forEach((file, index) => {
|
|
8402
8617
|
const isLast = index === group2.files.length - 1;
|
|
8403
8618
|
const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
8404
8619
|
const name = file.source.split("/").pop() || file.source;
|
|
8405
|
-
const arrow =
|
|
8406
|
-
const dest =
|
|
8407
|
-
console.log(
|
|
8620
|
+
const arrow = colors.dim(" \u2192 ");
|
|
8621
|
+
const dest = colors.dim(file.source);
|
|
8622
|
+
console.log(colors.dim(prefix) + colors.cyan(name) + arrow + dest);
|
|
8408
8623
|
});
|
|
8409
8624
|
}
|
|
8410
8625
|
console.log();
|
|
@@ -8465,9 +8680,15 @@ init_git();
|
|
|
8465
8680
|
init_files();
|
|
8466
8681
|
init_errors();
|
|
8467
8682
|
import { Command as Command11 } from "commander";
|
|
8468
|
-
import chalk19 from "chalk";
|
|
8469
8683
|
import { join as join14 } from "path";
|
|
8684
|
+
init_tuckignore();
|
|
8470
8685
|
import { readFile as readFile9 } from "fs/promises";
|
|
8686
|
+
var isBinary = async (path) => {
|
|
8687
|
+
if (!await pathExists(path)) {
|
|
8688
|
+
return false;
|
|
8689
|
+
}
|
|
8690
|
+
return await isBinaryExecutable(path);
|
|
8691
|
+
};
|
|
8471
8692
|
var getFileDiff = async (tuckDir, source) => {
|
|
8472
8693
|
const tracked = await getTrackedFileBySource(tuckDir, source);
|
|
8473
8694
|
if (!tracked) {
|
|
@@ -8483,15 +8704,60 @@ var getFileDiff = async (tuckDir, source) => {
|
|
|
8483
8704
|
if (!await pathExists(systemPath)) {
|
|
8484
8705
|
diff.hasChanges = true;
|
|
8485
8706
|
if (await pathExists(repoPath)) {
|
|
8486
|
-
|
|
8707
|
+
const repoContent = await readFile9(repoPath, "utf-8");
|
|
8708
|
+
diff.repoContent = repoContent;
|
|
8709
|
+
diff.repoSize = repoContent.length;
|
|
8487
8710
|
}
|
|
8488
8711
|
return diff;
|
|
8489
8712
|
}
|
|
8490
8713
|
if (!await pathExists(repoPath)) {
|
|
8491
8714
|
diff.hasChanges = true;
|
|
8492
|
-
|
|
8715
|
+
const systemContent = await readFile9(systemPath, "utf-8");
|
|
8716
|
+
diff.systemContent = systemContent;
|
|
8717
|
+
diff.systemSize = systemContent.length;
|
|
8493
8718
|
return diff;
|
|
8494
8719
|
}
|
|
8720
|
+
const systemIsBinary = await isBinary(systemPath);
|
|
8721
|
+
const repoIsBinary = await isBinary(repoPath);
|
|
8722
|
+
if (systemIsBinary || repoIsBinary) {
|
|
8723
|
+
diff.isBinary = true;
|
|
8724
|
+
try {
|
|
8725
|
+
const systemBuffer = await readFile9(systemPath);
|
|
8726
|
+
diff.systemSize = systemBuffer.length;
|
|
8727
|
+
} catch {
|
|
8728
|
+
}
|
|
8729
|
+
try {
|
|
8730
|
+
const repoBuffer = await readFile9(repoPath);
|
|
8731
|
+
diff.repoSize = repoBuffer.length;
|
|
8732
|
+
} catch {
|
|
8733
|
+
}
|
|
8734
|
+
const systemChecksum2 = await getFileChecksum(systemPath);
|
|
8735
|
+
const repoChecksum2 = await getFileChecksum(repoPath);
|
|
8736
|
+
diff.hasChanges = systemChecksum2 !== repoChecksum2;
|
|
8737
|
+
return diff;
|
|
8738
|
+
}
|
|
8739
|
+
const systemIsDir = await isDirectory(systemPath);
|
|
8740
|
+
const repoIsDir = await isDirectory(repoPath);
|
|
8741
|
+
if (systemIsDir || repoIsDir) {
|
|
8742
|
+
diff.isDirectory = true;
|
|
8743
|
+
diff.hasChanges = true;
|
|
8744
|
+
if (systemIsDir) {
|
|
8745
|
+
const files = await getDirectoryFiles(systemPath);
|
|
8746
|
+
diff.fileCount = files.length;
|
|
8747
|
+
}
|
|
8748
|
+
if (repoIsDir) {
|
|
8749
|
+
const files = await getDirectoryFiles(repoPath);
|
|
8750
|
+
diff.fileCount = (diff.fileCount || 0) + files.length;
|
|
8751
|
+
}
|
|
8752
|
+
return diff;
|
|
8753
|
+
}
|
|
8754
|
+
try {
|
|
8755
|
+
const systemSizeCheck = await checkFileSizeThreshold(systemPath);
|
|
8756
|
+
const repoSizeCheck = await checkFileSizeThreshold(repoPath);
|
|
8757
|
+
diff.systemSize = systemSizeCheck.size;
|
|
8758
|
+
diff.repoSize = repoSizeCheck.size;
|
|
8759
|
+
} catch {
|
|
8760
|
+
}
|
|
8495
8761
|
const systemChecksum = await getFileChecksum(systemPath);
|
|
8496
8762
|
const repoChecksum = await getFileChecksum(repoPath);
|
|
8497
8763
|
if (systemChecksum !== repoChecksum) {
|
|
@@ -8501,28 +8767,45 @@ var getFileDiff = async (tuckDir, source) => {
|
|
|
8501
8767
|
}
|
|
8502
8768
|
return diff;
|
|
8503
8769
|
};
|
|
8504
|
-
var formatUnifiedDiff = (
|
|
8770
|
+
var formatUnifiedDiff = (diff) => {
|
|
8505
8771
|
const lines = [];
|
|
8506
|
-
lines.push(
|
|
8507
|
-
lines.push(
|
|
8772
|
+
lines.push(colors.bold(`--- a/${diff.source} (system)`));
|
|
8773
|
+
lines.push(colors.bold(`+++ b/${diff.source} (repository)`));
|
|
8774
|
+
if (diff.isBinary) {
|
|
8775
|
+
const sysSize = diff.systemSize ? formatFileSize(diff.systemSize) : "0 B";
|
|
8776
|
+
const repoSize = diff.repoSize ? formatFileSize(diff.repoSize) : "0 B";
|
|
8777
|
+
lines.push(colors.dim("Binary files differ"));
|
|
8778
|
+
lines.push(colors.dim(` System: ${sysSize}`));
|
|
8779
|
+
lines.push(colors.dim(` Repo: ${repoSize}`));
|
|
8780
|
+
return lines.join("\n");
|
|
8781
|
+
}
|
|
8782
|
+
if (diff.isDirectory) {
|
|
8783
|
+
const fileCount = diff.fileCount || 0;
|
|
8784
|
+
lines.push(colors.dim("Directory content changed"));
|
|
8785
|
+
lines.push(colors.dim(` Contains ${fileCount} file${fileCount > 1 ? "s" : ""}`));
|
|
8786
|
+
return lines.join("\n");
|
|
8787
|
+
}
|
|
8788
|
+
const { systemContent, repoContent } = diff;
|
|
8508
8789
|
if (!systemContent && repoContent) {
|
|
8509
|
-
lines.push(
|
|
8510
|
-
lines.push(
|
|
8790
|
+
lines.push(colors.red("File missing on system"));
|
|
8791
|
+
lines.push(colors.dim("Repository content:"));
|
|
8511
8792
|
repoContent.split("\n").forEach((line) => {
|
|
8512
|
-
lines.push(
|
|
8793
|
+
lines.push(colors.green(`+ ${line}`));
|
|
8513
8794
|
});
|
|
8514
8795
|
} else if (systemContent && !repoContent) {
|
|
8515
|
-
lines.push(
|
|
8516
|
-
lines.push(
|
|
8796
|
+
lines.push(colors.yellow("File not yet synced to repository"));
|
|
8797
|
+
lines.push(colors.dim("System content:"));
|
|
8517
8798
|
systemContent.split("\n").forEach((line) => {
|
|
8518
|
-
lines.push(
|
|
8799
|
+
lines.push(colors.red(`- ${line}`));
|
|
8519
8800
|
});
|
|
8520
8801
|
} else if (systemContent && repoContent) {
|
|
8802
|
+
const CONTEXT_LINES = 3;
|
|
8521
8803
|
const systemLines = systemContent.split("\n");
|
|
8522
8804
|
const repoLines = repoContent.split("\n");
|
|
8523
8805
|
const maxLines = Math.max(systemLines.length, repoLines.length);
|
|
8524
8806
|
let inDiff = false;
|
|
8525
8807
|
let diffStart = 0;
|
|
8808
|
+
let contextCount = 0;
|
|
8526
8809
|
for (let i = 0; i < maxLines; i++) {
|
|
8527
8810
|
const sysLine = systemLines[i];
|
|
8528
8811
|
const repoLine = repoLines[i];
|
|
@@ -8530,17 +8813,25 @@ var formatUnifiedDiff = (source, systemContent, repoContent) => {
|
|
|
8530
8813
|
if (!inDiff) {
|
|
8531
8814
|
inDiff = true;
|
|
8532
8815
|
diffStart = i;
|
|
8533
|
-
|
|
8816
|
+
const startLine = Math.max(0, diffStart - CONTEXT_LINES + 1);
|
|
8817
|
+
const endLine = Math.min(maxLines, diffStart + CONTEXT_LINES);
|
|
8818
|
+
lines.push(
|
|
8819
|
+
colors.cyan(
|
|
8820
|
+
`@@ -${startLine + 1},${endLine - startLine} +${startLine + 1},${endLine - startLine} @@`
|
|
8821
|
+
)
|
|
8822
|
+
);
|
|
8534
8823
|
}
|
|
8535
8824
|
if (sysLine !== void 0) {
|
|
8536
|
-
lines.push(
|
|
8825
|
+
lines.push(colors.red(`- ${sysLine}`));
|
|
8537
8826
|
}
|
|
8538
8827
|
if (repoLine !== void 0) {
|
|
8539
|
-
lines.push(
|
|
8828
|
+
lines.push(colors.green(`+ ${repoLine}`));
|
|
8540
8829
|
}
|
|
8830
|
+
contextCount = 0;
|
|
8541
8831
|
} else if (inDiff) {
|
|
8542
|
-
lines.push(
|
|
8543
|
-
|
|
8832
|
+
lines.push(colors.dim(` ${sysLine || ""}`));
|
|
8833
|
+
contextCount++;
|
|
8834
|
+
if (contextCount > CONTEXT_LINES * 2) {
|
|
8544
8835
|
inDiff = false;
|
|
8545
8836
|
}
|
|
8546
8837
|
}
|
|
@@ -8564,54 +8855,77 @@ var runDiff = async (paths, options) => {
|
|
|
8564
8855
|
}
|
|
8565
8856
|
return;
|
|
8566
8857
|
}
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8858
|
+
const allFiles = await getAllTrackedFiles(tuckDir);
|
|
8859
|
+
const changedFiles = [];
|
|
8860
|
+
const filesToCheck = paths.length === 0 ? Object.values(allFiles) : paths.map((path) => {
|
|
8861
|
+
const expandedPath = expandPath(path);
|
|
8862
|
+
const collapsedPath = collapsePath(expandedPath);
|
|
8863
|
+
const tracked = Object.entries(allFiles).find(([, f]) => f.source === collapsedPath);
|
|
8864
|
+
if (!tracked) {
|
|
8865
|
+
throw new FileNotFoundError(`Not tracked: ${path}`);
|
|
8866
|
+
}
|
|
8867
|
+
return tracked[1];
|
|
8868
|
+
});
|
|
8869
|
+
for (const file of filesToCheck) {
|
|
8870
|
+
if (options.category && file.category !== options.category) {
|
|
8871
|
+
continue;
|
|
8872
|
+
}
|
|
8873
|
+
if (await isIgnored(tuckDir, file.source)) {
|
|
8874
|
+
continue;
|
|
8875
|
+
}
|
|
8876
|
+
try {
|
|
8571
8877
|
const diff = await getFileDiff(tuckDir, file.source);
|
|
8572
|
-
if (diff.hasChanges) {
|
|
8878
|
+
if (diff && diff.hasChanges) {
|
|
8573
8879
|
changedFiles.push(diff);
|
|
8574
8880
|
}
|
|
8881
|
+
} catch (error) {
|
|
8882
|
+
if (error instanceof FileNotFoundError) {
|
|
8883
|
+
logger.warning(`File not found: ${file.source}`);
|
|
8884
|
+
} else if (error instanceof PermissionError) {
|
|
8885
|
+
logger.warning(`Permission denied: ${file.source}`);
|
|
8886
|
+
} else {
|
|
8887
|
+
throw error;
|
|
8888
|
+
}
|
|
8575
8889
|
}
|
|
8576
|
-
|
|
8890
|
+
}
|
|
8891
|
+
if (changedFiles.length === 0) {
|
|
8892
|
+
if (paths.length > 0) {
|
|
8577
8893
|
logger.success("No differences found");
|
|
8578
|
-
|
|
8579
|
-
}
|
|
8580
|
-
if (options.stat) {
|
|
8894
|
+
} else {
|
|
8581
8895
|
prompts.intro("tuck diff");
|
|
8582
8896
|
console.log();
|
|
8583
|
-
|
|
8584
|
-
console.log();
|
|
8585
|
-
for (const diff of changedFiles) {
|
|
8586
|
-
console.log(chalk19.yellow(` ~ ${diff.source}`));
|
|
8587
|
-
}
|
|
8897
|
+
logger.success("No differences found");
|
|
8588
8898
|
console.log();
|
|
8589
|
-
return;
|
|
8590
8899
|
}
|
|
8900
|
+
return;
|
|
8901
|
+
}
|
|
8902
|
+
prompts.intro("tuck diff");
|
|
8903
|
+
console.log();
|
|
8904
|
+
if (options.stat || options.nameOnly) {
|
|
8905
|
+
const label = options.nameOnly ? "Changed files:" : `${changedFiles.length} file${changedFiles.length > 1 ? "s" : ""} changed:`;
|
|
8906
|
+
console.log(colors.bold(label));
|
|
8907
|
+
console.log();
|
|
8591
8908
|
for (const diff of changedFiles) {
|
|
8592
|
-
|
|
8593
|
-
console.log(
|
|
8594
|
-
console.log();
|
|
8909
|
+
const status = diff.isDirectory ? colors.dim("[dir]") : diff.isBinary ? colors.dim("[bin]") : "";
|
|
8910
|
+
console.log(` ${colors.yellow("~")} ${diff.source} ${status}`);
|
|
8595
8911
|
}
|
|
8912
|
+
console.log();
|
|
8913
|
+
prompts.outro(`Found ${changedFiles.length} changed file(s)`);
|
|
8596
8914
|
return;
|
|
8597
8915
|
}
|
|
8598
|
-
for (const
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
}
|
|
8606
|
-
if (options.stat) {
|
|
8607
|
-
console.log(chalk19.yellow(`~ ${path}`));
|
|
8608
|
-
} else {
|
|
8609
|
-
console.log(formatUnifiedDiff(path, diff.systemContent, diff.repoContent));
|
|
8610
|
-
console.log();
|
|
8611
|
-
}
|
|
8916
|
+
for (const diff of changedFiles) {
|
|
8917
|
+
console.log(formatUnifiedDiff(diff));
|
|
8918
|
+
console.log();
|
|
8919
|
+
}
|
|
8920
|
+
prompts.outro(`Found ${changedFiles.length} changed file(s)`);
|
|
8921
|
+
if (options.exitCode) {
|
|
8922
|
+
process.exit(1);
|
|
8612
8923
|
}
|
|
8613
8924
|
};
|
|
8614
|
-
var diffCommand = new Command11("diff").description("Show differences between system and repository").argument("[paths...]", "Specific files to diff").option("--staged", "Show staged git changes").option("--stat", "Show diffstat only").
|
|
8925
|
+
var diffCommand = new Command11("diff").description("Show differences between system and repository").argument("[paths...]", "Specific files to diff").option("--staged", "Show staged git changes").option("--stat", "Show diffstat only").option(
|
|
8926
|
+
"--category <category>",
|
|
8927
|
+
"Filter by file category (shell, git, editors, terminal, ssh, misc)"
|
|
8928
|
+
).option("--name-only", "Show only changed file names").option("--exit-code", "Return exit code 1 if differences found").action(async (paths, options) => {
|
|
8615
8929
|
await runDiff(paths, options);
|
|
8616
8930
|
});
|
|
8617
8931
|
|
|
@@ -8622,41 +8936,106 @@ init_config();
|
|
|
8622
8936
|
init_manifest();
|
|
8623
8937
|
init_errors();
|
|
8624
8938
|
import { Command as Command12 } from "commander";
|
|
8625
|
-
import chalk20 from "chalk";
|
|
8626
8939
|
import { spawn } from "child_process";
|
|
8627
8940
|
var CONFIG_KEYS = [
|
|
8628
8941
|
// Repository settings
|
|
8629
|
-
{
|
|
8630
|
-
|
|
8631
|
-
|
|
8942
|
+
{
|
|
8943
|
+
path: "repository.defaultBranch",
|
|
8944
|
+
type: "string",
|
|
8945
|
+
description: "Default git branch name",
|
|
8946
|
+
section: "repository"
|
|
8947
|
+
},
|
|
8948
|
+
{
|
|
8949
|
+
path: "repository.autoCommit",
|
|
8950
|
+
type: "boolean",
|
|
8951
|
+
description: "Auto-commit changes on sync",
|
|
8952
|
+
section: "repository"
|
|
8953
|
+
},
|
|
8954
|
+
{
|
|
8955
|
+
path: "repository.autoPush",
|
|
8956
|
+
type: "boolean",
|
|
8957
|
+
description: "Auto-push after commit",
|
|
8958
|
+
section: "repository"
|
|
8959
|
+
},
|
|
8632
8960
|
// File settings
|
|
8633
|
-
{
|
|
8634
|
-
|
|
8635
|
-
|
|
8961
|
+
{
|
|
8962
|
+
path: "files.strategy",
|
|
8963
|
+
type: "enum",
|
|
8964
|
+
description: "File copy strategy",
|
|
8965
|
+
section: "files",
|
|
8966
|
+
options: ["copy", "symlink"]
|
|
8967
|
+
},
|
|
8968
|
+
{
|
|
8969
|
+
path: "files.backupOnRestore",
|
|
8970
|
+
type: "boolean",
|
|
8971
|
+
description: "Create backups before restore",
|
|
8972
|
+
section: "files"
|
|
8973
|
+
},
|
|
8974
|
+
{
|
|
8975
|
+
path: "files.backupDir",
|
|
8976
|
+
type: "string",
|
|
8977
|
+
description: "Backup directory path",
|
|
8978
|
+
section: "files"
|
|
8979
|
+
},
|
|
8636
8980
|
// UI settings
|
|
8637
8981
|
{ path: "ui.colors", type: "boolean", description: "Enable colored output", section: "ui" },
|
|
8638
8982
|
{ path: "ui.emoji", type: "boolean", description: "Enable emoji in output", section: "ui" },
|
|
8639
8983
|
{ path: "ui.verbose", type: "boolean", description: "Enable verbose logging", section: "ui" },
|
|
8640
8984
|
// Hook settings
|
|
8641
|
-
{
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8985
|
+
{
|
|
8986
|
+
path: "hooks.preSync",
|
|
8987
|
+
type: "string",
|
|
8988
|
+
description: "Command to run before sync",
|
|
8989
|
+
section: "hooks"
|
|
8990
|
+
},
|
|
8991
|
+
{
|
|
8992
|
+
path: "hooks.postSync",
|
|
8993
|
+
type: "string",
|
|
8994
|
+
description: "Command to run after sync",
|
|
8995
|
+
section: "hooks"
|
|
8996
|
+
},
|
|
8997
|
+
{
|
|
8998
|
+
path: "hooks.preRestore",
|
|
8999
|
+
type: "string",
|
|
9000
|
+
description: "Command to run before restore",
|
|
9001
|
+
section: "hooks"
|
|
9002
|
+
},
|
|
9003
|
+
{
|
|
9004
|
+
path: "hooks.postRestore",
|
|
9005
|
+
type: "string",
|
|
9006
|
+
description: "Command to run after restore",
|
|
9007
|
+
section: "hooks"
|
|
9008
|
+
},
|
|
8645
9009
|
// Template settings
|
|
8646
|
-
{
|
|
9010
|
+
{
|
|
9011
|
+
path: "templates.enabled",
|
|
9012
|
+
type: "boolean",
|
|
9013
|
+
description: "Enable template processing",
|
|
9014
|
+
section: "templates"
|
|
9015
|
+
},
|
|
8647
9016
|
// Encryption settings
|
|
8648
|
-
{
|
|
8649
|
-
|
|
9017
|
+
{
|
|
9018
|
+
path: "encryption.enabled",
|
|
9019
|
+
type: "boolean",
|
|
9020
|
+
description: "Enable file encryption",
|
|
9021
|
+
section: "encryption"
|
|
9022
|
+
},
|
|
9023
|
+
{
|
|
9024
|
+
path: "encryption.gpgKey",
|
|
9025
|
+
type: "string",
|
|
9026
|
+
description: "GPG key for encryption",
|
|
9027
|
+
section: "encryption"
|
|
9028
|
+
}
|
|
8650
9029
|
];
|
|
8651
9030
|
var getKeyInfo = (path) => {
|
|
8652
9031
|
return CONFIG_KEYS.find((k) => k.path === path);
|
|
8653
9032
|
};
|
|
8654
9033
|
var formatConfigValue = (value) => {
|
|
8655
|
-
if (value === void 0 || value === null) return
|
|
8656
|
-
if (typeof value === "boolean") return value ?
|
|
8657
|
-
if (Array.isArray(value)) return value.length ?
|
|
8658
|
-
if (typeof value === "object") return
|
|
8659
|
-
return
|
|
9034
|
+
if (value === void 0 || value === null) return colors.dim("(not set)");
|
|
9035
|
+
if (typeof value === "boolean") return value ? colors.green("true") : colors.yellow("false");
|
|
9036
|
+
if (Array.isArray(value)) return value.length ? colors.cyan(value.join(", ")) : colors.dim("[]");
|
|
9037
|
+
if (typeof value === "object") return colors.dim(JSON.stringify(value));
|
|
9038
|
+
return colors.white(String(value));
|
|
8660
9039
|
};
|
|
8661
9040
|
var printConfig = (config) => {
|
|
8662
9041
|
console.log(JSON.stringify(config, null, 2));
|
|
@@ -8722,7 +9101,7 @@ var runConfigList = async () => {
|
|
|
8722
9101
|
const config = await loadConfig(tuckDir);
|
|
8723
9102
|
prompts.intro("tuck config");
|
|
8724
9103
|
console.log();
|
|
8725
|
-
console.log(
|
|
9104
|
+
console.log(colors.dim("Configuration file:"), collapsePath(getConfigPath(tuckDir)));
|
|
8726
9105
|
console.log();
|
|
8727
9106
|
printConfig(config);
|
|
8728
9107
|
};
|
|
@@ -8750,7 +9129,10 @@ var runConfigEdit = async () => {
|
|
|
8750
9129
|
};
|
|
8751
9130
|
var runConfigReset = async () => {
|
|
8752
9131
|
const tuckDir = getTuckDir();
|
|
8753
|
-
const confirm2 = await prompts.confirm(
|
|
9132
|
+
const confirm2 = await prompts.confirm(
|
|
9133
|
+
"Reset configuration to defaults? This cannot be undone.",
|
|
9134
|
+
false
|
|
9135
|
+
);
|
|
8754
9136
|
if (!confirm2) {
|
|
8755
9137
|
prompts.cancel("Operation cancelled");
|
|
8756
9138
|
return;
|
|
@@ -8771,15 +9153,15 @@ var showConfigView = async (config) => {
|
|
|
8771
9153
|
for (const section of sections) {
|
|
8772
9154
|
const sectionConfig = configObj[section.key];
|
|
8773
9155
|
if (!sectionConfig || typeof sectionConfig !== "object") continue;
|
|
8774
|
-
console.log(
|
|
8775
|
-
console.log(
|
|
9156
|
+
console.log(colors.bold.cyan(`${section.icon} ${section.title}`));
|
|
9157
|
+
console.log(colors.dim("-".repeat(40)));
|
|
8776
9158
|
for (const [key, value] of Object.entries(sectionConfig)) {
|
|
8777
9159
|
const keyInfo = getKeyInfo(`${section.key}.${key}`);
|
|
8778
9160
|
const displayValue = formatConfigValue(value);
|
|
8779
9161
|
const description = keyInfo?.description || "";
|
|
8780
|
-
console.log(` ${
|
|
9162
|
+
console.log(` ${colors.white(key)}: ${displayValue}`);
|
|
8781
9163
|
if (description) {
|
|
8782
|
-
console.log(
|
|
9164
|
+
console.log(colors.dim(` ${description}`));
|
|
8783
9165
|
}
|
|
8784
9166
|
}
|
|
8785
9167
|
console.log();
|
|
@@ -8788,7 +9170,7 @@ var showConfigView = async (config) => {
|
|
|
8788
9170
|
var runConfigWizard = async (config, tuckDir) => {
|
|
8789
9171
|
prompts.log.info("Let's configure tuck for your workflow");
|
|
8790
9172
|
console.log();
|
|
8791
|
-
console.log(
|
|
9173
|
+
console.log(colors.bold.cyan("* Repository Behavior"));
|
|
8792
9174
|
const autoCommit = await prompts.confirm(
|
|
8793
9175
|
"Auto-commit changes when running sync?",
|
|
8794
9176
|
config.repository.autoCommit ?? true
|
|
@@ -8798,7 +9180,7 @@ var runConfigWizard = async (config, tuckDir) => {
|
|
|
8798
9180
|
config.repository.autoPush ?? false
|
|
8799
9181
|
);
|
|
8800
9182
|
console.log();
|
|
8801
|
-
console.log(
|
|
9183
|
+
console.log(colors.bold.cyan("> File Strategy"));
|
|
8802
9184
|
const rawStrategy = await prompts.select("How should tuck manage files?", [
|
|
8803
9185
|
{ value: "copy", label: "Copy files", hint: "Safe, independent copies" },
|
|
8804
9186
|
{ value: "symlink", label: "Symlink files", hint: "Real-time updates, single source of truth" }
|
|
@@ -8809,8 +9191,8 @@ var runConfigWizard = async (config, tuckDir) => {
|
|
|
8809
9191
|
config.files.backupOnRestore ?? true
|
|
8810
9192
|
);
|
|
8811
9193
|
console.log();
|
|
8812
|
-
console.log(
|
|
8813
|
-
const
|
|
9194
|
+
console.log(colors.bold.cyan("# User Interface"));
|
|
9195
|
+
const colors2 = await prompts.confirm("Enable colored output?", config.ui.colors ?? true);
|
|
8814
9196
|
const emoji = await prompts.confirm("Enable emoji in output?", config.ui.emoji ?? true);
|
|
8815
9197
|
const verbose = await prompts.confirm("Enable verbose logging?", config.ui.verbose ?? false);
|
|
8816
9198
|
const updatedConfig = {
|
|
@@ -8826,7 +9208,7 @@ var runConfigWizard = async (config, tuckDir) => {
|
|
|
8826
9208
|
backupOnRestore
|
|
8827
9209
|
},
|
|
8828
9210
|
ui: {
|
|
8829
|
-
colors,
|
|
9211
|
+
colors: colors2,
|
|
8830
9212
|
emoji,
|
|
8831
9213
|
verbose
|
|
8832
9214
|
}
|
|
@@ -8979,7 +9361,6 @@ import { join as join16 } from "path";
|
|
|
8979
9361
|
import { readFile as readFile12, rm as rm4, chmod as chmod3, stat as stat10 } from "fs/promises";
|
|
8980
9362
|
import { ensureDir as ensureDir7, pathExists as fsPathExists } from "fs-extra";
|
|
8981
9363
|
import { tmpdir as tmpdir2 } from "os";
|
|
8982
|
-
import chalk21 from "chalk";
|
|
8983
9364
|
|
|
8984
9365
|
// src/lib/timemachine.ts
|
|
8985
9366
|
init_paths();
|
|
@@ -9537,7 +9918,10 @@ var applyWithMerge = async (files, dryRun) => {
|
|
|
9537
9918
|
if (isShellFile(file.source) && await pathExists(file.destination)) {
|
|
9538
9919
|
const mergeResult = await smartMerge(file.destination, fileContent);
|
|
9539
9920
|
if (dryRun) {
|
|
9540
|
-
logger.file(
|
|
9921
|
+
logger.file(
|
|
9922
|
+
"merge",
|
|
9923
|
+
`${collapsePath(file.destination)} (${mergeResult.preservedBlocks} blocks preserved)`
|
|
9924
|
+
);
|
|
9541
9925
|
} else {
|
|
9542
9926
|
const { writeFile: writeFile8 } = await import("fs/promises");
|
|
9543
9927
|
const { ensureDir: ensureDir8 } = await import("fs-extra");
|
|
@@ -9557,10 +9941,7 @@ var applyWithMerge = async (files, dryRun) => {
|
|
|
9557
9941
|
const fileExists = await pathExists(file.destination);
|
|
9558
9942
|
await copyFileOrDir(file.repoPath, file.destination, { overwrite: true });
|
|
9559
9943
|
await fixSecurePermissions(file.destination);
|
|
9560
|
-
logger.file(
|
|
9561
|
-
fileExists ? "modify" : "add",
|
|
9562
|
-
collapsePath(file.destination)
|
|
9563
|
-
);
|
|
9944
|
+
logger.file(fileExists ? "modify" : "add", collapsePath(file.destination));
|
|
9564
9945
|
}
|
|
9565
9946
|
}
|
|
9566
9947
|
result.appliedCount++;
|
|
@@ -9591,10 +9972,7 @@ var applyWithReplace = async (files, dryRun) => {
|
|
|
9591
9972
|
const fileExists = await pathExists(file.destination);
|
|
9592
9973
|
await copyFileOrDir(file.repoPath, file.destination, { overwrite: true });
|
|
9593
9974
|
await fixSecurePermissions(file.destination);
|
|
9594
|
-
logger.file(
|
|
9595
|
-
fileExists ? "modify" : "add",
|
|
9596
|
-
collapsePath(file.destination)
|
|
9597
|
-
);
|
|
9975
|
+
logger.file(fileExists ? "modify" : "add", collapsePath(file.destination));
|
|
9598
9976
|
}
|
|
9599
9977
|
result.appliedCount++;
|
|
9600
9978
|
}
|
|
@@ -9603,14 +9981,14 @@ var applyWithReplace = async (files, dryRun) => {
|
|
|
9603
9981
|
var displayPlaceholderWarnings = (filesWithPlaceholders) => {
|
|
9604
9982
|
if (filesWithPlaceholders.length === 0) return;
|
|
9605
9983
|
console.log();
|
|
9606
|
-
console.log(
|
|
9984
|
+
console.log(colors.yellow("\u26A0 Warning: Some files contain unresolved placeholders:"));
|
|
9607
9985
|
console.log();
|
|
9608
9986
|
for (const { path, placeholders } of filesWithPlaceholders) {
|
|
9609
|
-
console.log(
|
|
9987
|
+
console.log(colors.dim(` ${path}:`));
|
|
9610
9988
|
const maxToShow = 5;
|
|
9611
9989
|
if (placeholders.length <= maxToShow) {
|
|
9612
9990
|
for (const placeholder of placeholders) {
|
|
9613
|
-
console.log(
|
|
9991
|
+
console.log(colors.yellow(` {{${placeholder}}}`));
|
|
9614
9992
|
}
|
|
9615
9993
|
} else {
|
|
9616
9994
|
const firstCount = 3;
|
|
@@ -9618,23 +9996,23 @@ var displayPlaceholderWarnings = (filesWithPlaceholders) => {
|
|
|
9618
9996
|
const firstPlaceholders = placeholders.slice(0, firstCount);
|
|
9619
9997
|
const lastPlaceholders = placeholders.slice(-lastCount);
|
|
9620
9998
|
for (const placeholder of firstPlaceholders) {
|
|
9621
|
-
console.log(
|
|
9999
|
+
console.log(colors.yellow(` {{${placeholder}}}`));
|
|
9622
10000
|
}
|
|
9623
|
-
console.log(
|
|
10001
|
+
console.log(colors.dim(" ..."));
|
|
9624
10002
|
for (const placeholder of lastPlaceholders) {
|
|
9625
|
-
console.log(
|
|
10003
|
+
console.log(colors.yellow(` {{${placeholder}}}`));
|
|
9626
10004
|
}
|
|
9627
10005
|
const shownCount = firstPlaceholders.length + lastPlaceholders.length;
|
|
9628
10006
|
const hiddenCount = placeholders.length - shownCount;
|
|
9629
10007
|
if (hiddenCount > 0) {
|
|
9630
|
-
console.log(
|
|
10008
|
+
console.log(colors.dim(` ... and ${hiddenCount} more not shown`));
|
|
9631
10009
|
}
|
|
9632
10010
|
}
|
|
9633
10011
|
}
|
|
9634
10012
|
console.log();
|
|
9635
|
-
console.log(
|
|
9636
|
-
console.log(
|
|
9637
|
-
console.log(
|
|
10013
|
+
console.log(colors.dim(" These placeholders need to be replaced with actual values."));
|
|
10014
|
+
console.log(colors.dim(" Use `tuck secrets set <NAME> <value>` to configure secrets,"));
|
|
10015
|
+
console.log(colors.dim(" then re-apply to populate them."));
|
|
9638
10016
|
};
|
|
9639
10017
|
var runInteractiveApply = async (source, options) => {
|
|
9640
10018
|
banner();
|
|
@@ -9651,10 +10029,10 @@ var runInteractiveApply = async (source, options) => {
|
|
|
9651
10029
|
}
|
|
9652
10030
|
let repoDir;
|
|
9653
10031
|
try {
|
|
9654
|
-
const
|
|
9655
|
-
|
|
10032
|
+
const spinner4 = prompts.spinner();
|
|
10033
|
+
spinner4.start("Cloning repository...");
|
|
9656
10034
|
repoDir = await cloneSource(repoId, isUrl);
|
|
9657
|
-
|
|
10035
|
+
spinner4.stop("Repository cloned");
|
|
9658
10036
|
} catch (error) {
|
|
9659
10037
|
prompts.log.error(`Failed to clone: ${error instanceof Error ? error.message : String(error)}`);
|
|
9660
10038
|
return;
|
|
@@ -9685,11 +10063,11 @@ var runInteractiveApply = async (source, options) => {
|
|
|
9685
10063
|
}
|
|
9686
10064
|
for (const [category, categoryFiles] of Object.entries(byCategory)) {
|
|
9687
10065
|
const categoryConfig = CATEGORIES[category] || { icon: "\u{1F4C4}" };
|
|
9688
|
-
console.log(
|
|
10066
|
+
console.log(colors.bold(` ${categoryConfig.icon} ${category}`));
|
|
9689
10067
|
for (const file of categoryFiles) {
|
|
9690
10068
|
const exists = await pathExists(file.destination);
|
|
9691
|
-
const status = exists ?
|
|
9692
|
-
console.log(
|
|
10069
|
+
const status = exists ? colors.yellow("(will update)") : colors.green("(new)");
|
|
10070
|
+
console.log(colors.dim(` ${collapsePath(file.destination)} ${status}`));
|
|
9693
10071
|
}
|
|
9694
10072
|
}
|
|
9695
10073
|
console.log();
|
|
@@ -9746,10 +10124,10 @@ var runInteractiveApply = async (source, options) => {
|
|
|
9746
10124
|
}
|
|
9747
10125
|
}
|
|
9748
10126
|
if (existingPaths.length > 0 && !options.dryRun) {
|
|
9749
|
-
const
|
|
9750
|
-
|
|
10127
|
+
const spinner4 = prompts.spinner();
|
|
10128
|
+
spinner4.start("Creating backup snapshot...");
|
|
9751
10129
|
const snapshot = await createPreApplySnapshot(existingPaths, repoId);
|
|
9752
|
-
|
|
10130
|
+
spinner4.stop(`Backup created: ${snapshot.id}`);
|
|
9753
10131
|
console.log();
|
|
9754
10132
|
}
|
|
9755
10133
|
if (options.dryRun) {
|
|
@@ -9855,7 +10233,6 @@ var applyCommand = new Command13("apply").description("Apply dotfiles from a rep
|
|
|
9855
10233
|
init_ui();
|
|
9856
10234
|
init_paths();
|
|
9857
10235
|
import { Command as Command14 } from "commander";
|
|
9858
|
-
import chalk22 from "chalk";
|
|
9859
10236
|
var showSnapshotList = async () => {
|
|
9860
10237
|
const snapshots = await listSnapshots();
|
|
9861
10238
|
if (snapshots.length === 0) {
|
|
@@ -9868,11 +10245,11 @@ var showSnapshotList = async () => {
|
|
|
9868
10245
|
for (const snapshot of snapshots) {
|
|
9869
10246
|
const date = formatSnapshotDate(snapshot.id);
|
|
9870
10247
|
const fileCount = snapshot.files.filter((f) => f.existed).length;
|
|
9871
|
-
console.log(
|
|
9872
|
-
console.log(
|
|
9873
|
-
console.log(
|
|
9874
|
-
console.log(
|
|
9875
|
-
console.log(
|
|
10248
|
+
console.log(colors.cyan(` ${snapshot.id}`));
|
|
10249
|
+
console.log(colors.dim(` Date: ${date}`));
|
|
10250
|
+
console.log(colors.dim(` Reason: ${snapshot.reason}`));
|
|
10251
|
+
console.log(colors.dim(` Files: ${fileCount} file(s) backed up`));
|
|
10252
|
+
console.log(colors.dim(` Machine: ${snapshot.machine}`));
|
|
9876
10253
|
console.log();
|
|
9877
10254
|
}
|
|
9878
10255
|
const totalSize = await getSnapshotsSize();
|
|
@@ -9883,18 +10260,18 @@ var showSnapshotList = async () => {
|
|
|
9883
10260
|
};
|
|
9884
10261
|
var showSnapshotDetails = (snapshot) => {
|
|
9885
10262
|
console.log();
|
|
9886
|
-
console.log(
|
|
9887
|
-
console.log(
|
|
9888
|
-
console.log(
|
|
9889
|
-
console.log(
|
|
9890
|
-
console.log(
|
|
10263
|
+
console.log(colors.bold("Snapshot Details:"));
|
|
10264
|
+
console.log(colors.dim(` ID: ${snapshot.id}`));
|
|
10265
|
+
console.log(colors.dim(` Date: ${formatSnapshotDate(snapshot.id)}`));
|
|
10266
|
+
console.log(colors.dim(` Reason: ${snapshot.reason}`));
|
|
10267
|
+
console.log(colors.dim(` Machine: ${snapshot.machine}`));
|
|
9891
10268
|
console.log();
|
|
9892
|
-
console.log(
|
|
10269
|
+
console.log(colors.bold("Files in snapshot:"));
|
|
9893
10270
|
for (const file of snapshot.files) {
|
|
9894
10271
|
if (file.existed) {
|
|
9895
|
-
console.log(
|
|
10272
|
+
console.log(colors.dim(` ok ${collapsePath(file.originalPath)}`));
|
|
9896
10273
|
} else {
|
|
9897
|
-
console.log(
|
|
10274
|
+
console.log(colors.dim(` - ${collapsePath(file.originalPath)} (did not exist)`));
|
|
9898
10275
|
}
|
|
9899
10276
|
}
|
|
9900
10277
|
console.log();
|
|
@@ -9989,10 +10366,7 @@ var runInteractiveUndo = async () => {
|
|
|
9989
10366
|
label: `${s.id} - ${s.reason.slice(0, 40)}${s.reason.length > 40 ? "..." : ""}`,
|
|
9990
10367
|
hint: `${s.files.filter((f) => f.existed).length} files`
|
|
9991
10368
|
}));
|
|
9992
|
-
const selectedId = await prompts.select(
|
|
9993
|
-
"Select a snapshot to restore:",
|
|
9994
|
-
snapshotOptions
|
|
9995
|
-
);
|
|
10369
|
+
const selectedId = await prompts.select("Select a snapshot to restore:", snapshotOptions);
|
|
9996
10370
|
const snapshot = await getSnapshot(selectedId);
|
|
9997
10371
|
if (!snapshot) {
|
|
9998
10372
|
prompts.log.error("Snapshot not found");
|
|
@@ -10002,11 +10376,11 @@ var runInteractiveUndo = async () => {
|
|
|
10002
10376
|
prompts.log.info("Files in this snapshot:");
|
|
10003
10377
|
for (const file of snapshot.files.slice(0, 10)) {
|
|
10004
10378
|
if (file.existed) {
|
|
10005
|
-
console.log(
|
|
10379
|
+
console.log(colors.dim(` ${collapsePath(file.originalPath)}`));
|
|
10006
10380
|
}
|
|
10007
10381
|
}
|
|
10008
10382
|
if (snapshot.files.length > 10) {
|
|
10009
|
-
console.log(
|
|
10383
|
+
console.log(colors.dim(` ... and ${snapshot.files.length - 10} more`));
|
|
10010
10384
|
}
|
|
10011
10385
|
console.log();
|
|
10012
10386
|
const confirmed = await prompts.confirm("Restore these files?", true);
|
|
@@ -10014,10 +10388,10 @@ var runInteractiveUndo = async () => {
|
|
|
10014
10388
|
prompts.cancel("Restore cancelled");
|
|
10015
10389
|
return;
|
|
10016
10390
|
}
|
|
10017
|
-
const
|
|
10018
|
-
|
|
10391
|
+
const spinner4 = prompts.spinner();
|
|
10392
|
+
spinner4.start("Restoring files...");
|
|
10019
10393
|
const restoredFiles = await restoreSnapshot(selectedId);
|
|
10020
|
-
|
|
10394
|
+
spinner4.stop(`Restored ${restoredFiles.length} files`);
|
|
10021
10395
|
prompts.outro("Done!");
|
|
10022
10396
|
};
|
|
10023
10397
|
var runUndo = async (snapshotId, options) => {
|
|
@@ -10060,7 +10434,6 @@ init_detect();
|
|
|
10060
10434
|
init_errors();
|
|
10061
10435
|
init_fileTracking();
|
|
10062
10436
|
import { Command as Command15 } from "commander";
|
|
10063
|
-
import chalk23 from "chalk";
|
|
10064
10437
|
init_tuckignore();
|
|
10065
10438
|
var groupSelectableByCategory = (files) => {
|
|
10066
10439
|
const grouped = {};
|
|
@@ -10085,18 +10458,18 @@ var displayGroupedFiles = (files, showAll) => {
|
|
|
10085
10458
|
const trackedFiles = categoryFiles.filter((f) => f.alreadyTracked);
|
|
10086
10459
|
console.log();
|
|
10087
10460
|
console.log(
|
|
10088
|
-
|
|
10461
|
+
colors.bold(`${config.icon} ${config.name}`) + colors.dim(` (${newFiles.length} new, ${trackedFiles.length} tracked)`)
|
|
10089
10462
|
);
|
|
10090
|
-
console.log(
|
|
10463
|
+
console.log(colors.dim("\u2500".repeat(50)));
|
|
10091
10464
|
for (const file of categoryFiles) {
|
|
10092
10465
|
if (!showAll && file.alreadyTracked) continue;
|
|
10093
|
-
const status = file.selected ?
|
|
10466
|
+
const status = file.selected ? colors.green("[x]") : colors.dim("[ ]");
|
|
10094
10467
|
const name = file.path;
|
|
10095
|
-
const tracked = file.alreadyTracked ?
|
|
10096
|
-
const sensitive = file.sensitive ?
|
|
10097
|
-
const dir = file.isDirectory ?
|
|
10468
|
+
const tracked = file.alreadyTracked ? colors.dim(" (tracked)") : "";
|
|
10469
|
+
const sensitive = file.sensitive ? colors.yellow(" [!]") : "";
|
|
10470
|
+
const dir = file.isDirectory ? colors.cyan(" [dir]") : "";
|
|
10098
10471
|
console.log(` ${status} ${name}${dir}${sensitive}${tracked}`);
|
|
10099
|
-
console.log(
|
|
10472
|
+
console.log(colors.dim(` ${file.description}`));
|
|
10100
10473
|
}
|
|
10101
10474
|
}
|
|
10102
10475
|
};
|
|
@@ -10111,16 +10484,16 @@ var runInteractiveSelection = async (files) => {
|
|
|
10111
10484
|
for (const [category, categoryFiles] of Object.entries(grouped)) {
|
|
10112
10485
|
const config = DETECTION_CATEGORIES[category] || { icon: "-", name: category };
|
|
10113
10486
|
console.log();
|
|
10114
|
-
console.log(
|
|
10115
|
-
console.log(
|
|
10487
|
+
console.log(colors.bold(`${config.icon} ${config.name}`));
|
|
10488
|
+
console.log(colors.dim(config.description || ""));
|
|
10116
10489
|
console.log();
|
|
10117
10490
|
const options = categoryFiles.map((file) => {
|
|
10118
10491
|
let label = file.path;
|
|
10119
10492
|
if (file.sensitive) {
|
|
10120
|
-
label +=
|
|
10493
|
+
label += colors.yellow(" [!]");
|
|
10121
10494
|
}
|
|
10122
10495
|
if (file.isDirectory) {
|
|
10123
|
-
label +=
|
|
10496
|
+
label += colors.cyan(" [dir]");
|
|
10124
10497
|
}
|
|
10125
10498
|
return {
|
|
10126
10499
|
value: file.path,
|
|
@@ -10149,11 +10522,11 @@ var runQuickScan = async (files) => {
|
|
|
10149
10522
|
const trackedFiles = files.filter((f) => f.alreadyTracked);
|
|
10150
10523
|
console.log();
|
|
10151
10524
|
console.log(
|
|
10152
|
-
|
|
10525
|
+
colors.bold.cyan("Detected Dotfiles: ") + colors.white(`${newFiles.length} new, ${trackedFiles.length} already tracked`)
|
|
10153
10526
|
);
|
|
10154
10527
|
displayGroupedFiles(files, false);
|
|
10155
10528
|
console.log();
|
|
10156
|
-
console.log(
|
|
10529
|
+
console.log(colors.dim("\u2500".repeat(60)));
|
|
10157
10530
|
console.log();
|
|
10158
10531
|
if (newFiles.length > 0) {
|
|
10159
10532
|
logger.info(`Found ${newFiles.length} new dotfiles to track`);
|
|
@@ -10169,23 +10542,23 @@ var showSummary = (selected) => {
|
|
|
10169
10542
|
return;
|
|
10170
10543
|
}
|
|
10171
10544
|
console.log();
|
|
10172
|
-
console.log(
|
|
10173
|
-
console.log(
|
|
10545
|
+
console.log(colors.bold.cyan(`Selected ${selected.length} files to track:`));
|
|
10546
|
+
console.log(colors.dim("\u2500".repeat(50)));
|
|
10174
10547
|
console.log();
|
|
10175
10548
|
const grouped = groupSelectableByCategory(selected);
|
|
10176
10549
|
for (const [category, files] of Object.entries(grouped)) {
|
|
10177
10550
|
const config = DETECTION_CATEGORIES[category] || { icon: "-", name: category };
|
|
10178
|
-
console.log(
|
|
10551
|
+
console.log(colors.bold(`${config.icon} ${config.name}`));
|
|
10179
10552
|
for (const file of files) {
|
|
10180
|
-
const sensitive = file.sensitive ?
|
|
10181
|
-
console.log(
|
|
10553
|
+
const sensitive = file.sensitive ? colors.yellow(" \u26A0") : "";
|
|
10554
|
+
console.log(colors.dim(` \u2022 ${collapsePath(file.path)}${sensitive}`));
|
|
10182
10555
|
}
|
|
10183
10556
|
console.log();
|
|
10184
10557
|
}
|
|
10185
10558
|
const sensitiveFiles = selected.filter((f) => f.sensitive);
|
|
10186
10559
|
if (sensitiveFiles.length > 0) {
|
|
10187
|
-
console.log(
|
|
10188
|
-
console.log(
|
|
10560
|
+
console.log(colors.yellow("\u26A0 Warning: Some files may contain sensitive data"));
|
|
10561
|
+
console.log(colors.dim(" Make sure your repository is private!"));
|
|
10189
10562
|
console.log();
|
|
10190
10563
|
}
|
|
10191
10564
|
};
|
|
@@ -10207,10 +10580,10 @@ var runScan = async (options) => {
|
|
|
10207
10580
|
} catch {
|
|
10208
10581
|
throw new NotInitializedError();
|
|
10209
10582
|
}
|
|
10210
|
-
const
|
|
10211
|
-
|
|
10583
|
+
const spinner4 = prompts.spinner();
|
|
10584
|
+
spinner4.start("Scanning for dotfiles...");
|
|
10212
10585
|
const detected = await detectDotfiles();
|
|
10213
|
-
|
|
10586
|
+
spinner4.stop(`Found ${detected.length} dotfiles on this system`);
|
|
10214
10587
|
if (detected.length === 0) {
|
|
10215
10588
|
logger.warning("No common dotfiles detected on this system");
|
|
10216
10589
|
return;
|
|
@@ -10238,7 +10611,7 @@ var runScan = async (options) => {
|
|
|
10238
10611
|
logger.warning(`No dotfiles found in category: ${options.category}`);
|
|
10239
10612
|
logger.info("Available categories:");
|
|
10240
10613
|
for (const [key, config] of Object.entries(DETECTION_CATEGORIES)) {
|
|
10241
|
-
console.log(
|
|
10614
|
+
console.log(colors.dim(` ${config.icon} ${key} - ${config.name}`));
|
|
10242
10615
|
}
|
|
10243
10616
|
return;
|
|
10244
10617
|
}
|
|
@@ -10296,10 +10669,7 @@ var runScan = async (options) => {
|
|
|
10296
10669
|
return;
|
|
10297
10670
|
}
|
|
10298
10671
|
showSummary(selected);
|
|
10299
|
-
const confirmed = await prompts.confirm(
|
|
10300
|
-
`Track these ${selected.length} files?`,
|
|
10301
|
-
true
|
|
10302
|
-
);
|
|
10672
|
+
const confirmed = await prompts.confirm(`Track these ${selected.length} files?`, true);
|
|
10303
10673
|
if (!confirmed) {
|
|
10304
10674
|
prompts.cancel("Operation cancelled");
|
|
10305
10675
|
return;
|
|
@@ -10335,7 +10705,7 @@ init_manifest();
|
|
|
10335
10705
|
init_git();
|
|
10336
10706
|
var program = new Command16();
|
|
10337
10707
|
program.name("tuck").description(DESCRIPTION).version(VERSION, "-v, --version", "Display version number").configureOutput({
|
|
10338
|
-
outputError: (str, write) => write(
|
|
10708
|
+
outputError: (str, write) => write(chalk5.red(str))
|
|
10339
10709
|
}).addHelpText("beforeAll", customHelp(VERSION)).helpOption("-h, --help", "Display this help message").showHelpAfterError(false);
|
|
10340
10710
|
program.configureHelp({
|
|
10341
10711
|
formatHelp: () => ""
|
|
@@ -10359,12 +10729,12 @@ var runDefaultAction = async () => {
|
|
|
10359
10729
|
const tuckDir = getTuckDir();
|
|
10360
10730
|
if (!await pathExists(tuckDir)) {
|
|
10361
10731
|
miniBanner();
|
|
10362
|
-
console.log(
|
|
10363
|
-
console.log(
|
|
10364
|
-
console.log(
|
|
10732
|
+
console.log(chalk5.bold("Get started with tuck:\n"));
|
|
10733
|
+
console.log(chalk5.cyan(" tuck init") + chalk5.dim(" - Set up tuck and create a GitHub repo"));
|
|
10734
|
+
console.log(chalk5.cyan(" tuck scan") + chalk5.dim(" - Find dotfiles to track"));
|
|
10365
10735
|
console.log();
|
|
10366
|
-
console.log(
|
|
10367
|
-
console.log(
|
|
10736
|
+
console.log(chalk5.dim("On a new machine:"));
|
|
10737
|
+
console.log(chalk5.cyan(" tuck apply <username>") + chalk5.dim(" - Apply your dotfiles"));
|
|
10368
10738
|
console.log();
|
|
10369
10739
|
return;
|
|
10370
10740
|
}
|
|
@@ -10373,38 +10743,38 @@ var runDefaultAction = async () => {
|
|
|
10373
10743
|
const trackedCount = Object.keys(manifest.files).length;
|
|
10374
10744
|
const gitStatus = await getStatus(tuckDir);
|
|
10375
10745
|
miniBanner();
|
|
10376
|
-
console.log(
|
|
10377
|
-
console.log(` Tracked files: ${
|
|
10746
|
+
console.log(chalk5.bold("Status:\n"));
|
|
10747
|
+
console.log(` Tracked files: ${chalk5.cyan(trackedCount.toString())}`);
|
|
10378
10748
|
const pendingChanges = gitStatus.modified.length + gitStatus.staged.length;
|
|
10379
10749
|
if (pendingChanges > 0) {
|
|
10380
|
-
console.log(` Pending changes: ${
|
|
10750
|
+
console.log(` Pending changes: ${chalk5.yellow(pendingChanges.toString())}`);
|
|
10381
10751
|
} else {
|
|
10382
|
-
console.log(` Pending changes: ${
|
|
10752
|
+
console.log(` Pending changes: ${chalk5.dim("none")}`);
|
|
10383
10753
|
}
|
|
10384
10754
|
if (gitStatus.ahead > 0) {
|
|
10385
|
-
console.log(` Commits to push: ${
|
|
10755
|
+
console.log(` Commits to push: ${chalk5.yellow(gitStatus.ahead.toString())}`);
|
|
10386
10756
|
}
|
|
10387
10757
|
console.log();
|
|
10388
|
-
console.log(
|
|
10758
|
+
console.log(chalk5.bold("Next steps:\n"));
|
|
10389
10759
|
if (trackedCount === 0) {
|
|
10390
|
-
console.log(
|
|
10391
|
-
console.log(
|
|
10760
|
+
console.log(chalk5.cyan(" tuck scan") + chalk5.dim(" - Find dotfiles to track"));
|
|
10761
|
+
console.log(chalk5.cyan(" tuck add <file>") + chalk5.dim(" - Track a specific file"));
|
|
10392
10762
|
} else if (pendingChanges > 0) {
|
|
10393
|
-
console.log(
|
|
10394
|
-
console.log(
|
|
10763
|
+
console.log(chalk5.cyan(" tuck sync") + chalk5.dim(" - Commit and push your changes"));
|
|
10764
|
+
console.log(chalk5.cyan(" tuck diff") + chalk5.dim(" - Preview what changed"));
|
|
10395
10765
|
} else if (gitStatus.ahead > 0) {
|
|
10396
|
-
console.log(
|
|
10766
|
+
console.log(chalk5.cyan(" tuck push") + chalk5.dim(" - Push commits to GitHub"));
|
|
10397
10767
|
} else {
|
|
10398
|
-
console.log(
|
|
10768
|
+
console.log(chalk5.dim(" All synced! Your dotfiles are up to date."));
|
|
10399
10769
|
console.log();
|
|
10400
|
-
console.log(
|
|
10401
|
-
console.log(
|
|
10770
|
+
console.log(chalk5.cyan(" tuck scan") + chalk5.dim(" - Find more dotfiles to track"));
|
|
10771
|
+
console.log(chalk5.cyan(" tuck list") + chalk5.dim(" - See tracked files"));
|
|
10402
10772
|
}
|
|
10403
10773
|
console.log();
|
|
10404
10774
|
} catch {
|
|
10405
10775
|
miniBanner();
|
|
10406
|
-
console.log(
|
|
10407
|
-
console.log(
|
|
10776
|
+
console.log(chalk5.yellow("Tuck directory exists but may be corrupted."));
|
|
10777
|
+
console.log(chalk5.dim("Run `tuck init` to reinitialize."));
|
|
10408
10778
|
console.log();
|
|
10409
10779
|
}
|
|
10410
10780
|
};
|