stapes-adsa 1.0.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.
- package/AGENTS.md +244 -0
- package/CHANGELOG.md +52 -0
- package/LICENSE +21 -0
- package/README.md +291 -0
- package/SECURITY.md +46 -0
- package/SKILL.md +198 -0
- package/dist/ast.d.ts +55 -0
- package/dist/ast.js +186 -0
- package/dist/ast.js.map +1 -0
- package/dist/checks/async-no-log.d.ts +19 -0
- package/dist/checks/async-no-log.js +77 -0
- package/dist/checks/async-no-log.js.map +1 -0
- package/dist/checks/decision-router.d.ts +16 -0
- package/dist/checks/decision-router.js +112 -0
- package/dist/checks/decision-router.js.map +1 -0
- package/dist/checks/fast-path-with-io.d.ts +19 -0
- package/dist/checks/fast-path-with-io.js +58 -0
- package/dist/checks/fast-path-with-io.js.map +1 -0
- package/dist/checks/index.d.ts +21 -0
- package/dist/checks/index.js +90 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/checks/log-schema.d.ts +18 -0
- package/dist/checks/log-schema.js +124 -0
- package/dist/checks/log-schema.js.map +1 -0
- package/dist/checks/slow-path-no-log.d.ts +16 -0
- package/dist/checks/slow-path-no-log.js +71 -0
- package/dist/checks/slow-path-no-log.js.map +1 -0
- package/dist/cli.d.ts +25 -0
- package/dist/cli.js +186 -0
- package/dist/cli.js.map +1 -0
- package/dist/column.d.ts +10 -0
- package/dist/column.js +30 -0
- package/dist/column.js.map +1 -0
- package/dist/fix.d.ts +34 -0
- package/dist/fix.js +110 -0
- package/dist/fix.js.map +1 -0
- package/dist/hook-install.d.ts +36 -0
- package/dist/hook-install.js +94 -0
- package/dist/hook-install.js.map +1 -0
- package/dist/lib/instrument.d.ts +49 -0
- package/dist/lib/instrument.js +53 -0
- package/dist/lib/instrument.js.map +1 -0
- package/dist/patterns.d.ts +36 -0
- package/dist/patterns.js +72 -0
- package/dist/patterns.js.map +1 -0
- package/dist/runner.d.ts +35 -0
- package/dist/runner.js +104 -0
- package/dist/runner.js.map +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +32 -0
- package/dist/util.js +105 -0
- package/dist/util.js.map +1 -0
- package/package.json +66 -0
package/dist/runner.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner — orchestrates check execution and produces the v1 --json shape.
|
|
3
|
+
*
|
|
4
|
+
* Inputs:
|
|
5
|
+
* - root directory (or list of files)
|
|
6
|
+
* - list of check names to run (default = all enabled)
|
|
7
|
+
* - whether --strict is on (warnings become blocks)
|
|
8
|
+
*
|
|
9
|
+
* Output:
|
|
10
|
+
* RunReport — see SPEC.md invariant #5 for the JSON shape.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { basename } from "node:path";
|
|
14
|
+
import { CHECKS, createProject } from "./checks/index.js";
|
|
15
|
+
import { NAME_COL_WIDTH, walkTsFiles } from "./util.js";
|
|
16
|
+
/** Tool version, read from package.json at runtime. */
|
|
17
|
+
const VERSION = (() => {
|
|
18
|
+
try {
|
|
19
|
+
// dist/cli.js → ../package.json
|
|
20
|
+
const pkgPath = new URL("../package.json", import.meta.url);
|
|
21
|
+
const raw = readFileSync(pkgPath, "utf8");
|
|
22
|
+
const pkg = JSON.parse(raw);
|
|
23
|
+
return pkg.version;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return "0.0.0";
|
|
27
|
+
}
|
|
28
|
+
})();
|
|
29
|
+
export function run(opts) {
|
|
30
|
+
const startedAt = new Date().toISOString();
|
|
31
|
+
const fileList = opts.files && opts.files.length > 0
|
|
32
|
+
? opts.files
|
|
33
|
+
: walkTsFiles(opts.root);
|
|
34
|
+
const project = createProject();
|
|
35
|
+
const sourceFiles = fileList.map((f) => {
|
|
36
|
+
const abs = f.startsWith("/") ? f : `${opts.root}/${f}`;
|
|
37
|
+
return project.addSourceFileAtPath(abs);
|
|
38
|
+
});
|
|
39
|
+
const enabled = (opts.checks && opts.checks.length > 0)
|
|
40
|
+
? CHECKS.filter((c) => opts.checks.includes(c.name))
|
|
41
|
+
: CHECKS.filter((c) => c.defaultEnabled);
|
|
42
|
+
const allFindings = [];
|
|
43
|
+
for (const sf of sourceFiles) {
|
|
44
|
+
for (const check of enabled) {
|
|
45
|
+
const result = check.run(sf);
|
|
46
|
+
for (const f of result.findings) {
|
|
47
|
+
// --strict promotes warnings to blocks
|
|
48
|
+
if (opts.strict && f.severity === "warn") {
|
|
49
|
+
allFindings.push({ ...f, severity: "block" });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
allFindings.push(f);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const failed = allFindings.filter((f) => f.severity === "block").length;
|
|
58
|
+
const warned = allFindings.filter((f) => f.severity === "warn").length;
|
|
59
|
+
const exitCode = failed > 0 ? 1 : 0;
|
|
60
|
+
return {
|
|
61
|
+
startedAt,
|
|
62
|
+
tool: `stapes-adsa@${VERSION}`,
|
|
63
|
+
version: VERSION,
|
|
64
|
+
exitCode,
|
|
65
|
+
checkCount: enabled.length,
|
|
66
|
+
failed,
|
|
67
|
+
warned,
|
|
68
|
+
findings: allFindings,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Pretty-print the human-readable summary (no JSON). */
|
|
72
|
+
export function formatHuman(report) {
|
|
73
|
+
const lines = [];
|
|
74
|
+
lines.push(`${report.tool}`);
|
|
75
|
+
lines.push("");
|
|
76
|
+
if (report.findings.length === 0) {
|
|
77
|
+
lines.push("✓ no findings");
|
|
78
|
+
return lines.join("\n");
|
|
79
|
+
}
|
|
80
|
+
// Group by check.
|
|
81
|
+
const byCheck = new Map();
|
|
82
|
+
for (const f of report.findings) {
|
|
83
|
+
const arr = byCheck.get(f.check) ?? [];
|
|
84
|
+
arr.push(f);
|
|
85
|
+
byCheck.set(f.check, arr);
|
|
86
|
+
}
|
|
87
|
+
for (const [check, findings] of byCheck) {
|
|
88
|
+
const block = findings.filter((f) => f.severity === "block").length;
|
|
89
|
+
const warn = findings.filter((f) => f.severity === "warn").length;
|
|
90
|
+
const symbol = block > 0 ? "✗" : warn > 0 ? "⚠" : "✓";
|
|
91
|
+
lines.push(`${symbol} ${check.padEnd(NAME_COL_WIDTH)} ${findings.length} finding(s)`);
|
|
92
|
+
for (const f of findings) {
|
|
93
|
+
lines.push(` ${f.file}:${f.line}:${f.column}`);
|
|
94
|
+
lines.push(` [${f.severity}] ${f.message}`);
|
|
95
|
+
lines.push(` ${f.evidence}`);
|
|
96
|
+
}
|
|
97
|
+
lines.push("");
|
|
98
|
+
}
|
|
99
|
+
lines.push(`${report.failed} block(s), ${report.warned} warning(s).`);
|
|
100
|
+
return lines.join("\n");
|
|
101
|
+
}
|
|
102
|
+
// Re-export basename for the CLI to use when listing tools.
|
|
103
|
+
export { basename };
|
|
104
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for stapes-adsa.
|
|
3
|
+
*
|
|
4
|
+
* `Finding` is the atomic unit emitted by every check. `CheckResult`
|
|
5
|
+
* is the per-check aggregate. The runner collects `CheckResult[]`
|
|
6
|
+
* into the v1 `--json` shape (see runner.ts).
|
|
7
|
+
*/
|
|
8
|
+
/** Severity of a finding. Determines exit code and `--strict` behavior. */
|
|
9
|
+
export type Severity = "block" | "warn";
|
|
10
|
+
/** ADSA path classification. The four-field log contract uses this as the enum. */
|
|
11
|
+
export type AdsaPath = "fast" | "slow";
|
|
12
|
+
/** A single violation reported by a check. */
|
|
13
|
+
export interface Finding {
|
|
14
|
+
/** Check that produced the finding, e.g. "slow-path-no-log". */
|
|
15
|
+
check: string;
|
|
16
|
+
/** Repo-relative path of the source file. */
|
|
17
|
+
file: string;
|
|
18
|
+
/** 1-based line number. */
|
|
19
|
+
line: number;
|
|
20
|
+
/** 1-based column number. */
|
|
21
|
+
column: number;
|
|
22
|
+
/** Human-readable message. No newlines, max 200 chars. */
|
|
23
|
+
message: string;
|
|
24
|
+
/** Severity per SPEC.md invariant #8. */
|
|
25
|
+
severity: Severity;
|
|
26
|
+
/** Verbatim source snippet (one line, truncated to 200 chars). */
|
|
27
|
+
evidence: string;
|
|
28
|
+
/** Optional suggested fix. `--fix` uses this when present and conservative. */
|
|
29
|
+
suggestedFix?: SuggestedFix;
|
|
30
|
+
}
|
|
31
|
+
/** A safe auto-fix that the runner can apply. */
|
|
32
|
+
export interface SuggestedFix {
|
|
33
|
+
/** Short label, e.g. "insert slow-path log line". */
|
|
34
|
+
description: string;
|
|
35
|
+
/** The exact text to insert at `line`, `column`. */
|
|
36
|
+
replacement: string;
|
|
37
|
+
/** 1-based line number where the fix applies. */
|
|
38
|
+
line: number;
|
|
39
|
+
/** 1-based column number where the fix applies. */
|
|
40
|
+
column: number;
|
|
41
|
+
}
|
|
42
|
+
/** A check's aggregate output. */
|
|
43
|
+
export interface CheckResult {
|
|
44
|
+
/** Check name, e.g. "slow-path-no-log". */
|
|
45
|
+
name: string;
|
|
46
|
+
/** All findings produced by this check run. */
|
|
47
|
+
findings: Finding[];
|
|
48
|
+
/** Total files inspected by this check. */
|
|
49
|
+
filesScanned: number;
|
|
50
|
+
/** Wall-clock duration in milliseconds. */
|
|
51
|
+
durationMs: number;
|
|
52
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for stapes-adsa.
|
|
3
|
+
*
|
|
4
|
+
* `Finding` is the atomic unit emitted by every check. `CheckResult`
|
|
5
|
+
* is the per-check aggregate. The runner collects `CheckResult[]`
|
|
6
|
+
* into the v1 `--json` shape (see runner.ts).
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility helpers for the checks and runner.
|
|
3
|
+
*
|
|
4
|
+
* - `walkTsFiles` — recursive .ts/.tsx file walker with default exclusions.
|
|
5
|
+
* - `inferAdsaArea` — deterministic area inference (SPEC.md invariant #9).
|
|
6
|
+
* - `isInsideLogCall` — true if a node is an argument to a log.*() call.
|
|
7
|
+
* - `hasAdsaLogCall` — true if a function body contains any matching adsa_path log call.
|
|
8
|
+
* - `truncate` — evidence truncation to 200 chars.
|
|
9
|
+
*/
|
|
10
|
+
/** Extensions this tool recognises. Anything else is silently skipped. */
|
|
11
|
+
export declare const TS_EXTENSIONS: Set<string>;
|
|
12
|
+
/** Directories always excluded from the scan. */
|
|
13
|
+
export declare const ALWAYS_EXCLUDED_DIRS: Set<string>;
|
|
14
|
+
/** Recursively walk a directory and yield .ts/.tsx paths (relative to root). */
|
|
15
|
+
export declare function walkTsFiles(root: string): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Deterministic ADSA area inference (SPEC.md invariant #9).
|
|
18
|
+
*
|
|
19
|
+
* Order:
|
|
20
|
+
* 1. Deepest enclosing directory whose name is not `src`/`lib`/etc.
|
|
21
|
+
* 2. Basename of the file without extension.
|
|
22
|
+
* 3. "unscoped" fallback.
|
|
23
|
+
*/
|
|
24
|
+
export declare function inferAdsaArea(filePath: string): string;
|
|
25
|
+
/** Width of the check-name column in human-readable output. */
|
|
26
|
+
export declare const NAME_COL_WIDTH = 22;
|
|
27
|
+
/** Width of the severity column in --list output. */
|
|
28
|
+
export declare const SEVERITY_COL_WIDTH = 5;
|
|
29
|
+
/** Truncate a string to `max` chars, appending an ellipsis marker. */
|
|
30
|
+
export declare function truncate(s: string, max?: number): string;
|
|
31
|
+
/** Normalize a path to forward-slash relative form, for stable output. */
|
|
32
|
+
export declare function normalizePath(p: string): string;
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility helpers for the checks and runner.
|
|
3
|
+
*
|
|
4
|
+
* - `walkTsFiles` — recursive .ts/.tsx file walker with default exclusions.
|
|
5
|
+
* - `inferAdsaArea` — deterministic area inference (SPEC.md invariant #9).
|
|
6
|
+
* - `isInsideLogCall` — true if a node is an argument to a log.*() call.
|
|
7
|
+
* - `hasAdsaLogCall` — true if a function body contains any matching adsa_path log call.
|
|
8
|
+
* - `truncate` — evidence truncation to 200 chars.
|
|
9
|
+
*/
|
|
10
|
+
import { readdirSync, statSync } from "node:fs";
|
|
11
|
+
import { join, relative, sep } from "node:path";
|
|
12
|
+
/** Extensions this tool recognises. Anything else is silently skipped. */
|
|
13
|
+
export const TS_EXTENSIONS = new Set([".ts", ".tsx"]);
|
|
14
|
+
/** Directories always excluded from the scan. */
|
|
15
|
+
export const ALWAYS_EXCLUDED_DIRS = new Set([
|
|
16
|
+
"node_modules",
|
|
17
|
+
"dist",
|
|
18
|
+
"build",
|
|
19
|
+
"out",
|
|
20
|
+
"coverage",
|
|
21
|
+
".git",
|
|
22
|
+
".next",
|
|
23
|
+
".turbo",
|
|
24
|
+
".cache",
|
|
25
|
+
]);
|
|
26
|
+
/** Recursively walk a directory and yield .ts/.tsx paths (relative to root). */
|
|
27
|
+
export function walkTsFiles(root) {
|
|
28
|
+
const out = [];
|
|
29
|
+
const stack = [root];
|
|
30
|
+
while (stack.length > 0) {
|
|
31
|
+
const dir = stack.pop();
|
|
32
|
+
let entries;
|
|
33
|
+
try {
|
|
34
|
+
entries = readdirSync(dir);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
for (const name of entries) {
|
|
40
|
+
if (ALWAYS_EXCLUDED_DIRS.has(name))
|
|
41
|
+
continue;
|
|
42
|
+
const full = join(dir, name);
|
|
43
|
+
let st;
|
|
44
|
+
try {
|
|
45
|
+
st = statSync(full);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (st.isDirectory()) {
|
|
51
|
+
stack.push(full);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const dot = name.lastIndexOf(".");
|
|
55
|
+
if (dot < 0)
|
|
56
|
+
continue;
|
|
57
|
+
const ext = name.slice(dot).toLowerCase();
|
|
58
|
+
if (TS_EXTENSIONS.has(ext))
|
|
59
|
+
out.push(relative(root, full));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
out.sort();
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Deterministic ADSA area inference (SPEC.md invariant #9).
|
|
67
|
+
*
|
|
68
|
+
* Order:
|
|
69
|
+
* 1. Deepest enclosing directory whose name is not `src`/`lib`/etc.
|
|
70
|
+
* 2. Basename of the file without extension.
|
|
71
|
+
* 3. "unscoped" fallback.
|
|
72
|
+
*/
|
|
73
|
+
export function inferAdsaArea(filePath) {
|
|
74
|
+
const parts = filePath.split(/[\\/]/).filter((p) => p.length > 0);
|
|
75
|
+
// Drop the filename.
|
|
76
|
+
const dirs = parts.slice(0, -1);
|
|
77
|
+
// Find the deepest dir that is meaningful (not generic).
|
|
78
|
+
const GENERIC = new Set(["src", "lib", "dist", "build", "out", "app", "index"]);
|
|
79
|
+
let deepest;
|
|
80
|
+
for (const d of dirs) {
|
|
81
|
+
if (d && !GENERIC.has(d.toLowerCase()))
|
|
82
|
+
deepest = d.toLowerCase();
|
|
83
|
+
}
|
|
84
|
+
if (deepest)
|
|
85
|
+
return deepest;
|
|
86
|
+
// Fallback to file basename without extension.
|
|
87
|
+
const base = parts[parts.length - 1] ?? "unscoped";
|
|
88
|
+
const dot = base.lastIndexOf(".");
|
|
89
|
+
return (dot > 0 ? base.slice(0, dot) : base).toLowerCase();
|
|
90
|
+
}
|
|
91
|
+
/** Width of the check-name column in human-readable output. */
|
|
92
|
+
export const NAME_COL_WIDTH = 22;
|
|
93
|
+
/** Width of the severity column in --list output. */
|
|
94
|
+
export const SEVERITY_COL_WIDTH = 5;
|
|
95
|
+
/** Truncate a string to `max` chars, appending an ellipsis marker. */
|
|
96
|
+
export function truncate(s, max = 200) {
|
|
97
|
+
if (s.length <= max)
|
|
98
|
+
return s;
|
|
99
|
+
return s.slice(0, max - 3) + "...";
|
|
100
|
+
}
|
|
101
|
+
/** Normalize a path to forward-slash relative form, for stable output. */
|
|
102
|
+
export function normalizePath(p) {
|
|
103
|
+
return p.split(sep).join("/");
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stapes-adsa",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AST-based ADSA (Adaptive Dual-System Architecture) linter for TypeScript. Flags slow paths without logging, fast paths doing I/O, decision points without routing, async boundaries missing the adsa_path log line, and invalid {adsa_path, adsa_area} shapes. Auto-fix safe insertions. Install as pre-commit hook. Stable --json for AI agents. Zero telemetry. MIT.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "stapesco <github@stapes.co.za>",
|
|
7
|
+
"homepage": "https://github.com/stapesco/adsa",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/stapesco/adsa.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/stapesco/adsa"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"adsa",
|
|
17
|
+
"fast-path",
|
|
18
|
+
"slow-path",
|
|
19
|
+
"logging",
|
|
20
|
+
"observability",
|
|
21
|
+
"ast",
|
|
22
|
+
"ts-morph",
|
|
23
|
+
"typescript",
|
|
24
|
+
"linter",
|
|
25
|
+
"static-analysis",
|
|
26
|
+
"pre-commit",
|
|
27
|
+
"git-hooks",
|
|
28
|
+
"ai-agent",
|
|
29
|
+
"cli",
|
|
30
|
+
"npx",
|
|
31
|
+
"zero-config",
|
|
32
|
+
"stapes"
|
|
33
|
+
],
|
|
34
|
+
"bin": {
|
|
35
|
+
"stapes-adsa": "dist/cli.js"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md",
|
|
40
|
+
"AGENTS.md",
|
|
41
|
+
"SKILL.md",
|
|
42
|
+
"CHANGELOG.md",
|
|
43
|
+
"SECURITY.md",
|
|
44
|
+
"LICENSE"
|
|
45
|
+
],
|
|
46
|
+
"type": "module",
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"prebuild": "rm -rf dist",
|
|
52
|
+
"build": "tsc",
|
|
53
|
+
"prepublishOnly": "npm run build && npm test",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:watch": "vitest"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/node": "^20.10.0",
|
|
59
|
+
"typescript": "^5.3.0",
|
|
60
|
+
"vitest": "^1.0.0"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"commander": "^11.1.0",
|
|
64
|
+
"ts-morph": "^22.0.0"
|
|
65
|
+
}
|
|
66
|
+
}
|