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
104 changes: 99 additions & 5 deletions bun.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion packages/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
},
"dependencies": {
"@pleaseai/code-format": "workspace:*",
"@pleaseai/code-lsp": "workspace:*"
"@pleaseai/code-lsp": "workspace:*",
"@pleaseai/logger": "workspace:*"
},
"devDependencies": {
"@types/bun": "latest",
Expand Down
5 changes: 4 additions & 1 deletion packages/code/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
import { Buffer } from 'node:buffer'
import process from 'node:process'
import { Format } from '@pleaseai/code-format'
import { createLogger } from '@pleaseai/logger'
import pkg from '../package.json'
import { runLSPDiagnostics } from './hooks/lsp'
import { parseArgs } from './utils'

const log = createLogger('code')

const VERSION = pkg.version

interface HookInput {
Expand Down Expand Up @@ -203,6 +206,6 @@ async function main(): Promise<void> {
}

main().catch((error) => {
console.error('[code] Error:', error)
log.fatal({ err: error }, 'Unhandled error')
process.exit(1)
})
1 change: 1 addition & 0 deletions packages/dora/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.0",
"@pleaseai/code-lsp": "workspace:*",
"@pleaseai/logger": "workspace:*",
"zod": "^3.25.0"
},
"devDependencies": {
Expand Down
9 changes: 6 additions & 3 deletions packages/dora/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@

import process from 'node:process'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { createLogger } from '@pleaseai/logger'
import pkg from '../package.json'
import { createDoraServer } from './server'

const log = createLogger('dora')

const VERSION = pkg.version

interface ParsedArgs {
Expand Down Expand Up @@ -48,13 +51,13 @@ function parseArgs(argv: string[]): ParsedArgs {
}

async function serveCommand(projectPath: string, timeout: number): Promise<void> {
console.error(`[dora] Starting MCP server for project: ${projectPath}`)
log.info({ projectPath }, 'Starting MCP server')

const server = await createDoraServer({ projectPath, timeout })
const transport = new StdioServerTransport()

await server.connect(transport)
console.error('[dora] MCP server connected and ready')
log.info('MCP server connected and ready')
}

function versionCommand(): void {
Expand Down Expand Up @@ -122,6 +125,6 @@ async function main(): Promise<void> {
}

main().catch((error) => {
console.error('[dora] Error:', error)
log.fatal({ err: error }, 'Unhandled error')
process.exit(1)
})
5 changes: 4 additions & 1 deletion packages/dora/src/client/port-discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
* Scans ports 24226-24245 to find the running IDE instance
*/

import { createLogger } from '@pleaseai/logger'
import { ServerNotFoundError } from '../errors'
import { transformResponse } from './response-transformer'

const log = createLogger('dora')

const BASE_PORT = 0x5EA2 // 24226
const PORT_RANGE = 20
const DISCOVERY_TIMEOUT = 1000 // 1 second for port discovery
Expand Down Expand Up @@ -74,7 +77,7 @@ export async function discoverPort(projectPath: string): Promise<number> {
if (await checkPort(port, projectPath)) {
cache.port = port
cache.projectPath = projectPath
console.error(`[dora] Found JetBrains IDE service at port ${port}`)
log.debug({ port }, 'Found JetBrains IDE service')
return port
}
}
Expand Down
5 changes: 4 additions & 1 deletion packages/dora/src/providers/ast-grep/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import type { CliMatch, RunSgOptions, SgResult } from './types'
import { existsSync } from 'node:fs'
import { createLogger } from '@pleaseai/logger'
import { spawn } from 'bun'
import {
CLI_LANGUAGES,
Expand All @@ -15,6 +16,8 @@ import {
} from './constants'
import { ensureAstGrepBinary, getInstallInstructions } from './downloader'

const log = createLogger('ast-grep')

// Cached binary path
let resolvedCliPath: string | null = null

Expand Down Expand Up @@ -233,7 +236,7 @@ export async function runSg(options: RunSgOptions, retried = false): Promise<SgR
else {
// Non-truncated output but failed to parse - log and return error
const errorMsg = parseError instanceof Error ? parseError.message : 'unknown'
console.error(`[ast-grep] Failed to parse output: ${errorMsg}`)
log.error({ err: errorMsg }, 'Failed to parse output')
return {
matches: [],
totalMatches: 0,
Expand Down
17 changes: 10 additions & 7 deletions packages/dora/src/providers/ast-grep/downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { PlatformId } from './types'
import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
import process from 'node:process'
import { createLogger } from '@pleaseai/logger'
import { spawn } from 'bun'
import {
AST_GREP_VERSION,
Expand All @@ -18,6 +19,8 @@ import {
PLATFORM_CONFIGS,
} from './constants'

const log = createLogger('ast-grep')

/**
* Verify a binary is actually ast-grep by checking --version output
*/
Expand All @@ -34,7 +37,7 @@ async function verifyAstGrepBinary(binaryPath: string): Promise<boolean> {
return stdout.includes('ast-grep') || /^\d+\.\d+\.\d+/.test(stdout.trim())
}
catch (e) {
console.error(`[ast-grep] Binary verification failed for ${binaryPath}: ${e instanceof Error ? e.message : String(e)}`)
log.warn({ binaryPath, err: e }, 'Binary verification failed')
return false
}
}
Expand Down Expand Up @@ -81,7 +84,7 @@ export function getCachedBinary(): string | null {
}
catch (e) {
// Log warning but continue - will trigger re-download
console.warn(`[ast-grep] Failed to read version marker at ${versionPath}: ${e instanceof Error ? e.message : String(e)}`)
log.warn({ versionPath, err: e }, 'Failed to read version marker')
return null
}
}
Expand Down Expand Up @@ -129,13 +132,13 @@ async function extractZip(archivePath: string, destDir: string): Promise<void> {
export async function downloadAstGrepBinary(platformId?: PlatformId): Promise<string | null> {
const platform = platformId ?? getPlatformId()
if (!platform) {
console.error('[dora] Unsupported platform for ast-grep binary download')
log.error('Unsupported platform for ast-grep binary download')
return null
}

const config = PLATFORM_CONFIGS[platform]
if (!config) {
console.error(`[dora] No binary configuration for platform: ${platform}`)
log.error({ platform }, 'No binary configuration for platform')
return null
}

Expand All @@ -153,7 +156,7 @@ export async function downloadAstGrepBinary(platformId?: PlatformId): Promise<st
}
}

console.log(`[dora] Downloading ast-grep binary v${AST_GREP_VERSION}...`)
log.info({ version: AST_GREP_VERSION }, 'Downloading ast-grep binary')

try {
// Create cache directory
Expand Down Expand Up @@ -188,12 +191,12 @@ export async function downloadAstGrepBinary(platformId?: PlatformId): Promise<st
// Write version marker
writeFileSync(getVersionMarkerPath(), AST_GREP_VERSION)

console.log(`[dora] ast-grep binary ready at ${binaryPath}`)
log.info({ binaryPath }, 'ast-grep binary ready')

return binaryPath
}
catch (err) {
console.error(`[dora] Failed to download ast-grep: ${err instanceof Error ? err.message : err}`)
log.error({ err }, 'Failed to download ast-grep')
return null
}
}
Expand Down
10 changes: 6 additions & 4 deletions packages/dora/src/providers/ast-grep/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@
import type { Provider, ToolDefinition, ToolResult } from '../provider'
import type { RegistryConfig } from '../registry'
import type { NapiLanguage } from './types'
import { createLogger } from '@pleaseai/logger'
import { z, ZodError } from 'zod'
import { isCliAvailable, runSg } from './cli'
import { CLI_LANGUAGES, NAPI_LANGUAGES } from './constants'
import { analyzeCode, getNapiError, isNapiAvailable, transformCode } from './napi'
import { formatAnalyzeResult, formatReplaceResult, formatSearchResult, formatTransformResult, getEmptyResultHint } from './utils'

const log = createLogger('ast-grep')

/**
* Format error for tool result, with special handling for Zod validation errors
*/
Expand Down Expand Up @@ -150,17 +153,16 @@ export class AstGrepProvider implements Provider {
// Pre-check CLI availability (don't fail, just log)
const cliAvailable = await isCliAvailable()
if (!cliAvailable) {
console.log('[ast-grep] CLI not found, will download on first use')
log.debug('CLI not found, will download on first use')
}

// Check NAPI availability
const napiAvailable = isNapiAvailable()
if (!napiAvailable) {
const error = getNapiError()

console.log(`[ast-grep] NAPI not available: ${error ?? 'unknown'}`)

console.log('[ast-grep] In-memory tools (analyze/transform) disabled')
log.debug({ error }, 'NAPI not available')
log.debug('In-memory tools (analyze/transform) disabled')
}

this.connected = true
Expand Down
5 changes: 4 additions & 1 deletion packages/dora/src/providers/ast-grep/napi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
*/

import type { AnalyzeResult, MetaVariable, NapiLanguage, Range } from './types'
import { createLogger } from '@pleaseai/logger'
import { NAPI_LANGUAGES } from './constants'

const log = createLogger('ast-grep')

type AstGrepNapiModule = any

type SgNode = any
Expand All @@ -33,7 +36,7 @@ export function isNapiAvailable(): boolean {
catch (e) {
napiLoadError = e instanceof Error ? e : new Error(String(e))
// Log full error on first load failure for diagnostics
console.error('[ast-grep] NAPI module load failed:', e)
log.debug({ err: e }, 'NAPI module load failed')
return false
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/format/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"test": "echo 'No tests yet'"
},
"dependencies": {
"@pleaseai/logger": "workspace:*",
"yaml": "^2.8.2"
},
"devDependencies": {
Expand Down
6 changes: 4 additions & 2 deletions packages/format/src/config/loader.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { Config } from './schema'
import path from 'node:path'
import { createLogger } from '@pleaseai/logger'
import YAML from 'yaml'
import { ConfigSchema, defaultConfig } from './schema'

const log = createLogger('config')

/**
* Error thrown when configuration validation fails
*/
Expand Down Expand Up @@ -155,8 +158,7 @@ export async function loadConfig(projectDir: string): Promise<Config> {
return defaultConfig
}

// Use stderr to avoid interfering with JSON output on stdout
console.error(`[config] Loading configuration from ${configPath}`)
log.debug({ configPath }, 'Loading configuration')
return loadConfigFromFile(configPath)
}

Expand Down
30 changes: 10 additions & 20 deletions packages/format/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import type { FormatterConfig } from './config'
import path from 'node:path'
import process from 'node:process'

import { createLogger } from '@pleaseai/logger'
import z from 'zod'
import { loadConfig } from './config'
import * as Formatter from './formatter'

const log = {
info: (...args: unknown[]) => console.error('[format]', ...args),
error: (...args: unknown[]) => console.error('[format:error]', ...args),
}
const log = createLogger('format')

export const FormatStatusSchema = z
.object({
Expand Down Expand Up @@ -97,7 +95,7 @@ function init(config: FormatConfig): void {
}

cachedState = { enabled, formatters, projectDir: config.projectDir }
log.info('initialized with', Object.keys(formatters).length, 'formatters')
log.info({ count: Object.keys(formatters).length }, 'initialized formatters')
}

function getState(): State {
Expand All @@ -121,12 +119,12 @@ async function getFormatter(ext: string): Promise<Formatter.Info[]> {
const s = getState()
const result: Formatter.Info[] = []
for (const item of Object.values(s.formatters)) {
log.info('checking', { name: item.name, ext })
log.debug({ name: item.name, ext }, 'checking formatter')
if (!item.extensions.includes(ext))
continue
if (!(await isEnabled(item)))
continue
log.info('enabled', { name: item.name, ext })
log.debug({ name: item.name, ext }, 'formatter enabled')
result.push(item)
}
return result
Expand All @@ -152,17 +150,17 @@ async function status(): Promise<FormatStatus[]> {
async function formatFile(file: string): Promise<boolean> {
const s = getState()
const ext = path.extname(file)
log.info('formatting', { file, ext })
log.debug({ file, ext }, 'formatting file')

const formatters = await getFormatter(ext)
if (formatters.length === 0) {
log.info('no formatter found for', ext)
log.debug({ ext }, 'no formatter found')
return false
}

let success = true
for (const item of formatters) {
log.info('running', { command: item.command })
log.debug({ command: item.command }, 'running formatter')
try {
const proc = Bun.spawn({
cmd: item.command.map(x => x.replace('$FILE', file)),
Expand All @@ -173,20 +171,12 @@ async function formatFile(file: string): Promise<boolean> {
})
const exit = await proc.exited
if (exit !== 0) {
log.error('failed', {
command: item.command,
...item.environment,
})
log.error({ command: item.command, env: item.environment }, 'formatter failed')
success = false
}
}
catch (error) {
log.error('failed to format file', {
error,
command: item.command,
...item.environment,
file,
})
log.error({ err: error, file, command: item.command, env: item.environment }, 'failed to format file')
success = false
}
}
Expand Down
Loading