dev-prism 0.1.0 → 0.3.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.
Files changed (45) hide show
  1. package/README.md +65 -21
  2. package/bin/dev-prism.js +20 -233
  3. package/dist/chunk-35SHBLIZ.js +69 -0
  4. package/dist/chunk-3ATDGV4Y.js +22 -0
  5. package/dist/chunk-3MSC3CGG.js +78 -0
  6. package/dist/chunk-5KDDYO6Y.js +168 -0
  7. package/dist/chunk-6YMQTISJ.js +84 -0
  8. package/dist/chunk-7YGOMAJG.js +106 -0
  9. package/dist/chunk-AW2FJGXA.js +38 -0
  10. package/dist/chunk-C4WLIOBR.js +67 -0
  11. package/dist/chunk-D6QWWXZD.js +49 -0
  12. package/dist/chunk-GBN67HYD.js +57 -0
  13. package/dist/chunk-HCCZKLC4.js +64 -0
  14. package/dist/chunk-HZUN6NRB.js +70 -0
  15. package/dist/chunk-J36LRUXM.js +60 -0
  16. package/dist/chunk-JHR4WADC.js +200 -0
  17. package/dist/chunk-JIU574KX.js +41 -0
  18. package/dist/chunk-LDK6QMR6.js +67 -0
  19. package/dist/chunk-LOVO4P3Y.js +41 -0
  20. package/dist/chunk-P3ETW2KK.js +166 -0
  21. package/dist/chunk-PKC2ZED2.js +168 -0
  22. package/dist/chunk-PS76Q3HD.js +168 -0
  23. package/dist/chunk-QUMZI5KK.js +98 -0
  24. package/dist/chunk-SSQ7XBY2.js +30 -0
  25. package/dist/chunk-UHI2QJFI.js +200 -0
  26. package/dist/chunk-X5A6H4Q7.js +70 -0
  27. package/dist/chunk-Y3GR6XK7.js +71 -0
  28. package/dist/commands/claude.js +3 -102
  29. package/dist/commands/create.js +6 -5
  30. package/dist/commands/destroy.js +4 -3
  31. package/dist/commands/info.js +7 -0
  32. package/dist/commands/list.js +4 -4
  33. package/dist/commands/logs.js +8 -0
  34. package/dist/commands/prune.js +10 -0
  35. package/dist/commands/start.js +9 -0
  36. package/dist/commands/stop-all.js +9 -0
  37. package/dist/commands/stop.js +7 -0
  38. package/dist/index.js +60 -10
  39. package/dist/lib/config.d.ts +1 -0
  40. package/dist/lib/docker.js +1 -1
  41. package/dist/lib/env.js +3 -1
  42. package/dist/lib/ports.js +1 -1
  43. package/dist/lib/store.js +6 -0
  44. package/dist/lib/worktree.js +1 -5
  45. package/package.json +14 -4
