@praneeth_54/agentdoctor 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +119 -5
- package/README.md +165 -332
- package/dist/agents/aider/adapter.d.ts +1 -0
- package/dist/agents/aider/adapter.js +1 -0
- package/dist/agents/aider/detector.d.ts +14 -0
- package/dist/agents/aider/detector.js +120 -0
- package/dist/agents/copilot/adapter.d.ts +1 -0
- package/dist/agents/copilot/adapter.js +1 -0
- package/dist/agents/copilot/detector.d.ts +13 -0
- package/dist/agents/copilot/detector.js +113 -0
- package/dist/agents/gemini/adapter.d.ts +1 -0
- package/dist/agents/gemini/adapter.js +1 -0
- package/dist/agents/gemini/detector.d.ts +13 -0
- package/dist/agents/gemini/detector.js +151 -0
- package/dist/agents/registry.js +13 -1
- package/dist/agents/types.d.ts +1 -1
- package/dist/agents/windsurf/adapter.d.ts +1 -0
- package/dist/agents/windsurf/adapter.js +1 -0
- package/dist/agents/windsurf/detector.d.ts +14 -0
- package/dist/agents/windsurf/detector.js +119 -0
- package/dist/architecture/c4.d.ts +22 -0
- package/dist/architecture/c4.js +62 -0
- package/dist/cli/commands/brain.d.ts +12 -0
- package/dist/cli/commands/brain.js +162 -0
- package/dist/cli/commands/complete.d.ts +23 -0
- package/dist/cli/commands/complete.js +232 -0
- package/dist/cli/commands/explain.js +1 -1
- package/dist/cli/commands/fix.js +22 -2
- package/dist/cli/commands/mcp.d.ts +8 -0
- package/dist/cli/commands/mcp.js +14 -0
- package/dist/cli/commands/platform.d.ts +15 -0
- package/dist/cli/commands/platform.js +191 -0
- package/dist/cli/commands/v2.d.ts +55 -0
- package/dist/cli/commands/v2.js +278 -0
- package/dist/cli/program.js +809 -2
- package/dist/constants.d.ts +3 -2
- package/dist/constants.js +5 -1
- package/dist/contracts/adapters.d.ts +7 -0
- package/dist/contracts/adapters.js +60 -0
- package/dist/contracts/index.d.ts +162 -0
- package/dist/contracts/index.js +18 -0
- package/dist/core/baseline/store.d.ts +48 -0
- package/dist/core/baseline/store.js +163 -0
- package/dist/core/brain-cli/service.d.ts +26 -0
- package/dist/core/brain-cli/service.js +175 -0
- package/dist/core/brain-product/init.d.ts +45 -0
- package/dist/core/brain-product/init.js +232 -0
- package/dist/core/changes/analyze.d.ts +41 -0
- package/dist/core/changes/analyze.js +223 -0
- package/dist/core/context-health/analyze.d.ts +15 -0
- package/dist/core/context-health/analyze.js +234 -0
- package/dist/core/fix/apply.d.ts +21 -4
- package/dist/core/fix/apply.js +118 -18
- package/dist/core/fix/backup.d.ts +30 -0
- package/dist/core/fix/backup.js +193 -0
- package/dist/core/fix/plan.d.ts +6 -3
- package/dist/core/fix/plan.js +113 -9
- package/dist/core/fix/render.d.ts +2 -0
- package/dist/core/fix/render.js +36 -21
- package/dist/core/fix/run.js +5 -1
- package/dist/core/fix/safe-target.d.ts +14 -0
- package/dist/core/fix/safe-target.js +81 -0
- package/dist/core/fix/types.d.ts +12 -1
- package/dist/core/fix/types.js +2 -0
- package/dist/core/fix/writers/claude-settings.js +3 -1
- package/dist/core/fix/writers/codex-config.d.ts +3 -0
- package/dist/core/fix/writers/codex-config.js +24 -3
- package/dist/core/fix/writers/cursorignore.js +3 -1
- package/dist/core/fix/writers/simple-ignore.d.ts +16 -0
- package/dist/core/fix/writers/simple-ignore.js +68 -0
- package/dist/core/monorepo/detect.d.ts +13 -0
- package/dist/core/monorepo/detect.js +121 -0
- package/dist/core/rules/build-context.js +22 -3
- package/dist/core/rules/context/generated-directory.js +11 -1
- package/dist/core/rules/context/large-instruction-file.js +6 -0
- package/dist/core/rules/context/large-log-file.js +16 -1
- package/dist/core/rules/ignore.d.ts +11 -1
- package/dist/core/rules/ignore.js +22 -1
- package/dist/core/rules/instructions/empty-instructions.js +7 -0
- package/dist/core/rules/security/env-file-exposure.js +23 -2
- package/dist/core/rules/security/private-key-file.js +1 -1
- package/dist/core/scanner/scan.js +1 -1
- package/dist/core/schemas/validate.d.ts +6 -0
- package/dist/core/schemas/validate.js +62 -0
- package/dist/core/scoring/compute-scores.d.ts +1 -1
- package/dist/core/scoring/compute-scores.js +5 -1
- package/dist/core/scoring/placeholder.d.ts +1 -1
- package/dist/core/scoring/placeholder.js +5 -1
- package/dist/core/secrets/scan.d.ts +23 -0
- package/dist/core/secrets/scan.js +172 -0
- package/dist/dashboard/server.d.ts +24 -0
- package/dist/dashboard/server.js +386 -0
- package/dist/enforcement/runner.d.ts +23 -0
- package/dist/enforcement/runner.js +56 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +12 -0
- package/dist/integrations/github/pr-analyze.d.ts +28 -0
- package/dist/integrations/github/pr-analyze.js +98 -0
- package/dist/integrations/local-ai/provider.d.ts +45 -0
- package/dist/integrations/local-ai/provider.js +104 -0
- package/dist/intelligence/git/analyze.d.ts +34 -0
- package/dist/intelligence/git/analyze.js +136 -0
- package/dist/intelligence/graph/build.d.ts +13 -0
- package/dist/intelligence/graph/build.js +215 -0
- package/dist/knowledge/store.d.ts +25 -0
- package/dist/knowledge/store.js +91 -0
- package/dist/mcp/agentdoctor/server.d.ts +16 -0
- package/dist/mcp/agentdoctor/server.js +67 -0
- package/dist/mcp/brain/session.js +7 -7
- package/dist/mcp/intelligence/handlers.d.ts +11 -0
- package/dist/mcp/intelligence/handlers.js +175 -0
- package/dist/mcp/intelligence/path-safety.d.ts +11 -0
- package/dist/mcp/intelligence/path-safety.js +82 -0
- package/dist/mcp/intelligence/registry.d.ts +8 -0
- package/dist/mcp/intelligence/registry.js +160 -0
- package/dist/ops/health.d.ts +18 -0
- package/dist/ops/health.js +51 -0
- package/dist/platform/ai-quality/analyze.d.ts +9 -0
- package/dist/platform/ai-quality/analyze.js +65 -0
- package/dist/platform/architecture/drift.d.ts +17 -0
- package/dist/platform/architecture/drift.js +74 -0
- package/dist/platform/auth/local.d.ts +15 -0
- package/dist/platform/auth/local.js +58 -0
- package/dist/platform/context-security/analyze.d.ts +6 -0
- package/dist/platform/context-security/analyze.js +97 -0
- package/dist/platform/firewall/evaluate.d.ts +54 -0
- package/dist/platform/firewall/evaluate.js +308 -0
- package/dist/platform/graph/build.d.ts +6 -0
- package/dist/platform/graph/build.js +193 -0
- package/dist/platform/health/analyze.d.ts +7 -0
- package/dist/platform/health/analyze.js +186 -0
- package/dist/platform/index.d.ts +27 -0
- package/dist/platform/index.js +87 -0
- package/dist/platform/knowledge/analyze.d.ts +20 -0
- package/dist/platform/knowledge/analyze.js +96 -0
- package/dist/platform/provenance/build.d.ts +44 -0
- package/dist/platform/provenance/build.js +65 -0
- package/dist/platform/readiness/scorecard.d.ts +5 -0
- package/dist/platform/readiness/scorecard.js +98 -0
- package/dist/platform/refactor/impact.d.ts +20 -0
- package/dist/platform/refactor/impact.js +60 -0
- package/dist/platform/reports/export.d.ts +12 -0
- package/dist/platform/reports/export.js +92 -0
- package/dist/platform/security/redact.d.ts +15 -0
- package/dist/platform/security/redact.js +87 -0
- package/dist/platform/sessions/store.d.ts +32 -0
- package/dist/platform/sessions/store.js +97 -0
- package/dist/platform/store.d.ts +6 -0
- package/dist/platform/store.js +57 -0
- package/dist/platform/test-impact/analyze.d.ts +32 -0
- package/dist/platform/test-impact/analyze.js +164 -0
- package/dist/platform/time-machine/compare.d.ts +17 -0
- package/dist/platform/time-machine/compare.js +62 -0
- package/dist/platform/tokens/plan.d.ts +28 -0
- package/dist/platform/tokens/plan.js +89 -0
- package/dist/platform/types.d.ts +111 -0
- package/dist/platform/types.js +5 -0
- package/dist/plugins/runtime.d.ts +36 -0
- package/dist/plugins/runtime.js +101 -0
- package/dist/plugins/sdk.d.ts +29 -0
- package/dist/plugins/sdk.js +114 -0
- package/dist/policy/packs.d.ts +9 -0
- package/dist/policy/packs.js +59 -0
- package/dist/reporters/terminal/report.js +38 -5
- package/dist/storage/provider.d.ts +35 -0
- package/dist/storage/provider.js +103 -0
- package/dist/team/auth.d.ts +35 -0
- package/dist/team/auth.js +70 -0
- package/dist/types/index.d.ts +5 -1
- package/package.json +14 -7
|
@@ -7,7 +7,39 @@ import { formatPackageManager, formatPackageManagers } from "../../detectors/pac
|
|
|
7
7
|
import { colors, symbolFail, symbolInfo, symbolOk, symbolWarn } from "../../utils/colors.js";
|
|
8
8
|
import { sanitizeTerminalText } from "../../security/redaction.js";
|
|
9
9
|
const DEFAULT_FINDING_LIMIT = 12;
|
|
10
|
-
const LIMITED_ANALYSIS_NOTE = "
|
|
10
|
+
const LIMITED_ANALYSIS_NOTE = "Agent-specific exposure checks are limited (no supported agent config); repository hygiene still applied.";
|
|
11
|
+
/** Agents with an implemented Safe Fix writer (detect-only agents are excluded). */
|
|
12
|
+
const SAFE_FIX_AGENT_IDS = new Set([
|
|
13
|
+
"cursor",
|
|
14
|
+
"claude-code",
|
|
15
|
+
"codex",
|
|
16
|
+
"gemini-cli",
|
|
17
|
+
"aider",
|
|
18
|
+
]);
|
|
19
|
+
function renderAgentCoverageSummary(result) {
|
|
20
|
+
const configured = result.agents.filter((a) => a.configured);
|
|
21
|
+
const detectedOnly = result.agents.filter((a) => a.detected && !a.configured);
|
|
22
|
+
const withFix = configured.filter((a) => SAFE_FIX_AGENT_IDS.has(a.id));
|
|
23
|
+
const detectOnlyConfigured = configured.filter((a) => !SAFE_FIX_AGENT_IDS.has(a.id));
|
|
24
|
+
const lines = [];
|
|
25
|
+
lines.push("");
|
|
26
|
+
if (result.agentSecurityAnalysis === "limited") {
|
|
27
|
+
lines.push(colors.dim(" Coverage: 0 configured · universal hygiene still runs (limited agent analysis)"));
|
|
28
|
+
return lines;
|
|
29
|
+
}
|
|
30
|
+
const parts = [`${configured.length} configured`];
|
|
31
|
+
if (detectedOnly.length > 0) {
|
|
32
|
+
parts.push(`${detectedOnly.length} detected-only`);
|
|
33
|
+
}
|
|
34
|
+
if (withFix.length > 0) {
|
|
35
|
+
parts.push(`Safe Fix: ${withFix.map((a) => a.displayName).join(", ")}`);
|
|
36
|
+
}
|
|
37
|
+
if (detectOnlyConfigured.length > 0) {
|
|
38
|
+
parts.push(`detect-only (no Safe Fix writer): ${detectOnlyConfigured.map((a) => a.displayName).join(", ")}`);
|
|
39
|
+
}
|
|
40
|
+
lines.push(colors.dim(` Coverage: ${parts.join(" · ")}`));
|
|
41
|
+
return lines;
|
|
42
|
+
}
|
|
11
43
|
function groupFindings(findings) {
|
|
12
44
|
return {
|
|
13
45
|
critical: findings.filter((f) => f.severity === "critical"),
|
|
@@ -152,6 +184,7 @@ export function renderTerminalReport(result, options = {}) {
|
|
|
152
184
|
}
|
|
153
185
|
}
|
|
154
186
|
}
|
|
187
|
+
lines.push(...renderAgentCoverageSummary(result));
|
|
155
188
|
lines.push("");
|
|
156
189
|
const grouped = groupFindings(result.findings);
|
|
157
190
|
const total = result.summary.total;
|
|
@@ -164,10 +197,10 @@ export function renderTerminalReport(result, options = {}) {
|
|
|
164
197
|
}
|
|
165
198
|
if (total === 0) {
|
|
166
199
|
if (limited) {
|
|
167
|
-
lines.push(` ${
|
|
200
|
+
lines.push(` ${symbolOk()} No repository-hygiene findings — agent readiness not scored (no supported agent config)`);
|
|
168
201
|
lines.push("");
|
|
169
|
-
lines.push(colors.dim("
|
|
170
|
-
lines.push(colors.dim("
|
|
202
|
+
lines.push(colors.dim(" Optional next: add project agent config (`.cursor/`, `CLAUDE.md`, `AGENTS.md`, `.github/copilot-instructions.md`, `.windsurf/rules/`, `GEMINI.md`, or `.aider.conf.yml`)"));
|
|
203
|
+
lines.push(colors.dim(" so agent exposure checks run fully. CLI, Action, and Project Brain MCP still work without an agent."));
|
|
171
204
|
}
|
|
172
205
|
else {
|
|
173
206
|
lines.push(` ${symbolOk()} No findings`);
|
|
@@ -217,7 +250,7 @@ export function renderTerminalReport(result, options = {}) {
|
|
|
217
250
|
lines.push("");
|
|
218
251
|
if (result.scoringAvailable && result.scores) {
|
|
219
252
|
if (limited) {
|
|
220
|
-
lines.push(` Readiness: n/a — configure Cursor, Claude Code, or
|
|
253
|
+
lines.push(` Readiness: n/a — configure a supported agent (Cursor, Claude Code, Codex, Copilot, Windsurf, Gemini CLI, or Aider) before treating scores as agent readiness`);
|
|
221
254
|
lines.push(colors.dim(` Numeric scores remain in JSON (overall ${result.scores.overall}/100) for repository-risk findings only`));
|
|
222
255
|
}
|
|
223
256
|
else {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { StorageProvider } from "../contracts/index.js";
|
|
2
|
+
export declare class FilesystemStorageProvider implements StorageProvider {
|
|
3
|
+
readonly kind: "filesystem";
|
|
4
|
+
private readonly root;
|
|
5
|
+
constructor(rootInput: string, subdir?: string);
|
|
6
|
+
private resolveKey;
|
|
7
|
+
get(key: string): Promise<string | null>;
|
|
8
|
+
set(key: string, value: string): Promise<void>;
|
|
9
|
+
delete(key: string): Promise<void>;
|
|
10
|
+
list(prefix: string): Promise<string[]>;
|
|
11
|
+
}
|
|
12
|
+
/** In-memory provider for tests and optional ephemeral mode. */
|
|
13
|
+
export declare class MemoryStorageProvider implements StorageProvider {
|
|
14
|
+
readonly kind: "memory";
|
|
15
|
+
private readonly map;
|
|
16
|
+
get(key: string): Promise<string | null>;
|
|
17
|
+
set(key: string, value: string): Promise<void>;
|
|
18
|
+
delete(key: string): Promise<void>;
|
|
19
|
+
list(prefix: string): Promise<string[]>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* SQLite provider — optional. Requires better-sqlite3 when enabled.
|
|
23
|
+
* Without the dependency, construction throws a documented error (no fake DB).
|
|
24
|
+
*/
|
|
25
|
+
export declare function tryCreateSqliteStorage(_root: string): Promise<StorageProvider | {
|
|
26
|
+
error: string;
|
|
27
|
+
}>;
|
|
28
|
+
export interface WorkspaceRecord {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
repositoryRoots: string[];
|
|
32
|
+
createdAt: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function saveWorkspace(storage: StorageProvider, workspace: WorkspaceRecord): Promise<void>;
|
|
35
|
+
export declare function loadWorkspace(storage: StorageProvider, id: string): Promise<WorkspaceRecord | null>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { resolveRepoRoot, isPathInsideRoot } from "../utils/path.js";
|
|
4
|
+
import { atomicWriteTextFile } from "../utils/fs.js";
|
|
5
|
+
export class FilesystemStorageProvider {
|
|
6
|
+
kind = "filesystem";
|
|
7
|
+
root;
|
|
8
|
+
constructor(rootInput, subdir = ".agentdoctor/storage") {
|
|
9
|
+
this.root = path.join(resolveRepoRoot(rootInput), subdir);
|
|
10
|
+
}
|
|
11
|
+
resolveKey(key) {
|
|
12
|
+
if (key.includes("\0") || key.split(/[/\\]/).includes("..") || path.isAbsolute(key)) {
|
|
13
|
+
throw new Error(`unsafe storage key: ${key}`);
|
|
14
|
+
}
|
|
15
|
+
const target = path.resolve(this.root, key);
|
|
16
|
+
if (!isPathInsideRoot(this.root, target))
|
|
17
|
+
throw new Error("storage path escape");
|
|
18
|
+
return target;
|
|
19
|
+
}
|
|
20
|
+
async get(key) {
|
|
21
|
+
try {
|
|
22
|
+
return await fs.readFile(this.resolveKey(key), "utf8");
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async set(key, value) {
|
|
29
|
+
const target = this.resolveKey(key);
|
|
30
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
31
|
+
await atomicWriteTextFile(target, value);
|
|
32
|
+
}
|
|
33
|
+
async delete(key) {
|
|
34
|
+
try {
|
|
35
|
+
await fs.unlink(this.resolveKey(key));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// ignore
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async list(prefix) {
|
|
42
|
+
const base = this.resolveKey(prefix || ".");
|
|
43
|
+
const out = [];
|
|
44
|
+
async function walk(dir, rel) {
|
|
45
|
+
let entries;
|
|
46
|
+
try {
|
|
47
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
for (const e of entries) {
|
|
53
|
+
const nextRel = rel ? `${rel}/${e.name}` : e.name;
|
|
54
|
+
if (e.isDirectory())
|
|
55
|
+
await walk(path.join(dir, e.name), nextRel);
|
|
56
|
+
else
|
|
57
|
+
out.push(nextRel);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const st = await fs.stat(base).catch(() => null);
|
|
61
|
+
if (!st)
|
|
62
|
+
return [];
|
|
63
|
+
if (st.isFile())
|
|
64
|
+
return [prefix];
|
|
65
|
+
await walk(base, prefix === "." ? "" : prefix);
|
|
66
|
+
return out.sort();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** In-memory provider for tests and optional ephemeral mode. */
|
|
70
|
+
export class MemoryStorageProvider {
|
|
71
|
+
kind = "memory";
|
|
72
|
+
map = new Map();
|
|
73
|
+
async get(key) {
|
|
74
|
+
return this.map.get(key) ?? null;
|
|
75
|
+
}
|
|
76
|
+
async set(key, value) {
|
|
77
|
+
this.map.set(key, value);
|
|
78
|
+
}
|
|
79
|
+
async delete(key) {
|
|
80
|
+
this.map.delete(key);
|
|
81
|
+
}
|
|
82
|
+
async list(prefix) {
|
|
83
|
+
return [...this.map.keys()].filter((k) => k.startsWith(prefix)).sort();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* SQLite provider — optional. Requires better-sqlite3 when enabled.
|
|
88
|
+
* Without the dependency, construction throws a documented error (no fake DB).
|
|
89
|
+
*/
|
|
90
|
+
export async function tryCreateSqliteStorage(_root) {
|
|
91
|
+
return {
|
|
92
|
+
error: "SQLite storage is feature-flagged and requires optional dependency better-sqlite3 (not bundled). Use filesystem provider for local-first default.",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export async function saveWorkspace(storage, workspace) {
|
|
96
|
+
await storage.set(`workspaces/${workspace.id}.json`, `${JSON.stringify(workspace, null, 2)}\n`);
|
|
97
|
+
}
|
|
98
|
+
export async function loadWorkspace(storage, id) {
|
|
99
|
+
const raw = await storage.get(`workspaces/${id}.json`);
|
|
100
|
+
if (!raw)
|
|
101
|
+
return null;
|
|
102
|
+
return JSON.parse(raw);
|
|
103
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { StorageProvider } from "../contracts/index.js";
|
|
2
|
+
export type TeamRole = "owner" | "admin" | "member" | "viewer";
|
|
3
|
+
export interface TeamUser {
|
|
4
|
+
id: string;
|
|
5
|
+
username: string;
|
|
6
|
+
passwordHash: string;
|
|
7
|
+
salt: string;
|
|
8
|
+
role: TeamRole;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
/** Development-only local auth — not enterprise SSO. */
|
|
11
|
+
authMode: "local-dev";
|
|
12
|
+
}
|
|
13
|
+
export interface SessionToken {
|
|
14
|
+
token: string;
|
|
15
|
+
userId: string;
|
|
16
|
+
expiresAt: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function registerLocalDevUser(options: {
|
|
19
|
+
root: string;
|
|
20
|
+
username: string;
|
|
21
|
+
password: string;
|
|
22
|
+
role?: TeamRole;
|
|
23
|
+
storage?: StorageProvider;
|
|
24
|
+
}): Promise<TeamUser>;
|
|
25
|
+
export declare function authenticateLocalDev(options: {
|
|
26
|
+
root: string;
|
|
27
|
+
username: string;
|
|
28
|
+
password: string;
|
|
29
|
+
storage?: StorageProvider;
|
|
30
|
+
}): Promise<SessionToken>;
|
|
31
|
+
export declare function authorize(root: string, token: string, minRole: TeamRole, storage?: StorageProvider): Promise<{
|
|
32
|
+
ok: boolean;
|
|
33
|
+
user?: TeamUser;
|
|
34
|
+
reason: string;
|
|
35
|
+
}>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createHash, randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { FilesystemStorageProvider } from "../storage/provider.js";
|
|
3
|
+
function hashPassword(password, salt) {
|
|
4
|
+
return scryptSync(password, salt, 32).toString("hex");
|
|
5
|
+
}
|
|
6
|
+
export async function registerLocalDevUser(options) {
|
|
7
|
+
if (options.password.length < 8)
|
|
8
|
+
throw new Error("password must be at least 8 characters");
|
|
9
|
+
const storage = options.storage ?? new FilesystemStorageProvider(options.root, ".agentdoctor/team");
|
|
10
|
+
const salt = randomBytes(16).toString("hex");
|
|
11
|
+
const user = {
|
|
12
|
+
id: `user_${createHash("sha1").update(options.username).digest("hex").slice(0, 10)}`,
|
|
13
|
+
username: options.username,
|
|
14
|
+
passwordHash: hashPassword(options.password, salt),
|
|
15
|
+
salt,
|
|
16
|
+
role: options.role ?? "member",
|
|
17
|
+
createdAt: new Date().toISOString(),
|
|
18
|
+
authMode: "local-dev",
|
|
19
|
+
};
|
|
20
|
+
await storage.set(`users/${user.id}.json`, `${JSON.stringify(user, null, 2)}\n`);
|
|
21
|
+
return {
|
|
22
|
+
...user,
|
|
23
|
+
passwordHash: "[REDACTED]",
|
|
24
|
+
salt: "[REDACTED]",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export async function authenticateLocalDev(options) {
|
|
28
|
+
const storage = options.storage ?? new FilesystemStorageProvider(options.root, ".agentdoctor/team");
|
|
29
|
+
const keys = await storage.list("users");
|
|
30
|
+
for (const key of keys) {
|
|
31
|
+
const raw = await storage.get(key.startsWith("users/") ? key : `users/${key}`);
|
|
32
|
+
if (!raw)
|
|
33
|
+
continue;
|
|
34
|
+
const user = JSON.parse(raw);
|
|
35
|
+
if (user.username !== options.username)
|
|
36
|
+
continue;
|
|
37
|
+
const next = hashPassword(options.password, user.salt);
|
|
38
|
+
const a = Buffer.from(next, "hex");
|
|
39
|
+
const b = Buffer.from(user.passwordHash, "hex");
|
|
40
|
+
if (a.length !== b.length || !timingSafeEqual(a, b))
|
|
41
|
+
break;
|
|
42
|
+
const token = randomBytes(24).toString("hex");
|
|
43
|
+
const session = {
|
|
44
|
+
token,
|
|
45
|
+
userId: user.id,
|
|
46
|
+
expiresAt: new Date(Date.now() + 8 * 3600_000).toISOString(),
|
|
47
|
+
};
|
|
48
|
+
await storage.set(`sessions/${token}.json`, `${JSON.stringify(session, null, 2)}\n`);
|
|
49
|
+
return session;
|
|
50
|
+
}
|
|
51
|
+
throw new Error("invalid credentials");
|
|
52
|
+
}
|
|
53
|
+
export async function authorize(root, token, minRole, storage) {
|
|
54
|
+
const store = storage ?? new FilesystemStorageProvider(root, ".agentdoctor/team");
|
|
55
|
+
const raw = await store.get(`sessions/${token}.json`);
|
|
56
|
+
if (!raw)
|
|
57
|
+
return { ok: false, reason: "missing session" };
|
|
58
|
+
const session = JSON.parse(raw);
|
|
59
|
+
if (Date.parse(session.expiresAt) < Date.now())
|
|
60
|
+
return { ok: false, reason: "expired session" };
|
|
61
|
+
const userRaw = await store.get(`users/${session.userId}.json`);
|
|
62
|
+
if (!userRaw)
|
|
63
|
+
return { ok: false, reason: "missing user" };
|
|
64
|
+
const user = JSON.parse(userRaw);
|
|
65
|
+
const rank = { viewer: 1, member: 2, admin: 3, owner: 4 };
|
|
66
|
+
if ((rank[user.role] ?? 0) < (rank[minRole] ?? 99)) {
|
|
67
|
+
return { ok: false, reason: "insufficient role", user };
|
|
68
|
+
}
|
|
69
|
+
return { ok: true, user, reason: "authorized" };
|
|
70
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type LanguageId = "typescript" | "javascript" | "python" | "dart" | "php"
|
|
|
9
9
|
export type FrameworkId = "nextjs" | "react" | "vue" | "nuxt" | "svelte" | "nodejs" | "express" | "nestjs" | "flutter" | "laravel" | "django" | "fastapi" | "unknown";
|
|
10
10
|
export type PackageManagerId = "npm" | "pnpm" | "yarn" | "bun" | "composer" | "pub" | "pip" | "poetry" | "cargo" | "gradle" | "unknown";
|
|
11
11
|
export type MonorepoToolId = "npm-workspaces" | "pnpm-workspaces" | "turborepo" | "nx" | "multi-project" | "none";
|
|
12
|
-
export type AgentId = "cursor" | "claude-code" | "codex";
|
|
12
|
+
export type AgentId = "cursor" | "claude-code" | "codex" | "copilot" | "windsurf" | "gemini-cli" | "aider";
|
|
13
13
|
export interface DiscoveredFile {
|
|
14
14
|
/** Absolute path */
|
|
15
15
|
absolutePath: string;
|
|
@@ -76,6 +76,10 @@ export interface AgentScores {
|
|
|
76
76
|
cursor: number;
|
|
77
77
|
"claude-code": number;
|
|
78
78
|
codex: number;
|
|
79
|
+
copilot: number;
|
|
80
|
+
windsurf: number;
|
|
81
|
+
"gemini-cli": number;
|
|
82
|
+
aider: number;
|
|
79
83
|
}
|
|
80
84
|
export interface Scores {
|
|
81
85
|
overall: number;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praneeth_54/agentdoctor",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Codebase intelligence, repository analysis, safety controls, and MCP tools for developers and engineering teams.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"agentdoctor": "./dist/cli/index.js"
|
|
@@ -54,6 +54,10 @@
|
|
|
54
54
|
"cursor",
|
|
55
55
|
"claude-code",
|
|
56
56
|
"codex",
|
|
57
|
+
"copilot",
|
|
58
|
+
"windsurf",
|
|
59
|
+
"gemini",
|
|
60
|
+
"aider",
|
|
57
61
|
"mcp",
|
|
58
62
|
"security",
|
|
59
63
|
"linter",
|
|
@@ -77,19 +81,22 @@
|
|
|
77
81
|
"dependencies": {
|
|
78
82
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
79
83
|
"commander": "^14.0.3",
|
|
80
|
-
"picocolors": "^1.1.1"
|
|
84
|
+
"picocolors": "^1.1.1",
|
|
85
|
+
"typescript": "^5.9.3"
|
|
81
86
|
},
|
|
82
87
|
"devDependencies": {
|
|
83
88
|
"@eslint/js": "^9.39.2",
|
|
84
89
|
"@types/node": "^22.19.1",
|
|
85
90
|
"eslint": "^9.39.2",
|
|
86
91
|
"prettier": "^3.7.4",
|
|
87
|
-
"tsx": "^4.
|
|
88
|
-
"typescript": "^
|
|
89
|
-
"typescript-eslint": "^8.50.0",
|
|
92
|
+
"tsx": "^4.23.13",
|
|
93
|
+
"typescript-eslint": "^8.70.0",
|
|
90
94
|
"vitest": "^3.2.4"
|
|
91
95
|
},
|
|
92
96
|
"overrides": {
|
|
93
|
-
"js-yaml": "4.3.
|
|
97
|
+
"js-yaml": "4.3.2",
|
|
98
|
+
"fast-uri": "3.1.8",
|
|
99
|
+
"hono": "4.13.8",
|
|
100
|
+
"qs": "6.16.0"
|
|
94
101
|
}
|
|
95
102
|
}
|