octocode-cli 1.5.1 → 1.5.3
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 +28 -9
- package/out/chunks/chunk-375ZNU3A.js +317 -0
- package/out/chunks/{chunk-V64AXUWB.js → chunk-AYILFPX4.js} +1 -1
- package/out/chunks/chunk-BRHVQB64.js +10 -0
- package/out/chunks/{command-help-specs-JZXVSLZ5.js → chunk-KWJUBVXJ.js} +1 -1
- package/out/chunks/chunk-OMXWZJBJ.js +100 -0
- package/out/chunks/command-help-specs-RVJK6V6M.js +8 -0
- package/out/chunks/commands-JFDCC4Q7.js +8 -0
- package/out/chunks/{help-P7TCOYAJ.js → help-H3WGUJO7.js} +1 -1
- package/out/chunks/main-help-BUTDD4IR.js +10 -0
- package/out/chunks/{spinner-URV2OX6O.js → spinner-DXD4JQIU.js} +1 -1
- package/out/chunks/tool-command-L6PVHUNS.js +8 -0
- package/out/octocode-cli.js +1 -1
- package/package.json +2 -2
- package/skills/octocode-brainstorming/scripts/tavily-search.mjs +1 -10
- package/skills/octocode-chrome-devtools/scripts/cdp-runner.mjs +0 -2
- package/skills/octocode-chrome-devtools/scripts/cdp-sandbox.mjs +1 -5
- package/skills/octocode-chrome-devtools/scripts/cdp-template.mjs +1 -10
- package/skills/octocode-chrome-devtools/scripts/open-browser.mjs +0 -3
- package/skills/octocode-chrome-devtools/scripts/sourcemap-resolver.mjs +1 -4
- package/skills/octocode-chrome-devtools/scripts/undercover.mjs +0 -2
- package/skills/octocode-engineer/SKILL.md +7 -33
- package/skills/octocode-engineer/references/cli-reference.md +5 -11
- package/skills/octocode-engineer/references/externals.md +0 -4
- package/skills/octocode-engineer/references/quality-indicators.md +10 -50
- package/skills/octocode-engineer/references/tool-workflows.md +10 -13
- package/skills/octocode-engineer/src/analysis/dependency-summary.test.ts +2 -2
- package/skills/octocode-engineer/src/ast/metrics.test.ts +2 -2
- package/skills/octocode-engineer/src/ast/search-main.ts +2 -15
- package/skills/octocode-engineer/src/ast/search.ts +1 -7
- package/skills/octocode-engineer/src/ast/tree-sitter.test.ts +4 -4
- package/skills/octocode-engineer/src/ast/tree-sitter.ts +2 -2
- package/skills/octocode-engineer/src/collectors/chains.ts +3 -10
- package/skills/octocode-engineer/src/collectors/prototype-pollution.ts +3 -3
- package/skills/octocode-engineer/src/collectors/security.ts +3 -3
- package/skills/octocode-engineer/src/common/ensure-deps.ts +3 -20
- package/skills/octocode-engineer/src/detectors/code-quality.ts +0 -1
- package/skills/octocode-engineer/src/index.test.ts +1 -1
- package/skills/octocode-engineer/src/index.ts +1 -11
- package/skills/octocode-engineer/src/pipeline/affected.ts +2 -7
- package/skills/octocode-engineer/src/pipeline/baseline.ts +2 -8
- package/skills/octocode-engineer/src/pipeline/cache.ts +2 -2
- package/skills/octocode-engineer/src/pipeline/config-loader.test.ts +1 -2
- package/skills/octocode-engineer/src/pipeline/config-loader.ts +0 -9
- package/skills/octocode-engineer/src/pipeline/create-options.ts +2 -15
- package/skills/octocode-engineer/src/pipeline/reporters.ts +2 -12
- package/skills/octocode-engineer/src/reporting/summary-md.test.ts +2 -2
- package/skills/octocode-engineer/src/run.ts +1 -7
- package/skills/octocode-engineer/src/types/constants.ts +0 -5
- package/skills/octocode-engineer/src/types/core.ts +0 -5
- package/skills/octocode-news/eslint.config.mjs +1 -6
- package/skills/octocode-news/src/rss-core.ts +2 -4
- package/skills/octocode-research/ecosystem.config.cjs +0 -31
- package/skills/octocode-research/package.json +1 -1
- package/skills/octocode-research/scripts/server.js +272 -230
- package/skills/octocode-research/src/__tests__/integration/circuitBreaker.test.ts +0 -24
- package/skills/octocode-research/src/__tests__/integration/routes.test.ts +0 -10
- package/skills/octocode-research/src/__tests__/integration/serverLifecycle.test.ts +5 -22
- package/skills/octocode-research/src/__tests__/integration/toolsRoutes.test.ts +0 -27
- package/skills/octocode-research/src/__tests__/unit/circuitBreaker.test.ts +0 -29
- package/skills/octocode-research/src/__tests__/unit/errorHandler.test.ts +1 -12
- package/skills/octocode-research/src/__tests__/unit/httpPreprocess.test.ts +1 -9
- package/skills/octocode-research/src/__tests__/unit/logger.test.ts +0 -7
- package/skills/octocode-research/src/__tests__/unit/queryParser.test.ts +1 -7
- package/skills/octocode-research/src/__tests__/unit/responseBuilder.test.ts +0 -11
- package/skills/octocode-research/src/__tests__/unit/retry.test.ts +1 -18
- package/skills/octocode-research/src/__tests__/unit/schemas.test.ts +2 -25
- package/skills/octocode-research/src/index.ts +0 -27
- package/skills/octocode-research/src/mcpCache.ts +3 -18
- package/skills/octocode-research/src/middleware/errorHandler.ts +1 -4
- package/skills/octocode-research/src/middleware/logger.ts +1 -6
- package/skills/octocode-research/src/middleware/queryParser.ts +3 -22
- package/skills/octocode-research/src/routes/github.ts +0 -11
- package/skills/octocode-research/src/routes/local.ts +0 -12
- package/skills/octocode-research/src/routes/lsp.ts +1 -10
- package/skills/octocode-research/src/routes/package.ts +0 -2
- package/skills/octocode-research/src/routes/prompts.ts +2 -32
- package/skills/octocode-research/src/routes/tools.ts +12 -177
- package/skills/octocode-research/src/server-init.ts +0 -32
- package/skills/octocode-research/src/server.ts +8 -37
- package/skills/octocode-research/src/types/errorGuards.ts +0 -15
- package/skills/octocode-research/src/types/guards.ts +6 -27
- package/skills/octocode-research/src/types/mcp.ts +1 -8
- package/skills/octocode-research/src/types/responses.ts +0 -15
- package/skills/octocode-research/src/utils/asyncTimeout.ts +1 -63
- package/skills/octocode-research/src/utils/circuitBreaker.ts +43 -166
- package/skills/octocode-research/src/utils/colors.ts +3 -17
- package/skills/octocode-research/src/utils/errorQueue.ts +6 -28
- package/skills/octocode-research/src/utils/logger.ts +11 -93
- package/skills/octocode-research/src/utils/resilience.ts +22 -77
- package/skills/octocode-research/src/utils/responseBuilder.ts +26 -78
- package/skills/octocode-research/src/utils/responseFactory.ts +0 -7
- package/skills/octocode-research/src/utils/responseParser.ts +11 -59
- package/skills/octocode-research/src/utils/retry.ts +12 -75
- package/skills/octocode-research/src/utils/routeFactory.ts +10 -61
- package/skills/octocode-research/src/utils/url.ts +0 -5
- package/skills/octocode-research/src/validation/httpPreprocess.ts +15 -61
- package/skills/octocode-research/src/validation/schemas.ts +14 -101
- package/skills/octocode-research/src/validation/toolCallSchema.ts +5 -65
- package/skills/octocode-research/tsdown.config.ts +2 -7
- package/skills/octocode-search-skill/SKILL.md +9 -2
- package/skills/octocode-search-skill/references/discovery-surfaces.md +2 -1
- package/skills/octocode-slides/scripts/navbridge.js +1 -14
- package/skills/octocode-slides/scripts/presenter.js +2 -61
- package/skills/octocode-stats/scripts/build_dashboard.mjs +1 -19
- package/out/chunks/chunk-7476PETK.js +0 -309
- package/out/chunks/chunk-OQBJTZWK.js +0 -60
- package/out/chunks/chunk-UCZCF3BQ.js +0 -9
- package/out/chunks/commands-XBFPLHSQ.js +0 -8
- package/out/chunks/main-help-ULF5PAQY.js +0 -10
- package/out/chunks/tool-command-M6VA7P2F.js +0 -8
- package/skills/octocode-research/AGENTS.md +0 -349
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
* Entry point for `scripts/ast/search.js`. Verifies the @ast-grep native
|
|
4
|
-
* addons and other runtime deps are installed before loading the main
|
|
5
|
-
* search logic. If missing, the bootstrap detects the user's package
|
|
6
|
-
* manager and installs into the skill directory, or prints an actionable
|
|
7
|
-
* manual command.
|
|
8
|
-
*/
|
|
2
|
+
|
|
9
3
|
import { ensureNativeDependencies } from '../common/ensure-deps.js';
|
|
10
4
|
|
|
11
5
|
ensureNativeDependencies(import.meta.url, { tag: '[octocode-ast-search]' });
|
|
@@ -405,10 +405,10 @@ function f() {
|
|
|
405
405
|
|
|
406
406
|
it('computes cognitiveComplexity > 0 for nested control flow', () => {
|
|
407
407
|
const code = `function complexFn(x: number, y: boolean) {
|
|
408
|
-
if (x > 0) {
|
|
409
|
-
if (y) {
|
|
410
|
-
for (let i = 0; i < x; i++) {
|
|
411
|
-
if (i % 2 === 0) {
|
|
408
|
+
if (x > 0) {
|
|
409
|
+
if (y) {
|
|
410
|
+
for (let i = 0; i < x; i++) {
|
|
411
|
+
if (i % 2 === 0) {
|
|
412
412
|
console.log(i);
|
|
413
413
|
}
|
|
414
414
|
}
|
|
@@ -473,8 +473,8 @@ export async function resolveTreeSitter(): Promise<TreeSitterRuntime> {
|
|
|
473
473
|
parserPy.setLanguage(pyLang as Parameters<Parser['setLanguage']>[0]);
|
|
474
474
|
}
|
|
475
475
|
} catch {
|
|
476
|
-
|
|
477
|
-
|
|
476
|
+
void 0;
|
|
477
|
+
}
|
|
478
478
|
|
|
479
479
|
treeSitterRuntime = {
|
|
480
480
|
available: true,
|
|
@@ -4,15 +4,10 @@ import { getLineAndCharacter } from '../common/utils.js';
|
|
|
4
4
|
|
|
5
5
|
import type { FileEntry, MessageChainEntry } from '../types/index.js';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
const MIN_CHAIN_DEPTH = 4;
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
* Walk a property-access or element-access expression to its root and return
|
|
12
|
-
* the full chain text and depth (number of dot-steps).
|
|
13
|
-
*
|
|
14
|
-
* Handles: a.b.c.d a?.b?.c?.d a['b']['c']['d']
|
|
15
|
-
*/
|
|
10
|
+
|
|
16
11
|
function measureChain(node: ts.Node, sourceFile: ts.SourceFile): { text: string; depth: number } | null {
|
|
17
12
|
let depth = 0;
|
|
18
13
|
let current: ts.Node = node;
|
|
@@ -27,14 +22,12 @@ function measureChain(node: ts.Node, sourceFile: ts.SourceFile): { text: string;
|
|
|
27
22
|
|
|
28
23
|
if (depth < MIN_CHAIN_DEPTH) return null;
|
|
29
24
|
|
|
30
|
-
// Avoid reporting intermediate nodes — only report the outermost chain.
|
|
31
|
-
// (The outermost node is the one whose parent is NOT itself a property/element access)
|
|
32
25
|
const parent = node.parent;
|
|
33
26
|
if (
|
|
34
27
|
ts.isPropertyAccessExpression(parent) ||
|
|
35
28
|
ts.isElementAccessExpression(parent)
|
|
36
29
|
) {
|
|
37
|
-
return null;
|
|
30
|
+
return null;
|
|
38
31
|
}
|
|
39
32
|
|
|
40
33
|
return { text: node.getText(sourceFile), depth };
|
|
@@ -16,7 +16,7 @@ const DEEP_MERGE_NAMES = new Set([
|
|
|
16
16
|
'mixin',
|
|
17
17
|
]);
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
function isKeyFromInternalIteration(
|
|
21
21
|
node: ts.ElementAccessExpression,
|
|
22
22
|
sourceFile: ts.SourceFile
|
|
@@ -49,7 +49,7 @@ function isKeyFromInternalIteration(
|
|
|
49
49
|
return false;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
function hasProtoKeyGuard(node: ts.Node, sourceFile: ts.SourceFile): boolean {
|
|
54
54
|
const block = findParentBlock(node);
|
|
55
55
|
if (!block) return false;
|
|
@@ -63,7 +63,7 @@ function hasProtoKeyGuard(node: ts.Node, sourceFile: ts.SourceFile): boolean {
|
|
|
63
63
|
);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
function isTargetSafeObject(
|
|
68
68
|
node: ts.ElementAccessExpression,
|
|
69
69
|
sourceFile: ts.SourceFile
|
|
@@ -50,9 +50,9 @@ const SECRET_PATTERNS = [
|
|
|
50
50
|
const SQL_KEYWORDS =
|
|
51
51
|
/\b(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|TRUNCATE)\b/i;
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
const PLACEHOLDER_PATTERN = /^(YOUR_|REPLACE_ME|<[a-z_-]+>|\$\{|{{)/i;
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
const UUID_PATTERN =
|
|
57
57
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
58
58
|
|
|
@@ -74,7 +74,7 @@ function isPlaceholderOrUuid(value: string): boolean {
|
|
|
74
74
|
return PLACEHOLDER_PATTERN.test(value) || UUID_PATTERN.test(value);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
const METADATA_PROP_NAMES = new Set([
|
|
79
79
|
'suggestedFix',
|
|
80
80
|
'strategy',
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ensures native and pure-JS dependencies required at runtime are installed.
|
|
3
|
-
*
|
|
4
|
-
* The skill's bundle cannot inline native addons (@ast-grep/napi, tree-sitter
|
|
5
|
-
* variants) — they must exist in the skill's node_modules at runtime. When the
|
|
6
|
-
* skill ships standalone (outside the monorepo's hoisted node_modules), the
|
|
7
|
-
* first run needs to install them. This module detects the user's package
|
|
8
|
-
* manager from lockfiles, runs install against the skill directory, and exits
|
|
9
|
-
* with an actionable message if install fails.
|
|
10
|
-
*/
|
|
11
1
|
import { spawnSync } from 'node:child_process';
|
|
12
2
|
import { existsSync } from 'node:fs';
|
|
13
3
|
import { createRequire } from 'node:module';
|
|
@@ -75,7 +65,6 @@ function planInstall(skillDir: string): InstallPlan {
|
|
|
75
65
|
}
|
|
76
66
|
|
|
77
67
|
function findSkillDir(entryUrl: string): string {
|
|
78
|
-
// Walk up from the entry file until we find a package.json — that's the skill root.
|
|
79
68
|
let dir = dirname(fileURLToPath(entryUrl));
|
|
80
69
|
for (let i = 0; i < MAX_SKILL_DIR_HOPS; i++) {
|
|
81
70
|
if (existsSync(join(dir, 'package.json'))) return dir;
|
|
@@ -83,7 +72,6 @@ function findSkillDir(entryUrl: string): string {
|
|
|
83
72
|
if (parent === dir) break;
|
|
84
73
|
dir = parent;
|
|
85
74
|
}
|
|
86
|
-
// Fallback: assume scripts/<file>.js → skill root is one level up.
|
|
87
75
|
return dirname(dirname(fileURLToPath(entryUrl)));
|
|
88
76
|
}
|
|
89
77
|
|
|
@@ -102,18 +90,13 @@ function missingPackagesFrom(skillDir: string, entryUrl: string): string[] {
|
|
|
102
90
|
}
|
|
103
91
|
|
|
104
92
|
export interface EnsureDepsOptions {
|
|
105
|
-
|
|
93
|
+
|
|
106
94
|
autoInstall?: boolean;
|
|
107
|
-
|
|
95
|
+
|
|
108
96
|
tag?: string;
|
|
109
97
|
}
|
|
110
98
|
|
|
111
|
-
|
|
112
|
-
* Verify the skill's runtime dependencies are resolvable. If not:
|
|
113
|
-
* - Detect the user's package manager from the skill directory's lockfile.
|
|
114
|
-
* - If autoInstall is true (default), run the installer in the skill dir.
|
|
115
|
-
* - Otherwise, print the exact command for the user and exit 1.
|
|
116
|
-
*/
|
|
99
|
+
|
|
117
100
|
export function ensureNativeDependencies(
|
|
118
101
|
entryUrl: string,
|
|
119
102
|
options: EnsureDepsOptions = {},
|
|
@@ -921,7 +921,6 @@ export function detectMessageChains(fileSummaries: FileEntry[]): FindingDraft[]
|
|
|
921
921
|
const findings: FindingDraft[] = [];
|
|
922
922
|
for (const entry of fileSummaries) {
|
|
923
923
|
if (!entry.messageChains || entry.messageChains.length === 0) continue;
|
|
924
|
-
// Group by line start — take the deepest chain at each location
|
|
925
924
|
const byLine = new Map<number, typeof entry.messageChains[0]>();
|
|
926
925
|
for (const chain of entry.messageChains) {
|
|
927
926
|
const existing = byLine.get(chain.lineStart);
|
|
@@ -1234,7 +1234,7 @@ describe('diversifyFindings', () => {
|
|
|
1234
1234
|
|
|
1235
1235
|
it('returns all findings when limit >= length', () => {
|
|
1236
1236
|
const input = [makeDraft('high', 'a', 1), makeDraft('high', 'b', 1)];
|
|
1237
|
-
expect(diversifyFindings(input, 10)).toBe(input);
|
|
1237
|
+
expect(diversifyFindings(input, 10)).toBe(input);
|
|
1238
1238
|
expect(diversifyFindings(input, 2)).toBe(input);
|
|
1239
1239
|
});
|
|
1240
1240
|
|
|
@@ -433,17 +433,7 @@ export function assignFindingIds(
|
|
|
433
433
|
return { findings, byFile };
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
-
|
|
437
|
-
* Programmatic scan API — the equivalent of dependency-cruiser's `cruise()`
|
|
438
|
-
* or madge's constructor. Runs the full analysis pipeline without CLI I/O.
|
|
439
|
-
*
|
|
440
|
-
* Usage:
|
|
441
|
-
* ```ts
|
|
442
|
-
* import { scan, DEFAULT_OPTS } from './index.js';
|
|
443
|
-
* const result = await scan({ root: '/path/to/project', graph: true });
|
|
444
|
-
* console.log(result.exitCode); // 0=clean, 1=findings, 2=error
|
|
445
|
-
* ```
|
|
446
|
-
*/
|
|
436
|
+
|
|
447
437
|
export async function scan(
|
|
448
438
|
overrides: Partial<AnalysisOptions> = {}
|
|
449
439
|
): Promise<ScanResult> {
|
|
@@ -6,10 +6,7 @@ import { ALLOWED_EXTS } from '../types/index.js';
|
|
|
6
6
|
|
|
7
7
|
import type { DependencyState } from '../types/index.js';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
* Resolves --affected: git changed files + transitive dependents.
|
|
11
|
-
* Inspired by dependency-cruiser's --affected (uses watskeburt internally).
|
|
12
|
-
*/
|
|
9
|
+
|
|
13
10
|
export function resolveAffectedFiles(
|
|
14
11
|
root: string,
|
|
15
12
|
revision: string,
|
|
@@ -45,9 +42,7 @@ function getGitChangedFiles(root: string, revision: string): string[] {
|
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
* BFS: walk incoming edges to collect all transitive dependents of changed files.
|
|
50
|
-
*/
|
|
45
|
+
|
|
51
46
|
function collectTransitiveDependents(
|
|
52
47
|
seeds: Set<string>,
|
|
53
48
|
state: DependencyState,
|
|
@@ -9,10 +9,7 @@ export interface BaselineEntry {
|
|
|
9
9
|
title: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
* Saves current findings as a baseline for future --ignore-known runs.
|
|
14
|
-
* Inspired by dependency-cruiser's --ignore-known baseline file.
|
|
15
|
-
*/
|
|
12
|
+
|
|
16
13
|
export function saveBaseline(
|
|
17
14
|
root: string,
|
|
18
15
|
findings: Finding[]
|
|
@@ -37,10 +34,7 @@ export function saveBaseline(
|
|
|
37
34
|
return baselinePath;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
* Loads a baseline file and filters out known findings.
|
|
42
|
-
* Match key: (category, file) — title changes don't break the match.
|
|
43
|
-
*/
|
|
37
|
+
|
|
44
38
|
export function filterKnownFindings<T extends Pick<Finding, 'category' | 'file'>>(
|
|
45
39
|
findings: T[],
|
|
46
40
|
baselinePath: string,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
export const ANALYSIS_SCHEMA_VERSION = '1.1.0';
|
|
4
|
+
export const ANALYSIS_SCHEMA_VERSION = '1.1.0';
|
|
5
5
|
|
|
6
6
|
interface CacheEntry {
|
|
7
7
|
mtimeMs: number;
|
|
@@ -18,7 +18,7 @@ interface AnalysisCache {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const CACHE_VERSION = 1;
|
|
21
|
-
const DEFAULT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
21
|
+
const DEFAULT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
22
22
|
|
|
23
23
|
export function loadCache(root: string): AnalysisCache | null {
|
|
24
24
|
const cachePath = path.join(
|
|
@@ -167,9 +167,8 @@ describe('config-loader', () => {
|
|
|
167
167
|
fs.writeFileSync(
|
|
168
168
|
path.join(root, '.octocode-scan.json'),
|
|
169
169
|
`{
|
|
170
|
-
// Enable graph
|
|
171
170
|
"graph": true,
|
|
172
|
-
"flow": false
|
|
171
|
+
"flow": false
|
|
173
172
|
}`
|
|
174
173
|
);
|
|
175
174
|
const config = loadConfigFile(root, null);
|
|
@@ -7,11 +7,6 @@ type ConfigOverrides = Partial<Omit<AnalysisOptions, 'root' | 'packageRoot' | 'c
|
|
|
7
7
|
|
|
8
8
|
const CONFIG_NAMES = ['.octocode-scan.json', '.octocode-scan.jsonc'];
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Loads config from file, auto-discovered config, or package.json#octocode.
|
|
12
|
-
* CLI flags always win over config file values.
|
|
13
|
-
* Inspired by knip's .knip.json and eslint's flat config.
|
|
14
|
-
*/
|
|
15
10
|
export function loadConfigFile(
|
|
16
11
|
root: string,
|
|
17
12
|
explicitPath: string | null
|
|
@@ -75,10 +70,6 @@ function normalizeConfig(obj: Record<string, unknown>): ConfigOverrides {
|
|
|
75
70
|
return result as ConfigOverrides;
|
|
76
71
|
}
|
|
77
72
|
|
|
78
|
-
/**
|
|
79
|
-
* Merges config file overrides into defaults, then CLI overrides on top.
|
|
80
|
-
* CLI args that differ from defaults always win.
|
|
81
|
-
*/
|
|
82
73
|
export function mergeConfigIntoDefaults(
|
|
83
74
|
defaults: AnalysisOptions,
|
|
84
75
|
config: ConfigOverrides,
|
|
@@ -8,17 +8,7 @@ export interface CreateOptionsInput {
|
|
|
8
8
|
args: AnalysisOptions;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
* Transforms raw parsed CLI args into validated, normalized runtime options.
|
|
13
|
-
* This is Layer 2 in the 3-layer CLI pattern (args → options → engine).
|
|
14
|
-
*
|
|
15
|
-
* Responsible for:
|
|
16
|
-
* - Deriving computed fields (packageRoot)
|
|
17
|
-
* - Auto-enabling flags based on feature selection (test-quality → includeTests)
|
|
18
|
-
*
|
|
19
|
-
* Inspired by knip's create-options.ts, eslint's translate-cli-options.js,
|
|
20
|
-
* and dependency-cruiser's normalize-cli-options.mjs.
|
|
21
|
-
*/
|
|
11
|
+
|
|
22
12
|
export function createOptions({ args }: CreateOptionsInput): AnalysisOptions {
|
|
23
13
|
const opts = { ...args };
|
|
24
14
|
|
|
@@ -37,10 +27,7 @@ function autoEnableTestQuality(opts: AnalysisOptions): void {
|
|
|
37
27
|
}
|
|
38
28
|
}
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
* Resolves `--exclude` into a features set by subtracting from ALL_CATEGORIES.
|
|
42
|
-
* Called during CLI arg parsing when --exclude is used.
|
|
43
|
-
*/
|
|
30
|
+
|
|
44
31
|
export function resolveExcludeToFeatures(
|
|
45
32
|
excludeSet: Set<string>
|
|
46
33
|
): Set<string> {
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import type { Finding, ReporterFormat } from '../types/index.js';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Formats findings for alternative output targets.
|
|
5
|
-
* Inspired by eslint's --format flag and dependency-cruiser's reporter plugins.
|
|
6
|
-
*/
|
|
7
3
|
|
|
8
4
|
export function formatFindings(
|
|
9
5
|
findings: Finding[],
|
|
@@ -34,10 +30,7 @@ function severityToLevel(severity: string): string {
|
|
|
34
30
|
}
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
* Compact: one-line per finding for terminal/CI logs.
|
|
39
|
-
* Format: severity:file:line - [category] title
|
|
40
|
-
*/
|
|
33
|
+
|
|
41
34
|
function formatCompact(findings: Finding[], root: string): string {
|
|
42
35
|
return findings
|
|
43
36
|
.map(f => {
|
|
@@ -48,10 +41,7 @@ function formatCompact(findings: Finding[], root: string): string {
|
|
|
48
41
|
.join('\n');
|
|
49
42
|
}
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
* GitHub Actions: ::warning / ::error annotations.
|
|
53
|
-
* These appear inline on PR diffs.
|
|
54
|
-
*/
|
|
44
|
+
|
|
55
45
|
function formatGitHubActions(findings: Finding[], root: string): string {
|
|
56
46
|
return findings
|
|
57
47
|
.map(f => {
|
|
@@ -173,13 +173,13 @@ describe('computeHealthScore', () => {
|
|
|
173
173
|
it('penalizes medium findings (weight 3)', () => {
|
|
174
174
|
const findings = [makeFinding({ severity: 'medium' })];
|
|
175
175
|
const score = computeHealthScore(findings, 10);
|
|
176
|
-
expect(score).toBe(97);
|
|
176
|
+
expect(score).toBe(97);
|
|
177
177
|
});
|
|
178
178
|
|
|
179
179
|
it('penalizes low findings (weight 1)', () => {
|
|
180
180
|
const findings = [makeFinding({ severity: 'low' })];
|
|
181
181
|
const score = computeHealthScore(findings, 10);
|
|
182
|
-
expect(score).toBe(99);
|
|
182
|
+
expect(score).toBe(99);
|
|
183
183
|
});
|
|
184
184
|
|
|
185
185
|
it('info severity has weight 0', () => {
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
* Entry point for the octocode-engineer scanner. Verifies runtime
|
|
4
|
-
* dependencies (native addons that cannot be bundled + the TypeScript
|
|
5
|
-
* compiler) are installed before loading the pipeline. If missing, the
|
|
6
|
-
* bootstrap detects the user's package manager from lockfiles and installs
|
|
7
|
-
* into the skill's own node_modules, or prints an actionable manual command.
|
|
8
|
-
*/
|
|
2
|
+
|
|
9
3
|
import { ensureNativeDependencies } from './common/ensure-deps.js';
|
|
10
4
|
|
|
11
5
|
ensureNativeDependencies(import.meta.url, { tag: '[octocode-scan]' });
|
|
@@ -6,7 +6,6 @@ import type { AnalysisOptions, Thresholds } from './core.js';
|
|
|
6
6
|
import type { DependencyProfile } from './dependency.js';
|
|
7
7
|
|
|
8
8
|
export const DEFAULT_THRESHOLDS: Thresholds = {
|
|
9
|
-
// Architecture
|
|
10
9
|
couplingThreshold: 15,
|
|
11
10
|
fanInThreshold: 20,
|
|
12
11
|
fanOutThreshold: 15,
|
|
@@ -17,7 +16,6 @@ export const DEFAULT_THRESHOLDS: Thresholds = {
|
|
|
17
16
|
sdpMaxSourceInstability: 0.6,
|
|
18
17
|
layerOrder: [],
|
|
19
18
|
|
|
20
|
-
// Code quality
|
|
21
19
|
minFunctionStatements: 6,
|
|
22
20
|
minFlowStatements: 6,
|
|
23
21
|
criticalComplexityThreshold: 30,
|
|
@@ -35,15 +33,12 @@ export const DEFAULT_THRESHOLDS: Thresholds = {
|
|
|
35
33
|
magicStringMinOccurrences: 3,
|
|
36
34
|
booleanParamThreshold: 3,
|
|
37
35
|
|
|
38
|
-
// Semantic
|
|
39
36
|
overrideChainThreshold: 3,
|
|
40
37
|
shotgunThreshold: 8,
|
|
41
38
|
|
|
42
|
-
// Security
|
|
43
39
|
secretEntropyThreshold: 4.5,
|
|
44
40
|
secretMinLength: 20,
|
|
45
41
|
|
|
46
|
-
// Test quality
|
|
47
42
|
mockThreshold: 10,
|
|
48
43
|
};
|
|
49
44
|
|
|
@@ -123,7 +123,6 @@ export interface ControlMapEntry extends FlowEntry {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
export interface Thresholds {
|
|
126
|
-
// Architecture
|
|
127
126
|
couplingThreshold: number;
|
|
128
127
|
fanInThreshold: number;
|
|
129
128
|
fanOutThreshold: number;
|
|
@@ -134,7 +133,6 @@ export interface Thresholds {
|
|
|
134
133
|
sdpMaxSourceInstability: number;
|
|
135
134
|
layerOrder: string[];
|
|
136
135
|
|
|
137
|
-
// Code quality
|
|
138
136
|
minFunctionStatements: number;
|
|
139
137
|
minFlowStatements: number;
|
|
140
138
|
criticalComplexityThreshold: number;
|
|
@@ -152,15 +150,12 @@ export interface Thresholds {
|
|
|
152
150
|
magicStringMinOccurrences: number;
|
|
153
151
|
booleanParamThreshold: number;
|
|
154
152
|
|
|
155
|
-
// Semantic
|
|
156
153
|
overrideChainThreshold: number;
|
|
157
154
|
shotgunThreshold: number;
|
|
158
155
|
|
|
159
|
-
// Security
|
|
160
156
|
secretEntropyThreshold: number;
|
|
161
157
|
secretMinLength: number;
|
|
162
158
|
|
|
163
|
-
// Test quality
|
|
164
159
|
mockThreshold: number;
|
|
165
160
|
}
|
|
166
161
|
|
|
@@ -5,12 +5,7 @@ import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"
|
|
|
5
5
|
|
|
6
6
|
export default tseslint.config(
|
|
7
7
|
{
|
|
8
|
-
ignores: ["node_modules
|
|
9
|
-
},
|
|
10
|
-
js.configs.recommended,
|
|
11
|
-
...tseslint.configs.recommended,
|
|
12
|
-
{
|
|
13
|
-
files: ["src/**/*.ts", "*.mjs"],
|
|
8
|
+
ignores: ["node_modules*.ts", "*.mjs"],
|
|
14
9
|
languageOptions: {
|
|
15
10
|
ecmaVersion: "latest",
|
|
16
11
|
sourceType: "module",
|
|
@@ -82,7 +82,6 @@ export function parseFeedCatalog(markdown) {
|
|
|
82
82
|
pastSeparator = false;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// Fenced-block parsing (legacy ### RSS Feeds format)
|
|
86
85
|
if (/^### RSS Feeds/.test(trimmed)) {
|
|
87
86
|
awaitingFence = true;
|
|
88
87
|
insideFeedBlock = false;
|
|
@@ -107,7 +106,6 @@ export function parseFeedCatalog(markdown) {
|
|
|
107
106
|
return;
|
|
108
107
|
}
|
|
109
108
|
|
|
110
|
-
// Table parsing: extract from RSS column
|
|
111
109
|
if (trimmed.startsWith("|")) {
|
|
112
110
|
const cells = trimmed
|
|
113
111
|
.replace(/^\|/, "")
|
|
@@ -326,8 +324,8 @@ export async function readResponseBody(response, maxBytes = 1024 * 1024) {
|
|
|
326
324
|
try {
|
|
327
325
|
await reader.cancel();
|
|
328
326
|
} catch {
|
|
329
|
-
|
|
330
|
-
|
|
327
|
+
void 0;
|
|
328
|
+
}
|
|
331
329
|
}
|
|
332
330
|
|
|
333
331
|
text += decoder.decode();
|
|
@@ -11,76 +11,45 @@
|
|
|
11
11
|
* @see docs/SERVER_FLOWS.md for detailed flow documentation
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
// PM2 logs disabled - app handles all logging with rotation
|
|
15
14
|
|
|
16
15
|
module.exports = {
|
|
17
16
|
apps: [{
|
|
18
17
|
name: 'octocode-research',
|
|
19
18
|
script: './scripts/server.js',
|
|
20
19
|
|
|
21
|
-
// ============================================
|
|
22
|
-
// RESTART STRATEGIES
|
|
23
|
-
// ============================================
|
|
24
20
|
|
|
25
|
-
// ❌ REMOVED: cron_restart: '0 * * * *'
|
|
26
|
-
// Replaced by idle-based restart in server.ts (30 min idle → restart)
|
|
27
21
|
|
|
28
|
-
// ✅ Memory guard (PM2 safety net)
|
|
29
22
|
max_memory_restart: '500M',
|
|
30
23
|
|
|
31
|
-
// ============================================
|
|
32
|
-
// GRACEFUL SHUTDOWN
|
|
33
|
-
// ============================================
|
|
34
24
|
|
|
35
|
-
// Allow 2 minutes for connection draining during shutdown
|
|
36
|
-
// This gives long-running requests time to complete
|
|
37
25
|
kill_timeout: 120000,
|
|
38
26
|
|
|
39
|
-
// Wait for process.send('ready') before marking as online
|
|
40
27
|
wait_ready: true,
|
|
41
28
|
|
|
42
|
-
// Timeout for ready signal (15s for MCP initialization)
|
|
43
29
|
listen_timeout: 15000,
|
|
44
30
|
|
|
45
|
-
// ============================================
|
|
46
|
-
// RESTART BEHAVIOR
|
|
47
|
-
// ============================================
|
|
48
31
|
|
|
49
|
-
// Auto-restart on crash
|
|
50
32
|
autorestart: true,
|
|
51
33
|
|
|
52
|
-
// Max consecutive restarts before stopping
|
|
53
34
|
max_restarts: 10,
|
|
54
35
|
|
|
55
|
-
// Delay between restarts
|
|
56
36
|
restart_delay: 1000,
|
|
57
37
|
|
|
58
|
-
// Exponential backoff on repeated crashes
|
|
59
38
|
exp_backoff_restart_delay: 100,
|
|
60
39
|
|
|
61
|
-
// Minimum uptime to consider app started successfully
|
|
62
40
|
min_uptime: 5000,
|
|
63
41
|
|
|
64
|
-
// ============================================
|
|
65
|
-
// LOGGING
|
|
66
|
-
// ============================================
|
|
67
42
|
|
|
68
|
-
// PM2 logs disabled - app handles logging in ~/.octocode/logs/
|
|
69
43
|
out_file: '/dev/null',
|
|
70
44
|
error_file: '/dev/null',
|
|
71
45
|
merge_logs: true,
|
|
72
46
|
combine_logs: true,
|
|
73
47
|
|
|
74
|
-
// ============================================
|
|
75
|
-
// ENVIRONMENT
|
|
76
|
-
// ============================================
|
|
77
48
|
|
|
78
|
-
// Node.js environment
|
|
79
49
|
env: {
|
|
80
50
|
NODE_ENV: 'production',
|
|
81
51
|
},
|
|
82
52
|
|
|
83
|
-
// Development environment (pm2 start --env development)
|
|
84
53
|
env_development: {
|
|
85
54
|
NODE_ENV: 'development',
|
|
86
55
|
},
|