@@ -0,0 +1,70 @@
1
+ import {
2
+ calculatePorts
3
+ } from "./chunk-PJKUD2N2.js";
4
+ import {
5
+ isRunning
6
+ } from "./chunk-GBN67HYD.js";
7
+ import {
8
+ loadConfig
9
+ } from "./chunk-25WQHUYW.js";
10
+ import {
11
+ SessionStore
12
+ } from "./chunk-6YMQTISJ.js";
13
+
14
+ // src/commands/list.ts
15
+ import { existsSync } from "fs";
16
+ import { resolve } from "path";
17
+ import chalk from "chalk";
18
+ async function listSessions(projectRoot) {
19
+ const config = await loadConfig(projectRoot);
20
+ const store = new SessionStore();
21
+ let sessions;
22
+ try {
23
+ sessions = store.listByProject(projectRoot);
24
+ } finally {
25
+ store.close();
26
+ }
27
+ if (sessions.length === 0) {
28
+ console.log(chalk.gray("No active sessions found."));
29
+ console.log(chalk.gray("\nTo create a session:"));
30
+ console.log(chalk.cyan(" dev-prism create"));
31
+ return;
32
+ }
33
+ console.log(chalk.blue("Active Sessions:"));
34
+ console.log(chalk.gray("================\n"));
35
+ for (const session of sessions) {
36
+ const status = await getSessionStatus(session.session_id, session.session_dir, session.branch, config);
37
+ printSessionStatus(status);
38
+ }
39
+ }
40
+ async function getSessionStatus(sessionId, path, branch, config) {
41
+ const ports = calculatePorts(config, sessionId);
42
+ let running = false;
43
+ const envFile = resolve(path, ".env.session");
44
+ if (existsSync(envFile)) {
45
+ running = await isRunning({ cwd: path });
46
+ }
47
+ return {
48
+ sessionId,
49
+ path,
50
+ branch,
51
+ running,
52
+ ports
53
+ };
54
+ }
55
+ function printSessionStatus(status) {
56
+ const statusIcon = status.running ? chalk.green("\u25CF") : chalk.red("\u25CB");
57
+ const statusText = status.running ? chalk.green("running") : chalk.gray("stopped");
58
+ console.log(`${statusIcon} Session ${chalk.bold(status.sessionId)} ${statusText}`);
59
+ console.log(chalk.gray(` Path: ${status.path}`));
60
+ console.log(chalk.gray(` Branch: ${status.branch}`));
61
+ console.log(chalk.gray(" Ports:"));
62
+ for (const [name, port] of Object.entries(status.ports)) {
63
+ console.log(chalk.gray(` ${name}: ${port}`));
64
+ }
65
+ console.log("");
66
+ }
67
+
68
+ export {
69
+ listSessions
70
+ };
@@ -0,0 +1,71 @@
1
+ // src/lib/worktree.ts
2
+ import { existsSync, rmSync } from "fs";
3
+ import { execa } from "execa";
4
+ async function branchExists(projectRoot, branchName) {
5
+ try {
6
+ await execa("git", ["rev-parse", "--verify", branchName], {
7
+ cwd: projectRoot,
8
+ stdio: "pipe"
9
+ });
10
+ return true;
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
15
+ function findNextSessionId(usedIds) {
16
+ for (let i = 1; i <= 999; i++) {
17
+ const sessionId = String(i).padStart(3, "0");
18
+ if (!usedIds.has(sessionId)) {
19
+ return sessionId;
20
+ }
21
+ }
22
+ throw new Error("No available session IDs (001-999 all in use)");
23
+ }
24
+ function generateDefaultBranchName(sessionId) {
25
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
26
+ return `session/${today}/${sessionId}`;
27
+ }
28
+ async function createWorktree(projectRoot, sessionDir, branchName) {
29
+ if (existsSync(sessionDir)) {
30
+ throw new Error(`Session directory already exists: ${sessionDir}`);
31
+ }
32
+ const exists = await branchExists(projectRoot, branchName);
33
+ if (exists) {
34
+ await execa("git", ["worktree", "add", sessionDir, branchName], {
35
+ cwd: projectRoot,
36
+ stdio: "inherit"
37
+ });
38
+ } else {
39
+ await execa("git", ["worktree", "add", sessionDir, "-b", branchName, "HEAD"], {
40
+ cwd: projectRoot,
41
+ stdio: "inherit"
42
+ });
43
+ }
44
+ }
45
+ async function removeWorktree(projectRoot, sessionDir, branchName) {
46
+ if (existsSync(sessionDir)) {
47
+ try {
48
+ await execa("git", ["worktree", "remove", "--force", sessionDir], {
49
+ cwd: projectRoot,
50
+ stdio: "inherit"
51
+ });
52
+ } catch {
53
+ rmSync(sessionDir, { recursive: true, force: true });
54
+ }
55
+ }
56
+ try {
57
+ await execa("git", ["branch", "-D", branchName], {
58
+ cwd: projectRoot,
59
+ stdio: "pipe"
60
+ // Don't show output, branch might not exist
61
+ });
62
+ } catch {
63
+ }
64
+ }
65
+
66
+ export {
67
+ findNextSessionId,
68
+ generateDefaultBranchName,
69
+ createWorktree,
70
+ removeWorktree
71
+ };
@@ -1,105 +1,6 @@
1
- // src/commands/claude.ts
2
- import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from "fs";
3
- import { join } from "path";
4
- import chalk from "chalk";
5
- var SKILL_CONTENT = `---
6
- allowed-tools: Bash(dev-prism *)
7
- description: Manage isolated development sessions (create, list, start, stop, destroy)
8
- ---
9
-
10
- # Dev Session Manager
11
-
12
- Manage isolated parallel development sessions using git worktrees and Docker.
13
-
14
- ## Parse Intent from: $ARGUMENTS
15
-
16
- - "create" / "new" -> dev-prism create
17
- - "list" / "status" -> dev-prism list
18
- - "start <id>" -> dev-prism start <id>
19
- - "stop <id>" -> dev-prism stop <id>
20
- - "destroy <id>" -> dev-prism destroy <id>
21
- - "logs <id>" -> dev-prism logs <id>
22
- - "stop all" -> dev-prism stop-all
23
- - "prune" -> dev-prism prune
24
-
25
- ## Commands
26
-
27
- Run from the project root (where session.config.mjs exists).
28
-
29
- After running commands, explain:
30
- 1. What happened
31
- 2. Relevant ports/paths
32
- 3. Next steps
33
-
34
- Warn before destructive operations (destroy, prune).
35
- `;
36
- var CLAUDE_MD_SECTION = `
37
- ## Dev Sessions
38
-
39
- Isolated parallel development sessions using git worktrees and Docker.
40
-
41
- ### Commands
42
- \`\`\`bash
43
- dev-prism create [id] # Create session (auto-assigns ID)
44
- dev-prism list # Show all sessions with status
45
- dev-prism start <id> # Start stopped session
46
- dev-prism stop <id> # Stop session (preserves data)
47
- dev-prism stop-all # Stop all running sessions
48
- dev-prism destroy <id> # Remove session completely
49
- dev-prism logs <id> # Stream Docker logs
50
- dev-prism prune # Remove stopped sessions
51
- \`\`\`
52
-
53
- ### Port Allocation
54
- Port = 47000 + (sessionId \xD7 100) + offset
55
-
56
- | Service | Offset | Session 001 |
57
- |---------|--------|-------------|
58
- | APP | 0 | 47100 |
59
- | WEB | 1 | 47101 |
60
- | POSTGRES| 10 | 47110 |
61
-
62
- ### AI Notes
63
- - In sessions, use DATABASE_URL from \`.env.session\`
64
- - Run \`dev-prism list\` to discover ports
65
- - Commands run from project root, not session worktrees
66
- `;
67
- async function installClaude(projectRoot, options) {
68
- const skillDir = join(projectRoot, ".claude", "commands");
69
- const skillPath = join(skillDir, "session.md");
70
- const claudeMdPath = join(projectRoot, "CLAUDE.md");
71
- if (existsSync(skillPath) && !options.force) {
72
- console.log(chalk.yellow(`Skill already exists: ${skillPath}`));
73
- console.log(chalk.gray("Use --force to overwrite"));
74
- } else {
75
- mkdirSync(skillDir, { recursive: true });
76
- writeFileSync(skillPath, SKILL_CONTENT);
77
- console.log(chalk.green(`Created: ${skillPath}`));
78
- }
79
- const marker = "## Dev Sessions";
80
- if (existsSync(claudeMdPath)) {
81
- const content = readFileSync(claudeMdPath, "utf-8");
82
- if (content.includes(marker)) {
83
- if (options.force) {
84
- const beforeSection = content.split(marker)[0];
85
- writeFileSync(claudeMdPath, beforeSection.trimEnd() + CLAUDE_MD_SECTION);
86
- console.log(chalk.green(`Updated: ${claudeMdPath}`));
87
- } else {
88
- console.log(chalk.yellow("CLAUDE.md already has Dev Sessions section"));
89
- console.log(chalk.gray("Use --force to overwrite"));
90
- }
91
- } else {
92
- appendFileSync(claudeMdPath, CLAUDE_MD_SECTION);
93
- console.log(chalk.green(`Updated: ${claudeMdPath}`));
94
- }
95
- } else {
96
- writeFileSync(claudeMdPath, `# Project
97
- ${CLAUDE_MD_SECTION}`);
98
- console.log(chalk.green(`Created: ${claudeMdPath}`));
99
- }
100
- console.log(chalk.blue("\nClaude Code integration installed!"));
101
- console.log(chalk.gray("Use /session in Claude Code to manage sessions."));
102
- }
1
+ import {
2
+ installClaude
3
+ } from "../chunk-7YGOMAJG.js";
103
4
  export {
104
5
  installClaude
105
6
  };
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  createSession
3
- } from "../chunk-QSG5CXPX.js";
4
- import "../chunk-PJKUD2N2.js";
5
- import "../chunk-GWDGC2OE.js";
3
+ } from "../chunk-JHR4WADC.js";
4
+ import "../chunk-J36LRUXM.js";
5
+ import "../chunk-3ATDGV4Y.js";
6
+ import "../chunk-Y3GR6XK7.js";
7
+ import "../chunk-GBN67HYD.js";
6
8
  import "../chunk-25WQHUYW.js";
