specrails-core 1.0.1 → 1.2.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.
@@ -9,7 +9,7 @@ Run a full health check for **{{PROJECT_NAME}}**: detect available tools, execut
9
9
 
10
10
  **Input:** $ARGUMENTS — optional flags:
11
11
  - `--since <date>` — use the report from this date (ISO format: YYYY-MM-DD) as the comparison baseline instead of the most recent
12
- - `--only <checks>` — comma-separated subset to run. Valid values: `tests`, `coverage`, `lint`, `complexity`, `deps`, `perf`
12
+ - `--only <checks>` — comma-separated subset to run. Valid values: `tests`, `coverage`, `lint`, `complexity`, `deps`, `perf`, `static`
13
13
  - `--save` — always save the snapshot even when `--only` is used (default: skip save for partial runs)
14
14
 
15
15
  ---
@@ -29,8 +29,8 @@ Parse `$ARGUMENTS` to set runtime variables.
29
29
  1. Scan `$ARGUMENTS` for `--since <date>`. If found, set `COMPARE_DATE=<date>`. Strip from arguments.
30
30
  2. Scan for `--only <checks>`. If found:
31
31
  - Split `<checks>` on commas to produce an array.
32
- - Validate each entry against the allowed set: `tests`, `coverage`, `lint`, `complexity`, `deps`, `perf`.
33
- - If any unknown value is found: print `Error: unknown check "<value>". Valid checks: tests, coverage, lint, complexity, deps, perf` and stop.
32
+ - Validate each entry against the allowed set: `tests`, `coverage`, `lint`, `complexity`, `deps`, `perf`, `static`.
33
+ - If any unknown value is found: print `Error: unknown check "<value>". Valid checks: tests, coverage, lint, complexity, deps, perf, static` and stop.
34
34
  - Set `CHECKS_FILTER=<validated-array>`.
35
35
  - Set `SAVE_SNAPSHOT=false` (partial run — snapshot may be incomplete).
36
36
  3. Scan for `--save`. If found, set `SAVE_SNAPSHOT=true` regardless of `CHECKS_FILTER`.
@@ -38,7 +38,7 @@ Parse `$ARGUMENTS` to set runtime variables.
38
38
  **Print active configuration:**
39
39
 
40
40
  ```
41
- Running checks: <all | comma-separated list> | Comparing to: <COMPARE_DATE or "latest">
41
+ Running checks: <all | comma-separated list> | Static analysis: <enabled|disabled> | Comparing to: <COMPARE_DATE or "latest">
42
42
  ```
43
43
 
44
44
  ---
@@ -79,8 +79,11 @@ For each category, set two variables:
79
79
  | complexity | yes/no | <tool or N/A> |
80
80
  | deps | yes/no | <tool or N/A> |
81
81
  | perf | yes/no | <tool or N/A> |
