skillscript-runtime 0.5.0 → 0.7.2

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 (43) hide show
  1. package/README.md +105 -35
  2. package/dist/bootstrap.d.ts.map +1 -1
  3. package/dist/bootstrap.js +15 -0
  4. package/dist/bootstrap.js.map +1 -1
  5. package/dist/compile.d.ts.map +1 -1
  6. package/dist/compile.js +42 -2
  7. package/dist/compile.js.map +1 -1
  8. package/dist/connectors/config.d.ts.map +1 -1
  9. package/dist/connectors/config.js +10 -0
  10. package/dist/connectors/config.js.map +1 -1
  11. package/dist/connectors/local-model-mcp.d.ts +9 -0
  12. package/dist/connectors/local-model-mcp.d.ts.map +1 -0
  13. package/dist/connectors/local-model-mcp.js +73 -0
  14. package/dist/connectors/local-model-mcp.js.map +1 -0
  15. package/dist/connectors/memory-store-mcp.d.ts +9 -0
  16. package/dist/connectors/memory-store-mcp.d.ts.map +1 -0
  17. package/dist/connectors/memory-store-mcp.js +94 -0
  18. package/dist/connectors/memory-store-mcp.js.map +1 -0
  19. package/dist/help-content.d.ts.map +1 -1
  20. package/dist/help-content.js +316 -238
  21. package/dist/help-content.js.map +1 -1
  22. package/dist/lint.d.ts.map +1 -1
  23. package/dist/lint.js +296 -53
  24. package/dist/lint.js.map +1 -1
  25. package/dist/parser.d.ts +48 -3
  26. package/dist/parser.d.ts.map +1 -1
  27. package/dist/parser.js +377 -31
  28. package/dist/parser.js.map +1 -1
  29. package/dist/runtime.d.ts.map +1 -1
  30. package/dist/runtime.js +93 -16
  31. package/dist/runtime.js.map +1 -1
  32. package/examples/classify-support-ticket.skill.md +13 -13
  33. package/examples/cut-release-tag.skill.md +14 -14
  34. package/examples/doc-qa-with-citations.skill.md +3 -3
  35. package/examples/feedback-sentiment-scan.skill.md +13 -13
  36. package/examples/hello.skill.md +2 -2
  37. package/examples/hello.skill.provenance.json +1 -1
  38. package/examples/morning-brief.skill.md +6 -6
  39. package/examples/queue-length-monitor.skill.md +4 -4
  40. package/examples/service-health-watch.skill.md +7 -7
  41. package/examples/youtrack-morning-sweep.skill.md +4 -4
  42. package/package.json +1 -1
  43. package/scaffold/connectors.json +15 -13
