aiblueprint-cli 1.4.24 → 1.4.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. package/claude-code-config/skills/workflow-apex-free/SKILL.md +261 -0
  2. package/claude-code-config/skills/workflow-apex-free/scripts/setup-templates.sh +100 -0
  3. package/claude-code-config/skills/workflow-apex-free/scripts/update-progress.sh +80 -0
  4. package/claude-code-config/skills/workflow-apex-free/steps/step-00-init.md +267 -0
  5. package/claude-code-config/skills/workflow-apex-free/steps/step-00b-branch.md +126 -0
  6. package/claude-code-config/skills/workflow-apex-free/steps/step-00b-economy.md +244 -0
  7. package/claude-code-config/skills/workflow-apex-free/steps/step-00b-interactive.md +153 -0
  8. package/claude-code-config/skills/workflow-apex-free/steps/step-01-analyze.md +361 -0
  9. package/claude-code-config/skills/workflow-apex-free/steps/step-02-plan.md +264 -0
  10. package/claude-code-config/skills/workflow-apex-free/steps/step-03-execute.md +239 -0
  11. package/claude-code-config/skills/workflow-apex-free/steps/step-04-validate.md +251 -0
  12. package/claude-code-config/skills/workflow-apex-free/templates/00-context.md +43 -0
  13. package/claude-code-config/skills/workflow-apex-free/templates/01-analyze.md +10 -0
  14. package/claude-code-config/skills/workflow-apex-free/templates/02-plan.md +10 -0
  15. package/claude-code-config/skills/workflow-apex-free/templates/03-execute.md +10 -0
  16. package/claude-code-config/skills/workflow-apex-free/templates/04-validate.md +10 -0
  17. package/claude-code-config/skills/workflow-apex-free/templates/README.md +176 -0
  18. package/claude-code-config/skills/workflow-apex-free/templates/step-complete.md +7 -0
  19. package/dist/cli.js +179 -37
  20. package/package.json +1 -1
