pmpt-cli 1.14.14 → 1.14.17
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/clone.js +5 -7
- package/dist/commands/remix.js +180 -0
- package/dist/index.js +5 -0
- package/package.json +1 -1
package/dist/commands/clone.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import { join, dirname, resolve, sep } from 'path';
|
|
3
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
4
4
|
import { isInitialized, getConfigDir, getHistoryDir, getDocsDir, initializeProject } from '../lib/config.js';
|
|
5
5
|
import { validatePmptFile, isSafeFilename } from '../lib/pmptFile.js';
|
|
6
6
|
import { fetchPmptFile, trackClone } from '../lib/api.js';
|
|
@@ -110,7 +110,8 @@ export async function cmdClone(slug) {
|
|
|
110
110
|
const pmptDir = getConfigDir(projectPath);
|
|
111
111
|
const historyDir = getHistoryDir(projectPath);
|
|
112
112
|
const docsDir = getDocsDir(projectPath);
|
|
113
|
-
|
|
113
|
+
// History is not restored — user's journey starts fresh from v1.
|
|
114
|
+
// The version summary is embedded in pmpt.ai.md for AI reference.
|
|
114
115
|
if (pmptData.docs) {
|
|
115
116
|
restoreDocs(docsDir, pmptData.docs);
|
|
116
117
|
}
|
|
@@ -175,16 +176,13 @@ export async function cmdClone(slug) {
|
|
|
175
176
|
answers: pmptData.plan,
|
|
176
177
|
}, null, 2), 'utf-8');
|
|
177
178
|
}
|
|
178
|
-
let versionCount = 0;
|
|
179
|
-
if (existsSync(historyDir)) {
|
|
180
|
-
versionCount = readdirSync(historyDir).filter((d) => d.startsWith('v')).length;
|
|
181
|
-
}
|
|
182
179
|
importSpinner.stop('Restore complete!');
|
|
183
180
|
// Track clone event (fire-and-forget)
|
|
184
181
|
trackClone(slug);
|
|
185
182
|
p.note([
|
|
186
183
|
`Project: ${pmptData.meta.projectName}`,
|
|
187
|
-
`
|
|
184
|
+
`Cloned from: @${pmptData.meta.author || 'unknown'} (${pmptData.history.length} versions)`,
|
|
185
|
+
`Your history: starts fresh from v1`,
|
|
188
186
|
`Location: ${pmptDir}`,
|
|
189
187
|
].join('\n'), 'Clone Summary');
|
|
190
188
|
// Copy AI prompt to clipboard
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import { join, basename } from 'path';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
4
|
+
import { isInitialized, getDocsDir, getPmptDir } from '../lib/config.js';
|
|
5
|
+
import { getPlanProgress } from '../lib/plan.js';
|
|
6
|
+
import { copyToClipboard } from '../lib/clipboard.js';
|
|
7
|
+
export async function cmdRemix() {
|
|
8
|
+
const projectPath = process.cwd();
|
|
9
|
+
if (!isInitialized(projectPath)) {
|
|
10
|
+
p.log.error('Project not initialized. Run `pmpt init` or `pmpt clone <slug>` first.');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
p.intro('pmpt remix');
|
|
14
|
+
const docsDir = getDocsDir(projectPath);
|
|
15
|
+
const aiMdPath = join(docsDir, 'pmpt.ai.md');
|
|
16
|
+
const planPath = join(getPmptDir(projectPath), 'plan-progress.json');
|
|
17
|
+
// Read original project info
|
|
18
|
+
const originalAiMd = existsSync(aiMdPath) ? readFileSync(aiMdPath, 'utf-8') : '';
|
|
19
|
+
const planProgress = getPlanProgress(projectPath);
|
|
20
|
+
const originalName = planProgress?.answers?.projectName || basename(projectPath);
|
|
21
|
+
const originalIdea = planProgress?.answers?.productIdea || '';
|
|
22
|
+
const originalTech = planProgress?.answers?.techStack || '';
|
|
23
|
+
p.note([
|
|
24
|
+
`Original: ${originalName}`,
|
|
25
|
+
originalIdea ? `Idea: ${originalIdea.slice(0, 100)}${originalIdea.length > 100 ? '...' : ''}` : '',
|
|
26
|
+
originalTech ? `Tech: ${originalTech}` : '',
|
|
27
|
+
].filter(Boolean).join('\n'), 'Remixing From');
|
|
28
|
+
// Ask remix questions
|
|
29
|
+
const newName = await p.text({
|
|
30
|
+
message: 'New project name?',
|
|
31
|
+
placeholder: `my-${basename(projectPath)}-variant`,
|
|
32
|
+
validate: (v) => {
|
|
33
|
+
if (!v.trim())
|
|
34
|
+
return 'Project name is required.';
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
if (p.isCancel(newName)) {
|
|
38
|
+
p.cancel('Cancelled');
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
const twist = await p.text({
|
|
42
|
+
message: 'What\'s different about your version? (your key differentiation)',
|
|
43
|
+
placeholder: 'e.g., Web-based version, Korean market focus, B2B instead of B2C',
|
|
44
|
+
validate: (v) => {
|
|
45
|
+
if (!v.trim())
|
|
46
|
+
return 'Please describe your differentiation.';
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
if (p.isCancel(twist)) {
|
|
50
|
+
p.cancel('Cancelled');
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
const targetAudience = await p.text({
|
|
54
|
+
message: 'Target audience or context? (optional)',
|
|
55
|
+
placeholder: 'e.g., Small business owners, Students, Enterprise teams',
|
|
56
|
+
});
|
|
57
|
+
if (p.isCancel(targetAudience)) {
|
|
58
|
+
p.cancel('Cancelled');
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
const techOverride = await p.text({
|
|
62
|
+
message: 'Tech stack? (leave blank to keep original)',
|
|
63
|
+
placeholder: originalTech || 'e.g., Next.js, Supabase, Vercel',
|
|
64
|
+
});
|
|
65
|
+
if (p.isCancel(techOverride)) {
|
|
66
|
+
p.cancel('Cancelled');
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
const techStack = (typeof techOverride === 'string' && techOverride.trim())
|
|
70
|
+
? techOverride.trim()
|
|
71
|
+
: originalTech;
|
|
72
|
+
// Generate remixed pmpt.ai.md
|
|
73
|
+
const contextSection = (typeof targetAudience === 'string' && targetAudience.trim())
|
|
74
|
+
? `\n## Target Audience\n${targetAudience.trim()}\n`
|
|
75
|
+
: '';
|
|
76
|
+
const techSection = techStack
|
|
77
|
+
? `\n## Tech Stack\n${techStack}\n`
|
|
78
|
+
: '';
|
|
79
|
+
const remixedPrompt = `<!-- This file is for AI tools only. Do not edit manually. -->
|
|
80
|
+
<!-- Paste this into Claude Code, Codex, Cursor, or any AI coding tool. -->
|
|
81
|
+
|
|
82
|
+
# ${newName.trim()} — Remix
|
|
83
|
+
|
|
84
|
+
## My Version
|
|
85
|
+
${twist.trim()}
|
|
86
|
+
${contextSection}${techSection}
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Instructions for AI
|
|
90
|
+
|
|
91
|
+
This is a **remix** of an existing project. Before building anything, do the following:
|
|
92
|
+
|
|
93
|
+
**Step 1 — Understand & Clarify (do this first)**
|
|
94
|
+
|
|
95
|
+
Read my "My Version" section above and the original project below carefully, then ask me clarifying questions. Specifically, identify:
|
|
96
|
+
- Anything in my differentiation that is ambiguous or unclear
|
|
97
|
+
- Information that would be needed to build my version but is not provided
|
|
98
|
+
- Assumptions you would have to make — ask instead of assuming
|
|
99
|
+
- Any conflict between my twist and the original that needs resolution
|
|
100
|
+
|
|
101
|
+
Ask all your questions in a single message. Wait for my answers before proceeding.
|
|
102
|
+
|
|
103
|
+
**Step 2 — Build**
|
|
104
|
+
|
|
105
|
+
After I answer your questions, build MY version based on the differentiation and my answers.
|
|
106
|
+
- Same core concept as the original, but with my differentiation applied throughout
|
|
107
|
+
- Do NOT copy content verbatim. Adapt everything to fit my context
|
|
108
|
+
- Start with core features first, then iterate
|
|
109
|
+
|
|
110
|
+
### Documentation Rule
|
|
111
|
+
|
|
112
|
+
When you make progress, update \`.pmpt/docs/pmpt.md\`:
|
|
113
|
+
- When architecture or tech decisions are finalized
|
|
114
|
+
- When a feature is implemented (mark as done)
|
|
115
|
+
- When a development phase is completed
|
|
116
|
+
|
|
117
|
+
Keep the Snapshot Log up to date. Run \`pmpt save\` after milestones.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Original Project Reference
|
|
122
|
+
|
|
123
|
+
${originalAiMd}
|
|
124
|
+
`;
|
|
125
|
+
writeFileSync(aiMdPath, remixedPrompt, 'utf-8');
|
|
126
|
+
// Update pmpt.md project name if exists
|
|
127
|
+
const pmptMdPath = join(docsDir, 'pmpt.md');
|
|
128
|
+
if (existsSync(pmptMdPath)) {
|
|
129
|
+
let content = readFileSync(pmptMdPath, 'utf-8');
|
|
130
|
+
// Replace first heading
|
|
131
|
+
content = content.replace(/^# .+$/m, `# ${newName.trim()}`);
|
|
132
|
+
writeFileSync(pmptMdPath, content, 'utf-8');
|
|
133
|
+
}
|
|
134
|
+
// Update plan-progress.json
|
|
135
|
+
if (existsSync(planPath)) {
|
|
136
|
+
try {
|
|
137
|
+
const plan = JSON.parse(readFileSync(planPath, 'utf-8'));
|
|
138
|
+
plan.answers = {
|
|
139
|
+
...plan.answers,
|
|
140
|
+
projectName: newName.trim(),
|
|
141
|
+
productIdea: `${originalIdea}\n\nMy differentiation: ${twist.trim()}`,
|
|
142
|
+
techStack,
|
|
143
|
+
};
|
|
144
|
+
writeFileSync(planPath, JSON.stringify(plan, null, 2), 'utf-8');
|
|
145
|
+
}
|
|
146
|
+
catch { /* ignore */ }
|
|
147
|
+
}
|
|
148
|
+
// Copy to clipboard
|
|
149
|
+
const copied = copyToClipboard(remixedPrompt);
|
|
150
|
+
p.log.success(`Remix prompt generated for "${newName.trim()}"`);
|
|
151
|
+
p.log.message('');
|
|
152
|
+
p.log.info('Next steps:');
|
|
153
|
+
p.log.message(' 1. Paste the prompt into your AI tool');
|
|
154
|
+
p.log.message(' 2. Build your version');
|
|
155
|
+
p.log.message(' 3. pmpt save — save your progress');
|
|
156
|
+
p.log.message(' 4. pmpt publish — share your remix');
|
|
157
|
+
p.log.message('');
|
|
158
|
+
if (copied) {
|
|
159
|
+
const banner = [
|
|
160
|
+
'┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓',
|
|
161
|
+
'┃ ┃',
|
|
162
|
+
'┃ 📋 NEXT STEP ┃',
|
|
163
|
+
'┃ ┃',
|
|
164
|
+
'┃ Remix prompt copied to clipboard! ┃',
|
|
165
|
+
'┃ Open your AI coding tool and paste it: ┃',
|
|
166
|
+
'┃ ┃',
|
|
167
|
+
'┃ ⌘ + V (Mac) ┃',
|
|
168
|
+
'┃ Ctrl + V (Windows/Linux) ┃',
|
|
169
|
+
'┃ ┃',
|
|
170
|
+
'┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛',
|
|
171
|
+
'',
|
|
172
|
+
];
|
|
173
|
+
console.log(banner.join('\n'));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
p.log.warn('Could not copy to clipboard.');
|
|
177
|
+
p.log.info(`Read it at: ${aiMdPath}`);
|
|
178
|
+
}
|
|
179
|
+
p.outro('Ready to remix!');
|
|
180
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ import { cmdEdit } from './commands/edit.js';
|
|
|
39
39
|
import { cmdUnpublish } from './commands/unpublish.js';
|
|
40
40
|
import { cmdGraduate } from './commands/graduate.js';
|
|
41
41
|
import { cmdClone } from './commands/clone.js';
|
|
42
|
+
import { cmdRemix } from './commands/remix.js';
|
|
42
43
|
import { cmdExplore } from './commands/browse.js';
|
|
43
44
|
import { cmdRecover } from './commands/recover.js';
|
|
44
45
|
import { cmdDiff } from './commands/diff.js';
|
|
@@ -196,6 +197,10 @@ program
|
|
|
196
197
|
.command('clone <slug>')
|
|
197
198
|
.description('Clone a project from pmptwiki platform')
|
|
198
199
|
.action(cmdClone);
|
|
200
|
+
program
|
|
201
|
+
.command('remix')
|
|
202
|
+
.description('Remix a cloned project — customize it with your own twist')
|
|
203
|
+
.action(cmdRemix);
|
|
199
204
|
program
|
|
200
205
|
.command('explore')
|
|
201
206
|
.alias('exp')
|