octocode-cli 1.5.0 → 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 +167 -90
- 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/assets/example.png +0 -0
- 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
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __createRequire } from 'module';
|
|
3
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
4
|
+
import { dirname as __dirname_fn } from 'path';
|
|
5
|
+
const require = __createRequire(import.meta.url);
|
|
6
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __dirname_fn(__filename);
|
|
8
|
+
import{k as o}from"./chunk-OMXWZJBJ.js";import"./chunk-BRHVQB64.js";import"./chunk-CVNNNSMQ.js";import"./chunk-375ZNU3A.js";import"./chunk-KWJUBVXJ.js";import"./chunk-AYILFPX4.js";import"./chunk-YBBF4DTY.js";function C(n){let m=o(n);return m?e(m):void 0}function e(n){let{handler:m,...d}=n;return d}export{C as findStaticCommandHelp};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __createRequire } from 'module';
|
|
3
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
4
|
+
import { dirname as __dirname_fn } from 'path';
|
|
5
|
+
const require = __createRequire(import.meta.url);
|
|
6
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __dirname_fn(__filename);
|
|
8
|
+
import{a,b,c,d,e,f,g,h,i,j,k}from"./chunk-OMXWZJBJ.js";import"./chunk-BRHVQB64.js";import"./chunk-CVNNNSMQ.js";import"./chunk-375ZNU3A.js";import"./chunk-KWJUBVXJ.js";import"./chunk-AYILFPX4.js";import"./chunk-YBBF4DTY.js";export{e as authCommand,h as cacheCommand,k as findCommand,a as installCommand,c as loginCommand,d as logoutCommand,g as mcpCommand,f as skillsCommand,i as statusCommand,j as syncCommand,b as tokenCommand};
|
|
@@ -5,6 +5,6 @@ import { dirname as __dirname_fn } from 'path';
|
|
|
5
5
|
const require = __createRequire(import.meta.url);
|
|
6
6
|
const __filename = __fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = __dirname_fn(__filename);
|
|
8
|
-
import{a as n,b as e,c as p}from"./chunk-
|
|
8
|
+
import{a as n,b as e,c as p}from"./chunk-AYILFPX4.js";import"./chunk-YBBF4DTY.js";function r(t){let o=["",` ${n("magenta",e("\u{1F50D}\u{1F419} octocode "+t.name))}`,"",` ${t.description}`,""];if(t.usage&&(o.push(` ${e("USAGE")}`),o.push(` ${t.usage}`),o.push("")),t.options&&t.options.length>0){o.push(` ${e("OPTIONS")}`);for(let s of t.options){let i=s.short?`-${s.short}, `:" ",u=`--${s.name}`,a=s.hasValue?" <value>":"",l=s.default!==void 0?p(` (default: ${s.default})`):"";o.push(` ${n("cyan",i+u+a)}${l}`),o.push(` ${s.description}`)}o.push("")}process.stdout.write(`${o.join(`
|
|
9
9
|
`)}
|
|
10
10
|
`)}export{r as showCommandHelp};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __createRequire } from 'module';
|
|
3
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
4
|
+
import { dirname as __dirname_fn } from 'path';
|
|
5
|
+
const require = __createRequire(import.meta.url);
|
|
6
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __dirname_fn(__filename);
|
|
8
|
+
import{a as i,b as c,d as r}from"./chunk-375ZNU3A.js";import"./chunk-KWJUBVXJ.js";import{a as o,b as e,c as t}from"./chunk-AYILFPX4.js";import"./chunk-YBBF4DTY.js";function $(){let l=[];for(let a of i){let s=c.filter(n=>r(n.name)===a);if(s.length!==0){l.push(` ${t(a)}`);for(let n of s)l.push(` ${o("cyan",n.name)}`)}}return l}function g(){let l=$(),a=c.length,s=["",` ${o("magenta",e("\u{1F50D}\u{1F419} Octocode CLI"))}`,"",` ${e("INSTRUCTIONS FOR AGENTS")} ${t("(do this before making any tool request)")}`,` ${t("0.")} Load agent context (protocol + tools + fields) ${o("yellow","octocode --agent")}`,` ${t("1.")} List all available tools ${o("yellow","octocode tools")}`,` ${t("2.")} Read a tool's input schema ${o("yellow","octocode tools <name>")}`,` ${t("3.")} Full context with every JSON schema inline ${o("yellow","octocode --agent --full")}`,"",` ${e("USAGE")}`,` ${o("magenta","octocode")} <command> [options] ${t("manage Octocode")}`,` ${o("magenta","octocode")} tools ${t("list all tools")}`,` ${o("magenta","octocode")} tools <name> ${t("show input schema")}`,` ${o("magenta","octocode")} tools <n1> <n2> ... ${t("batch input schemas")}`,` ${o("magenta","octocode")} tools <name> --queries '<json>' ${t("run a tool")}`,` ${o("magenta","octocode")} instructions ${t("MCP instructions + all schemas")}`,"",` ${e("COMMANDS")} ${t("(manage Octocode configuration)")}`,` ${o("magenta","install")} Configure octocode-mcp for an IDE`,` ${o("magenta","auth")} Manage GitHub authentication`,` ${o("magenta","login / logout")} Sign in or out of GitHub`,` ${o("magenta","status / token")} Show auth status or print token`,` ${o("magenta","skills")} Search, install & manage Octocode skills`,` ${o("magenta","mcp")} Manage MCP marketplace`,` ${o("magenta","sync")} Sync MCP configs across IDEs`,` ${o("magenta","cache")} Inspect and clean Octocode cache`,"",` ${e("SKILLS")} ${t("(octocode skills <subcommand>)")}`,` ${o("magenta","search")} ${t("<query>")} Find skills ${t("(agent protocol; --direct for skills.sh results)")}`,` ${o("magenta","read")} ${t("<path|url>")} Print a SKILL.md ${t("(local path, owner/repo/path, or GitHub URL)")}`,` ${o("magenta","list")} List skills installed across all AI clients`,` ${o("magenta","install")} Install skills ${t("(--skill <name>, --local <path>, --targets <list>)")}`,` ${o("magenta","remove")} Remove a skill ${t("(--skill <name> or --local <path>)")}`,` ${o("magenta","sync")} ${t("<from> <to>")} Copy skills from one client target to another`,"",` ${e("TOOLS")} ${t(`(${a} tools \u2014 run directly from terminal)`)}`,...l,"",` ${e("OPTIONS")}`,` ${o("cyan","--json")} Raw JSON (full MCP envelope) for tool runs`,` ${o("cyan","--compact")} Leanest tool output (concise verbosity, fewer tokens)`,` ${o("cyan","--no-color")} Disable ANSI colors (also via NO_COLOR=1)`,` ${o("cyan","-h, --help")} Show this help`,` ${o("cyan","-v, --version")} Show version`,"",` ${e("EXAMPLES")}`,` ${o("yellow","octocode tools")} ${t("# list")}`,` ${o("yellow","octocode tools localSearchCode")} ${t("# schema")}`,` ${o("yellow","octocode tools localSearchCode githubSearchCode")} ${t("# batch schemas")}`,` ${o("yellow",`octocode tools localSearchCode --queries '{"path":".","pattern":"fn"}'`)} ${t("# run")}`,` ${o("yellow","octocode instructions")} ${t("# full context")}`,"",` ${o("yellow","octocode install --ide cursor")}`,` ${o("yellow",'octocode skills search "code review"')} ${t("# find skills")}`,` ${o("yellow",'octocode skills search "code review" --direct')} ${t("# skills.sh results")}`,` ${o("yellow","octocode skills install --targets claude-code,cursor")}`,"",o("magenta",` \u2500\u2500\u2500 \u{1F50D}\u{1F419} ${e("https://octocode.ai")} \u2500\u2500\u2500`),""];process.stdout.write(`${s.join(`
|
|
9
|
+
`)}
|
|
10
|
+
`)}export{g as showHelp};
|
|
@@ -5,4 +5,4 @@ import { dirname as __dirname_fn } from 'path';
|
|
|
5
5
|
const require = __createRequire(import.meta.url);
|
|
6
6
|
const __filename = __fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = __dirname_fn(__filename);
|
|
8
|
-
import{a}from"./chunk-
|
|
8
|
+
import{a}from"./chunk-BRHVQB64.js";import"./chunk-AYILFPX4.js";import"./chunk-YBBF4DTY.js";export{a as Spinner};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __createRequire } from 'module';
|
|
3
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
4
|
+
import { dirname as __dirname_fn } from 'path';
|
|
5
|
+
const require = __createRequire(import.meta.url);
|
|
6
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __dirname_fn(__filename);
|
|
8
|
+
import{a,b,c,d,e,f,g,h,i,j,k,l}from"./chunk-375ZNU3A.js";import"./chunk-KWJUBVXJ.js";import"./chunk-AYILFPX4.js";import"./chunk-YBBF4DTY.js";export{a as TOOL_CATEGORIES,b as TOOL_DEFINITIONS,k as executeToolCommand,c as findToolDefinition,e as getDisplayFields,d as getToolCategory,i as getToolsContextString,j as printToolsContext,f as showAvailableTools,h as showMultipleToolSchemas,g as showToolHelp,l as toolCommand};
|
package/out/octocode-cli.js
CHANGED
|
@@ -5,4 +5,4 @@ import { dirname as __dirname_fn } from 'path';
|
|
|
5
5
|
const require = __createRequire(import.meta.url);
|
|
6
6
|
const __filename = __fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = __dirname_fn(__filename);
|
|
8
|
-
import{c as
|
|
8
|
+
import{a as c}from"./chunks/chunk-KWJUBVXJ.js";import{c as u}from"./chunks/chunk-AYILFPX4.js";import"./chunks/chunk-YBBF4DTY.js";var d=new Set(["ide","method","m","output","o","hostname","H","git-protocol","p","type","t","skill","k","local","limit","l","targets","mode","model","resume","r","id","client","c","search","category","env","config","tool","queries","format","input","responseCharLength","responseCharOffset","target","backup-path"]),S=new Set(["help","h","version","v","force","source","json","status","dry-run","installed","repos","skills","logs","all","tools","full","direct","lsp","api","list","schema","tools-context","agent","compact","full","no-color","reveal","raw","check","rollback","install","yes","y","validate","sync"]),_=new Set(["output","responseCharLength","responseCharOffset","tool","queries"]);function g(t,o){return S.has(o)?!1:d.has(o)?!0:t.command==="tool"||typeof t.options.tool=="string"}function w(t=process.argv.slice(2)){let o={command:null,args:[],options:{}},e=0;for(;e<t.length;){let i=t[e];if(i.startsWith("--")){let[s,n]=i.slice(2).split("=");n!==void 0?o.options[s]=n:g(o,s)&&e+1<t.length&&!t[e+1].startsWith("-")?(o.options[s]=t[e+1],e++):o.options[s]=!0}else if(i.startsWith("-")&&!i.startsWith("--")&&i.length>2){let s=i.slice(1),[n,r]=s.split("=");if(_.has(n))r!==void 0?o.options[n]=r:g(o,n)&&e+1<t.length&&!t[e+1].startsWith("-")?(o.options[n]=t[e+1],e++):o.options[n]=!0;else{let l=s,m=l[l.length-1];if(l.length===1&&d.has(m)&&e+1<t.length&&!t[e+1].startsWith("-"))o.options[m]=t[e+1],e++;else for(let p of l)o.options[p]=!0}}else if(i.startsWith("-")&&i.length>1){let s=i.slice(1),n=s[s.length-1];if(s.length===1&&d.has(n)&&e+1<t.length&&!t[e+1].startsWith("-"))o.options[n]=t[e+1],e++;else for(let r of s)o.options[r]=!0}else o.command||typeof o.options.tool=="string"?o.args.push(i):o.command=i;e++}return!o.command&&typeof o.options.tool=="string"&&(o.command="tool",o.args=[o.options.tool,...o.args]),o}function C(t){return!!(t.options.help||t.options.h)}function P(t){return!!(t.options.version||t.options.v)}async function y(){return import("./chunks/commands-JFDCC4Q7.js")}async function A(){return import("./chunks/command-help-specs-RVJK6V6M.js")}async function a(){return import("./chunks/tool-command-L6PVHUNS.js")}async function f(){return import("./chunks/main-help-BUTDD4IR.js")}async function H(){return import("./chunks/help-H3WGUJO7.js")}function I(){console.log(),console.log(" Use octocode --tool <toolName> --queries '<json-stringified-input>'."),console.log(` Example: octocode --tool localSearchCode --queries '{"path":".","pattern":"runCLI"}'`),console.log()}function L(){console.log("octocode v1.5.3")}async function x(t){let o=w(t);if(o.options["no-color"]===!0&&(process.env.NO_COLOR="1"),o.options["tools-context"]===!0||o.options.agent===!0){let{printToolsContext:s}=await a();return await s({full:o.options.full===!0}),!0}if(C(o)){if(o.command==="tool"&&typeof o.options.tool=="string"&&typeof o.args[0]=="string"){let{showToolHelp:n}=await a();if(await n(o.args[0]))return!0}if(o.command==="tool"){let{showHelp:n}=await f();return n(),!0}if(o.command==="tools"){if(typeof o.args[0]=="string"){let{showToolHelp:r}=await a();if(await r(o.args[0]))return!0}let{showAvailableTools:n}=await a();return await n(),!0}if(o.command){let[{findStaticCommandHelp:n},{showCommandHelp:r}]=await Promise.all([A(),H()]),l=n(o.command);if(l)return r(l),!0;let[{findCommand:m},{showHelp:p}]=await Promise.all([y(),f()]),h=m(o.command);return h?(r(h),!0):(p(),!0)}let{showHelp:s}=await f();return s(),!0}if(P(o))return L(),!0;if(!o.command)return!1;if(o.command==="tool")return typeof o.options.tool!="string"?(I(),process.exitCode=c.USAGE,!0):(!await(await a()).executeToolCommand(o)&&!process.exitCode&&(process.exitCode=c.GENERAL),!0);if(o.command==="tools"){let{executeToolCommand:s}=await a();return!await s(o)&&!process.exitCode&&(process.exitCode=c.GENERAL),!0}if(o.command==="instructions"||o.command==="agent"){let{printToolsContext:s}=await a();return await s({full:o.options.full===!0}),!0}let{findCommand:e}=await y(),i=e(o.command);return i?(await i.handler(o),!0):(console.log(),console.log(` Unknown command: ${o.command}`),console.log(" Run 'octocode --help' to see available commands."),console.log(),process.exitCode=c.NOT_FOUND,!0)}async function N(){let{showHelp:t}=await import("./chunks/main-help-BUTDD4IR.js");t()}async function O(){await x()||await N()}function T(){process.stdout.write("\x1B[?25h"),console.log(),console.log(u(" Goodbye! \u{1F44B}")),process.exit(0)}process.on("SIGINT",T);process.on("SIGTERM",T);O().catch(t=>{t?.name==="ExitPromptError"&&(console.log(),console.log(u(" Goodbye! \u{1F44B}")),process.exit(0)),console.error("Error:",t),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "octocode-cli",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "Interactive CLI installer for octocode-mcp - Configure MCP servers for Cursor, Claude Desktop, and more",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-coding",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@vitest/coverage-v8": "^4.0.16",
|
|
69
69
|
"esbuild": "^0.27.2",
|
|
70
70
|
"eslint": "^9.18.0",
|
|
71
|
-
"octocode-mcp": "^15.0.
|
|
71
|
+
"octocode-mcp": "^15.0.2",
|
|
72
72
|
"octocode-shared": "workspace:^",
|
|
73
73
|
"open": "^11.0.0",
|
|
74
74
|
"tsx": "^4.21.0",
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
* Tavily web search for octocode-brainstorming.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* node tavily-search.mjs --query "prior art for X" [--depth basic|advanced] [--max-results 8] [--topic general|news] [--time-range month|year]
|
|
7
|
-
* node tavily-search.mjs --check # exits 0 if key is set, 1 otherwise
|
|
8
|
-
*
|
|
9
|
-
* Requires TAVILY_API_KEY in env or in <skill_dir>/.env
|
|
10
|
-
* Outputs JSON to stdout; progress/errors to stderr. Never prints the key.
|
|
11
|
-
*/
|
|
2
|
+
|
|
12
3
|
|
|
13
4
|
import { readFileSync } from 'node:fs';
|
|
14
5
|
import { resolve, dirname } from 'node:path';
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Run a generated `run(cdp)` script against a Chrome CDP target.
|
|
3
2
|
|
|
4
3
|
import { resolve, join } from 'path';
|
|
5
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
@@ -392,7 +391,6 @@ async function main() {
|
|
|
392
391
|
console.error(`[CDP_RUNNER] Session meta dir: ${sessionMetaDir}`);
|
|
393
392
|
console.error(`[CDP_RUNNER] Connected - running ${scriptArg}`);
|
|
394
393
|
|
|
395
|
-
// Node PM has no scoped networking; keep generated script fetch/WebSocket on localhost.
|
|
396
394
|
const _origFetch = globalThis.fetch;
|
|
397
395
|
const _OrigWS = globalThis.WebSocket;
|
|
398
396
|
function isLocalhost(url) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Run cdp-runner.mjs with Node permissions, output isolation, and a process timeout.
|
|
3
2
|
|
|
4
3
|
import { spawn } from 'child_process';
|
|
5
4
|
import { resolve, dirname, join } from 'path';
|
|
@@ -32,7 +31,6 @@ mkdirSync(OUTPUT_DIR, { recursive: true, mode: 0o700 });
|
|
|
32
31
|
const SESSION_META_DIR = join(tmpdir(), '.octocode-chrome-devtools', 'session-meta', `port-${PORT}`);
|
|
33
32
|
mkdirSync(SESSION_META_DIR, { recursive: true, mode: 0o700 });
|
|
34
33
|
|
|
35
|
-
// Node 22 normalizes --allow-fs-* entries to realpaths; pass symlink and real forms.
|
|
36
34
|
const safePath = (p) => { try { return realpathSync(p); } catch { return p; } };
|
|
37
35
|
|
|
38
36
|
const TMPDIR_RAW = tmpdir();
|
|
@@ -41,7 +39,6 @@ const RUNNER_REAL = safePath(RUNNER);
|
|
|
41
39
|
const OUTPUT_REAL = safePath(OUTPUT_DIR);
|
|
42
40
|
const SESSION_META_REAL = safePath(SESSION_META_DIR);
|
|
43
41
|
|
|
44
|
-
// Stage helpers next to generated $TMPDIR scripts so relative imports work.
|
|
45
42
|
const HELPERS = ['sourcemap-resolver.mjs', 'undercover.mjs'];
|
|
46
43
|
for (const helper of HELPERS) {
|
|
47
44
|
const src = resolve(__dir, helper);
|
|
@@ -66,7 +63,6 @@ if (scriptArg) {
|
|
|
66
63
|
|
|
67
64
|
const spawnArgv = argv.map(a => (a === scriptArg && scriptReal) ? scriptReal : a);
|
|
68
65
|
|
|
69
|
-
// Node PM has no stable scoped --allow-net; cdp-runner restricts fetch/WebSocket to localhost.
|
|
70
66
|
const readPaths = [...new Set([RUNNER, RUNNER_REAL, TMPDIR_RAW, TMPDIR_REAL, ...allowReadExtra])];
|
|
71
67
|
const writePaths = [...new Set([OUTPUT_DIR, OUTPUT_REAL, SESSION_META_DIR, SESSION_META_REAL])];
|
|
72
68
|
|
|
@@ -106,7 +102,7 @@ const scriptTimer = setTimeout(() => {
|
|
|
106
102
|
child.kill('SIGTERM');
|
|
107
103
|
setTimeout(() => child.kill('SIGKILL'), 2000).unref();
|
|
108
104
|
}, SCRIPT_TIMEOUT_MS);
|
|
109
|
-
scriptTimer.unref();
|
|
105
|
+
scriptTimer.unref();
|
|
110
106
|
|
|
111
107
|
child.on('exit', (code, signal) => {
|
|
112
108
|
clearTimeout(scriptTimer);
|
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
// Base run(cdp) template. Save task copies in $TMPDIR and run with cdp-sandbox.mjs.
|
|
2
1
|
|
|
3
2
|
export async function run(cdp) {
|
|
4
|
-
// Optional: source maps must be registered before navigation.
|
|
5
|
-
// const { createSourceMapResolver } = await import(new URL('./sourcemap-resolver.mjs', import.meta.url).href);
|
|
6
|
-
// const resolver = await createSourceMapResolver(cdp);
|
|
7
3
|
|
|
8
4
|
await cdp.send('Runtime.enable', {});
|
|
9
5
|
await cdp.send('Network.enable', {});
|
|
10
6
|
await cdp.send('Log.enable', {});
|
|
11
|
-
// await cdp.send('Page.enable', {});
|
|
12
|
-
// await cdp.send('DOM.enable', {});
|
|
13
|
-
// await cdp.send('CSS.enable', {});
|
|
14
|
-
// await cdp.send('Performance.enable', {});
|
|
15
7
|
|
|
16
8
|
console.log(`[METRIC] Inspecting: ${cdp.targetInfo.url}`);
|
|
17
9
|
cdp.addReasoningStep?.({
|
|
@@ -64,9 +56,8 @@ export async function run(cdp) {
|
|
|
64
56
|
console.log(`[LOG:${entry.level.toUpperCase()}] [${entry.source}] ${entry.text}${entry.url ? ` @ ${entry.url}:${entry.lineNumber}` : ''}`);
|
|
65
57
|
});
|
|
66
58
|
|
|
67
|
-
// Add task-specific cdp.send() calls here. See SCRIPT_PATTERNS.md.
|
|
68
59
|
|
|
69
|
-
const MONITOR_MS = 10000;
|
|
60
|
+
const MONITOR_MS = 10000;
|
|
70
61
|
console.log(`[METRIC] Monitoring for ${MONITOR_MS / 1000}s...`);
|
|
71
62
|
await new Promise(r => setTimeout(r, MONITOR_MS));
|
|
72
63
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Launch Chrome with CDP enabled; tracks isolated sessions for cleanup.
|
|
3
2
|
|
|
4
3
|
import { spawn, execSync, execFileSync } from 'child_process';
|
|
5
4
|
import { platform, tmpdir } from 'os';
|
|
@@ -26,7 +25,6 @@ const CONFIG_PATH = getArg('--config', '');
|
|
|
26
25
|
|
|
27
26
|
const TMP = tmpdir();
|
|
28
27
|
const SESSION_FILE = join(TMP, `cdp-session-${PORT}.json`);
|
|
29
|
-
// Headless always uses an isolated temp profile.
|
|
30
28
|
const HEADLESS_PROFILE_DIR = join(TMP, `cdp-chrome-profile-${PORT}`);
|
|
31
29
|
|
|
32
30
|
function ok(payload) { console.log(JSON.stringify(payload)); }
|
|
@@ -265,7 +263,6 @@ if (CHROME_PATH && !existsSync(CHROME_PATH)) err(`Chrome not found at --chromePa
|
|
|
265
263
|
|
|
266
264
|
const HOME = process.env.HOME ?? process.env.USERPROFILE;
|
|
267
265
|
|
|
268
|
-
// If Chrome is already running without CDP, real-profile launches may hand off to it.
|
|
269
266
|
function isChromeRunning() {
|
|
270
267
|
if (platform() === 'darwin') {
|
|
271
268
|
try { execSync('pgrep -x "Google Chrome" > /dev/null 2>&1'); return true; } catch { return false; }
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Minimal source-map resolver for CDP scripts; strips sourcesContent.
|
|
2
1
|
|
|
3
2
|
const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
4
3
|
const B64_MAP = new Uint8Array(256).fill(255);
|
|
@@ -20,7 +19,6 @@ function decodeVLQList(str) {
|
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
function parseMap(mapJson) {
|
|
23
|
-
// Never retain original source bodies.
|
|
24
22
|
delete mapJson.sourcesContent;
|
|
25
23
|
|
|
26
24
|
const sources = mapJson.sources ?? [];
|
|
@@ -63,7 +61,6 @@ function originalPositionFor(parsed, genLine, genCol) {
|
|
|
63
61
|
const { segments, sources, names } = parsed;
|
|
64
62
|
if (!segments.length) return null;
|
|
65
63
|
|
|
66
|
-
// Same-line lookup only; previous-line matches look plausible but are wrong.
|
|
67
64
|
let lo = 0, hi = segments.length - 1, best = -1;
|
|
68
65
|
|
|
69
66
|
while (lo <= hi) {
|
|
@@ -141,7 +138,7 @@ export async function createSourceMapResolver(cdp) {
|
|
|
141
138
|
}
|
|
142
139
|
|
|
143
140
|
const mapJson = JSON.parse(mapText);
|
|
144
|
-
const parsed = parseMap(mapJson);
|
|
141
|
+
const parsed = parseMap(mapJson);
|
|
145
142
|
parsedMaps.set(scriptId, parsed);
|
|
146
143
|
stats.loaded++;
|
|
147
144
|
} catch {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// Drop-in stealth patches for public sites likely to fingerprint headless Chrome.
|
|
2
|
-
// Import from generated scripts before Page.navigate.
|
|
3
1
|
export async function applyStealthPatches(cdp, opts = {}) {
|
|
4
2
|
const ua = opts.userAgent ??
|
|
5
3
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
|
|
@@ -68,7 +68,7 @@ Every **non-trivial** task MUST satisfy this contract:
|
|
|
68
68
|
|
|
69
69
|
## When To Use It
|
|
70
70
|
|
|
71
|
-
Use when the user asks to **understand** a codebase/feature end-to-end, **change** unclear/shared/cross-file code, **review** quality/architecture/tech-debt/dead-code/security/build issues, or **decide** architecture and validate RFCs against real behavior. Any language; strongest on Node/TypeScript and Python. For
|
|
71
|
+
Use when the user asks to **understand** a codebase/feature end-to-end, **change** unclear/shared/cross-file code, **review** quality/architecture/tech-debt/dead-code/security/build issues, or **decide** architecture and validate RFCs against real behavior. Any language; strongest on Node/TypeScript and Python. For architecture options, trade-offs, or migration strategy that need a formal proposal before coding, pair with [octocode-rfc-generator](https://skills.sh/bgauryy/octocode-mcp/octocode-rfc-generator).
|
|
72
72
|
|
|
73
73
|
## Trivial vs. non-trivial — when the contract binds
|
|
74
74
|
|
|
@@ -120,12 +120,12 @@ Cover all six on a full review; on a scoped task, cover those the change touches
|
|
|
120
120
|
|------|------|-----------------|
|
|
121
121
|
| Small functions | `scripts/run.js` | `god-function`, `cognitive-complexity`, `halstead-effort`, `excessive-parameters` |
|
|
122
122
|
| Duplication | `scripts/run.js` | `duplicate-function-body`, `duplicate-flow-structure`, `similar-function-body` |
|
|
123
|
-
| Silent failures | `scripts/ast/search.js` | `--preset empty-catch`, `--preset py-bare-except`, `--preset
|
|
123
|
+
| Silent failures | `scripts/ast/search.js` | `--preset empty-catch`, `--preset py-bare-except`, `--preset catch-rethrow` |
|
|
124
124
|
| Loose types | `scripts/ast/search.js` | `--preset any-type`, `--preset type-assertion`, `--preset non-null-assertion` |
|
|
125
125
|
| Intent-revealing names | code read + `lspFindReferences` | widely-used cryptic symbols, abbreviations that spread |
|
|
126
126
|
| Dead / unreachable | scanner + `knip` | `dead-export`, `dead-file`, `unused-import`, `unused-npm-dependency` |
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
Full detector catalog, metric definitions, and severity rubric: [quality-indicators.md](./references/quality-indicators.md).
|
|
129
129
|
|
|
130
130
|
### Required output: understanding artifact
|
|
131
131
|
|
|
@@ -260,36 +260,15 @@ Non-trivial tasks follow this arc (recommended, not mandatory): clarify the ques
|
|
|
260
260
|
|
|
261
261
|
## Task shapes
|
|
262
262
|
|
|
263
|
-
Same working order; emphasis differs
|
|
264
|
-
|
|
265
|
-
| Task | Weight on | Notes |
|
|
266
|
-
|------|-----------|-------|
|
|
267
|
-
| **Code understanding** | steps 3–8 (layout → LSP → AST → scoped scanner → read) | Deliverable is the artifact. |
|
|
268
|
-
| **Bug fixing** | Flows + Execution from failing behavior to entry point; adjacent error/retry/contract risk | Fix the smallest layer that solves the root cause; escalate via Smallest-fix vs. safest-fix gate when systemic. |
|
|
269
|
-
| **Refactor** | blast radius (`lspFindReferences`), scoped scan, duplication inventory, then plan | Prefer extracting modules, clarifying contracts, simplifying flows over cosmetic reshuffling. Verify per batch. |
|
|
270
|
-
| **Architecture review** | scanner `--graph` first, then LSP on candidate modules, then read representatives | Report both local and system-level causes. |
|
|
271
|
-
| **RFC / design validation** | map each claim to code ownership; verify flow, contract, and architecture alignment | Mark claims `confirmed|likely|uncertain`; report mismatches as doc or code follow-ups. |
|
|
263
|
+
Same working order; emphasis differs. **Code understanding**: steps 3–8 (layout → LSP → AST → scanner → read), deliverable is the artifact. **Bug fixing**: Flows + Execution from failing behavior inward; fix the smallest responsible layer; escalate at the Smallest-fix gate if systemic. **Refactor**: blast radius first (`lspFindReferences`), then scoped scan + duplication inventory; prefer extracting modules and clarifying contracts over cosmetic reshuffling; verify per batch. **Architecture review**: scanner `--graph` first, then LSP on candidates; report local and system-level causes. **RFC/design validation**: map each claim to code ownership; verify flow, contract, and architecture alignment; mark `confirmed|likely|uncertain`.
|
|
272
264
|
|
|
273
265
|
## Before / During / After A Change
|
|
274
266
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
### Before
|
|
278
|
-
- Produce the understanding artifact.
|
|
279
|
-
- Map design-doc / RFC claims to concrete code ownership when one exists.
|
|
280
|
-
- Look for an existing local pattern before inventing a new one.
|
|
267
|
+
**Before**: produce the understanding artifact; map RFC/design-doc claims to code ownership; look for an existing pattern before inventing one.
|
|
281
268
|
|
|
282
|
-
|
|
283
|
-
- Keep edits in the smallest responsible layer; preserve boundaries unless the plan intentionally changes them.
|
|
284
|
-
- Maintain contract/protocol compatibility unless an explicit migration is in scope.
|
|
285
|
-
- If root cause turns out structural mid-task, stop and hit the Smallest-fix vs. safest-fix gate instead of continuing with a cosmetic patch.
|
|
269
|
+
**During**: stay in the smallest responsible layer; preserve contract/protocol compatibility unless migration is in scope; if root cause is structural mid-task, stop at the Smallest-fix vs. safest-fix gate.
|
|
286
270
|
|
|
287
|
-
|
|
288
|
-
- Run tests, lint, and build/type-check.
|
|
289
|
-
- Re-check changed symbols with LSP after renames/moves; run a scoped scanner pass for non-trivial changes.
|
|
290
|
-
- Run `knip` when the refactor may leave dead artifacts; CSS checks when styles changed.
|
|
291
|
-
- Re-validate the artifact's dimensions against the final implementation; note any remaining architectural risk even if the code now works.
|
|
292
|
-
- Sync docs / RFC sections touched by the change.
|
|
271
|
+
**After**: run tests, lint, build/type-check; re-check changed symbols with LSP; run scoped scanner pass for non-trivial changes; run `knip` if dead artifacts are likely; sync docs/RFC sections the change touches.
|
|
293
272
|
|
|
294
273
|
## Confidence Rules
|
|
295
274
|
|
|
@@ -394,8 +373,3 @@ Fallback applies only when an Octocode tool is truly **unavailable** — not reg
|
|
|
394
373
|
|
|
395
374
|
**If degraded but completed:** treat the response as valid; on empty/wrong results, retry with a simpler input (drop regex meta-characters, switch to literal search, narrow the path). **Do not** switch to native Claude Code tools — that leaves the skill's evidence model.
|
|
396
375
|
|
|
397
|
-
## Companion Skill
|
|
398
|
-
|
|
399
|
-
Pair with the RFC skill when architecture options, trade-offs, or migration strategy need a formal proposal before coding:
|
|
400
|
-
|
|
401
|
-
- [octocode-rfc-generator](https://skills.sh/bgauryy/octocode-mcp/octocode-rfc-generator) — generate a smart RFC from validated system evidence.
|
|
@@ -77,17 +77,11 @@ This skill is private (no `bin` entry) — **do not use `npx`**. `npx` applies o
|
|
|
77
77
|
|
|
78
78
|
## Flag Details
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
`--semantic` enables TypeChecker + LanguageService analysis (additional categories). Off by default since it adds ~3-5s. Semantic categories require `--semantic` to appear in results.
|
|
87
|
-
|
|
88
|
-
`--out` changes the output destination. If the path ends with `.json`, writes a single monolithic file (legacy mode). Otherwise, writes to the given directory instead of the default timestamped directory.
|
|
89
|
-
|
|
90
|
-
`--parser` selects the parse engine: `auto` (default — uses tree-sitter with TS fallback), `typescript` (TS compiler only), or `tree-sitter` (tree-sitter only).
|
|
80
|
+
- **`--scope`**: comma-separated paths relative to root. Use `file:symbol` to drill into a specific exported function — only findings overlapping that symbol are returned; the full dependency graph is still built. If `file:symbol` can't resolve (symbol name mismatch), falls back to file-level scope with a warning — check the exact exported name or switch to file-level scope.
|
|
81
|
+
- **`--features` / `--exclude`**: mutually exclusive. Accept pillar names (`architecture`, `code-quality`, `dead-code`, `security`, `test-quality`) and individual category names, comma-separated.
|
|
82
|
+
- **`--semantic`**: enables TypeChecker + LanguageService analysis (~3-5s overhead). Semantic categories only appear in results when this flag is set.
|
|
83
|
+
- **`--out`**: output destination. Path ending in `.json` → single monolithic file (legacy). Otherwise writes to the given directory instead of the default timestamped directory.
|
|
84
|
+
- **`--parser`**: `auto` (default — tree-sitter with TS fallback), `typescript` (TS compiler only), or `tree-sitter` (tree-sitter only).
|
|
91
85
|
|
|
92
86
|
---
|
|
93
87
|
|
|
@@ -68,9 +68,7 @@ Declarative rule DSL (`forbidden`/`allowed`/`required`). Scanner has 28 built-in
|
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
70
|
npx depcruise --no-config --output-type err src/
|
|
71
|
-
npx depcruise --no-config --output-type metrics src/
|
|
72
71
|
npx depcruise --no-config --output-type mermaid src/ > deps.md
|
|
73
|
-
npx depcruise --no-config --output-type mermaid --focus "^src/session" src/
|
|
74
72
|
npx depcruise --no-config --output-type err --affected HEAD src/
|
|
75
73
|
```
|
|
76
74
|
|
|
@@ -127,10 +125,8 @@ bandit -r <target> -s B101 # skip specific check (e.g. assert)
|
|
|
127
125
|
### pytest — Test runner
|
|
128
126
|
|
|
129
127
|
```bash
|
|
130
|
-
pytest
|
|
131
128
|
pytest --tb=short -q
|
|
132
129
|
pytest --cov=<package> --cov-report=term-missing
|
|
133
|
-
pytest -x # stop on first failure
|
|
134
130
|
```
|
|
135
131
|
|
|
136
132
|
---
|
|
@@ -91,56 +91,16 @@ The scanner detects quality indicators across 5 pillars: **Architecture**, **Cod
|
|
|
91
91
|
|
|
92
92
|
## New Detector Details
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
- **Fix strategy**: Single result variable, guard clauses for error paths only
|
|
105
|
-
|
|
106
|
-
### catch-rethrow
|
|
107
|
-
- **Signal**: Catch blocks containing a throw statement — candidates for simplification (may include other statements alongside the rethrow)
|
|
108
|
-
- **Severity**: Always low (noise reduction)
|
|
109
|
-
- **Fix strategy**: Remove the try-catch if only rethrowing, or add logging/wrapping before re-throw
|
|
110
|
-
|
|
111
|
-
### magic-string
|
|
112
|
-
- **Threshold**: `magicStringMinOccurrences` (default: 3)
|
|
113
|
-
- **Signal**: String literals used in `===`, `!==`, `==`, `!=` comparisons or `case` clauses, appearing ≥ threshold times
|
|
114
|
-
- **Severity mapping**: `≥ 8` → high, `≥ 5` → medium, else → low
|
|
115
|
-
- **Fix strategy**: Extract to named constant or enum
|
|
116
|
-
|
|
117
|
-
### boolean-parameter-cluster
|
|
118
|
-
- **Threshold**: `booleanParamThreshold` (default: 3)
|
|
119
|
-
- **Signal**: Functions with ≥ threshold parameters typed as `boolean`
|
|
120
|
-
- **Severity**: Always medium
|
|
121
|
-
- **Fix strategy**: Replace with options object or split into separate functions
|
|
122
|
-
|
|
123
|
-
### promise-all-unhandled
|
|
124
|
-
- **Signal**: `Promise.all/allSettled/race/any` calls without surrounding try-catch or `.catch()` chain
|
|
125
|
-
- **Severity**: Always medium
|
|
126
|
-
- **Fix strategy**: Wrap in try-catch or chain `.catch()`
|
|
127
|
-
|
|
128
|
-
### export-surface-density
|
|
129
|
-
- **Signal**: `exportCount / totalStatements` ratio ≥ 0.5 (modules with ≥ 20 statements)
|
|
130
|
-
- **Severity mapping**: `≥ 80%` → high, `≥ 60%` → medium, else → low
|
|
131
|
-
- **Fix strategy**: Make non-essential symbols private, split into facade + implementation
|
|
132
|
-
|
|
133
|
-
### change-risk (composite)
|
|
134
|
-
- **Signal**: Weighted sum of overlapping quality problems in a single file
|
|
135
|
-
- **Components scored**:
|
|
136
|
-
- High average complexity → +2
|
|
137
|
-
- High cognitive complexity → +2
|
|
138
|
-
- Low maintainability functions → +count
|
|
139
|
-
- Empty catches → +1
|
|
140
|
-
- Unhandled promise combinators → +1
|
|
141
|
-
- Excessive exports (>15) → +1
|
|
142
|
-
- **Threshold**: Score ≥ 4 triggers finding
|
|
143
|
-
- **Severity mapping**: `≥ 8` → critical, `≥ 6` → high, else → medium
|
|
94
|
+
| Category | Threshold flag (default) | Signal | Severity | Fix |
|
|
95
|
+
|----------|--------------------------|--------|----------|-----|
|
|
96
|
+
| `deep-nesting` | `--deep-nesting-threshold N` (5) | `max(maxBranchDepth, maxLoopDepth)` per fn | `≥T+3`→high, `≥T+1`→med, else low | Guard clauses, early returns, extract helpers |
|
|
97
|
+
| `multiple-return-paths` | `--multiple-return-threshold N` (6) | return/throw count per fn | `≥T+4`→high, `≥T+2`→med, else low | Single result var; guard clauses for error paths |
|
|
98
|
+
| `catch-rethrow` | — | catch containing a throw (simplification candidates) | always low | Remove try-catch if only re-throwing, or add logging before re-throw |
|
|
99
|
+
| `magic-string` | `--magic-string-min-occurrences N` (3) | string literals in `===`/`!==`/`case` appearing ≥N times | `≥8`→high, `≥5`→med, else low | Extract to named constant or enum |
|
|
100
|
+
| `boolean-parameter-cluster` | `--boolean-param-threshold N` (3) | fns with ≥N `boolean` params | always medium | Options object or split into separate fns |
|
|
101
|
+
| `promise-all-unhandled` | — | `Promise.all/allSettled/race/any` without try-catch or `.catch()` | always medium | Wrap in try-catch or chain `.catch()` |
|
|
102
|
+
| `export-surface-density` | — | `exportCount / totalStatements ≥ 0.5` (files with ≥20 stmts) | `≥80%`→high, `≥60%`→med, else low | Make non-essential symbols private; split facade + impl |
|
|
103
|
+
| `change-risk` | — | weighted sum: complexity+2, cognitive+2, low-MI+count, empty-catch+1, unhandled-promise+1, exports>15+1; fires at ≥4 | `≥8`→critical, `≥6`→high, else med | Fix overlapping quality signals in the file |
|
|
144
104
|
|
|
145
105
|
---
|
|
146
106
|
|
|
@@ -49,16 +49,13 @@ Trace a function: definition → callers → callees.
|
|
|
49
49
|
5. **Find callers** — incoming call hierarchy → who depends on this?
|
|
50
50
|
6. **Find callees** — outgoing call hierarchy → what does this depend on?
|
|
51
51
|
|
|
52
|
-
### 3 — Impact Analysis (
|
|
52
|
+
### 3 — Impact Analysis (Quick Blast Radius)
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
Fast pre-change check. For a full refactoring plan with coupling and cycle analysis, use [Workflow 12](#12--refactoring-plan-safe-restructure).
|
|
55
55
|
|
|
56
|
-
1. **
|
|
57
|
-
2. **
|
|
58
|
-
3. **
|
|
59
|
-
4. **Count test consumers** — find references filtered to test directories only
|
|
60
|
-
5. **Count production consumers** — find references excluding test directories
|
|
61
|
-
6. **Assess safety** — few production refs + high test coverage = safe to change. Many production refs = plan carefully, consider incremental migration.
|
|
56
|
+
1. **Locate the symbol** — text search to get file + lineHint
|
|
57
|
+
2. **Count all consumers** — find references, excluding declaration; split test vs production
|
|
58
|
+
3. **Assess safety** — few production refs + high test coverage = safe to change. Many refs = plan carefully → Workflow 12.
|
|
62
59
|
|
|
63
60
|
### 4 — Dead Export Validation
|
|
64
61
|
|
|
@@ -162,17 +159,17 @@ Before writing new code, understand the existing landscape to pick the right loc
|
|
|
162
159
|
|
|
163
160
|
### 12 — Refactoring Plan (Safe Restructure)
|
|
164
161
|
|
|
165
|
-
|
|
162
|
+
Full blast-radius refactoring plan. Use when Workflow 3 (quick check) reveals high consumer count or cross-package scope.
|
|
166
163
|
|
|
167
164
|
1. **Map all files containing the symbol** — text search with file-list mode
|
|
168
165
|
2. **Count all consumers** — find references excluding declaration
|
|
169
|
-
3. **Split test vs production consumers** — find references
|
|
170
|
-
4. **Map
|
|
171
|
-
5. **
|
|
166
|
+
3. **Split test vs production consumers** — find references filtered to test dirs; 0 test refs = coverage risk
|
|
167
|
+
4. **Map structural imports** — AST search for import patterns involving the symbol
|
|
168
|
+
5. **Map callers and dependencies** — incoming and outgoing call hierarchy
|
|
172
169
|
6. **Check coupling/cycle risk** — scoped scan with architecture + graph features
|
|
173
170
|
7. **Assess test quality around target** — scoped scan with test-quality feature
|
|
174
171
|
|
|
175
|
-
**Output**: file list + consumer count +
|
|
172
|
+
**Output**: file list + consumer count (test/prod split) + import graph + coupling risk = refactoring confidence level.
|
|
176
173
|
|
|
177
174
|
### 13 — Codebase Exploration (New Repo Orientation)
|
|
178
175
|
|
|
@@ -117,7 +117,7 @@ describe('buildDependencySummary', () => {
|
|
|
117
117
|
it('detects test-only modules (no production imports, only test imports)', () => {
|
|
118
118
|
const state = makeDependencyState();
|
|
119
119
|
addEdges(state, [
|
|
120
|
-
['src/foo.test.ts', 'src/helper.ts', true],
|
|
120
|
+
['src/foo.test.ts', 'src/helper.ts', true],
|
|
121
121
|
]);
|
|
122
122
|
const criticality = new Map<string, FileCriticality>();
|
|
123
123
|
const summary = buildDependencySummary(state, criticality, DEFAULT_OPTS);
|
|
@@ -129,7 +129,7 @@ describe('buildDependencySummary', () => {
|
|
|
129
129
|
it('excludes test files from test-only modules list', () => {
|
|
130
130
|
const state = makeDependencyState();
|
|
131
131
|
addEdges(state, [
|
|
132
|
-
['src/foo.test.ts', 'src/bar.test.ts', true],
|
|
132
|
+
['src/foo.test.ts', 'src/bar.test.ts', true],
|
|
133
133
|
]);
|
|
134
134
|
const criticality = new Map<string, FileCriticality>();
|
|
135
135
|
const summary = buildDependencySummary(state, criticality, DEFAULT_OPTS);
|
|
@@ -150,14 +150,14 @@ describe('collectMetrics', () => {
|
|
|
150
150
|
const sf = parse('function foo() { if (a && b) {} }');
|
|
151
151
|
const fn = firstFn(sf);
|
|
152
152
|
const m = collectMetrics(fn);
|
|
153
|
-
expect(m.complexity).toBe(3);
|
|
153
|
+
expect(m.complexity).toBe(3);
|
|
154
154
|
});
|
|
155
155
|
|
|
156
156
|
it('logical || → complexity+1', () => {
|
|
157
157
|
const sf = parse('function foo() { if (a || b) {} }');
|
|
158
158
|
const fn = firstFn(sf);
|
|
159
159
|
const m = collectMetrics(fn);
|
|
160
|
-
expect(m.complexity).toBe(3);
|
|
160
|
+
expect(m.complexity).toBe(3);
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
it('for-in loop → loops 1', () => {
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
* AST-aware structural code search powered by @ast-grep/napi.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* node scripts/ast/search.js --pattern 'console.log($$$ARGS)' --root ./src
|
|
7
|
-
* node scripts/ast/search.js --preset empty-catch --root ./packages
|
|
8
|
-
* node scripts/ast/search.js --kind function_declaration --root ./src --json
|
|
9
|
-
*/
|
|
2
|
+
|
|
10
3
|
|
|
11
4
|
import fs from 'node:fs';
|
|
12
5
|
import { createRequire } from 'node:module';
|
|
@@ -16,10 +9,6 @@ import { ALLOWED_EXTS, isPythonFile } from '../types/index.js';
|
|
|
16
9
|
|
|
17
10
|
import type { NapiConfig, SgNode, SgRoot } from '@ast-grep/napi';
|
|
18
11
|
|
|
19
|
-
// Require (not static-import) @ast-grep/napi so the resolution happens when this
|
|
20
|
-
// module is first evaluated — not when the outer bundle loads. The entry at
|
|
21
|
-
// src/ast/search.ts runs the dependency bootstrap before dynamic-importing this
|
|
22
|
-
// module, so by the time this line executes the native addon is present.
|
|
23
12
|
const nodeRequire = createRequire(import.meta.url);
|
|
24
13
|
const astGrep = nodeRequire('@ast-grep/napi') as typeof import('@ast-grep/napi');
|
|
25
14
|
const {
|
|
@@ -75,7 +64,7 @@ export interface AstSearchResult {
|
|
|
75
64
|
totalMatches: number;
|
|
76
65
|
totalFiles: number;
|
|
77
66
|
matches: AstMatch[];
|
|
78
|
-
|
|
67
|
+
|
|
79
68
|
_sourceByFile?: Map<string, string[]>;
|
|
80
69
|
}
|
|
81
70
|
|
|
@@ -272,7 +261,6 @@ export const PRESETS: Record<string, PresetRule> = {
|
|
|
272
261
|
description: 'Variable declarations without call expressions (candidates for dead code)',
|
|
273
262
|
},
|
|
274
263
|
|
|
275
|
-
// ── Python presets ──────────────────────────────────────────────
|
|
276
264
|
'py-bare-except': {
|
|
277
265
|
rule: {
|
|
278
266
|
kind: 'except_clause',
|
|
@@ -846,4 +834,3 @@ export async function main(): Promise<void> {
|
|
|
846
834
|
}
|
|
847
835
|
|
|
848
836
|
export { ensurePythonRegistered };
|
|
849
|
-
// Direct execution lives in ./search.ts; this file is an internal module.
|