pmpt-cli 1.12.0 → 1.12.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.
package/README.md CHANGED
@@ -2,17 +2,15 @@
2
2
 
3
3
  # pmpt
4
4
 
5
- **Answer 5 questions. Start building with AI.**
5
+ **Record and share your AI-driven product development journey.**
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/pmpt-cli.svg?style=flat-square&color=cb3837)](https://www.npmjs.com/package/pmpt-cli)
8
8
  [![license](https://img.shields.io/github/license/pmptwiki/pmpt-cli?style=flat-square&v=2)](https://github.com/pmptwiki/pmpt-cli/blob/main/LICENSE)
9
9
  [![node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](https://github.com/pmptwiki/pmpt-cli)
10
10
 
11
- The CLI that turns your idea into an AI-ready prompt in 30 seconds.
11
+ Plan with 5 questions. Build with AI. Save every version. Share and reproduce.
12
12
 
13
- No coding required. No complex setup. Just answer 5 questions.
14
-
15
- [Quick Start](#quick-start) · [Commands](#commands) · [How It Works](#how-it-works) · [Explore Projects](#explore-projects)
13
+ [Quick Start](#quick-start) · [Commands](#commands) · [MCP Server](#mcp-server) · [Explore Projects](#explore-projects)
16
14
 
17
15
  </div>
18
16
 
@@ -57,7 +55,7 @@ pmpt save
57
55
  pmpt login && pmpt publish
58
56
 
59
57
  # Bonus: Explore what others are building
60
- pmpt browse
58
+ pmpt explore
61
59
  ```
62
60
 
63
61
  ---
@@ -113,17 +111,51 @@ The generated prompt is **automatically copied to your clipboard**. Just paste i
113
111
  | Command | Description |
114
112
  |---------|-------------|
115
113
  | `pmpt login` | Authenticate via GitHub (one-time) |
116
- | `pmpt publish` | Publish your project (requires pmpt.md, quality ≥ 40) |
117
- | `pmpt publish --force` | Publish even if quality score is below minimum |
114
+ | `pmpt publish` | Publish your project (requires quality score ≥ 40) |
115
+ | `pmpt update` | Quick re-publish: update content without changing metadata |
118
116
  | `pmpt edit` | Edit published project metadata (description, tags, category) |
119
117
  | `pmpt unpublish` | Remove a published project from pmptwiki |
120
118
  | `pmpt clone <slug>` | Clone and reproduce someone's project |
121
- | `pmpt browse` | Browse and search published projects |
119
+ | `pmpt explore` | Open pmptwiki.com/explore in your browser |
120
+
121
+ > Quality score below 40? pmpt copies an **AI improvement prompt** to your clipboard — paste it into your AI tool to get help improving your project.
122
122
 
123
123
  > See the full documentation at [pmptwiki.com/docs](https://pmptwiki.com/docs)
124
124
 
125
125
  ---
126
126
 
127
+ ## MCP Server
128
+
129
+ pmpt includes a built-in [MCP](https://modelcontextprotocol.io) server so AI tools can interact with pmpt directly. This means Claude Code, Cursor, and other MCP-compatible tools can save snapshots, check status, and review history without you typing commands.
130
+
131
+ ### Setup
132
+
133
+ Add to your `.mcp.json` (or IDE MCP config):
134
+
135
+ ```json
136
+ {
137
+ "mcpServers": {
138
+ "pmpt": {
139
+ "command": "pmpt-mcp"
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ ### Available Tools
146
+
147
+ | Tool | Description |
148
+ |------|-------------|
149
+ | `pmpt_save` | Save a snapshot after completing features, fixes, or milestones |
150
+ | `pmpt_status` | Check tracked files, snapshot count, and quality score |
151
+ | `pmpt_history` | View version history with git commit info |
152
+ | `pmpt_diff` | Compare two versions, or a version against working copy |
153
+ | `pmpt_quality` | Check quality score and publish readiness |
154
+
155
+ All tools accept an optional `projectPath` parameter (defaults to cwd).
156
+
157
+ ---
158
+
127
159
  ## How It Works
128
160
 
129
161
  ```
@@ -158,7 +190,8 @@ your-project/
158
190
  ├── config.json # Project configuration
159
191
  ├── docs/ # Generated documents
160
192
  │ ├── plan.md # Product plan (features checklist)
161
- └── pmpt.md # AI-ready prompt
193
+ ├── pmpt.md # Progress tracking & decisions
194
+ │ └── pmpt.ai.md # AI-ready prompt (project context & instructions)
162
195
  └── .history/ # Auto-saved version history
163
196
  ├── v1-2024-02-20/
164
197
  ├── v2-2024-02-21/
@@ -186,7 +219,8 @@ A single portable file containing your entire development journey:
186
219
  },
187
220
  "docs": {
188
221
  "plan.md": "...",
189
- "pmpt.md": "..."
222
+ "pmpt.md": "...",
223
+ "pmpt.ai.md": "..."
190
224
  },
191
225
  "history": [
192
226
  { "version": 1, "timestamp": "...", "files": {} },
@@ -210,11 +244,11 @@ Share it. Clone it. Reproduce it.
210
244
 
211
245
  ## Explore Projects
212
246
 
213
- Don't know what to build? Browse what others have created with AI.
247
+ Don't know what to build? See what others have created with AI.
214
248
 
215
249
  ```bash
216
- # Discover projects from the community
217
- pmpt browse
250
+ # Open the explore page
251
+ pmpt explore
218
252
 
219
253
  # Found something interesting? Clone it and make it yours
220
254
  pmpt clone budget-tracker-app
@@ -222,7 +256,7 @@ pmpt clone budget-tracker-app
222
256
 
223
257
  **[Explore Projects on pmptwiki.com →](https://pmptwiki.com/explore)**
224
258
 
225
- See how others planned their products, what prompts they used, and how their projects evolved step by step. Clone any project and use it as a starting point for your own.
259
+ Clone any project to see how it was planned, what prompts were used, and how it evolved step by step. The clone output shows the product idea, tech stack, and full version history.
226
260
 
227
261
  ---
228
262
 
@@ -2,7 +2,7 @@ import * as p from '@clack/prompts';
2
2
  import { existsSync, readFileSync } from 'fs';
3
3
  import { resolve } from 'path';
4
4
  import { initializeProject, isInitialized } from '../lib/config.js';
5
- import { isGitRepo, getGitInfo, formatGitInfo } from '../lib/git.js';
5
+ import { isGitRepo, getGitInfo, formatGitInfo, getCommitCount } from '../lib/git.js';
6
6
  import { cmdPlan } from './plan.js';
7
7
  import { scanProject, scanResultToAnswers } from '../lib/scanner.js';
8
8
  import { savePlanDocuments, initPlanProgress, savePlanProgress } from '../lib/plan.js';
@@ -84,12 +84,17 @@ export async function cmdInit(path, options) {
84
84
  }
85
85
  }
86
86
  }
87
+ // Detect project origin
88
+ const gitCommits = isGit ? getCommitCount(projectPath) : 0;
89
+ const origin = gitCommits >= 10 ? 'adopted' : 'new';
87
90
  const s = p.spinner();
88
91
  s.start('Initializing project...');
89
92
  try {
90
93
  const config = initializeProject(projectPath, {
91
94
  repo: repoUrl,
92
95
  trackGit: isGit,
96
+ origin,
97
+ gitCommitsAtInit: isGit ? gitCommits : undefined,
93
98
  });
94
99
  s.stop('Initialized');
95
100
  // Build folder structure display
@@ -224,89 +224,120 @@ export async function cmdPublish(path, options) {
224
224
  }
225
225
  }
226
226
  else {
227
- const slugInput = await p.text({
228
- message: 'Project slug (used in URL):',
229
- placeholder: defaultSlug,
230
- defaultValue: savedSlug || '',
231
- validate: (v) => {
232
- if (!/^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$/.test(v)) {
233
- return '3-50 chars, lowercase letters, numbers, and hyphens only.';
234
- }
235
- },
236
- });
237
- if (p.isCancel(slugInput)) {
238
- p.cancel('Cancelled');
239
- process.exit(0);
240
- }
241
- slug = slugInput;
242
- const descriptionInput = await p.text({
243
- message: 'Project description (brief):',
244
- placeholder: existing?.description || planProgress?.answers?.productIdea?.slice(0, 100) || '',
245
- defaultValue: existing?.description || planProgress?.answers?.productIdea?.slice(0, 200) || '',
246
- });
247
- if (p.isCancel(descriptionInput)) {
248
- p.cancel('Cancelled');
249
- process.exit(0);
250
- }
251
- description = descriptionInput;
252
- const tagsInput = await p.text({
253
- message: 'Tags (comma-separated):',
254
- placeholder: 'react, saas, mvp',
255
- defaultValue: existing?.tags?.join(', ') || '',
256
- });
257
- if (p.isCancel(tagsInput)) {
258
- p.cancel('Cancelled');
259
- process.exit(0);
227
+ // If previously published, offer to reuse settings
228
+ let reuseExisting = false;
229
+ if (existing && savedSlug) {
230
+ const categoryLabel = CATEGORY_OPTIONS.find((o) => o.value === existing.category)?.label ?? existing.category ?? '';
231
+ p.note([
232
+ `Slug: ${savedSlug}`,
233
+ `Description: ${existing.description || '(none)'}`,
234
+ existing.tags?.length ? `Tags: ${existing.tags.join(', ')}` : '',
235
+ categoryLabel ? `Category: ${categoryLabel}` : '',
236
+ existing.productUrl ? `Product: ${existing.productUrl}` : '',
237
+ ].filter(Boolean).join('\n'), 'Previous Settings');
238
+ const reuse = await p.confirm({
239
+ message: 'Publish with same settings?',
240
+ initialValue: true,
241
+ });
242
+ if (p.isCancel(reuse)) {
243
+ p.cancel('Cancelled');
244
+ process.exit(0);
245
+ }
246
+ reuseExisting = !!reuse;
260
247
  }
261
- tags = normalizeTags(tagsInput);
262
- const categoryInput = await p.select({
263
- message: 'Project category:',
264
- initialValue: existing?.category || 'other',
265
- options: CATEGORY_OPTIONS,
266
- });
267
- if (p.isCancel(categoryInput)) {
268
- p.cancel('Cancelled');
269
- process.exit(0);
248
+ if (reuseExisting && existing && savedSlug) {
249
+ slug = savedSlug;
250
+ description = existing.description || '';
251
+ tags = existing.tags || [];
252
+ category = existing.category || 'other';
253
+ productUrl = existing.productUrl || '';
254
+ productUrlType = existing.productUrlType || '';
270
255
  }
271
- category = categoryInput;
272
- // Product link (optional)
273
- const linkTypeInput = await p.select({
274
- message: 'Product link (optional):',
275
- initialValue: existing?.productUrlType || 'none',
276
- options: [
277
- { value: 'none', label: 'No link' },
278
- { value: 'git', label: 'Git Repository' },
279
- { value: 'url', label: 'Website / URL' },
280
- ],
281
- });
282
- if (p.isCancel(linkTypeInput)) {
283
- p.cancel('Cancelled');
284
- process.exit(0);
285
- }
286
- if (linkTypeInput !== 'none') {
287
- productUrlType = linkTypeInput;
288
- const productUrlInput = await p.text({
289
- message: 'Product URL:',
290
- placeholder: linkTypeInput === 'git'
291
- ? `https://github.com/${auth.username}/${slug}`
292
- : 'https://...',
293
- defaultValue: existing?.productUrl || '',
256
+ else {
257
+ const slugInput = await p.text({
258
+ message: 'Project slug (used in URL):',
259
+ placeholder: defaultSlug,
260
+ defaultValue: savedSlug || '',
294
261
  validate: (v) => {
295
- if (!v.trim())
296
- return 'URL is required when link type is selected.';
297
- try {
298
- new URL(v);
299
- }
300
- catch {
301
- return 'Invalid URL format.';
262
+ if (!/^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$/.test(v)) {
263
+ return '3-50 chars, lowercase letters, numbers, and hyphens only.';
302
264
  }
303
265
  },
304
266
  });
305
- if (p.isCancel(productUrlInput)) {
267
+ if (p.isCancel(slugInput)) {
268
+ p.cancel('Cancelled');
269
+ process.exit(0);
270
+ }
271
+ slug = slugInput;
272
+ const descriptionInput = await p.text({
273
+ message: 'Project description (brief):',
274
+ placeholder: existing?.description || planProgress?.answers?.productIdea?.slice(0, 100) || '',
275
+ defaultValue: existing?.description || planProgress?.answers?.productIdea?.slice(0, 200) || '',
276
+ });
277
+ if (p.isCancel(descriptionInput)) {
278
+ p.cancel('Cancelled');
279
+ process.exit(0);
280
+ }
281
+ description = descriptionInput;
282
+ const tagsInput = await p.text({
283
+ message: 'Tags (comma-separated):',
284
+ placeholder: 'react, saas, mvp',
285
+ defaultValue: existing?.tags?.join(', ') || '',
286
+ });
287
+ if (p.isCancel(tagsInput)) {
306
288
  p.cancel('Cancelled');
307
289
  process.exit(0);
308
290
  }
309
- productUrl = productUrlInput;
291
+ tags = normalizeTags(tagsInput);
292
+ const categoryInput = await p.select({
293
+ message: 'Project category:',
294
+ initialValue: existing?.category || 'other',
295
+ options: CATEGORY_OPTIONS,
296
+ });
297
+ if (p.isCancel(categoryInput)) {
298
+ p.cancel('Cancelled');
299
+ process.exit(0);
300
+ }
301
+ category = categoryInput;
302
+ // Product link (optional)
303
+ const linkTypeInput = await p.select({
304
+ message: 'Product link (optional):',
305
+ initialValue: existing?.productUrlType || 'none',
306
+ options: [
307
+ { value: 'none', label: 'No link' },
308
+ { value: 'git', label: 'Git Repository' },
309
+ { value: 'url', label: 'Website / URL' },
310
+ ],
311
+ });
312
+ if (p.isCancel(linkTypeInput)) {
313
+ p.cancel('Cancelled');
314
+ process.exit(0);
315
+ }
316
+ if (linkTypeInput !== 'none') {
317
+ productUrlType = linkTypeInput;
318
+ const productUrlInput = await p.text({
319
+ message: 'Product URL:',
320
+ placeholder: linkTypeInput === 'git'
321
+ ? `https://github.com/${auth.username}/${slug}`
322
+ : 'https://...',
323
+ defaultValue: existing?.productUrl || '',
324
+ validate: (v) => {
325
+ if (!v.trim())
326
+ return 'URL is required when link type is selected.';
327
+ try {
328
+ new URL(v);
329
+ }
330
+ catch {
331
+ return 'Invalid URL format.';
332
+ }
333
+ },
334
+ });
335
+ if (p.isCancel(productUrlInput)) {
336
+ p.cancel('Cancelled');
337
+ process.exit(0);
338
+ }
339
+ productUrl = productUrlInput;
340
+ }
310
341
  }
311
342
  }
312
343
  // Build .pmpt content (resolve from optimized snapshots)
@@ -323,6 +354,8 @@ export async function cmdPublish(path, options) {
323
354
  description: description,
324
355
  createdAt: config?.createdAt || new Date().toISOString(),
325
356
  exportedAt: new Date().toISOString(),
357
+ origin: config?.origin,
358
+ gitCommitsAtInit: config?.gitCommitsAtInit,
326
359
  };
327
360
  const planAnswers = planProgress?.answers
328
361
  ? {
@@ -0,0 +1,154 @@
1
+ import * as p from '@clack/prompts';
2
+ import { resolve, basename } from 'path';
3
+ import { readFileSync, existsSync } from 'fs';
4
+ import { isInitialized, loadConfig, saveConfig, getDocsDir } from '../lib/config.js';
5
+ import { getAllSnapshots, resolveFullSnapshot } from '../lib/history.js';
6
+ import { getPlanProgress } from '../lib/plan.js';
7
+ import { createPmptFile } from '../lib/pmptFile.js';
8
+ import { loadAuth } from '../lib/auth.js';
9
+ import { publishProject, fetchProjects } from '../lib/api.js';
10
+ import { computeQuality } from '../lib/quality.js';
11
+ import glob from 'fast-glob';
12
+ import { join } from 'path';
13
+ function readDocsFolder(docsDir) {
14
+ const files = {};
15
+ if (!existsSync(docsDir))
16
+ return files;
17
+ const mdFiles = glob.sync('**/*.md', { cwd: docsDir });
18
+ for (const file of mdFiles) {
19
+ try {
20
+ files[file] = readFileSync(join(docsDir, file), 'utf-8');
21
+ }
22
+ catch { /* skip */ }
23
+ }
24
+ return files;
25
+ }
26
+ export async function cmdUpdate(path) {
27
+ const projectPath = path ? resolve(path) : process.cwd();
28
+ if (!isInitialized(projectPath)) {
29
+ p.log.error('Project not initialized. Run `pmpt init` first.');
30
+ process.exit(1);
31
+ }
32
+ const auth = loadAuth();
33
+ if (!auth?.token || !auth?.username) {
34
+ p.log.error('Login required. Run `pmpt login` first.');
35
+ process.exit(1);
36
+ }
37
+ p.intro('pmpt update');
38
+ const config = loadConfig(projectPath);
39
+ const savedSlug = config?.lastPublishedSlug;
40
+ if (!savedSlug) {
41
+ p.log.error('No previously published project found.');
42
+ p.log.info('Run `pmpt publish` first to publish your project.');
43
+ p.outro('');
44
+ process.exit(1);
45
+ }
46
+ // Find existing project on platform
47
+ let existing;
48
+ try {
49
+ const index = await fetchProjects();
50
+ existing = index.projects.find((proj) => proj.slug === savedSlug && proj.author === auth.username);
51
+ }
52
+ catch {
53
+ p.log.error('Failed to fetch project info. Check your internet connection.');
54
+ p.outro('');
55
+ process.exit(1);
56
+ }
57
+ if (!existing) {
58
+ p.log.error(`Project "${savedSlug}" not found on platform.`);
59
+ p.log.info('Run `pmpt publish` to publish it first.');
60
+ p.outro('');
61
+ process.exit(1);
62
+ }
63
+ const snapshots = getAllSnapshots(projectPath);
64
+ const planProgress = getPlanProgress(projectPath);
65
+ if (snapshots.length === 0) {
66
+ p.log.warn('No snapshots found. Run `pmpt save` first.');
67
+ p.outro('');
68
+ return;
69
+ }
70
+ // Quality gate
71
+ const docsDir = getDocsDir(projectPath);
72
+ const aiMdPath = join(docsDir, 'pmpt.ai.md');
73
+ const aiMdContent = existsSync(aiMdPath) ? readFileSync(aiMdPath, 'utf-8').trim() : '';
74
+ const trackedFiles = glob.sync('**/*.md', { cwd: docsDir });
75
+ const hasGit = snapshots.some((s) => !!s.git);
76
+ const quality = computeQuality({
77
+ pmptAiMd: aiMdContent || null,
78
+ planAnswers: planProgress?.answers ?? null,
79
+ versionCount: snapshots.length,
80
+ docFiles: trackedFiles,
81
+ hasGit,
82
+ });
83
+ if (!quality.passesMinimum) {
84
+ p.log.warn(`Quality score ${quality.score}/100 is below minimum (40).`);
85
+ p.log.info('Run `pmpt publish` for detailed quality breakdown and improvement tips.');
86
+ p.outro('');
87
+ process.exit(1);
88
+ }
89
+ // Build .pmpt content
90
+ const projectName = planProgress?.answers?.projectName || basename(projectPath);
91
+ const history = snapshots.map((snapshot, i) => ({
92
+ version: snapshot.version,
93
+ timestamp: snapshot.timestamp,
94
+ files: resolveFullSnapshot(snapshots, i),
95
+ git: snapshot.git,
96
+ }));
97
+ const docs = readDocsFolder(docsDir);
98
+ const meta = {
99
+ projectName,
100
+ author: auth.username,
101
+ description: existing.description,
102
+ createdAt: config?.createdAt || new Date().toISOString(),
103
+ exportedAt: new Date().toISOString(),
104
+ origin: config?.origin,
105
+ gitCommitsAtInit: config?.gitCommitsAtInit,
106
+ };
107
+ const planAnswers = planProgress?.answers
108
+ ? {
109
+ projectName: planProgress.answers.projectName,
110
+ productIdea: planProgress.answers.productIdea,
111
+ additionalContext: planProgress.answers.additionalContext,
112
+ coreFeatures: planProgress.answers.coreFeatures,
113
+ techStack: planProgress.answers.techStack,
114
+ }
115
+ : undefined;
116
+ const pmptContent = createPmptFile(meta, planAnswers, docs, history);
117
+ // Show summary
118
+ p.note([
119
+ `Slug: ${savedSlug}`,
120
+ `Versions: ${snapshots.length}`,
121
+ `Size: ${(pmptContent.length / 1024).toFixed(1)} KB`,
122
+ `Quality: ${quality.score}/100 (${quality.grade})`,
123
+ ].join('\n'), 'Update Preview');
124
+ // Upload
125
+ const s = p.spinner();
126
+ s.start('Uploading...');
127
+ try {
128
+ const result = await publishProject(auth.token, {
129
+ slug: savedSlug,
130
+ pmptContent,
131
+ description: existing.description,
132
+ tags: existing.tags || [],
133
+ category: existing.category,
134
+ ...(existing.productUrl && { productUrl: existing.productUrl, productUrlType: existing.productUrlType }),
135
+ });
136
+ s.stop('Updated!');
137
+ if (config) {
138
+ config.lastPublished = new Date().toISOString();
139
+ saveConfig(projectPath, config);
140
+ }
141
+ p.note([
142
+ `URL: ${result.url}`,
143
+ '',
144
+ 'Content updated. Metadata unchanged.',
145
+ 'To change metadata, use `pmpt edit`.',
146
+ ].join('\n'), 'Updated!');
147
+ }
148
+ catch (err) {
149
+ s.stop('Update failed');
150
+ p.log.error(err instanceof Error ? err.message : 'Failed to update.');
151
+ process.exit(1);
152
+ }
153
+ p.outro('');
154
+ }
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ import { cmdExport } from './commands/export.js';
34
34
  import { cmdImport } from './commands/import.js';
35
35
  import { cmdLogin } from './commands/login.js';
36
36
  import { cmdPublish } from './commands/publish.js';
37
+ import { cmdUpdate } from './commands/update.js';
37
38
  import { cmdEdit } from './commands/edit.js';
38
39
  import { cmdUnpublish } from './commands/unpublish.js';
39
40
  import { cmdClone } from './commands/clone.js';
@@ -63,6 +64,7 @@ Examples:
63
64
  $ pmpt import <file.pmpt> Import from .pmpt file
64
65
  $ pmpt login Authenticate with pmptwiki
65
66
  $ pmpt publish Publish project to pmptwiki
67
+ $ pmpt update Quick re-publish (content only)
66
68
  $ pmpt clone <slug> Clone a project from pmptwiki
67
69
  $ pmpt explore Explore projects on pmptwiki.com
68
70
  $ pmpt recover Recover damaged pmpt.md via AI
@@ -143,6 +145,10 @@ program
143
145
  .option('--product-url-type <type>', 'Product link type: git or url')
144
146
  .option('--yes', 'Skip confirmation prompt')
145
147
  .action(cmdPublish);
148
+ program
149
+ .command('update [path]')
150
+ .description('Quick re-publish: update content without changing metadata')
151
+ .action(cmdUpdate);
146
152
  program
147
153
  .command('edit')
148
154
  .description('Edit published project metadata (description, tags, category)')
@@ -35,6 +35,8 @@ export function initializeProject(projectPath, options) {
35
35
  createdAt: new Date().toISOString(),
36
36
  repo: options?.repo,
37
37
  trackGit: options?.trackGit ?? true,
38
+ origin: options?.origin,
39
+ gitCommitsAtInit: options?.gitCommitsAtInit,
38
40
  };
39
41
  saveConfig(projectPath, config);
40
42
  // Create README.md if it doesn't exist
package/dist/lib/git.js CHANGED
@@ -84,6 +84,13 @@ export function isCommitMatch(path, expectedCommit) {
84
84
  currentFull.startsWith(expectedCommit) ||
85
85
  expectedCommit.startsWith(currentShort));
86
86
  }
87
+ /**
88
+ * Count total commits in the repository
89
+ */
90
+ export function getCommitCount(path) {
91
+ const count = git(path, 'rev-list --count HEAD');
92
+ return count ? parseInt(count, 10) : 0;
93
+ }
87
94
  /**
88
95
  * Convert git info to human-readable string
89
96
  */
@@ -65,6 +65,8 @@ const MetaSchema = z.object({
65
65
  description: z.string().optional(),
66
66
  createdAt: z.string(),
67
67
  exportedAt: z.string(),
68
+ origin: z.enum(['new', 'adopted']).optional(),
69
+ gitCommitsAtInit: z.number().optional(),
68
70
  });
69
71
  // Full .pmpt file schema
70
72
  export const PmptFileSchema = z.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmpt-cli",
3
- "version": "1.12.0",
3
+ "version": "1.12.1",
4
4
  "description": "Record and share your AI-driven product development journey",
5
5
  "type": "module",
6
6
  "bin": {