82
+ | static | yes | ai-analysis |
82
83
  ```
83
84
 
85
+ The `static` check is always available (AI-assisted analysis requires no external tools).
86
+
84
87
  ---
85
88
 
86
89
  ## Phase 2: Load Previous Report
@@ -248,6 +251,99 @@ perf: <PASS|FAIL|SKIPPED> (<tool>)
248
251
 
249
252
  ---
250
253
 
254
+ ## Phase 3.5: Static Code Analysis
255
+
256
+ Perform static code inspection to detect issues that tools cannot surface: missing documentation, broken imports, and unused exports. These checks are language-aware and use AI-assisted code reading rather than external tooling.
257
+
258
+ Always exclude the following directories from analysis:
259
+ - `node_modules/`, `.git/`, `dist/`, `build/`, `vendor/`, `.claude/`, `coverage/`
260
+
261
+ For each finding, record a structured object:
262
+ ```
263
+ { severity: "critical"|"warning"|"info", check: string, file: string, line: number, description: string, action: string }
264
+ ```
265
+
266
+ Set `STATIC_FINDINGS = []` before starting. Append each finding to this list.
267
+
268
+ ---
269
+
270
+ ### Check: documentation
271
+
272
+ Scan public-facing code units (exported functions, classes, interfaces, modules) for missing documentation comments.
273
+
274
+ **Language-specific rules:**
275
+
276
+ - **TypeScript/JavaScript**: Identify functions/classes with `export` keyword. Check whether a JSDoc block (`/** ... */`) appears immediately above the declaration. Missing JSDoc on exported symbols = finding.
277
+ - **Python**: Identify top-level functions, classes, and methods. Check for a docstring (a string literal as the first statement in the body). Missing docstring on public (non-underscore-prefixed) symbols = finding.
278
+ - **Ruby**: Identify public methods and classes. Check for a YARD/RDoc comment (`# @param`, `##`, or any comment block) immediately above the definition. Missing comment on public methods in lib/ = finding.
279
+ - **Go**: Identify exported types and functions (capitalized names). Check for a doc comment (line starting with `// <ExportedName>`) immediately above. Missing comment on exported symbols = finding.
280
+
281
+ **Severity assignment:**
282
+ - `critical`: Main entry points (e.g., `index.ts`, `main.go`, `app.rb`) with no module-level documentation
283
+ - `warning`: Exported/public function or class with no documentation
284
+ - `info`: Non-exported function over 20 lines with no documentation
285
+
286
+ Scan all source files matching the detected language(s). Skip test files (`*.test.*`, `*.spec.*`, `_test.go`, `spec/**`).
287
+
288
+ Append each finding to `STATIC_FINDINGS`.
289
+
290
+ ---
291
+
292
+ ### Check: broken_imports
293
+
294
+ Detect import/require statements that reference non-existent paths. Focus on local relative imports (skip `node_modules`, stdlib, installed packages — these are validated by the runtime).
295
+
296
+ **Language-specific rules:**
297
+
298
+ - **TypeScript/JavaScript**: Scan all `import ... from '...'` and `require('...')` statements. For each import path starting with `.` or `/`:
299
+ 1. Resolve the path relative to the source file.
300
+ 2. Check whether the resolved file exists (try: exact path, `.ts`, `.tsx`, `.js`, `.jsx`, `/index.ts`, `/index.js`).
301
+ 3. If no match found: add a `critical` finding.
302
+ - **Python**: Scan all `from . import`, `from .. import`, and `import X` with relative markers. Resolve relative paths from the package root. If the resolved module file does not exist: add a `critical` finding.
303
+ - **Ruby**: Scan `require_relative '...'` statements. Resolve paths from the source file's directory (try `.rb` extension if missing). If the resolved path does not exist: add a `critical` finding.
304
+ - **Go**: Skip — Go's compiler validates imports; broken imports would fail the build.
305
+
306
+ **Severity:** Always `critical` — a broken import is a runtime error.
307
+
308
+ Append each finding to `STATIC_FINDINGS`.
309
+
310
+ ---
311
+
312
+ ### Check: unused_exports
313
+
314
+ Detect exported symbols that are never imported anywhere else in the codebase. Limit to the project source — do not flag exports intended as a library's public API (i.e., symbols re-exported from a barrel file like `index.ts`).
315
+
316
+ **Language-specific rules:**
317
+
318
+ - **TypeScript/JavaScript**:
319
+ 1. Build a list of all exported symbols: scan `export const`, `export function`, `export class`, `export type`, `export interface`, `export default` across all non-test `.ts`/`.tsx`/`.js` files.
320
+ 2. For each exported symbol, search the rest of the codebase for imports of that symbol by name. Exclude the barrel/index file itself.
321
+ 3. If no import found anywhere: add a finding with severity `warning` (may be an intentional API export) unless the file is not `index.ts` and not under `src/` root — in that case severity is `info`.
322
+ - **Python**:
323
+ 1. Scan for symbols listed in `__all__` or public top-level definitions (non-underscore).
324
+ 2. Search for usages across the codebase. If the symbol appears only in its definition file: add a `info` finding.
325
+ - **Ruby**:
326
+ 1. Scan for public constants, classes, and modules defined in `lib/`. Search for `require` or direct references elsewhere. If a class/module in `lib/` is never referenced outside its own file: add a `warning` finding.
327
+
328
+ **Severity:** `warning` for clearly unexported contexts; `info` when the symbol could be part of a public API.
329
+
330
+ Append each finding to `STATIC_FINDINGS`.
331
+
332
+ ---
333
+
334
+ ### Phase 3.5 summary
335
+
336
+ After all three static checks complete, print:
337
+
338
+ ```
339
+ documentation: <N findings> (<N critical, N warning, N info>)
340
+ broken_imports: <N findings> (<N critical>)
341
+ unused_exports: <N findings> (<N warning, N info>)
342
+ Static findings total: <N>
343
+ ```
344
+
345
+ ---
346
+
251
347
  ## Phase 4: Build Health Report
