@automagik/genie 4.260331.12 → 4.260331.13

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.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "genie",
13
- "version": "4.260331.12",
13
+ "version": "4.260331.13",
14
14
  "source": "./plugins/genie",
15
15
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, wish them into plans, make with parallel agents, ship as one team. A coding genie that grows with your project."
16
16
  }
@@ -2,7 +2,7 @@
2
2
  "id": "genie",
3
3
  "name": "Genie",
4
4
  "description": "Skills, agents, and hooks for the Genie CLI terminal orchestration toolkit",
5
- "version": "4.260331.12",
5
+ "version": "4.260331.13",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260331.12",
3
+ "version": "4.260331.13",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260331.12",
3
+ "version": "4.260331.13",
4
4
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
5
5
  "author": {
6
6
  "name": "Namastex Labs"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260331.12",
3
+ "version": "4.260331.13",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
@@ -0,0 +1,105 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { gzipSync } from 'node:zlib';
7
+
8
+ // ============================================================================
9
+ // getSnapshotPath — pure function, no DB needed
10
+ // ============================================================================
11
+
12
+ describe('getSnapshotPath', () => {
13
+ let tmpDir: string;
14
+
15
+ beforeEach(() => {
16
+ tmpDir = join(tmpdir(), `genie-backup-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
17
+ mkdirSync(tmpDir, { recursive: true });
18
+ spawnSync('git', ['init'], { cwd: tmpDir, stdio: 'ignore' });
19
+ });
20
+
21
+ afterEach(() => {
22
+ rmSync(tmpDir, { recursive: true, force: true });
23
+ });
24
+
25
+ test('returns path inside .genie/ at repo root', async () => {
26
+ const { getSnapshotPath } = await import('./db-backup.js');
27
+ const path = getSnapshotPath(tmpDir);
28
+ expect(path).toBe(join(tmpDir, '.genie', 'snapshot.sql.gz'));
29
+ });
30
+
31
+ test('path ends with snapshot.sql.gz', async () => {
32
+ const { getSnapshotPath } = await import('./db-backup.js');
33
+ const path = getSnapshotPath(tmpDir);
34
+ expect(path.endsWith('snapshot.sql.gz')).toBe(true);
35
+ });
36
+ });
37
+
38
+ // ============================================================================
39
+ // restore — error paths (no DB needed)
40
+ // ============================================================================
41
+
42
+ describe('restore error handling', () => {
43
+ test('throws for missing snapshot file', async () => {
44
+ const { restore } = await import('./db-backup.js');
45
+ expect(() => restore('/tmp/nonexistent-genie-snapshot-test.sql.gz')).toThrow('Snapshot not found');
46
+ });
47
+
48
+ test('throws for non-gzip file', async () => {
49
+ const { restore } = await import('./db-backup.js');
50
+ const tmpFile = join(tmpdir(), `genie-bad-snapshot-${Date.now()}.sql.gz`);
51
+ writeFileSync(tmpFile, 'not gzip data');
52
+ try {
53
+ // Should fail at decompression or psql — either way it throws
54
+ expect(() => restore(tmpFile)).toThrow();
55
+ } finally {
56
+ rmSync(tmpFile, { force: true });
57
+ }
58
+ });
59
+ });
60
+
61
+ // ============================================================================
62
+ // backup — error paths (no DB needed for these)
63
+ // ============================================================================
64
+
65
+ describe('backup error handling', () => {
66
+ let tmpDir: string;
67
+
68
+ beforeEach(() => {
69
+ tmpDir = join(tmpdir(), `genie-backup-err-${Date.now()}-${Math.random().toString(36).slice(2)}`);
70
+ mkdirSync(tmpDir, { recursive: true });
71
+ spawnSync('git', ['init'], { cwd: tmpDir, stdio: 'ignore' });
72
+ });
73
+
74
+ afterEach(() => {
75
+ rmSync(tmpDir, { recursive: true, force: true });
76
+ });
77
+
78
+ test('creates .genie directory if it does not exist', async () => {
79
+ const genieDir = join(tmpDir, '.genie');
80
+ expect(existsSync(genieDir)).toBe(false);
81
+
82
+ const { backup } = await import('./db-backup.js');
83
+ // backup will fail (no pg_dump or no DB) but should create the directory first
84
+ try {
85
+ backup(tmpDir);
86
+ } catch {
87
+ // Expected — pg_dump will fail without a running DB on this port
88
+ }
89
+ expect(existsSync(genieDir)).toBe(true);
90
+ });
91
+ });
92
+
93
+ // ============================================================================
94
+ // snapshot file format — verify gzip round-trip
95
+ // ============================================================================
96
+
97
+ describe('snapshot file format', () => {
98
+ test('gzipSync + gunzipSync round-trips SQL content', async () => {
99
+ const { gunzipSync } = await import('node:zlib');
100
+ const sql = '-- PostgreSQL dump\nCREATE TABLE test (id int);\n';
101
+ const compressed = gzipSync(Buffer.from(sql));
102
+ const decompressed = gunzipSync(compressed);
103
+ expect(decompressed.toString('utf-8')).toBe(sql);
104
+ });
105
+ });