pmpt-cli 1.3.0 → 1.4.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/README.md CHANGED
@@ -1,66 +1,137 @@
1
- # pmpt-cli
1
+ # pmpt
2
2
 
3
- CLI tool for recording and sharing your AI-driven product development journey.
3
+ **Record and share your AI-driven product development journey.**
4
+
5
+ AI와 대화하며 제품을 만드는 여정을 기록하고 공유하세요.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/pmpt-cli.svg)](https://www.npmjs.com/package/pmpt-cli)
4
8
 
5
9
  **Website**: [pmptwiki.com](https://pmptwiki.com)
6
10
 
11
+ ---
12
+
7
13
  ## Install
8
14
 
9
15
  ```bash
10
16
  npm install -g pmpt-cli
11
17
  ```
12
18
 
19
+ ---
20
+
13
21
  ## Quick Start
14
22
 
15
23
  ```bash
16
- # Initialize project
24
+ # 1. Initialize project
17
25
  pmpt init
18
26
 
19
- # Start product planning (6 questions → AI prompt)
27
+ # 2. Answer 5 questions → AI prompt generated
20
28
  pmpt plan
21
29
 
22
- # Save snapshot manually
23
- pmpt save
24
-
25
- # Or auto-detect file changes
26
- pmpt watch
30
+ # 3. Copy the prompt to Claude/ChatGPT/Cursor and build!
27
31
 
28
- # View version history
29
- pmpt history
30
- pmpt history --compact # Hide minor changes
32
+ # 4. Save your progress
33
+ pmpt save
31
34
 
32
- # Squash versions
33
- pmpt squash v2 v5 # Merge v2-v5 into v2
35
+ # 5. Export & share
36
+ pmpt export
34
37
  ```
35
38
 
36
- ## Folder Structure
39
+ ---
37
40
 
38
- ```
39
- .promptwiki/
40
- ├── config.json # Config file
41
- ├── pmpt/ # Working folder (MD files)
42
- └── .history/ # Version history
43
- ```
41
+ ## Why pmpt?
44
42
 
45
- ## Workflow
43
+ - **5 questions** — Quick product planning with AI-ready prompts
44
+ - **Version history** — Track every step of your AI-assisted development
45
+ - **Share & reproduce** — Export `.pmpt` files for others to learn from
46
46
 
47
- 1. `pmpt init` → Initialize project
48
- 2. `pmpt plan` → Answer 6 questions → `pmpt.md` generated
49
- 3. Copy `pmpt.md` to AI → Build with AI conversation
50
- 4. `pmpt save` or `pmpt watch` → Save progress
51
- 5. `pmpt submit` → Share to archive (coming soon)
47
+ ---
52
48
 
53
49
  ## Commands
54
50
 
55
51
  | Command | Description |
56
52
  |---------|-------------|
57
- | `pmpt init` | Initialize project folder |
58
- | `pmpt plan` | Quick product planning with 6 questions |
53
+ | `pmpt init` | Initialize project |
54
+ | `pmpt plan` | 5 questions AI prompt (copied to clipboard) |
59
55
  | `pmpt save` | Save current state as snapshot |
60
56
  | `pmpt watch` | Auto-detect file changes |
61
- | `pmpt status` | Check project status |
62
57
  | `pmpt history` | View version history |
63
- | `pmpt squash` | Merge multiple versions |
58
+ | `pmpt history --compact` | Hide minor changes |
59
+ | `pmpt squash v2 v5` | Merge versions v2-v5 into v2 |
60
+ | `pmpt export` | Export as `.pmpt` file (single JSON) |
61
+ | `pmpt import <file>` | Import from `.pmpt` file |
62
+ | `pmpt status` | Check project status |
63
+
64
+ ---
65
+
66
+ ## Folder Structure
67
+
68
+ ```
69
+ .pmpt/
70
+ ├── config.json # Config file
71
+ ├── docs/ # Working folder (MD files)
72
+ │ ├── plan.md # Product plan
73
+ │ └── pmpt.md # AI prompt
74
+ └── .history/ # Version history
75
+ ├── v1-2024-02-20/
76
+ ├── v2-2024-02-21/
77
+ └── ...
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Workflow
83
+
84
+ ```
85
+ [You]
86
+
87
+ ├─ pmpt plan ────→ 5 questions → AI prompt (clipboard)
88
+
89
+ ├─ Build with AI ─→ Create files, iterate
90
+
91
+ ├─ pmpt save ────→ Save to .pmpt/.history
92
+
93
+ ├─ pmpt export ──→ Create .pmpt file (shareable)
94
+
95
+ └─ pmpt import ──→ Reproduce someone's project
96
+ ```
97
+
98
+ ---
99
+
100
+ ## .pmpt File Format
101
+
102
+ Single JSON file containing your entire development journey:
103
+
104
+ ```json
105
+ {
106
+ "schemaVersion": "1.0",
107
+ "meta": { "projectName", "description", "createdAt" },
108
+ "plan": { "productIdea", "coreFeatures", "techStack" },
109
+ "docs": { "plan.md": "...", "pmpt.md": "..." },
110
+ "history": [
111
+ { "version": 1, "timestamp": "...", "files": {...} },
112
+ { "version": 2, "timestamp": "...", "files": {...} }
113
+ ]
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Use Cases
120
+
121
+ - **Side project builders** — Track your AI-assisted development
122
+ - **Startup founders** — Document MVP creation process
123
+ - **Content creators** — Share your coding journey
124
+ - **Learners** — Study how others build with AI
125
+
126
+ ---
127
+
128
+ ## Links
129
+
130
+ - [Website](https://pmptwiki.com)
131
+ - [GitHub](https://github.com/pmptwiki/pmpt-cli)
132
+ - [npm](https://www.npmjs.com/package/pmpt-cli)
133
+
134
+ ---
64
135
 
65
136
  ## License
66
137
 
@@ -0,0 +1,73 @@
1
+ import * as p from '@clack/prompts';
2
+ import { fetchProjects } from '../lib/api.js';
3
+ export async function cmdBrowse() {
4
+ p.intro('pmpt browse');
5
+ const s = p.spinner();
6
+ s.start('프로젝트 목록 불러오는 중...');
7
+ let projects;
8
+ try {
9
+ const index = await fetchProjects();
10
+ projects = index.projects;
11
+ }
12
+ catch (err) {
13
+ s.stop('불러오기 실패');
14
+ p.log.error(err instanceof Error ? err.message : '프로젝트 목록을 불러올 수 없습니다.');
15
+ process.exit(1);
16
+ }
17
+ s.stop(`${projects.length}개 프로젝트`);
18
+ if (projects.length === 0) {
19
+ p.log.info('아직 공개된 프로젝트가 없습니다.');
20
+ p.log.message(' pmpt publish — 첫 번째 프로젝트를 공유해보세요!');
21
+ p.outro('');
22
+ return;
23
+ }
24
+ // Select project
25
+ const selected = await p.select({
26
+ message: '프로젝트를 선택하세요:',
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: '어떻게 할까요?',
51
+ options: [
52
+ { value: 'clone', label: '이 프로젝트 복제', hint: 'pmpt clone' },
53
+ { value: 'url', label: 'URL 표시', hint: '브라우저에서 보기' },
54
+ { value: 'back', label: '돌아가기' },
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/ko/p/${project.slug}`;
68
+ p.log.info(`URL: ${url}`);
69
+ p.log.message(`Download: ${project.downloadUrl}`);
70
+ p.log.message(`\npmpt clone ${project.slug} — 터미널에서 복제`);
71
+ p.outro('');
72
+ }
73
+ }
@@ -0,0 +1,127 @@
1
+ import * as p from '@clack/prompts';
2
+ import { join, dirname } from 'path';
3
+ import { existsSync, mkdirSync, writeFileSync, readdirSync } from 'fs';
4
+ import { isInitialized, getConfigDir, getHistoryDir, getDocsDir, initializeProject } from '../lib/config.js';
5
+ import { validatePmptFile } from '../lib/pmptFile.js';
6
+ import { fetchPmptFile } from '../lib/api.js';
7
+ /**
8
+ * Restore history from .pmpt data (shared with import command)
9
+ */
10
+ export function restoreHistory(historyDir, history) {
11
+ mkdirSync(historyDir, { recursive: true });
12
+ for (const version of history) {
13
+ const timestamp = version.timestamp.replace(/[:.]/g, '-').slice(0, 19);
14
+ const snapshotName = `v${version.version}-${timestamp}`;
15
+ const snapshotDir = join(historyDir, snapshotName);
16
+ mkdirSync(snapshotDir, { recursive: true });
17
+ for (const [filename, content] of Object.entries(version.files)) {
18
+ const filePath = join(snapshotDir, filename);
19
+ const fileDir = dirname(filePath);
20
+ if (fileDir !== snapshotDir) {
21
+ mkdirSync(fileDir, { recursive: true });
22
+ }
23
+ writeFileSync(filePath, content, 'utf-8');
24
+ }
25
+ writeFileSync(join(snapshotDir, '.meta.json'), JSON.stringify({
26
+ version: version.version,
27
+ timestamp: version.timestamp,
28
+ files: Object.keys(version.files),
29
+ git: version.git,
30
+ }, null, 2), 'utf-8');
31
+ }
32
+ }
33
+ /**
34
+ * Restore docs from .pmpt data (shared with import command)
35
+ */
36
+ export function restoreDocs(docsDir, docs) {
37
+ mkdirSync(docsDir, { recursive: true });
38
+ for (const [filename, content] of Object.entries(docs)) {
39
+ const filePath = join(docsDir, filename);
40
+ const fileDir = dirname(filePath);
41
+ if (fileDir !== docsDir) {
42
+ mkdirSync(fileDir, { recursive: true });
43
+ }
44
+ writeFileSync(filePath, content, 'utf-8');
45
+ }
46
+ }
47
+ export async function cmdClone(slug) {
48
+ if (!slug) {
49
+ p.log.error('slug를 입력하세요.');
50
+ p.log.info('사용법: pmpt clone <slug>');
51
+ process.exit(1);
52
+ }
53
+ p.intro(`pmpt clone — ${slug}`);
54
+ const s = p.spinner();
55
+ s.start('프로젝트 다운로드 중...');
56
+ let fileContent;
57
+ try {
58
+ fileContent = await fetchPmptFile(slug);
59
+ }
60
+ catch (err) {
61
+ s.stop('다운로드 실패');
62
+ p.log.error(err instanceof Error ? err.message : '프로젝트를 찾을 수 없습니다.');
63
+ process.exit(1);
64
+ }
65
+ s.message('검증 중...');
66
+ const validation = validatePmptFile(fileContent);
67
+ if (!validation.success || !validation.data) {
68
+ s.stop('검증 실패');
69
+ p.log.error(validation.error || '잘못된 .pmpt 파일입니다.');
70
+ process.exit(1);
71
+ }
72
+ const pmptData = validation.data;
73
+ s.stop('다운로드 완료');
74
+ // Show summary
75
+ p.note([
76
+ `Project: ${pmptData.meta.projectName}`,
77
+ `Versions: ${pmptData.history.length}`,
78
+ pmptData.meta.author ? `Author: @${pmptData.meta.author}` : '',
79
+ pmptData.meta.description ? `Description: ${pmptData.meta.description.slice(0, 80)}` : '',
80
+ ].filter(Boolean).join('\n'), 'Project Info');
81
+ const projectPath = process.cwd();
82
+ if (isInitialized(projectPath)) {
83
+ const overwrite = await p.confirm({
84
+ message: '이미 초기화된 프로젝트입니다. 히스토리를 병합하시겠습니까?',
85
+ initialValue: true,
86
+ });
87
+ if (p.isCancel(overwrite) || !overwrite) {
88
+ p.cancel('취소됨');
89
+ process.exit(0);
90
+ }
91
+ }
92
+ const importSpinner = p.spinner();
93
+ importSpinner.start('프로젝트 복원 중...');
94
+ if (!isInitialized(projectPath)) {
95
+ initializeProject(projectPath, { trackGit: true });
96
+ }
97
+ const pmptDir = getConfigDir(projectPath);
98
+ const historyDir = getHistoryDir(projectPath);
99
+ const docsDir = getDocsDir(projectPath);
100
+ restoreHistory(historyDir, pmptData.history);
101
+ if (pmptData.docs) {
102
+ restoreDocs(docsDir, pmptData.docs);
103
+ }
104
+ if (pmptData.plan) {
105
+ writeFileSync(join(pmptDir, 'plan-progress.json'), JSON.stringify({
106
+ completed: true,
107
+ startedAt: pmptData.meta.createdAt,
108
+ updatedAt: pmptData.meta.exportedAt,
109
+ answers: pmptData.plan,
110
+ }, null, 2), 'utf-8');
111
+ }
112
+ let versionCount = 0;
113
+ if (existsSync(historyDir)) {
114
+ versionCount = readdirSync(historyDir).filter((d) => d.startsWith('v')).length;
115
+ }
116
+ importSpinner.stop('복원 완료!');
117
+ p.note([
118
+ `Project: ${pmptData.meta.projectName}`,
119
+ `Versions: ${versionCount}`,
120
+ `Location: ${pmptDir}`,
121
+ ].join('\n'), 'Clone Summary');
122
+ p.log.info('다음 단계:');
123
+ p.log.message(' pmpt history — 버전 히스토리 보기');
124
+ p.log.message(' pmpt plan — AI 프롬프트 보기');
125
+ p.log.message(' pmpt save — 새 스냅샷 저장');
126
+ p.outro('프로젝트가 복제되었습니다!');
127
+ }
@@ -0,0 +1,46 @@
1
+ import * as p from '@clack/prompts';
2
+ import { loadAuth, saveAuth } from '../lib/auth.js';
3
+ import { registerAuth } from '../lib/api.js';
4
+ export async function cmdLogin() {
5
+ p.intro('pmpt login');
6
+ const existing = loadAuth();
7
+ if (existing?.token && existing?.username) {
8
+ p.log.info(`Currently logged in as @${existing.username}`);
9
+ const reauth = await p.confirm({
10
+ message: 'Re-authenticate?',
11
+ initialValue: false,
12
+ });
13
+ if (p.isCancel(reauth) || !reauth) {
14
+ p.outro('');
15
+ return;
16
+ }
17
+ }
18
+ p.log.info('GitHub Personal Access Token이 필요합니다.\n' +
19
+ ' https://github.com/settings/tokens/new\n' +
20
+ ' 필요 권한: read:user');
21
+ const pat = await p.password({
22
+ message: 'GitHub PAT를 입력하세요:',
23
+ validate: (v) => (v.trim().length < 10 ? '올바른 토큰을 입력하세요' : undefined),
24
+ });
25
+ if (p.isCancel(pat)) {
26
+ p.cancel('취소됨');
27
+ process.exit(0);
28
+ }
29
+ const s = p.spinner();
30
+ s.start('인증 중...');
31
+ try {
32
+ const result = await registerAuth(pat);
33
+ saveAuth({
34
+ token: result.token,
35
+ githubToken: pat,
36
+ username: result.username,
37
+ });
38
+ s.stop(`인증 완료 — @${result.username}`);
39
+ }
40
+ catch (err) {
41
+ s.stop('인증 실패');
42
+ p.log.error(err instanceof Error ? err.message : '인증에 실패했습니다.');
43
+ process.exit(1);
44
+ }
45
+ p.outro('로그인 완료! pmpt publish로 프로젝트를 공유하세요.');
46
+ }
@@ -3,7 +3,7 @@ import { writeFileSync, mkdirSync } from 'fs';
3
3
  import { dirname } from 'path';
4
4
  import { generateContent, generateFilePath } from '../lib/template.js';
5
5
  export async function cmdNew() {
6
- p.intro('PromptWiki — 새 문서 만들기');
6
+ p.intro('pmptwiki — 새 문서 만들기');
7
7
  const answers = await p.group({
8
8
  lang: () => p.select({
9
9
  message: '언어를 선택하세요',
@@ -73,6 +73,6 @@ export async function cmdNew() {
73
73
 
74
74
  다음 단계:
75
75
  1. 파일을 열어 본문을 작성하세요
76
- 2. promptwiki validate ${filePath}
77
- 3. promptwiki submit ${filePath}`);
76
+ 2. pmpt validate ${filePath}
77
+ 3. pmpt submit ${filePath}`);
78
78
  }
@@ -0,0 +1,167 @@
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 } 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 } from '../lib/api.js';
10
+ import glob from 'fast-glob';
11
+ import { join } from 'path';
12
+ function readSnapshotFiles(snapshotDir) {
13
+ const files = {};
14
+ if (!existsSync(snapshotDir))
15
+ return files;
16
+ const mdFiles = glob.sync('**/*.md', { cwd: snapshotDir });
17
+ for (const file of mdFiles) {
18
+ try {
19
+ files[file] = readFileSync(join(snapshotDir, file), 'utf-8');
20
+ }
21
+ catch { /* skip */ }
22
+ }
23
+ return files;
24
+ }
25
+ function readDocsFolder(docsDir) {
26
+ const files = {};
27
+ if (!existsSync(docsDir))
28
+ return files;
29
+ const mdFiles = glob.sync('**/*.md', { cwd: docsDir });
30
+ for (const file of mdFiles) {
31
+ try {
32
+ files[file] = readFileSync(join(docsDir, file), 'utf-8');
33
+ }
34
+ catch { /* skip */ }
35
+ }
36
+ return files;
37
+ }
38
+ export async function cmdPublish(path) {
39
+ const projectPath = path ? resolve(path) : process.cwd();
40
+ if (!isInitialized(projectPath)) {
41
+ p.log.error('프로젝트가 초기화되지 않았습니다. `pmpt init`을 먼저 실행하세요.');
42
+ process.exit(1);
43
+ }
44
+ const auth = loadAuth();
45
+ if (!auth?.token || !auth?.username) {
46
+ p.log.error('로그인이 필요합니다. `pmpt login`을 먼저 실행하세요.');
47
+ process.exit(1);
48
+ }
49
+ p.intro('pmpt publish');
50
+ const config = loadConfig(projectPath);
51
+ const snapshots = getAllSnapshots(projectPath);
52
+ const planProgress = getPlanProgress(projectPath);
53
+ if (snapshots.length === 0) {
54
+ p.log.warn('스냅샷이 없습니다. `pmpt save` 또는 `pmpt plan`을 먼저 실행하세요.');
55
+ p.outro('');
56
+ return;
57
+ }
58
+ const projectName = planProgress?.answers?.projectName || basename(projectPath);
59
+ // Collect publish info
60
+ const slug = await p.text({
61
+ message: '프로젝트 slug (URL에 사용될 이름):',
62
+ placeholder: projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-'),
63
+ validate: (v) => {
64
+ if (!/^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$/.test(v)) {
65
+ return '3~50자, 소문자/숫자/하이픈만 사용 가능합니다.';
66
+ }
67
+ },
68
+ });
69
+ if (p.isCancel(slug)) {
70
+ p.cancel('취소됨');
71
+ process.exit(0);
72
+ }
73
+ const description = await p.text({
74
+ message: '프로젝트 설명 (짧게):',
75
+ placeholder: planProgress?.answers?.productIdea?.slice(0, 100) || '',
76
+ defaultValue: planProgress?.answers?.productIdea?.slice(0, 200) || '',
77
+ });
78
+ if (p.isCancel(description)) {
79
+ p.cancel('취소됨');
80
+ process.exit(0);
81
+ }
82
+ const tagsInput = await p.text({
83
+ message: '태그 (쉼표로 구분):',
84
+ placeholder: 'react, saas, mvp',
85
+ defaultValue: '',
86
+ });
87
+ if (p.isCancel(tagsInput)) {
88
+ p.cancel('취소됨');
89
+ process.exit(0);
90
+ }
91
+ const tags = tagsInput
92
+ .split(',')
93
+ .map((t) => t.trim().toLowerCase())
94
+ .filter(Boolean);
95
+ // Build .pmpt content (reuse export logic)
96
+ const history = snapshots.map((snapshot) => ({
97
+ version: snapshot.version,
98
+ timestamp: snapshot.timestamp,
99
+ files: readSnapshotFiles(snapshot.snapshotDir),
100
+ git: snapshot.git,
101
+ }));
102
+ const docsDir = getDocsDir(projectPath);
103
+ const docs = readDocsFolder(docsDir);
104
+ const meta = {
105
+ projectName,
106
+ author: auth.username,
107
+ description: description,
108
+ createdAt: config?.createdAt || new Date().toISOString(),
109
+ exportedAt: new Date().toISOString(),
110
+ };
111
+ const planAnswers = planProgress?.answers
112
+ ? {
113
+ projectName: planProgress.answers.projectName,
114
+ productIdea: planProgress.answers.productIdea,
115
+ additionalContext: planProgress.answers.additionalContext,
116
+ coreFeatures: planProgress.answers.coreFeatures,
117
+ techStack: planProgress.answers.techStack,
118
+ }
119
+ : undefined;
120
+ const pmptContent = createPmptFile(meta, planAnswers, docs, history);
121
+ // Confirm
122
+ p.note([
123
+ `Project: ${projectName}`,
124
+ `Slug: ${slug}`,
125
+ `Versions: ${snapshots.length}`,
126
+ `Size: ${(pmptContent.length / 1024).toFixed(1)} KB`,
127
+ `Author: @${auth.username}`,
128
+ tags.length ? `Tags: ${tags.join(', ')}` : '',
129
+ ].filter(Boolean).join('\n'), 'Publish Preview');
130
+ const confirm = await p.confirm({
131
+ message: '게시하시겠습니까?',
132
+ initialValue: true,
133
+ });
134
+ if (p.isCancel(confirm) || !confirm) {
135
+ p.cancel('취소됨');
136
+ process.exit(0);
137
+ }
138
+ // Upload
139
+ const s = p.spinner();
140
+ s.start('업로드 중...');
141
+ try {
142
+ const result = await publishProject(auth.token, {
143
+ slug: slug,
144
+ pmptContent,
145
+ description: description,
146
+ tags,
147
+ });
148
+ s.stop('게시 완료!');
149
+ // Update config
150
+ if (config) {
151
+ config.lastPublished = new Date().toISOString();
152
+ saveConfig(projectPath, config);
153
+ }
154
+ p.note([
155
+ `URL: ${result.url}`,
156
+ `Download: ${result.downloadUrl}`,
157
+ '',
158
+ `pmpt clone ${slug} — 다른 사람이 이 프로젝트를 복제할 수 있습니다`,
159
+ ].join('\n'), 'Published!');
160
+ }
161
+ catch (err) {
162
+ s.stop('게시 실패');
163
+ p.log.error(err instanceof Error ? err.message : '게시에 실패했습니다.');
164
+ process.exit(1);
165
+ }
166
+ p.outro('');
167
+ }
@@ -6,7 +6,7 @@ import { createClient, createBranch, createPR, ensureFork, getAuthUser, pushFile
6
6
  import { validate } from '../lib/schema.js';
7
7
  import { today } from '../lib/template.js';
8
8
  export async function cmdSubmit(filePath) {
9
- p.intro(`PromptWiki — 제출: ${filePath}`);
9
+ p.intro(`pmptwiki — 제출: ${filePath}`);
10
10
  // 1. 검증
11
11
  const s1 = p.spinner();
12
12
  s1.start('파일 검증 중...');
@@ -15,7 +15,7 @@ export async function cmdSubmit(filePath) {
15
15
  s1.stop('검증 실패');
16
16
  for (const err of result.errors)
17
17
  p.log.error(err);
18
- p.outro('오류를 수정한 후 다시 시도하세요: promptwiki validate ' + filePath);
18
+ p.outro('오류를 수정한 후 다시 시도하세요: pmpt validate ' + filePath);
19
19
  process.exit(1);
20
20
  }
21
21
  s1.stop(`검증 통과${result.warnings.length ? ` (경고 ${result.warnings.length}개)` : ''}`);
@@ -91,7 +91,7 @@ export async function cmdSubmit(filePath) {
91
91
  `- [ ] 제목과 내용이 일치하는가?`,
92
92
  ``,
93
93
  `---`,
94
- `_promptwiki-cli로 제출됨_`,
94
+ `_pmpt-cli로 제출됨_`,
95
95
  ]
96
96
  .filter((l) => l !== null)
97
97
  .join('\n');
@@ -1,7 +1,7 @@
1
1
  import * as p from '@clack/prompts';
2
2
  import { validate } from '../lib/schema.js';
3
3
  export function cmdValidate(filePath) {
4
- p.intro(`PromptWiki — 검증: ${filePath}`);
4
+ p.intro(`pmptwiki — 검증: ${filePath}`);
5
5
  const result = validate(filePath);
6
6
  if (result.errors.length === 0 && result.warnings.length === 0) {
7
7
  p.outro('모든 검증 통과');
package/dist/index.js CHANGED
@@ -12,11 +12,15 @@ import { cmdSave } from './commands/save.js';
12
12
  import { cmdSquash } from './commands/squash.js';
13
13
  import { cmdExport } from './commands/export.js';
14
14
  import { cmdImport } from './commands/import.js';
15
+ import { cmdLogin } from './commands/login.js';
16
+ import { cmdPublish } from './commands/publish.js';
17
+ import { cmdClone } from './commands/clone.js';
18
+ import { cmdBrowse } from './commands/browse.js';
15
19
  const program = new Command();
16
20
  program
17
21
  .name('pmpt')
18
22
  .description('pmpt — Record and share your AI-driven product development journey')
19
- .version('1.3.0')
23
+ .version('1.4.0')
20
24
  .addHelpText('after', `
21
25
  Examples:
22
26
  $ pmpt init Initialize project
@@ -24,16 +28,13 @@ Examples:
24
28
  $ pmpt save Save snapshot of docs folder
25
29
  $ pmpt watch Auto-detect file changes
26
30
  $ pmpt history View version history
27
- $ pmpt history --compact Hide minor changes
28
31
  $ pmpt squash v2 v5 Merge versions v2-v5 into v2
29
32
  $ pmpt export Export as .pmpt file (single JSON)
30
33
  $ pmpt import <file.pmpt> Import from .pmpt file
31
-
32
- Folder structure:
33
- .pmpt/
34
- ├── config.json Config file
35
- ├── docs/ Working folder (MD files)
36
- └── .history/ Version history
34
+ $ pmpt login Authenticate with pmptwiki
35
+ $ pmpt publish Publish project to pmptwiki
36
+ $ pmpt clone <slug> Clone a project from pmptwiki
37
+ $ pmpt browse Browse published projects
37
38
 
38
39
  Documentation: https://pmptwiki.com
39
40
  `);
@@ -104,4 +105,21 @@ program
104
105
  clearAuth();
105
106
  console.log('Logged out successfully');
106
107
  });
108
+ // Platform commands
109
+ program
110
+ .command('login')
111
+ .description('Authenticate with pmptwiki platform')
112
+ .action(cmdLogin);
113
+ program
114
+ .command('publish [path]')
115
+ .description('Publish project to pmptwiki platform')
116
+ .action(cmdPublish);
117
+ program
118
+ .command('clone <slug>')
119
+ .description('Clone a project from pmptwiki platform')
120
+ .action(cmdClone);
121
+ program
122
+ .command('browse')
123
+ .description('Browse and search published projects')
124
+ .action(cmdBrowse);
107
125
  program.parse();
@@ -0,0 +1,46 @@
1
+ /**
2
+ * pmptwiki API client
3
+ */
4
+ const API_BASE = 'https://pmptwiki-api.sin2da.workers.dev';
5
+ const R2_PUBLIC_URL = 'https://pub-ce73b2410943490d80b60ddad9243d31.r2.dev';
6
+ export async function registerAuth(githubToken) {
7
+ const res = await fetch(`${API_BASE}/auth/register`, {
8
+ method: 'POST',
9
+ headers: { 'Content-Type': 'application/json' },
10
+ body: JSON.stringify({ githubToken }),
11
+ });
12
+ if (!res.ok) {
13
+ const err = await res.json().catch(() => ({ error: 'Auth failed' }));
14
+ throw new Error(err.error);
15
+ }
16
+ return res.json();
17
+ }
18
+ export async function publishProject(token, data) {
19
+ const res = await fetch(`${API_BASE}/publish`, {
20
+ method: 'POST',
21
+ headers: {
22
+ Authorization: `Bearer ${token}`,
23
+ 'Content-Type': 'application/json',
24
+ },
25
+ body: JSON.stringify(data),
26
+ });
27
+ if (!res.ok) {
28
+ const err = await res.json().catch(() => ({ error: 'Publish failed' }));
29
+ throw new Error(err.error);
30
+ }
31
+ return res.json();
32
+ }
33
+ export async function fetchProjects() {
34
+ const res = await fetch(`${API_BASE}/projects`);
35
+ if (!res.ok) {
36
+ throw new Error('Failed to fetch projects');
37
+ }
38
+ return res.json();
39
+ }
40
+ export async function fetchPmptFile(slug) {
41
+ const res = await fetch(`${R2_PUBLIC_URL}/projects/${slug}.pmpt`);
42
+ if (!res.ok) {
43
+ throw new Error(`Project not found: ${slug}`);
44
+ }
45
+ return res.text();
46
+ }
package/dist/lib/auth.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
2
2
  import { homedir } from 'os';
3
3
  import { join } from 'path';
4
- const CONFIG_DIR = join(homedir(), '.config', 'promptwiki');
4
+ const CONFIG_DIR = join(homedir(), '.config', 'pmptwiki');
5
5
  const TOKEN_FILE = join(CONFIG_DIR, 'auth.json');
6
6
  export function loadAuth() {
7
7
  try {
@@ -1,6 +1,6 @@
1
1
  import { Octokit } from '@octokit/rest';
2
2
  import { readFileSync } from 'fs';
3
- const CONTENT_OWNER = 'promptwiki';
3
+ const CONTENT_OWNER = 'pmptwiki';
4
4
  const CONTENT_REPO = 'content';
5
5
  export function createClient(token) {
6
6
  return new Octokit({ auth: token });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmpt-cli",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Record and share your AI-driven product development journey",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  "homepage": "https://pmptwiki.com",
22
22
  "repository": {
23
23
  "type": "git",
24
- "url": "https://github.com/promptwiki/cli.git"
24
+ "url": "https://github.com/pmptwiki/pmpt-cli.git"
25
25
  },
26
26
  "license": "MIT",
27
27
  "engines": {