gm-skill 2.0.1254 → 2.0.1256

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/README.md CHANGED
@@ -35,7 +35,7 @@ An earlier generation fanned out fifteen per-platform downstream repos (gm-cc, g
35
35
 
36
36
  ## Version
37
37
 
38
- `2.0.1254` — auto-bumped from the canonical `gm` repo. Every push to `AnEntrypoint/gm` (or any cascading sibling crate) republishes this package.
38
+ `2.0.1256` — auto-bumped from the canonical `gm` repo. Every push to `AnEntrypoint/gm` (or any cascading sibling crate) republishes this package.
39
39
 
40
40
  ## Source of truth
41
41
 
@@ -1 +1 @@
1
- 0.1.463
1
+ 0.1.464
package/bin/plugkit.wasm CHANGED
Binary file
@@ -1 +1 @@
1
- 9ca233bf9de58547c43bd2263f14fd89535c722db3f5050f5a0afb4612ff9457 plugkit.wasm
1
+ 5f964bb1da8059f4d6969dc179e407cc14fa91860135389a611d1896085b59be plugkit.wasm
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1254",
3
+ "version": "2.0.1256",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -17,5 +17,5 @@
17
17
  "publishConfig": {
18
18
  "access": "public"
19
19
  },
20
- "plugkitVersion": "0.1.463"
20
+ "plugkitVersion": "0.1.464"
21
21
  }
@@ -328,202 +328,6 @@ function ensureBuildToolIgnores(cwd) {
328
328
  }
329
329
  }
330
330
 
