@tech-leads-club/harness-toolkit 0.10.4 → 0.10.5
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/dist/compact-before.mjs +21 -21
- package/dist/doctor.mjs +23 -23
- package/dist/init-project.mjs +24 -24
- package/dist/install-runtime.mjs +21 -21
- package/dist/lessons-cli.mjs +21 -21
- package/dist/obs-cli.mjs +23 -23
- package/dist/prompt-submit.mjs +21 -21
- package/dist/refresh-model-prices.mjs +22 -22
- package/dist/response-after.mjs +21 -21
- package/dist/run.mjs +22 -22
- package/dist/session-end.mjs +23 -23
- package/dist/session-start.mjs +25 -25
- package/dist/shim.mjs +21 -21
- package/dist/ship-gate.mjs +22 -22
- package/dist/stop.mjs +25 -25
- package/dist/subagent-start.mjs +21 -21
- package/dist/subagent-stop.mjs +22 -22
- package/dist/support.mjs +23 -23
- package/dist/tlc-cli.mjs +37 -37
- package/dist/tool-after.mjs +22 -22
- package/dist/tool-before.mjs +23 -23
- package/dist/tool-failure.mjs +36 -36
- package/docs/concepts.md +1 -1
- package/docs/log.md +1 -0
- package/package.json +1 -1
- package/src/core/rules/rules.trigger.ts +19 -1
package/docs/concepts.md
CHANGED
|
@@ -353,7 +353,7 @@ most common way a new rule reads as protection and is not:
|
|
|
353
353
|
| proof | what it names | how it matches |
|
|
354
354
|
|---|---|---|
|
|
355
355
|
| `subagent(<type>)` | the *declared type* of a spawn that finished, not the name the spawning agent gave it | exact string |
|
|
356
|
-
| `command(<pattern>)` | a shell command that ran and did not fail | the pattern's words, in order, as a contiguous run inside the command — `command(gh pr create)` matches `gh pr create --fill`, not `gh api pulls
|
|
356
|
+
| `command(<pattern>)` | a shell command that ran and did not fail | the pattern's words, in order, as a contiguous run inside the command — `command(gh pr create)` matches `gh pr create --fill`, not `gh api pulls`. A word with no `/` of its own also matches a path ending in it: `command(build.sh)` matches `./scripts/build.sh` and `/opt/tools/build.sh` alike, never a different file that merely ends the same way |
|
|
357
357
|
| `gate(<name>)` | one of this harness's own gates *passing* — never just running | exact string; the only names a gate ever reports are `lint`, `test`, `docs` |
|
|
358
358
|
| `file(<pattern>)` | a file edited by a write tool | exact path, **or** `*<suffix>` (a leading `*`, matched against the path's end), **or** `<dir>/` (a trailing `/`, matched as a prefix) — three shapes, not a glob engine |
|
|
359
359
|
|
package/docs/log.md
CHANGED
|
@@ -16,6 +16,7 @@ newest first. For what landed in which npm release, see `CHANGELOG.md` at the re
|
|
|
16
16
|
## 2026-08-30
|
|
17
17
|
|
|
18
18
|
- **AD-120** — A rule denial names the directory and sha it actually checked ([/decisions/ad-120.md](/decisions/ad-120.md))
|
|
19
|
+
- **AD-121** — command(<pattern>) matches a bare word against a path ending in it, not only an exact word ([/decisions/ad-121.md](/decisions/ad-121.md))
|
|
19
20
|
|
|
20
21
|
## 2026-08-29
|
|
21
22
|
|
package/package.json
CHANGED
|
@@ -75,6 +75,24 @@ function matchesShape(words: readonly string[], shape: ShellShape): boolean {
|
|
|
75
75
|
return !shape.excludeIfAny?.some((flag) => words.includes(flag));
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* why a basename fallback: a token with no `/` of its own names an act, not a location — `build.sh` is the
|
|
80
|
+
* same script whether it runs as `build.sh`, `./scripts/build.sh` or `/home/user/tools/scripts/build.sh`.
|
|
81
|
+
* Confirmed live: a real `command(<script>)` proof stayed unsatisfied forever because every recorded
|
|
82
|
+
* invocation ran the script by its full installed path, and a whole word is never `===` one of its own path
|
|
83
|
+
* segments ([/decisions/ad-121.md](/decisions/ad-121.md)).
|
|
84
|
+
*
|
|
85
|
+
* why the `/` guard and not a bare suffix check: a token that already contains a `/` is the operator naming a
|
|
86
|
+
* location, and a location is exact or it is the wrong one — treating `not_review.py` as a match for
|
|
87
|
+
* `review.py` would turn a word boundary into a substring scan.
|
|
88
|
+
*/
|
|
89
|
+
function tokenMatches(word: string | undefined, token: string): boolean {
|
|
90
|
+
if (word === undefined) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return word === token || (!token.includes("/") && word.endsWith(`/${token}`));
|
|
94
|
+
}
|
|
95
|
+
|
|
78
96
|
/**
|
|
79
97
|
* why a phrase and not a word: an operator writes `command(gh pr review)`, meaning those words in that order.
|
|
80
98
|
* Matching the raw string against the whole command would let a heredoc or an unrelated argument satisfy it.
|
|
@@ -84,7 +102,7 @@ export function matchesPhrase(words: readonly string[], pattern: string): boolea
|
|
|
84
102
|
if (phrase.length === 0) {
|
|
85
103
|
return false;
|
|
86
104
|
}
|
|
87
|
-
return words.some((_, start) => phrase.every((token, index) => words[start + index]
|
|
105
|
+
return words.some((_, start) => phrase.every((token, index) => tokenMatches(words[start + index], token)));
|
|
88
106
|
}
|
|
89
107
|
|
|
90
108
|
export function triggerMatches(trigger: RuleTrigger, context: TriggerContext): boolean {
|