@hienlh/ppm 0.12.12 → 0.13.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/CHANGELOG.md +10 -1
- package/README.md +11 -0
- package/assets/skills/ppm/SKILL.md +74 -0
- package/assets/skills/ppm/references/cli-reference.md +728 -0
- package/assets/skills/ppm/references/common-tasks.md +139 -0
- package/assets/skills/ppm/references/http-api.md +204 -0
- package/dist/web/assets/ai-settings-section-QE6nBNgN.js +1 -0
- package/dist/web/assets/{api-settings-C3T95dWg.js → api-settings-DAk7D-NP.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-DvZbltvY.js +1 -0
- package/dist/web/assets/{audio-preview-BkbgGtDH.js → audio-preview-J5neETTY.js} +1 -1
- package/dist/web/assets/chat-tab-sVHRa1Fz.js +12 -0
- package/dist/web/assets/{code-editor-BtspASkW.js → code-editor-tMfcFaQ5.js} +4 -4
- package/dist/web/assets/{conflict-editor-Dgsu6fmj.js → conflict-editor-FydCxWTC.js} +1 -1
- package/dist/web/assets/{csv-preview-DcWCjQkZ.js → csv-preview-HMSavgBb.js} +1 -1
- package/dist/web/assets/{database-viewer-C85RxdMV.js → database-viewer-Celi1puH.js} +2 -2
- package/dist/web/assets/{diff-viewer-2pPy97Tl.js → diff-viewer-NgDJLTk9.js} +1 -1
- package/dist/web/assets/{esm-_CLpyLJ_.js → esm-K1XIK4vc.js} +1 -1
- package/dist/web/assets/{extension-store-BZDZ9QRc.js → extension-store-3yZYn07W.js} +1 -1
- package/dist/web/assets/{extension-webview-U1lMYZ0p.js → extension-webview-xWAdCj3q.js} +1 -1
- package/dist/web/assets/{file-store-4BpOJthN.js → file-store-BrbCNyLm.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BxhdxFgj.js +1 -0
- package/dist/web/assets/{image-preview-BcT1SbY2.js → image-preview-C6bFkdZD.js} +1 -1
- package/dist/web/assets/index-BMhiElt6.css +2 -0
- package/dist/web/assets/index-DtbAoxyy.js +23 -0
- package/dist/web/assets/info-3K5VOQVL-BwAZ2zd8.js +1 -0
- package/dist/web/assets/{input-2eDVjcRZ.js → input-Dk49gO8E.js} +1 -1
- package/dist/web/assets/{keybindings-store-BOG1yviy.js → keybindings-store-B-zET-0o.js} +1 -1
- package/dist/web/assets/keybindings-store-DaBV6qhz.js +1 -0
- package/dist/web/assets/{markdown-renderer-Dbam_-04.js → markdown-renderer-BAnnk1pI.js} +3 -3
- package/dist/web/assets/packet-RMMSAZCW-tx2n5Qry.js +1 -0
- package/dist/web/assets/{pdf-preview-BmHVGx32.js → pdf-preview-BNuFTSOL.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-D6S2MqVT.js +1 -0
- package/dist/web/assets/plus-51UQ45rf.js +1 -0
- package/dist/web/assets/{port-forwarding-tab-Dkq1upWC.js → port-forwarding-tab-BbDlGxAs.js} +1 -1
- package/dist/web/assets/{postgres-viewer-BgBJAJ9q.js → postgres-viewer-Cman1YRO.js} +3 -3
- package/dist/web/assets/radar-KQ55EAFF-BviZcL-b.js +1 -0
- package/dist/web/assets/{scroll-area-CdxNNnN-.js → scroll-area-BEllam7_.js} +1 -1
- package/dist/web/assets/{settings-store-CMAssqyb.js → settings-store-BLLR7ed8.js} +2 -2
- package/dist/web/assets/settings-tab-n5X_Dbu4.js +1 -0
- package/dist/web/assets/{sql-query-editor-b7zJ8XPp.js → sql-query-editor-CVAnRFbi.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-4lLAz1es.js → sqlite-viewer-D6JT11uu.js} +1 -1
- package/dist/web/assets/{tab-store-DNBsLdPn.js → tab-store-B3M9hjho.js} +1 -1
- package/dist/web/assets/{terminal-tab-BtnqkN1H.js → terminal-tab-B4kMthYo.js} +1 -1
- package/dist/web/assets/treemap-KZPCXAKY-CM54VdaB.js +1 -0
- package/dist/web/assets/{use-blob-url-QX-XajU8.js → use-blob-url-e9uTXjv5.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-D68oX3XU.js → use-monaco-theme-BkZDwoVd.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-sQS4C_iL.js → vendor-mermaid-Dx86tuVP.js} +2 -2
- package/dist/web/assets/{video-preview-CkOKvVLt.js → video-preview-BftQOOzF.js} +1 -1
- package/dist/web/index.html +18 -18
- package/dist/web/sw.js +1 -1
- package/docs/project-changelog.md +15 -1
- package/package.json +3 -3
- package/scripts/generate-ppm-skill.ts +23 -0
- package/scripts/lib/generate-cli-reference.ts +81 -0
- package/scripts/lib/generate-common-tasks.ts +14 -0
- package/scripts/lib/generate-http-api.ts +145 -0
- package/scripts/lib/generate-skill-md.ts +28 -0
- package/scripts/lib/write-output.ts +17 -0
- package/src/cli/commands/export-cmd.ts +85 -0
- package/src/index.ts +167 -153
- package/src/services/skill-export/backup-existing.ts +33 -0
- package/src/services/skill-export/copy-bundled-skill.ts +36 -0
- package/src/services/skill-export/generate-db-schema.ts +66 -0
- package/src/services/skill-export/index.ts +6 -0
- package/src/services/skill-export/resolve-assets-dir.ts +31 -0
- package/src/services/skill-export/resolve-target-dir.ts +17 -0
- package/src/web/components/chat/chat-tab.tsx +6 -1
- package/src/web/components/chat/message-list.tsx +96 -43
- package/src/web/hooks/use-chat.ts +37 -1
- package/src/web/lib/flatten-expansions.ts +36 -0
- package/templates/skill/SKILL.md.tmpl +74 -0
- package/templates/skill/common-tasks.md +139 -0
- package/assets/skills/ppm-guide/SKILL.md +0 -61
- package/dist/web/assets/ai-settings-section-NNWp6nw7.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-DDuzYaUV.js +0 -1
- package/dist/web/assets/chat-tab-BZlP1qjX.js +0 -12
- package/dist/web/assets/chevron-up-BWBvMZkp.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BURAevTc.js +0 -1
- package/dist/web/assets/index-BWSRKVZn.js +0 -23
- package/dist/web/assets/index-b6tIZImC.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-tSD4Fpi3.js +0 -1
- package/dist/web/assets/keybindings-store-BvdUoEC7.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-DmDLZUrV.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-w03Pc9ZR.js +0 -1
- package/dist/web/assets/pre-compact-button-Dp7Hs49L.js +0 -1
- package/dist/web/assets/pre-compact-section-DnM5fGSR.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-C9XQvoey.js +0 -1
- package/dist/web/assets/settings-tab-zYWKTq5z.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-lmftxSky.js +0 -1
- package/scripts/generate-ppm-guide.ts +0 -92
- package/src/web/components/chat/pre-compact-section.tsx +0 -69
- /package/dist/web/assets/{api-client-DIhJ5qVW.js → api-client-Dvzcc_EO.js} +0 -0
- /package/dist/web/assets/{csv-parser-B5QW8pZ6.js → csv-parser--2WJNgS7.js} +0 -0
- /package/dist/web/assets/{dist-GtkSekuX.js → dist-im4ynINo.js} +0 -0
- /package/dist/web/assets/{katex-C3cZrCvP.js → katex-CKoArbIw.js} +0 -0
- /package/dist/web/assets/{lib-Bu71-TFS.js → lib-DQHnkzGy.js} +0 -0
- /package/dist/web/assets/{react-DMIOAtcX.js → react-GqWghJ-L.js} +0 -0
- /package/dist/web/assets/{refresh-cw-BjrAbUJe.js → refresh-cw-LlbZDJpO.js} +0 -0
- /package/dist/web/assets/{sql-completion-provider-CULTsCqR.js → sql-completion-provider-C3cq9j99.js} +0 -0
- /package/dist/web/assets/{table-tf7pRkME.js → table-Dq575bPF.js} +0 -0
- /package/dist/web/assets/{text-wrap-BV-R4Vvy.js → text-wrap-Cn6BNQfq.js} +0 -0
- /package/dist/web/assets/{trash-2-DjQOpgUV.js → trash-2-CJYoLw7Q.js} +0 -0
- /package/dist/web/assets/{utils-CQux7CsO.js → utils-CTg5uAYR.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-K3_Xwigj.js → vendor-xterm-CU2c3f0A.js} +0 -0
package/src/index.ts
CHANGED
|
@@ -3,156 +3,170 @@
|
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { VERSION } from "./version.ts";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
program
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
program
|
|
6
|
+
/**
|
|
7
|
+
* Assemble the CLI program without parsing argv. Exported so build-time tools
|
|
8
|
+
* (e.g. scripts/generate-ppm-skill.ts) can introspect the Commander tree for
|
|
9
|
+
* auto-generated documentation. `preAction` hooks and action callbacks are
|
|
10
|
+
* registered but not invoked until `.parseAsync()` runs.
|
|
11
|
+
*/
|
|
12
|
+
export async function buildProgram(): Promise<Command> {
|
|
13
|
+
const program = new Command();
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.name("ppm")
|
|
17
|
+
.description("Personal Project Manager — mobile-first web IDE")
|
|
18
|
+
.version(VERSION)
|
|
19
|
+
.hook("preAction", () => {
|
|
20
|
+
console.log(` PPM v${VERSION}\n`);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.command("start")
|
|
25
|
+
.description("Start the PPM server (background by default)")
|
|
26
|
+
.option("-p, --port <port>", "Port to listen on")
|
|
27
|
+
.option("-s, --share", "(deprecated) Tunnel is now always enabled")
|
|
28
|
+
.option("--profile <name>", "DB profile name (e.g. 'dev' → ppm.dev.db)")
|
|
29
|
+
.action(async (options) => {
|
|
30
|
+
const { setDbProfile } = await import("./services/db.service.ts");
|
|
31
|
+
if (options.profile) {
|
|
32
|
+
setDbProfile(options.profile);
|
|
33
|
+
}
|
|
34
|
+
const { hasConfig, initProject } = await import("./cli/commands/init.ts");
|
|
35
|
+
if (!hasConfig()) {
|
|
36
|
+
await initProject();
|
|
37
|
+
}
|
|
38
|
+
const { startServer } = await import("./server/index.ts");
|
|
39
|
+
await startServer(options);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.command("stop")
|
|
44
|
+
.description("Stop the PPM server (supervisor stays alive)")
|
|
45
|
+
.option("-a, --all", "Kill all PPM and cloudflared processes (including untracked)")
|
|
46
|
+
.option("--kill", "Full shutdown (kills supervisor too)")
|
|
47
|
+
.action(async (options) => {
|
|
48
|
+
const { stopServer } = await import("./cli/commands/stop.ts");
|
|
49
|
+
await stopServer(options);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
program
|
|
53
|
+
.command("down")
|
|
54
|
+
.description("Fully shut down PPM (supervisor + server + tunnel)")
|
|
55
|
+
.action(async () => {
|
|
56
|
+
const { stopServer } = await import("./cli/commands/stop.ts");
|
|
57
|
+
await stopServer({ kill: true });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
program
|
|
61
|
+
.command("restart")
|
|
62
|
+
.description("Restart the server (keeps tunnel alive)")
|
|
63
|
+
.option("--force", "Force resume from paused state")
|
|
64
|
+
.action(async (options) => {
|
|
65
|
+
const { restartServer } = await import("./cli/commands/restart.ts");
|
|
66
|
+
await restartServer(options);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
program
|
|
70
|
+
.command("status")
|
|
71
|
+
.description("Show PPM daemon status")
|
|
72
|
+
.option("-a, --all", "Show all PPM and cloudflared processes (including untracked)")
|
|
73
|
+
.option("--json", "Output as JSON")
|
|
74
|
+
.action(async (options) => {
|
|
75
|
+
const { showStatus } = await import("./cli/commands/status.ts");
|
|
76
|
+
await showStatus(options);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
program
|
|
80
|
+
.command("open")
|
|
81
|
+
.description("Open PPM in browser")
|
|
82
|
+
.action(async () => {
|
|
83
|
+
const { openBrowser } = await import("./cli/commands/open.ts");
|
|
84
|
+
await openBrowser();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
program
|
|
88
|
+
.command("logs")
|
|
89
|
+
.description("View PPM daemon logs")
|
|
90
|
+
.option("-n, --tail <lines>", "Number of lines to show", "50")
|
|
91
|
+
.option("-f, --follow", "Follow log output")
|
|
92
|
+
.option("--clear", "Clear log file")
|
|
93
|
+
.action(async (options) => {
|
|
94
|
+
const { showLogs } = await import("./cli/commands/logs.ts");
|
|
95
|
+
await showLogs(options);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
program
|
|
99
|
+
.command("report")
|
|
100
|
+
.description("Report a bug on GitHub (pre-fills env info + logs)")
|
|
101
|
+
.action(async () => {
|
|
102
|
+
const { reportBug } = await import("./cli/commands/report.ts");
|
|
103
|
+
await reportBug();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
program
|
|
107
|
+
.command("init")
|
|
108
|
+
.description("Initialize PPM configuration (interactive or via flags)")
|
|
109
|
+
.option("-p, --port <port>", "Port to listen on")
|
|
110
|
+
.option("--scan <path>", "Directory to scan for git repos")
|
|
111
|
+
.option("--auth", "Enable authentication")
|
|
112
|
+
.option("--no-auth", "Disable authentication")
|
|
113
|
+
.option("--password <pw>", "Set access password")
|
|
114
|
+
.option("--share", "Pre-install cloudflared for sharing")
|
|
115
|
+
.option("-y, --yes", "Non-interactive mode (use defaults + flags)")
|
|
116
|
+
.action(async (options) => {
|
|
117
|
+
const { initProject } = await import("./cli/commands/init.ts");
|
|
118
|
+
await initProject(options);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const { registerProjectsCommands } = await import("./cli/commands/projects.ts");
|
|
122
|
+
registerProjectsCommands(program);
|
|
123
|
+
|
|
124
|
+
const { registerConfigCommands } = await import("./cli/commands/config-cmd.ts");
|
|
125
|
+
registerConfigCommands(program);
|
|
126
|
+
|
|
127
|
+
const { registerGitCommands } = await import("./cli/commands/git-cmd.ts");
|
|
128
|
+
registerGitCommands(program);
|
|
129
|
+
|
|
130
|
+
const { registerChatCommands } = await import("./cli/commands/chat-cmd.ts");
|
|
131
|
+
registerChatCommands(program);
|
|
132
|
+
|
|
133
|
+
program
|
|
134
|
+
.command("upgrade")
|
|
135
|
+
.description("Check for and install PPM updates")
|
|
136
|
+
.option("--check", "Only check for updates, don't install")
|
|
137
|
+
.action(async (options) => {
|
|
138
|
+
const { upgradeCmd } = await import("./cli/commands/upgrade.ts");
|
|
139
|
+
await upgradeCmd(options);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const { registerAutoStartCommands } = await import("./cli/commands/autostart.ts");
|
|
143
|
+
registerAutoStartCommands(program);
|
|
144
|
+
|
|
145
|
+
const { registerCloudCommands } = await import("./cli/commands/cloud.ts");
|
|
146
|
+
registerCloudCommands(program);
|
|
147
|
+
|
|
148
|
+
const { registerSkillsCommands } = await import("./cli/commands/skills-cmd.ts");
|
|
149
|
+
registerSkillsCommands(program);
|
|
150
|
+
|
|
151
|
+
const { registerExtCommands } = await import("./cli/commands/ext-cmd.ts");
|
|
152
|
+
registerExtCommands(program);
|
|
153
|
+
|
|
154
|
+
const { registerDbCommands } = await import("./cli/commands/db-cmd.ts");
|
|
155
|
+
registerDbCommands(program);
|
|
156
|
+
|
|
157
|
+
const { registerBotCommands } = await import("./cli/commands/bot-cmd.ts");
|
|
158
|
+
registerBotCommands(program);
|
|
159
|
+
|
|
160
|
+
const { registerJiraCommands } = await import("./cli/commands/jira-cmd.ts");
|
|
161
|
+
await registerJiraCommands(program);
|
|
162
|
+
|
|
163
|
+
const { registerExportCommands } = await import("./cli/commands/export-cmd.ts");
|
|
164
|
+
registerExportCommands(program);
|
|
165
|
+
|
|
166
|
+
return program;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (import.meta.main) {
|
|
170
|
+
const program = await buildProgram();
|
|
171
|
+
program.parse();
|
|
172
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Rename existing skill files to `.bak-<timestamp>` before overwriting.
|
|
2
|
+
// Preserves earlier backups by embedding a UTC timestamp in the suffix.
|
|
3
|
+
import { existsSync, renameSync, readdirSync, statSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
/** Produce a compact UTC timestamp like `202604211733` (YYYYMMDDHHmm). */
|
|
7
|
+
export function makeTimestamp(d: Date = new Date()): string {
|
|
8
|
+
return d.toISOString().replace(/[-:T]/g, "").slice(0, 12);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function backupExisting(targetDir: string, ts: string = makeTimestamp()): string[] {
|
|
12
|
+
if (!existsSync(targetDir)) return [];
|
|
13
|
+
const backedUp: string[] = [];
|
|
14
|
+
walkAndBackup(targetDir, ts, backedUp);
|
|
15
|
+
return backedUp;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function walkAndBackup(dir: string, ts: string, collected: string[]): void {
|
|
19
|
+
const entries = readdirSync(dir);
|
|
20
|
+
for (const name of entries) {
|
|
21
|
+
// Skip already-backed-up files to avoid `.md.bak-X.bak-Y` chains.
|
|
22
|
+
if (name.includes(".bak-")) continue;
|
|
23
|
+
const abs = resolve(dir, name);
|
|
24
|
+
const st = statSync(abs);
|
|
25
|
+
if (st.isDirectory()) {
|
|
26
|
+
walkAndBackup(abs, ts, collected);
|
|
27
|
+
} else if (st.isFile() && name.endsWith(".md")) {
|
|
28
|
+
const dest = `${abs}.bak-${ts}`;
|
|
29
|
+
renameSync(abs, dest);
|
|
30
|
+
collected.push(dest);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Recursive copy of the bundled skill package to a target directory.
|
|
2
|
+
// Skips any stale `.bak-*` files in source (defensive, should not occur in bundle).
|
|
3
|
+
import { cpSync, mkdirSync, existsSync, readdirSync, statSync, copyFileSync } from "node:fs";
|
|
4
|
+
import { resolve, relative } from "node:path";
|
|
5
|
+
|
|
6
|
+
export function copyBundledSkill(sourceDir: string, targetDir: string): string[] {
|
|
7
|
+
if (!existsSync(sourceDir)) {
|
|
8
|
+
throw new Error(`Source skill dir not found: ${sourceDir}`);
|
|
9
|
+
}
|
|
10
|
+
mkdirSync(targetDir, { recursive: true });
|
|
11
|
+
const copied: string[] = [];
|
|
12
|
+
walk(sourceDir, sourceDir, targetDir, copied);
|
|
13
|
+
return copied;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function walk(rootSrc: string, dir: string, targetRoot: string, collected: string[]): void {
|
|
17
|
+
for (const name of readdirSync(dir)) {
|
|
18
|
+
if (name.includes(".bak-")) continue;
|
|
19
|
+
const abs = resolve(dir, name);
|
|
20
|
+
const rel = relative(rootSrc, abs);
|
|
21
|
+
const dest = resolve(targetRoot, rel);
|
|
22
|
+
const st = statSync(abs);
|
|
23
|
+
if (st.isDirectory()) {
|
|
24
|
+
mkdirSync(dest, { recursive: true });
|
|
25
|
+
walk(rootSrc, abs, targetRoot, collected);
|
|
26
|
+
} else if (st.isFile()) {
|
|
27
|
+
copyFileSync(abs, dest);
|
|
28
|
+
collected.push(dest);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fallback single-shot copy using Node 16.7+ cpSync (kept in case walk is bypassed).
|
|
34
|
+
export function copyTree(src: string, dest: string): void {
|
|
35
|
+
cpSync(src, dest, { recursive: true });
|
|
36
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Runtime: read the user's PPM SQLite config DB (readonly) and render a markdown schema doc.
|
|
2
|
+
// Never opens the DB read-write. Gracefully handles missing DB.
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { Database } from "bun:sqlite";
|
|
6
|
+
import { getPpmDir } from "../ppm-dir.ts";
|
|
7
|
+
|
|
8
|
+
interface ColumnInfo {
|
|
9
|
+
cid: number;
|
|
10
|
+
name: string;
|
|
11
|
+
type: string;
|
|
12
|
+
notnull: number;
|
|
13
|
+
dflt_value: string | null;
|
|
14
|
+
pk: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface TableRow {
|
|
18
|
+
name: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function generateDbSchemaMarkdown(dbPath?: string): string {
|
|
22
|
+
const path = dbPath ?? resolve(getPpmDir(), "ppm.db");
|
|
23
|
+
const header = "# PPM Database Schema\n\n_Auto-generated at install time from your local config DB._\n";
|
|
24
|
+
|
|
25
|
+
if (!existsSync(path)) {
|
|
26
|
+
return `${header}\n_Database not found at \`${path}\`. Run \`ppm init\` to create it._\n`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let db: Database | null = null;
|
|
30
|
+
try {
|
|
31
|
+
db = new Database(path, { readonly: true });
|
|
32
|
+
const tables = db
|
|
33
|
+
.query<TableRow, []>("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name")
|
|
34
|
+
.all();
|
|
35
|
+
|
|
36
|
+
if (tables.length === 0) {
|
|
37
|
+
return `${header}\n_No tables found in \`${path}\`._\n`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const parts: string[] = [header, ""];
|
|
41
|
+
parts.push(`Source: \`${path}\``);
|
|
42
|
+
parts.push("");
|
|
43
|
+
|
|
44
|
+
for (const t of tables) {
|
|
45
|
+
parts.push(`## ${t.name}`);
|
|
46
|
+
parts.push("");
|
|
47
|
+
const cols = db.query<ColumnInfo, []>(`PRAGMA table_info("${t.name}")`).all();
|
|
48
|
+
parts.push("| Column | Type | Nullable | PK | Default |");
|
|
49
|
+
parts.push("|---|---|---|---|---|");
|
|
50
|
+
for (const c of cols) {
|
|
51
|
+
const nullable = c.notnull === 0 ? "yes" : "no";
|
|
52
|
+
const pk = c.pk > 0 ? "yes" : "";
|
|
53
|
+
const def = c.dflt_value !== null ? `\`${c.dflt_value}\`` : "";
|
|
54
|
+
parts.push(`| \`${c.name}\` | \`${c.type || "—"}\` | ${nullable} | ${pk} | ${def} |`);
|
|
55
|
+
}
|
|
56
|
+
parts.push("");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return parts.join("\n") + "\n";
|
|
60
|
+
} catch (e) {
|
|
61
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
62
|
+
return `${header}\n_Failed to read database at \`${path}\`: ${msg}_\n`;
|
|
63
|
+
} finally {
|
|
64
|
+
db?.close();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Barrel re-exports for the skill-export service.
|
|
2
|
+
export { resolveTargetDir, type SkillScope, type ResolveTargetOpts } from "./resolve-target-dir.ts";
|
|
3
|
+
export { resolveAssetsDir } from "./resolve-assets-dir.ts";
|
|
4
|
+
export { backupExisting, makeTimestamp } from "./backup-existing.ts";
|
|
5
|
+
export { copyBundledSkill } from "./copy-bundled-skill.ts";
|
|
6
|
+
export { generateDbSchemaMarkdown } from "./generate-db-schema.ts";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Resolve the bundled skill assets dir. Works both in dev (`bun src/index.ts`)
|
|
2
|
+
// and installed npm package. Throws a clear error if assets are missing.
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { resolve, dirname } from "node:path";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
|
|
7
|
+
const ASSETS_REL = "assets/skills/ppm";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Walks up from this file to find the repo/package root that contains
|
|
11
|
+
* `assets/skills/ppm/SKILL.md`. Covers both:
|
|
12
|
+
* - dev: src/services/skill-export/*.ts → repo root is ../../..
|
|
13
|
+
* - bundled: compiled binary walks up similarly when `bun build --compile` preserves structure
|
|
14
|
+
*/
|
|
15
|
+
export function resolveAssetsDir(): string {
|
|
16
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const candidates = [
|
|
18
|
+
resolve(here, "../../..", ASSETS_REL),
|
|
19
|
+
resolve(here, "../..", ASSETS_REL),
|
|
20
|
+
resolve(here, "..", ASSETS_REL),
|
|
21
|
+
resolve(process.cwd(), ASSETS_REL),
|
|
22
|
+
];
|
|
23
|
+
for (const c of candidates) {
|
|
24
|
+
if (existsSync(resolve(c, "SKILL.md"))) return c;
|
|
25
|
+
}
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Bundled PPM skill assets not found. Searched:\n ${candidates.join(
|
|
28
|
+
"\n ",
|
|
29
|
+
)}\nRun \`bun run generate:skill\` (dev) or reinstall PPM.`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Resolve the install target directory based on scope/output flags.
|
|
2
|
+
// Precedence: --output > --scope project > --scope user (default).
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
|
|
6
|
+
export type SkillScope = "user" | "project";
|
|
7
|
+
|
|
8
|
+
export interface ResolveTargetOpts {
|
|
9
|
+
scope?: SkillScope;
|
|
10
|
+
output?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function resolveTargetDir(opts: ResolveTargetOpts): string {
|
|
14
|
+
if (opts.output) return resolve(opts.output);
|
|
15
|
+
if (opts.scope === "project") return resolve(process.cwd(), ".claude/skills/ppm");
|
|
16
|
+
return resolve(homedir(), ".claude/skills/ppm");
|
|
17
|
+
}
|
|
@@ -88,6 +88,9 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
|
|
|
88
88
|
|
|
89
89
|
const {
|
|
90
90
|
messages,
|
|
91
|
+
renderedMessages,
|
|
92
|
+
expandCompact,
|
|
93
|
+
isCompactExpanded,
|
|
91
94
|
messagesLoading,
|
|
92
95
|
isStreaming,
|
|
93
96
|
phase,
|
|
@@ -384,7 +387,9 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
|
|
|
384
387
|
|
|
385
388
|
{/* Messages */}
|
|
386
389
|
<MessageList
|
|
387
|
-
messages={
|
|
390
|
+
messages={renderedMessages}
|
|
391
|
+
onExpandCompact={expandCompact}
|
|
392
|
+
isCompactExpanded={isCompactExpanded}
|
|
388
393
|
messagesLoading={messagesLoading}
|
|
389
394
|
pendingApproval={pendingApproval}
|
|
390
395
|
onApprovalResponse={respondToApproval}
|