7
- import "../chunk-VR3QWHHB.js";
8
- import "../chunk-LEHA65A7.js";
9
+ import "../chunk-6YMQTISJ.js";
9
10
  export {
10
11
  createSession
11
12
  };
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  destroySession
3
- } from "../chunk-XUWQUDLT.js";
4
- import "../chunk-GWDGC2OE.js";
3
+ } from "../chunk-3MSC3CGG.js";
4
+ import "../chunk-Y3GR6XK7.js";
5
+ import "../chunk-GBN67HYD.js";
5
6
  import "../chunk-25WQHUYW.js";
6
- import "../chunk-VR3QWHHB.js";
7
+ import "../chunk-6YMQTISJ.js";
7
8
  export {
8
9
  destroySession
9
10
  };
@@ -0,0 +1,7 @@
1
+ import {
2
+ showInfo
3
+ } from "../chunk-JIU574KX.js";
4
+ import "../chunk-GBN67HYD.js";
5
+ export {
6
+ showInfo
7
+ };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  listSessions
3
- } from "../chunk-SMFAL2VP.js";
4
- import "../chunk-PJKUD2N2.js";
5
- import "../chunk-GWDGC2OE.js";
3
+ } from "../chunk-HZUN6NRB.js";
4
+ import "../chunk-3ATDGV4Y.js";
5
+ import "../chunk-GBN67HYD.js";
6
6
  import "../chunk-25WQHUYW.js";
