@hienlh/ppm 0.17.47 → 0.17.48
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 +3657 -3649
- package/CLAUDE.md +1 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/dist/web/assets/{ai-resource-editor-BywFZ8kq.js → ai-resource-editor-DxM8JFQ0.js} +1 -1
- package/dist/web/assets/{audio-preview-0l5TJLgd.js → audio-preview-DE1sEUmx.js} +1 -1
- package/dist/web/assets/{chat-tab-DhE2XceO.js → chat-tab-BKZAcGU7.js} +3 -3
- package/dist/web/assets/{code-editor-BfuUP1De.js → code-editor-DjconF_F.js} +2 -2
- package/dist/web/assets/{conflict-editor-B47yD0OS.js → conflict-editor-CLfc44RC.js} +1 -1
- package/dist/web/assets/{csv-preview-C7xm3SBD.js → csv-preview-DxD081uz.js} +1 -1
- package/dist/web/assets/{database-viewer-C7IEdTpP.js → database-viewer-Du1HOmex.js} +1 -1
- package/dist/web/assets/{diff-viewer-DRAU_SI7.js → diff-viewer-CeznVBNn.js} +1 -1
- package/dist/web/assets/{docx-preview-BK7hQo6W.js → docx-preview-DYbOm4bo.js} +1 -1
- package/dist/web/assets/{extension-webview-JK1Z7erV.js → extension-webview-BYBwAaZv.js} +1 -1
- package/dist/web/assets/{git-log-panel-D9Obmd3x.js → git-log-panel-CgUkbt6i.js} +1 -1
- package/dist/web/assets/{glide-data-grid-cWxDaC9T.js → glide-data-grid-CBLhaW5g.js} +1 -1
- package/dist/web/assets/{group-chat-tab-8lgr5lig.js → group-chat-tab-5EKPaL_r.js} +1 -1
- package/dist/web/assets/{image-preview-BDJ5dspF.js → image-preview-DjqmBn2G.js} +1 -1
- package/dist/web/assets/{index-BN3MQIrY.js → index-nsuPdCaW.js} +3 -3
- package/dist/web/assets/{keybindings-store-BohTqglo.js → keybindings-store-DTJlq-2C.js} +1 -1
- package/dist/web/assets/{markdown-renderer-DgfXiX1E.js → markdown-renderer-BqthPP7U.js} +1 -1
- package/dist/web/assets/{markdown-renderer-CzfDFcb9.js → markdown-renderer-CrZWFsD-.js} +1 -1
- package/dist/web/assets/{notification-store-D7YxkIs7.js → notification-store-ClZEc-lT.js} +1 -1
- package/dist/web/assets/{pdf-preview-Bg5Df9YY.js → pdf-preview-BZY4PbMF.js} +1 -1
- package/dist/web/assets/{postgres-viewer-C0ibwn7l.js → postgres-viewer-DzpIkZtk.js} +1 -1
- package/dist/web/assets/{settings-tab-B0SUPLm8.js → settings-tab-o3KSdtxR.js} +1 -1
- package/dist/web/assets/{sql-query-editor-B14enbgV.js → sql-query-editor-COXQHj5D.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-DL3s7Xcm.js → sqlite-viewer-Dew5JH18.js} +1 -1
- package/dist/web/assets/{system-monitor-tab-Bw3Cy1dg.js → system-monitor-tab-CwGbHdi_.js} +1 -1
- package/dist/web/assets/{terminal-tab-Bz8t4LSC.js → terminal-tab-DqdmayEL.js} +1 -1
- package/dist/web/assets/{tool-cards-DOrdvoz5.js → tool-cards-CNuiAf2X.js} +2 -2
- package/dist/web/assets/{use-monaco-theme-I6u-7ZEM.js → use-monaco-theme-uvD7nRfD.js} +1 -1
- package/dist/web/assets/{video-preview-CI_4NIE7.js → video-preview-BDpCb2Pd.js} +1 -1
- package/dist/web/index.html +1 -1
- package/dist/web/sw.js +1 -1
- package/package.json +106 -106
- package/src/server/ws/chat.ts +27 -0
- package/src/services/slash-discovery/discover-skill-roots.ts +22 -3
- package/src/services/slash-discovery/index.ts +1 -0
- package/src/services/slash-discovery/rewrite-slash-alias.ts +35 -0
- package/src/services/slash-discovery/skill-loader.ts +40 -5
- package/src/services/slash-discovery/types.ts +6 -0
- package/src/types/api.ts +1 -0
- package/src/web/hooks/use-chat.ts +20 -0
|
@@ -53,6 +53,34 @@ function walkDir(dir: string, visitor: (filePath: string) => void, visited = new
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Resolve the invocable name for an item found under `root`.
|
|
58
|
+
*
|
|
59
|
+
* Plugin items are namespaced by their owning plugin, but the two kinds differ
|
|
60
|
+
* in what follows the prefix. A skill or command is named after where its file
|
|
61
|
+
* lives, so a kit that self-namespaces instead (AgentKit ships `name: ak:debug`)
|
|
62
|
+
* is not invocable under that declared name — it is kept as an alias so typed
|
|
63
|
+
* input can be rewritten to the canonical one. An agent is named after its
|
|
64
|
+
* frontmatter `name`, which is also how the runtime addresses it for delegation.
|
|
65
|
+
*/
|
|
66
|
+
function qualify(
|
|
67
|
+
root: SkillRoot,
|
|
68
|
+
pathName: string,
|
|
69
|
+
declared: string | undefined,
|
|
70
|
+
nameSource: "path" | "declared" = "path",
|
|
71
|
+
): { name: string; aliases?: string[] } {
|
|
72
|
+
if (!root.pluginName) return { name: declared ?? pathName };
|
|
73
|
+
if (nameSource === "declared") {
|
|
74
|
+
const suffix = declared || pathName;
|
|
75
|
+
return { name: suffix ? `${root.pluginName}:${suffix}` : "" };
|
|
76
|
+
}
|
|
77
|
+
// A SKILL.md sitting at the plugin root has no directory to take a name from
|
|
78
|
+
const suffix = pathName || declared;
|
|
79
|
+
if (!suffix) return { name: "" };
|
|
80
|
+
const name = `${root.pluginName}:${suffix}`;
|
|
81
|
+
return { name, ...(declared && declared !== name && { aliases: [declared] }) };
|
|
82
|
+
}
|
|
83
|
+
|
|
56
84
|
/** Collect commands from a root with origin "commands" */
|
|
57
85
|
function loadCommands(root: SkillRoot): SlashItemWithSource[] {
|
|
58
86
|
const items: SlashItemWithSource[] = [];
|
|
@@ -66,10 +94,13 @@ function loadCommands(root: SkillRoot): SlashItemWithSource[] {
|
|
|
66
94
|
const content = readFileSync(filePath, "utf-8");
|
|
67
95
|
const { meta } = parseFrontmatter(content);
|
|
68
96
|
const rel = relative(root.path, filePath);
|
|
69
|
-
const
|
|
97
|
+
const pathName = rel.replace(/\.md$/, "").split(sep).join("/");
|
|
98
|
+
const { name, aliases } = qualify(root, pathName, str(meta.name));
|
|
99
|
+
if (!name) return;
|
|
70
100
|
items.push({
|
|
71
101
|
type: "command",
|
|
72
|
-
name
|
|
102
|
+
name,
|
|
103
|
+
...(aliases && { aliases }),
|
|
73
104
|
description: str(meta.description) ?? "",
|
|
74
105
|
argumentHint: str(meta["argument-hint"]),
|
|
75
106
|
scope,
|
|
@@ -106,11 +137,12 @@ function loadSkills(root: SkillRoot): SlashItemWithSource[] {
|
|
|
106
137
|
dirsWithSkillMd.add(skillDir);
|
|
107
138
|
const rel = relative(root.path, skillDir);
|
|
108
139
|
const pathName = rel.split(sep).join("/");
|
|
109
|
-
const name = str(meta.name)
|
|
140
|
+
const { name, aliases } = qualify(root, pathName, str(meta.name));
|
|
110
141
|
if (!name) return;
|
|
111
142
|
items.push({
|
|
112
143
|
type: "skill",
|
|
113
144
|
name,
|
|
145
|
+
...(aliases && { aliases }),
|
|
114
146
|
description: str(meta.description) ?? "",
|
|
115
147
|
argumentHint: str(meta["argument-hint"]),
|
|
116
148
|
scope,
|
|
@@ -136,11 +168,12 @@ function loadSkills(root: SkillRoot): SlashItemWithSource[] {
|
|
|
136
168
|
const { meta } = parseFrontmatter(content);
|
|
137
169
|
const rel = relative(root.path, filePath);
|
|
138
170
|
const pathName = rel.replace(/\.md$/, "").split(sep).join("/");
|
|
139
|
-
const name = str(meta.name)
|
|
171
|
+
const { name, aliases } = qualify(root, pathName, str(meta.name));
|
|
140
172
|
if (!name) return;
|
|
141
173
|
items.push({
|
|
142
174
|
type: "skill",
|
|
143
175
|
name,
|
|
176
|
+
...(aliases && { aliases }),
|
|
144
177
|
description: str(meta.description) ?? "",
|
|
145
178
|
argumentHint: str(meta["argument-hint"]),
|
|
146
179
|
scope,
|
|
@@ -168,7 +201,9 @@ function loadAgents(root: SkillRoot): SlashItemWithSource[] {
|
|
|
168
201
|
const content = readFileSync(filePath, "utf-8");
|
|
169
202
|
const { meta } = parseFrontmatter(content);
|
|
170
203
|
const rel = relative(root.path, filePath);
|
|
171
|
-
const
|
|
204
|
+
const pathName = rel.replace(/\.md$/, "").split(sep).join("/");
|
|
205
|
+
const { name } = qualify(root, pathName, str(meta.name), "declared");
|
|
206
|
+
if (!name) return;
|
|
172
207
|
items.push({
|
|
173
208
|
type: "agent",
|
|
174
209
|
name,
|
|
@@ -41,6 +41,12 @@ export interface SkillRoot {
|
|
|
41
41
|
path: string; // Resolved absolute path
|
|
42
42
|
source: DefinitionSource;
|
|
43
43
|
origin: ItemOrigin;
|
|
44
|
+
/**
|
|
45
|
+
* Owning Claude Code plugin, set only for roots under ~/.claude/plugins/.
|
|
46
|
+
* Items below such a root are namespaced `<pluginName>:<path>`, matching how
|
|
47
|
+
* Claude Code itself registers them.
|
|
48
|
+
*/
|
|
49
|
+
pluginName?: string;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
/** Extends SlashItem with source metadata */
|
package/src/types/api.ts
CHANGED
|
@@ -70,6 +70,7 @@ export type ChatWsServerMessage =
|
|
|
70
70
|
| { type: "phase_changed"; phase: SessionPhase; elapsed?: number }
|
|
71
71
|
| { type: "session_state"; sessionId: string; phase: SessionPhase; pendingApproval: { requestId: string; tool: string; input: unknown } | null; sessionTitle: string | null; model?: string; effort?: string; thinking?: boolean }
|
|
72
72
|
| { type: "turn_events"; events: unknown[] }
|
|
73
|
+
| { type: "user_message"; content: string; imageCount?: number; timestamp?: string }
|
|
73
74
|
| { type: "title_updated"; title: string }
|
|
74
75
|
| { type: "compact_status"; status: "compacting" | "done" }
|
|
75
76
|
| { type: "ping" };
|
|
@@ -610,6 +610,26 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
610
610
|
// on the global bus (`use-global-events.ts`) instead of here — a chat socket is
|
|
611
611
|
// not guaranteed to exist since chat tabs mount lazily.
|
|
612
612
|
|
|
613
|
+
// A user message sent from another device/tab of this session — render its
|
|
614
|
+
// bubble. The sender never receives this echo (server excludes the sender),
|
|
615
|
+
// so no dedupe against the optimistic append is needed.
|
|
616
|
+
if ((data as any).type === "user_message") {
|
|
617
|
+
const content = (data as any).content as string;
|
|
618
|
+
setMessages((prev) => {
|
|
619
|
+
const last = prev[prev.length - 1];
|
|
620
|
+
// Dedupe: a concurrent turn_events replay or REST refetch may already
|
|
621
|
+
// have appended this same turn's user message.
|
|
622
|
+
if (last?.role === "user" && last.content === content) return prev;
|
|
623
|
+
return [...prev, {
|
|
624
|
+
id: `user-remote-${Date.now()}`,
|
|
625
|
+
role: "user" as const,
|
|
626
|
+
content,
|
|
627
|
+
timestamp: (data as any).timestamp ?? new Date().toISOString(),
|
|
628
|
+
}];
|
|
629
|
+
});
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
613
633
|
// Handle title updates from SDK summary
|
|
614
634
|
if ((data as any).type === "title_updated") {
|
|
615
635
|
setSessionTitle((data as any).title ?? null);
|