gm-skill 0.1.2 → 2.0.1080

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 (85) hide show
  1. package/AGENTS.md +1 -0
  2. package/LICENSE +21 -0
  3. package/README.md +20 -84
  4. package/agents/gm.md +22 -0
  5. package/agents/memorize.md +100 -0
  6. package/agents/research-worker.md +36 -0
  7. package/agents/textprocessing.md +47 -0
  8. package/bin/bootstrap.js +702 -0
  9. package/bin/plugkit.js +136 -0
  10. package/bin/plugkit.sha256 +7 -0
  11. package/bin/plugkit.version +1 -0
  12. package/bin/plugkit.wasm +0 -0
  13. package/bin/plugkit.wasm.sha256 +1 -0
  14. package/bin/rtk.sha256 +6 -0
  15. package/bin/rtk.version +1 -0
  16. package/gm-plugkit/bootstrap.js +694 -0
  17. package/gm-plugkit/cli.js +48 -0
  18. package/gm-plugkit/index.js +12 -0
  19. package/gm-plugkit/package.json +26 -0
  20. package/gm-plugkit/plugkit-wasm-wrapper.js +190 -0
  21. package/gm-plugkit/plugkit.sha256 +6 -0
  22. package/gm-plugkit/plugkit.version +1 -0
  23. package/gm.json +27 -0
  24. package/lang/browser.js +45 -0
  25. package/lang/ssh.js +166 -0
  26. package/lib/browser-spool-handler.js +130 -0
  27. package/lib/browser.js +131 -0
  28. package/lib/codeinsight.js +109 -0
  29. package/lib/daemon-bootstrap.js +253 -132
  30. package/lib/git.js +0 -1
  31. package/lib/learning.js +169 -0
  32. package/lib/skill-bootstrap.js +406 -0
  33. package/lib/spool-dispatch.js +100 -0
  34. package/lib/spool.js +87 -49
  35. package/lib/wasm-host.js +241 -0
  36. package/package.json +38 -20
  37. package/prompts/bash-deny.txt +22 -0
  38. package/prompts/pre-compact.txt +21 -0
  39. package/prompts/prompt-submit.txt +83 -0
  40. package/prompts/session-start.txt +15 -0
  41. package/scripts/run-hook.sh +7 -0
  42. package/scripts/watch-cascade.js +166 -0
  43. package/skills/browser/SKILL.md +80 -0
  44. package/skills/code-search/SKILL.md +48 -0
  45. package/skills/create-lang-plugin/SKILL.md +121 -0
  46. package/skills/gm/SKILL.md +10 -49
  47. package/skills/gm-complete/SKILL.md +16 -87
  48. package/skills/gm-emit/SKILL.md +17 -50
  49. package/skills/gm-execute/SKILL.md +18 -69
  50. package/skills/gm-skill/SKILL.md +43 -0
  51. package/skills/gm-skill/index.js +21 -0
  52. package/skills/governance/SKILL.md +97 -0
  53. package/skills/pages/SKILL.md +208 -0
  54. package/skills/planning/SKILL.md +21 -97
  55. package/skills/research/SKILL.md +43 -0
  56. package/skills/ssh/SKILL.md +71 -0
  57. package/skills/textprocessing/SKILL.md +40 -0
  58. package/skills/update-docs/SKILL.md +24 -43
  59. package/gm-complete.SKILL.md +0 -106
  60. package/gm-emit.SKILL.md +0 -70
  61. package/gm-execute.SKILL.md +0 -88
  62. package/gm.SKILL.md +0 -63
  63. package/index.js +0 -1
  64. package/lib/index.js +0 -37
  65. package/lib/loader.js +0 -66
  66. package/lib/manifest.js +0 -99
  67. package/lib/prepare.js +0 -14
  68. package/planning.SKILL.md +0 -118
  69. package/skills/gm/index.js +0 -113
  70. package/skills/gm-complete/index.js +0 -118
  71. package/skills/gm-complete.SKILL.md +0 -106
  72. package/skills/gm-emit/index.js +0 -90
  73. package/skills/gm-emit.SKILL.md +0 -70
  74. package/skills/gm-execute/index.js +0 -91
  75. package/skills/gm-execute.SKILL.md +0 -88
  76. package/skills/gm.SKILL.md +0 -63
  77. package/skills/planning/index.js +0 -107
  78. package/skills/planning.SKILL.md +0 -118
  79. package/skills/update-docs/index.js +0 -108
  80. package/skills/update-docs.SKILL.md +0 -66
  81. package/test-build.js +0 -29
  82. package/test-e2e.js +0 -117
  83. package/test-unified.js +0 -24
  84. package/test.js +0 -89
  85. package/update-docs.SKILL.md +0 -66