7
- import "../chunk-VR3QWHHB.js";
7
+ import "../chunk-6YMQTISJ.js";
8
8
  export {
9
9
  listSessions
10
10
  };
@@ -0,0 +1,8 @@
1
+ import {
2
+ streamLogs
3
+ } from "../chunk-D6QWWXZD.js";
4
+ import "../chunk-25WQHUYW.js";
5
+ import "../chunk-6YMQTISJ.js";
6
+ export {
7
+ streamLogs
8
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ pruneSessions
3
+ } from "../chunk-QUMZI5KK.js";
4
+ import "../chunk-Y3GR6XK7.js";
5
+ import "../chunk-GBN67HYD.js";
6
+ import "../chunk-25WQHUYW.js";
7
+ import "../chunk-6YMQTISJ.js";
8
+ export {
9
+ pruneSessions
10
+ };
@@ -0,0 +1,9 @@
1
+ import {
2
+ startSession
3
+ } from "../chunk-AW2FJGXA.js";
4
+ import "../chunk-GBN67HYD.js";
5
+ import "../chunk-25WQHUYW.js";
6
+ import "../chunk-6YMQTISJ.js";
7
+ export {
8
+ startSession
9
+ };
@@ -0,0 +1,9 @@
1
+ import {
2
+ stopAllSessions
3
+ } from "../chunk-C4WLIOBR.js";
4
+ import "../chunk-GBN67HYD.js";
5
+ import "../chunk-25WQHUYW.js";
6
+ import "../chunk-6YMQTISJ.js";
7
+ export {
8
+ stopAllSessions
9
+ };
@@ -0,0 +1,7 @@
1
+ import {
2
+ stopSession
3
+ } from "../chunk-SSQ7XBY2.js";
4
+ import "../chunk-6YMQTISJ.js";
5
+ export {
6
+ stopSession
7
+ };
package/dist/index.js CHANGED
@@ -1,30 +1,80 @@
1
+ import {
2
+ startSession
3
+ } from "./chunk-AW2FJGXA.js";
4
+ import {
5
+ stopAllSessions
6
+ } from "./chunk-C4WLIOBR.js";
7
+ import {
8
+ stopSession
9
+ } from "./chunk-SSQ7XBY2.js";
10
+ import {
11
+ installClaude
12
+ } from "./chunk-7YGOMAJG.js";
1
13
  import {
2
14
  createSession
3
- } from "./chunk-QSG5CXPX.js";
15
+ } from "./chunk-JHR4WADC.js";
16
+ import {
17
+ generateEnvContent,
18
+ renderAppEnv,
19
+ writeAppEnvFiles,
20
+ writeEnvFile
21
+ } from "./chunk-J36LRUXM.js";
4
22
  import {
5
23
  destroySession
6
- } from "./chunk-XUWQUDLT.js";
24
+ } from "./chunk-3MSC3CGG.js";
25
+ import {
26
+ showInfo
27
+ } from "./chunk-JIU574KX.js";
7
28
  import {
8
29
  listSessions
9
- } from "./chunk-SMFAL2VP.js";
30
+ } from "./chunk-HZUN6NRB.js";
10
31
  import {
11
32
  calculatePorts
12
- } from "./chunk-PJKUD2N2.js";
33
+ } from "./chunk-3ATDGV4Y.js";
34
+ import {
35
+ streamLogs
36
+ } from "./chunk-D6QWWXZD.js";
37
+ import {
38
+ pruneSessions
39
+ } from "./chunk-QUMZI5KK.js";
13
40
  import {
14
- findNextSessionId,
15
41
  generateDefaultBranchName
16
- } from "./chunk-GWDGC2OE.js";
42
+ } from "./chunk-Y3GR6XK7.js";
43
+ import {
44
+ down,
45
+ isRunning,
46
+ logs,
47
+ ps,
48
+ up
49
+ } from "./chunk-GBN67HYD.js";
17
50
  import {
18
51
  loadConfig
19
52
  } from "./chunk-25WQHUYW.js";