252
348
 
253
349
  Using all `RESULT_<CHECK>` values and `PREV_REPORT` (if `IS_FIRST_RUN=false`), compute the final health report object.
@@ -315,6 +411,7 @@ HEALTH_REPORT = {
315
411
  deps: { status, tool, metrics: { vuln_critical, vuln_high, vuln_moderate, vuln_low, vuln_total } },
316
412
  perf: { status, tool, metrics: { perf_p50_ms, perf_p95_ms, perf_p99_ms, perf_custom } }
317
413
  },
414
+ static_findings: <STATIC_FINDINGS array — each: { severity, check, file, line, description, action }>,
318
415
  grade: <"A"|"B"|"C"|"D"|"F">,
319
416
  regressions: <REGRESSIONS array>,
320
417
  comparison_report: <PREV_REPORT_PATH basename or null>
@@ -364,6 +461,40 @@ Regressions detected: N
364
461
  - <check>: <metric> changed from X to Y (<delta>)
365
462
  <if N == 0:>
366
463
  No regressions detected.
464
+
465
+ ---
466
+
467
+ ### Static Analysis Findings [<N total: N critical, N warning, N info>]
468
+
469
+ <if STATIC_FINDINGS is empty:>
470
+ No static findings detected.
471
+
472
+ <if STATIC_FINDINGS is non-empty:>
473
+
474
+ | Severity | Check | File | Line | Finding | Action |
475
+ |----------|-------|------|------|---------|--------|
476
+ <for each finding in STATIC_FINDINGS, sorted by severity (critical first, then warning, then info):>
477
+ | <CRITICAL|WARNING|INFO> | <check> | <file> | <line> | <description> | <action> |
478
+
479
+ <if any critical findings:>
480
+ #### Critical Findings (require immediate attention)
481
+ <for each critical finding:>
482
+ **[CRITICAL] <check>** — `<file>:<line>`
483
+ > <description>
484
+ > **Action:** <action>
485
+
486
+ <if any warning findings (limit to top 5 by file breadth — prefer findings spread across more files):>
487
+ #### Warnings (recommended fixes)
488
+ <for each warning finding, up to 5:>
489
+ **[WARNING] <check>** — `<file>:<line>`
490
+ > <description>
491
+ > **Action:** <action>
492
+ <if more than 5 warning findings:>
493
+ > ... and N more warnings. Run with `--only static` to see all.
494
+
495
+ <if any info findings:>
496
+ #### Info (low-priority improvements)
497
+ Summary: N info-level findings across N files. Run with `--only static` to see full list.
367
498
  ```
368
499
 
369
500
  For delta display: wrap positive deltas on error/failure metrics in `(+N)` to indicate regression; wrap negative deltas on pass-rate/coverage in `(-N%)` styled as improvement. For terminal rendering, use plain notation — the sign alone conveys direction.