package/lib/browser.js ADDED
@@ -0,0 +1,131 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const spool = require('./spool.js');
5
+
6
+ const LOG_DIR = path.join(os.homedir(), '.claude', 'gm-log');
7
+ const SESSION_STATE_DIR = path.join(os.homedir(), '.gm', 'browser-sessions');
8
+
9
+ function emitBrowserEvent(severity, message, details) {
10
+ try {
11
+ const date = new Date().toISOString().split('T')[0];
12
+ const logDir = path.join(LOG_DIR, date);
13
+ if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
14
+ fs.appendFileSync(path.join(logDir, 'browser.jsonl'), JSON.stringify({ ts: new Date().toISOString(), severity, message, ...details }) + '\n');
15
+ } catch (e) {
16
+ console.error(`[browser] Failed to emit event: ${e.message}`);
17
+ }
18
+ }
19
+
20
+ function loadSessionState(sessionId) {
21
+ try {
22
+ fs.mkdirSync(SESSION_STATE_DIR, { recursive: true });
23
+ const stateFile = path.join(SESSION_STATE_DIR, `${sessionId}.json`);
24
+ if (fs.existsSync(stateFile)) return JSON.parse(fs.readFileSync(stateFile, 'utf8'));
25
+ } catch (e) {
26
+ emitBrowserEvent('warn', 'Failed to load session state', { sessionId, error: e.message });
27
+ }
28
+ return { sessionId, createdAt: new Date().toISOString(), commands: [] };
29
+ }
30
+
31
+ function saveSessionState(sessionId, state) {
32
+ try {
33
+ fs.mkdirSync(SESSION_STATE_DIR, { recursive: true });
34
+ fs.writeFileSync(path.join(SESSION_STATE_DIR, `${sessionId}.json`), JSON.stringify(state, null, 2));
35
+ } catch (e) {
36
+ emitBrowserEvent('warn', 'Failed to save session state', { sessionId, error: e.message });
37
+ }
38
+ }
39
+
40
+ function parseJsonFromStdout(stdout) {
41
+ const trimmed = (stdout || '').trim();
42
+ if (!trimmed) return null;
43
+ const lines = trimmed.split(/\r?\n/).filter(Boolean);
44
+ for (let i = lines.length - 1; i >= 0; i--) {
45
+ try {
46
+ return JSON.parse(lines[i]);
47
+ } catch (e) {}
48
+ }
49
+ try {
50
+ return JSON.parse(trimmed);
51
+ } catch (e) {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ async function runBrowserVerb(action, args, sessionId, timeoutMs = 30000) {
57
+ const payload = args && Object.keys(args).length > 0 ? `${action}\n${JSON.stringify(args)}` : action;
58
+ const result = await spool.execSpool(payload, 'browser', { timeoutMs, sessionId });
59
+ if (!result.ok) throw new Error(result.stderr || result.stdout || `browser verb failed: ${action}`);
60
+ const parsed = parseJsonFromStdout(result.stdout);
61
+ return parsed || { ok: true };
62
+ }
63
+
64
+ async function isBrowserAvailable(sessionId = process.env.CLAUDE_SESSION_ID || 'unknown') {
65
+ try {
66
+ const result = await spool.execSpool('health', 'health', { timeoutMs: 1000, sessionId });
67
+ return !!(result && result.ok);
68
+ } catch (e) {
69
+ return false;
70
+ }
71
+ }
72
+
73
+ async function createSession(sessionId) {
74
+ if (!sessionId) throw new Error('sessionId is required');
75
+ const result = await runBrowserVerb('start', {}, sessionId, 30000);
76
+ const state = loadSessionState(sessionId);
77
+ state.browserSessionId = result.browserSessionId || result.sessionId || sessionId;
78
+ state.status = 'active';
79
+ state.createdAt = new Date().toISOString();
80
+ saveSessionState(sessionId, state);
81
+ return { ok: true, sessionId, browserSessionId: state.browserSessionId };
82
+ }
83
+
84
+ async function sendCommand(sessionId, commandType, args) {
85
+ if (!sessionId) throw new Error('sessionId is required');
86
+ const result = await runBrowserVerb(commandType, args || {}, sessionId, 30000);
87
+ const state = loadSessionState(sessionId);
88
+ state.commands = state.commands || [];
89
+ state.commands.push({ type: commandType, args: args || {}, timestamp: new Date().toISOString() });
90
+ if (state.commands.length > 1000) state.commands = state.commands.slice(-500);
91
+ saveSessionState(sessionId, state);
92
+ return { ok: true, result: result.result || result.data || result };
93
+ }
94
+
95
+ async function executeScript(sessionId, code, options = {}) {
96
+ if (!sessionId) throw new Error('sessionId is required');
97
+ if (!code || typeof code !== 'string') throw new Error('code must be a non-empty string');
98
+ const payload = { code, ...options };
99
+ return sendCommand(sessionId, 'execute', payload);
100
+ }
101
+
102
+ async function getScreenshot(sessionId) {
103
+ if (!sessionId) throw new Error('sessionId is required');
104
+ const result = await runBrowserVerb('screenshot', {}, sessionId, 30000);
105
+ let screenshotData = result.screenshot || result.data || '';
106
+ if (screenshotData && !screenshotData.startsWith('data:image')) screenshotData = `data:image/png;base64,${screenshotData}`;
107
+ return { ok: true, screenshot: screenshotData, mimeType: 'image/png' };
108
+ }
109
+
110
+ async function closeSession(sessionId) {
111
+ if (!sessionId) throw new Error('sessionId is required');
112
+ try { await runBrowserVerb('stop', {}, sessionId, 10000); } catch (e) {}
113
+ const stateFile = path.join(SESSION_STATE_DIR, `${sessionId}.json`);
114
+ if (fs.existsSync(stateFile)) fs.unlinkSync(stateFile);
115
+ return { ok: true, sessionId };
116
+ }
117
+
118
+ async function closeAllSessions(excludeSessionId = null) {
119
+ if (!fs.existsSync(SESSION_STATE_DIR)) return { ok: true, closed: 0 };
120
+ const files = fs.readdirSync(SESSION_STATE_DIR);
121
+ let closed = 0;
122
+ for (const file of files) {
123
+ if (!file.endsWith('.json')) continue;
124
+ const sessionId = file.replace('.json', '');
125
+ if (excludeSessionId && sessionId === excludeSessionId) continue;
126
+ try { await closeSession(sessionId); closed++; } catch (e) {}
127
+ }
128
+ return { ok: true, closed };
129
+ }
130
+
131
+ module.exports = { createSession, sendCommand, executeScript, getScreenshot, closeSession, closeAllSessions, isBrowserAvailable, loadSessionState, saveSessionState };
@@ -0,0 +1,109 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const spool = require('./spool.js');
5
+
6
+ const CODEINSIGHT_HOST = '127.0.0.1';
7
+ const CODEINSIGHT_PORT = 4802;
8
+ const REQUEST_TIMEOUT_MS = 30000;
9
+
10
+ function emitEvent(severity, message, details = {}) {
11
+ try {
12
+ const date = new Date().toISOString().split('T')[0];
13
+ const logDir = path.join(os.homedir(), '.claude', 'gm-log', date);
14
+ if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
15
+ const entry = { ts: new Date().toISOString(), severity, message, ...details };
16
+ fs.appendFileSync(path.join(logDir, 'codeinsight.jsonl'), JSON.stringify(entry) + '\n');
17
+ } catch (e) {
18
+ console.error(`[codeinsight] emit failed: ${e.message}`);
19
+ }
20
+ }
21
+
22
+ async function checkSocketReachable(host = CODEINSIGHT_HOST, port = CODEINSIGHT_PORT, timeoutMs = 1000) {
23
+ try {
24
+ const result = await spool.execSpool('health', 'health', { timeoutMs });
25
+ return !!(result && result.ok);
26
+ } catch (e) {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ async function sendRequest(request, sessionId = 'unknown') {
32
+ const startTime = Date.now();
33
+ const reachable = await checkSocketReachable();
34
+
35
+ if (!reachable) {
36
+ emitEvent('warn', 'Codeinsight socket unreachable', {
37
+ host: CODEINSIGHT_HOST,
38
+ port: CODEINSIGHT_PORT,
39
+ sessionId,
40
+ durationMs: Date.now() - startTime,
41
+ });
42
+ return { ok: false, error: `Codeinsight daemon unavailable at ${CODEINSIGHT_HOST}:${CODEINSIGHT_PORT}`, durationMs: Date.now() - startTime };
43
+ }
44
+
45
+ try {
46
+ if (request.action === 'search') {
47
+ const q = request.discipline && request.discipline !== 'default' ? `@${request.discipline} ${request.query}` : request.query;
48
+ const result = await spool.execCodesearch(q, { timeoutMs: REQUEST_TIMEOUT_MS, sessionId });
49
+ if (!result.ok) return { ok: false, error: result.stderr || result.stdout || 'codesearch failed', durationMs: Date.now() - startTime };
50
+ return { ok: true, raw: result.stdout || '', durationMs: Date.now() - startTime };
51
+ }
52
+ return { ok: false, error: `Unsupported action via spool: ${request.action}`, durationMs: Date.now() - startTime };
53
+ } catch (err) {
54
+ return { ok: false, error: err.message, durationMs: Date.now() - startTime };
55
+ }
56
+ }
57
+
58
+ async function searchCode(query, discipline = 'default', sessionId = 'unknown') {
59
+ if (!query || typeof query !== 'string' || query.trim().length === 0) {
60
+ return { ok: false, error: 'Query must be a non-empty string' };
61
+ }
62
+ const result = await sendRequest({ action: 'search', query: query.trim(), discipline, sessionId }, sessionId);
63
+ if (result.ok) {
64
+ emitEvent('info', 'Search completed', { query: query.trim(), discipline, resultCount: (result.results || []).length, sessionId, durationMs: result.durationMs });
65
+ }
66
+ return result;
67
+ }
68
+
69
+ async function indexProject(projectPath, discipline = 'default', sessionId = 'unknown') {
70
+ if (!projectPath || typeof projectPath !== 'string') {
71
+ return { ok: false, error: 'Project path must be a non-empty string' };
72
+ }
73
+ if (!fs.existsSync(projectPath)) {
74
+ emitEvent('warn', 'Project path does not exist', { projectPath, discipline, sessionId });
75
+ return { ok: false, error: `Project path does not exist: ${projectPath}` };
76
+ }
77
+ const result = await sendRequest({ action: 'index', projectPath: path.resolve(projectPath), discipline, sessionId }, sessionId);
78
+ if (result.ok) {
79
+ emitEvent('info', 'Index completed', { projectPath: path.resolve(projectPath), discipline, filesIndexed: result.filesIndexed, sessionId, durationMs: result.durationMs });
80
+ }
81
+ return result;
82
+ }
83
+
84
+ async function getDiagnostics(projectPath = null, discipline = 'default', sessionId = 'unknown') {
85
+ if (projectPath && !fs.existsSync(projectPath)) {
86
+ return { ok: false, error: `Project path does not exist: ${projectPath}` };
87
+ }
88
+ const result = await sendRequest({ action: 'diagnostics', projectPath: projectPath ? path.resolve(projectPath) : null, discipline, sessionId }, sessionId);
89
+ if (result.ok) {
90
+ emitEvent('info', 'Diagnostics retrieved', { projectPath: projectPath ? path.resolve(projectPath) : 'system-wide', discipline, diagnosticCount: (result.diagnostics || []).length, sessionId, durationMs: result.durationMs });
91
+ }
92
+ return result;
93
+ }
94
+
95
+ async function getIndexStatus(discipline = 'default', sessionId = 'unknown') {
96
+ const result = await sendRequest({ action: 'status', discipline, sessionId }, sessionId);
97
+ if (result.ok) {
98
+ emitEvent('info', 'Index status retrieved', { discipline, indexed: result.indexed, sessionId, durationMs: result.durationMs });
99
+ }
100
+ return result;
101
+ }
102
+
103
+ module.exports = {
104
+ searchCode,
105
+ indexProject,
106
+ getDiagnostics,
107
+ getIndexStatus,
108
+ checkSocketReachable,
109
+ };