pmpt-cli 1.2.0 → 1.2.1

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.
@@ -0,0 +1,152 @@
1
+ import * as p from '@clack/prompts';
2
+ import { resolve, join } from 'path';
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, cpSync, rmSync } from 'fs';
4
+ import { execSync } from 'child_process';
5
+ import { isInitialized, getConfigDir, getHistoryDir, getDocsDir, initializeProject } from '../lib/config.js';
6
+ /**
7
+ * Extract zip file
8
+ * Uses native unzip command on macOS/Linux, PowerShell on Windows
9
+ */
10
+ function extractZip(zipPath, destDir) {
11
+ try {
12
+ const platform = process.platform;
13
+ mkdirSync(destDir, { recursive: true });
14
+ if (platform === 'win32') {
15
+ // PowerShell Expand-Archive
16
+ execSync(`powershell -command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`, {
17
+ stdio: 'pipe',
18
+ });
19
+ }
20
+ else {
21
+ // Unix unzip command
22
+ execSync(`unzip -o "${zipPath}" -d "${destDir}"`, {
23
+ stdio: 'pipe',
24
+ });
25
+ }
26
+ return true;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ export async function cmdImport(zipFile, options) {
33
+ if (!zipFile) {
34
+ p.log.error('Please provide a zip file path.');
35
+ p.log.info('Usage: pmpt import <file.zip>');
36
+ process.exit(1);
37
+ }
38
+ const zipPath = resolve(zipFile);
39
+ if (!existsSync(zipPath)) {
40
+ p.log.error(`File not found: ${zipPath}`);
41
+ process.exit(1);
42
+ }
43
+ if (!zipPath.endsWith('.zip')) {
44
+ p.log.error('Please provide a .zip file.');
45
+ process.exit(1);
46
+ }
47
+ p.intro('pmpt import');
48
+ const projectPath = process.cwd();
49
+ // Check if already initialized
50
+ if (isInitialized(projectPath) && !options?.force) {
51
+ const overwrite = await p.confirm({
52
+ message: 'Project already initialized. Merge imported history?',
53
+ initialValue: true,
54
+ });
55
+ if (p.isCancel(overwrite)) {
56
+ p.cancel('Import cancelled.');
57
+ process.exit(0);
58
+ }
59
+ if (!overwrite) {
60
+ p.log.info('Use --force to overwrite existing project.');
61
+ p.outro('');
62
+ return;
63
+ }
64
+ }
65
+ const s = p.spinner();
66
+ s.start('Extracting archive...');
67
+ // Create temp directory for extraction
68
+ const tempDir = join(projectPath, '.pmpt-import-temp');
69
+ rmSync(tempDir, { recursive: true, force: true });
70
+ const extracted = extractZip(zipPath, tempDir);
71
+ if (!extracted) {
72
+ rmSync(tempDir, { recursive: true, force: true });
73
+ s.stop('Extraction failed');
74
+ p.log.error('Failed to extract zip file.');
75
+ p.log.info('Make sure `unzip` command is available on your system.');
76
+ process.exit(1);
77
+ }
78
+ s.message('Importing project...');
79
+ // Read imported data
80
+ let importedPlan = null;
81
+ let importedConfig = null;
82
+ let projectName = 'imported-project';
83
+ // Read plan.json if exists
84
+ const planPath = join(tempDir, 'plan.json');
85
+ if (existsSync(planPath)) {
86
+ try {
87
+ importedPlan = JSON.parse(readFileSync(planPath, 'utf-8'));
88
+ if (importedPlan.answers?.projectName) {
89
+ projectName = importedPlan.answers.projectName;
90
+ }
91
+ }
92
+ catch {
93
+ // Ignore parse errors
94
+ }
95
+ }
96
+ // Read config.json if exists
97
+ const configPath = join(tempDir, 'config.json');
98
+ if (existsSync(configPath)) {
99
+ try {
100
+ importedConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
101
+ }
102
+ catch {
103
+ // Ignore parse errors
104
+ }
105
+ }
106
+ // Initialize project if not exists
107
+ if (!isInitialized(projectPath)) {
108
+ initializeProject(projectPath, {
109
+ trackGit: importedConfig?.trackGit ?? true,
110
+ });
111
+ }
112
+ const pmptDir = getConfigDir(projectPath);
113
+ const historyDir = getHistoryDir(projectPath);
114
+ const docsDir = getDocsDir(projectPath);
115
+ // Import history
116
+ const importedHistoryDir = join(tempDir, 'history');
117
+ if (existsSync(importedHistoryDir)) {
118
+ // Copy all version folders
119
+ cpSync(importedHistoryDir, historyDir, { recursive: true, force: true });
120
+ }
121
+ // Import docs
122
+ const importedDocsDir = join(tempDir, 'docs');
123
+ if (existsSync(importedDocsDir)) {
124
+ cpSync(importedDocsDir, docsDir, { recursive: true, force: true });
125
+ }
126
+ // Import plan progress
127
+ if (importedPlan) {
128
+ const planProgressPath = join(pmptDir, 'plan-progress.json');
129
+ writeFileSync(planProgressPath, JSON.stringify(importedPlan, null, 2), 'utf-8');
130
+ }
131
+ // Count imported versions
132
+ let versionCount = 0;
133
+ if (existsSync(historyDir)) {
134
+ const { readdirSync } = await import('fs');
135
+ versionCount = readdirSync(historyDir).filter(d => d.startsWith('v')).length;
136
+ }
137
+ // Cleanup temp directory
138
+ rmSync(tempDir, { recursive: true, force: true });
139
+ s.stop('Import complete!');
140
+ // Summary
141
+ const summary = [
142
+ `Project: ${projectName}`,
143
+ `Versions imported: ${versionCount}`,
144
+ `Location: ${pmptDir}`,
145
+ ];
146
+ p.note(summary.join('\n'), 'Import Summary');
147
+ p.log.info('Next steps:');
148
+ p.log.message(' pmpt history — View imported versions');
149
+ p.log.message(' pmpt plan — View or copy AI prompt');
150
+ p.log.message(' pmpt save — Save a new snapshot');
151
+ p.outro('Ready to continue the journey!');
152
+ }
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import { cmdPlan } from './commands/plan.js';
11
11
  import { cmdSave } from './commands/save.js';
12
12
  import { cmdSquash } from './commands/squash.js';
13
13
  import { cmdExport } from './commands/export.js';
14
+ import { cmdImport } from './commands/import.js';
14
15
  const program = new Command();
15
16
  program
16
17
  .name('pmpt')
@@ -26,6 +27,7 @@ Examples:
26
27
  $ pmpt history --compact Hide minor changes
27
28
  $ pmpt squash v2 v5 Merge versions v2-v5 into v2
28
29
  $ pmpt export Export history as shareable zip
30
+ $ pmpt import <file.zip> Import project from zip
29
31
 
30
32
  Folder structure:
31
33
  .pmpt/
@@ -67,6 +69,11 @@ program
67
69
  .description('Export project history as a shareable zip archive')
68
70
  .option('-o, --output <file>', 'Output file path')
69
71
  .action(cmdExport);
72
+ program
73
+ .command('import <file>')
74
+ .description('Import project from exported zip archive')
75
+ .option('-f, --force', 'Overwrite existing project')
76
+ .action(cmdImport);
70
77
  program
71
78
  .command('plan [path]')
72
79
  .description('Quick product planning with 5 questions — auto-generate AI prompt')
package/dist/lib/plan.js CHANGED
@@ -90,7 +90,19 @@ Add a "## Progress" section below and keep it updated:
90
90
  - [ ] Pending item
91
91
  \`\`\`
92
92
 
93
- This keeps a living record of our development journey.
93
+ Also, add a "## Snapshot Log" section to record what was built at each checkpoint:
94
+ \`\`\`
95
+ ## Snapshot Log
96
+ ### v1 - Initial Setup
97
+ - Set up project structure
98
+ - Installed dependencies: React, Tailwind
99
+
100
+ ### v2 - Auth Feature
101
+ - Implemented login/signup
102
+ - Added JWT authentication
103
+ \`\`\`
104
+
105
+ This helps others understand what was built at each version when they import this project.
94
106
  `;
95
107
  }
96
108
  // Generate plan document
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmpt-cli",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Record and share your AI-driven product development journey",
5
5
  "type": "module",
6
6
  "bin": {