@@ -1 +1 @@
1
- {"version":3,"file":"help-content.js","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"help-content.js","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"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"}
package/dist/lint.js CHANGED
@@ -658,7 +658,7 @@ const UNPARSED_JSON_FIELD_ACCESS = {
658
658
  remediation: "Replace with `$ json_parse $(VAR) -> P` then access `$(P.field)`. The op binds the parsed structure so dotted descent works in conditions + emit. See help({topic: \"ops\"}).",
659
659
  check: (ctx) => {
660
660
  const findings = [];
661
- const BAD = /\$\([^)]*\|\s*json_parse\s*\)\.([A-Za-z_]\w*)/;
661
+ const BAD = /\$(?:\([^)]*\|\s*json_parse\s*\)|\{[^}]*\|\s*json_parse\s*\})\.([A-Za-z_]\w*)/;
662
662
  const reportIfMatches = (text, targetName) => {
663
663
  const m = BAD.exec(text);
664
664
  if (m === null)
@@ -1073,8 +1073,8 @@ const MUTATING_TOOL_PATTERN = /^(?:write_|update_|delete_|remove_|set_|create_|i
1073
1073
  const UNCONFIRMED_MUTATION = {
1074
1074
  id: "unconfirmed-mutation",
1075
1075
  severity: "warning",
1076
- description: "A `$` op invokes a tool whose name suggests mutation (write/update/delete/...) without a preceding `??` confirmation step. Silent when the skill declares `# Autonomous: true` (v0.4.2).",
1077
- remediation: "Add a `??` confirmation op before the mutation, or declare `# Autonomous: true` at the skill header level when the skill is intentionally autonomous (cron-fired, agent-fired, etc.) and the user-confirmation pattern doesn't apply.",
1076
+ description: "A mutation-class op runs without author authorization. Mutation classes: `$ tool` with mutating-name shape (write/update/delete/...); `$ memory_write` MCP dispatch; `file_write(...)` function-call op. Silent when the skill declares `# Autonomous: true` (v0.4.2), when a preceding `??` / `ask(...)` confirmation gates the op, or (v0.7.0+) when the op carries `approved=\"reason\"` per-op authorization.",
1077
+ remediation: "Three ways to authorize: (1) add `# Autonomous: true` at the skill header for cron/agent-fired skills; (2) add a preceding `??` / `ask(prompt=\"...\")` confirmation op in the same target; (3) v0.7.0+: pass `approved=\"reason\"` kwarg on the mutation op itself (any non-empty string; presence is what matters, value not parsed semantically).",
1078
1078
  check: (ctx) => {
1079
1079
  // v0.4.2 — `# Autonomous: true` skills are unattended by design;
1080
1080
  // the user-confirmation pattern doesn't apply. Silent for the
@@ -1085,19 +1085,44 @@ const UNCONFIRMED_MUTATION = {
1085
1085
  for (const [targetName, target] of ctx.parsed.targets) {
1086
1086
  let sawConfirm = false;
1087
1087
  for (const op of target.ops) {
1088
+ // v0.7.0+: ask(prompt=...) parses to kind "??" in the AST, so the
1089
+ // existing check captures both legacy `??` and canonical `ask()`.
1088
1090
  if (op.kind === "??")
1089
1091
  sawConfirm = true;
1090
- if (op.kind === "$" && !sawConfirm) {
1092
+ if (sawConfirm)
1093
+ continue;
1094
+ // v0.7.0+: per-op `approved="reason"` kwarg authorizes individual ops
1095
+ // without requiring # Autonomous: true skill-level or `??` step.
1096
+ const isApproved = typeof op.approved === "string" && op.approved.length > 0;
1097
+ if (isApproved)
1098
+ continue;
1099
+ // Class 1: `$` MCP dispatch with mutating tool name.
1100
+ if (op.kind === "$") {
1091
1101
  const toolName = op.body.split(/\s+/)[0] ?? "";
1092
- if (MUTATING_TOOL_PATTERN.test(toolName)) {
1102
+ // memory_write is explicitly a mutation tool name — flag it even
1103
+ // though it doesn't start with `write_` (the pattern's anchor).
1104
+ const isMemoryWrite = toolName === "memory_write" || /(?:^|_)memory_write(?:_|$)/.test(toolName);
1105
+ if (MUTATING_TOOL_PATTERN.test(toolName) || isMemoryWrite) {
1093
1106
  findings.push({
1094
1107
  rule: "unconfirmed-mutation",
1095
1108
  severity: "warning",
1096
- message: `\`$\` op in target '${targetName}' invokes '${toolName}' (mutating shape) without a preceding \`??\` confirmation.`,
1109
+ message: `\`$\` op in target '${targetName}' invokes '${toolName}' (mutating shape) without authorization. Add \`approved="..."\` kwarg, precede with \`ask(...)\`, or declare \`# Autonomous: true\`.`,
1097
1110
  block: targetName,
1111
+ extras: { tool_name: toolName },
1098
1112
  });
1099
1113
  }
1100
1114
  }
1115
+ // Class 2: file_write runtime-intrinsic op (v0.7.0).
1116
+ if (op.kind === "file_write") {
1117
+ const path = op.fileParams?.path ?? "";
1118
+ findings.push({
1119
+ rule: "unconfirmed-mutation",
1120
+ severity: "warning",
1121
+ message: `\`file_write(path="${path}")\` in target '${targetName}' is a mutation op without authorization. Add \`approved="..."\` kwarg, precede with \`ask(...)\`, or declare \`# Autonomous: true\`.`,
1122
+ block: targetName,
1123
+ extras: { op_kind: "file_write", path },
1124
+ });
1125
+ }
1101
1126
  }
1102
1127
  }
1103
1128
  return findings;
@@ -1445,7 +1470,7 @@ const APPEND_TO_NON_LIST = {
1445
1470
  const findings = [];
1446
1471
  for (const [targetName, target] of ctx.parsed.targets) {
1447
1472
  walkOps(target.ops, (op) => {
1448
- if (op.kind === "$set" && op.setName !== undefined && op.setValue !== undefined && !/\$\(/.test(op.setValue)) {
1473
+ if (op.kind === "$set" && op.setName !== undefined && op.setValue !== undefined && !/\$[(\{]/.test(op.setValue)) {
1449
1474
  staticInits.set(op.setName, op.setValue);
1450
1475
  }
1451
1476
  });
@@ -1485,7 +1510,7 @@ function buildBindingOrigins(parsed) {
1485
1510
  // v0.5.0 item 3: $set RHS interpolates $(REF) at bind time. If the
1486
1511
  // RHS is a static literal (no $(REF)), record its value for the
1487
1512
  // whitespace check. If it contains $(REF), treat as suspect.
1488
- if (/\$\(/.test(op.setValue)) {
1513
+ if (/\$[(\{]/.test(op.setValue)) {
1489
1514
  origins.set(op.setName, { kind: "set-ref" });
1490
1515
  }
1491
1516
  else {
@@ -1530,51 +1555,84 @@ function isOriginSuspect(origin) {
1530
1555
  const UNQUOTED_SUBSTITUTION_IN_KWARG_VALUE = {
1531
1556
  id: "unquoted-substitution-in-kwarg-value",
1532
1557
  severity: "warning",
1533
- description: "A `$ tool key=$(VAR)` op kwarg has an unquoted `$(VAR)` substitution where VAR may resolve to a value containing whitespace. Runtime renders into `key=value with spaces` then re-tokenizes on whitespace — only the first chunk binds to `key`. Silent arg truncation.",
1534
- remediation: "Wrap the substitution in quotes: `key=\"$(VAR)\"`. The MCP arg tokenizer respects quoted regions, preventing the re-tokenization split.",
1558
+ description: "A `$ tool key=$(VAR)` op kwarg OR a legacy `@ cmd ... $(VAR)` shell arg has an unquoted `$(VAR)` / `${VAR}` substitution where VAR may resolve to a value containing whitespace. Runtime renders into `key=value with spaces` then re-tokenizes on whitespace — only the first chunk binds to `key` (MCP) or first arg (shell). Silent arg truncation. v0.7.2 extends coverage from `$` ops to `@` ops per R4 minion 4 finding.",
1559
+ remediation: "Wrap the substitution in quotes: `key=\"$(VAR)\"` for MCP kwargs, `\"$(VAR)\"` for shell args. The arg tokenizer respects quoted regions, preventing the re-tokenization split.",
1535
1560
  check: (ctx) => {
1536
1561
  const findings = [];
1537
1562
  const origins = buildBindingOrigins(ctx.parsed);
1538
1563
  const reported = new Set();
1564
+ // v0.7.2: shared pattern matches both legacy `$(VAR)` and canonical
1565
+ // `${VAR}` substitution forms. Only the opening delimiter + var-name
1566
+ // are required to match (no closing `)`/`}`) so filter chains like
1567
+ // `$(VAR|trim)` and `${VAR|filter:"x"}` parse cleanly. The capture
1568
+ // groups (1 = paren-form name, 2 = brace-form name) get coalesced.
1569
+ const subStPattern = /\$(?:\(([^|)\s]+)|\{([^|}\s]+))/;
1539
1570
  for (const [targetName, target] of ctx.parsed.targets) {
1540
1571
  walkOps(target.ops, (op) => {
1541
- if (op.kind !== "$")
1542
- return;
1543
- // Scan op.body for `key=$(VAR)` patterns where the $( is NOT
1544
- // immediately preceded by a quote character. Tokenize once, then
1545
- // inspect each token.
1546
- const tokens = tokenizeKeywordArgs(op.body);
1547
- for (const tok of tokens) {
1548
- const eq = tok.indexOf("=");
1549
- if (eq === -1)
1550
- continue;
1551
- const key = tok.slice(0, eq);
1552
- const value = tok.slice(eq + 1);
1553
- // Unquoted $(VAR) pattern: starts with $( (not " or '). May
1554
- // include filter chain and trailing characters.
1555
- if (!value.startsWith("$("))
1556
- continue;
1557
- // Extract the var name (everything up to | or ) ).
1558
- const m = /^\$\(([^|)\s]+)/.exec(value);
1559
- if (m === null)
1560
- continue;
1561
- const varName = m[1];
1562
- // Dotted access: trim to the root.
1563
- const rootVar = varName.split(".")[0];
1564
- const origin = origins.get(rootVar);
1565
- if (!isOriginSuspect(origin))
1566
- continue;
1567
- const key2 = `${targetName}:${key}:${varName}`;
1568
- if (reported.has(key2))
1569
- continue;
1570
- reported.add(key2);
1571
- findings.push({
1572
- rule: "unquoted-substitution-in-kwarg-value",
1573
- severity: "warning",
1574
- message: `\`$ ... ${key}=$(${varName})\` in target '${targetName}': unquoted substitution. ${describeOriginRisk(origin)} Wrap as \`${key}="$(${varName})"\` to prevent silent arg truncation if the value contains whitespace.`,
1575
- block: targetName,
1576
- extras: { kwarg: key, var_name: varName, origin: origin.kind },
1577
- });
1572
+ if (op.kind === "$") {
1573
+ // $ MCP dispatch — scan kwarg values for unquoted substitutions.
1574
+ const tokens = tokenizeKeywordArgs(op.body);
1575
+ for (const tok of tokens) {
1576
+ const eq = tok.indexOf("=");
1577
+ if (eq === -1)
1578
+ continue;
1579
+ const key = tok.slice(0, eq);
1580
+ const value = tok.slice(eq + 1);
1581
+ if (!(value.startsWith("$(") || value.startsWith("${")))
1582
+ continue;
1583
+ const m = subStPattern.exec(value);
1584
+ if (m === null)
1585
+ continue;
1586
+ const varName = (m[1] ?? m[2]);
1587
+ const rootVar = varName.split(".")[0];
1588
+ const origin = origins.get(rootVar);
1589
+ if (!isOriginSuspect(origin))
1590
+ continue;
1591
+ const dedupKey = `${targetName}:$:${key}:${varName}`;
1592
+ if (reported.has(dedupKey))
1593
+ continue;
1594
+ reported.add(dedupKey);
1595
+ findings.push({
1596
+ rule: "unquoted-substitution-in-kwarg-value",
1597
+ severity: "warning",
1598
+ message: `\`$ ... ${key}=\${${varName}}\` in target '${targetName}': unquoted substitution. ${describeOriginRisk(origin)} Wrap as \`${key}="\${${varName}}"\` to prevent silent arg truncation if the value contains whitespace.`,
1599
+ block: targetName,
1600
+ extras: { kwarg: key, var_name: varName, origin: origin.kind, op: "$" },
1601
+ });
1602
+ }
1603
+ }
1604
+ else if (op.kind === "@") {
1605
+ // v0.7.2 legacy @ shell op. Tokenize the body the same way the
1606
+ // runtime would (whitespace-separated, quotes respected), then
1607
+ // flag any token that is a bare unquoted substitution. Quoted
1608
+ // tokens (`"${VAR}"` / `'${VAR}'`) are safe.
1609
+ const tokens = tokenizeKeywordArgs(op.body);
1610
+ for (const tok of tokens) {
1611
+ // Skip quoted tokens — the quotes protect against whitespace split.
1612
+ if ((tok.startsWith('"') && tok.endsWith('"')) || (tok.startsWith("'") && tok.endsWith("'")))
1613
+ continue;
1614
+ if (!(tok.startsWith("$(") || tok.startsWith("${")))
1615
+ continue;
1616
+ const m = subStPattern.exec(tok);
1617
+ if (m === null)
1618
+ continue;
1619
+ const varName = (m[1] ?? m[2]);
1620
+ const rootVar = varName.split(".")[0];
1621
+ const origin = origins.get(rootVar);
1622
+ if (!isOriginSuspect(origin))
1623
+ continue;
1624
+ const dedupKey = `${targetName}:@:${varName}`;
1625
+ if (reported.has(dedupKey))
1626
+ continue;
1627
+ reported.add(dedupKey);
1628
+ findings.push({
1629
+ rule: "unquoted-substitution-in-kwarg-value",
1630
+ severity: "warning",
1631
+ message: `\`@ ... \${${varName}}\` shell arg in target '${targetName}': unquoted substitution. ${describeOriginRisk(origin)} Wrap as \`"\${${varName}}"\` to prevent silent word-splitting if the value contains whitespace.`,
1632
+ block: targetName,
1633
+ extras: { var_name: varName, origin: origin.kind, op: "@" },
1634
+ });
1635
+ }
1578
1636
  }
1579
1637
  });
1580
1638
  }
@@ -1595,6 +1653,183 @@ function describeOriginRisk(origin) {
1595
1653
  return `Variable is a \`foreach\` iterator — element type unknown statically.`;
1596
1654
  }
1597
1655
  }
1656
+ /**
1657
+ * v0.7.1 — tier-2 visibility nudge for legacy symbol-form ops (`~`, `>`,
1658
+ * `@`, `!`, `??`, `&`). The parser still accepts these during the v0.7.x
1659
+ * grace period; the runtime dispatches them as before. This rule surfaces
1660
+ * the canonical replacement so authors editing skills see the migration
1661
+ * path. Tier-1 promotion (refuse-to-compile) lands in v0.8 or v0.9 once
1662
+ * the adopter ecosystem confirms migration is settled.
1663
+ *
1664
+ * The `$ tool` op is NOT flagged — that shape is canonical (MCP dispatch
1665
+ * marker). Only the 6 symbol ops that became function-call ops in v0.7.0.
1666
+ */
1667
+ const DEPRECATED_SYMBOL_OP_REPLACEMENT = {
1668
+ // v0.7.2: bridge classes ship default-wired so `$ llm` / `$ memory` work
1669
+ // out of the box in default deployments. Suggestions are load-bearing
1670
+ // (no more "(or your wired connector name)" caveat).
1671
+ "~": "$ llm prompt=\"...\" [maxTokens=N] [model=\"...\"] -> R",
1672
+ ">": "$ memory mode=\"fts|semantic|rerank\" query=\"...\" limit=N -> R",
1673
+ "@": "shell(command=\"...\") [-> R]",
1674
+ "!": "emit(text=\"...\")",
1675
+ "??": "ask(prompt=\"...\") -> R",
1676
+ "&": "inline(skill=\"...\") (or execute_skill(skill_name=\"...\", ...) -> R for procedural composition)",
1677
+ };
1678
+ const DEPRECATED_SYMBOL_OP = {
1679
+ id: "deprecated-symbol-op",
1680
+ severity: "warning",
1681
+ description: "An op uses the legacy symbol form deprecated in v0.7.0.",
1682
+ remediation: "Rewrite to the canonical v0.7.0 form (see message). All legacy ops continue to compile during the grace period; tier-1 promotion (refuse-to-compile) lands in v0.8/v0.9. See CHANGELOG.md `## 0.7.0 — Migration` for the full rewrite rules.",
1683
+ check: (ctx) => {
1684
+ const findings = [];
1685
+ for (const [targetName, target] of ctx.parsed.targets) {
1686
+ const reported = new Set();
1687
+ walkOps(target.ops, (op) => {
1688
+ const replacement = DEPRECATED_SYMBOL_OP_REPLACEMENT[op.kind];
1689
+ if (replacement === undefined)
1690
+ return;
1691
+ // v0.7.1: skip ops authored in canonical function-call form. The
1692
+ // parser collapses both `! x` and `emit(text="x")` to kind "!",
1693
+ // so without the sourceForm marker the lint would fire on
1694
+ // canonical code. Source-form marker preserves which surface
1695
+ // the author wrote.
1696
+ if (op.sourceForm === "function-call")
1697
+ return;
1698
+ // Dedupe per-kind-per-target — one nudge per legacy op type per
1699
+ // target is plenty; further occurrences don't add signal.
1700
+ const key = `${targetName}:${op.kind}`;
1701
+ if (reported.has(key))
1702
+ return;
1703
+ reported.add(key);
1704
+ findings.push({
1705
+ rule: "deprecated-symbol-op",
1706
+ severity: "warning",
1707
+ message: `Op '${op.kind}' in target '${targetName}' is deprecated in v0.7.0. Rewrite as: \`${replacement}\``,
1708
+ block: targetName,
1709
+ extras: { legacy_op: op.kind, canonical_replacement: replacement },
1710
+ });
1711
+ });
1712
+ if (target.elseBlock !== undefined) {
1713
+ walkOps(target.elseBlock, (op) => {
1714
+ const replacement = DEPRECATED_SYMBOL_OP_REPLACEMENT[op.kind];
1715
+ if (replacement === undefined)
1716
+ return;
1717
+ if (op.sourceForm === "function-call")
1718
+ return;
1719
+ const key = `${targetName}:else:${op.kind}`;
1720
+ if (reported.has(key))
1721
+ return;
1722
+ reported.add(key);
1723
+ findings.push({
1724
+ rule: "deprecated-symbol-op",
1725
+ severity: "warning",
1726
+ message: `Op '${op.kind}' in target '${targetName}' (else block) is deprecated in v0.7.0. Rewrite as: \`${replacement}\``,
1727
+ block: targetName,
1728
+ extras: { legacy_op: op.kind, canonical_replacement: replacement },
1729
+ });
1730
+ });
1731
+ }
1732
+ }
1733
+ return findings;
1734
+ },
1735
+ };
1736
+ /**
1737
+ * v0.7.1 — tier-2 visibility nudge for legacy `$(VAR)` substitution form.
1738
+ * Canonical v0.7.0+ form is `${VAR}`. Parser/runtime accept both during
1739
+ * grace period. Dedupes per-var-per-target; one nudge per `$(VAR)` form
1740
+ * per scope.
1741
+ *
1742
+ * Skips the `$$(...)` escape (used in `@ unsafe` op bodies for shell
1743
+ * literal pass-through). Skips ops where the body is `$set` source
1744
+ * (because $set's RHS is its own substitution context and the lint would
1745
+ * double-fire).
1746
+ */
1747
+ const DEPRECATED_SUBSTITUTION_SHAPE = {
1748
+ id: "deprecated-substitution-shape",
1749
+ severity: "warning",
1750
+ description: "A `$(VAR)` substitution uses the legacy v0.6.x form deprecated in v0.7.0.",
1751
+ remediation: "Rewrite to `${VAR}` canonical form. Both forms produce identical results during the v0.7.x grace period; tier-1 promotion lands in v0.8/v0.9. The `$$(VAR)` escape (for `@ unsafe` shell literal pass-through) is unchanged.",
1752
+ check: (ctx) => {
1753
+ const findings = [];
1754
+ // Negative lookbehind blocks `$$(VAR)` escape form so the lint doesn't
1755
+ // fire on shell-escape sites authors deliberately wrote.
1756
+ const legacyRe = /(?<!\$)\$\(([^|)\s]+)/g;
1757
+ for (const [targetName, target] of ctx.parsed.targets) {
1758
+ const reported = new Set();
1759
+ const scanOp = (op, scope) => {
1760
+ const text = collectOpText(op);
1761
+ let m;
1762
+ while ((m = legacyRe.exec(text)) !== null) {
1763
+ const varName = m[1];
1764
+ const key = `${targetName}:${scope}:${varName}`;
1765
+ if (reported.has(key))
1766
+ continue;
1767
+ reported.add(key);
1768
+ findings.push({
1769
+ rule: "deprecated-substitution-shape",
1770
+ severity: "warning",
1771
+ message: `Substitution '$(${varName})' in target '${targetName}'${scope === "else" ? " (else block)" : ""} uses the legacy v0.6.x form. Rewrite as '\${${varName}}'.`,
1772
+ block: targetName,
1773
+ extras: { var_name: varName, legacy_form: `$(${varName})`, canonical_form: `\${${varName}}` },
1774
+ });
1775
+ }
1776
+ legacyRe.lastIndex = 0;
1777
+ };
1778
+ walkOps(target.ops, (op) => scanOp(op, "main"));
1779
+ if (target.elseBlock !== undefined) {
1780
+ walkOps(target.elseBlock, (op) => scanOp(op, "else"));
1781
+ }
1782
+ }
1783
+ return findings;
1784
+ },
1785
+ };
1786
+ /**
1787
+ * v0.7.2 — tier-3 advisory for the R4 cold-author footgun (4 of 5 minions).
1788
+ * `foreach IT in ${VAR}` where VAR's binding origin is a `$` MCP tool output
1789
+ * (and the iteration expression has no `.field` accessor). MCP tools commonly
1790
+ * wrap arrays in an envelope object (e.g., `{issuesPage: [...], hasNextPage}`,
1791
+ * `{items: [...]}`, `{results: [...]}`) — cold authors iterating the bare
1792
+ * bound var get silent stringification + a single-iteration loop with the
1793
+ * stringification as the iterator value. Downstream `${IT.field}` errors.
1794
+ *
1795
+ * Placeholder for the v0.8 tool-schema-introspection solution that catches
1796
+ * this precisely. Advisory hints at the common envelope-field names.
1797
+ */
1798
+ const OBJECT_ITERATION_ADVISORY = {
1799
+ id: "object-iteration-advisory",
1800
+ severity: "info",
1801
+ description: "A `foreach IT in ${VAR}` iterates a bound variable whose origin is a `$` MCP tool output, without a `.field` accessor. MCP tools commonly wrap arrays in an envelope.",
1802
+ remediation: "Check the tool's response shape — most MCP services wrap arrays under fields like `.items`, `.results`, `.issuesPage`, `.data`, `.records`. Rewrite as `foreach IT in ${VAR.items}` (or the correct field) once you know the shape. v0.8 tool-schema introspection will catch this precisely; today the advisory is a soft nudge.",
1803
+ check: (ctx) => {
1804
+ const findings = [];
1805
+ const origins = buildBindingOrigins(ctx.parsed);
1806
+ // Bare var ref pattern: `$(VAR)` or `${VAR}` — no dotted accessor, no filter chain.
1807
+ const bareRef = /^\s*\$(?:\(([A-Za-z_]\w*)\)|\{([A-Za-z_]\w*)\})\s*$/;
1808
+ for (const [targetName, target] of ctx.parsed.targets) {
1809
+ walkOps(target.ops, (op) => {
1810
+ if (op.kind !== "foreach" || op.foreachList === undefined)
1811
+ return;
1812
+ const m = bareRef.exec(op.foreachList);
1813
+ if (m === null)
1814
+ return;
1815
+ const varName = (m[1] ?? m[2]);
1816
+ const origin = origins.get(varName);
1817
+ if (origin === undefined)
1818
+ return;
1819
+ if (origin.kind !== "op-output" || origin.op !== "$")
1820
+ return;
1821
+ findings.push({
1822
+ rule: "object-iteration-advisory",
1823
+ severity: "info",
1824
+ message: `In target '${targetName}': \`foreach ${op.foreachIter} in \${${varName}}\` iterates a bare \`$\` op output without a \`.field\` accessor. Most MCP tools wrap arrays in an envelope (e.g., \`.items\`, \`.results\`, \`.issuesPage\`, \`.data\`). Check the tool's response shape; rewrite as \`foreach ${op.foreachIter} in \${${varName}.items}\` (or the actual array field) if so.`,
1825
+ block: targetName,
1826
+ extras: { var_name: varName, foreach_iter: op.foreachIter },
1827
+ });
1828
+ });
1829
+ }
1830
+ return findings;
1831
+ },
1832
+ };
1598
1833
  const RULES = [
1599
1834
  // Tier-1 (error)
1600
1835
  PARSE_ERROR,
@@ -1628,6 +1863,8 @@ const RULES = [
1628
1863
  MISSING_SKILLSTORE_FOR_DATA_REF,
1629
1864
  // Tier-2 (warning)
1630
1865
  DEPRECATED_QUESTION,
1866
+ DEPRECATED_SYMBOL_OP,
1867
+ DEPRECATED_SUBSTITUTION_SHAPE,
1631
1868
  UNSAFE_SHELL_AMBIGUOUS_SUBST,
1632
1869
  UNSAFE_SHELL_OP,
1633
1870
  UNSAFE_SHELL_DISABLED,
@@ -1642,6 +1879,7 @@ const RULES = [
1642
1879
  DUPLICATE_SKILL_NAME,
1643
1880
  PLUGIN_COLLISION,
1644
1881
  UNPARSED_JSON_FIELD_ACCESS,
1882
+ OBJECT_ITERATION_ADVISORY,
1645
1883
  ];
1646
1884
  /** Read-only view of the rule registry — for tooling that introspects v1 rules. */
1647
1885
  export function listRules() {
@@ -1692,14 +1930,17 @@ function extractVarRefs(op) {
1692
1930
  // suppressed from undeclared-var. The author has explicitly opted into
1693
1931
  // "may not resolve at runtime" semantics — making this a lint error
1694
1932
  // would defeat the purpose.
1695
- const re = /\$\(([^|)\s]+)((?:\s*\|\s*[A-Za-z_]\w*(?:\s*:\s*"[^"]*")?)*)\)/g;
1933
+ // v0.7.0: alternation matches both `$(REF|chain)` and `${REF|chain}`.
1934
+ // Capture groups: 1+2 = paren form, 3+4 = brace form.
1935
+ const re = /\$(?:\(([^|)\s]+)((?:\s*\|\s*[A-Za-z_]\w*(?:\s*:\s*"[^"]*")?)*)\)|\{([^|}\s]+)((?:\s*\|\s*[A-Za-z_]\w*(?:\s*:\s*"[^"]*")?)*)\})/g;
1696
1936
  const refs = [];
1697
1937
  let m;
1698
1938
  while ((m = re.exec(text)) !== null) {
1699
- const chain = m[2] ?? "";
1939
+ const name = m[1] ?? m[3];
1940
+ const chain = (m[2] ?? m[4]) ?? "";
1700
1941
  if (/\|\s*fallback(?:\s*:|[\s|)])/.test(chain))
1701
1942
  continue;
1702
- refs.push(m[1]);
1943
+ refs.push(name);
1703
1944
  }
1704
1945
  return refs;
1705
1946
  }
@@ -1709,12 +1950,14 @@ function extractVarRefsWithFilter(op) {
1709
1950
  // Multiple filters in a chain produce one entry per filter (preserves
1710
1951
  // the per-filter unknown-filter check that pre-existed for single-filter
1711
1952
  // refs).
1712
- const re = /\$\(([^|)\s]+)((?:\s*\|\s*[A-Za-z_]\w*(?:\s*:\s*"[^"]*")?)*)\)/g;
1953
+ // v0.7.0: alternation matches both `$(REF|chain)` and `${REF|chain}`.
1954
+ // Capture groups: 1+2 = paren form, 3+4 = brace form.
1955
+ const re = /\$(?:\(([^|)\s]+)((?:\s*\|\s*[A-Za-z_]\w*(?:\s*:\s*"[^"]*")?)*)\)|\{([^|}\s]+)((?:\s*\|\s*[A-Za-z_]\w*(?:\s*:\s*"[^"]*")?)*)\})/g;
1713
1956
  const out = [];
1714
1957
  let m;
1715
1958
  while ((m = re.exec(text)) !== null) {
1716
- const name = m[1];
1717
- const chain = m[2];
1959
+ const name = (m[1] ?? m[3]);
1960
+ const chain = (m[2] ?? m[4]) ?? "";
1718
1961
  if (!chain) {
1719
1962
  out.push({ name });
1720
1963
  continue;