@sleekcms/sync 1.4.4 → 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 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
- ### Watch mode commands
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/cli.d.ts CHANGED
@@ -6,14 +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>;
12
13
  onQuit?: () => void | Promise<unknown>;
13
14
  onRefetch?: () => void | Promise<unknown>;
15
+ onSync?: () => void | Promise<unknown>;
14
16
  }
15
17
  export declare function parseArgs(): CliOptions;
16
18
  export declare function prompt(question: string): Promise<string>;
17
- export declare function showWatchHelp(): void;
18
- export declare function setupKeyboardInput(handlers: KeyboardHandlers): void;
19
- 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
- console.log("📋 Commands: [r] Re-fetch all files [x] Exit (cleanup) [q] Quit (keep files)\n");
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,11 +87,16 @@ function setupKeyboardInput(handlers) {
84
87
  return;
85
88
  }
86
89
  const cmd = key.toLowerCase();
87
- if (cmd === "r" && handlers.onRefetch) {
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
- console.log("👀 Watching for changes...");
91
- showWatchHelp();
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();
@@ -102,14 +110,19 @@ const EDITOR_CANDIDATES = [
102
110
  { name: "VS Code", cmd: "code", args: (dir) => ["-n", dir] },
103
111
  { name: "Cursor", cmd: "cursor", args: (dir) => ["-n", dir] },
104
112
  ];
105
- function showEditorMenu(viewsDir, handlers) {
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) {
106
118
  const editors = EDITOR_CANDIDATES
107
119
  .filter(e => commandExists(e.cmd))
108
120
  .map((e, i) => ({ ...e, key: String(i + 1) }));
109
121
  if (editors.length === 0) {
110
- console.log("\n👀 Watching for changes...");
111
- showWatchHelp();
112
- setupKeyboardInput(handlers);
122
+ console.log("");
123
+ statusLine(manual);
124
+ showWatchHelp(manual);
125
+ setupKeyboardInput(handlers, manual);
113
126
  return;
114
127
  }
115
128
  console.log("\n📂 Open in editor:");
@@ -144,7 +157,7 @@ function showEditorMenu(viewsDir, handlers) {
144
157
  }
145
158
  const selected = editors.find(e => e.key === answer.trim());
146
159
  if (selected) {
147
- console.log(`👀 Watching for changes... (opened ${selected.name})`);
160
+ statusLine(manual, ` (opened ${selected.name})`);
148
161
  (0, child_process_1.spawn)(selected.cmd, selected.args(viewsDir), {
149
162
  detached: true,
150
163
  stdio: "ignore",
@@ -152,9 +165,9 @@ function showEditorMenu(viewsDir, handlers) {
152
165
  }).unref();
153
166
  }
154
167
  else {
155
- console.log("👀 Watching for changes...");
168
+ statusLine(manual);
156
169
  }
157
- showWatchHelp();
158
- setupKeyboardInput(handlers);
170
+ showWatchHelp(manual);
171
+ setupKeyboardInput(handlers, manual);
159
172
  });
160
173
  }
package/dist/index.js CHANGED
@@ -127,6 +127,21 @@ async function handleQuit() {
127
127
  console.log(`📁 Workspace left at: ${VIEWS_DIR}`);
128
128
  process.exit(0);
129
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
+ }
130
145
  async function main() {
131
146
  await initConfig();
132
147
  try {
@@ -137,18 +152,24 @@ async function main() {
137
152
  console.error("❌ Sync failed:", e.body || e.message);
138
153
  process.exit(1);
139
154
  }
140
- watcher.init({ viewsDir: VIEWS_DIR, onSync: runSync, onIdle: handleExit });
141
- watcher.monitorFiles();
155
+ const manual = options.manual ?? false;
156
+ if (!manual) {
157
+ watcher.init({ viewsDir: VIEWS_DIR, onSync: runSync, onIdle: handleExit });
158
+ watcher.monitorFiles();
159
+ }
142
160
  console.log(`\n✅ Ready! Editing session started for site - ${site.name}.`);
143
161
  console.log(`\n📁 Workspace created at: ${VIEWS_DIR}`);
144
162
  if (ENV !== "production")
145
163
  console.log(`🌐 Environment: ${ENV}`);
164
+ if (manual)
165
+ console.log(`\n✋ Manual sync mode — changes are pushed only when you press [s].`);
146
166
  console.log(`\n⚠️ Files will be cleaned up on exit (Ctrl+C).`);
147
167
  cli.showEditorMenu(VIEWS_DIR, {
148
168
  onExit: handleExit,
149
169
  onQuit: handleQuit,
150
170
  onRefetch: () => runSync({ flush: true }),
151
- });
171
+ ...(manual ? { onSync: handleManualSync } : {}),
172
+ }, manual);
152
173
  process.on("SIGINT", async () => {
153
174
  console.log("\n🛑 Caught interrupt signal (Ctrl+C)");
154
175
  await handleExit();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleekcms/sync",
3
- "version": "1.4.4",
3
+ "version": "1.5.0",
4
4
  "description": "Edit SleekCMS sites locally — models, content, templates, images — with live two-way sync and AI agent support.",
5
5
  "keywords": [
6
6
  "sleekcms",