gm-skill 2.0.2575 → 2.0.2576

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/AGENTS.md CHANGED
@@ -47,7 +47,7 @@ Wasm host-import link-module rule (`#[link(wasm_import_module="env")]` on every
47
47
  Dispatch = Write `.gm/exec-spool/in/<verb>/<N>.txt`, Read `.gm/exec-spool/out/<verb>-<N>.json` (nested) or `out/<N>.json` (root). `<N>` is session-id-prefixed (`<session_id>-<local-counter>`), never a bare small integer -- the shared daemon claims and answers in-files by the literal `(verb, N)` pair with no per-session partition, so unprefixed sequential ids collide across concurrent sessions on the same project (recall: `gm spool dispatch id collision cross-session crosstalk`). Wasm orchestrator services every verb; harness never executes side effects directly.
48
48
 
49
49
  - **Orchestrator verbs**: `instruction`, `transition`, `phase-status`, `mutable-resolve`, `mutable-add`, `mutable-list`, `memorize-fire`, `memorize-continue`, `discipline-note`, `residual-scan`, `auto-recall`, `prd-add`, `prd-resolve`, `prd-list`, `task-spawn`, `task-list`, `task-stop`, `task-output`, `fsm-vendor`, `fsm-validate`, `fsm-propose-override`, `claim-audit`, `submodule-check`.
50
- - **`fsm-propose-override`**: session-authored `.gm/instructions` override, gated two-phase (`{proposed:true,requires_confirmation:true}` first, real write only on a second dispatch carrying `user_confirmed:true`). `kind:"graph"` additionally requires `confirmation_witness` citing an `AskUserQuestion` dispatch -- graph overrides grant execution authority (hooks) per Section 4. Body: `{kind:"prose"|"graph"|"config", key, proposed_text, reason, witness}`. Validates the proposed graph/config before ever proposing it; a `stuck-loop-escalation` gate denial also emits `self-reconfig-candidate` as the machine-readable trigger for when a proposal is worth authoring.
50
+ - **`fsm-propose-override`**: session-authored `.gm/instructions` override, gated two-phase (`{proposed:true,requires_confirmation:true}` first, real write only on a second dispatch carrying `user_confirmed:true`). `kind:"graph"` additionally requires `confirmation_witness` citing an `AskUserQuestion` dispatch -- graph overrides grant execution authority (hooks) per Section 4. Body: `{kind:"prose"|"graph"|"config", key, proposed_text, reason, witness}`. Validates the proposed graph/config before ever proposing it; a `stuck-loop-escalation` gate denial also emits `self-reconfig-candidate` as the machine-readable trigger for when a proposal is worth authoring. Its `kind:"prose"` content-shape rule (attributed anchors, mermaid backreference graphs, no external catalog citation) is served content, not local doc -- it lives in `gm-config/prose/entry.md` (every project reaches it), never here alone.
51
51
  - **Wasm-direct verbs**: fs/kv/exec/fetch/env, recall, codesearch, memorize, memorize-prune, memorize-vacuum, memorize-retention, health, filter, full git verb family. Enumeration in the recall store (`recall: wasm-direct plugkit verbs full list`).
52
52
  - **Host-native verb**: `background-convert` (`{verb, task}`) detaches an already-in-flight dispatch so the daemon worker stops waiting on it -- agent-initiated only, never routed to gm.wasm. Detail: `.gm/daemon-config-reference.md`.
53
53
  - **memorize-prune**: prune bad/superseded memories; two-mode spec (key-remove vs query-review) in the recall store (`recall: memorize-prune verb two-mode spec`). **memorize-vacuum**/**memorize-retention**: compaction and retention-policy enforcement over the same memory store; both also accept underscore-form aliases (`memorize_vacuum`/`memorize_retention`), matching every `memorize-*` verb's hyphen/underscore dual registration.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.2575",
3
+ "version": "2.0.2576",
4
4
  "description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform wasm, verifies SHA256, and launches agentplug-runner (the native wasm host) as the spool watcher daemon.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.2575",
3
+ "version": "2.0.2576",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.2575",
3
+ "version": "2.0.2576",
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",
@@ -125,6 +125,13 @@ const SUSPICIOUS_UNICODE = [
125
125
  { cp: 0x0421, name: 'CYRILLIC CAPITAL С (looks like Latin C)' },
126
126
  { cp: 0x0441, name: 'CYRILLIC SMALL с (looks like Latin c)' },
127
127
  ]
128
+ const SUSPICIOUS_UNICODE_MAP = new Map(SUSPICIOUS_UNICODE.map(u => [u.cp, u.name]))
129
+ // Single regex alternation, scanned natively by the engine in one pass --
130
+ // the previous per-character for-loop with an inner .find() was O(n) work
131
+ // PER CHARACTER (n = codepoint list length) and made a multi-MB minified
132
+ // bundle file (a real case: a 3.3MB single-line webpack chunk) take minutes
133
+ // instead of milliseconds.
134
+ const SUSPICIOUS_UNICODE_RE = new RegExp('[' + SUSPICIOUS_UNICODE.map(u => '\\u' + u.cp.toString(16).padStart(4, '0')).join('') + ']', 'g')
128
135
 
129
136
  // A file whose non-ASCII payload is almost entirely \uXXXX-style JS escape
130
137
  // sequences decoding to plain ASCII is itself a tell (deliberate obfuscation
@@ -205,25 +212,39 @@ function scanFile(filePath) {
205
212
  }
206
213
 
207
214
  if (CODE_EXT.has(path.extname(filePath))) {
208
- // De-duplicate per (codepoint, line): a run of several confusables on one
209
- // line (a whole disguised word) is one finding, not one per character.
210
- const seen = new Set()
211
- for (let i = 0; i < text.length; i++) {
212
- const cp = text.codePointAt(i)
213
- const hit = SUSPICIOUS_UNICODE.find(u => u.cp === cp)
214
- if (hit) {
215
- const line = text.slice(0, i).split('\n').length
215
+ const matches = text.match(SUSPICIOUS_UNICODE_RE)
216
+ if (matches && matches.length) {
217
+ // Line numbers computed via one pass over newline offsets (not a fresh
218
+ // split() per hit) -- still correct even on a huge single-line file,
219
+ // just no longer quadratic.
220
+ let newlineOffsets = null
221
+ const lineOf = (idx) => {
222
+ if (!newlineOffsets) {
223
+ newlineOffsets = []
224
+ for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) newlineOffsets.push(i)
225
+ }
226
+ let lo = 0, hi = newlineOffsets.length
227
+ while (lo < hi) { const mid = (lo + hi) >> 1; if (newlineOffsets[mid] < idx) lo = mid + 1; else hi = mid }
228
+ return lo + 1
229
+ }
230
+ const seen = new Set()
231
+ SUSPICIOUS_UNICODE_RE.lastIndex = 0
232
+ let m
233
+ while ((m = SUSPICIOUS_UNICODE_RE.exec(text)) !== null) {
234
+ const cp = m[0].codePointAt(0)
235
+ const line = lineOf(m.index)
216
236
  const key = cp + ':' + line
217
237
  if (seen.has(key)) continue
218
238
  seen.add(key)
219
239
  findings.push({
220
240
  filePath,
221
241
  kind: 'unicode',
222
- name: hit.name,
242
+ name: SUSPICIOUS_UNICODE_MAP.get(cp),
223
243
  codepoint: '0x' + cp.toString(16),
224
244
  line,
225
245
  why: 'invisible or confusable Unicode codepoint inside a code file — used to hide code from visual review or disguise an identifier/URL as something else',
226
246
  })
247
+ if (SUSPICIOUS_UNICODE_RE.lastIndex === m.index) SUSPICIOUS_UNICODE_RE.lastIndex++
227
248
  }
228
249
  }
229
250
  }