331
- const SPOOL_POLL_GATE_MARK = '__gm_spool_poll_gate__';
332
-
333
- function spoolPollGateScript() {
334
- return `#!/usr/bin/env node
335
- // ${SPOOL_POLL_GATE_MARK}
336
- // PreToolUse hook that blocks bash polling of .gm/exec-spool.
337
- // Plugkit is synchronous from the agent's view; the Read tool is the canonical
338
- // way to inspect response files. This hook denies Bash commands that try to
339
- // poll or shell-read the spool directory.
340
-
341
- const SPOOL_POLL_PATTERNS = [
342
- /\\bsleep\\s+\\d+(?:\\.\\d+)?\\s*[;&]+\\s*(?:cat|ls|tail|head|find|test|grep)\\b[^|]*\\.gm[\\\\/](?:exec-spool|spool)/i,
343
- /\\bStart-Sleep\\b[^;|]*?[;|]\\s*(?:Get-Content|Test-Path|Get-ChildItem|cat|ls|gci|gc|tp)\\b[^|]*\\.gm[\\\\/](?:exec-spool|spool)/i,
344
- /\\b(?:cat|ls|tail|head|Get-Content|Test-Path|Get-ChildItem)\\b[^|]*\\.gm[\\\\/](?:exec-spool|spool)[^|]*?[;&|]+\\s*(?:sleep|Start-Sleep)\\b/i,
345
- /\\bwhile\\b[^;]*?(?:!|-not)\\s*(?:-(?:f|e)\\s+|Test-Path\\s+)[^;]*?\\.gm[\\\\/](?:exec-spool|spool)/i,
346
- /\\buntil\\b[^;]*?(?:-f|-e|Test-Path)\\s+[^;]*?\\.gm[\\\\/](?:exec-spool|spool)/i,
347
- /\\bfor\\s+i\\s+in\\b[^;]*?;\\s*do\\b[^;]*?(?:sleep|Start-Sleep)[^;]*?\\.gm[\\\\/](?:exec-spool|spool)/i,
348
- /\\b(?:cat|head|tail|less|more|type|Get-Content|gc)\\s+(?:-[A-Za-z]+\\s+)*['"]?[^'"|;&]*\\.gm[\\\\/](?:exec-spool|spool)[\\\\/]/i,
349
- /\\b(?:ls|dir|Get-ChildItem|gci)\\s+(?:-[A-Za-z]+\\s+)*['"]?[^'"|;&]*\\.gm[\\\\/](?:exec-spool|spool)[\\\\/]/i,
350
- /\\b(?:test|Test-Path|tp)\\s+(?:-[A-Za-z]+\\s+)?['"]?[^'"|;&]*\\.gm[\\\\/](?:exec-spool|spool)[\\\\/]/i,
351
- /\\bfind\\b[^|]*['"]?[^'"|;&]*\\.gm[\\\\/](?:exec-spool|spool)\\b/i,
352
- /\\b(?:stat|file|wc|Get-Item|gi|Get-Acl|Resolve-Path|Select-String|sls)\\b[^|]*['"]?[^'"|;&]*\\.gm[\\\\/](?:exec-spool|spool)[\\\\/]?/i,
353
- /\\b(?:xargs|parallel|fzf)\\b[^|]*\\.gm[\\\\/](?:exec-spool|spool)/i,
354
- ];
355
-
356
- const SPOOL_POLL_REASON = 'spool polling and bash-reads of .gm/exec-spool/ are forbidden — plugkit is synchronous from your view, and the Read tool is the canonical way to inspect spool files. Specific replacements:\\n\\n- Instead of \`cat .gm/exec-spool/.status.json\` → use the Read tool: \`Read .gm/exec-spool/.status.json\`\\n- Instead of \`ls .gm/exec-spool/out/\` → check the specific response file you wrote, e.g. \`Read .gm/exec-spool/out/<verb>-<N>.json\`\\n- Instead of \`cat .gm/exec-spool/.watcher.log\` → use the Read tool with offset for tailing\\n- Instead of \`sleep N; cat .gm/exec-spool/<...>\` → just Read the response file directly; if it doesn\\'t exist yet, the watcher is dead (Read .gm/exec-spool/.status.json — fresh ts means alive) or the verb is slow (Read .gm/exec-spool/.watcher.log for the dispatch trace)\\n\\nYou are the state machine. Plugkit serves the response the moment you write the request file. If you find yourself thinking "let me just check whether the file is there yet" — use Read. If you find yourself thinking "the watcher might have died" — Read .gm/exec-spool/.status.json. Bash on .gm/exec-spool/ is wrong every single time.';
357
-
358
- function stripHeredocsAndStringLiterals(command) {
359
- let s = String(command);
360
- s = s.replace(/<<-?\\s*'([A-Z_]+)'[\\s\\S]*?\\n\\1/g, '');
361
- s = s.replace(/<<-?\\s*"?([A-Z_]+)"?[\\s\\S]*?\\n\\1/g, '');
362
- s = s.replace(/\\$\\(cat\\s+<<-?\\s*'?([A-Z_]+)'?[\\s\\S]*?\\n\\1\\s*\\)/g, '');
363
- s = s.replace(/-m\\s+(['"])(?:\\\\.|(?!\\1)[^\\\\])*\\1/g, '-m STR');
364
- s = s.replace(/--message[= ]+(['"])(?:\\\\.|(?!\\1)[^\\\\])*\\1/g, '--message STR');
365
- return s;
366
- }
367
-
368
- function isSpoolPollCommand(command) {
369
- if (!command) return null;
370
- const stripped = stripHeredocsAndStringLiterals(command);
371
- for (const re of SPOOL_POLL_PATTERNS) {
372
- if (re.test(stripped)) return re.source;
373
- }
374
- return null;
375
- }
376
-
377
- function isBrowserRunningFileLocal(rel) {
378
- if (!rel) return false;
379
- const norm = String(rel).replace(/\\\\/g, '/');
380
- if (/\\.(html?|tsx|jsx|vue|svelte)$/i.test(norm)) return true;
381
- if (/\\.(mjs|cjs|js|ts|css|scss|sass)$/i.test(norm) && /^(src|public|site|app|pages|components|client|web)\\//i.test(norm)) return true;
382
- return false;
383
- }
384
-
385
- function recordBrowserEditLocal(cwd, filePath) {
386
- try {
387
- const fs = require('fs');
388
- const path = require('path');
389
- let rel = filePath;
390
- try { rel = path.relative(cwd, filePath); } catch (_) {}
391
- if (!isBrowserRunningFileLocal(rel)) return false;
392
- const editsFile = path.join(cwd, '.gm', 'exec-spool', '.turn-browser-edits.json');
393
- fs.mkdirSync(path.dirname(editsFile), { recursive: true });
394
- let list = [];
395
- try { list = JSON.parse(fs.readFileSync(editsFile, 'utf8')); if (!Array.isArray(list)) list = []; } catch (_) {}
396
- const entry = { file: rel.replace(/\\\\/g, '/'), ts: Date.now() };
397
- if (!list.some(e => e && e.file === entry.file)) list.push(entry);
398
- fs.writeFileSync(editsFile, JSON.stringify(list));
399
- return true;
400
- } catch (_) { return false; }
401
- }
402
-
403
- let raw = '';
404
- process.stdin.setEncoding('utf8');
405
- process.stdin.on('data', (chunk) => { raw += chunk; });
406
- process.stdin.on('end', () => {
407
- let event = {};
408
- try { event = JSON.parse(raw || '{}'); } catch (_) { event = {}; }
409
- const tool = event.tool_name || event.tool || '';
410
- const input = event.tool_input || event.input || {};
411
- const cwd = event.cwd || process.env.CLAUDE_PROJECT_DIR || process.cwd();
412
-
413
- if (tool === 'Write' || tool === 'Edit' || tool === 'MultiEdit') {
414
- const fp = input.file_path || input.filePath || input.path || '';
415
- if (fp) {
416
- try { recordBrowserEditLocal(cwd, fp); } catch (_) {}
417
- }
418
- process.stdout.write(JSON.stringify({ continue: true }));
419
- process.exit(0);
420
- }
421
-
422
- if (tool !== 'Bash') {
423
- process.stdout.write(JSON.stringify({ continue: true }));
424
- process.exit(0);
425
- }
426
- const command = input.command || input.cmd || '';
427
- const pattern = isSpoolPollCommand(command);
428
- if (!pattern) {
429
- process.stdout.write(JSON.stringify({ continue: true }));
430
- process.exit(0);
431
- }
432
- try {
433
- const fs = require('fs');
434
- const path = require('path');
435
- const os = require('os');
436
- const day = new Date().toISOString().slice(0, 10);
437
- const dir = path.join(process.env.GM_LOG_DIR || path.join(os.homedir(), '.claude', 'gm-log'), day);
438
- fs.mkdirSync(dir, { recursive: true });
439
- fs.appendFileSync(path.join(dir, 'hook.jsonl'), JSON.stringify({
440
- ts: new Date().toISOString(),
441
- sub: 'hook',
442
- event: 'deviation.spool-poll',
443
- pid: process.pid,
444
- sess: event.session_id || process.env.CLAUDE_SESSION_ID || process.env.GM_SESSION_ID || '',
445
- cwd: process.cwd(),
446
- operation: 'bash',
447
- pattern,
448
- command_excerpt: String(command).slice(0, 200),
449
- via: 'pre-tool-use-hook',
450
- }) + '\\n');
451
- } catch (_) {}
452
- process.stdout.write(JSON.stringify({
453
- decision: 'block',
454
- reason: SPOOL_POLL_REASON,
455
- }));
456
- process.exit(2);
457
- });
458
- `;
459
- }
460
-
461
- function ensureSpoolPollGate(cwd) {
462
- try {
463
- const gmHooks = path.join(cwd, '.gm', 'hooks');
464
- fs.mkdirSync(gmHooks, { recursive: true });
465
- const gateScript = path.join(gmHooks, 'spool-poll-gate.js');
466
- const want = spoolPollGateScript();
467
- let need = true;
468
- let oldSha = '';
469
- let existed = false;
470
- try {
471
- const existing = fs.readFileSync(gateScript, 'utf8');
472
- existed = true;
473
- if (existing === want) need = false;
474
- else {
475
- try {
476
- const _crypto = require('crypto');
477
- oldSha = _crypto.createHash('sha256').update(existing).digest('hex').slice(0, 12);
478
- } catch (_) {}
479
- }
480
- } catch (_) {}
481
- if (need) {
482
- fs.writeFileSync(gateScript, want);
483
- try {
484
- const _crypto = require('crypto');
485
- const newSha = _crypto.createHash('sha256').update(want).digest('hex').slice(0, 12);
486
- emitBootstrapEvent('info', existed ? 'gate.refreshed' : 'gate.installed', {
487
- cwd,
488
- path: gateScript,
489
- old_sha: oldSha || null,
490
- new_sha: newSha,
491
- bytes: Buffer.byteLength(want, 'utf8'),
492
- });
493
- } catch (_) {}
494
- }
495
-
496
- const claudeDir = path.join(cwd, '.claude');
497
- fs.mkdirSync(claudeDir, { recursive: true });
498
- const settingsPath = path.join(claudeDir, 'settings.json');
499
- let settings = {};
500
- try {
501
- const raw = fs.readFileSync(settingsPath, 'utf8');
502
- settings = JSON.parse(raw || '{}');
503
- } catch (_) { settings = {}; }
504
- if (!settings.hooks || typeof settings.hooks !== 'object') settings.hooks = {};
505
- if (!Array.isArray(settings.hooks.PreToolUse)) settings.hooks.PreToolUse = [];
506
- const wantCommand = `node "\${CLAUDE_PROJECT_DIR}/.gm/hooks/spool-poll-gate.js"`;
507
- let anyAlready = true;
508
- for (const matcher of ['Bash', 'Write', 'Edit', 'MultiEdit']) {
509
- let entry = settings.hooks.PreToolUse.find(e => e && e.matcher === matcher);
510
- if (!entry) {
511
- entry = { matcher, hooks: [] };
512
- settings.hooks.PreToolUse.push(entry);
513
- }
514
- if (!Array.isArray(entry.hooks)) entry.hooks = [];
515
- const already = entry.hooks.some(h => h && typeof h.command === 'string' && h.command.includes('spool-poll-gate.js'));
516
- if (!already) {
517
- entry.hooks.push({ type: 'command', command: wantCommand });
518
- anyAlready = false;
519
- }
520
- }
521
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
522
- emitBootstrapEvent('info', 'Spool-poll gate ensured', { gateScript, settingsPath, hookAlreadyPresent: anyAlready });
523
- } catch (e) {
524
- emitBootstrapEvent('warn', 'ensureSpoolPollGate failed', { error: e.message });
525
- }
526
- }
527
331
 
