@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.
Files changed (44) hide show
  1. package/CHANGELOG.md +3657 -3649
  2. package/CLAUDE.md +1 -0
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/http-api.md +1 -1
  5. package/dist/web/assets/{ai-resource-editor-BywFZ8kq.js → ai-resource-editor-DxM8JFQ0.js} +1 -1
  6. package/dist/web/assets/{audio-preview-0l5TJLgd.js → audio-preview-DE1sEUmx.js} +1 -1
  7. package/dist/web/assets/{chat-tab-DhE2XceO.js → chat-tab-BKZAcGU7.js} +3 -3
  8. package/dist/web/assets/{code-editor-BfuUP1De.js → code-editor-DjconF_F.js} +2 -2
  9. package/dist/web/assets/{conflict-editor-B47yD0OS.js → conflict-editor-CLfc44RC.js} +1 -1
  10. package/dist/web/assets/{csv-preview-C7xm3SBD.js → csv-preview-DxD081uz.js} +1 -1
  11. package/dist/web/assets/{database-viewer-C7IEdTpP.js → database-viewer-Du1HOmex.js} +1 -1
  12. package/dist/web/assets/{diff-viewer-DRAU_SI7.js → diff-viewer-CeznVBNn.js} +1 -1
  13. package/dist/web/assets/{docx-preview-BK7hQo6W.js → docx-preview-DYbOm4bo.js} +1 -1
  14. package/dist/web/assets/{extension-webview-JK1Z7erV.js → extension-webview-BYBwAaZv.js} +1 -1
  15. package/dist/web/assets/{git-log-panel-D9Obmd3x.js → git-log-panel-CgUkbt6i.js} +1 -1
  16. package/dist/web/assets/{glide-data-grid-cWxDaC9T.js → glide-data-grid-CBLhaW5g.js} +1 -1
  17. package/dist/web/assets/{group-chat-tab-8lgr5lig.js → group-chat-tab-5EKPaL_r.js} +1 -1
  18. package/dist/web/assets/{image-preview-BDJ5dspF.js → image-preview-DjqmBn2G.js} +1 -1
  19. package/dist/web/assets/{index-BN3MQIrY.js → index-nsuPdCaW.js} +3 -3
  20. package/dist/web/assets/{keybindings-store-BohTqglo.js → keybindings-store-DTJlq-2C.js} +1 -1
  21. package/dist/web/assets/{markdown-renderer-DgfXiX1E.js → markdown-renderer-BqthPP7U.js} +1 -1
  22. package/dist/web/assets/{markdown-renderer-CzfDFcb9.js → markdown-renderer-CrZWFsD-.js} +1 -1
  23. package/dist/web/assets/{notification-store-D7YxkIs7.js → notification-store-ClZEc-lT.js} +1 -1
  24. package/dist/web/assets/{pdf-preview-Bg5Df9YY.js → pdf-preview-BZY4PbMF.js} +1 -1
  25. package/dist/web/assets/{postgres-viewer-C0ibwn7l.js → postgres-viewer-DzpIkZtk.js} +1 -1
  26. package/dist/web/assets/{settings-tab-B0SUPLm8.js → settings-tab-o3KSdtxR.js} +1 -1
  27. package/dist/web/assets/{sql-query-editor-B14enbgV.js → sql-query-editor-COXQHj5D.js} +1 -1
  28. package/dist/web/assets/{sqlite-viewer-DL3s7Xcm.js → sqlite-viewer-Dew5JH18.js} +1 -1
  29. package/dist/web/assets/{system-monitor-tab-Bw3Cy1dg.js → system-monitor-tab-CwGbHdi_.js} +1 -1
  30. package/dist/web/assets/{terminal-tab-Bz8t4LSC.js → terminal-tab-DqdmayEL.js} +1 -1
  31. package/dist/web/assets/{tool-cards-DOrdvoz5.js → tool-cards-CNuiAf2X.js} +2 -2
  32. package/dist/web/assets/{use-monaco-theme-I6u-7ZEM.js → use-monaco-theme-uvD7nRfD.js} +1 -1
  33. package/dist/web/assets/{video-preview-CI_4NIE7.js → video-preview-BDpCb2Pd.js} +1 -1
  34. package/dist/web/index.html +1 -1
  35. package/dist/web/sw.js +1 -1
  36. package/package.json +106 -106
  37. package/src/server/ws/chat.ts +27 -0
  38. package/src/services/slash-discovery/discover-skill-roots.ts +22 -3
  39. package/src/services/slash-discovery/index.ts +1 -0
  40. package/src/services/slash-discovery/rewrite-slash-alias.ts +35 -0
  41. package/src/services/slash-discovery/skill-loader.ts +40 -5
  42. package/src/services/slash-discovery/types.ts +6 -0
  43. package/src/types/api.ts +1 -0
  44. 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 name = rel.replace(/\.md$/, "").split(sep).join("/");
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: str(meta.name) ?? 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) ?? pathName;
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) ?? pathName;
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 name = str(meta.name) ?? rel.replace(/\.md$/, "").split(sep).join("/");
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);