pmpt-cli 1.1.0 → 1.2.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.
- package/dist/commands/export.js +206 -0
- package/dist/commands/plan.js +36 -5
- package/dist/index.js +9 -2
- package/package.json +1 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import { resolve, join, basename } from 'path';
|
|
3
|
+
import { existsSync, writeFileSync, statSync } from 'fs';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import { isInitialized, getConfigDir, getHistoryDir, loadConfig } from '../lib/config.js';
|
|
6
|
+
import { getAllSnapshots } from '../lib/history.js';
|
|
7
|
+
import { getPlanProgress } from '../lib/plan.js';
|
|
8
|
+
/**
|
|
9
|
+
* Create a zip file without external dependencies
|
|
10
|
+
* Uses native zip command on macOS/Linux, PowerShell on Windows
|
|
11
|
+
*/
|
|
12
|
+
function createZip(sourceDir, outputPath) {
|
|
13
|
+
try {
|
|
14
|
+
const platform = process.platform;
|
|
15
|
+
if (platform === 'win32') {
|
|
16
|
+
// PowerShell Compress-Archive
|
|
17
|
+
execSync(`powershell -command "Compress-Archive -Path '${sourceDir}/*' -DestinationPath '${outputPath}'"`, {
|
|
18
|
+
stdio: 'pipe',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
// Unix zip command
|
|
23
|
+
execSync(`cd "${sourceDir}" && zip -r "${outputPath}" .`, {
|
|
24
|
+
stdio: 'pipe',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generate metadata markdown file
|
|
35
|
+
*/
|
|
36
|
+
function generateMetadataMarkdown(projectPath, snapshots, planProgress, config) {
|
|
37
|
+
const answers = planProgress?.answers || {};
|
|
38
|
+
const projectName = answers.projectName || basename(projectPath);
|
|
39
|
+
// Timeline info
|
|
40
|
+
const firstSnapshot = snapshots[0];
|
|
41
|
+
const lastSnapshot = snapshots[snapshots.length - 1];
|
|
42
|
+
const startDate = firstSnapshot?.timestamp || 'N/A';
|
|
43
|
+
const lastDate = lastSnapshot?.timestamp || 'N/A';
|
|
44
|
+
// Git info from last snapshot
|
|
45
|
+
const gitInfo = lastSnapshot?.git;
|
|
46
|
+
// All unique files across snapshots
|
|
47
|
+
const allFiles = new Set();
|
|
48
|
+
for (const snapshot of snapshots) {
|
|
49
|
+
for (const file of snapshot.files) {
|
|
50
|
+
allFiles.add(file);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Parse features
|
|
54
|
+
const features = answers.coreFeatures
|
|
55
|
+
? answers.coreFeatures
|
|
56
|
+
.split(/[,;\n]/)
|
|
57
|
+
.map((f) => f.trim())
|
|
58
|
+
.filter((f) => f)
|
|
59
|
+
.map((f) => `- ${f}`)
|
|
60
|
+
.join('\n')
|
|
61
|
+
: 'Not specified';
|
|
62
|
+
let md = `# ${projectName}
|
|
63
|
+
|
|
64
|
+
> Exported from pmpt-cli
|
|
65
|
+
|
|
66
|
+
## Project Overview
|
|
67
|
+
|
|
68
|
+
**Product Idea:**
|
|
69
|
+
${answers.productIdea || 'Not specified'}
|
|
70
|
+
|
|
71
|
+
${answers.additionalContext ? `**Additional Context:**\n${answers.additionalContext}\n` : ''}
|
|
72
|
+
**Key Features:**
|
|
73
|
+
${features}
|
|
74
|
+
|
|
75
|
+
**Tech Stack:**
|
|
76
|
+
${answers.techStack || 'Not specified'}
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Export Information
|
|
81
|
+
|
|
82
|
+
| Item | Value |
|
|
83
|
+
|------|-------|
|
|
84
|
+
| Total Iterations | ${snapshots.length} |
|
|
85
|
+
| First Version | ${startDate} |
|
|
86
|
+
| Last Version | ${lastDate} |
|
|
87
|
+
| Files Tracked | ${allFiles.size} |
|
|
88
|
+
`;
|
|
89
|
+
if (gitInfo) {
|
|
90
|
+
md += `| Git Branch | ${gitInfo.branch} |
|
|
91
|
+
| Last Commit | ${gitInfo.commit} |
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
md += `
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Version Timeline
|
|
98
|
+
|
|
99
|
+
| Version | Date | Files | Git |
|
|
100
|
+
|---------|------|-------|-----|
|
|
101
|
+
`;
|
|
102
|
+
for (const snapshot of snapshots) {
|
|
103
|
+
const gitCol = snapshot.git
|
|
104
|
+
? `${snapshot.git.commit}${snapshot.git.dirty ? ' (dirty)' : ''}`
|
|
105
|
+
: '-';
|
|
106
|
+
md += `| v${snapshot.version} | ${snapshot.timestamp} | ${snapshot.files.length} | ${gitCol} |\n`;
|
|
107
|
+
}
|
|
108
|
+
md += `
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## File List
|
|
112
|
+
|
|
113
|
+
`;
|
|
114
|
+
for (const file of Array.from(allFiles).sort()) {
|
|
115
|
+
md += `- ${file}\n`;
|
|
116
|
+
}
|
|
117
|
+
md += `
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
*Generated by [pmpt-cli](https://pmptwiki.com)*
|
|
121
|
+
`;
|
|
122
|
+
return md;
|
|
123
|
+
}
|
|
124
|
+
export async function cmdExport(path, options) {
|
|
125
|
+
const projectPath = path ? resolve(path) : process.cwd();
|
|
126
|
+
if (!isInitialized(projectPath)) {
|
|
127
|
+
p.log.error('Project not initialized. Run `pmpt init` first.');
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
p.intro('pmpt export');
|
|
131
|
+
const config = loadConfig(projectPath);
|
|
132
|
+
const snapshots = getAllSnapshots(projectPath);
|
|
133
|
+
const planProgress = getPlanProgress(projectPath);
|
|
134
|
+
if (snapshots.length === 0) {
|
|
135
|
+
p.log.warn('No snapshots found.');
|
|
136
|
+
p.log.info('Run `pmpt save` or `pmpt plan` first to create versions.');
|
|
137
|
+
p.outro('');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const projectName = planProgress?.answers?.projectName || basename(projectPath);
|
|
141
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 10);
|
|
142
|
+
const exportName = `pmpt-export-${projectName}-${timestamp}`;
|
|
143
|
+
// Output path
|
|
144
|
+
const outputPath = options?.output
|
|
145
|
+
? resolve(options.output)
|
|
146
|
+
: resolve(projectPath, `${exportName}.zip`);
|
|
147
|
+
const s = p.spinner();
|
|
148
|
+
s.start('Preparing export...');
|
|
149
|
+
// Create temp directory for export
|
|
150
|
+
const tempDir = join(projectPath, '.pmpt', '.export-temp');
|
|
151
|
+
execSync(`rm -rf "${tempDir}" && mkdir -p "${tempDir}"`, { stdio: 'pipe' });
|
|
152
|
+
// Copy history folder
|
|
153
|
+
const historyDir = getHistoryDir(projectPath);
|
|
154
|
+
execSync(`cp -r "${historyDir}" "${tempDir}/history"`, { stdio: 'pipe' });
|
|
155
|
+
// Generate and save metadata
|
|
156
|
+
const metadataContent = generateMetadataMarkdown(projectPath, snapshots, planProgress, config);
|
|
157
|
+
writeFileSync(join(tempDir, 'README.md'), metadataContent, 'utf-8');
|
|
158
|
+
// Copy current docs if exists
|
|
159
|
+
const docsDir = join(getConfigDir(projectPath), 'docs');
|
|
160
|
+
if (existsSync(docsDir)) {
|
|
161
|
+
execSync(`cp -r "${docsDir}" "${tempDir}/docs"`, { stdio: 'pipe' });
|
|
162
|
+
}
|
|
163
|
+
// Copy config (without sensitive data)
|
|
164
|
+
if (config) {
|
|
165
|
+
const exportConfig = {
|
|
166
|
+
projectPath: basename(projectPath),
|
|
167
|
+
docsPath: config.docsPath,
|
|
168
|
+
createdAt: config.createdAt,
|
|
169
|
+
trackGit: config.trackGit,
|
|
170
|
+
};
|
|
171
|
+
writeFileSync(join(tempDir, 'config.json'), JSON.stringify(exportConfig, null, 2), 'utf-8');
|
|
172
|
+
}
|
|
173
|
+
// Copy plan progress if exists
|
|
174
|
+
if (planProgress) {
|
|
175
|
+
writeFileSync(join(tempDir, 'plan.json'), JSON.stringify(planProgress, null, 2), 'utf-8');
|
|
176
|
+
}
|
|
177
|
+
s.message('Creating archive...');
|
|
178
|
+
// Create zip
|
|
179
|
+
const zipSuccess = createZip(tempDir, outputPath);
|
|
180
|
+
// Cleanup temp directory
|
|
181
|
+
execSync(`rm -rf "${tempDir}"`, { stdio: 'pipe' });
|
|
182
|
+
if (!zipSuccess) {
|
|
183
|
+
s.stop('Export failed');
|
|
184
|
+
p.log.error('Failed to create zip archive.');
|
|
185
|
+
p.log.info('Make sure `zip` command is available on your system.');
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
s.stop('Export complete!');
|
|
189
|
+
// Summary
|
|
190
|
+
const fileSizeBytes = statSync(outputPath).size;
|
|
191
|
+
const fileSize = fileSizeBytes < 1024
|
|
192
|
+
? `${fileSizeBytes} B`
|
|
193
|
+
: fileSizeBytes < 1024 * 1024
|
|
194
|
+
? `${(fileSizeBytes / 1024).toFixed(1)} KB`
|
|
195
|
+
: `${(fileSizeBytes / 1024 / 1024).toFixed(1)} MB`;
|
|
196
|
+
const summary = [
|
|
197
|
+
`Project: ${projectName}`,
|
|
198
|
+
`Versions: ${snapshots.length}`,
|
|
199
|
+
`Size: ${fileSize}`,
|
|
200
|
+
'',
|
|
201
|
+
`Output: ${outputPath}`,
|
|
202
|
+
];
|
|
203
|
+
p.note(summary.join('\n'), 'Export Summary');
|
|
204
|
+
p.log.info('Share this file to let others reproduce your AI development journey!');
|
|
205
|
+
p.outro('');
|
|
206
|
+
}
|
package/dist/commands/plan.js
CHANGED
|
@@ -86,8 +86,24 @@ export async function cmdPlan(path, options) {
|
|
|
86
86
|
if (copied) {
|
|
87
87
|
p.log.success('AI prompt copied to clipboard!');
|
|
88
88
|
p.log.message('');
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// Eye-catching next step banner
|
|
90
|
+
const banner = [
|
|
91
|
+
'',
|
|
92
|
+
'┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓',
|
|
93
|
+
'┃ ┃',
|
|
94
|
+
'┃ 📋 NEXT STEP ┃',
|
|
95
|
+
'┃ ┃',
|
|
96
|
+
'┃ Open your AI coding tool and press: ┃',
|
|
97
|
+
'┃ ┃',
|
|
98
|
+
'┃ ⌘ + V (Mac) ┃',
|
|
99
|
+
'┃ Ctrl + V (Windows/Linux) ┃',
|
|
100
|
+
'┃ ┃',
|
|
101
|
+
'┃ Your product journey starts now! 🚀 ┃',
|
|
102
|
+
'┃ ┃',
|
|
103
|
+
'┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛',
|
|
104
|
+
'',
|
|
105
|
+
];
|
|
106
|
+
console.log(banner.join('\n'));
|
|
91
107
|
}
|
|
92
108
|
else {
|
|
93
109
|
p.log.warn('Could not copy to clipboard. Showing content instead:');
|
|
@@ -182,9 +198,24 @@ export async function cmdPlan(path, options) {
|
|
|
182
198
|
p.log.message('');
|
|
183
199
|
p.log.success('AI prompt copied to clipboard!');
|
|
184
200
|
p.log.message('');
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
201
|
+
// Eye-catching next step banner
|
|
202
|
+
const banner = [
|
|
203
|
+
'',
|
|
204
|
+
'┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓',
|
|
205
|
+
'┃ ┃',
|
|
206
|
+
'┃ 📋 NEXT STEP ┃',
|
|
207
|
+
'┃ ┃',
|
|
208
|
+
'┃ Open your AI coding tool and press: ┃',
|
|
209
|
+
'┃ ┃',
|
|
210
|
+
'┃ ⌘ + V (Mac) ┃',
|
|
211
|
+
'┃ Ctrl + V (Windows/Linux) ┃',
|
|
212
|
+
'┃ ┃',
|
|
213
|
+
'┃ Your product journey starts now! 🚀 ┃',
|
|
214
|
+
'┃ ┃',
|
|
215
|
+
'┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛',
|
|
216
|
+
'',
|
|
217
|
+
];
|
|
218
|
+
console.log(banner.join('\n'));
|
|
188
219
|
}
|
|
189
220
|
else {
|
|
190
221
|
// Fallback: show prompt
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { cmdWatch } from './commands/watch.js';
|
|
|
10
10
|
import { cmdPlan } from './commands/plan.js';
|
|
11
11
|
import { cmdSave } from './commands/save.js';
|
|
12
12
|
import { cmdSquash } from './commands/squash.js';
|
|
13
|
+
import { cmdExport } from './commands/export.js';
|
|
13
14
|
const program = new Command();
|
|
14
15
|
program
|
|
15
16
|
.name('pmpt')
|
|
@@ -18,12 +19,13 @@ program
|
|
|
18
19
|
.addHelpText('after', `
|
|
19
20
|
Examples:
|
|
20
21
|
$ pmpt init Initialize project
|
|
21
|
-
$ pmpt plan Start product planning (
|
|
22
|
+
$ pmpt plan Start product planning (5 questions → AI prompt)
|
|
22
23
|
$ pmpt save Save snapshot of docs folder
|
|
23
24
|
$ pmpt watch Auto-detect file changes
|
|
24
25
|
$ pmpt history View version history
|
|
25
26
|
$ pmpt history --compact Hide minor changes
|
|
26
27
|
$ pmpt squash v2 v5 Merge versions v2-v5 into v2
|
|
28
|
+
$ pmpt export Export history as shareable zip
|
|
27
29
|
|
|
28
30
|
Folder structure:
|
|
29
31
|
.pmpt/
|
|
@@ -60,9 +62,14 @@ program
|
|
|
60
62
|
.command('squash <from> <to> [path]')
|
|
61
63
|
.description('Squash multiple versions into one (e.g., pmpt squash v2 v5)')
|
|
62
64
|
.action(cmdSquash);
|
|
65
|
+
program
|
|
66
|
+
.command('export [path]')
|
|
67
|
+
.description('Export project history as a shareable zip archive')
|
|
68
|
+
.option('-o, --output <file>', 'Output file path')
|
|
69
|
+
.action(cmdExport);
|
|
63
70
|
program
|
|
64
71
|
.command('plan [path]')
|
|
65
|
-
.description('Quick product planning with
|
|
72
|
+
.description('Quick product planning with 5 questions — auto-generate AI prompt')
|
|
66
73
|
.option('--reset', 'Restart plan from scratch')
|
|
67
74
|
.action(cmdPlan);
|
|
68
75
|
// Contribution commands
|