@cosyte/script-utils 0.1.3 → 0.1.4
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/CHANGELOG.md +6 -0
- package/README.md +32 -3
- package/bin/cosyte-agent-notes.js +23 -0
- package/bin/cosyte-attw.js +24 -0
- package/bin/cosyte-catalog.js +23 -0
- package/bin/cosyte-claude-card.js +23 -0
- package/bin/cosyte-internal-refs.js +23 -0
- package/bin/cosyte-no-emdash.js +18 -0
- package/bin/cosyte-phi-scan.js +26 -0
- package/bin/cosyte-readme.js +22 -0
- package/bin/script-utils.js +44 -0
- package/check-no-emdash.sh +69 -22
- package/package.json +14 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b9b2a7c: Every gate is now a bin, so a repository with no `package.json` can run it through npx: `cosyte-no-emdash`, `cosyte-attw`, `cosyte-phi-scan`, `cosyte-internal-refs`, `cosyte-readme`, `cosyte-claude-card`, `cosyte-agent-notes` and `cosyte-catalog`, each with `--help`, and `script-utils`, the dispatcher npx runs by the package's name (`npx -y @cosyte/script-utils@0.1 no-emdash`). The dash gate also matches every form the marketing and consulting scanner caught, in any case: the characters, URL encoding, the backslash-u escape (plain, braced and eight-digit), the braced backslash-x escape, the CSS escape, the Unicode name, and the named, decimal and hexadecimal HTML entities with or without the semicolon and with leading zeros. It now reads every tracked file's name as well as its content, and answers `--help`. A tracked file or name carrying one of the newly matched forms is refused where 0.1.3 printed OK.
|
|
8
|
+
|
|
3
9
|
## 0.1.3
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -247,6 +247,31 @@ Each shared gate runs from a repository's package script with no file of its own
|
|
|
247
247
|
notes), with exit codes 0 clean, 1 hits, 2 the gate could not run. The file may not set
|
|
248
248
|
`exitCodes`, `repoRoot` or `write`, which the command-line form owns.
|
|
249
249
|
|
|
250
|
+
### The bins, for a repository with no package.json
|
|
251
|
+
|
|
252
|
+
Every gate is also a bin (D56), so a repository with no `package.json`, or none that depends on this
|
|
253
|
+
package, runs it through npx. `script-utils` is named after the package, so npx runs it by the
|
|
254
|
+
package's name and it dispatches to the gate named first:
|
|
255
|
+
|
|
256
|
+
```sh
|
|
257
|
+
npx -y @cosyte/script-utils@0.1 no-emdash # the dispatcher: script-utils <command>
|
|
258
|
+
npx -y -p @cosyte/script-utils@0.1 cosyte-readme # or one gate's own bin
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
| bin | `script-utils` command | the gate |
|
|
262
|
+
| ---------------------- | ---------------------- | --------------------------------------------- |
|
|
263
|
+
| `cosyte-no-emdash` | `no-emdash` | the em and en dash gate, over files and names |
|
|
264
|
+
| `cosyte-attw` | `attw` | the attw publish gate, for the package here |
|
|
265
|
+
| `cosyte-phi-scan` | `phi-scan` | the PHI commit-gate |
|
|
266
|
+
| `cosyte-internal-refs` | `internal-refs` | the internal-reference gate |
|
|
267
|
+
| `cosyte-readme` | `readme` | the README gate |
|
|
268
|
+
| `cosyte-claude-card` | `claude-card` | the CLAUDE.md card checker |
|
|
269
|
+
| `cosyte-agent-notes` | `agent-notes` | the agent-notes checker |
|
|
270
|
+
| `cosyte-catalog` | `catalog` | the catalog check |
|
|
271
|
+
|
|
272
|
+
Each runs over the working directory, takes the arguments its gate takes, exits with the gate's own
|
|
273
|
+
code, and prints what it does with `--help`. `script-utils` with no command prints the list and exits 2.
|
|
274
|
+
|
|
250
275
|
### The em-dash gate, which is a file rather than a subpath
|
|
251
276
|
|
|
252
277
|
This package also ships `check-no-emdash.sh`, the one implementation of the em-dash gate for every
|
|
@@ -275,9 +300,13 @@ A repository narrows WHAT IS SCANNED, and only that, through a tracked file at i
|
|
|
275
300
|
`scripts/check-no-emdash.exclude`: one entry per line, `#` comments and blank lines ignored, an entry
|
|
276
301
|
ending in `/` covering every tracked path beneath it, and every entry anchored at the repository
|
|
277
302
|
root. An entry that matches no tracked path is refused rather than ignored, because an exclusion that
|
|
278
|
-
has outlived its subject is a hole nobody is looking at
|
|
279
|
-
banned forms are fixed in the gate. They are the em
|
|
280
|
-
the
|
|
303
|
+
has outlived its subject is a hole nobody is looking at; an excluded path's name is left out with its
|
|
304
|
+
content. Nothing narrows WHAT IS MATCHED: the banned forms are fixed in the gate. They are the em
|
|
305
|
+
dash (U+2014) and the en dash (U+2013), in any case, each as the literal character, its URL encoding,
|
|
306
|
+
its backslash-u escape (plain, braced and eight-digit), its braced backslash-x escape, its CSS escape
|
|
307
|
+
(plain and zero-padded), its Unicode name escape (a backslash, `N` and the character's name in braces), and its named, decimal and hexadecimal
|
|
308
|
+
HTML entities, with or without the semicolon and with leading zeros. The default scan also reads
|
|
309
|
+
every tracked file's NAME, because a name is copy too.
|
|
281
310
|
|
|
282
311
|
### The README gate
|
|
283
312
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cosyte-agent-notes: the agent-notes checker for the repository in the working directory.
|
|
3
|
+
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
|
+
// `npx -y -p @cosyte/script-utils cosyte-agent-notes`. It forwards every argument to the gate and exits with
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0.
|
|
6
|
+
|
|
7
|
+
import { runAgentNotesCheck } from "../agent-notes.js";
|
|
8
|
+
|
|
9
|
+
const HELP = `cosyte-agent-notes: check a repository's agent notes and every pointer into them.
|
|
10
|
+
|
|
11
|
+
cosyte-agent-notes [--dir <repository root>]
|
|
12
|
+
|
|
13
|
+
The notes are one file, documentation/agent-notes.md, and CLAUDE.md points into it as
|
|
14
|
+
documentation/agent-notes.md#<anchor>. Every pointer in a tracked file must name a heading or an
|
|
15
|
+
explicit anchor. Exit 0 clean, 1 a finding, 2 the check could not run.
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
20
|
+
process.stdout.write(HELP);
|
|
21
|
+
} else {
|
|
22
|
+
process.exitCode = runAgentNotesCheck({ argv: args });
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cosyte-attw: the attw publish gate for the package in the working directory.
|
|
3
|
+
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
|
+
// `npx -y -p @cosyte/script-utils cosyte-attw`. It forwards every argument to the gate and exits with
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0.
|
|
6
|
+
|
|
7
|
+
import { runAttwCli } from "../attw.js";
|
|
8
|
+
|
|
9
|
+
const HELP = `cosyte-attw: run the attw publish gate for the package in the working directory.
|
|
10
|
+
|
|
11
|
+
cosyte-attw [--no-definitely-typed]
|
|
12
|
+
|
|
13
|
+
It runs this package's own node_modules/.bin/attw over the tarball npm would publish, with four
|
|
14
|
+
nets attw lacks. scripts/attw.exclude names the resolutions the package leaves out, one per line:
|
|
15
|
+
node10 selects attw's node16 profile, node10 with node16-cjs selects esm-only. Exit 0 every net
|
|
16
|
+
passed; attw's own status when attw judged the package; 1 a refusal.
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const args = process.argv.slice(2);
|
|
20
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
21
|
+
process.stdout.write(HELP);
|
|
22
|
+
} else {
|
|
23
|
+
process.exitCode = runAttwCli(args);
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cosyte-catalog: the catalog check for the library in the working directory.
|
|
3
|
+
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
|
+
// `npx -y -p @cosyte/script-utils cosyte-catalog`. It forwards every argument to the gate and exits with
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0.
|
|
6
|
+
|
|
7
|
+
import { runCatalogCheck } from "../catalog.js";
|
|
8
|
+
|
|
9
|
+
const HELP = `cosyte-catalog: check that a library's package.json description is the catalog's.
|
|
10
|
+
|
|
11
|
+
cosyte-catalog [--dir <package directory>]
|
|
12
|
+
|
|
13
|
+
The catalog is the one list of our libraries, generated in cosyte/config from each package.json.
|
|
14
|
+
Exit 0 the description is the catalog's, 1 it is not (both values printed), 2 the package is not
|
|
15
|
+
listed or package.json cannot be read.
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
20
|
+
process.stdout.write(HELP);
|
|
21
|
+
} else {
|
|
22
|
+
process.exitCode = runCatalogCheck({ argv: args });
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cosyte-claude-card: the CLAUDE.md card checker for the repository in the working directory.
|
|
3
|
+
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
|
+
// `npx -y -p @cosyte/script-utils cosyte-claude-card`. It forwards every argument to the gate and exits with
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0.
|
|
6
|
+
|
|
7
|
+
import { runClaudeCardGate } from "../claude-card.js";
|
|
8
|
+
|
|
9
|
+
const HELP = `cosyte-claude-card: check a repository's CLAUDE.md against the card spec.
|
|
10
|
+
|
|
11
|
+
cosyte-claude-card [--dir <repository root>]
|
|
12
|
+
|
|
13
|
+
Gate, Hazard and Rules in order, at most 60 lines of at most 100 characters and 5,120 bytes, and an
|
|
14
|
+
@AGENTS.md import when an AGENTS.md sits beside it. Exit 0 the card follows the spec, 1 a finding,
|
|
15
|
+
2 CLAUDE.md could not be read.
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
20
|
+
process.stdout.write(HELP);
|
|
21
|
+
} else {
|
|
22
|
+
process.exitCode = runClaudeCardGate({ argv: args });
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cosyte-internal-refs: the internal-reference gate over the repository in the working directory.
|
|
3
|
+
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
|
+
// `npx -y -p @cosyte/script-utils cosyte-internal-refs`. It forwards every argument to the gate and exits with
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0.
|
|
6
|
+
|
|
7
|
+
import { runInternalRefsCli } from "../internal-refs.js";
|
|
8
|
+
|
|
9
|
+
const HELP = `cosyte-internal-refs: run the internal-reference gate over the repository in the working directory.
|
|
10
|
+
|
|
11
|
+
cosyte-internal-refs [--config <path>]
|
|
12
|
+
|
|
13
|
+
It refuses internal bookkeeping on a public surface: item identifiers, phase and wave language, ADR
|
|
14
|
+
numbers, internal repo paths. The axes come from scripts/internal-refs.json (or --config). Exit 0
|
|
15
|
+
clean, 1 hits, 2 the gate could not run.
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
20
|
+
process.stdout.write(HELP);
|
|
21
|
+
} else {
|
|
22
|
+
process.exitCode = runInternalRefsCli(args);
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cosyte-no-emdash: the em and en dash gate over the repository in the working directory.
|
|
3
|
+
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
|
+
// `npx -y -p @cosyte/script-utils cosyte-no-emdash`. The gate is the shell script beside this
|
|
5
|
+
// package's entry points; this runs it with bash, forwards every argument (`--help` included, which
|
|
6
|
+
// the gate answers itself) and exits with its code.
|
|
7
|
+
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const gate = fileURLToPath(new URL("../check-no-emdash.sh", import.meta.url));
|
|
12
|
+
const run = spawnSync("bash", [gate, ...process.argv.slice(2)], { stdio: "inherit" });
|
|
13
|
+
if (run.error) {
|
|
14
|
+
process.stderr.write(`cosyte-no-emdash: could not run bash ${gate}: ${run.error.message}\n`);
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
} else {
|
|
17
|
+
process.exitCode = run.status ?? 1;
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cosyte-phi-scan: the PHI commit-gate over the repository in the working directory.
|
|
3
|
+
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
|
+
// `npx -y -p @cosyte/script-utils cosyte-phi-scan`. It forwards every argument to the gate and exits with
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0.
|
|
6
|
+
|
|
7
|
+
import { runPhiScanCli } from "../phi-scan.js";
|
|
8
|
+
|
|
9
|
+
const HELP = `cosyte-phi-scan: run the PHI commit-gate over the repository in the working directory.
|
|
10
|
+
|
|
11
|
+
cosyte-phi-scan scan the whole repository
|
|
12
|
+
cosyte-phi-scan --staged scan what is staged, Markdown excepted (the pre-commit route)
|
|
13
|
+
cosyte-phi-scan <path>... scan these paths
|
|
14
|
+
|
|
15
|
+
It carries the cross-cutting floor (a dashed SSN shape, an email at an undeclared domain) and
|
|
16
|
+
reports a place and a rule, never the matched value. scripts/phi-allow-list.txt declares synthetic
|
|
17
|
+
values, and scripts/phi-scan.exclude names literal tracked paths to leave out. Exit 0 clean, 1 hits,
|
|
18
|
+
2 the scan could not account for something.
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
23
|
+
process.stdout.write(HELP);
|
|
24
|
+
} else {
|
|
25
|
+
process.exitCode = runPhiScanCli(args);
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cosyte-readme: the README gate for the library in the working directory.
|
|
3
|
+
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
|
+
// `npx -y -p @cosyte/script-utils cosyte-readme`. It forwards every argument to the gate and exits with
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0.
|
|
6
|
+
|
|
7
|
+
import { runReadmeGate } from "../readme.js";
|
|
8
|
+
|
|
9
|
+
const HELP = `cosyte-readme: check a library README against the house skeleton and npm's size limit.
|
|
10
|
+
|
|
11
|
+
cosyte-readme [--dir <package directory>]
|
|
12
|
+
|
|
13
|
+
It reads README.md and package.json. Exit 0 the README follows the skeleton, 1 a finding (each
|
|
14
|
+
names its line and rule), 2 the package could not be read.
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
19
|
+
process.stdout.write(HELP);
|
|
20
|
+
} else {
|
|
21
|
+
process.exitCode = runReadmeGate({ argv: args });
|
|
22
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// script-utils: one entry for every gate in @cosyte/script-utils (D56). npx runs the bin named
|
|
3
|
+
// after the package, so `npx -y @cosyte/script-utils@<version> <command> [args]` reaches each gate
|
|
4
|
+
// from a repository with no package.json. `<command> --help` is the gate's own help.
|
|
5
|
+
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const COMMANDS = {
|
|
10
|
+
"no-emdash": ["cosyte-no-emdash", "the em and en dash gate, over tracked files and their names"],
|
|
11
|
+
attw: ["cosyte-attw", "the attw publish gate, for the package here"],
|
|
12
|
+
"phi-scan": ["cosyte-phi-scan", "the PHI commit-gate, over the repository here"],
|
|
13
|
+
"internal-refs": [
|
|
14
|
+
"cosyte-internal-refs",
|
|
15
|
+
"the internal-reference gate, over the repository here",
|
|
16
|
+
],
|
|
17
|
+
readme: ["cosyte-readme", "the README gate, for the library here"],
|
|
18
|
+
"claude-card": ["cosyte-claude-card", "the CLAUDE.md card checker"],
|
|
19
|
+
"agent-notes": ["cosyte-agent-notes", "the agent-notes checker"],
|
|
20
|
+
catalog: ["cosyte-catalog", "the catalog check, for the library here"],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const HELP = `script-utils: run one of the @cosyte/script-utils gates over the working directory.
|
|
24
|
+
|
|
25
|
+
script-utils <command> [arguments]
|
|
26
|
+
script-utils <command> --help
|
|
27
|
+
|
|
28
|
+
${Object.entries(COMMANDS)
|
|
29
|
+
.map(([command, [bin, what]]) => ` ${command.padEnd(14)} ${what} (also the bin ${bin})`)
|
|
30
|
+
.join("\n")}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
34
|
+
if (command === undefined || command === "--help" || command === "-h") {
|
|
35
|
+
process.stdout.write(HELP);
|
|
36
|
+
process.exitCode = command === undefined ? 2 : 0;
|
|
37
|
+
} else if (!(command in COMMANDS)) {
|
|
38
|
+
process.stderr.write(`script-utils: ${JSON.stringify(command)} is not a command.\n\n${HELP}`);
|
|
39
|
+
process.exitCode = 2;
|
|
40
|
+
} else {
|
|
41
|
+
const bin = fileURLToPath(new URL(`./${COMMANDS[command][0]}.js`, import.meta.url));
|
|
42
|
+
const run = spawnSync(process.execPath, [bin, ...rest], { stdio: "inherit" });
|
|
43
|
+
process.exitCode = run.error ? 2 : (run.status ?? 2);
|
|
44
|
+
}
|
package/check-no-emdash.sh
CHANGED
|
@@ -228,19 +228,32 @@ unset -f grep xargs sed awk 2>/dev/null || true
|
|
|
228
228
|
# refuse_if_incomplete reads. That branch only refines wording, never the red.
|
|
229
229
|
export LC_ALL=C.UTF-8
|
|
230
230
|
|
|
231
|
-
#
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
# entities
|
|
237
|
-
#
|
|
238
|
-
#
|
|
239
|
-
#
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
|
|
243
|
-
|
|
231
|
+
# What counts as a dash, for U+2014 and U+2013 alike. Case never matters, so `&MDASH;`
|
|
232
|
+
# and `%e2%80%94` count too (D56: the forms the marketing and consulting scanner caught,
|
|
233
|
+
# which this gate now matches so those repos lose nothing by moving to it):
|
|
234
|
+
#
|
|
235
|
+
# the characters U+2014 U+2013
|
|
236
|
+
# HTML named entities, ; optional — –
|
|
237
|
+
# HTML decimal references — – leading zeros and a missing ; too
|
|
238
|
+
# HTML hex references — – leading zeros and a missing ; too
|
|
239
|
+
# URL encoding of the UTF-8 bytes %E2%80%94 %E2%80%93
|
|
240
|
+
# backslash-u escapes (JS, JSON, Java, \u2014 \u{2014} \U00002014
|
|
241
|
+
# C#, Python, Rust, Swift)
|
|
242
|
+
# backslash-x escapes (PCRE, Perl) \x{2014}
|
|
243
|
+
# CSS escapes \2014 \002014
|
|
244
|
+
# Unicode names (Python, Perl) \N{EM DASH} \N{EN DASH}
|
|
245
|
+
#
|
|
246
|
+
# The lookaheads keep the near misses out: 𔃈 and the CSS escape \20140 name other
|
|
247
|
+
# characters, and &mdashboard is not an entity. The escape forms are not theoretical: the
|
|
248
|
+
# parser-template manifest once carried a backslash-u em dash, and a literal-only sweep
|
|
249
|
+
# passed straight over it.
|
|
250
|
+
#
|
|
251
|
+
# A consumer may narrow WHAT IS SCANNED through the declaration above; nothing may narrow
|
|
252
|
+
# WHAT IS MATCHED, and widening it is a change every consuming repo inherits at once. The
|
|
253
|
+
# en dash's forms (operator decision, 2026-11-07) and these (D56) were such changes.
|
|
254
|
+
PATTERN='(?i)\x{2014}|\x{2013}|&[mn]dash(?![a-z])|�*821[12](?![0-9])|�*201[34](?![0-9a-f])'
|
|
255
|
+
PATTERN+='|%E2%80%9[34]|\\u(?:0000)?201[34]|\\u\{0*201[34]\}|\\x\{0*201[34]\}'
|
|
256
|
+
PATTERN+='|\\0{0,2}201[34](?![0-9a-f])|\\N\{E[MN] DASH\}'
|
|
244
257
|
|
|
245
258
|
# The declaration's path, relative to the top level of the repo under scan. Deliberately
|
|
246
259
|
# NOT relative to this file: in a consuming repo this file lives under `node_modules/`
|
|
@@ -281,10 +294,11 @@ SCAN_MODE=files
|
|
|
281
294
|
|
|
282
295
|
refuse_arguments() {
|
|
283
296
|
echo "ERROR: check-no-emdash - $1" >&2
|
|
284
|
-
echo " The modes are: (no argument) to scan every tracked file
|
|
285
|
-
echo " to scan the text arriving on stdin; --list-scanned to write the
|
|
286
|
-
echo " default scan reads; --self-id to write the sha256 of the
|
|
287
|
-
echo " that is executing. Refusing to run rather
|
|
297
|
+
echo " The modes are: (no argument) to scan every tracked file and its name;" >&2
|
|
298
|
+
echo " --stdin LABEL to scan the text arriving on stdin; --list-scanned to write the" >&2
|
|
299
|
+
echo " paths the default scan reads; --self-id to write the sha256 of the" >&2
|
|
300
|
+
echo " implementation that is executing; --help for this. Refusing to run rather" >&2
|
|
301
|
+
echo " than guess which was meant." >&2
|
|
288
302
|
exit 1
|
|
289
303
|
}
|
|
290
304
|
|
|
@@ -324,6 +338,22 @@ case "${1:-}" in
|
|
|
324
338
|
refuse_arguments "--self-id takes no further argument, and this was given: ${2}"
|
|
325
339
|
fi
|
|
326
340
|
;;
|
|
341
|
+
--help | -h)
|
|
342
|
+
cat << 'USAGE'
|
|
343
|
+
check-no-emdash: refuse the em dash (U+2014) and the en dash (U+2013), as the characters and
|
|
344
|
+
in every encoded form, in the tracked files of the repository it runs in and in their names.
|
|
345
|
+
|
|
346
|
+
check-no-emdash.sh scan every tracked file and every tracked file name
|
|
347
|
+
check-no-emdash.sh --stdin LABEL scan the text on stdin (a PR title, body, commit message)
|
|
348
|
+
check-no-emdash.sh --list-scanned write the NUL-separated paths the default scan reads
|
|
349
|
+
check-no-emdash.sh --self-id write the sha256 of the implementation that is executing
|
|
350
|
+
|
|
351
|
+
A repository leaves paths out in scripts/check-no-emdash.exclude: one per line, an entry ending
|
|
352
|
+
in / covers everything beneath it, # starts a comment. Exit 0 means the mode completed and found
|
|
353
|
+
nothing banned; exit 1 means anything else.
|
|
354
|
+
USAGE
|
|
355
|
+
exit 0
|
|
356
|
+
;;
|
|
327
357
|
*)
|
|
328
358
|
refuse_arguments "unrecognised argument: ${1}"
|
|
329
359
|
;;
|
|
@@ -341,6 +371,7 @@ GITERR=$(mktemp)
|
|
|
341
371
|
FILELIST=$(mktemp)
|
|
342
372
|
SCANLIST=$(mktemp)
|
|
343
373
|
PLAINLIST=$(mktemp)
|
|
374
|
+
NAMELIST=$(mktemp)
|
|
344
375
|
STDINBUF=$(mktemp)
|
|
345
376
|
BINPROBE=$(mktemp)
|
|
346
377
|
PROBEERR=$(mktemp)
|
|
@@ -353,8 +384,8 @@ PROBEERR=$(mktemp)
|
|
|
353
384
|
# not lost with it.
|
|
354
385
|
close_exit_vocabulary() {
|
|
355
386
|
local code=$?
|
|
356
|
-
rm -f "$ERRLOG" "$GITERR" "$FILELIST" "$SCANLIST" "$PLAINLIST" "$
|
|
357
|
-
"$PROBEERR"
|
|
387
|
+
rm -f "$ERRLOG" "$GITERR" "$FILELIST" "$SCANLIST" "$PLAINLIST" "$NAMELIST" "$STDINBUF" \
|
|
388
|
+
"$BINPROBE" "$PROBEERR"
|
|
358
389
|
if [ "$code" -ne 0 ] && [ "$code" -ne 1 ]; then
|
|
359
390
|
echo "ERROR: check-no-emdash - the run ended with status ${code}. This gate reports 0" >&2
|
|
360
391
|
echo " for a mode that completed clean and 1 for everything else, so it is" >&2
|
|
@@ -816,6 +847,7 @@ esac
|
|
|
816
847
|
|
|
817
848
|
: > "$SCANLIST"
|
|
818
849
|
: > "$PLAINLIST"
|
|
850
|
+
: > "$NAMELIST"
|
|
819
851
|
gitlinks=0
|
|
820
852
|
scanned=0
|
|
821
853
|
self_excluded=0
|
|
@@ -831,11 +863,16 @@ for i in "${!TRACKED[@]}"; do
|
|
|
831
863
|
|
|
832
864
|
# What the repo declared it leaves out. Checked before the entry is opened, because an
|
|
833
865
|
# excluded path is not read and its type on disk is therefore not this gate's business.
|
|
866
|
+
# It is not scanned at all, so its NAME is left out with its content.
|
|
834
867
|
if is_excluded "$f"; then
|
|
835
868
|
declared_excluded=$((declared_excluded + 1))
|
|
836
869
|
continue
|
|
837
870
|
fi
|
|
838
871
|
|
|
872
|
+
# A NAME IS COPY TOO (D56): every tracked path the declaration does not leave out has its
|
|
873
|
+
# name scanned, a gitlink's included, before any test of what is on disk.
|
|
874
|
+
printf '%s\0' "$f" >> "$NAMELIST"
|
|
875
|
+
|
|
839
876
|
# `git ls-files` lists a submodule as a gitlink, which on disk is a REAL directory.
|
|
840
877
|
# config has none today (checked: `git ls-files -s` lists no mode 160000 entry). The
|
|
841
878
|
# skip is keyed on the INDEX MODE and never on the type on disk, because the two
|
|
@@ -886,12 +923,22 @@ if [ "$MODE" = list ]; then
|
|
|
886
923
|
exit 0
|
|
887
924
|
fi
|
|
888
925
|
|
|
926
|
+
# File names first: NUL-separated in, one per line out. `-a` because a name is text whatever
|
|
927
|
+
# bytes it holds, and the stderr capture refuses a name scan that could not complete.
|
|
928
|
+
NAME_HITS=$(grep -z -a -P -e "$PATTERN" < "$NAMELIST" 2>> "$ERRLOG" | tr '\0' '\n' || true)
|
|
929
|
+
refuse_if_incomplete
|
|
930
|
+
|
|
889
931
|
HITS=$(xargs -0 -r grep -H -nP -e "$PATTERN" -- < "$SCANLIST" 2>> "$ERRLOG" || true)
|
|
890
932
|
|
|
891
933
|
refuse_if_incomplete
|
|
892
934
|
|
|
935
|
+
if [ -n "$NAME_HITS" ] && [ -n "$HITS" ]; then
|
|
936
|
+
fail_with_hits "the tracked file names and the tracked files listed above" \
|
|
937
|
+
"$NAME_HITS"$'\n'"$HITS"
|
|
938
|
+
fi
|
|
939
|
+
[ -n "$NAME_HITS" ] && fail_with_hits "the tracked file names listed above" "$NAME_HITS"
|
|
893
940
|
[ -n "$HITS" ] && fail_with_hits "the tracked files listed above" "$HITS"
|
|
894
941
|
|
|
895
|
-
echo "check-no-emdash: OK (no em or en dashes in ${scanned} of ${#TRACKED[@]} tracked file(s)
|
|
896
|
-
"${gitlinks} gitlink(s) skipped, ${self_excluded} excluded as the
|
|
897
|
-
"scan, ${declared_excluded} excluded by ${DECLARATION})"
|
|
942
|
+
echo "check-no-emdash: OK (no em or en dashes in ${scanned} of ${#TRACKED[@]} tracked file(s)" \
|
|
943
|
+
"or their names; ${gitlinks} gitlink(s) skipped, ${self_excluded} excluded as the" \
|
|
944
|
+
"implementation under scan, ${declared_excluded} excluded by ${DECLARATION})"
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosyte/script-utils",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Zero-dependency helpers for the repo-local gate scripts in @cosyte/* repos.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
7
|
+
"bin",
|
|
7
8
|
"entrypoint",
|
|
8
9
|
"import-meta",
|
|
9
10
|
"phi",
|
|
@@ -21,6 +22,17 @@
|
|
|
21
22
|
"author": "Cosyte <hello@cosyte.com> (https://cosyte.com)",
|
|
22
23
|
"type": "module",
|
|
23
24
|
"main": "index.js",
|
|
25
|
+
"bin": {
|
|
26
|
+
"script-utils": "bin/script-utils.js",
|
|
27
|
+
"cosyte-no-emdash": "bin/cosyte-no-emdash.js",
|
|
28
|
+
"cosyte-attw": "bin/cosyte-attw.js",
|
|
29
|
+
"cosyte-phi-scan": "bin/cosyte-phi-scan.js",
|
|
30
|
+
"cosyte-internal-refs": "bin/cosyte-internal-refs.js",
|
|
31
|
+
"cosyte-readme": "bin/cosyte-readme.js",
|
|
32
|
+
"cosyte-claude-card": "bin/cosyte-claude-card.js",
|
|
33
|
+
"cosyte-agent-notes": "bin/cosyte-agent-notes.js",
|
|
34
|
+
"cosyte-catalog": "bin/cosyte-catalog.js"
|
|
35
|
+
},
|
|
24
36
|
"types": "index.d.ts",
|
|
25
37
|
"exports": {
|
|
26
38
|
".": {
|
|
@@ -75,6 +87,7 @@
|
|
|
75
87
|
"agent-notes.js",
|
|
76
88
|
"agent-notes.d.ts",
|
|
77
89
|
"check-no-emdash.sh",
|
|
90
|
+
"bin",
|
|
78
91
|
"README.md",
|
|
79
92
|
"CHANGELOG.md"
|
|
80
93
|
],
|