@@ -0,0 +1,176 @@
1
+ # APEX Template System
2
+
3
+ ## Overview
4
+
5
+ This directory contains template files used to initialize APEX workflow outputs when save mode (`-s`) is enabled. This template system significantly reduces token usage by moving repetitive content out of step files.
6
+
7
+ ## Template Files
8
+
9
+ | Template | Purpose | Created When |
10
+ |----------|---------|--------------|
11
+ | `00-context.md` | Workflow configuration and progress tracking | Always (if save_mode) |
12
+ | `01-analyze.md` | Analysis findings | Always (if save_mode) |
13
+ | `02-plan.md` | Implementation plan | Always (if save_mode) |
14
+ | `03-execute.md` | Implementation log | Always (if save_mode) |
15
+ | `04-validate.md` | Validation results and workflow completion | Always (if save_mode) |
16
+ | `step-complete.md` | Completion marker template | Referenced in steps |
17
+
18
+ ## Template Variables
19
+
20
+ Templates use `{{variable}}` syntax for placeholders:
21
+
22
+ | Variable | Description | Example |
23
+ |----------|-------------|---------|
24
+ | `{{task_id}}` | Kebab-case task identifier | `01-add-auth-middleware` |
25
+ | `{{task_description}}` | Plain text task description | `add authentication middleware` |
26
+ | `{{timestamp}}` | ISO 8601 timestamp | `2026-01-12T10:30:00Z` |
27
+ | `{{auto_mode}}` | Auto mode flag | `true` or `false` |
28
+ | `{{save_mode}}` | Save mode flag | `true` or `false` |
29
+ | `{{economy_mode}}` | Economy mode flag | `true` or `false` |
30
+ | `{{branch_mode}}` | Branch mode flag | `true` or `false` |
31
+ | `{{interactive_mode}}` | Interactive mode flag | `true` or `false` |
32
+ | `{{branch_name}}` | Git branch name | `feature/add-auth` |
33
+ | `{{original_input}}` | Raw user input | `/apex -a -s add auth` |
34
+
35
+ ## Setup Script
36
+
37
+ ### `setup-templates.sh`
38
+
39
+ Initializes all template files in the output directory with variables replaced.
40
+
41
+ **Usage:**
42
+ ```bash
43
+ bash scripts/setup-templates.sh \
44
+ "feature_name" \
45
+ "task_description" \
46
+ "auto_mode" \
47
+ "save_mode" \
48
+ "economy_mode" \
49
+ "branch_mode" \
50
+ "interactive_mode" \
51
+ "branch_name" \
52
+ "original_input"
53
+ ```
54
+
55
+ **Output:**
56
+ ```
57
+ .claude/output/apex/01-add-auth-middleware/
58
+ ├── 00-context.md # Always created
59
+ ├── 01-analyze.md # Always created
60
+ ├── 02-plan.md # Always created
61
+ ├── 03-execute.md # Always created
62
+ └── 04-validate.md # Always created
63
+ ```
64
+
65
+ ## Progress Update Script
66
+
67
+ ### `update-progress.sh`
68
+
69
+ Updates the progress table in `00-context.md` without manual markdown editing.
70
+
71
+ **Usage:**
72
+ ```bash
73
+ bash scripts/update-progress.sh <task_id> <step_number> <step_name> <status>
74
+ ```
75
+
76
+ **Examples:**
77
+ ```bash
78
+ # Mark step 01 as in progress
79
+ bash scripts/update-progress.sh "01-add-auth" "01" "analyze" "in_progress"
80
+
81
+ # Mark step 01 as complete
82
+ bash scripts/update-progress.sh "01-add-auth" "01" "analyze" "complete"
83
+
84
+ # Mark step 02 as in progress
85
+ bash scripts/update-progress.sh "01-add-auth" "02" "plan" "in_progress"
86
+ ```
87
+
88
+ **Status Values:**
89
+ - `in_progress` → `⏳ In Progress`
90
+ - `complete` → `✓ Complete`
91
+
92
+ ## Token Savings
93
+
94
+ ### Before Optimization
95
+
96
+ Each step file contained full template content inline:
97
+
98
+ ```markdown
99
+ ### 1. Initialize Save Output (if save_mode)
100
+
101
+ **If `{save_mode}` = true:**
102
+
103
+ Create `{output_dir}/01-analyze.md`:
104
+ ```markdown
105
+ # Step 01: Analyze
106
+
107
+ **Task:** {task_description}
108
+ **Started:** {ISO timestamp}
109
+
110
+ ---
111
+
112
+ ## Context Discovery
113
+ ```
114
+
115
+ Update `00-context.md` progress:
116
+ ```markdown
117
+ | 01-analyze | ⏳ In Progress | {timestamp} |
118
+ ```
119
+ ```
120
+
121
+ **Token cost per step:** ~200 tokens × 5 steps = ~1,000 tokens
122
+
123
+ ### After Optimization
124
+
125
+ Step files now reference templates and scripts:
126
+
127
+ ```markdown
128
+ ### 1. Initialize Save Output (if save_mode)
129
+
130
+ **If `{save_mode}` = true:**
131
+
132
+ The file `{output_dir}/01-analyze.md` has already been created by the setup script.
133
+
134
+ Update progress:
135
+ ```bash
136
+ bash {skill_dir}/scripts/update-progress.sh "{task_id}" "01" "analyze" "in_progress"
137
+ ```
138
+
139
+ Append your findings to `01-analyze.md` as you work.
140
+ ```
141
+
142
+ **Token cost per step:** ~50 tokens × 5 steps = ~250 tokens
143
+
144
+ **Total savings:** ~750 tokens per workflow execution (75% reduction)
145
+
146
+ ## How It Works
147
+
148
+ 1. **Initialization (step-00-init.md):**
149
+ - Runs `setup-templates.sh` once at workflow start
150
+ - Creates all template files with variables replaced
151
+ - Output directory is ready with pre-initialized files
152
+
153
+ 2. **Each Step:**
154
+ - Runs `update-progress.sh` to mark step as "in_progress"
155
+ - Appends findings/logs to the pre-created step file
156
+ - Runs `update-progress.sh` again to mark step as "complete"
157
+
158
+ 3. **Benefits:**
159
+ - AI doesn't need to hold template content in context
160
+ - Consistent formatting across all workflows
161
+ - Easy to update templates without editing step files
162
+ - Scripts handle the tedious markdown updates
163
+
164
+ ## Updating Templates
165
+
166
+ To modify template content:
167
+
168
+ 1. Edit the template file in `templates/`
169
+ 2. Changes apply to all future workflows automatically
170
+ 3. No need to update step files
171
+
172
+ ## Maintenance
173
+
174
+ - Templates are stateless (no workflow-specific logic)
175
+ - Scripts are idempotent (safe to run multiple times)
176
+ - Variables use `{{var}}` syntax to avoid conflicts with markdown
@@ -0,0 +1,7 @@
1
+ ---
2
+
3
+ ## Step Complete
4
+
5
+ **Status:** ✓ Complete
6
+ **Next:** {{next_step}}
7
+ **Timestamp:** {{timestamp}}
package/dist/cli.js CHANGED
@@ -12963,7 +12963,7 @@ var require_signal_exit = __commonJS((exports, module) => {
12963
12963
  emitter.on(ev, cb);
12964
12964
  return remove;
12965
12965
  };
12966
- unload = function unload() {
12966
+ unload = function unload2() {
12967
12967
  if (!loaded || !processOk(global.process)) {
12968
12968
  return;
12969
12969
  }
@@ -12978,7 +12978,7 @@ var require_signal_exit = __commonJS((exports, module) => {
12978
12978
  emitter.count -= 1;
12979
12979
  };
12980
12980
  module.exports.unload = unload;
12981
- emit = function emit(event, code, signal) {
12981
+ emit = function emit2(event, code, signal) {
12982
12982
  if (emitter.emitted[event]) {
12983
12983
  return;
12984
12984
  }
@@ -13007,7 +13007,7 @@ var require_signal_exit = __commonJS((exports, module) => {
13007
13007
  return signals;
13008
13008
  };
13009
13009
  loaded = false;
13010
- load = function load() {
13010
+ load = function load2() {
13011
13011
  if (loaded || !processOk(global.process)) {
13012
13012
  return;
13013
13013
  }
@@ -13026,7 +13026,7 @@ var require_signal_exit = __commonJS((exports, module) => {
13026
13026
  };
13027
13027
  module.exports.load = load;
13028
13028
  originalProcessReallyExit = process3.reallyExit;
13029
- processReallyExit = function processReallyExit(code) {
13029
+ processReallyExit = function processReallyExit2(code) {
13030
13030
  if (!processOk(global.process)) {
13031
13031
  return;
13032
13032
  }
@@ -13036,7 +13036,7 @@ var require_signal_exit = __commonJS((exports, module) => {
13036
13036
  originalProcessReallyExit.call(process3, process3.exitCode);
13037
13037
  };
13038
13038
  originalProcessEmit = process3.emit;
13039
- processEmit = function processEmit(ev, arg) {
13039
+ processEmit = function processEmit2(ev, arg) {
13040
13040
  if (ev === "exit" && processOk(global.process)) {
13041
13041
  if (arg !== undefined) {
13042
13042
  process3.exitCode = arg;
@@ -15063,12 +15063,12 @@ var require_wcwidth = __commonJS((exports, module) => {
15063
15063
  nul: 0,
15064
15064
  control: 0
15065
15065
  };
15066
- module.exports = function wcwidth(str) {
15066
+ module.exports = function wcwidth2(str) {
15067
15067
  return wcswidth(str, DEFAULTS);
15068
15068
  };
15069
15069
  module.exports.config = function(opts) {
15070
15070
  opts = defaults(opts || {}, DEFAULTS);
15071
- return function wcwidth(str) {
15071
+ return function wcwidth2(str) {
15072
15072
  return wcswidth(str, opts);
15073
15073
  };
15074
15074
  };
@@ -15738,7 +15738,7 @@ var require__stream_writable = __commonJS((exports, module) => {
15738
15738
  }
15739
15739
  });
15740
15740
  } else {
15741
- realHasInstance = function realHasInstance(object) {
15741
+ realHasInstance = function realHasInstance2(object) {
15742
15742
  return object instanceof this;
15743
15743
  };
15744
15744
  }
@@ -16536,28 +16536,28 @@ var require_end_of_stream = __commonJS((exports, module) => {
16536
16536
  callback = once(callback || noop);
16537
16537
  var readable = opts.readable || opts.readable !== false && stream.readable;
16538
16538
  var writable = opts.writable || opts.writable !== false && stream.writable;
16539
- var onlegacyfinish = function onlegacyfinish() {
16539
+ var onlegacyfinish = function onlegacyfinish2() {
16540
16540
  if (!stream.writable)
16541
16541
  onfinish();
16542
16542
  };
16543
16543
  var writableEnded = stream._writableState && stream._writableState.finished;
16544
- var onfinish = function onfinish() {
16544
+ var onfinish = function onfinish2() {
16545
16545
  writable = false;
16546
16546
  writableEnded = true;
16547
16547
  if (!readable)
16548
16548
  callback.call(stream);
16549
16549
  };
16550
16550
  var readableEnded = stream._readableState && stream._readableState.endEmitted;
16551
- var onend = function onend() {
16551
+ var onend = function onend2() {
16552
16552
  readable = false;
16553
16553
  readableEnded = true;
16554
16554
  if (!writable)
16555
16555
  callback.call(stream);
16556
16556
  };
16557
- var onerror = function onerror(err) {
16557
+ var onerror = function onerror2(err) {
16558
16558
  callback.call(stream, err);
16559
16559
  };
16560
- var onclose = function onclose() {
16560
+ var onclose = function onclose2() {
16561
16561
  var err;
16562
16562
  if (readable && !readableEnded) {
16563
16563
  if (!stream._readableState || !stream._readableState.ended)
@@ -16570,7 +16570,7 @@ var require_end_of_stream = __commonJS((exports, module) => {
16570
16570
  return callback.call(stream, err);
16571
16571
  }
16572
16572
  };
16573
- var onrequest = function onrequest() {
16573
+ var onrequest = function onrequest2() {
16574
16574
  stream.req.on("finish", onfinish);
16575
16575
  };
16576
16576
  if (isRequest(stream)) {
@@ -16727,7 +16727,7 @@ var require_async_iterator = __commonJS((exports, module) => {
16727
16727
  });
16728
16728
  });
16729
16729
  }), _Object$setPrototypeO), AsyncIteratorPrototype);
16730
- var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) {
16730
+ var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator2(stream) {
16731
16731
  var _Object$create;
16732
16732
  var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, {
16733
16733
  value: stream,
@@ -16916,7 +16916,7 @@ var require__stream_readable = __commonJS((exports, module) => {
16916
16916
  var Duplex;
16917
16917
  Readable.ReadableState = ReadableState;
16918
16918
  var EE = __require("events").EventEmitter;
16919
- var EElistenerCount = function EElistenerCount(emitter, type) {
16919
+ var EElistenerCount = function EElistenerCount2(emitter, type) {
16920
16920
  return emitter.listeners(type).length;
16921
16921
  };
16922
16922
  var Stream = __require("stream");
@@ -16933,7 +16933,7 @@ var require__stream_readable = __commonJS((exports, module) => {
16933
16933
  if (debugUtil && debugUtil.debuglog) {
16934
16934
  debug = debugUtil.debuglog("stream");
16935
16935
  } else {
16936
- debug = function debug() {};
16936
+ debug = function debug2() {};
16937
16937
  }
16938
16938
  var BufferList = require_buffer_list();
16939
16939
  var destroyImpl = require_destroy();
@@ -17993,14 +17993,14 @@ var require_BufferList = __commonJS((exports, module) => {
17993
17993
  if (srcEnd <= 0) {
17994
17994
  return dst || Buffer2.alloc(0);
17995
17995
  }
17996
- const copy = !!dst;
17996
+ const copy2 = !!dst;
17997
17997
  const off = this._offset(srcStart);
17998
17998
  const len = srcEnd - srcStart;
17999
17999
  let bytes = len;
18000
- let bufoff = copy && dstStart || 0;
18000
+ let bufoff = copy2 && dstStart || 0;
18001
18001
  let start = off[1];
18002
18002
  if (srcStart === 0 && srcEnd === this.length) {
18003
- if (!copy) {
18003
+ if (!copy2) {
18004
18004
  return this._bufs.length === 1 ? this._bufs[0] : Buffer2.concat(this._bufs, this.length);
18005
18005
  }
18006
18006
  for (let i = 0;i < this._bufs.length; i++) {
@@ -18010,9 +18010,9 @@ var require_BufferList = __commonJS((exports, module) => {
18010
18010
  return dst;
18011
18011
  }
18012
18012
  if (bytes <= this._bufs[off[0]].length - start) {
18013
- return copy ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes) : this._bufs[off[0]].slice(start, start + bytes);
18013
+ return copy2 ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes) : this._bufs[off[0]].slice(start, start + bytes);
18014
18014
  }
18015
- if (!copy) {
18015
+ if (!copy2) {
18016
18016
  dst = Buffer2.allocUnsafe(len);
18017
18017
  }
18018
18018
  for (let i = off[0];i < this._bufs.length; i++) {
@@ -18234,7 +18234,7 @@ var require_bl = __commonJS((exports, module) => {
18234
18234
  }
18235
18235
  if (typeof callback === "function") {
18236
18236
  this._callback = callback;
18237
- const piper = function piper(err) {
18237
+ const piper = function piper2(err) {
18238
18238
  if (this._callback) {
18239
18239
  this._callback(err);
18240
18240
  this._callback = null;
@@ -28633,9 +28633,9 @@ GFS4: `);
28633
28633
  function readdir(path2, options, cb) {
28634
28634
  if (typeof options === "function")
28635
28635
  cb = options, options = null;
28636
- var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir(path3, options2, cb2, startTime) {
28636
+ var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(path3, options2, cb2, startTime) {
28637
28637
  return fs$readdir(path3, fs$readdirCallback(path3, options2, cb2, startTime));
28638
- } : function go$readdir(path3, options2, cb2, startTime) {
28638
+ } : function go$readdir2(path3, options2, cb2, startTime) {
28639
28639
  return fs$readdir(path3, options2, fs$readdirCallback(path3, options2, cb2, startTime));
28640
28640
  };
28641
28641
  return go$readdir(path2, options, cb);
@@ -29482,7 +29482,7 @@ var require_empty2 = __commonJS((exports, module) => {
29482
29482
  var path2 = __require("path");
29483
29483
  var mkdir = require_mkdirs();
29484
29484
  var remove = require_remove();
29485
- var emptyDir = u(async function emptyDir(dir) {
29485
+ var emptyDir = u(async function emptyDir2(dir) {
29486
29486
  let items;
29487
29487
  try {
29488
29488
  items = await fs.readdir(dir);
@@ -33301,6 +33301,44 @@ function formatDate(date) {
33301
33301
  const pad = (n) => n.toString().padStart(2, "0");
33302
33302
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;
33303
33303
  }
33304
+ async function listBackups() {
33305
+ const exists = await import_fs_extra5.default.pathExists(BACKUP_BASE_DIR);
33306
+ if (!exists) {
33307
+ return [];
33308
+ }
33309
+ const entries = await import_fs_extra5.default.readdir(BACKUP_BASE_DIR, { withFileTypes: true });
33310
+ const backups = [];
33311
+ for (const entry of entries) {
33312
+ if (!entry.isDirectory())
33313
+ continue;
33314
+ const match = entry.name.match(/^(\d{4})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})$/);
33315
+ if (!match)
33316
+ continue;
33317
+ const [, year, month, day, hour, minute, second] = match;
33318
+ const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day), parseInt(hour), parseInt(minute), parseInt(second));
33319
+ backups.push({
33320
+ name: entry.name,
33321
+ path: path7.join(BACKUP_BASE_DIR, entry.name),
33322
+ date
33323
+ });
33324
+ }
33325
+ return backups.sort((a, b) => b.date.getTime() - a.date.getTime());
33326
+ }
33327
+ async function loadBackup(backupPath, claudeDir) {
33328
+ const exists = await import_fs_extra5.default.pathExists(backupPath);
33329
+ if (!exists) {
33330
+ throw new Error(`Backup not found: ${backupPath}`);
33331
+ }
33332
+ await import_fs_extra5.default.ensureDir(claudeDir);
33333
+ const itemsToCopy = ["commands", "agents", "skills", "scripts", "song", "settings.json"];
33334
+ for (const item of itemsToCopy) {
33335
+ const sourcePath = path7.join(backupPath, item);
33336
+ const destPath = path7.join(claudeDir, item);
33337
+ if (await import_fs_extra5.default.pathExists(sourcePath)) {
33338
+ await import_fs_extra5.default.copy(sourcePath, destPath, { overwrite: true });
33339
+ }
33340
+ }
33341
+ }
33304
33342
  async function createBackup(claudeDir) {
33305
33343
  const exists = await import_fs_extra5.default.pathExists(claudeDir);
33306
33344
  if (!exists) {
@@ -33459,8 +33497,13 @@ async function setupCommand(params = {}) {
33459
33497
  }
33460
33498
  if (options.aiblueprintCommands) {
33461
33499
  s.start("Setting up AIBlueprint commands");
33462
- await import_fs_extra6.default.copy(path8.join(sourceDir, "commands"), path8.join(claudeDir, "commands"), { overwrite: true });
33463
- s.stop("Commands installed");
33500
+ const commandsSourcePath = path8.join(sourceDir, "commands");
33501
+ if (await import_fs_extra6.default.pathExists(commandsSourcePath)) {
33502
+ await import_fs_extra6.default.copy(commandsSourcePath, path8.join(claudeDir, "commands"), { overwrite: true });
33503
+ s.stop("Commands installed");
33504
+ } else {
33505
+ s.stop("Commands not available in repository");
33506
+ }
33464
33507
  }
33465
33508
  if (options.codexSymlink && options.aiblueprintCommands) {
33466
33509
  s.start("Setting up Codex symlink");
@@ -35732,6 +35775,22 @@ async function proSyncCommand(options = {}) {
35732
35775
  if (syncMode === "updates") {
35733
35776
  selectedItems = [...newItems, ...modifiedItems];
35734
35777
  } else if (syncMode === "updates_and_delete") {
35778
+ M2.message("");
35779
+ M2.message(source_default.red.bold("⚠️ WARNING: DESTRUCTIVE ACTION"));
35780
+ M2.message(source_default.red("━".repeat(50)));
35781
+ M2.message(source_default.red("All your custom skills, commands, agents, and configuration files"));
35782
+ M2.message(source_default.red("that are not in the premium version will be PERMANENTLY DELETED"));
35783
+ M2.message(source_default.red("and replaced by the new version."));
35784
+ M2.message(source_default.red("━".repeat(50)));
35785
+ M2.message("");
35786
+ const deleteConfirm = await ye({
35787
+ message: source_default.red.bold("Are you sure you want to delete and replace all files?"),
35788
+ initialValue: false
35789
+ });
35790
+ if (pD(deleteConfirm) || !deleteConfirm) {
35791
+ xe("Sync cancelled");
35792
+ process.exit(0);
35793
+ }
35735
35794
  selectedItems = [...newItems, ...modifiedItems, ...deletedItems];
35736
35795
  } else {
35737
35796
  const fileChoices = choices.filter((c) => c.value.type !== "hook");
@@ -35879,13 +35938,90 @@ async function proSyncCommand(options = {}) {
35879
35938
  }
35880
35939
  }
35881
35940
 
35941
+ // src/commands/backup.ts
35942
+ import os15 from "os";
35943
+ import path15 from "path";
35944
+ function formatBackupDate(date) {
35945
+ const now = new Date;
35946
+ const diffMs = now.getTime() - date.getTime();
35947
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
35948
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
35949
+ const diffMinutes = Math.floor(diffMs / (1000 * 60));
35950
+ let relative;
35951
+ if (diffMinutes < 60) {
35952
+ relative = `${diffMinutes} minute${diffMinutes !== 1 ? "s" : ""} ago`;
35953
+ } else if (diffHours < 24) {
35954
+ relative = `${diffHours} hour${diffHours !== 1 ? "s" : ""} ago`;
35955
+ } else if (diffDays < 7) {
35956
+ relative = `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
35957
+ } else {
35958
+ relative = date.toLocaleDateString();
35959
+ }
35960
+ return `${date.toLocaleString()} (${relative})`;
35961
+ }
35962
+ async function backupLoadCommand(options = {}) {
35963
+ const claudeDir = options.folder || path15.join(os15.homedir(), ".claude");
35964
+ Ie(source_default.blue("\uD83D\uDCE6 Load Backup"));
35965
+ const spinner = Y2();
35966
+ spinner.start("Scanning for backups...");
35967
+ const backups = await listBackups();
35968
+ spinner.stop(`Found ${backups.length} backup${backups.length !== 1 ? "s" : ""}`);
35969
+ if (backups.length === 0) {
35970
+ M2.warn("No backups found in ~/.config/aiblueprint/backup/");
35971
+ M2.info("Backups are created automatically when you run setup or sync commands.");
35972
+ Se(source_default.gray("Nothing to restore"));
35973
+ return;
35974
+ }
35975
+ const backupOptions = backups.map((backup) => ({
35976
+ value: backup,
35977
+ label: backup.name,
35978
+ hint: formatBackupDate(backup.date)
35979
+ }));
35980
+ const selected = await ve({
35981
+ message: "Select a backup to restore:",
35982
+ options: backupOptions
35983
+ });
35984
+ if (pD(selected)) {
35985
+ xe("Operation cancelled");
35986
+ process.exit(0);
35987
+ }
35988
+ M2.info(`Selected: ${source_default.cyan(selected.name)}`);
35989
+ M2.info(`Date: ${source_default.gray(formatBackupDate(selected.date))}`);
35990
+ const confirm = await ye({
35991
+ message: `This will overwrite your current configuration in ${source_default.cyan(claudeDir)}. Continue?`,
35992
+ initialValue: false
35993
+ });
35994
+ if (pD(confirm) || !confirm) {
35995
+ xe("Operation cancelled");
35996
+ process.exit(0);
35997
+ }
35998
+ spinner.start("Creating backup of current configuration...");
35999
+ const currentBackup = await createBackup(claudeDir);
36000
+ if (currentBackup) {
36001
+ spinner.stop(`Current config backed up to: ${source_default.gray(currentBackup)}`);
36002
+ } else {
36003
+ spinner.stop("No current config to backup");
36004
+ }
36005
+ spinner.start("Restoring backup...");
36006
+ try {
36007
+ await loadBackup(selected.path, claudeDir);
36008
+ spinner.stop("Backup restored successfully");
36009
+ M2.success(`Restored configuration from ${source_default.cyan(selected.name)}`);
36010
+ Se(source_default.green("✅ Backup loaded successfully"));
36011
+ } catch (error) {
36012
+ spinner.stop("Restore failed");
36013
+ M2.error(`Failed to restore backup: ${error}`);
36014
+ process.exit(1);
36015
+ }
36016
+ }
36017
+
35882
36018
  // src/commands/dynamic-scripts.ts
35883
- import path17 from "path";
36019
+ import path18 from "path";
35884
36020
  import { homedir } from "os";
35885
36021
 
35886
36022
  // src/lib/script-parser.ts
35887
36023
  var import_fs_extra12 = __toESM(require_lib4(), 1);
35888
- import path15 from "path";
36024
+ import path16 from "path";
35889
36025
  var EXCLUDED_SCRIPTS = ["test", "lint", "format", "start"];
35890
36026
  var EXCLUDED_SUFFIXES = [":test", ":lint", ":test-fixtures", ":start"];
35891
36027
  function shouldIncludeScript(scriptName) {
@@ -35896,7 +36032,7 @@ function shouldIncludeScript(scriptName) {
35896
36032
  return true;
35897
36033
  }
35898
36034
  async function readScriptsPackageJson(claudeDir) {
35899
- const packageJsonPath = path15.join(claudeDir, "scripts", "package.json");
36035
+ const packageJsonPath = path16.join(claudeDir, "scripts", "package.json");
35900
36036
  try {
35901
36037
  if (!await import_fs_extra12.default.pathExists(packageJsonPath)) {
35902
36038
  return null;
@@ -35948,11 +36084,11 @@ function groupScriptsByPrefix(commands) {
35948
36084
  var import_fs_extra13 = __toESM(require_lib4(), 1);
35949
36085
  import { spawn as spawn2 } from "child_process";
35950
36086
  import { execSync as execSync4 } from "child_process";
35951
- import path16 from "path";
35952
- import os15 from "os";
36087
+ import path17 from "path";
36088
+ import os16 from "os";
35953
36089
  function checkCommand(cmd) {
35954
36090
  try {
35955
- const isWindows = os15.platform() === "win32";
36091
+ const isWindows = os16.platform() === "win32";
35956
36092
  const whichCmd = isWindows ? `where ${cmd}` : `which ${cmd}`;
35957
36093
  execSync4(whichCmd, { stdio: "ignore" });
35958
36094
  return true;
@@ -35978,13 +36114,13 @@ async function executeScript(scriptName, claudeDir) {
35978
36114
  console.error(source_default.red("Bun is not installed. Install with: npm install -g bun"));
35979
36115
  return 1;
35980
36116
  }
35981
- const scriptsDir = path16.join(claudeDir, "scripts");
36117
+ const scriptsDir = path17.join(claudeDir, "scripts");
35982
36118
  if (!await import_fs_extra13.default.pathExists(scriptsDir)) {
35983
36119
  console.error(source_default.red(`Scripts directory not found at ${scriptsDir}`));
35984
36120
  console.log(source_default.gray("Run: aiblueprint claude-code setup"));
35985
36121
  return 1;
35986
36122
  }
35987
- const packageJsonPath = path16.join(scriptsDir, "package.json");
36123
+ const packageJsonPath = path17.join(scriptsDir, "package.json");
35988
36124
  if (!await import_fs_extra13.default.pathExists(packageJsonPath)) {
35989
36125
  console.error(source_default.red(`package.json not found in ${scriptsDir}`));
35990
36126
  return 1;
@@ -36012,7 +36148,7 @@ async function executeScript(scriptName, claudeDir) {
36012
36148
 
36013
36149
  // src/commands/dynamic-scripts.ts
36014
36150
  function getClaudeDir(parentOptions) {
36015
- return parentOptions.claudeCodeFolder || parentOptions.folder ? path17.resolve(parentOptions.claudeCodeFolder || parentOptions.folder) : path17.join(homedir(), ".claude");
36151
+ return parentOptions.claudeCodeFolder || parentOptions.folder ? path18.resolve(parentOptions.claudeCodeFolder || parentOptions.folder) : path18.join(homedir(), ".claude");
36016
36152
  }
36017
36153
  async function registerDynamicScriptCommands(claudeCodeCmd, claudeDir) {
36018
36154
  const scripts = await readScriptsPackageJson(claudeDir);
@@ -36095,6 +36231,12 @@ proCmd.command("sync").description("Sync premium configurations with selective u
36095
36231
  const claudeCodeFolder = parentOptions.claudeCodeFolder || parentOptions.folder;
36096
36232
  proSyncCommand({ folder: claudeCodeFolder });
36097
36233
  });
36234
+ var backupCmd = claudeCodeCmd.command("backup").description("Manage Claude Code configuration backups");
36235
+ backupCmd.command("load").description("Load a previous backup interactively").action((options, command) => {
36236
+ const parentOptions = command.parent.parent.opts();
36237
+ const claudeCodeFolder = parentOptions.claudeCodeFolder || parentOptions.folder;
36238
+ backupLoadCommand({ folder: claudeCodeFolder });
36239
+ });
36098
36240
  try {
36099
36241
  const claudeDir = join2(homedir2(), ".claude");
36100
36242
  await registerDynamicScriptCommands(claudeCodeCmd, claudeDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiblueprint-cli",
3
- "version": "1.4.24",
3
+ "version": "1.4.26",
4
4
  "description": "AIBlueprint CLI for setting up Claude Code configurations",
5
5
  "author": "AIBlueprint",
6
6
  "license": "MIT",