Restructure into a monorepo: models, VS Code extension, daemon, shared

- models/: GGUFs (Git LFS), manifest.json, model card, Apache-2.0 LICENSE + NOTICE
- extensions/vscode/: the Local Autocomplete extension (v0.5.0, publisher harvmaster)
- daemon/: acd completion daemon (bundled into the extension; also runs standalone)
- shared/: prompt layout + symbol extraction shared with the training pipeline
- scripts/: make-model-release.mjs, publish-extension-release.mjs (Gitea releases)
- README with quick start, settings, architecture, build and release instructions;
  MIT license for code; CHANGELOG

The default model manifest URL moves to models/manifest.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 15:37:19 +00:00
co-authored by Claude Opus 5.5
parent f8dcf1b7df
commit fa8f775779
52 changed files with 309630 additions and 207 deletions
+95
View File
@@ -0,0 +1,95 @@
import fs from 'node:fs'
import path from 'node:path'
import type { FimOrder } from '../../shared/prompt.ts'
export type ContextMode = 'symbols' | 'simple' | 'raw'
/** What a model was trained on; the toggle only offers these modes. */
export interface ModelProfile {
path: string
order: FimOrder
modes: ContextMode[]
defaultMode: ContextMode
}
export interface DaemonConfig {
host: string
port: number
tokenizer: { json: string; config: string }
llamaServer: {
binary: string
port: number
ctx: number
/** Extra flags, e.g. ["-ngl", "99"] on Apple Silicon, ["-t", "16"] on the laptop CPU. */
args: string[]
}
models: Record<string, ModelProfile>
defaultModel: string
prompt: {
prefixTokens: number
suffixTokens: number
/** Prefix window start moves in multiples of this many lines (KV-cache stability). */
prefixChunkLines: number
maxLines: number
nPredictSingleLine: number
nPredictMultiLine: number
}
symbols: {
/** Token budget for the __symbols__.d.ts context (Stage C was trained with up to 2048). */
budget: number
/** How long a request waits for fresh context when the anchor changes before using stale/none. */
waitMs: number
/** Member completions after `expr.`: in training, but every `.` changes the header (cold prefill). */
members: boolean
}
thisRepair: { enabled: boolean; waitMs: number }
telemetry: { enabled: boolean; dir: string }
/** Where opted-in edit recordings from editors are appended (the opt-in is the editor setting). */
recording: { dir: string }
stateFile: string
/** Exit after this many minutes without any request (0 = never). Editors poll every 15 s while open. */
idleShutdownMinutes?: number
/** Opaque tag reported by /health (the VS Code extension uses it to replace daemons left over from older versions). */
instanceTag?: string
}
/** Toggles that survive daemon restarts and are shared by every editor talking to this daemon. */
export interface RuntimeState {
enabled: boolean
model: string
mode: ContextMode
}
export function loadConfig(configPath: string): DaemonConfig {
const dir = path.dirname(path.resolve(configPath))
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8')) as DaemonConfig
const abs = (p: string) => (path.isAbsolute(p) || !p.includes('/') ? p : path.resolve(dir, p)) // bare names (e.g. "llama-server") stay on PATH
raw.tokenizer = { json: abs(raw.tokenizer.json), config: abs(raw.tokenizer.config) }
raw.llamaServer.binary = abs(raw.llamaServer.binary)
for (const m of Object.values(raw.models)) m.path = abs(m.path)
raw.telemetry.dir = abs(raw.telemetry.dir)
raw.recording.dir = abs(raw.recording.dir)
raw.stateFile = abs(raw.stateFile)
if (!raw.models[raw.defaultModel]) throw new Error(`defaultModel ${raw.defaultModel} is not in models`)
for (const [name, m] of Object.entries(raw.models)) {
if (!m.modes.includes(m.defaultMode)) throw new Error(`model ${name}: defaultMode must be one of its modes`)
}
return raw
}
export function loadState(config: DaemonConfig): RuntimeState {
const fallback: RuntimeState = { enabled: true, model: config.defaultModel, mode: config.models[config.defaultModel].defaultMode }
try {
const s = JSON.parse(fs.readFileSync(config.stateFile, 'utf8')) as Partial<RuntimeState>
const model = s.model && config.models[s.model] ? s.model : fallback.model
const mode = s.mode && config.models[model].modes.includes(s.mode) ? s.mode : config.models[model].defaultMode
return { enabled: s.enabled ?? true, model, mode }
} catch {
return fallback
}
}
export function saveState(config: DaemonConfig, state: RuntimeState): void {
fs.mkdirSync(path.dirname(config.stateFile), { recursive: true })
fs.writeFileSync(config.stateFile, JSON.stringify(state, null, 2) + '\n')
}