@ccoalm/ccl-skills 0.6.2 → 0.8.0

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 (139) hide show
  1. package/README.md +2 -2
  2. package/dist/assets/marketplace/plugins/ccl-skills/hooks/hooks.json +11 -0
  3. package/dist/assets/marketplace/plugins/ccl-skills/hooks/remind-unverified-cli-flag.sh +309 -0
  4. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_remind_unverified_cli_flag.sh +483 -0
  5. package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts +5 -0
  6. package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/SKILL.md +10 -8
  7. package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/references/mobile-quality-release.md +1 -1
  8. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/SKILL.md +16 -17
  9. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/client-routing.md +1 -1
  10. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +195 -7
  11. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/timeout-auth-and-capabilities.md +3 -3
  12. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/claude_review.sh +13 -5
  13. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/codex_review.sh +9 -3
  14. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/kimi_review.sh +9 -3
  15. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/normalize_review_timeout.sh +22 -0
  16. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/opencode_review.sh +9 -3
  17. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +1540 -129
  18. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_claude_review_probe.sh +8 -3
  19. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_client_compat.py +76 -1
  20. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh +1858 -3
  21. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_update_review_plan_intent.sh +789 -0
  22. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/update_review_plan_intent.py +513 -0
  23. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/SKILL.md +1 -1
  24. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/architecture-playbook.md +2 -0
  25. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/data-platform-architecture.md +1 -1
  26. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/event-driven-architecture.md +14 -11
  27. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/multi-tenant-isolation.md +2 -2
  28. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/SKILL.md +5 -1
  29. package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/SKILL.md +2 -1
  30. package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/SKILL.md +13 -11
  31. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/SKILL.md +64 -0
  32. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/agents/openai.yaml +4 -0
  33. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/async-lifecycle-and-performance.md +72 -0
  34. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/runtime-and-project-contract.md +58 -0
  35. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/source-map.md +41 -0
  36. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/verification-diagnostics-and-security.md +63 -0
  37. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/SKILL.md +1 -1
  38. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/sli-slo-design.md +25 -9
  39. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/source-register.md +1 -0
  40. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/SKILL.md +1 -1
  41. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/promotion-gate-and-review.md +16 -0
  42. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/secret-and-config-management.md +7 -0
  43. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/references/retry-timeout-circuit-breaker.md +11 -0
  44. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +8 -10
  45. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/design-routing-and-readiness.md +10 -14
  46. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/verify-developer-experience.md +1 -1
  47. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/SKILL.md +135 -86
  48. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/behavioral-aesthetic-logic.md +66 -80
  49. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/delivery-contract.md +275 -0
  50. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-execution-checklist.md +88 -214
  51. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-impl-naming-and-versioning.md +2 -2
  52. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-intake-and-acceptance.md +10 -8
  53. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-system-source-of-truth.md +4 -5
  54. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/external-ui-ux-quality-benchmarks.md +112 -95
  55. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/frontend-code-evidence-map.md +30 -21
  56. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/interaction-design-patterns.md +22 -3
  57. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/layout-recipes-and-screenshot-acceptance.md +20 -17
  58. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/multi-project-token-consistency.md +7 -9
  59. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/multi-stack-strategy.md +14 -10
  60. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/operational-processing-workflows.md +2 -0
  61. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/platform-mobile-patterns.md +1 -1
  62. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/product-lifecycle-acceptance-and-iteration.md +9 -6
  63. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/product-surface-patterns.md +3 -0
  64. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/source-map.md +37 -10
  65. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/tokens-and-components.md +7 -1
  66. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-audit.md +8 -5
  67. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-design-development.md +16 -5
  68. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/visual-craft.md +4 -2
  69. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/SKILL.md +5 -1
  70. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/architecture-playbook.md +1 -1
  71. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/audit-history-architecture.md +31 -0
  72. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/data-platform-architecture.md +1 -1
  73. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/event-driven-architecture.md +7 -4
  74. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/multi-tenant-isolation.md +2 -2
  75. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/notification-architecture.md +28 -0
  76. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/packaging-runtime-readiness.md +1 -1
  77. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/replay-comparison-architecture.md +28 -0
  78. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/workflow-state-architecture.md +39 -0
  79. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/SKILL.md +10 -7
  80. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/ai-service-wiring-patterns.md +8 -0
  81. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/audit-history-patterns.md +29 -0
  82. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/background-job-patterns.md +16 -0
  83. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/batch-and-artifact-patterns.md +25 -1
  84. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/notification-patterns.md +40 -0
  85. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/public-api-security-patterns.md +1 -1
  86. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/replay-comparison-patterns.md +30 -0
  87. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/state-machine-task-patterns.md +48 -0
  88. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/testing-and-quality-patterns.md +10 -1
  89. package/dist/assets/marketplace/plugins/ccl-skills/skills/release-coordination/SKILL.md +2 -0
  90. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +4 -4
  91. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/coverage-exhaustion-traps.md +45 -0
  92. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +142 -4
  93. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/external-practice-controls.md +21 -2
  94. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +11 -9
  95. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/firing-point-placement.md +8 -0
  96. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/parallel-stack-references-pattern.md +5 -4
  97. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/r0-leakage-audit.md +102 -0
  98. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +69 -0
  99. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +10 -0
  100. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/uiux-judgment-extraction.md +6 -6
  101. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +4 -3
  102. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-ccl-skills.sh +93 -2
  103. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-parallel-stack-parity.sh +119 -0
  104. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +22 -0
  105. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/impact-chain-gate.rb +49 -4
  106. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/obligation-ledger.py +2748 -0
  107. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/register-firing-path-resolution.rb +20 -5
  108. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/shared_git_surface_gate.py +1142 -0
  109. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_parallel_stack_parity.sh +183 -0
  110. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +19 -0
  111. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_skill_catalog.sh +41 -4
  112. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_ci_checkout_ref_binding.sh +120 -0
  113. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_entrypoint_domain_scan_terms.sh +82 -8
  114. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +336 -0
  115. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_self_adjudication.sh +82 -10
  116. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_obligation_ledger.sh +1416 -0
  117. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_obligation_ledger_repo_audit.sh +57 -0
  118. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_register_firing_path_wiring.sh +141 -4
  119. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_routing_pointer_integrity.sh +3 -1
  120. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_shared_git_surface_gate.sh +1696 -0
  121. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_uiux_delivery_contract.sh +2117 -0
  122. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_uiux_loading_budget.sh +316 -0
  123. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +1176 -0
  124. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_skill_cross_refs.sh +31 -1
  125. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate-skill.sh +9 -4
  126. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +980 -0
  127. package/dist/assets/marketplace/plugins/ccl-skills/skills/terminal-cli-dev/SKILL.md +9 -6
  128. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/SKILL.md +11 -11
  129. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/client-runtime-test-matrices.md +10 -2
  130. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/fitness-functions.md +16 -0
  131. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/scenario-testing.md +1 -1
  132. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-code-authoring-patterns.md +16 -5
  133. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/SKILL.md +5 -3
  134. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/delivery-face-closeout.md +16 -6
  135. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/self-benchmark-baseline.md +37 -0
  136. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/SKILL.md +7 -5
  137. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/complex-workspace-patterns.md +1 -1
  138. package/dist/assets/release.json +275 -105
  139. package/package.json +1 -1
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # @ccoalm/ccl-skills
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@ccoalm/ccl-skills)](https://www.npmjs.com/package/@ccoalm/ccl-skills)
4
- [![downloads](https://img.shields.io/npm/dm/@ccoalm/ccl-skills)](https://www.npmjs.com/package/@ccoalm/ccl-skills)
4
+ [![downloads](https://img.shields.io/npm/dt/@ccoalm/ccl-skills)](https://www.npmjs.com/package/@ccoalm/ccl-skills)
5
5
  [![license](https://img.shields.io/npm/l/@ccoalm/ccl-skills)](https://github.com/ccoalm/ccl-skills/blob/main/LICENSE)
6
6
 
7
7
  **Reusable workflows that help coding agents plan, build, test, review, and release software.**
8
8
 
9
- Not a prompt pack — a routed delivery system. 32 skills cover the whole lifecycle, and a routing layer reads what you asked for and hands it to the skill that owns that deliverable, so you never look one up.
9
+ Not a prompt pack — a routed delivery system. 33 skills cover the whole lifecycle, and a routing layer reads what you asked for and hands it to the skill that owns that deliverable, so you never look one up.
10
10
 
11
11
  Ask it to fix a bug and `defect-diagnosis` takes over: reproduce from first-hand failure evidence, isolate the cause, verify the fix, leave a regression test behind. Ask for a feature and `product-rd-workflow` routes it through requirement shaping, risk gates, implementation, and release. Ask it to touch code at all and `worktree-isolation` puts the work on its own branch first. Every skill also states when *not* to use it and which one to use instead — that is what keeps the routing sharp.
12
12
 
@@ -47,6 +47,17 @@
47
47
  }
48
48
  ]
49
49
  },
50
+ {
51
+ "matcher": "Bash",
52
+ "hooks": [
53
+ {
54
+ "type": "command",
55
+ "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/remind-unverified-cli-flag.sh\"",
56
+ "timeout": 10,
57
+ "async": false
58
+ }
59
+ ]
60
+ },
50
61
  {
51
62
  "matcher": "Task|Agent",
52
63
  "hooks": [
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env bash
2
+ # PreToolUse advisory — read the local CLI's own help before passing it a long
3
+ # flag (Bash tool only).
4
+ #
5
+ # WHY: distilled from 399 recorded agent failures across 122 sessions (round
6
+ # 059). "Flag guessed instead of read" accounts for ~20 of them, and the SAME
7
+ # flag — `glab mr list --state` — failed in EIGHT independent sessions spread
8
+ # over months. Every one of those eight was "fixed" the same way (read help, use
9
+ # the supported flag) and every fix only repaired that one instance, so the class
10
+ # came straight back. Same shape for `--jq` (3x), `--output`, `--pipeline-id`,
11
+ # `--branch`, `--old-text`, `--allow-scripts`.
12
+ #
13
+ # The rule already existed in prose, but only at two NARROW firing points:
14
+ # `worktree-isolation` (merge-authorization execution) and
15
+ # `skill-extraction-workflow` (verifying CLI examples written INTO a reference).
16
+ # Neither fires where the corpus actually fails: an agent about to run an
17
+ # ordinary inspection command. This hook is that missing firing point.
18
+ #
19
+ # DELIBERATELY DOES NOT PARSE THE COMMAND. An earlier version segmented on shell
20
+ # separators and handled heredocs, line continuations, the `--` terminator,
21
+ # launcher words, global options and hierarchical subcommands. Independent review
22
+ # found a new legal shell form it mishandled in essentially every round — fifteen
23
+ # of them — because the set of legal shell spellings is open-ended and a regex
24
+ # parser can only enumerate a prefix of it. This advisory does not need to know
25
+ # where the subcommand is or when a heredoc ends. It needs to know that a risky
26
+ # tool is being invoked with a long flag. So the predicate is: the tool name
27
+ # appears as a WORD, and some long flag appears. Nothing else is inspected.
28
+ #
29
+ # PREDICATE CHOICE (unchanged, and why TOOLS is a list of TOOLS): flag
30
+ # vocabularies are owned upstream and rot at every release, so the hook keys on
31
+ # the small, slow-moving set of tool NAMES. The same reasoning retired the
32
+ # parser: predicate on an invariant you own, never on someone else's vocabulary
33
+ # or grammar.
34
+ #
35
+ # NON-BLOCKING by design; NOT a security boundary (obfuscated invocations are out
36
+ # of scope, same declared class as the sibling hooks).
37
+ #
38
+ # ONE FIRE PER (session, tool): the first advisory for a tool in a session is
39
+ # enough of a nudge; the agent can check the whole command line from there. And
40
+ # within one Bash call only the FIRST listed tool is named — `glab ... && gh ...`
41
+ # yields one advisory, about glab. The reminder says to check the flags in this
42
+ # command, not just the named subcommand, so a second one would add nothing but
43
+ # noise; naming every tool would also mean deciding where each invocation begins,
44
+ # which is the parsing this hook exists without.
45
+ #
46
+ # ACCEPTED residuals — do NOT chase these with more string heuristics; chasing
47
+ # them is precisely what produced the fifteen-round parser:
48
+ # - FALSE FIRE: a command that merely MENTIONS a listed tool alongside a long
49
+ # flag (a heredoc body, a commit message whose metacharacters defeat the
50
+ # quote mask, a grep pattern) draws one advisory for that session. One extra
51
+ # line of context; the text says to ignore it if the flag is already known.
52
+ # - FALSE FIRE: a help invocation that is not the command's first word
53
+ # (`cd /x && glab mr list --help`) still draws the advisory.
54
+ # - NON-FIRE: a payload hidden inside a whitespace-bearing quoted string
55
+ # (`sh -c 'glab mr list --state opened'`). The quote mask exists to stop
56
+ # commit messages and grep patterns from firing, and it cannot tell those
57
+ # from a launcher payload without parsing the command — which is the thing
58
+ # this hook does not do. Accepted in the QUIET direction: a missed nudge, not
59
+ # a wrong one.
60
+ # - DUPLICATE FIRE: two Bash calls using the same tool that start concurrently
61
+ # can both pass the marker check before either append lands, so both advise.
62
+ # The dedup is best-effort by construction and a lock is not worth buying for
63
+ # a duplicated one-line nudge.
64
+ # - NON-FIRE: short flags, and any tool outside TOOLS. Extend the list only
65
+ # when a tool accumulates recorded flag failures.
66
+ # - NON-FIRE: payloads over the 1 MiB read cap are ignored entirely.
67
+ #
68
+ # Degrade semantics (per hooks/AGENTS.md): jq missing → emit nothing (exit 0);
69
+ # any internal issue → stay silent rather than disrupt the session.
70
+
71
+ set -f
72
+
73
+ # Bound the READ, not just the parse: this hook sits in front of every Bash call,
74
+ # so its cost must not scale with command size. Past the cap jq sees truncated
75
+ # JSON, yields nothing, and the hook exits silently.
76
+ input=$(head -c 1048576)
77
+
78
+ command -v jq >/dev/null 2>&1 || exit 0
79
+
80
+ cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty' 2>/dev/null)
81
+ [ -z "$cmd" ] && exit 0
82
+
83
+ # Mask whitespace-bearing quoted spans so an ordinary commit message or grep
84
+ # pattern mentioning a tool does not fire. Single-word quoted values keep their
85
+ # content. This is the only text transform the hook performs.
86
+ masked=$(printf '%s' "$cmd" | sed -E \
87
+ -e 's/"([^"[:space:];|&<>()]*)"/\1/g' \
88
+ -e "s/'([^'[:space:];|&<>()]*)'/\\1/g" \
89
+ -e 's/"[^"]*"/QUOTED/g' \
90
+ -e "s/'[^']*'/QUOTED/g")
91
+
92
+ TOOLS='glab|gh|lark-cli|uv'
93
+
94
+ # EVERY listed tool in the command, not just the first. Taking only the first
95
+ # meant a tool that always trails another — `glab ... && gh ...` — could never
96
+ # reach its own advisory once the leader was deduped, so it stayed silent for the
97
+ # whole session. The dedup step below picks the first of these not yet advised.
98
+ # Still no parsing: this is the set of tool words present, not their positions.
99
+ tools_present=$(printf '%s' "$masked" | grep -Eow "(${TOOLS})" | awk '!seen[$0]++')
100
+ [ -z "$tools_present" ] && exit 0
101
+ tool=$(printf '%s' "$tools_present" | head -1)
102
+
103
+ printf '%s' "$masked" | grep -Eq -- '(^|[[:space:]])--[a-zA-Z][a-zA-Z0-9-]+' || exit 0
104
+
105
+ # Suppress only the unambiguous case: the command's FIRST WORD is the tool and it
106
+ # is asking for help. Taking one token is not parsing the command; it keeps the
107
+ # hook from telling someone to run the help they are already running, without a
108
+ # whole-string help check. Suppression is additionally limited to commands with
109
+ # NO shell separator: in `glab mr list --state opened && git --help` the first
110
+ # word is still the tool, so a whole-command help grep swallowed the advisory for
111
+ # a genuine invocation. Testing for a separator is one character class, not a
112
+ # parse — a compound command simply never qualifies for suppression.
113
+ first=$(printf '%s' "$masked" | awk '{print $1; exit}')
114
+ # A NEWLINE separates commands just as `;` does, and grep is line-oriented so a
115
+ # character class can never see one — a two-line command read as "simple" and its
116
+ # second line's `--help` suppressed the advisory for the first line's invocation.
117
+ # A standalone `--` hands the rest of the line to another program, so a `--help`
118
+ # after it belongs to that program, not to this tool: `uv run --python 3.12 --
119
+ # python --help` still needs the advisory for `--python`.
120
+ sep_lines=$(printf '%s' "$masked" | grep -c '')
121
+ if [ "$first" = "$tool" ] \
122
+ && [ "$sep_lines" -le 1 ] \
123
+ && ! printf '%s' "$masked" | grep -Eq -- '(^|[[:space:]])--([[:space:]]|$)' \
124
+ && ! printf '%s' "$masked" | grep -q '[;|&]'; then
125
+ # `-h` / `--help` anywhere in this simple command, or a bare `help` only in the
126
+ # SUBCOMMAND slot. Accepting a bare `help` anywhere matched positional
127
+ # arguments — `gh api help --paginate` asks for an endpoint named `help`, not
128
+ # for help — and swallowed the advisory for a genuine flag.
129
+ second=$(printf '%s' "$masked" | awk '{print $2; exit}')
130
+ { printf '%s' "$masked" | grep -Eq -- '(^|[[:space:]])(-h|--help)([[:space:]]|$)' \
131
+ || [ "$second" = "help" ]; } && exit 0
132
+ fi
133
+
134
+ # Best-effort per-session dedup. Any obstacle → skip dedup and remind again;
135
+ # reminding twice is harmless, the failure modes below are not.
136
+ #
137
+ # HOSTILE-TMPDIR HARDENING (adversarial challenge; every attack below was
138
+ # reproduced first-hand before being fixed). A predictable marker path directly
139
+ # under TMPDIR let another user on a shared /tmp pre-create it as a SYMLINK (the
140
+ # append wrote into an arbitrary file), as a FIFO (the append blocked until the
141
+ # host killed the hook at its 10s timeout — a denial of service on the agent's
142
+ # own Bash tool), or as a HARD LINK (which passes -f, -O and the symlink test
143
+ # alike). Sanitizing the session id does not help; the path is guessable either
144
+ # way. So the marker lives inside a directory this hook creates mode 0700 and
145
+ # verifies it owns, where another user cannot plant anything.
146
+ #
147
+ # The append runs inside a subshell whose stderr is already redirected, which
148
+ # closes the ENTIRE failed-open class — unwritable file, immutable flag, full
149
+ # filesystem, a state change between the checks and the open. An explicit `-w`
150
+ # check was removed once mutation showed it changed nothing the subshell did not
151
+ # already cover; keeping it would also have hidden the subshell from every probe
152
+ # by short-circuiting first. Enumerating open-failure causes is the same mistake
153
+ # as enumerating shell forms.
154
+ #
155
+ # THREAT MODEL, stated so this stops being re-litigated one finding at a time.
156
+ # The marker is a temp file holding a tool name. A successful attack on it buys
157
+ # exactly one thing: getting this user's uid to append a fixed short string
158
+ # (`glab`) to a file the attacker picks but cannot write themselves, or making
159
+ # the advisory silent. It cannot execute anything, cannot read anything, and
160
+ # carries no attacker-chosen content. The precondition walk below is therefore
161
+ # sized to that payload: resolve the physical path once, refuse the whole
162
+ # mechanism where the path is not exclusively ours or root's, and otherwise stop.
163
+ # The ancestor check was rewritten three times as review found successive holes —
164
+ # mode bits only, then bits plus sticky, then ownership, then physical
165
+ # resolution — which is the same recurrence signal the parser produced. The
166
+ # convergent form is the ownership invariant plus a physical path; further
167
+ # hardening of a fixed-string append is not worth more rounds.
168
+ #
169
+ # UNVERIFIED PROPERTIES (labelled, not counted as audited). Both need a second
170
+ # user id to construct, so no portable probe here reaches either, and a mutation
171
+ # removing each one leaves the suite fully green:
172
+ # - the `-O` cross-uid rejection on the marker directory;
173
+ # - the third-party-ownership rejection in the ancestor walk (a directory owned
174
+ # by another non-root user, whose owner may rename its entries regardless of
175
+ # the mode bits).
176
+ # The suite covers the same-uid and no-sticky directions of both. Do not read its
177
+ # green as covering these; they rest on code inspection alone.
178
+ # The suite does carry a PRESENCE check for each of them — a grep proving the
179
+ # guard is still written — so deleting one silently turns the suite red even
180
+ # though its behavior stays unverified. Presence is not behavior; the check
181
+ # exists only so an unverified guard cannot also become an unnoticed one.
182
+ session=$(printf '%s' "$input" | jq -r '.session_id // empty' 2>/dev/null)
183
+ if [ -n "$session" ]; then
184
+ tmp_root="${TMPDIR:-/tmp}"
185
+ # PRECONDITION, because the TOCTOU below cannot be closed from bash. Every
186
+ # check on the marker directory is followed by a separate open of a path
187
+ # underneath it, and in a directory another user may write to they can rename
188
+ # the validated directory away and drop their own in its place between the two.
189
+ # Closing that needs a directory handle and openat-relative operations, which
190
+ # this language does not have. So the hook declines to dedup where dedup cannot
191
+ # be made safe, rather than pretending the window is shut: if the temp root is
192
+ # group- or world-writable and NOT sticky, skip the marker entirely and remind
193
+ # again. Sticky is what makes the usual shared /tmp safe — only the owner may
194
+ # rename or remove their entries there — and a per-user temp root is not
195
+ # other-writable at all, so this fires in neither normal case.
196
+ # Walk the whole ANCESTOR CHAIN, not just the temp root: a mode-0700 TMPDIR
197
+ # sitting under a world-writable non-sticky parent is swappable wholesale, so
198
+ # checking one level proves nothing about the path. Bounded by path depth, and
199
+ # `/` is never other-writable so it always terminates.
200
+ # Walk the PHYSICAL path. `dirname` on a logical path never resolves symlinks
201
+ # or `..`, so a relative TMPDIR or one whose target sits under a hostile
202
+ # ancestor walked a chain that does not exist on disk. Resolve once, up front.
203
+ # `cd -- ` because a TMPDIR of `-P` or `-L` is otherwise taken as a cd OPTION,
204
+ # leaving cd with no operand and landing in HOME — after which the ownership
205
+ # checks pass and the marker directory gets created there.
206
+ tmp_root=$(cd -- "$tmp_root" 2>/dev/null && pwd -P) || tmp_root=""
207
+ probe_dir="$tmp_root"
208
+ [ -z "$probe_dir" ] && probe_dir="/nonexistent"
209
+ walk_n=0
210
+ while : ; do
211
+ dir_perm=$(ls -ld "$probe_dir" 2>/dev/null | cut -c1-10)
212
+ if [ -z "$dir_perm" ]; then tmp_root=""; break; fi
213
+ # TWO independent ways an ancestor lets someone move the path out from under
214
+ # us, and the first is not about permission bits at all:
215
+ # (1) it is OWNED by a third party — a directory owner may rename entries
216
+ # in their own directory whatever the mode says, so 0755 owned by
217
+ # another non-root user is just as exploitable as 0777;
218
+ # (2) it is group/other-writable and NOT sticky — the classic shared-/tmp
219
+ # hole, where any writer may rename anyone's entry.
220
+ # Earlier versions checked only the mode bits and then only bits-plus-sticky,
221
+ # and independent review walked in through the gap each time. Ownership is
222
+ # the invariant; the bits are the secondary case.
223
+ dir_owner=$(ls -ld "$probe_dir" 2>/dev/null | awk '{print $3}')
224
+ if [ ! -O "$probe_dir" ] && [ "$dir_owner" != "root" ]; then
225
+ tmp_root=""; break
226
+ fi
227
+ if { [ "$(printf '%s' "$dir_perm" | cut -c6)" = "w" ] \
228
+ || [ "$(printf '%s' "$dir_perm" | cut -c9)" = "w" ]; } \
229
+ && [ ! -k "$probe_dir" ]; then
230
+ tmp_root=""; break
231
+ fi
232
+ # Bound the walk: a legitimate path can have hundreds of components and each
233
+ # iteration spawns several processes, in front of every Bash call. Real temp
234
+ # roots are shallow; past the bound, refuse rather than keep paying.
235
+ walk_n=$((walk_n + 1))
236
+ if [ "$walk_n" -gt 40 ]; then tmp_root=""; break; fi
237
+ # `--` for the same reason `cd` needed it: a path component that looks like
238
+ # an option is taken as one. Not reachable from a `pwd -P` result today, but
239
+ # the guard costs nothing and the omission is the exact shape already found
240
+ # once in this file.
241
+ parent_dir=$(dirname -- "$probe_dir")
242
+ [ "$parent_dir" = "$probe_dir" ] && break
243
+ probe_dir="$parent_dir"
244
+ done
245
+ marker_dir="${tmp_root}/ccl-skills-cliflag"
246
+ # The explicit chmod is load-bearing: `mkdir -p -m 700` applies its mode only
247
+ # when it CREATES the directory, so an already-present group/world-writable one
248
+ # keeps its permissions and leaves the swap window open.
249
+ if [ -n "$tmp_root" ] \
250
+ && mkdir -p -m 700 "$marker_dir" 2>/dev/null \
251
+ && [ -d "$marker_dir" ] && [ ! -L "$marker_dir" ] && [ -O "$marker_dir" ] \
252
+ && chmod 700 "$marker_dir" 2>/dev/null; then
253
+ # Bound the NAME: an over-long session id became an over-long filename, and
254
+ # `>>file 2>/dev/null` applies redirections left to right, so the failed open
255
+ # printed before the suppression took effect.
256
+ safe_session=$(printf '%s' "$session" | tr -c 'A-Za-z0-9._-' '_' | cut -c1-100)
257
+ marker="${marker_dir}/${safe_session}"
258
+ marker_links=1
259
+ if [ -e "$marker" ]; then
260
+ marker_links=$(ls -ld "$marker" 2>/dev/null | awk '{print $2}')
261
+ case "$marker_links" in ''|*[!0-9]*) marker_links=99 ;; esac
262
+ fi
263
+ if [ -L "$marker" ] || [ "$marker_links" -gt 1 ] \
264
+ || { [ -e "$marker" ] && { [ ! -f "$marker" ] || [ ! -O "$marker" ]; }; }; then
265
+ : # unexpected object — skip dedup entirely, do not open it
266
+ else
267
+ # SIZE-capped, and the story of why is worth keeping. This cap was added,
268
+ # then removed on the argument that the line-count bound below already
269
+ # prevented the append — an argument checked only against a MANY-LINE file,
270
+ # where it happens to hold. A single enormous LINE has a line count of 1,
271
+ # sails past that bound, gets appended to, and is re-scanned by both greps
272
+ # on every matching call. Varying one dimension of the input and concluding
273
+ # over all of them is the exact mistake the round's own material names.
274
+ # Anything past a few lines of tool names is not ours: skip it, unread.
275
+ marker_bytes=$(wc -c "$marker" 2>/dev/null | awk '{print $1; exit}')
276
+ case "$marker_bytes" in ''|*[!0-9]*) marker_bytes=0 ;; esac
277
+ if [ "$marker_bytes" -gt 4096 ]; then
278
+ # Oversized: neither read nor written. Guarding only the read left the
279
+ # append running, which is how a single enormous line kept growing.
280
+ :
281
+ else
282
+ # Advise the first tool in this command not yet recorded this session; if
283
+ # every one of them is recorded, stay quiet.
284
+ if [ -f "$marker" ]; then
285
+ tool=""
286
+ for candidate in $tools_present; do
287
+ grep -Fqx "$candidate" "$marker" 2>/dev/null || { tool="$candidate"; break; }
288
+ done
289
+ [ -z "$tool" ] && exit 0
290
+ fi
291
+ # `grep -c ''` on an EMPTY file prints 0 but EXITS 1, so trusting the
292
+ # exit code produced "0\n0" and a stderr diagnostic from the test below.
293
+ marker_lines=$(grep -c '' "$marker" 2>/dev/null | head -1)
294
+ case "$marker_lines" in ''|*[!0-9]*) marker_lines=0 ;; esac
295
+ if [ "$marker_lines" -lt 32 ]; then
296
+ ( printf '%s\n' "$tool" >>"$marker" ) 2>/dev/null || true
297
+ fi
298
+ fi
299
+ fi
300
+ fi
301
+ fi
302
+
303
+ advisory="🔎 CLI flag 核验提醒(本会话对 \`${tool}\` 只提示一次):\`${tool}\` 的 flag 词表**跨版本/跨平台差异很大**,凭记忆用 flag 是本仓记录中最高频的机械失败类之一——同一个 \`glab mr list --state\` 在 8 个独立会话里各踩一次,每次都只修了当次那一条。
304
+ 这条命令里的 flag 若**本会话还没在本机确认过**,先读一次对应子命令的 help 再用。
305
+ 已确认过就照常执行,忽略本提示。命令因无法识别的 flag 失败时,先怀疑这版不支持它,而不是先改用法之外的东西。"
306
+
307
+ jq -nc --arg r "$advisory" \
308
+ '{hookSpecificOutput:{hookEventName:"PreToolUse",additionalContext:$r}}'
309
+ exit 0