@hienlh/ppm 0.9.80 → 0.9.82

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 (162) hide show
  1. package/.opencode/.env.example +98 -0
  2. package/.opencode/skills/ads-management/scripts/.env.example +13 -0
  3. package/.opencode/skills/ai-multimodal/.env.example +230 -0
  4. package/.opencode/skills/cip-design/.env.example +6 -0
  5. package/.opencode/skills/devops/.env.example +76 -0
  6. package/.opencode/skills/docs-seeker/.env.example +15 -0
  7. package/.opencode/skills/elevenlabs/.env.example +3 -0
  8. package/.opencode/skills/marketing-dashboard/.env.example +15 -0
  9. package/.opencode/skills/marketing-dashboard/app/.env.example +2 -0
  10. package/.opencode/skills/marketing-dashboard/server/.env.example +2 -0
  11. package/.opencode/skills/mcp-management/scripts/dist/analyze-tools.js +70 -0
  12. package/.opencode/skills/mcp-management/scripts/dist/cli.js +160 -0
  13. package/.opencode/skills/mcp-management/scripts/dist/mcp-client.js +183 -0
  14. package/.opencode/skills/payment-integration/scripts/.env.example +20 -0
  15. package/.opencode/skills/sequential-thinking/.env.example +8 -0
  16. package/.repomixignore +22 -0
  17. package/AGENTS.md +62 -0
  18. package/CHANGELOG.md +17 -0
  19. package/CLAUDE.md +12 -0
  20. package/assets/skills/ppm-guide/SKILL.md +61 -0
  21. package/bun.lock +9 -1
  22. package/dist/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  23. package/dist/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  24. package/dist/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  25. package/dist/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  26. package/dist/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  27. package/dist/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  28. package/dist/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  29. package/dist/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  30. package/dist/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  31. package/dist/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  32. package/dist/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  33. package/dist/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  34. package/dist/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  35. package/dist/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  36. package/dist/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  37. package/dist/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  38. package/dist/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  39. package/dist/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  40. package/dist/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  41. package/dist/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  42. package/dist/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  43. package/dist/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  44. package/dist/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  45. package/dist/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  46. package/dist/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  47. package/dist/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  48. package/dist/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  49. package/dist/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  50. package/dist/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  51. package/dist/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  52. package/dist/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  53. package/dist/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  54. package/dist/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  55. package/dist/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  56. package/dist/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  57. package/dist/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  58. package/dist/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  59. package/dist/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  60. package/dist/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  61. package/dist/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  62. package/dist/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  63. package/dist/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  64. package/dist/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  65. package/dist/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  66. package/dist/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  67. package/dist/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  68. package/dist/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  69. package/dist/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  70. package/dist/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  71. package/dist/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  72. package/dist/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  73. package/dist/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  74. package/dist/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  75. package/dist/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  76. package/dist/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  77. package/dist/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  78. package/dist/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  79. package/dist/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  80. package/dist/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  81. package/dist/web/assets/chat-tab-bS86TsT5.js +10 -0
  82. package/dist/web/assets/{code-editor-BFe-hnpF.js → code-editor-BaNaQ33b.js} +1 -1
  83. package/dist/web/assets/{database-viewer-BeY2V5QI.js → database-viewer-C5MVw8cJ.js} +1 -1
  84. package/dist/web/assets/{diff-viewer-D6xzs8PP.js → diff-viewer-CUbFMWVo.js} +1 -1
  85. package/dist/web/assets/{extension-webview-Cd1XYFXO.js → extension-webview-CwGufYEP.js} +1 -1
  86. package/dist/web/assets/{git-graph-D2XXpiMQ.js → git-graph-BD7A7MLo.js} +1 -1
  87. package/dist/web/assets/index-BYXjCNlK.css +2 -0
  88. package/dist/web/assets/index-CpzkPHOC.js +30 -0
  89. package/dist/web/assets/keybindings-store-DsaANvBz.js +1 -0
  90. package/dist/web/assets/markdown-renderer-C19IsITh.js +326 -0
  91. package/dist/web/assets/{port-forwarding-tab-B5rj_I66.js → port-forwarding-tab-BF79F1iL.js} +1 -1
  92. package/dist/web/assets/{postgres-viewer-DnlqzOnm.js → postgres-viewer-_nYiO_wp.js} +1 -1
  93. package/dist/web/assets/{settings-tab-CNZpuPD3.js → settings-tab-C1SQMbSu.js} +1 -1
  94. package/dist/web/assets/{sql-query-editor-Df2kzbPj.js → sql-query-editor-6OFvxxuN.js} +1 -1
  95. package/dist/web/assets/{sqlite-viewer-Cj1G70z4.js → sqlite-viewer-SNVYFXvB.js} +1 -1
  96. package/dist/web/assets/{terminal-tab-Dv9A7Xe2.js → terminal-tab-BJEkmrDt.js} +1 -1
  97. package/dist/web/assets/{use-monaco-theme-CPfIEo8t.js → use-monaco-theme-r8FzlCWr.js} +1 -1
  98. package/dist/web/index.html +2 -2
  99. package/dist/web/sw.js +1 -1
  100. package/docs/codebase-summary.md +78 -0
  101. package/docs/project-changelog.md +29 -0
  102. package/docs/system-architecture.md +2 -0
  103. package/package.json +5 -2
  104. package/release-manifest.json +15784 -0
  105. package/scripts/check-ppm-dir-usage.sh +21 -0
  106. package/scripts/generate-ppm-guide.ts +92 -0
  107. package/src/cli/commands/init.ts +2 -1
  108. package/src/cli/commands/logs.ts +11 -11
  109. package/src/cli/commands/report.ts +3 -2
  110. package/src/cli/commands/restart.ts +22 -23
  111. package/src/cli/commands/skills-cmd.ts +123 -0
  112. package/src/cli/commands/status.ts +7 -8
  113. package/src/cli/commands/stop.ts +18 -19
  114. package/src/index.ts +3 -0
  115. package/src/lib/account-crypto.ts +12 -7
  116. package/src/providers/claude-agent-sdk.ts +42 -11
  117. package/src/server/index.ts +8 -8
  118. package/src/server/routes/chat.ts +4 -2
  119. package/src/server/routes/upgrade.ts +3 -5
  120. package/src/server/ws/chat.ts +31 -0
  121. package/src/services/cloud-ws.service.ts +6 -3
  122. package/src/services/cloud.service.ts +20 -19
  123. package/src/services/cloudflared.service.ts +13 -13
  124. package/src/services/config.service.ts +5 -7
  125. package/src/services/db.service.ts +5 -6
  126. package/src/services/extension-rpc-handlers.ts +2 -2
  127. package/src/services/extension.service.ts +9 -12
  128. package/src/services/ppm-dir.ts +14 -0
  129. package/src/services/slash-discovery/builtin-commands.ts +53 -0
  130. package/src/services/slash-discovery/builtin-handlers.ts +65 -0
  131. package/src/services/slash-discovery/definition-source.ts +27 -0
  132. package/src/services/slash-discovery/discover-skill-roots.ts +128 -0
  133. package/src/services/slash-discovery/fuzzy-search.ts +76 -0
  134. package/src/services/slash-discovery/index.ts +42 -0
  135. package/src/services/slash-discovery/resolve-overrides.ts +41 -0
  136. package/src/services/slash-discovery/skill-loader.ts +156 -0
  137. package/src/services/slash-discovery/types.ts +51 -0
  138. package/src/services/slash-items.service.ts +4 -182
  139. package/src/services/supervisor-state.ts +14 -15
  140. package/src/services/supervisor-stopped-page.ts +2 -4
  141. package/src/services/supervisor.ts +15 -15
  142. package/src/services/tunnel.service.ts +22 -5
  143. package/src/services/upgrade.service.ts +2 -3
  144. package/src/types/chat.ts +3 -1
  145. package/src/web/components/chat/chat-history-bar.tsx +2 -15
  146. package/src/web/components/chat/chat-tab.tsx +5 -2
  147. package/src/web/components/chat/message-input.tsx +48 -6
  148. package/src/web/components/chat/message-list.tsx +19 -5
  149. package/src/web/components/chat/slash-command-picker.tsx +21 -12
  150. package/src/web/components/layout/mobile-nav.tsx +47 -21
  151. package/src/web/components/layout/panel-layout.tsx +11 -0
  152. package/src/web/components/layout/upgrade-banner.tsx +48 -2
  153. package/src/web/components/shared/markdown-renderer.tsx +5 -2
  154. package/src/web/hooks/use-chat.ts +33 -1
  155. package/src/web/main.tsx +1 -0
  156. package/src/web/stores/panel-store.ts +25 -1
  157. package/src/web/styles/globals.css +14 -0
  158. package/dist/web/assets/chat-tab-CmSLt4tg.js +0 -10
  159. package/dist/web/assets/index-BtwsLrdT.css +0 -2
  160. package/dist/web/assets/index-D6_wwsL_.js +0 -30
  161. package/dist/web/assets/keybindings-store-C8ryKudw.js +0 -1
  162. package/dist/web/assets/markdown-renderer-xYMhd9cE.js +0 -69
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Management CLI - Command-line interface for MCP operations
4
+ */
5
+ import { MCPClientManager } from './mcp-client.js';
6
+ import { writeFileSync, mkdirSync } from 'fs';
7
+ import { dirname, join } from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ const GLOBAL_TIMEOUT_MS = parseInt(process.env.MCP_TIMEOUT || '120000', 10);
12
+ let globalManager = null;
13
+ function setupShutdownHandlers() {
14
+ const shutdown = async (signal) => {
15
+ console.log(`\nReceived ${signal}, cleaning up...`);
16
+ if (globalManager) {
17
+ await globalManager.cleanup();
18
+ }
19
+ process.exit(0);
20
+ };
21
+ process.on('SIGINT', () => shutdown('SIGINT'));
22
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
23
+ process.on('SIGHUP', () => shutdown('SIGHUP'));
24
+ process.on('unhandledRejection', (reason) => {
25
+ console.error('Unhandled rejection:', reason);
26
+ process.exit(1);
27
+ });
28
+ }
29
+ async function main() {
30
+ const args = process.argv.slice(2);
31
+ const command = args[0];
32
+ // Setup shutdown handlers
33
+ setupShutdownHandlers();
34
+ // Check for help flags BEFORE connecting to servers
35
+ if (!command || command === '--help' || command === 'help') {
36
+ printUsage();
37
+ process.exit(0);
38
+ }
39
+ // Global timeout
40
+ const timeoutHandle = setTimeout(() => {
41
+ console.error('Global timeout exceeded, forcing exit');
42
+ process.exit(1);
43
+ }, GLOBAL_TIMEOUT_MS);
44
+ timeoutHandle.unref();
45
+ const manager = new MCPClientManager();
46
+ globalManager = manager;
47
+ try {
48
+ // Load config
49
+ await manager.loadConfig();
50
+ console.log('✓ Config loaded');
51
+ // Connect to all servers
52
+ await manager.connectAll();
53
+ console.log('✓ Connected to all MCP servers\n');
54
+ switch (command) {
55
+ case 'list-tools':
56
+ await listTools(manager);
57
+ break;
58
+ case 'list-prompts':
59
+ await listPrompts(manager);
60
+ break;
61
+ case 'list-resources':
62
+ await listResources(manager);
63
+ break;
64
+ case 'call-tool':
65
+ await callTool(manager, args[1], args[2], args[3]);
66
+ break;
67
+ default:
68
+ printUsage();
69
+ }
70
+ await manager.cleanup();
71
+ process.exit(0);
72
+ }
73
+ catch (error) {
74
+ console.error('Error:', error);
75
+ process.exit(1);
76
+ }
77
+ }
78
+ async function listTools(manager) {
79
+ const tools = await manager.getAllTools();
80
+ console.log(`Found ${tools.length} tools:\n`);
81
+ for (const tool of tools) {
82
+ console.log(`📦 ${tool.serverName} / ${tool.name}`);
83
+ console.log(` ${tool.description}`);
84
+ if (tool.inputSchema?.properties) {
85
+ console.log(` Parameters: ${Object.keys(tool.inputSchema.properties).join(', ')}`);
86
+ }
87
+ console.log('');
88
+ }
89
+ // Save tools to JSON file
90
+ const assetsDir = join(__dirname, '..', 'assets');
91
+ const toolsPath = join(assetsDir, 'tools.json');
92
+ try {
93
+ mkdirSync(assetsDir, { recursive: true });
94
+ writeFileSync(toolsPath, JSON.stringify(tools, null, 2));
95
+ console.log(`\n✓ Tools saved to ${toolsPath}`);
96
+ }
97
+ catch (error) {
98
+ console.error(`\n✗ Failed to save tools: ${error}`);
99
+ }
100
+ }
101
+ async function listPrompts(manager) {
102
+ const prompts = await manager.getAllPrompts();
103
+ console.log(`Found ${prompts.length} prompts:\n`);
104
+ for (const prompt of prompts) {
105
+ console.log(`💬 ${prompt.serverName} / ${prompt.name}`);
106
+ console.log(` ${prompt.description}`);
107
+ if (prompt.arguments && prompt.arguments.length > 0) {
108
+ console.log(` Arguments: ${prompt.arguments.map((a) => a.name).join(', ')}`);
109
+ }
110
+ console.log('');
111
+ }
112
+ }
113
+ async function listResources(manager) {
114
+ const resources = await manager.getAllResources();
115
+ console.log(`Found ${resources.length} resources:\n`);
116
+ for (const resource of resources) {
117
+ console.log(`📄 ${resource.serverName} / ${resource.name}`);
118
+ console.log(` URI: ${resource.uri}`);
119
+ if (resource.description) {
120
+ console.log(` ${resource.description}`);
121
+ }
122
+ if (resource.mimeType) {
123
+ console.log(` Type: ${resource.mimeType}`);
124
+ }
125
+ console.log('');
126
+ }
127
+ }
128
+ async function callTool(manager, serverName, toolName, argsJson) {
129
+ if (!serverName || !toolName || !argsJson) {
130
+ console.error('Usage: cli.ts call-tool <server> <tool> <json-args>');
131
+ process.exit(1);
132
+ }
133
+ const args = JSON.parse(argsJson);
134
+ console.log(`Calling ${serverName}/${toolName}...`);
135
+ const result = await manager.callTool(serverName, toolName, args);
136
+ console.log('\nResult:');
137
+ console.log(JSON.stringify(result, null, 2));
138
+ }
139
+ function printUsage() {
140
+ console.log(`
141
+ MCP Management CLI
142
+
143
+ Usage:
144
+ cli.ts <command> [options]
145
+
146
+ Commands:
147
+ list-tools List all tools and save to assets/tools.json
148
+ list-prompts List all prompts from all MCP servers
149
+ list-resources List all resources from all MCP servers
150
+ call-tool <server> <tool> <json> Call a specific tool
151
+
152
+ Examples:
153
+ cli.ts list-tools
154
+ cli.ts call-tool memory create_entities '{"entities":[{"name":"Alice","entityType":"person"}]}'
155
+ cli.ts call-tool human-mcp playwright_screenshot_fullpage '{"url":"https://example.com"}'
156
+
157
+ Note: Tool analysis is done by the LLM reading assets/tools.json directly.
158
+ `);
159
+ }
160
+ main();
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Client - Core client for interacting with MCP servers
4
+ */
5
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
6
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
7
+ import { readFile } from 'fs/promises';
8
+ import { resolve } from 'path';
9
+ export class MCPClientManager {
10
+ config = null;
11
+ clients = new Map();
12
+ transports = new Map();
13
+ async loadConfig(configPath = '.claude/.mcp.json') {
14
+ const fullPath = resolve(process.cwd(), configPath);
15
+ const content = await readFile(fullPath, 'utf-8');
16
+ const config = JSON.parse(content);
17
+ this.config = config;
18
+ return config;
19
+ }
20
+ async connectToServer(serverName) {
21
+ if (!this.config?.mcpServers[serverName]) {
22
+ throw new Error(`Server ${serverName} not found in config`);
23
+ }
24
+ const serverConfig = this.config.mcpServers[serverName];
25
+ const transport = new StdioClientTransport({
26
+ command: serverConfig.command,
27
+ args: serverConfig.args,
28
+ env: serverConfig.env
29
+ });
30
+ const client = new Client({
31
+ name: `mcp-manager-${serverName}`,
32
+ version: '1.0.0'
33
+ }, { capabilities: {} });
34
+ await client.connect(transport);
35
+ this.clients.set(serverName, client);
36
+ this.transports.set(serverName, transport); // Track transport!
37
+ return client;
38
+ }
39
+ async connectAll() {
40
+ if (!this.config) {
41
+ throw new Error('Config not loaded. Call loadConfig() first.');
42
+ }
43
+ const serverNames = Object.keys(this.config.mcpServers);
44
+ console.log(`Connecting to ${serverNames.length} servers sequentially...`);
45
+ for (const serverName of serverNames) {
46
+ try {
47
+ await this.connectToServer(serverName);
48
+ console.log(`✓ ${serverName} connected`);
49
+ }
50
+ catch (error) {
51
+ console.error(`✗ ${serverName} failed:`, error);
52
+ // Continue with other servers
53
+ }
54
+ }
55
+ }
56
+ async getAllTools() {
57
+ const allTools = [];
58
+ for (const [serverName, client] of this.clients.entries()) {
59
+ try {
60
+ const response = await client.listTools({}, { timeout: 300000 });
61
+ for (const tool of response.tools) {
62
+ allTools.push({
63
+ serverName,
64
+ name: tool.name,
65
+ description: tool.description || '',
66
+ inputSchema: tool.inputSchema,
67
+ outputSchema: tool.outputSchema
68
+ });
69
+ }
70
+ }
71
+ catch (error) {
72
+ if (error?.code === -32601) {
73
+ console.warn(`${serverName} does not support listTools`);
74
+ }
75
+ else {
76
+ console.error(`Error from ${serverName}:`, error);
77
+ }
78
+ // Continue with other servers!
79
+ }
80
+ }
81
+ return allTools;
82
+ }
83
+ async getAllPrompts() {
84
+ const allPrompts = [];
85
+ for (const [serverName, client] of this.clients.entries()) {
86
+ try {
87
+ const response = await client.listPrompts({}, { timeout: 300000 });
88
+ for (const prompt of response.prompts) {
89
+ allPrompts.push({
90
+ serverName,
91
+ name: prompt.name,
92
+ description: prompt.description || '',
93
+ arguments: prompt.arguments
94
+ });
95
+ }
96
+ }
97
+ catch (error) {
98
+ if (error?.code === -32601) {
99
+ console.warn(`${serverName} does not support listPrompts`);
100
+ }
101
+ else {
102
+ console.error(`Error from ${serverName}:`, error);
103
+ }
104
+ // Continue with other servers!
105
+ }
106
+ }
107
+ return allPrompts;
108
+ }
109
+ async getAllResources() {
110
+ const allResources = [];
111
+ for (const [serverName, client] of this.clients.entries()) {
112
+ try {
113
+ const response = await client.listResources({}, { timeout: 300000 });
114
+ for (const resource of response.resources) {
115
+ allResources.push({
116
+ serverName,
117
+ uri: resource.uri,
118
+ name: resource.name,
119
+ description: resource.description,
120
+ mimeType: resource.mimeType
121
+ });
122
+ }
123
+ }
124
+ catch (error) {
125
+ if (error?.code === -32601) {
126
+ console.warn(`${serverName} does not support listResources`);
127
+ }
128
+ else {
129
+ console.error(`Error from ${serverName}:`, error);
130
+ }
131
+ // Continue with other servers!
132
+ }
133
+ }
134
+ return allResources;
135
+ }
136
+ async callTool(serverName, toolName, args) {
137
+ const client = this.clients.get(serverName);
138
+ if (!client)
139
+ throw new Error(`Not connected to server: ${serverName}`);
140
+ return await client.callTool({ name: toolName, arguments: args }, undefined, { timeout: 300000 });
141
+ }
142
+ async getPrompt(serverName, promptName, args) {
143
+ const client = this.clients.get(serverName);
144
+ if (!client)
145
+ throw new Error(`Not connected to server: ${serverName}`);
146
+ return await client.getPrompt({ name: promptName, arguments: args }, { timeout: 300000 });
147
+ }
148
+ async readResource(serverName, uri) {
149
+ const client = this.clients.get(serverName);
150
+ if (!client)
151
+ throw new Error(`Not connected to server: ${serverName}`);
152
+ return await client.readResource({ uri }, { timeout: 300000 });
153
+ }
154
+ async cleanup() {
155
+ // Close clients with timeout
156
+ const cleanupPromises = [];
157
+ for (const [serverName, client] of this.clients.entries()) {
158
+ cleanupPromises.push((async () => {
159
+ try {
160
+ await client.close();
161
+ }
162
+ catch (error) {
163
+ console.warn(`Warning closing ${serverName}:`, error);
164
+ }
165
+ })());
166
+ }
167
+ await Promise.race([
168
+ Promise.all(cleanupPromises),
169
+ new Promise((resolve) => setTimeout(resolve, 5000))
170
+ ]);
171
+ // CRITICAL: Close transports to kill subprocesses
172
+ for (const [serverName, transport] of this.transports.entries()) {
173
+ try {
174
+ await transport.close();
175
+ }
176
+ catch (error) {
177
+ console.warn(`Warning closing ${serverName} transport:`, error);
178
+ }
179
+ }
180
+ this.clients.clear();
181
+ this.transports.clear();
182
+ }
183
+ }
@@ -0,0 +1,20 @@
1
+ # SePay Configuration
2
+ SEPAY_MERCHANT_ID=SP-TEST-XXXXXXX
3
+ SEPAY_SECRET_KEY=spsk_test_xxxxxxxxxxxxx
4
+ SEPAY_ENV=sandbox # or 'production'
5
+
6
+ # SePay Webhook Configuration
7
+ SEPAY_WEBHOOK_AUTH_TYPE=api_key # or 'oauth2' or 'none'
8
+ SEPAY_WEBHOOK_API_KEY=your_webhook_api_key
9
+
10
+ # Polar Configuration
11
+ POLAR_ACCESS_TOKEN=polar_xxxxxxxxxxxxxxxx
12
+ POLAR_SERVER=sandbox # or 'production'
13
+ POLAR_ORG_ID=org_xxxxxxxxxxxxx
14
+
15
+ # Polar Webhook Configuration
16
+ POLAR_WEBHOOK_SECRET=base64_encoded_secret
17
+
18
+ # Optional: Database or other configuration
19
+ # DATABASE_URL=postgresql://user:password@localhost:5432/dbname
20
+ # REDIS_URL=redis://localhost:6379
@@ -0,0 +1,8 @@
1
+ # Sequential Thinking Configuration
2
+
3
+ # Disable thought logging output (useful for automated processing)
4
+ # Set to "true" to disable console logging
5
+ DISABLE_THOUGHT_LOGGING=false
6
+
7
+ # History file location (optional, defaults to scripts/.thought-history.json)
8
+ # THOUGHT_HISTORY_FILE=/path/to/custom/history.json
package/.repomixignore ADDED
@@ -0,0 +1,22 @@
1
+ docs/*
2
+ plans/*
3
+ assets/*
4
+ dist/*
5
+ coverage/*
6
+ build/*
7
+ ios/*
8
+ android/*
9
+ tests/*
10
+ __tests__/*
11
+ __pycache__/*
12
+ node_modules/*
13
+
14
+ .opencode/*
15
+ .claude/*
16
+ .serena/*
17
+ .pnpm-store/*
18
+ .github/*
19
+ .dart_tool/*
20
+ .idea/*
21
+ .husky/*
22
+ .venv/*
package/AGENTS.md ADDED
@@ -0,0 +1,62 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to OpenCode when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ **Name:** claudekit-marketing
8
+ **Type:** Node.js/TypeScript
9
+ **Description:** AI-powered marketing automation toolkit built on Claude Code subagent orchestration. Enables autonomous workflows for content creation, campaign management, SEO optimization, and lead generation.
10
+
11
+ ## Role & Responsibilities
12
+
13
+ Your role is to analyze user requirements, delegate tasks to appropriate sub-agents, and ensure cohesive delivery of features that meet specifications and architectural standards.
14
+
15
+ ## Workflows
16
+
17
+ - Primary workflow: `./.claude/workflows/primary-workflow.md`
18
+ - Development rules: `./.claude/workflows/development-rules.md`
19
+ - Orchestration protocols: `./.claude/workflows/orchestration-protocol.md`
20
+ - Documentation management: `./.claude/workflows/documentation-management.md`
21
+ - And other workflows: `./.claude/workflows/*`
22
+
23
+ **IMPORTANT:** Analyze the skills catalog and activate the skills that are needed for the task during the process.
24
+ **IMPORTANT:** You must follow strictly the development rules in `./.claude/workflows/development-rules.md` file.
25
+ **IMPORTANT:** Before you plan or proceed any implementation, always read the `./README.md` file first to get context.
26
+ **IMPORTANT:** Sacrifice grammar for the sake of concision when writing reports.
27
+ **IMPORTANT:** In reports, list any unresolved questions at the end, if any.
28
+ **IMPORTANT**: Date format is configured in `.ck.json` and injected by session hooks via `$CK_PLAN_DATE_FORMAT` env var. Use this format for plan/report naming.
29
+
30
+ ## Development Principles
31
+
32
+ - **YAGNI**: You Aren't Gonna Need It - avoid over-engineering
33
+ - **KISS**: Keep It Simple, Stupid - prefer simple solutions
34
+ - **DRY**: Don't Repeat Yourself - eliminate code duplication
35
+
36
+ ## Documentation
37
+
38
+ Keep all important docs in `./docs` folder:
39
+
40
+ ```
41
+ ./docs
42
+ ├── project-overview-pdr.md
43
+ ├── code-standards.md
44
+ ├── codebase-summary.md
45
+ ├── design-guidelines.md
46
+ └── system-architecture.md
47
+ ```
48
+
49
+ ## External Files
50
+
51
+ Reference external instruction files in `opencode.json`:
52
+
53
+ ```json
54
+ {
55
+ "instructions": ["docs/*.md", ".opencode/agents/*.md"]
56
+ }
57
+ ```
58
+
59
+ ---
60
+
61
+ *Generated by ClaudeKit OpenCode Generator*
62
+ *Date: 2026-03-28*
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.82] - 2026-04-11
4
+
5
+ ### Added
6
+ - **Math formula rendering in markdown**: LaTeX math expressions now render in chat and markdown preview using KaTeX. Supports inline `$...$` and block `$$...$$` notation. Malformed LaTeX degrades gracefully to raw text.
7
+
8
+ ## [0.9.81] - 2026-04-10
9
+
10
+ ### Added
11
+ - **Persistent toast notification for pending approvals**: When an approval request or AI question arrives on a non-active session tab, a Sonner toast with "Go to session" action appears and persists until dismissed or resolved.
12
+
13
+ ### Fixed
14
+ - **Approval timeout removed**: `waitForApproval` no longer auto-rejects after 5 minutes. Approvals wait indefinitely until user responds or session is cleaned up.
15
+ - **`deleteSession` approval cleanup bug**: Was using wrong key (`sessionId` on a `requestId`-keyed map) — now properly iterates and resolves all pending approvals for the deleted session.
16
+ - **Reconnect tool_result reliability**: Tool results are now embedded onto matching tool_use events in the buffer, so reconnecting clients don't lose tool output.
17
+ - **Cloud WS reconnect backoff**: Reconnect attempt counter only resets after auth succeeds, preventing tight reconnect loops when server closes immediately after connect.
18
+ - **Tunnel URL lazy sync**: `getTunnelUrl()` now falls back to reading `status.json` when supervisor sets the URL after server startup.
19
+
3
20
  ## [0.9.80] - 2026-04-10
4
21
 
5
22
  ### Fixed
package/CLAUDE.md CHANGED
@@ -49,6 +49,18 @@ bun test-tool.mjs /path/to/project "dùng thử tool bash" # custom prompt
49
49
 
50
50
  This uses `ClaudeAgentSdkProvider` directly — same env/settings overrides as production.
51
51
 
52
+ ## PPM Directory
53
+
54
+ **Never** use `resolve(homedir(), ".ppm")`, `join(homedir(), ".ppm")`, or `process.env.PPM_HOME || resolve(homedir(), ".ppm")` directly in service code.
55
+ Always import `getPpmDir()` from `src/services/ppm-dir.ts`. This ensures test isolation via `PPM_HOME` env var.
56
+
57
+ Exceptions (intentionally use real `homedir()`):
58
+ - `autostart-generator.ts` / `autostart-register.ts` — system service paths (launchd, systemd)
59
+ - `claude-usage.service.ts` — reads `~/.claude/` credentials (different dir)
60
+ - `fs-browse.service.ts`, `git-dirs.service.ts` — file browser starting from real home
61
+ - `ppmbot/` — bot files in real home
62
+ - `slash-discovery/` — discovers skills from real home
63
+
52
64
  ## Known Gotchas
53
65
 
54
66
  - **SDK .env poisoning**: Projects with `ANTHROPIC_API_KEY` in `.env` break SDK tool execution. Provider neutralizes these vars. See `docs/lessons-learned.md`.
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: ppm-guide
3
+ description: PPM project structure, commands, config, and development workflow reference
4
+ argument-hint: "[topic]"
5
+ ---
6
+
7
+ # PPM Guide
8
+
9
+ ## Overview
10
+ # PPM Project Overview & Product Development Requirements
11
+ ## Project Description
12
+ **PPM** (Personal Project Manager) is a full-stack, mobile-first web IDE designed for developers to manage code projects with AI-powered assistance. It combines a responsive web interface, real-time terminal access, AI chat with tool support, and Git integration into a cohesive development environment.
13
+ Built on the **Bun runtime** for performance, PPM enables developers to:
14
+ - Browse and edit project files with Monaco Editor syntax highlighting
15
+ - Execute commands via xterm.js terminal with full PTY support
16
+ - Chat with Claude AI with file attachments and slash commands
17
+ - View Git status, diffs, and commit graphs in real-time
18
+
19
+ ## CLI Commands
20
+ ```bash
21
+ bun dev:server # Start backend dev (port 8081, uses ~/.ppm/ppm.dev.db)
22
+ bun dev:web # Start Vite frontend (port 5173)
23
+ bun test # Run all tests
24
+ bun test tests/integration/ # Integration tests only
25
+ ```
26
+
27
+ ## Dev Config
28
+ Config is stored in **SQLite** (`~/.ppm/ppm.db`). Dev uses a separate DB:
29
+
30
+ - **Dev**: `~/.ppm/ppm.dev.db` — port **8081**
31
+ - **Production**: `~/.ppm/ppm.db` — port **8080**
32
+
33
+ `bun dev:server` automatically uses the dev database. On a new machine, run `ppm init` to create default config, then `ppm config set port 8081` for dev.
34
+
35
+ ## Architecture
36
+ - `src/providers/claude-agent-sdk.ts` — SDK integration, tool execution, streaming
37
+ - `src/server/ws/chat.ts` — WebSocket chat handler
38
+ - `src/web/hooks/use-chat.ts` — Frontend chat state management
39
+ - `src/services/config.service.ts` — Config from SQLite (`~/.ppm/ppm.db`)
40
+
41
+ ## Code Standards
42
+ # PPM Code Standards & Conventions
43
+ ## File Naming
44
+ | File Type | Convention | Example | Purpose |
45
+ |-----------|-----------|---------|---------|
46
+ | CLI commands | kebab-case | `start-cmd.ts`, `init.ts` | Descriptive command names |
47
+ | Services | kebab-case | `chat.service.ts`, `file.service.ts` | `{feature}.service.ts` pattern |
48
+ | Providers | kebab-case | `claude-agent-sdk.ts`, `mock-provider.ts` | `{name}-provider.ts` or `{name}.ts` |
49
+ | Routes | kebab-case | `chat.ts`, `project-scoped.ts` | Describe HTTP route group |
50
+ | WebSocket | kebab-case | `chat.ts`, `terminal.ts` | Match feature area |
51
+ | Components | PascalCase | `ChatTab.tsx`, `FileTree.tsx` | React convention |
52
+
53
+ ## Slash Commands
54
+ Use `/skills` to list all available skills and commands.
55
+ Use `/help` for session help, `/status` for context usage, `/compact` to reduce context.
56
+
57
+ ## Dev Workflow
58
+ 1. `bun dev:server` — Start backend (port 8081, dev DB)
59
+ 2. `bun dev:web` — Start Vite frontend (port 5173)
60
+ 3. `bun test` — Run all tests
61
+ 4. `bun run typecheck` — TypeScript type checking
package/bun.lock CHANGED
@@ -25,8 +25,10 @@
25
25
  "hono": "^4.12.8",
26
26
  "ignore": "^7.0.5",
27
27
  "js-yaml": "^4.1.1",
28
+ "katex": "^0.16.45",
28
29
  "lucide-react": "^0.577.0",
29
30
  "marked": "^17.0.4",
31
+ "marked-katex-extension": "^5.1.8",
30
32
  "mermaid": "^11.13.0",
31
33
  "monaco-editor": "0.55.1",
32
34
  "next-themes": "^0.4.6",
@@ -1237,7 +1239,7 @@
1237
1239
 
1238
1240
  "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
1239
1241
 
1240
- "katex": ["katex@0.16.42", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-sZ4jqyEXfHTLEFK+qsFYToa3UZ0rtFcPGwKpyiRYh2NJn8obPWOQ+/u7ux0F6CAU/y78+Mksh1YkxTPXTh47TQ=="],
1242
+ "katex": ["katex@0.16.45", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA=="],
1241
1243
 
1242
1244
  "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="],
1243
1245
 
@@ -1289,6 +1291,8 @@
1289
1291
 
1290
1292
  "marked": ["marked@17.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ=="],
1291
1293
 
1294
+ "marked-katex-extension": ["marked-katex-extension@5.1.8", "", { "peerDependencies": { "katex": ">=0.16 <0.17", "marked": ">=4 <19" } }, "sha512-TsV9OCHHDjVBf4IH0RSjLs4Eqsjj8HGfmVCKlimrS391EtBBxzXj2gBYdF9tY7f7oXu9tb1kHV86ExJsG3iMhw=="],
1295
+
1292
1296
  "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
1293
1297
 
1294
1298
  "mermaid": ["mermaid@11.13.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.0.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.1", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.19", "dompurify": "^3.3.1", "katex": "^0.16.25", "khroma": "^2.1.0", "lodash-es": "^4.17.23", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw=="],
@@ -1713,6 +1717,8 @@
1713
1717
 
1714
1718
  "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
1715
1719
 
1720
+ "mermaid/katex": ["katex@0.16.42", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-sZ4jqyEXfHTLEFK+qsFYToa3UZ0rtFcPGwKpyiRYh2NJn8obPWOQ+/u7ux0F6CAU/y78+Mksh1YkxTPXTh47TQ=="],
1721
+
1716
1722
  "mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
1717
1723
 
1718
1724
  "monaco-editor/dompurify": ["dompurify@3.2.7", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw=="],
@@ -1777,6 +1783,8 @@
1777
1783
 
1778
1784
  "lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
1779
1785
 
1786
+ "mermaid/katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
1787
+
1780
1788
  "archiver-utils/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
1781
1789
 
1782
1790
  "archiver-utils/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],