tackbox 0.1.68 → 0.1.70
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 +105 -16
- package/bin/tackbox-eslint.js +11 -1
- package/bin/tackbox-mdlint.js +15 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -457,6 +457,74 @@ event, `dev.py check`, and CI reports until the entry lands or the
|
|
|
457
457
|
marker is reverted. Removing a manifest line is free; a marker whose
|
|
458
458
|
text, scope, or count changes needs its entry updated the same way.
|
|
459
459
|
|
|
460
|
+
## Generated and vendored code
|
|
461
|
+
|
|
462
|
+
Committed code that carries a generated or vendored git attribute is
|
|
463
|
+
excluded from the whole lint - findings there are not fixable in the
|
|
464
|
+
file (they belong in the generator), and a suppression marker cannot
|
|
465
|
+
survive regeneration. tackbox honors exactly three attributes,
|
|
466
|
+
`linguist-generated`, `gitlab-generated`, and `linguist-vendored`,
|
|
467
|
+
read from `.gitattributes` the same way the host (GitHub, GitLab)
|
|
468
|
+
reads them - no exclude surface of tackbox's own. The best fix stays
|
|
469
|
+
organizational: generated code should normally not be committed at
|
|
470
|
+
all; this serves the forced residue.
|
|
471
|
+
|
|
472
|
+
A file is excluded when `git check-attr` reports one of the three as
|
|
473
|
+
set. Semantics by example:
|
|
474
|
+
|
|
475
|
+
```text
|
|
476
|
+
gen/** linguist-generated
|
|
477
|
+
vendor/** linguist-vendored
|
|
478
|
+
gen/keep.go linguist-generated=false
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
`gen/**` excludes everything under `gen/`; `vendor/**` the same for
|
|
482
|
+
vendored. Note `dir/**`, not `dir/` - gitattributes patterns, unlike
|
|
483
|
+
gitignore, do not match a trailing-slash directory form. `=false`
|
|
484
|
+
re-includes a single file inside an excluded tree (`gen/keep.go` above
|
|
485
|
+
is linted normally); `-attr` and `!attr` also leave a file in. Only
|
|
486
|
+
`set` / `=true` excludes.
|
|
487
|
+
|
|
488
|
+
The exclusion covers everything: per-file engines, the erclint Go
|
|
489
|
+
package run (a mixed package's excluded file is compiled but its
|
|
490
|
+
findings drop; a compile break still fails loudly), duplication, the
|
|
491
|
+
CodeClimate report, and the marker inventory - an excluded file's
|
|
492
|
+
markers are dead, so a manifest entry addressing one orphans. A lint
|
|
493
|
+
run whose scope touches excluded files prints one summary line:
|
|
494
|
+
|
|
495
|
+
```text
|
|
496
|
+
excluded by attributes: 12 files in scope (tackbox escapes lists all)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
It counts unique excluded files in the current scope (absent at zero),
|
|
500
|
+
so scoped runs are not wallpapered with a global constant;
|
|
501
|
+
`tackbox escapes` lists the full population as `attribute-excluded`
|
|
502
|
+
entries.
|
|
503
|
+
|
|
504
|
+
Because the excluded population is where the lint, the marker
|
|
505
|
+
inventory, and host diff review are all blind, the agent hook makes
|
|
506
|
+
the two ways into it loud:
|
|
507
|
+
|
|
508
|
+
- adding a positive exclusion line (a bare `<attr>` or `<attr>=true`)
|
|
509
|
+
to any `.gitattributes` draws a PreToolUse ask, one joint ask per
|
|
510
|
+
edit listing every added line; removals, `=false`, `-attr`, and
|
|
511
|
+
non-exclusion lines are free;
|
|
512
|
+
- editing (or creating) a file that is effective-excluded draws an
|
|
513
|
+
ask naming the attributes.
|
|
514
|
+
|
|
515
|
+
Generators run through Bash and are unaffected - the boundary is that
|
|
516
|
+
the change stays in the commit/PR diff, and hosts collapse
|
|
517
|
+
excluded-file diffs, so reviewers must expand them.
|
|
518
|
+
|
|
519
|
+
`tackbox doctor` adds an informational `attributes` section (never a
|
|
520
|
+
check, no exit-code effect) naming local conditions that can make a
|
|
521
|
+
run diverge from a clean CI clone - an `info/attributes` or an
|
|
522
|
+
untracked/index-hidden `.gitattributes` carrier mentioning the
|
|
523
|
+
attributes, or a neutralized attribute source override. `tackbox
|
|
524
|
+
escapes --since <rev>` resolves the baseline's attributes as of the
|
|
525
|
+
rev and so needs git >= 2.40 (older git is a named infra error on the
|
|
526
|
+
`--since` path only; the plain listing needs no version bump).
|
|
527
|
+
|
|
460
528
|
## Runtime reporting helpers
|
|
461
529
|
|
|
462
530
|
Direct reporting helpers ship per language; their shared runtime behavior -
|
|
@@ -483,9 +551,11 @@ Claude Code hook event on stdin and dispatches by `hook_event_name`:
|
|
|
483
551
|
nothing, and the block repeats on every event until the tree is
|
|
484
552
|
consistent. The authoritative gate stays pre-commit / CI.
|
|
485
553
|
- **PreToolUse** asks for approval before a new `.tackbox/approvals`
|
|
486
|
-
line or a new `.tackbox/reporters` line lands
|
|
487
|
-
|
|
488
|
-
|
|
554
|
+
line or a new `.tackbox/reporters` line lands, before a positive
|
|
555
|
+
exclusion line is added to any `.gitattributes`, and before an edit
|
|
556
|
+
to an attribute-excluded file (see "Generated and vendored code");
|
|
557
|
+
removing a line is free. Editing markers in code draws no Pre ask -
|
|
558
|
+
the consistency check owns them.
|
|
489
559
|
|
|
490
560
|
Only markers in files an engine would lint participate in the check
|
|
491
561
|
(D012): a marker in a Go `testdata/` path or a non-lintable fixture
|
|
@@ -543,9 +613,11 @@ uvx tackbox@latest escapes --since origin/main --context 5
|
|
|
543
613
|
|
|
544
614
|
```json
|
|
545
615
|
{
|
|
546
|
-
"version":
|
|
616
|
+
"version": 2,
|
|
547
617
|
"since": null,
|
|
548
618
|
"entries": [
|
|
619
|
+
{"kind": "attribute-excluded", "file": "gen/api.pb.go",
|
|
620
|
+
"attribute": "linguist-generated"},
|
|
549
621
|
{"kind": "marker", "file": "a/b.py", "line": 12,
|
|
550
622
|
"text": "no-report: central boundary already captures it",
|
|
551
623
|
"reason": "central boundary already captures it",
|
|
@@ -559,13 +631,19 @@ uvx tackbox@latest escapes --since origin/main --context 5
|
|
|
559
631
|
{"kind": "quiet-site", "file": "go/x.go", "line": 9,
|
|
560
632
|
"text": "report.Quiet(ctx, ...)", "context": ["..."]}
|
|
561
633
|
],
|
|
562
|
-
"counts": {"marker": 1, "reporter-decl": 1, "notify-site": 1,
|
|
634
|
+
"counts": {"marker": 1, "reporter-decl": 1, "notify-site": 1,
|
|
635
|
+
"quiet-site": 1, "attribute-excluded": 1}
|
|
563
636
|
}
|
|
564
637
|
```
|
|
565
638
|
|
|
566
|
-
- `version` is the schema version (`
|
|
567
|
-
kinds, even at zero, so consumers see a stable shape.
|
|
639
|
+
- `version` is the schema version (`2`); `counts` always carries all five
|
|
640
|
+
kinds, even at zero, so consumers see a stable shape. Every count is an
|
|
641
|
+
entry count except `attribute-excluded`, which counts unique files.
|
|
568
642
|
- `since` echoes the `--since` rev, or `null`.
|
|
643
|
+
- `attribute-excluded` entries carry only `kind` / `file` / `attribute` (no
|
|
644
|
+
line or text): the whole file is the bypass, one entry per set attribute
|
|
645
|
+
of the three (`linguist-generated`, `gitlab-generated`,
|
|
646
|
+
`linguist-vendored`). See "Generated and vendored code".
|
|
569
647
|
- `text` is the trimmed source line; for a marker it runs from the marker
|
|
570
648
|
keyword to end of line.
|
|
571
649
|
- `reason` (markers only) is what follows the keyword's colon, trimmed -
|
|
@@ -574,14 +652,18 @@ uvx tackbox@latest escapes --since origin/main --context 5
|
|
|
574
652
|
(default 3), inclusive of the entry line itself - the window
|
|
575
653
|
`[line-N, line+N]`, clipped at file edges, each line trimmed of trailing
|
|
576
654
|
whitespace. It is plain source; the entry line is not marked.
|
|
577
|
-
- `entries` are sorted by `(file,
|
|
655
|
+
- `entries` are sorted by `(file, kind, kind-subkey)` - the subkey is
|
|
656
|
+
`(line, text)` for the line-bearing kinds and `(attribute,)` for
|
|
657
|
+
`attribute-excluded`.
|
|
578
658
|
|
|
579
659
|
### Scope and detection
|
|
580
660
|
|
|
581
661
|
The scan covers the same lintable source set the linter would scan (the
|
|
582
662
|
D012 predicate: extension match plus each engine's path filter, so a Go
|
|
583
|
-
`testdata/` file is out), plus the root
|
|
584
|
-
non-empty line is one declaration - the file has
|
|
663
|
+
`testdata/` file is out) minus the attribute-excluded files, plus the root
|
|
664
|
+
`.tackbox/reporters` (every non-empty line is one declaration - the file has
|
|
665
|
+
no comment syntax). An attribute-excluded file's own markers are dead, so it
|
|
666
|
+
surfaces only as its `attribute-excluded` entries.
|
|
585
667
|
notify / quiet call sites are detected **textually per language**
|
|
586
668
|
(`report_quiet` / `notify` in Python, `reportQuiet` / `notify` in the JS
|
|
587
669
|
family, `.Quiet(` / `.Notify(` in Go, `.quiet(` / `.notify(` in Java),
|
|
@@ -592,12 +674,19 @@ this is observability, not a lint.
|
|
|
592
674
|
### `--since <rev>`
|
|
593
675
|
|
|
594
676
|
`--since <rev>` prints only entries **new against `<rev>`**, compared by
|
|
595
|
-
content identity `(kind, file, text)
|
|
596
|
-
the
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
677
|
+
content identity (`(kind, file, text)`, or `(kind, file, attribute)` for
|
|
678
|
+
attribute-excluded) - the same extraction run against the tree at `<rev>`
|
|
679
|
+
(via `git ls-tree` + `git show`) subtracted, count aware, from the current
|
|
680
|
+
tree's entries. The baseline is attribute-aware: it resolves the attributes
|
|
681
|
+
as of `<rev>` (via the seam's `git check-attr --source`), so an attribute
|
|
682
|
+
added since the rev reports its newly-excluded files, a removed one
|
|
683
|
+
re-activates its markers as new (never a silent subtraction), and an
|
|
684
|
+
unchanged one adds no noise. Because `--source` needs git >= 2.40, an older
|
|
685
|
+
git is a named infra error on the `--since` path only (the plain listing
|
|
686
|
+
needs no version bump). It over-reports on moved code (a new file path is a
|
|
687
|
+
new identity) but never silently drops an entry - the conservative direction
|
|
688
|
+
for a review aid. A bad rev is the other infra error: one stderr line,
|
|
689
|
+
exit 1.
|
|
601
690
|
|
|
602
691
|
## Layout
|
|
603
692
|
|
package/bin/tackbox-eslint.js
CHANGED
|
@@ -11,9 +11,13 @@ function parseArgv(argv) {
|
|
|
11
11
|
const decls = []
|
|
12
12
|
const files = []
|
|
13
13
|
let machine = false
|
|
14
|
-
for (
|
|
14
|
+
for (let i = 0; i < argv.length; i++) {
|
|
15
|
+
const a = argv[i]
|
|
15
16
|
if (a === '--machine') {
|
|
16
17
|
machine = true
|
|
18
|
+
} else if (a === '--files-from') {
|
|
19
|
+
// The file set rides a list-file, not positional argv (ARG_MAX safety).
|
|
20
|
+
files.push(...readFilesFrom(argv[++i]))
|
|
17
21
|
} else if (a.startsWith(REPORTERS_FLAG)) {
|
|
18
22
|
for (const d of a.slice(REPORTERS_FLAG.length).split(',')) {
|
|
19
23
|
if (!d) continue
|
|
@@ -27,6 +31,12 @@ function parseArgv(argv) {
|
|
|
27
31
|
return { decls, files, machine }
|
|
28
32
|
}
|
|
29
33
|
|
|
34
|
+
// readFilesFrom reads a newline-separated UTF-8 list-file into its non-empty
|
|
35
|
+
// paths. Additive to positional paths - the bin is public on npm.
|
|
36
|
+
function readFilesFrom(listPath) {
|
|
37
|
+
return fs.readFileSync(listPath, 'utf8').split(/\r?\n/).filter(Boolean)
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
function parseModule(file, code) {
|
|
31
41
|
const ext = path.extname(file)
|
|
32
42
|
if (ext === '.ts' || ext === '.tsx') {
|
package/bin/tackbox-mdlint.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs')
|
|
2
3
|
const { lint } = require('markdownlint/promise')
|
|
3
4
|
const noNonAscii = require('../js/markdownlint-rules/no-non-ascii')
|
|
4
5
|
|
|
6
|
+
// readFilesFrom reads a newline-separated UTF-8 list-file into its non-empty
|
|
7
|
+
// paths. Additive to positional paths - the bin is public on npm.
|
|
8
|
+
function readFilesFrom(listPath) {
|
|
9
|
+
return fs.readFileSync(listPath, 'utf8').split(/\r?\n/).filter(Boolean)
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
async function run() {
|
|
6
13
|
const argv = process.argv.slice(2)
|
|
7
14
|
const machine = argv.includes('--machine')
|
|
8
|
-
const files =
|
|
15
|
+
const files = []
|
|
16
|
+
for (let i = 0; i < argv.length; i++) {
|
|
17
|
+
const a = argv[i]
|
|
18
|
+
if (a === '--machine') continue
|
|
19
|
+
// The file set rides a list-file, not positional argv (ARG_MAX safety).
|
|
20
|
+
if (a === '--files-from') { files.push(...readFilesFrom(argv[++i])); continue }
|
|
21
|
+
files.push(a)
|
|
22
|
+
}
|
|
9
23
|
if (files.length === 0) {
|
|
10
24
|
process.stderr.write('tackbox-mdlint: no files supplied\n')
|
|
11
25
|
process.exit(2)
|
package/package.json
CHANGED