20
- import "./chunk-VR3QWHHB.js";
21
- import "./chunk-LEHA65A7.js";
53
+ import {
54
+ SessionStore
55
+ } from "./chunk-6YMQTISJ.js";
22
56
  export {
57
+ SessionStore,
23
58
  calculatePorts,
24
59
  createSession,
25
60
  destroySession,
26
- findNextSessionId,
61
+ logs as dockerLogs,
62
+ down,
27
63
  generateDefaultBranchName,
64
+ generateEnvContent,
65
+ installClaude,
66
+ isRunning,
28
67
  listSessions,
29
- loadConfig
68
+ loadConfig,
69
+ pruneSessions,
70
+ ps,
71
+ renderAppEnv,
72
+ showInfo,
73
+ startSession,
74
+ stopAllSessions,
75
+ stopSession,
76
+ streamLogs,
77
+ up,
78
+ writeAppEnvFiles,
79
+ writeEnvFile
30
80
  };
@@ -5,6 +5,7 @@ interface SessionConfig {
5
5
  ports: Record<string, number>;
6
6
  appEnv?: Record<string, Record<string, string>>;
7
7
  apps?: string[];
8
+ envFiles?: string[];
8
9
  setup: string[];
9
10
  }
10
11
  declare function loadConfig(projectRoot: string): Promise<SessionConfig>;
