Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/stale-docs-cleanup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@prosdevlab/dev-agent": patch
---

### Docs Cleanup & Tool Refinements

**CLI:**
- Removed `dev explore` — merged `--similar-to` flag into `dev search`
- Search threshold default changed from 0.7 to 0 (RRF scores are much lower than cosine similarity)

**MCP Tools:**
- Renamed `dev_inspect` → `dev_patterns` (focused on pattern analysis)
- Removed `threshold` parameter from `dev_patterns`
- Removed 3 prompts: `analyze-issue`, `search-github`, `create-plan`

**Scanner:**
- Extended default exclusions: `.env*`, `*.min.js`, `*.d.ts`, `generated/`, `.terraform/`, `.claude/`, `*.wasm`, `public/`, `static/`
3 changes: 1 addition & 2 deletions .claude/agents/bug-investigator.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ User Command → Commander.js → Core Service → Scanner/Vector/GitHub

**Indexing path:**
```
dev index . → Indexer → Scanner (ts-morph/tree-sitter) → Vector Storage (LanceDB)
→ Embedding (@xenova/transformers) → Persisted Index
dev index → Indexer → Scanner (ts-morph/tree-sitter) → Antfly (embed + store + hybrid search)
```

### Phase 3: Identify Root Cause
Expand Down
2 changes: 1 addition & 1 deletion .claude/agents/logic-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Every finding MUST include confidence: **HIGH** (verified from code), **MEDIUM**

### Tier 2 (Standard+ Effort)
- [ ] Scanner handles malformed source files gracefully (ts-morph, tree-sitter)
- [ ] Vector storage operations handle LanceDB connection failures
- [ ] Vector storage operations handle Antfly connection failures
- [ ] MCP adapter responses follow the expected schema
- [ ] Event bus listeners cleaned up properly (no memory leaks)
- [ ] Subagent coordinator handles agent failures without crashing
Expand Down
8 changes: 3 additions & 5 deletions .claude/agents/quick-scout.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,11 @@ Do NOT guess at architectural reasoning or make recommendations.
packages/
core/src/
scanner/ # ts-morph (TS/JS) and tree-sitter (Go) analysis
vector/ # LanceDB vector storage + embeddings
services/ # Coordinator, search, GitHub, health, metrics
vector/ # Antfly vector storage + embeddings
services/ # Coordinator, search, health
events/ # Event bus system
indexer/ # Repository indexing orchestration
map/ # Codebase structure mapping
git/ # Git history indexing
metrics/ # Metrics store
observability/ # Logger integration

cli/src/
Expand All @@ -50,7 +48,7 @@ packages/

mcp-server/src/
server/ # MCP server setup
adapters/ # Tool adapters (search, refs, map, history, etc.)
adapters/ # Tool adapters (search, refs, map, inspect, status, health)
formatters/ # Compact and verbose output formatters
utils/ # Logger

Expand Down
2 changes: 1 addition & 1 deletion .claude/agents/security-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ This agent **NEVER modifies code**. It reports issues for the developer to fix.
### Data Exposure
- [ ] Vector storage doesn't leak sensitive file contents in error messages
- [ ] GitHub integration doesn't expose private repo data unintentionally
- [ ] Embedding model doesn't send data externally (local-only with @xenova/transformers)
- [ ] Embedding model doesn't send data externally (local-only with Antfly/Termite ONNX)

## Output Format

Expand Down
2 changes: 1 addition & 1 deletion .claude/da-plans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Implementation deviations are logged at the bottom of each plan file.
|-------|-------------|--------|
| [Core](core/) | Scanner, vector storage, services, indexer | Phase 1: Merged, Phase 2: Draft (indexing rethink) |
| [CLI](cli/) | Command-line interface | Not started |
| [MCP Server](mcp-server/) | Model Context Protocol server + adapters | Phase 1: Draft (blocked on core/phase-1) |
| [MCP Server](mcp/) | Model Context Protocol server + adapters | Phase 1: Draft (tools improvement) |
| [Subagents](subagents/) | Coordinator, explorer, planner, GitHub agents | Not started |
| [Integrations](integrations/) | Claude Code, VS Code, Cursor | Not started |
| [Logger](logger/) | @prosdevlab/kero centralized logging | Not started |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Part 1.1: Extract Pure Pattern Analyzers

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Extract the pattern analysis logic from private methods into exported pure functions that are testable without file I/O.

**User stories:** US-1, US-3

**Files:**
- Modify: `packages/core/src/services/pattern-analysis-service.ts`
- Modify: `packages/core/src/services/__tests__/pattern-analysis-service.test.ts`

---

## Task 1: Write tests for pure extraction functions

- [ ] **Step 1: Add test block for extractImportStyleFromContent**

