@hasna/loops 0.3.18 → 0.3.19

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
@@ -318,9 +318,10 @@ them with `no_tmux_dispatch=true` metadata. Use `--dry-run --json` before
318
318
  turning it into a production loop.
319
319
 
320
320
  `hygiene names` reports canonical `machine-*` or `repo-<name>-*` loop names and
321
- renames only with `--apply`. `hygiene duplicates` groups loops with the same
322
- normalized name, cwd, and schedule. `hygiene scripts` inventories loops whose
323
- command still references `~/.hasna/loops/scripts`.
321
+ renames only with `--apply`. Apply mode writes a SQLite backup under
322
+ `<LOOPS_DATA_DIR>/backups` before changing loop names. `hygiene duplicates`
323
+ groups loops with the same normalized name, cwd, and schedule. `hygiene scripts`
324
+ inventories loops whose command still references `~/.hasna/loops/scripts`.
324
325
 
325
326
  Archive loops when retiring old automation but preserving history:
326
327
 
package/dist/cli/index.js CHANGED
@@ -2196,8 +2196,10 @@ class Store {
2196
2196
 
2197
2197
  // src/cli/index.ts
2198
2198
  import { createHash as createHash2 } from "crypto";
2199
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2199
+ import { existsSync as existsSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
2200
2200
  import { spawnSync as spawnSync5 } from "child_process";
2201
+ import { join as join3 } from "path";
2202
+ import { Database as Database2 } from "bun:sqlite";
2201
2203
  import { Command } from "commander";
2202
2204
 
2203
2205
  // src/lib/format.ts
@@ -5089,7 +5091,7 @@ function buildScriptInventoryReport(store, opts = {}) {
5089
5091
  // package.json
5090
5092
  var package_default = {
5091
5093
  name: "@hasna/loops",
5092
- version: "0.3.18",
5094
+ version: "0.3.19",
5093
5095
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
5094
5096
  type: "module",
5095
5097
  main: "dist/index.js",
@@ -5742,6 +5744,19 @@ function ensureTodosTaskList(project, slug, name, description) {
5742
5744
  throw new Error(`todos task list not found after ensure: ${slug}`);
5743
5745
  return found.id;
5744
5746
  }
5747
+ function backupLoopsDatabase(reason) {
5748
+ const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\..+$/, "Z");
5749
+ const backupDir = join3(dataDir(), "backups");
5750
+ mkdirSync5(backupDir, { recursive: true, mode: 448 });
5751
+ const backupPath = join3(backupDir, `loops.db.bak-${reason}-${stamp}`);
5752
+ const db = new Database2(dbPath(), { readonly: true });
5753
+ try {
5754
+ writeFileSync3(backupPath, db.serialize(), { mode: 384 });
5755
+ } finally {
5756
+ db.close();
5757
+ }
5758
+ return backupPath;
5759
+ }
5745
5760
  function eventData(event) {
5746
5761
  const data = event.data;
5747
5762
  if (data && typeof data === "object" && !Array.isArray(data))
@@ -6560,16 +6575,20 @@ var hygiene = program.command("hygiene").description("deterministic OpenLoops hy
6560
6575
  hygiene.command("names").description("check or apply canonical machine-/repo-prefixed loop names").option("--apply", "rename loops in-place").option("--include-stopped", "include stopped loops").option("--include-inactive", "include stopped, expired, and archived loops").option("--limit <n>", "maximum loops to inspect", "1000").option("--json", "print JSON").action((opts) => {
6561
6576
  const store = new Store;
6562
6577
  try {
6578
+ const backupPath = opts.apply ? backupLoopsDatabase("name-hygiene") : undefined;
6563
6579
  const report = buildNameHygieneReport(store, {
6564
6580
  apply: Boolean(opts.apply),
6565
6581
  includeStopped: Boolean(opts.includeStopped),
6566
6582
  includeInactive: Boolean(opts.includeInactive),
6567
6583
  limit: Number(opts.limit)
6568
6584
  });
6585
+ const output = backupPath ? { ...report, backupPath } : report;
6569
6586
  if (isJson() || opts.json)
6570
- console.log(JSON.stringify(report, null, 2));
6587
+ console.log(JSON.stringify(output, null, 2));
6571
6588
  else {
6572
6589
  console.log(`hygiene_names checked=${report.checked} changed=${report.changed} applied=${report.applied}`);
6590
+ if (backupPath)
6591
+ console.log(`backup=${backupPath}`);
6573
6592
  for (const change of report.changes.filter((entry) => entry.changed)) {
6574
6593
  console.log(`${report.applied ? "renamed" : "would-rename"} ${change.id} ${change.oldName} -> ${change.newName}`);
6575
6594
  }
@@ -4419,7 +4419,7 @@ function enableStartup(result) {
4419
4419
  // package.json
4420
4420
  var package_default = {
4421
4421
  name: "@hasna/loops",
4422
- version: "0.3.18",
4422
+ version: "0.3.19",
4423
4423
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4424
4424
  type: "module",
4425
4425
  main: "dist/index.js",
package/docs/USAGE.md CHANGED
@@ -325,10 +325,11 @@ loops hygiene scripts --json
325
325
  ```
326
326
 
327
327
  `hygiene names` reports canonical `machine-*` or `repo-<name>-*` loop names and
328
- only renames when `--apply` is present. `hygiene duplicates` groups loops with
329
- the same normalized name, cwd, and schedule. `hygiene scripts` inventories loops
330
- whose command still references `~/.hasna/loops/scripts`; use it as a migration
331
- gate before deleting local scripts.
328
+ only renames when `--apply` is present. Apply mode writes a SQLite backup under
329
+ `<LOOPS_DATA_DIR>/backups` before changing loop names. `hygiene duplicates`
330
+ groups loops with the same normalized name, cwd, and schedule. `hygiene scripts`
331
+ inventories loops whose command still references `~/.hasna/loops/scripts`; use
332
+ it as a migration gate before deleting local scripts.
332
333
 
333
334
  Archive loops when retiring old automation but preserving history:
334
335
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/loops",
3
- "version": "0.3.18",
3
+ "version": "0.3.19",
4
4
  "description": "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",