@@ -4,7 +4,7 @@ import {
4
4
  logs,
5
5
  ps,
6
6
  up
7
- } from "../chunk-VR3QWHHB.js";
7
+ } from "../chunk-GBN67HYD.js";
8
8
  export {
9
9
  down,
10
10
  isRunning,
package/dist/lib/env.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  generateEnvContent,
3
+ renderAppEnv,
3
4
  writeAppEnvFiles,
4
5
  writeEnvFile
5
- } from "../chunk-LEHA65A7.js";
6
+ } from "../chunk-J36LRUXM.js";
6
7
  export {
7
8
  generateEnvContent,
9
+ renderAppEnv,
8
10
  writeAppEnvFiles,
9
11
  writeEnvFile
10
12
  };
package/dist/lib/ports.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  calculatePorts,
3
3
  formatPortsTable
4
- } from "../chunk-PJKUD2N2.js";
4
+ } from "../chunk-3ATDGV4Y.js";
5
5
  export {
6
6
  calculatePorts,
7
7
  formatPortsTable
@@ -0,0 +1,6 @@
1
+ import {
2
+ SessionStore
3
+ } from "../chunk-6YMQTISJ.js";
4
+ export {
5
+ SessionStore
6
+ };
@@ -2,15 +2,11 @@ import {
2
2
  createWorktree,
3
3
  findNextSessionId,
4
4
  generateDefaultBranchName,
5
- getSessionWorktrees,
6
- listWorktrees,
7
5
  removeWorktree
8
- } from "../chunk-GWDGC2OE.js";
6
+ } from "../chunk-Y3GR6XK7.js";
9
7
  export {
10
8
  createWorktree,
11
9
  findNextSessionId,
12
10
  generateDefaultBranchName,
13
- getSessionWorktrees,
14
- listWorktrees,
15
11
  removeWorktree
16
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-prism",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Minimal CLI for isolated parallel development sessions using git worktrees and Docker Compose",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -31,19 +31,29 @@
31
31
  "main": "./dist/index.js",
32
32
  "types": "./dist/index.d.ts",
33
33
  "scripts": {
34
- "build": "tsup src/index.ts src/commands/create.ts src/commands/destroy.ts src/commands/list.ts src/commands/claude.ts src/lib/config.ts src/lib/docker.ts src/lib/env.ts src/lib/ports.ts src/lib/worktree.ts --format esm --dts",
34
+ "build": "tsup src/index.ts src/commands/create.ts src/commands/destroy.ts src/commands/list.ts src/commands/claude.ts src/commands/info.ts src/commands/start.ts src/commands/stop.ts src/commands/stop-all.ts src/commands/prune.ts src/commands/logs.ts src/lib/config.ts src/lib/docker.ts src/lib/env.ts src/lib/ports.ts src/lib/worktree.ts src/lib/store.ts --format esm --dts",
35
35
  "dev": "tsup src/index.ts --format esm --dts --watch",
36
- "typecheck": "tsc --noEmit"
36
+ "typecheck": "tsc --noEmit",
37
+ "test": "vitest run"
37
38
  },
38
39
  "dependencies": {
40
+ "better-sqlite3": "^11.7.0",
39
41
  "chalk": "^5.3.0",
40
42
  "commander": "^11.1.0",
41
43
  "execa": "^8.0.1"
42
44
  },
43
45
  "devDependencies": {
46
+ "@types/better-sqlite3": "^7.6.12",
44
47
  "@types/node": "^20.10.0",
45
48
  "tsup": "^8.0.1",
46
- "typescript": "^5.3.2"
49
+ "typescript": "^5.3.2",
50
+ "vitest": "^3.0.0"
51
+ },
52
+ "pnpm": {
53
+ "onlyBuiltDependencies": [
54
+ "better-sqlite3",
55
+ "esbuild"
56
+ ]
47
57
  },
48
58
  "engines": {
49
59
  "node": ">=18"