```typescript
// In pattern-analysis-service.test.ts, add:
import {
extractImportStyleFromContent,
extractErrorHandlingFromContent,
extractTypeCoverageFromSignatures,
} from '../pattern-analysis-service';

describe('Pure Pattern Extractors', () => {
describe('extractImportStyleFromContent', () => {
it('should detect ESM imports', () => {
const content = 'import { foo } from "./bar";\nimport * as baz from "baz";';
const result = extractImportStyleFromContent(content);
expect(result).toEqual({ style: 'esm', importCount: 2 });
});

it('should detect CJS requires', () => {
const content = 'const foo = require("bar");\nconst baz = require("baz");';
const result = extractImportStyleFromContent(content);
expect(result).toEqual({ style: 'cjs', importCount: 2 });
});

it('should detect mixed imports', () => {
const content = 'import { foo } from "./bar";\nconst baz = require("baz");';
const result = extractImportStyleFromContent(content);
expect(result).toEqual({ style: 'mixed', importCount: 2 });
});

it('should return unknown for no imports', () => {
const content = 'const x = 1;';
const result = extractImportStyleFromContent(content);
expect(result).toEqual({ style: 'unknown', importCount: 0 });
});
});

describe('extractErrorHandlingFromContent', () => {
it('should detect throw style', () => {
const content = 'throw new Error("oops");\nthrow new TypeError("bad");';
const result = extractErrorHandlingFromContent(content);
expect(result.style).toBe('throw');
});

it('should return unknown for no error handling', () => {
const content = 'const x = 1;';
const result = extractErrorHandlingFromContent(content);
expect(result.style).toBe('unknown');
});
});

describe('extractTypeCoverageFromSignatures', () => {
it('should detect full coverage', () => {
const signatures = [
'function foo(x: string): number',
'function bar(y: boolean): void',
];
const result = extractTypeCoverageFromSignatures(signatures);
expect(result.coverage).toBe('full');
expect(result.annotatedCount).toBe(2);
expect(result.totalCount).toBe(2);
});

it('should detect partial coverage', () => {
const signatures = [
'function foo(x: string): number',
'function bar(y)',
];
const result = extractTypeCoverageFromSignatures(signatures);
expect(result.coverage).toBe('partial');
});

it('should return none for empty', () => {
const result = extractTypeCoverageFromSignatures([]);
expect(result.coverage).toBe('none');
});
});
});
```

- [ ] **Step 2: Run tests to verify they fail**

Run: `pnpm test -- packages/core/src/services/__tests__/pattern-analysis-service.test.ts`
Expected: FAIL — functions not exported

---

## Task 2: Implement and export pure functions

- [ ] **Step 1: Add exported pure functions to pattern-analysis-service.ts**

Add before the class definition:

```typescript
/**
* Extract import style from raw file content. Pure function — no I/O.
*/
export function extractImportStyleFromContent(content: string): ImportStylePattern {
const esmImports = content.match(/^import\s/gm) || [];
const cjsImports = content.match(/require\s*\(/g) || [];
const hasESM = esmImports.length > 0;
const hasCJS = cjsImports.length > 0;

if (!hasESM && !hasCJS) return { style: 'unknown', importCount: 0 };

const importCount = esmImports.length + cjsImports.length;
const style: ImportStylePattern['style'] =
hasESM && hasCJS ? 'mixed' : hasESM ? 'esm' : 'cjs';
return { style, importCount };
}

/**
* Extract error handling pattern from raw file content. Pure function — no I/O.
*/
export function extractErrorHandlingFromContent(content: string): ErrorHandlingPattern {
const counts = {
throw: [...content.matchAll(/throw\s+new\s+\w*Error/g)].length,
result: [...content.matchAll(/Result<|{\s*ok:\s*(true|false)/g)].length,
errorReturn: [...content.matchAll(/\)\s*:\s*\([^)]*,\s*error\)/g)].length,
};
const total = counts.throw + counts.result + counts.errorReturn;
if (total === 0) return { style: 'unknown', examples: [] };

const max = Math.max(counts.throw, counts.result, counts.errorReturn);
const hasMultiple = Object.values(counts).filter((c) => c > 0).length > 1;
let style: ErrorHandlingPattern['style'] = 'unknown';
if (hasMultiple) style = 'mixed';
else if (counts.throw === max) style = 'throw';
else if (counts.result === max) style = 'result';
else if (counts.errorReturn === max) style = 'error-return';
return { style, examples: [] };
}

/**
* Extract type coverage from function/method signatures. Pure function — no I/O.
*/
export function extractTypeCoverageFromSignatures(signatures: string[]): TypeAnnotationPattern {
if (signatures.length === 0) return { coverage: 'none', annotatedCount: 0, totalCount: 0 };

const annotated = signatures.filter((sig) => /(\)|=>)\s*:\s*\w+/.test(sig));
const ratio = annotated.length / signatures.length;
let coverage: TypeAnnotationPattern['coverage'];
if (ratio >= 0.9) coverage = 'full';
else if (ratio >= 0.5) coverage = 'partial';
else if (ratio > 0) coverage = 'minimal';
else coverage = 'none';
return { coverage, annotatedCount: annotated.length, totalCount: signatures.length };
}
```

- [ ] **Step 2: Run tests to verify they pass**

Run: `pnpm test -- packages/core/src/services/__tests__/pattern-analysis-service.test.ts`
Expected: ALL PASS (new + existing)

- [ ] **Step 3: Commit**

```bash
git add packages/core/src/services/pattern-analysis-service.ts packages/core/src/services/__tests__/pattern-analysis-service.test.ts
git commit -m "refactor(core): extract pure pattern analyzers for testability"
```
Loading
Loading