pmpt-cli 1.11.0 → 1.12.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/browse.js +9 -71
- package/dist/commands/clone.js +51 -5
- package/dist/commands/edit.js +43 -0
- package/dist/commands/hist.js +6 -2
- package/dist/commands/internal-seed.js +3 -4
- package/dist/commands/publish.js +39 -4
- package/dist/commands/save.js +6 -0
- package/dist/index.js +5 -6
- package/dist/lib/config.js +57 -0
- package/dist/lib/history.js +22 -5
- package/dist/lib/plan.js +6 -0
- package/dist/mcp.js +247 -0
- package/package.json +4 -2
package/dist/commands/browse.js
CHANGED
|
@@ -1,73 +1,11 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
catch (err) {
|
|
13
|
-
s.stop('Failed to load');
|
|
14
|
-
p.log.error(err instanceof Error ? err.message : 'Could not load project list.');
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
s.stop(`${projects.length} projects`);
|
|
18
|
-
if (projects.length === 0) {
|
|
19
|
-
p.log.info('No published projects yet.');
|
|
20
|
-
p.log.message(' pmpt publish — share your first project!');
|
|
21
|
-
p.outro('');
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
// Select project
|
|
25
|
-
const selected = await p.select({
|
|
26
|
-
message: 'Select a project:',
|
|
27
|
-
options: projects.map((proj) => ({
|
|
28
|
-
value: proj.slug,
|
|
29
|
-
label: proj.projectName,
|
|
30
|
-
hint: `v${proj.versionCount} · @${proj.author}${proj.description ? ` — ${proj.description.slice(0, 40)}` : ''}`,
|
|
31
|
-
})),
|
|
32
|
-
});
|
|
33
|
-
if (p.isCancel(selected)) {
|
|
34
|
-
p.cancel('');
|
|
35
|
-
process.exit(0);
|
|
36
|
-
}
|
|
37
|
-
const project = projects.find((p) => p.slug === selected);
|
|
38
|
-
// Show details
|
|
39
|
-
p.note([
|
|
40
|
-
`Project: ${project.projectName}`,
|
|
41
|
-
`Author: @${project.author}`,
|
|
42
|
-
`Versions: ${project.versionCount}`,
|
|
43
|
-
project.description ? `Description: ${project.description}` : '',
|
|
44
|
-
project.tags.length ? `Tags: ${project.tags.join(', ')}` : '',
|
|
45
|
-
`Published: ${project.publishedAt.slice(0, 10)}`,
|
|
46
|
-
`Size: ${(project.fileSize / 1024).toFixed(1)} KB`,
|
|
47
|
-
].filter(Boolean).join('\n'), 'Project Details');
|
|
48
|
-
// Action
|
|
49
|
-
const action = await p.select({
|
|
50
|
-
message: 'What would you like to do?',
|
|
51
|
-
options: [
|
|
52
|
-
{ value: 'clone', label: 'Clone this project', hint: 'pmpt clone' },
|
|
53
|
-
{ value: 'url', label: 'Show URL', hint: 'View in browser' },
|
|
54
|
-
{ value: 'back', label: 'Go back' },
|
|
55
|
-
],
|
|
56
|
-
});
|
|
57
|
-
if (p.isCancel(action) || action === 'back') {
|
|
58
|
-
p.outro('');
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if (action === 'clone') {
|
|
62
|
-
const { cmdClone } = await import('./clone.js');
|
|
63
|
-
await cmdClone(project.slug);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (action === 'url') {
|
|
67
|
-
const url = `https://pmptwiki.com/p/${project.slug}`;
|
|
68
|
-
p.log.info(`URL: ${url}`);
|
|
69
|
-
p.log.message(`Download: ${project.downloadUrl}`);
|
|
70
|
-
p.log.message(`\npmpt clone ${project.slug} — clone via terminal`);
|
|
71
|
-
p.outro('');
|
|
72
|
-
}
|
|
2
|
+
import open from 'open';
|
|
3
|
+
const EXPLORE_URL = 'https://pmptwiki.com/explore';
|
|
4
|
+
export async function cmdExplore() {
|
|
5
|
+
p.intro('pmpt explore');
|
|
6
|
+
p.log.info(`Opening ${EXPLORE_URL}`);
|
|
7
|
+
await open(EXPLORE_URL);
|
|
8
|
+
p.log.message(' Search, filter, and clone projects from the web.');
|
|
9
|
+
p.log.message(' Found something you like? → pmpt clone <slug>');
|
|
10
|
+
p.outro('');
|
|
73
11
|
}
|
package/dist/commands/clone.js
CHANGED
|
@@ -4,6 +4,7 @@ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from
|
|
|
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';
|
|
7
|
+
import { copyToClipboard } from '../lib/clipboard.js';
|
|
7
8
|
/**
|
|
8
9
|
* Restore history from .pmpt data (shared with import command)
|
|
9
10
|
*/
|
|
@@ -81,12 +82,15 @@ export async function cmdClone(slug) {
|
|
|
81
82
|
const pmptData = validation.data;
|
|
82
83
|
s.stop('Download complete');
|
|
83
84
|
// Show summary
|
|
84
|
-
|
|
85
|
+
const infoLines = [
|
|
85
86
|
`Project: ${pmptData.meta.projectName}`,
|
|
86
87
|
`Versions: ${pmptData.history.length}`,
|
|
87
88
|
pmptData.meta.author ? `Author: @${pmptData.meta.author}` : '',
|
|
88
|
-
pmptData.meta.description ?
|
|
89
|
-
|
|
89
|
+
pmptData.meta.description ? `\n${pmptData.meta.description}` : '',
|
|
90
|
+
pmptData.plan?.productIdea ? `\n💡 ${pmptData.plan.productIdea.slice(0, 120)}` : '',
|
|
91
|
+
pmptData.plan?.techStack ? `🛠 ${pmptData.plan.techStack.slice(0, 80)}` : '',
|
|
92
|
+
];
|
|
93
|
+
p.note(infoLines.filter(Boolean).join('\n'), 'Project Info');
|
|
90
94
|
const projectPath = process.cwd();
|
|
91
95
|
if (isInitialized(projectPath)) {
|
|
92
96
|
const overwrite = await p.confirm({
|
|
@@ -147,6 +151,19 @@ export async function cmdClone(slug) {
|
|
|
147
151
|
'',
|
|
148
152
|
'---',
|
|
149
153
|
'',
|
|
154
|
+
`## Documentation Rule`,
|
|
155
|
+
'',
|
|
156
|
+
`**Important:** When you make progress, update \`.pmpt/docs/pmpt.md\` (the human-facing project document) at these moments:`,
|
|
157
|
+
`- When architecture or tech decisions are finalized`,
|
|
158
|
+
`- When a feature is implemented (mark as done)`,
|
|
159
|
+
`- When a development phase is completed`,
|
|
160
|
+
`- When requirements change or new decisions are made`,
|
|
161
|
+
'',
|
|
162
|
+
`Keep the Progress and Snapshot Log sections in pmpt.md up to date.`,
|
|
163
|
+
`After significant milestones, run \`pmpt save\` to create a snapshot.`,
|
|
164
|
+
'',
|
|
165
|
+
'---',
|
|
166
|
+
'',
|
|
150
167
|
originalAiMd,
|
|
151
168
|
].join('\n');
|
|
152
169
|
writeFileSync(aiMdPath, cloneGuide, 'utf-8');
|
|
@@ -170,9 +187,38 @@ export async function cmdClone(slug) {
|
|
|
170
187
|
`Versions: ${versionCount}`,
|
|
171
188
|
`Location: ${pmptDir}`,
|
|
172
189
|
].join('\n'), 'Clone Summary');
|
|
173
|
-
|
|
190
|
+
// Copy AI prompt to clipboard
|
|
191
|
+
const aiContent = readFileSync(aiMdPath, 'utf-8');
|
|
192
|
+
const copied = copyToClipboard(aiContent);
|
|
193
|
+
if (copied) {
|
|
194
|
+
p.log.message('');
|
|
195
|
+
p.log.success('AI prompt copied to clipboard!');
|
|
196
|
+
p.log.message('');
|
|
197
|
+
const banner = [
|
|
198
|
+
'',
|
|
199
|
+
'┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓',
|
|
200
|
+
'┃ ┃',
|
|
201
|
+
'┃ 📋 NEXT STEP ┃',
|
|
202
|
+
'┃ ┃',
|
|
203
|
+
'┃ Open your AI coding tool and press: ┃',
|
|
204
|
+
'┃ ┃',
|
|
205
|
+
'┃ ⌘ + V (Mac) ┃',
|
|
206
|
+
'┃ Ctrl + V (Windows/Linux) ┃',
|
|
207
|
+
'┃ ┃',
|
|
208
|
+
'┃ Your cloned project context is ready! 🚀 ┃',
|
|
209
|
+
'┃ ┃',
|
|
210
|
+
'┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛',
|
|
211
|
+
'',
|
|
212
|
+
];
|
|
213
|
+
console.log(banner.join('\n'));
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
p.log.warn('Could not copy to clipboard.');
|
|
217
|
+
p.log.info(`Read it at: ${aiMdPath}`);
|
|
218
|
+
}
|
|
219
|
+
p.log.info('Tips:');
|
|
174
220
|
p.log.message(' pmpt history — view version history');
|
|
175
|
-
p.log.message(' pmpt plan — view AI prompt');
|
|
221
|
+
p.log.message(' pmpt plan — view or edit AI prompt');
|
|
176
222
|
p.log.message(' pmpt save — save a new snapshot');
|
|
177
223
|
p.outro('Project cloned!');
|
|
178
224
|
}
|
package/dist/commands/edit.js
CHANGED
|
@@ -79,6 +79,47 @@ export async function cmdEdit() {
|
|
|
79
79
|
p.cancel('Cancelled');
|
|
80
80
|
process.exit(0);
|
|
81
81
|
}
|
|
82
|
+
// Product link (optional)
|
|
83
|
+
const linkTypeInput = await p.select({
|
|
84
|
+
message: 'Product link (optional):',
|
|
85
|
+
initialValue: project.productUrlType || 'none',
|
|
86
|
+
options: [
|
|
87
|
+
{ value: 'none', label: 'No link' },
|
|
88
|
+
{ value: 'git', label: 'Git Repository' },
|
|
89
|
+
{ value: 'url', label: 'Website / URL' },
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
if (p.isCancel(linkTypeInput)) {
|
|
93
|
+
p.cancel('Cancelled');
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
let productUrl = '';
|
|
97
|
+
let productUrlType = '';
|
|
98
|
+
if (linkTypeInput !== 'none') {
|
|
99
|
+
productUrlType = linkTypeInput;
|
|
100
|
+
const productUrlInput = await p.text({
|
|
101
|
+
message: 'Product URL:',
|
|
102
|
+
placeholder: linkTypeInput === 'git'
|
|
103
|
+
? `https://github.com/${auth.username}/${slug}`
|
|
104
|
+
: 'https://...',
|
|
105
|
+
defaultValue: project.productUrl || '',
|
|
106
|
+
validate: (v) => {
|
|
107
|
+
if (!v.trim())
|
|
108
|
+
return 'URL is required when link type is selected.';
|
|
109
|
+
try {
|
|
110
|
+
new URL(v);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return 'Invalid URL format.';
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
if (p.isCancel(productUrlInput)) {
|
|
118
|
+
p.cancel('Cancelled');
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
productUrl = productUrlInput;
|
|
122
|
+
}
|
|
82
123
|
const s2 = p.spinner();
|
|
83
124
|
s2.start('Updating...');
|
|
84
125
|
try {
|
|
@@ -86,6 +127,8 @@ export async function cmdEdit() {
|
|
|
86
127
|
description: description,
|
|
87
128
|
tags,
|
|
88
129
|
category: category,
|
|
130
|
+
productUrl,
|
|
131
|
+
productUrlType,
|
|
89
132
|
});
|
|
90
133
|
s2.stop('Updated!');
|
|
91
134
|
p.log.success(`Project "${slug}" has been updated.`);
|
package/dist/commands/hist.js
CHANGED
|
@@ -75,8 +75,12 @@ export function cmdHistory(path, options) {
|
|
|
75
75
|
if (snapshot.git.dirty)
|
|
76
76
|
header += ' (dirty)';
|
|
77
77
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
78
|
+
const lines = [];
|
|
79
|
+
if (snapshot.note) {
|
|
80
|
+
lines.push(` ${snapshot.note}`, '');
|
|
81
|
+
}
|
|
82
|
+
lines.push(...snapshot.files.map((f) => ` - ${f}`));
|
|
83
|
+
p.note(lines.join('\n') || ' (no files)', header);
|
|
80
84
|
}
|
|
81
85
|
if (options?.compact && hiddenVersions.length > 0) {
|
|
82
86
|
p.log.info(`Hidden versions (minor changes): ${hiddenVersions.map(v => `v${v}`).join(', ')}`);
|
|
@@ -84,13 +84,12 @@ export async function cmdInternalSeed(options) {
|
|
|
84
84
|
const content = readFileSync(resolve(specDir, fromPath), 'utf-8');
|
|
85
85
|
writeDocFile(docsDir, fileName, content);
|
|
86
86
|
}
|
|
87
|
-
const entry = createFullSnapshot(projectPath);
|
|
88
|
-
const
|
|
89
|
-
p.log.success(`v${entry.version} saved${
|
|
87
|
+
const entry = createFullSnapshot(projectPath, { note: step.saveNote });
|
|
88
|
+
const noteStr = entry.note ? ` — ${entry.note}` : '';
|
|
89
|
+
p.log.success(`v${entry.version} saved${noteStr}`);
|
|
90
90
|
}
|
|
91
91
|
if (spec.publish?.enabled) {
|
|
92
92
|
await cmdPublish(projectPath, {
|
|
93
|
-
force: spec.publish.force ?? false,
|
|
94
93
|
nonInteractive: true,
|
|
95
94
|
yes: spec.publish.yes ?? true,
|
|
96
95
|
metaFile: spec.publish.metaFile ? resolve(specDir, spec.publish.metaFile) : undefined,
|
package/dist/commands/publish.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createPmptFile } from '../lib/pmptFile.js';
|
|
|
8
8
|
import { loadAuth } from '../lib/auth.js';
|
|
9
9
|
import { publishProject, fetchProjects } from '../lib/api.js';
|
|
10
10
|
import { computeQuality } from '../lib/quality.js';
|
|
11
|
+
import { copyToClipboard } from '../lib/clipboard.js';
|
|
11
12
|
import pc from 'picocolors';
|
|
12
13
|
import glob from 'fast-glob';
|
|
13
14
|
import { join } from 'path';
|
|
@@ -22,6 +23,29 @@ const CATEGORY_OPTIONS = [
|
|
|
22
23
|
{ value: 'other', label: 'Other' },
|
|
23
24
|
];
|
|
24
25
|
const VALID_CATEGORIES = new Set(CATEGORY_OPTIONS.map((o) => o.value));
|
|
26
|
+
function generateImprovementPrompt(quality) {
|
|
27
|
+
const missing = [];
|
|
28
|
+
for (const item of quality.details) {
|
|
29
|
+
if (item.score < item.maxScore && item.tip) {
|
|
30
|
+
missing.push(`- ${item.label}: ${item.tip}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return [
|
|
34
|
+
`My pmpt project scored ${quality.score}/100 (Grade ${quality.grade}) and needs at least 40 to publish.`,
|
|
35
|
+
'',
|
|
36
|
+
'Areas to improve:',
|
|
37
|
+
...missing,
|
|
38
|
+
'',
|
|
39
|
+
'Please help me improve the project quality:',
|
|
40
|
+
'',
|
|
41
|
+
'1. Read `.pmpt/docs/pmpt.ai.md` and `.pmpt/docs/pmpt.md`',
|
|
42
|
+
'2. Expand pmpt.ai.md to 500+ characters with clear project context, architecture, and instructions for AI',
|
|
43
|
+
'3. Make sure pmpt.md has progress tracking, decisions, and a snapshot log',
|
|
44
|
+
'4. If plan.md is missing, create it with product overview',
|
|
45
|
+
'5. After improving, run `pmpt save` to create a new snapshot',
|
|
46
|
+
'6. Then try `pmpt publish` again',
|
|
47
|
+
].join('\n');
|
|
48
|
+
}
|
|
25
49
|
function normalizeTags(value) {
|
|
26
50
|
if (Array.isArray(value)) {
|
|
27
51
|
return value
|
|
@@ -124,11 +148,22 @@ export async function cmdPublish(path, options) {
|
|
|
124
148
|
if (tips.length > 0) {
|
|
125
149
|
p.log.info('How to improve:\n' + tips.join('\n'));
|
|
126
150
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
151
|
+
// Generate and copy AI improvement prompt
|
|
152
|
+
const improvementPrompt = generateImprovementPrompt(quality);
|
|
153
|
+
const copied = copyToClipboard(improvementPrompt);
|
|
154
|
+
if (copied) {
|
|
155
|
+
p.log.message('');
|
|
156
|
+
p.log.success('AI improvement prompt copied to clipboard!');
|
|
157
|
+
p.log.message(' Paste it into Claude Code, Cursor, or any AI tool to improve your project.');
|
|
158
|
+
p.log.message(' After improving, run `pmpt save` then `pmpt publish` again.');
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
p.log.message('');
|
|
162
|
+
p.note(improvementPrompt, 'AI Improvement Prompt');
|
|
163
|
+
p.log.message(' Copy the prompt above and paste into your AI tool.');
|
|
130
164
|
}
|
|
131
|
-
p.
|
|
165
|
+
p.outro('');
|
|
166
|
+
process.exit(1);
|
|
132
167
|
}
|
|
133
168
|
const projectName = planProgress?.answers?.projectName || basename(projectPath);
|
|
134
169
|
// Try to load existing published data for prefill
|
package/dist/commands/save.js
CHANGED
|
@@ -38,6 +38,12 @@ export async function cmdSave(fileOrPath) {
|
|
|
38
38
|
msg += ` (${changedCount} changed, ${unchangedCount} skipped)`;
|
|
39
39
|
}
|
|
40
40
|
p.log.success(msg);
|
|
41
|
+
// Warn if pmpt.md was not updated since last save
|
|
42
|
+
if (entry.version > 1 && entry.changedFiles && !entry.changedFiles.includes('pmpt.md')) {
|
|
43
|
+
p.log.message('');
|
|
44
|
+
p.log.warn('pmpt.md has not been updated since the last save.');
|
|
45
|
+
p.log.message(' Tip: Mark completed features and update the Snapshot Log before saving.');
|
|
46
|
+
}
|
|
41
47
|
p.log.message('');
|
|
42
48
|
p.log.info('Files included:');
|
|
43
49
|
for (const file of entry.files) {
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import { cmdPublish } from './commands/publish.js';
|
|
|
37
37
|
import { cmdEdit } from './commands/edit.js';
|
|
38
38
|
import { cmdUnpublish } from './commands/unpublish.js';
|
|
39
39
|
import { cmdClone } from './commands/clone.js';
|
|
40
|
-
import {
|
|
40
|
+
import { cmdExplore } from './commands/browse.js';
|
|
41
41
|
import { cmdRecover } from './commands/recover.js';
|
|
42
42
|
import { cmdDiff } from './commands/diff.js';
|
|
43
43
|
import { cmdInternalSeed } from './commands/internal-seed.js';
|
|
@@ -64,7 +64,7 @@ Examples:
|
|
|
64
64
|
$ pmpt login Authenticate with pmptwiki
|
|
65
65
|
$ pmpt publish Publish project to pmptwiki
|
|
66
66
|
$ pmpt clone <slug> Clone a project from pmptwiki
|
|
67
|
-
$ pmpt
|
|
67
|
+
$ pmpt explore Explore projects on pmptwiki.com
|
|
68
68
|
$ pmpt recover Recover damaged pmpt.md via AI
|
|
69
69
|
|
|
70
70
|
Documentation: https://pmptwiki.com
|
|
@@ -133,7 +133,6 @@ program
|
|
|
133
133
|
program
|
|
134
134
|
.command('publish [path]')
|
|
135
135
|
.description('Publish project to pmptwiki platform')
|
|
136
|
-
.option('--force', 'Publish even if quality score is below minimum')
|
|
137
136
|
.option('--non-interactive', 'Run without interactive prompts')
|
|
138
137
|
.option('--meta-file <file>', 'JSON file with slug, description, tags, category')
|
|
139
138
|
.option('--slug <slug>', 'Project slug')
|
|
@@ -157,9 +156,9 @@ program
|
|
|
157
156
|
.description('Clone a project from pmptwiki platform')
|
|
158
157
|
.action(cmdClone);
|
|
159
158
|
program
|
|
160
|
-
.command('
|
|
161
|
-
.description('
|
|
162
|
-
.action(
|
|
159
|
+
.command('explore')
|
|
160
|
+
.description('Open pmptwiki.com to explore and search projects')
|
|
161
|
+
.action(cmdExplore);
|
|
163
162
|
program
|
|
164
163
|
.command('recover [path]')
|
|
165
164
|
.description('Generate a recovery prompt to regenerate pmpt.md via AI')
|
package/dist/lib/config.js
CHANGED
|
@@ -37,6 +37,11 @@ export function initializeProject(projectPath, options) {
|
|
|
37
37
|
trackGit: options?.trackGit ?? true,
|
|
38
38
|
};
|
|
39
39
|
saveConfig(projectPath, config);
|
|
40
|
+
// Create README.md if it doesn't exist
|
|
41
|
+
const readmePath = join(configDir, 'README.md');
|
|
42
|
+
if (!existsSync(readmePath)) {
|
|
43
|
+
writeFileSync(readmePath, PMPT_README, 'utf-8');
|
|
44
|
+
}
|
|
40
45
|
return config;
|
|
41
46
|
}
|
|
42
47
|
export function loadConfig(projectPath) {
|
|
@@ -54,3 +59,55 @@ export function saveConfig(projectPath, config) {
|
|
|
54
59
|
const configPath = join(getConfigDir(projectPath), CONFIG_FILE);
|
|
55
60
|
writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
56
61
|
}
|
|
62
|
+
const PMPT_README = `# .pmpt — Your Project's Development Journal
|
|
63
|
+
|
|
64
|
+
This folder is managed by [pmpt](https://pmptwiki.com). It records your product development journey with AI.
|
|
65
|
+
|
|
66
|
+
## What's Inside
|
|
67
|
+
|
|
68
|
+
\`\`\`
|
|
69
|
+
.pmpt/
|
|
70
|
+
├── config.json ← Project settings (auto-generated)
|
|
71
|
+
├── docs/
|
|
72
|
+
│ ├── pmpt.md ← Human-facing project document (YOU update this)
|
|
73
|
+
│ ├── pmpt.ai.md ← AI-facing prompt (paste into your AI tool)
|
|
74
|
+
│ └── plan.md ← Original plan from pmpt plan
|
|
75
|
+
└── .history/ ← Version snapshots (auto-managed)
|
|
76
|
+
\`\`\`
|
|
77
|
+
|
|
78
|
+
## Quick Reference
|
|
79
|
+
|
|
80
|
+
| Command | What it does |
|
|
81
|
+
|---------|-------------|
|
|
82
|
+
| \`pmpt plan\` | Create or view your AI prompt |
|
|
83
|
+
| \`pmpt save\` | Save a snapshot of current docs |
|
|
84
|
+
| \`pmpt history\` | View version history |
|
|
85
|
+
| \`pmpt diff\` | Compare versions side by side |
|
|
86
|
+
| \`pmpt publish\` | Share your journey on pmptwiki.com |
|
|
87
|
+
|
|
88
|
+
## How to Get the Most Out of pmpt
|
|
89
|
+
|
|
90
|
+
1. **Paste \`pmpt.ai.md\` into your AI tool** to start building
|
|
91
|
+
2. **Update \`pmpt.md\` as you go** — mark features done, log decisions
|
|
92
|
+
3. **Run \`pmpt save\` at milestones** — after setup, after each feature, after big changes
|
|
93
|
+
4. **Publish when ready** — others can clone your journey and learn from it
|
|
94
|
+
|
|
95
|
+
## When Things Go Wrong
|
|
96
|
+
|
|
97
|
+
| Problem | Solution |
|
|
98
|
+
|---------|----------|
|
|
99
|
+
| Lost your AI prompt | \`pmpt plan\` to regenerate or view it |
|
|
100
|
+
| Messed up docs | \`pmpt history\` → \`pmpt diff\` to find the good version |
|
|
101
|
+
| Need to start over | \`pmpt recover\` rebuilds context from history |
|
|
102
|
+
| Accidentally deleted .pmpt | Re-clone from pmptwiki.com if published |
|
|
103
|
+
|
|
104
|
+
## One Request
|
|
105
|
+
|
|
106
|
+
Please keep \`pmpt.md\` updated as you build. It's the human-readable record of your journey — what you tried, what worked, what you decided. When you publish, this is what others will learn from.
|
|
107
|
+
|
|
108
|
+
Your snapshots tell a story. Make it a good one.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
*Learn more at [pmptwiki.com](https://pmptwiki.com)*
|
|
113
|
+
`;
|
package/dist/lib/history.js
CHANGED
|
@@ -3,18 +3,31 @@ import { basename, join, relative } from 'path';
|
|
|
3
3
|
import { getHistoryDir, getDocsDir, loadConfig } from './config.js';
|
|
4
4
|
import { getGitInfo, isGitRepo } from './git.js';
|
|
5
5
|
import glob from 'fast-glob';
|
|
6
|
+
/** Generate compact timestamp for snapshot dir names: 20260225T163000 */
|
|
7
|
+
function compactTimestamp() {
|
|
8
|
+
return new Date().toISOString().replace(/[-:\.]/g, '').slice(0, 15);
|
|
9
|
+
}
|
|
10
|
+
/** Parse snapshot dir timestamp (compact or legacy) to ISO string */
|
|
11
|
+
function parseTimestamp(raw) {
|
|
12
|
+
// Compact: 20260225T163000
|
|
13
|
+
if (/^\d{8}T\d{6}$/.test(raw)) {
|
|
14
|
+
return `${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}T${raw.slice(9, 11)}:${raw.slice(11, 13)}:${raw.slice(13, 15)}`;
|
|
15
|
+
}
|
|
16
|
+
// Legacy: 2026-02-25T16-30-00
|
|
17
|
+
return raw.replace(/T(.+)$/, (_, time) => 'T' + time.replace(/-/g, ':'));
|
|
18
|
+
}
|
|
6
19
|
/**
|
|
7
20
|
* Save .pmpt/docs MD files as snapshot
|
|
8
21
|
* Copy only changed files to optimize storage
|
|
9
22
|
*/
|
|
10
|
-
export function createFullSnapshot(projectPath) {
|
|
23
|
+
export function createFullSnapshot(projectPath, options) {
|
|
11
24
|
const historyDir = getHistoryDir(projectPath);
|
|
12
25
|
const docsDir = getDocsDir(projectPath);
|
|
13
26
|
mkdirSync(historyDir, { recursive: true });
|
|
14
27
|
// Find next version number
|
|
15
28
|
const existing = getAllSnapshots(projectPath);
|
|
16
29
|
const version = existing.length + 1;
|
|
17
|
-
const timestamp =
|
|
30
|
+
const timestamp = compactTimestamp();
|
|
18
31
|
const snapshotName = `v${version}-${timestamp}`;
|
|
19
32
|
const snapshotDir = join(historyDir, snapshotName);
|
|
20
33
|
mkdirSync(snapshotDir, { recursive: true });
|
|
@@ -63,12 +76,14 @@ export function createFullSnapshot(projectPath) {
|
|
|
63
76
|
}
|
|
64
77
|
}
|
|
65
78
|
// Save metadata
|
|
79
|
+
const note = options?.note;
|
|
66
80
|
const metaPath = join(snapshotDir, '.meta.json');
|
|
67
81
|
writeFileSync(metaPath, JSON.stringify({
|
|
68
82
|
version,
|
|
69
83
|
timestamp,
|
|
70
84
|
files,
|
|
71
85
|
changedFiles,
|
|
86
|
+
...(note ? { note } : {}),
|
|
72
87
|
git: gitData,
|
|
73
88
|
}, null, 2), 'utf-8');
|
|
74
89
|
return {
|
|
@@ -77,6 +92,7 @@ export function createFullSnapshot(projectPath) {
|
|
|
77
92
|
snapshotDir,
|
|
78
93
|
files,
|
|
79
94
|
changedFiles,
|
|
95
|
+
note,
|
|
80
96
|
git: gitData,
|
|
81
97
|
};
|
|
82
98
|
}
|
|
@@ -98,7 +114,7 @@ export function createSnapshot(projectPath, filePath) {
|
|
|
98
114
|
}
|
|
99
115
|
function createSingleFileSnapshot(projectPath, filePath, relPath) {
|
|
100
116
|
const historyDir = getHistoryDir(projectPath);
|
|
101
|
-
const timestamp =
|
|
117
|
+
const timestamp = compactTimestamp();
|
|
102
118
|
// Check existing version count for this file
|
|
103
119
|
const existing = getFileHistory(projectPath, relPath);
|
|
104
120
|
const version = existing.length + 1;
|
|
@@ -168,10 +184,11 @@ export function getAllSnapshots(projectPath) {
|
|
|
168
184
|
}
|
|
169
185
|
entries.push({
|
|
170
186
|
version: parseInt(match[1], 10),
|
|
171
|
-
timestamp: match[2]
|
|
187
|
+
timestamp: parseTimestamp(match[2]),
|
|
172
188
|
snapshotDir,
|
|
173
189
|
files: meta.files || [],
|
|
174
190
|
changedFiles: meta.changedFiles,
|
|
191
|
+
note: meta.note,
|
|
175
192
|
git: meta.git,
|
|
176
193
|
});
|
|
177
194
|
}
|
|
@@ -210,7 +227,7 @@ export function getFileHistory(projectPath, relPath) {
|
|
|
210
227
|
}
|
|
211
228
|
entries.push({
|
|
212
229
|
version: parseInt(match[1], 10),
|
|
213
|
-
timestamp: match[2]
|
|
230
|
+
timestamp: parseTimestamp(match[2]),
|
|
214
231
|
filePath: relPath,
|
|
215
232
|
historyPath: filePath,
|
|
216
233
|
git: gitData,
|
package/dist/lib/plan.js
CHANGED
|
@@ -88,6 +88,12 @@ I'll confirm progress at each step before moving to the next.
|
|
|
88
88
|
|
|
89
89
|
Keep the Progress and Snapshot Log sections in pmpt.md up to date.
|
|
90
90
|
After significant milestones, run \`pmpt save\` to create a snapshot.
|
|
91
|
+
|
|
92
|
+
### Per-Feature Checklist
|
|
93
|
+
After completing each feature above:
|
|
94
|
+
1. Mark the feature done in \`.pmpt/docs/pmpt.md\` (change \`- [ ]\` to \`- [x]\`)
|
|
95
|
+
2. Add a brief note to the Snapshot Log section
|
|
96
|
+
3. Run \`pmpt save\` in terminal
|
|
91
97
|
`;
|
|
92
98
|
}
|
|
93
99
|
// Generate human-facing project document (pmpt.md)
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* pmpt MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes pmpt functionality as MCP tools so AI tools
|
|
6
|
+
* (Claude Code, Cursor, etc.) can interact with pmpt directly.
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { resolve, join } from 'path';
|
|
12
|
+
import { existsSync, readFileSync } from 'fs';
|
|
13
|
+
import glob from 'fast-glob';
|
|
14
|
+
import { createRequire } from 'module';
|
|
15
|
+
import { isInitialized, loadConfig, getDocsDir } from './lib/config.js';
|
|
16
|
+
import { createFullSnapshot, getAllSnapshots, getTrackedFiles, resolveFullSnapshot } from './lib/history.js';
|
|
17
|
+
import { computeQuality } from './lib/quality.js';
|
|
18
|
+
import { getPlanProgress } from './lib/plan.js';
|
|
19
|
+
import { isGitRepo } from './lib/git.js';
|
|
20
|
+
import { diffSnapshots } from './lib/diff.js';
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
const { version } = require('../package.json');
|
|
23
|
+
// ── Server ──────────────────────────────────────────
|
|
24
|
+
const server = new McpServer({
|
|
25
|
+
name: 'pmpt',
|
|
26
|
+
version,
|
|
27
|
+
});
|
|
28
|
+
// ── Helpers ─────────────────────────────────────────
|
|
29
|
+
function resolveProjectPath(projectPath) {
|
|
30
|
+
return projectPath ? resolve(projectPath) : process.cwd();
|
|
31
|
+
}
|
|
32
|
+
function assertInitialized(pp) {
|
|
33
|
+
if (!isInitialized(pp)) {
|
|
34
|
+
throw new Error(`Project not initialized at ${pp}. Run \`pmpt init\` first.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function readWorkingCopy(pp) {
|
|
38
|
+
const docsDir = getDocsDir(pp);
|
|
39
|
+
const files = {};
|
|
40
|
+
if (!existsSync(docsDir))
|
|
41
|
+
return files;
|
|
42
|
+
const mdFiles = glob.sync('**/*.md', { cwd: docsDir });
|
|
43
|
+
for (const file of mdFiles) {
|
|
44
|
+
try {
|
|
45
|
+
files[file] = readFileSync(join(docsDir, file), 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
catch { /* skip */ }
|
|
48
|
+
}
|
|
49
|
+
return files;
|
|
50
|
+
}
|
|
51
|
+
function buildQualityInput(pp) {
|
|
52
|
+
const docsDir = getDocsDir(pp);
|
|
53
|
+
const aiMdPath = join(docsDir, 'pmpt.ai.md');
|
|
54
|
+
const pmptAiMd = existsSync(aiMdPath) ? readFileSync(aiMdPath, 'utf-8') : null;
|
|
55
|
+
const planProgress = getPlanProgress(pp);
|
|
56
|
+
const tracked = getTrackedFiles(pp);
|
|
57
|
+
const snapshots = getAllSnapshots(pp);
|
|
58
|
+
const hasGit = snapshots.some((s) => !!s.git) || isGitRepo(pp);
|
|
59
|
+
return {
|
|
60
|
+
pmptAiMd,
|
|
61
|
+
planAnswers: planProgress?.answers ?? null,
|
|
62
|
+
versionCount: snapshots.length,
|
|
63
|
+
docFiles: tracked,
|
|
64
|
+
hasGit,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function formatDiffs(diffs) {
|
|
68
|
+
if (diffs.length === 0)
|
|
69
|
+
return 'No differences found.';
|
|
70
|
+
const lines = [];
|
|
71
|
+
const modified = diffs.filter((d) => d.status === 'modified').length;
|
|
72
|
+
const added = diffs.filter((d) => d.status === 'added').length;
|
|
73
|
+
const removed = diffs.filter((d) => d.status === 'removed').length;
|
|
74
|
+
const parts = [];
|
|
75
|
+
if (modified > 0)
|
|
76
|
+
parts.push(`${modified} modified`);
|
|
77
|
+
if (added > 0)
|
|
78
|
+
parts.push(`${added} added`);
|
|
79
|
+
if (removed > 0)
|
|
80
|
+
parts.push(`${removed} removed`);
|
|
81
|
+
lines.push(`${diffs.length} file(s) changed: ${parts.join(', ')}`);
|
|
82
|
+
lines.push('');
|
|
83
|
+
for (const fd of diffs) {
|
|
84
|
+
const icon = fd.status === 'added' ? 'A' : fd.status === 'removed' ? 'D' : 'M';
|
|
85
|
+
lines.push(`[${icon}] ${fd.fileName}`);
|
|
86
|
+
for (const hunk of fd.hunks) {
|
|
87
|
+
lines.push(`@@ -${hunk.oldStart},${hunk.oldCount} +${hunk.newStart},${hunk.newCount} @@`);
|
|
88
|
+
for (const line of hunk.lines) {
|
|
89
|
+
const prefix = line.type === 'add' ? '+' : line.type === 'remove' ? '-' : ' ';
|
|
90
|
+
lines.push(`${prefix}${line.content}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
lines.push('');
|
|
94
|
+
}
|
|
95
|
+
return lines.join('\n');
|
|
96
|
+
}
|
|
97
|
+
// ── Tools ───────────────────────────────────────────
|
|
98
|
+
server.tool('pmpt_save', 'Save a snapshot of .pmpt/docs/ files. Call after completing features, fixes, or milestones.', { projectPath: z.string().optional().describe('Project root path. Defaults to cwd.') }, async ({ projectPath }) => {
|
|
99
|
+
try {
|
|
100
|
+
const pp = resolveProjectPath(projectPath);
|
|
101
|
+
assertInitialized(pp);
|
|
102
|
+
const tracked = getTrackedFiles(pp);
|
|
103
|
+
if (tracked.length === 0) {
|
|
104
|
+
return { content: [{ type: 'text', text: 'No files to save. Add .md files to .pmpt/docs/ first.' }] };
|
|
105
|
+
}
|
|
106
|
+
const entry = createFullSnapshot(pp);
|
|
107
|
+
const changedCount = entry.changedFiles?.length ?? entry.files.length;
|
|
108
|
+
return {
|
|
109
|
+
content: [{
|
|
110
|
+
type: 'text',
|
|
111
|
+
text: [
|
|
112
|
+
`Snapshot v${entry.version} saved (${changedCount} changed, ${entry.files.length - changedCount} unchanged).`,
|
|
113
|
+
'',
|
|
114
|
+
`Files: ${entry.files.join(', ')}`,
|
|
115
|
+
entry.changedFiles ? `Changed: ${entry.changedFiles.join(', ')}` : '',
|
|
116
|
+
entry.git ? `Git: ${entry.git.commit} (${entry.git.branch}${entry.git.dirty ? ', dirty' : ''})` : '',
|
|
117
|
+
].filter(Boolean).join('\n'),
|
|
118
|
+
}],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
server.tool('pmpt_status', 'Check pmpt project status: tracked files, snapshot count, and quality score.', { projectPath: z.string().optional().describe('Project root path. Defaults to cwd.') }, async ({ projectPath }) => {
|
|
126
|
+
try {
|
|
127
|
+
const pp = resolveProjectPath(projectPath);
|
|
128
|
+
if (!isInitialized(pp)) {
|
|
129
|
+
return { content: [{ type: 'text', text: 'Project not initialized. Run `pmpt init` to start.' }] };
|
|
130
|
+
}
|
|
131
|
+
const config = loadConfig(pp);
|
|
132
|
+
const tracked = getTrackedFiles(pp);
|
|
133
|
+
const snapshots = getAllSnapshots(pp);
|
|
134
|
+
const quality = computeQuality(buildQualityInput(pp));
|
|
135
|
+
const lines = [
|
|
136
|
+
`pmpt status: ${tracked.length} file(s), ${snapshots.length} snapshot(s), quality ${quality.score}/100 (${quality.grade})`,
|
|
137
|
+
`Files: ${tracked.join(', ') || '(none)'}`,
|
|
138
|
+
config?.lastPublished ? `Last published: ${config.lastPublished.slice(0, 10)}` : '',
|
|
139
|
+
'',
|
|
140
|
+
];
|
|
141
|
+
for (const d of quality.details) {
|
|
142
|
+
const icon = d.score === d.maxScore ? '[PASS]' : '[FAIL]';
|
|
143
|
+
lines.push(`${icon} ${d.label}: ${d.score}/${d.maxScore}${d.tip ? ` — ${d.tip}` : ''}`);
|
|
144
|
+
}
|
|
145
|
+
return { content: [{ type: 'text', text: lines.filter(Boolean).join('\n') }] };
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
server.tool('pmpt_history', 'View version history of pmpt snapshots.', {
|
|
152
|
+
projectPath: z.string().optional().describe('Project root path. Defaults to cwd.'),
|
|
153
|
+
limit: z.number().optional().describe('Max snapshots to return (most recent). Defaults to all.'),
|
|
154
|
+
}, async ({ projectPath, limit }) => {
|
|
155
|
+
try {
|
|
156
|
+
const pp = resolveProjectPath(projectPath);
|
|
157
|
+
assertInitialized(pp);
|
|
158
|
+
const snapshots = getAllSnapshots(pp);
|
|
159
|
+
if (snapshots.length === 0) {
|
|
160
|
+
return { content: [{ type: 'text', text: 'No snapshots yet. Run `pmpt save` to create one.' }] };
|
|
161
|
+
}
|
|
162
|
+
let display = snapshots;
|
|
163
|
+
if (limit && limit > 0 && limit < snapshots.length) {
|
|
164
|
+
display = snapshots.slice(-limit);
|
|
165
|
+
}
|
|
166
|
+
const lines = [`${snapshots.length} snapshot(s)${limit ? `, showing last ${display.length}` : ''}:`, ''];
|
|
167
|
+
for (const s of display) {
|
|
168
|
+
const changed = s.changedFiles?.length ?? s.files.length;
|
|
169
|
+
const git = s.git ? ` [${s.git.commit}]` : '';
|
|
170
|
+
lines.push(`v${s.version} — ${s.timestamp.slice(0, 16)} — ${changed} changed, ${s.files.length} total${git}`);
|
|
171
|
+
}
|
|
172
|
+
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
server.tool('pmpt_diff', 'Compare two versions, or a version against the current working copy.', {
|
|
179
|
+
projectPath: z.string().optional().describe('Project root path. Defaults to cwd.'),
|
|
180
|
+
v1: z.number().describe('First version number (e.g. 1 for v1).'),
|
|
181
|
+
v2: z.number().optional().describe('Second version. If omitted, compares against working copy.'),
|
|
182
|
+
}, async ({ projectPath, v1, v2 }) => {
|
|
183
|
+
try {
|
|
184
|
+
const pp = resolveProjectPath(projectPath);
|
|
185
|
+
assertInitialized(pp);
|
|
186
|
+
const snapshots = getAllSnapshots(pp);
|
|
187
|
+
const fromIndex = snapshots.findIndex((s) => s.version === v1);
|
|
188
|
+
if (fromIndex === -1) {
|
|
189
|
+
return { content: [{ type: 'text', text: `Version v${v1} not found.` }], isError: true };
|
|
190
|
+
}
|
|
191
|
+
const oldFiles = resolveFullSnapshot(snapshots, fromIndex);
|
|
192
|
+
let newFiles;
|
|
193
|
+
let targetLabel;
|
|
194
|
+
if (v2 !== undefined) {
|
|
195
|
+
const toIndex = snapshots.findIndex((s) => s.version === v2);
|
|
196
|
+
if (toIndex === -1) {
|
|
197
|
+
return { content: [{ type: 'text', text: `Version v${v2} not found.` }], isError: true };
|
|
198
|
+
}
|
|
199
|
+
newFiles = resolveFullSnapshot(snapshots, toIndex);
|
|
200
|
+
targetLabel = `v${v2}`;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
newFiles = readWorkingCopy(pp);
|
|
204
|
+
targetLabel = 'working copy';
|
|
205
|
+
}
|
|
206
|
+
const diffs = diffSnapshots(oldFiles, newFiles);
|
|
207
|
+
return {
|
|
208
|
+
content: [
|
|
209
|
+
{ type: 'text', text: `Diff: v${v1} → ${targetLabel}` },
|
|
210
|
+
{ type: 'text', text: formatDiffs(diffs) },
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
server.tool('pmpt_quality', 'Check project quality score and publish readiness.', { projectPath: z.string().optional().describe('Project root path. Defaults to cwd.') }, async ({ projectPath }) => {
|
|
219
|
+
try {
|
|
220
|
+
const pp = resolveProjectPath(projectPath);
|
|
221
|
+
assertInitialized(pp);
|
|
222
|
+
const quality = computeQuality(buildQualityInput(pp));
|
|
223
|
+
const lines = [
|
|
224
|
+
`Quality: ${quality.score}/100 (Grade ${quality.grade})`,
|
|
225
|
+
`Publish ready: ${quality.passesMinimum ? 'Yes' : 'No (minimum 40 required)'}`,
|
|
226
|
+
'',
|
|
227
|
+
];
|
|
228
|
+
for (const item of quality.details) {
|
|
229
|
+
const icon = item.score === item.maxScore ? '[PASS]' : '[FAIL]';
|
|
230
|
+
lines.push(`${icon} ${item.label}: ${item.score}/${item.maxScore}${item.tip ? ` — ${item.tip}` : ''}`);
|
|
231
|
+
}
|
|
232
|
+
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
// ── Start ───────────────────────────────────────────
|
|
239
|
+
async function main() {
|
|
240
|
+
const transport = new StdioServerTransport();
|
|
241
|
+
await server.connect(transport);
|
|
242
|
+
console.error('pmpt MCP server running on stdio');
|
|
243
|
+
}
|
|
244
|
+
main().catch((error) => {
|
|
245
|
+
console.error('Fatal error:', error);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
});
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmpt-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Record and share your AI-driven product development journey",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"pmpt": "./dist/index.js"
|
|
7
|
+
"pmpt": "./dist/index.js",
|
|
8
|
+
"pmpt-mcp": "./dist/mcp.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"dist"
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
37
38
|
"@clack/prompts": "^0.7.0",
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
38
40
|
"chokidar": "^3.6.0",
|
|
39
41
|
"commander": "^12.0.0",
|
|
40
42
|
"fast-glob": "^3.3.0",
|