tina4-nodejs 3.13.99 → 3.13.101
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/CLAUDE.md +3 -3
- package/README.md +16 -0
- package/package.json +1 -1
- package/packages/cli/dist/bin.js +585 -692
- package/packages/cli/src/bin.ts +0 -6
- package/packages/core/dist/index.js +573 -550
- package/packages/core/src/ai.ts +15 -6
- package/packages/core/src/aiClient.ts +288 -0
- package/packages/core/src/devAdmin.ts +1 -2
- package/packages/core/src/index.ts +2 -0
- package/packages/core/src/metrics.ts +79 -631
- package/packages/frond/dist/index.js +118 -36
- package/packages/frond/src/engine.ts +195 -45
- package/packages/orm/dist/index.js +574 -557
- package/types/core/src/ai.d.ts +29 -0
- package/types/core/src/aiClient.d.ts +66 -0
- package/types/core/src/index.d.ts +2 -0
- package/types/core/src/metrics.d.ts +0 -35
- package/types/frond/src/engine.d.ts +50 -8
- package/packages/cli/src/commands/metrics.ts +0 -160
- package/types/cli/src/commands/metrics.d.ts +0 -6
package/types/core/src/ai.d.ts
CHANGED
|
@@ -6,6 +6,35 @@ export interface AiTool {
|
|
|
6
6
|
}
|
|
7
7
|
export declare const AI_TOOLS: AiTool[];
|
|
8
8
|
export declare const DEV_SKILL = "tina4-developer-nodejs";
|
|
9
|
+
/**
|
|
10
|
+
* Fetch a set of skill files over the network SYNCHRONOUSLY.
|
|
11
|
+
*
|
|
12
|
+
* Node has no built-in synchronous HTTP, and the whole installer chain
|
|
13
|
+
* (installSelected → installForTool → installClaudeSkills) is synchronous and
|
|
14
|
+
* can't be made async without breaking its existing callers/tests. So we run
|
|
15
|
+
* ONE blocking child `node` process that fetches every URL in parallel with the
|
|
16
|
+
* global `fetch` (Node 18+) and writes each body to all of its destinations.
|
|
17
|
+
* The child prints a JSON array of the URLs it fetched successfully; any fetch
|
|
18
|
+
* failure is skipped, never fatal.
|
|
19
|
+
*
|
|
20
|
+
* MEASURED (2026-08-13): a real GitHub raw-content fetch occasionally drops a
|
|
21
|
+
* request under load (transient DNS/TLS hiccup, not a missing file — every
|
|
22
|
+
* URL here resolves fine on its own) while its siblings in the same batch
|
|
23
|
+
* succeed. One retry pass over only transport failures and transient HTTP
|
|
24
|
+
* statuses, still inside the same child process, fixes that for real installer
|
|
25
|
+
* users too. Permanent 4xx responses are final answers and are not retried.
|
|
26
|
+
*
|
|
27
|
+
* Exported (like `writeOrMerge`/`markersFor`/`skillBlock` above) so
|
|
28
|
+
* aiFetchRetry.test.ts can drive it directly against a real local server —
|
|
29
|
+
* a pure visibility change, no behaviour change.
|
|
30
|
+
*
|
|
31
|
+
* @param jobs one entry per unique URL, with every file path it should land in
|
|
32
|
+
* @returns the set of URLs that were fetched and written to disk
|
|
33
|
+
*/
|
|
34
|
+
export declare function downloadSkillsSync(jobs: {
|
|
35
|
+
url: string;
|
|
36
|
+
dests: string[];
|
|
37
|
+
}[]): Set<string>;
|
|
9
38
|
/**
|
|
10
39
|
* Install the Tina4 SKILL.md skills into the project AND the global
|
|
11
40
|
* ~/.claude/skills, fetched from the release ref matching this framework
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export declare class AiError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export declare class AiConfigError extends AiError {
|
|
4
|
+
}
|
|
5
|
+
export declare class AiTimeoutError extends AiError {
|
|
6
|
+
}
|
|
7
|
+
export declare class AiParseError extends AiError {
|
|
8
|
+
}
|
|
9
|
+
export declare class AiHTTPError extends AiError {
|
|
10
|
+
readonly status: number | null;
|
|
11
|
+
constructor(message: string, status?: number | null);
|
|
12
|
+
}
|
|
13
|
+
export interface ChatResponse {
|
|
14
|
+
text: string;
|
|
15
|
+
model: string;
|
|
16
|
+
usage: {
|
|
17
|
+
promptTokens: number;
|
|
18
|
+
completionTokens: number;
|
|
19
|
+
totalTokens: number;
|
|
20
|
+
};
|
|
21
|
+
finishReason: string | null;
|
|
22
|
+
raw: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
export interface AiMessage {
|
|
25
|
+
role: "system" | "user" | "assistant";
|
|
26
|
+
content: string;
|
|
27
|
+
}
|
|
28
|
+
export interface AiChatOptions {
|
|
29
|
+
model?: string;
|
|
30
|
+
temperature?: number;
|
|
31
|
+
maxTokens?: number;
|
|
32
|
+
stream?: boolean;
|
|
33
|
+
timeout?: number;
|
|
34
|
+
provider?: "local" | "openai" | "anthropic";
|
|
35
|
+
}
|
|
36
|
+
export interface AiEmbedOptions {
|
|
37
|
+
model?: string;
|
|
38
|
+
timeout?: number;
|
|
39
|
+
provider?: "local" | "openai" | "anthropic";
|
|
40
|
+
}
|
|
41
|
+
export declare class Ai {
|
|
42
|
+
static chat(messages: AiMessage[], options: AiChatOptions & {
|
|
43
|
+
stream: true;
|
|
44
|
+
}): AsyncGenerator<string>;
|
|
45
|
+
static chat(messages: AiMessage[], options?: AiChatOptions & {
|
|
46
|
+
stream?: false;
|
|
47
|
+
}): Promise<ChatResponse>;
|
|
48
|
+
static complete(prompt: string, options?: Omit<AiChatOptions, "stream">): Promise<string>;
|
|
49
|
+
static embed(textOrTexts: string | string[], options?: AiEmbedOptions): Promise<number[] | number[][]>;
|
|
50
|
+
private static validateMessages;
|
|
51
|
+
private static number;
|
|
52
|
+
private static config;
|
|
53
|
+
private static endpoint;
|
|
54
|
+
private static headers;
|
|
55
|
+
private static chatBody;
|
|
56
|
+
private static open;
|
|
57
|
+
private static readBody;
|
|
58
|
+
private static retryDelay;
|
|
59
|
+
private static requestJson;
|
|
60
|
+
private static normalizeChat;
|
|
61
|
+
private static chatResponse;
|
|
62
|
+
private static streamDelta;
|
|
63
|
+
private static streamData;
|
|
64
|
+
private static streamError;
|
|
65
|
+
private static streamRequest;
|
|
66
|
+
}
|
|
@@ -55,6 +55,8 @@ export { HtmlElement, htmlElement, addHtmlHelpers, Raw, SafeString } from "./htm
|
|
|
55
55
|
export { renderErrorOverlay, isDebugMode } from "./errorOverlay.js";
|
|
56
56
|
export { AI_TOOLS, isInstalled, showMenu, installSelected, installAll, generateContext } from "./ai.js";
|
|
57
57
|
export type { AiTool } from "./ai.js";
|
|
58
|
+
export { Ai, AiError, AiConfigError, AiHTTPError, AiTimeoutError, AiParseError } from "./aiClient.js";
|
|
59
|
+
export type { ChatResponse, AiMessage, AiChatOptions, AiEmbedOptions } from "./aiClient.js";
|
|
58
60
|
export type { ImapMessage, ImapFullMessage, ImapAttachment } from "./messenger.js";
|
|
59
61
|
export { LiteBackend } from "./queueBackends/liteBackend.js";
|
|
60
62
|
export { RabbitMQBackend, parseAmqpUrl } from "./queueBackends/rabbitmqBackend.js";
|
|
@@ -1,41 +1,6 @@
|
|
|
1
|
-
export declare function quickMetrics(root?: string): Record<string, any>;
|
|
2
|
-
/**
|
|
3
|
-
* The native metrics engine could not produce a payload.
|
|
4
|
-
*
|
|
5
|
-
* Thrown instead of falling back to a second implementation.
|
|
6
|
-
*/
|
|
7
1
|
export declare class MetricsEngineError extends Error {
|
|
8
2
|
constructor(message: string);
|
|
9
3
|
}
|
|
10
|
-
export declare const SEVERITY_RANK: Record<string, number>;
|
|
11
|
-
/**
|
|
12
|
-
* Return [directory to scan, scanMode] for any metrics producer.
|
|
13
|
-
*
|
|
14
|
-
* The engine is language-agnostic and cannot know which directory holds a
|
|
15
|
-
* framework package, so root resolution and the "framework" label stay here,
|
|
16
|
-
* shared by the census and the engine adapter so the two never disagree.
|
|
17
|
-
*/
|
|
18
|
-
export declare function resolveScanTarget(root?: string): [string, string];
|
|
19
|
-
/** Absolute path to the tina4 CLI binary, or null when it is not installed. */
|
|
20
4
|
export declare function enginePath(): string | null;
|
|
21
|
-
/** Full code analysis from the native engine, shaped for the dashboard. */
|
|
22
5
|
export declare function fullAnalysis(root?: string): Record<string, any>;
|
|
23
|
-
export interface OffendersResult {
|
|
24
|
-
offenders: Record<string, any>[];
|
|
25
|
-
summary: Record<string, any>;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Top code-health offenders from the native engine.
|
|
29
|
-
*
|
|
30
|
-
* The engine ranks and severity-tags them, and its own --fail-on gate reads the
|
|
31
|
-
* same list, so the CLI and the dashboard can never disagree about what counts
|
|
32
|
-
* as an offender.
|
|
33
|
-
*/
|
|
34
|
-
export declare function offenders(root?: string, top?: number): OffendersResult;
|
|
35
|
-
/**
|
|
36
|
-
* Per-file metrics from the native engine.
|
|
37
|
-
*
|
|
38
|
-
* The engine accepts a single file for --path, so one code path serves both the
|
|
39
|
-
* whole-tree scan and one file.
|
|
40
|
-
*/
|
|
41
6
|
export declare function fileDetail(filePath: string): Record<string, any>;
|
|
@@ -14,6 +14,15 @@ export interface LiveResponse {
|
|
|
14
14
|
}
|
|
15
15
|
/** WebSocket broadcaster hook wired by @tina4/core so pushLive can broadcast. */
|
|
16
16
|
export type LiveBroadcaster = (wsPath: string | null, name: string, envelope: string) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Cache for parsed filter chains: expr string -> [variable, filters].
|
|
19
|
+
* Exported (like TEMPLATE_CACHE_MAX) so the ADR-0004 bound has something for
|
|
20
|
+
* a test to inspect directly — module-level state has no instance to read
|
|
21
|
+
* off, unlike `compiled`/`compiledStrings`/`fragmentCache`.
|
|
22
|
+
*/
|
|
23
|
+
export declare const filterChainCache: Map<string, [string, [string, unknown[]][]]>;
|
|
24
|
+
/** Cache for parsed dotted/bracket paths: expr string -> [parts, fromBracket]. Exported for the same reason as filterChainCache. */
|
|
25
|
+
export declare const pathParseCache: Map<string, [string[], boolean[]]>;
|
|
17
26
|
/**
|
|
18
27
|
* Hard cap on the template caches — `compiled` and `compiledStrings`
|
|
19
28
|
* (ADR-0004, parity with PHP/Python/Ruby TEMPLATE_CACHE_MAX).
|
|
@@ -26,6 +35,18 @@ export type LiveBroadcaster = (wsPath: string | null, name: string, envelope: st
|
|
|
26
35
|
* dynamically adds an entry per distinct string.
|
|
27
36
|
*/
|
|
28
37
|
export declare const TEMPLATE_CACHE_MAX = 256;
|
|
38
|
+
/**
|
|
39
|
+
* Hard cap on every per-expression memo cache — `filterChainCache` and
|
|
40
|
+
* `pathParseCache` (ADR-0004, parity with PHP's MEMO_CACHE_MAX and the
|
|
41
|
+
* Python master's `@lru_cache(maxsize=1024)` on the equivalent module-level
|
|
42
|
+
* parsers). Deliberately higher than TEMPLATE_CACHE_MAX: one entry here is a
|
|
43
|
+
* small parsed-path array, orders of magnitude smaller than a token list.
|
|
44
|
+
*
|
|
45
|
+
* Also reused for `fragmentCache` (the `{% cache %}` tag's runtime store):
|
|
46
|
+
* TEMPLATE_CACHE_MAX, not this one — a rendered fragment is a whole HTML
|
|
47
|
+
* string, the same order of magnitude as a compiled template.
|
|
48
|
+
*/
|
|
49
|
+
export declare const MEMO_CACHE_MAX = 1024;
|
|
29
50
|
/**
|
|
30
51
|
* Set the session ID used by formToken() / form_token() for CSRF session binding.
|
|
31
52
|
*/
|
|
@@ -95,20 +116,16 @@ export declare class Frond {
|
|
|
95
116
|
sandbox(filters?: string[], tags?: string[], vars?: string[]): Frond;
|
|
96
117
|
unsandbox(): Frond;
|
|
97
118
|
/**
|
|
98
|
-
* Register a custom filter
|
|
99
|
-
*
|
|
100
|
-
* the live instance's local filter map also receives the addition
|
|
101
|
-
* immediately. Mirrors Python's _ClassOrInstanceMethod dual-call.
|
|
119
|
+
* Register a custom filter on this instance only. Use the static method
|
|
120
|
+
* for process-global registration. tina4: ADR-0052.
|
|
102
121
|
*/
|
|
103
122
|
addFilter(name: string, fn: FilterFn): void;
|
|
104
123
|
/**
|
|
105
|
-
* Register a global variable
|
|
106
|
-
* at class level — see ``addFilter`` for the dual-call semantics.
|
|
124
|
+
* Register a global variable on this instance only.
|
|
107
125
|
*/
|
|
108
126
|
addGlobal(name: string, value: unknown): void;
|
|
109
127
|
/**
|
|
110
|
-
* Register a custom test
|
|
111
|
-
* ``addFilter`` for the dual-call semantics.
|
|
128
|
+
* Register a custom test on this instance only.
|
|
112
129
|
*/
|
|
113
130
|
addTest(name: string, fn: TestFn): void;
|
|
114
131
|
/**
|
|
@@ -143,6 +160,31 @@ export declare class Frond {
|
|
|
143
160
|
private executeWithSource;
|
|
144
161
|
private execute;
|
|
145
162
|
private extractBlocks;
|
|
163
|
+
/**
|
|
164
|
+
* Depth-aware block substitution against `source` (typically the
|
|
165
|
+
* fully-resolved root template).
|
|
166
|
+
*
|
|
167
|
+
* A single regex `.replace()` pass (the flat `pattern` this replaces in
|
|
168
|
+
* renderWithBlocks) pairs an OUTER block's open tag with the FIRST
|
|
169
|
+
* `{% endblock %}` found -- which, when the outer block wraps a NESTED
|
|
170
|
+
* `{% block %}`, is the nested block's own close tag, not the outer's.
|
|
171
|
+
* That silently truncates the outer block's captured content and drops
|
|
172
|
+
* everything after the inner endblock (the root-nested-block
|
|
173
|
+
* content-loss bug). This scans with an open/close depth counter
|
|
174
|
+
* instead (mirroring extractBlocks), so an outer block always captures
|
|
175
|
+
* its FULL body, nested child blocks included.
|
|
176
|
+
*
|
|
177
|
+
* The content chosen for each block -- the child override in `blocks`
|
|
178
|
+
* if present, else the block's own default body -- is then recursively
|
|
179
|
+
* substituted against the SAME `blocks` map before being tokenized and
|
|
180
|
+
* rendered, so a block nested inside another block resolves correctly
|
|
181
|
+
* regardless of which template in the inheritance chain declared the
|
|
182
|
+
* nesting (the root, an intermediate, however many levels deep).
|
|
183
|
+
*
|
|
184
|
+
* `{{ parent() }}` / `{{ super() }}` inside a block still render that
|
|
185
|
+
* block's OWN default content at this level (lazy, on first call).
|
|
186
|
+
*/
|
|
187
|
+
private substituteBlocks;
|
|
146
188
|
private renderWithBlocks;
|
|
147
189
|
private renderTokens;
|
|
148
190
|
/**
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI command: metrics — Rank top code-quality offenders.
|
|
3
|
-
*
|
|
4
|
-
* Exposes the existing static analyzer (cyclomatic complexity, maintainability,
|
|
5
|
-
* large-file, too-many-functions, and the now-precise has_tests) on the CLI as
|
|
6
|
-
* a self-monitoring tool.
|
|
7
|
-
*
|
|
8
|
-
* tina4nodejs metrics # human report, scans src/ (or framework)
|
|
9
|
-
* tina4nodejs metrics --top 10 # only the worst 10
|
|
10
|
-
* tina4nodejs metrics --path packages/core/src # scan a specific directory
|
|
11
|
-
* tina4nodejs metrics --json # machine-readable for CI (ONLY json printed)
|
|
12
|
-
* tina4nodejs metrics --fail-on warn # exit 1 if any warn/error offender
|
|
13
|
-
* tina4nodejs metrics --fail-on error # exit 1 only on error-severity
|
|
14
|
-
*
|
|
15
|
-
* Returns the intended process exit code (the caller exits). Splitting "compute
|
|
16
|
-
* exit code" from "exit the process" keeps the handler unit-testable.
|
|
17
|
-
*/
|
|
18
|
-
import { offenders, MetricsEngineError } from "../../../core/src/metrics.js";
|
|
19
|
-
|
|
20
|
-
type Flags = {
|
|
21
|
-
top: number;
|
|
22
|
-
json: boolean;
|
|
23
|
-
path: string;
|
|
24
|
-
failOn: "warn" | "error" | null;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/** Parse `--top N`, `--json`, `--fail-on warn|error`, `--path DIR` out of argv. */
|
|
28
|
-
function parseFlags(args: string[]): Flags | { error: string } {
|
|
29
|
-
const flags: Flags = { top: 20, json: false, path: "src", failOn: null };
|
|
30
|
-
|
|
31
|
-
for (let i = 0; i < args.length; i++) {
|
|
32
|
-
const a = args[i];
|
|
33
|
-
switch (a) {
|
|
34
|
-
case "--json":
|
|
35
|
-
flags.json = true;
|
|
36
|
-
break;
|
|
37
|
-
case "--top": {
|
|
38
|
-
const v = args[++i];
|
|
39
|
-
if (v === undefined || !/^\d+$/.test(v)) {
|
|
40
|
-
return { error: `--top expects a number (got '${v ?? ""}')` };
|
|
41
|
-
}
|
|
42
|
-
flags.top = parseInt(v, 10);
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
case "--path": {
|
|
46
|
-
const v = args[++i];
|
|
47
|
-
if (v === undefined) return { error: "--path expects a directory" };
|
|
48
|
-
flags.path = v;
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
case "--fail-on": {
|
|
52
|
-
const v = args[++i];
|
|
53
|
-
if (v !== "warn" && v !== "error") {
|
|
54
|
-
return { error: `invalid --fail-on '${v ?? ""}' (use warn or error)` };
|
|
55
|
-
}
|
|
56
|
-
flags.failOn = v;
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
default:
|
|
60
|
-
return { error: `unknown option '${a}'` };
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return flags;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Run the metrics report. Returns the process exit code; does NOT call
|
|
69
|
-
* process.exit (the bin wrapper does). 0 = ok / below threshold, 1 = gated
|
|
70
|
-
* failure, 2 = bad arguments / analysis error.
|
|
71
|
-
*/
|
|
72
|
-
export function runMetrics(args: string[] = []): number {
|
|
73
|
-
const parsed = parseFlags(args);
|
|
74
|
-
if ("error" in parsed) {
|
|
75
|
-
console.log(` ${parsed.error}`);
|
|
76
|
-
return 2;
|
|
77
|
-
}
|
|
78
|
-
const { top, json, path, failOn } = parsed;
|
|
79
|
-
|
|
80
|
-
// ONE engine run. Ask for every offender and slice for display: the gate must
|
|
81
|
-
// read the FULL set, not the printed top-N, and the old second call re-ran the
|
|
82
|
-
// whole analysis (its "mtime-cached" comment stopped being true when the
|
|
83
|
-
// in-process analyzer and its cache were deleted -- ADR-0002).
|
|
84
|
-
let result;
|
|
85
|
-
try {
|
|
86
|
-
result = offenders(path, Number.MAX_SAFE_INTEGER);
|
|
87
|
-
} catch (e) {
|
|
88
|
-
if (e instanceof MetricsEngineError) {
|
|
89
|
-
console.error(` metrics error: ${e.message}`);
|
|
90
|
-
return 2;
|
|
91
|
-
}
|
|
92
|
-
throw e;
|
|
93
|
-
}
|
|
94
|
-
const summary = result.summary;
|
|
95
|
-
const allOffenders = result.offenders;
|
|
96
|
-
const found = allOffenders.slice(0, top);
|
|
97
|
-
|
|
98
|
-
const severities = new Set(allOffenders.map((o) => o.severity));
|
|
99
|
-
let exitCode = 0;
|
|
100
|
-
if (failOn === "warn" && (severities.has("warn") || severities.has("error"))) {
|
|
101
|
-
exitCode = 1;
|
|
102
|
-
} else if (failOn === "error" && severities.has("error")) {
|
|
103
|
-
exitCode = 1;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (json) {
|
|
107
|
-
// Print ONLY the JSON — machine-readable for CI / tooling.
|
|
108
|
-
console.log(JSON.stringify({ summary, offenders: found }, null, 2));
|
|
109
|
-
return exitCode;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ── Human report ──────────────────────────────────────────────────
|
|
113
|
-
const useColor = Boolean(process.stdout.isTTY);
|
|
114
|
-
const c = (text: string, code: string): string =>
|
|
115
|
-
useColor ? `\x1b[${code}m${text}\x1b[0m` : text;
|
|
116
|
-
const sevColor: Record<string, string> = { error: "31", warn: "33", info: "2" }; // red / yellow / dim
|
|
117
|
-
|
|
118
|
-
console.log("");
|
|
119
|
-
console.log(` Tina4 Metrics — ${summary.scan_mode} scan (${summary.scan_root})`);
|
|
120
|
-
console.log(
|
|
121
|
-
` files: ${summary.files_analyzed} ` +
|
|
122
|
-
`functions: ${summary.total_functions} ` +
|
|
123
|
-
`avg complexity: ${summary.avg_complexity} ` +
|
|
124
|
-
`avg maintainability: ${summary.avg_maintainability}`
|
|
125
|
-
);
|
|
126
|
-
console.log(
|
|
127
|
-
` offenders: ${summary.total_offenders} total` +
|
|
128
|
-
(found.length ? ` (showing top ${found.length})` : "")
|
|
129
|
-
);
|
|
130
|
-
console.log("");
|
|
131
|
-
|
|
132
|
-
if (found.length === 0) {
|
|
133
|
-
console.log(" " + c("✓ no offenders — clean", "32"));
|
|
134
|
-
console.log("");
|
|
135
|
-
return exitCode;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Column widths so the table lines up.
|
|
139
|
-
const locs = found.map((o) => `${o.file}:${o.line}`);
|
|
140
|
-
const locW = Math.max("FILE:LINE".length, ...locs.map((s) => s.length));
|
|
141
|
-
const kindW = Math.max("KIND".length, ...found.map((o) => o.kind.length));
|
|
142
|
-
|
|
143
|
-
const pad = (s: string, w: number) => s.padEnd(w);
|
|
144
|
-
const header = ` ${pad("#", 3)} ${pad("SEVERITY", 8)} ${pad("KIND", kindW)} ${pad(
|
|
145
|
-
"FILE:LINE",
|
|
146
|
-
locW
|
|
147
|
-
)} DETAIL`;
|
|
148
|
-
console.log(c(header, "1"));
|
|
149
|
-
console.log(" " + "-".repeat(header.length - 2));
|
|
150
|
-
found.forEach((o, idx) => {
|
|
151
|
-
const i = idx + 1;
|
|
152
|
-
const sevCell = c(pad(o.severity, 8), sevColor[o.severity]);
|
|
153
|
-
console.log(
|
|
154
|
-
` ${String(i).padStart(3)} ${sevCell} ${pad(o.kind, kindW)} ` +
|
|
155
|
-
`${pad(locs[idx], locW)} ${o.detail}`
|
|
156
|
-
);
|
|
157
|
-
});
|
|
158
|
-
console.log("");
|
|
159
|
-
return exitCode;
|
|
160
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Run the metrics report. Returns the process exit code; does NOT call
|
|
3
|
-
* process.exit (the bin wrapper does). 0 = ok / below threshold, 1 = gated
|
|
4
|
-
* failure, 2 = bad arguments / analysis error.
|
|
5
|
-
*/
|
|
6
|
-
export declare function runMetrics(args?: string[]): number;
|