@sleekcms/sync 1.4.3 → 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/README.md +18 -3
- package/dist/AGENT.md +1 -1
- package/dist/cli.d.ts +6 -3
- package/dist/cli.js +41 -18
- package/dist/index.js +36 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,12 +48,15 @@ sleekcms --token <YOUR_AUTH_TOKEN>
|
|
|
48
48
|
## Usage
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
# Basic
|
|
51
|
+
# Basic — auto-sync on every save
|
|
52
52
|
npx @sleekcms/sync --token abc123-xxxx
|
|
53
53
|
|
|
54
|
+
# Manual sync — push changes only when you press [s]
|
|
55
|
+
npx @sleekcms/sync --token abc123-xxxx --manual
|
|
56
|
+
|
|
54
57
|
```
|
|
55
58
|
|
|
56
|
-
Once running, press `r` to re-fetch all files or `x` / `Ctrl+C` to exit.
|
|
59
|
+
Once running, press `r` to re-fetch all files or `x` / `Ctrl+C` to exit. In `--manual` mode, press `s` to push your changes on demand.
|
|
57
60
|
|
|
58
61
|
---
|
|
59
62
|
|
|
@@ -136,12 +139,24 @@ On first run, the CLI pulls the full site state from the server. After that, a l
|
|
|
136
139
|
Your editor → file save → watcher → SleekCMS API → rebuild → live site
|
|
137
140
|
```
|
|
138
141
|
|
|
139
|
-
###
|
|
142
|
+
### Manual sync mode (`--manual`)
|
|
143
|
+
|
|
144
|
+
Pass `--manual` (`-m`) to skip the live watcher. The CLI still does the initial fetch, but after that nothing is pushed until you press `s`. On each `s`, it scans the workspace, diffs against the cached state in `.cache/`, and pushes only the files that changed since your last fetch or sync — the same diff logic, run on demand instead of on every save.
|
|
145
|
+
|
|
146
|
+
This is useful when you want to batch a set of edits (or let an AI agent finish writing several files) and push them as one deliberate action.
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
Your editor → edit freely → press [s] → diff vs last fetch → SleekCMS API → live site
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Session commands
|
|
140
153
|
|
|
141
154
|
| Key | Action |
|
|
142
155
|
|---|---|
|
|
156
|
+
| `s` | Push changed files now (`--manual` mode only) |
|
|
143
157
|
| `r` | Re-fetch all files from server |
|
|
144
158
|
| `x` or `Ctrl+C` | Exit and clean up local workspace |
|
|
159
|
+
| `q` | Quit, keeping local files |
|
|
145
160
|
|
|
146
161
|
---
|
|
147
162
|
|
package/dist/AGENT.md
CHANGED
|
@@ -732,4 +732,4 @@ Template:
|
|
|
732
732
|
17. Always create RSS feed for blogs and link them in meta so it is discoverable. Use "rss.xml" as the key.
|
|
733
733
|
18. Make the sites extremely SEO friendly and sharing friendly.
|
|
734
734
|
19. When naming files for models, use - (dash) as word separator. Don't use _ (underscore) as it is mapped to / (slash) in path
|
|
735
|
-
|
|
735
|
+
20. Not all content details need to be modeled. If there is content which is not meant to be updated, such as button labels, links etc. you can inline that in the EJS view instead of adding fields to model and using from there. Decide what is relevant.
|
package/dist/cli.d.ts
CHANGED
|
@@ -6,13 +6,16 @@ export interface CliOptions {
|
|
|
6
6
|
token?: string;
|
|
7
7
|
env?: string;
|
|
8
8
|
path?: string;
|
|
9
|
+
manual?: boolean;
|
|
9
10
|
}
|
|
10
11
|
export interface KeyboardHandlers {
|
|
11
12
|
onExit?: () => void | Promise<unknown>;
|
|
13
|
+
onQuit?: () => void | Promise<unknown>;
|
|
12
14
|
onRefetch?: () => void | Promise<unknown>;
|
|
15
|
+
onSync?: () => void | Promise<unknown>;
|
|
13
16
|
}
|
|
14
17
|
export declare function parseArgs(): CliOptions;
|
|
15
18
|
export declare function prompt(question: string): Promise<string>;
|
|
16
|
-
export declare function showWatchHelp(): void;
|
|
17
|
-
export declare function setupKeyboardInput(handlers: KeyboardHandlers): void;
|
|
18
|
-
export declare function showEditorMenu(viewsDir: string, handlers: KeyboardHandlers): void;
|
|
19
|
+
export declare function showWatchHelp(manual?: boolean): void;
|
|
20
|
+
export declare function setupKeyboardInput(handlers: KeyboardHandlers, manual?: boolean): void;
|
|
21
|
+
export declare function showEditorMenu(viewsDir: string, handlers: KeyboardHandlers, manual?: boolean): void;
|
package/dist/cli.js
CHANGED
|
@@ -23,11 +23,13 @@ function parseArgs() {
|
|
|
23
23
|
.option("-t, --token <token>", "API authentication token (required)")
|
|
24
24
|
.addOption(new commander_1.Option("-e, --env <env>", "Environment (localhost, development, production)").default("production").hideHelp())
|
|
25
25
|
.option("-p, --path <path>", "Directory path for files (default: <token-prefix>-views)")
|
|
26
|
+
.option("-m, --manual", "Manual sync mode: don't auto-sync on file changes; press [s] to push diffs on demand")
|
|
26
27
|
.addHelpText("after", `
|
|
27
28
|
Examples:
|
|
28
29
|
$ cms-sync --token abc123-xxxx
|
|
29
30
|
$ cms-sync -t abc123-xxxx -e development
|
|
30
31
|
$ cms-sync -t abc123-xxxx -p ./my-templates
|
|
32
|
+
$ cms-sync -t abc123-xxxx -m # manual sync — push only when you press [s]
|
|
31
33
|
`)
|
|
32
34
|
.parse(process.argv);
|
|
33
35
|
return commander_1.program.opts();
|
|
@@ -66,10 +68,11 @@ function commandExists(cmd) {
|
|
|
66
68
|
return false;
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
|
-
function showWatchHelp() {
|
|
70
|
-
|
|
71
|
+
function showWatchHelp(manual = false) {
|
|
72
|
+
const sync = manual ? "[s] Sync changes " : "";
|
|
73
|
+
console.log(`📋 Commands: ${sync}[r] Re-fetch all files [x] Exit (cleanup) [q] Quit (keep files)\n`);
|
|
71
74
|
}
|
|
72
|
-
function setupKeyboardInput(handlers) {
|
|
75
|
+
function setupKeyboardInput(handlers, manual = false) {
|
|
73
76
|
if (process.stdin.isTTY) {
|
|
74
77
|
process.stdin.setRawMode(true);
|
|
75
78
|
rawModeEnabled = true;
|
|
@@ -84,41 +87,55 @@ function setupKeyboardInput(handlers) {
|
|
|
84
87
|
return;
|
|
85
88
|
}
|
|
86
89
|
const cmd = key.toLowerCase();
|
|
87
|
-
if (cmd === "
|
|
90
|
+
if (cmd === "s" && handlers.onSync) {
|
|
91
|
+
await handlers.onSync();
|
|
92
|
+
showWatchHelp(manual);
|
|
93
|
+
}
|
|
94
|
+
else if (cmd === "r" && handlers.onRefetch) {
|
|
88
95
|
console.log("\n🔄 Re-fetching all files...");
|
|
89
96
|
await handlers.onRefetch();
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
if (!manual)
|
|
98
|
+
console.log("👀 Watching for changes...");
|
|
99
|
+
showWatchHelp(manual);
|
|
92
100
|
}
|
|
93
101
|
else if (cmd === "x" && handlers.onExit) {
|
|
94
102
|
await handlers.onExit();
|
|
95
103
|
}
|
|
104
|
+
else if (cmd === "q" && handlers.onQuit) {
|
|
105
|
+
await handlers.onQuit();
|
|
106
|
+
}
|
|
96
107
|
});
|
|
97
108
|
}
|
|
98
109
|
const EDITOR_CANDIDATES = [
|
|
99
110
|
{ name: "VS Code", cmd: "code", args: (dir) => ["-n", dir] },
|
|
100
111
|
{ name: "Cursor", cmd: "cursor", args: (dir) => ["-n", dir] },
|
|
101
112
|
];
|
|
102
|
-
function
|
|
113
|
+
function statusLine(manual, suffix = "") {
|
|
114
|
+
const base = manual ? "✋ Manual sync mode — press [s] to push changes." : "👀 Watching for changes...";
|
|
115
|
+
console.log(`${base}${suffix}`);
|
|
116
|
+
}
|
|
117
|
+
function showEditorMenu(viewsDir, handlers, manual = false) {
|
|
103
118
|
const editors = EDITOR_CANDIDATES
|
|
104
119
|
.filter(e => commandExists(e.cmd))
|
|
105
120
|
.map((e, i) => ({ ...e, key: String(i + 1) }));
|
|
106
121
|
if (editors.length === 0) {
|
|
107
|
-
console.log("
|
|
108
|
-
|
|
109
|
-
|
|
122
|
+
console.log("");
|
|
123
|
+
statusLine(manual);
|
|
124
|
+
showWatchHelp(manual);
|
|
125
|
+
setupKeyboardInput(handlers, manual);
|
|
110
126
|
return;
|
|
111
127
|
}
|
|
112
128
|
console.log("\n📂 Open in editor:");
|
|
113
129
|
editors.forEach(e => console.log(` [${e.key}] ${e.name}`));
|
|
114
130
|
console.log(" [Enter] Skip");
|
|
115
|
-
console.log(" [x]
|
|
131
|
+
console.log(" [x] Exit (cleanup)");
|
|
132
|
+
console.log(" [q] Quit (keep files)\n");
|
|
116
133
|
const rl = readline_1.default.createInterface({
|
|
117
134
|
input: process.stdin,
|
|
118
135
|
output: process.stdout,
|
|
119
136
|
});
|
|
120
|
-
// Count lines to clear (menu header + editors + skip + quit + empty + prompt)
|
|
121
|
-
const linesToClear = editors.length +
|
|
137
|
+
// Count lines to clear (menu header + editors + skip + exit + quit + empty + prompt)
|
|
138
|
+
const linesToClear = editors.length + 6;
|
|
122
139
|
rl.question("Select editor: ", async (answer) => {
|
|
123
140
|
rl.close();
|
|
124
141
|
// Clear the menu lines
|
|
@@ -127,14 +144,20 @@ function showEditorMenu(viewsDir, handlers) {
|
|
|
127
144
|
process.stdout.write("\x1b[2K\n"); // Clear each line
|
|
128
145
|
}
|
|
129
146
|
process.stdout.write(`\x1b[${linesToClear}A`); // Move back up
|
|
130
|
-
|
|
147
|
+
const choice = answer.trim().toLowerCase();
|
|
148
|
+
if (choice === "x") {
|
|
131
149
|
if (handlers.onExit)
|
|
132
150
|
await handlers.onExit();
|
|
133
151
|
return;
|
|
134
152
|
}
|
|
153
|
+
if (choice === "q") {
|
|
154
|
+
if (handlers.onQuit)
|
|
155
|
+
await handlers.onQuit();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
135
158
|
const selected = editors.find(e => e.key === answer.trim());
|
|
136
159
|
if (selected) {
|
|
137
|
-
|
|
160
|
+
statusLine(manual, ` (opened ${selected.name})`);
|
|
138
161
|
(0, child_process_1.spawn)(selected.cmd, selected.args(viewsDir), {
|
|
139
162
|
detached: true,
|
|
140
163
|
stdio: "ignore",
|
|
@@ -142,9 +165,9 @@ function showEditorMenu(viewsDir, handlers) {
|
|
|
142
165
|
}).unref();
|
|
143
166
|
}
|
|
144
167
|
else {
|
|
145
|
-
|
|
168
|
+
statusLine(manual);
|
|
146
169
|
}
|
|
147
|
-
showWatchHelp();
|
|
148
|
-
setupKeyboardInput(handlers);
|
|
170
|
+
showWatchHelp(manual);
|
|
171
|
+
setupKeyboardInput(handlers, manual);
|
|
149
172
|
});
|
|
150
173
|
}
|
package/dist/index.js
CHANGED
|
@@ -116,6 +116,32 @@ async function handleExit() {
|
|
|
116
116
|
await cleanupFiles(VIEWS_DIR);
|
|
117
117
|
process.exit(0);
|
|
118
118
|
}
|
|
119
|
+
async function handleQuit() {
|
|
120
|
+
if (isShuttingDown)
|
|
121
|
+
return;
|
|
122
|
+
isShuttingDown = true;
|
|
123
|
+
watcher.setShuttingDown(true);
|
|
124
|
+
console.log("\n⚠️ Quitting (keeping files)...");
|
|
125
|
+
await watcher.stopWatching();
|
|
126
|
+
if (VIEWS_DIR)
|
|
127
|
+
console.log(`📁 Workspace left at: ${VIEWS_DIR}`);
|
|
128
|
+
process.exit(0);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Manual sync trigger ([s] key). Scans the workspace on demand, diffs against
|
|
132
|
+
* the cached state in .cache/state.json, and pushes only changed files.
|
|
133
|
+
*/
|
|
134
|
+
async function handleManualSync() {
|
|
135
|
+
console.log("\n🔄 Syncing changes...");
|
|
136
|
+
try {
|
|
137
|
+
const { pushed } = await runSync();
|
|
138
|
+
console.log(pushed > 0 ? `✅ Synced ${pushed} change(s).` : "✔️ No changes to sync.");
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
const e = err;
|
|
142
|
+
console.error("❌ Sync failed:", e.body || e.message);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
119
145
|
async function main() {
|
|
120
146
|
await initConfig();
|
|
121
147
|
try {
|
|
@@ -126,17 +152,24 @@ async function main() {
|
|
|
126
152
|
console.error("❌ Sync failed:", e.body || e.message);
|
|
127
153
|
process.exit(1);
|
|
128
154
|
}
|
|
129
|
-
|
|
130
|
-
|
|
155
|
+
const manual = options.manual ?? false;
|
|
156
|
+
if (!manual) {
|
|
157
|
+
watcher.init({ viewsDir: VIEWS_DIR, onSync: runSync, onIdle: handleExit });
|
|
158
|
+
watcher.monitorFiles();
|
|
159
|
+
}
|
|
131
160
|
console.log(`\n✅ Ready! Editing session started for site - ${site.name}.`);
|
|
132
161
|
console.log(`\n📁 Workspace created at: ${VIEWS_DIR}`);
|
|
133
162
|
if (ENV !== "production")
|
|
134
163
|
console.log(`🌐 Environment: ${ENV}`);
|
|
164
|
+
if (manual)
|
|
165
|
+
console.log(`\n✋ Manual sync mode — changes are pushed only when you press [s].`);
|
|
135
166
|
console.log(`\n⚠️ Files will be cleaned up on exit (Ctrl+C).`);
|
|
136
167
|
cli.showEditorMenu(VIEWS_DIR, {
|
|
137
168
|
onExit: handleExit,
|
|
169
|
+
onQuit: handleQuit,
|
|
138
170
|
onRefetch: () => runSync({ flush: true }),
|
|
139
|
-
|
|
171
|
+
...(manual ? { onSync: handleManualSync } : {}),
|
|
172
|
+
}, manual);
|
|
140
173
|
process.on("SIGINT", async () => {
|
|
141
174
|
console.log("\n🛑 Caught interrupt signal (Ctrl+C)");
|
|
142
175
|
await handleExit();
|