528
332
  function writeSessionSidecar(sessionId) {
529
333
  try {
@@ -778,9 +582,7 @@ async function bootstrapPlugkit(sessionId, options) {
778
582
  emitBootstrapEvent('info', 'Bootstrap started', { forceLatest });
779
583
 
780
584
  writeSessionSidecar(sessionId);
781
- ensureManagedGitignore(process.cwd());
782
585
  ensureBuildToolIgnores(process.cwd());
783
- ensureSpoolPollGate(process.cwd());
784
586
 
785
587
  const manifest = readManifest();
786
588
  let targetVersion = manifest.version;
@@ -954,6 +756,4 @@ module.exports = {
954
756
  ensureBuildToolIgnores,
955
757
  detectBuildToolConfigs,
956
758
  ensureManagedGitignore,
957
- ensureSpoolPollGate,
958
- spoolPollGateScript,
959
759
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1254",
3
+ "version": "2.0.1256",
4
4
  "description": "Canonical universal harness — AI-native software engineering via skill-driven orchestration; bootstraps plugkit for task execution and session isolation. Install in any AI coding agent host.",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -39,7 +39,7 @@
39
39
  "gm.json"
40
40
  ],
41
41
  "dependencies": {
42
- "gm-plugkit": "^2.0.1254"
42
+ "gm-plugkit": "^2.0.1256"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=16.0.0"