@contextline/contextline 0.1.1 → 0.2.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/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/tools/appendFragment.ts","../src/config.ts","../src/paths.ts","../src/core/errors.ts","../src/core/atomicWrite.ts","../src/core/markdown.ts","../src/core/details.ts","../src/core/lock.ts","../src/tools/init.ts","../src/core/instructions.ts","../src/tools/hydrate.ts","../src/tools/inspect.ts","../src/core/topology.ts","../src/core/links.ts","../src/tools/validateTopology.ts","../src/hook.ts","../src/setup.ts","../src/host/codex.ts","../src/tools/updateDeepPointer.ts","../src/tools/updateIndexPointer.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { appendFragment } from \"./tools/appendFragment.js\";\nimport { hydrate } from \"./tools/hydrate.js\";\nimport { initContextLine } from \"./tools/init.js\";\nimport { resolveRoot, getMemoryRoot } from \"./config.js\";\nimport { inspect } from \"./tools/inspect.js\";\nimport { validateTopology } from \"./tools/validateTopology.js\";\nimport { runHook } from \"./hook.js\";\nimport { runSetup } from \"./setup.js\";\nimport { updateDeepPointer } from \"./tools/updateDeepPointer.js\";\nimport { updateIndexPointer } from \"./tools/updateIndexPointer.js\";\n\nfunction print(data: unknown) {\n console.log(JSON.stringify(data, null, 2));\n}\n\nconst program = new Command();\n\nprogram.name(\"contextline\").description(\"Local-first pointer-chain memory for AI agents.\").version(\"0.1.0\");\n\nprogram\n .command(\"init\")\n .argument(\"[root]\")\n .description(\"Create a .contextline memory folder.\")\n .action(async (root?: string) => print(await initContextLine(resolveRoot(root))));\n\nprogram\n .command(\"hydrate\")\n .argument(\"[root]\")\n .description(\"Read L1_Quick.md as the bounded quick memory cache.\")\n .action(async (root?: string) => print(await hydrate(root)));\n\nprogram\n .command(\"validate\")\n .argument(\"[root]\")\n .description(\"Validate ContextLine topology.\")\n .action(async (root?: string) => {\n const result = await validateTopology(root);\n print(result);\n if (!result.ok) process.exitCode = 1;\n });\n\nprogram\n .command(\"inspect\")\n .argument(\"[root]\")\n .description(\"Inspect ContextLine folder counts and paths.\")\n .action(async (root?: string) => print(await inspect(root)));\n\nprogram\n .command(\"append\")\n .argument(\"<text>\")\n .argument(\"[root]\")\n .option(\"--date <date>\", \"Date in YYYY-MM-DD format\")\n .description(\"Append an immutable L3 detail fragment.\")\n .action(async (text: string, root: string | undefined, options: { date?: string }) => print(await appendFragment(text, options.date, root)));\n\nprogram\n .command(\"update-deep\")\n .argument(\"<file>\")\n .argument(\"<fact-text>\")\n .argument(\"<detail-file>\")\n .argument(\"[root]\")\n .description(\"Update an L2 deep memory line with a pointer to an L3 detail file.\")\n .action(async (file: string, factText: string, detailFile: string, root?: string) => print(await updateDeepPointer(file, factText, detailFile, root)));\n\nprogram\n .command(\"update-index\")\n .argument(\"<index-line>\")\n .argument(\"<files...>\")\n .option(\"--root <root>\", \"Memory folder path\")\n .description(\"Update an L1 quick memory line with pointers to L2 deep memory files.\")\n .action(async (indexLine: string, files: string[], options: { root?: string }) => print(await updateIndexPointer(indexLine, files, options.root)));\n\nprogram\n .command(\"setup\")\n .option(\"--host <host>\", \"Host adapter: auto, codex, none\", \"auto\")\n .option(\"--verbose\", \"Print full JSON result\")\n .description(\"Initialize memory and configure supported host integrations.\")\n .action(async (options: { host?: string; verbose?: boolean }) => {\n const result = await runSetup(options);\n if (options.verbose) {\n print(result);\n } else {\n process.stdout.write(result.ok ? `ContextLine ready at ${result.root}\\n` : `Setup failed.\\n`);\n }\n if (!result.ok) process.exitCode = 1;\n });\n\nprogram\n .command(\"hook\")\n .option(\"--root <root>\", \"Memory folder path\")\n .description(\"Run ContextLine host hook. Reads hook JSON from stdin.\")\n .action(async (options: { root?: string }) => runHook(options.root));\n\nprogram.parseAsync(process.argv).catch((error) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n});\n","import fs from \"node:fs/promises\";\nimport { resolveRoot } from \"../config.js\";\nimport { detailPath } from \"../paths.js\";\nimport { atomicWriteFile } from \"../core/atomicWrite.js\";\nimport { ensureTrailingNewline } from \"../core/markdown.js\";\nimport { detailFilename, detailHeader, detailLine } from \"../core/details.js\";\nimport { withLock } from \"../core/lock.js\";\nimport { initContextLine } from \"./init.js\";\n\nexport async function appendFragment(text: string, date?: string, rootInput?: string) {\n const root = resolveRoot(rootInput);\n await initContextLine(root);\n return withLock(root, async () => {\n const detail_file = detailFilename(date);\n const filePath = detailPath(root, detail_file);\n let content: string;\n try {\n content = await fs.readFile(filePath, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw error;\n }\n content = detailHeader(date);\n }\n const fragment = detailLine(text, date);\n await atomicWriteFile(filePath, `${ensureTrailingNewline(content)}${fragment}\\n`);\n return {\n ok: true as const,\n detail_file,\n fragment,\n next_required_steps: [\n \"Call contextline with action save_memory to save L3, L2, and L1 together.\",\n \"If manually continuing, call contextline action update_deep for every relevant L2_Deep file and action update_index for compact L1_Quick facts.\"\n ],\n display: `ContextLine L3 detail appended\\n- Detail file: ${detail_file}`\n };\n });\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport function getMemoryRoot(): string {\n if (process.env.CONTEXTLINE_ROOT) return path.resolve(process.env.CONTEXTLINE_ROOT);\n // v1: memory lives in the current working directory.\n // The dream is to support a user-configured global root (OneDrive, GDrive, etc.)\n // so memory is shared across projects and synced across machines.\n // Blocked by host sandbox policies (e.g. Codex auto-review) that deny MCP access\n // to paths outside the project folder. Contributions welcome:\n // https://github.com/contextline/core/issues\n return path.join(process.cwd(), \".contextline\");\n}\n\nexport function resolveRoot(folder?: string): string {\n if (folder) return path.resolve(folder);\n return getMemoryRoot();\n}\n\nexport function ensureGlobalConfigDir(): void {\n const dir = getMemoryRoot();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n","import path from \"node:path\";\nimport { PathSafetyError } from \"./core/errors.js\";\n\nexport const L1_FILE = \"L1_Quick.md\";\nexport const L2_DIR = \"L2_Deep\";\nexport const L3_DIR = \"L3_Details\";\n\nexport function assertSafeRelative(input: string): void {\n if (!input || input.trim() !== input) {\n throw new PathSafetyError(\"Path input must be non-empty and contain no leading/trailing whitespace.\");\n }\n if (path.isAbsolute(input)) {\n throw new PathSafetyError(\"Absolute paths are not allowed in tool inputs.\");\n }\n const parts = input.split(/[\\\\/]+/);\n if (parts.includes(\"..\") || parts.includes(\"\")) {\n throw new PathSafetyError(\"Path traversal and empty path segments are not allowed.\");\n }\n}\n\nexport function assertSafeRelativeFile(input: string): void {\n assertSafeRelative(input);\n if (!input.endsWith(\".md\")) {\n throw new PathSafetyError(\"Memory file paths must end with .md.\");\n }\n}\n\nexport function safeJoin(root: string, ...segments: string[]): string {\n for (const segment of segments) {\n assertSafeRelative(segment);\n }\n const resolvedRoot = path.resolve(root);\n const target = path.resolve(resolvedRoot, ...segments);\n const rel = path.relative(resolvedRoot, target);\n if (rel.startsWith(\"..\") || path.isAbsolute(rel)) {\n throw new PathSafetyError(\"Resolved path escapes ContextLine root.\");\n }\n return target;\n}\n\nexport function deepPath(root: string, file: string): string {\n assertSafeRelativeFile(file);\n if (file.includes(\"/\") || file.includes(\"\\\\\")) {\n throw new PathSafetyError(\"L2 deep memory files must be flat filenames inside L2_Deep for V1.\");\n }\n return safeJoin(root, L2_DIR, file);\n}\n\nexport function detailPath(root: string, file: string): string {\n assertSafeRelativeFile(file);\n return safeJoin(root, L3_DIR, file);\n}\n\n\nexport function l1Path(root: string): string {\n return safeJoin(root, L1_FILE);\n}\n","export class ContextLineError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ContextLineError\";\n }\n}\n\nexport class PathSafetyError extends ContextLineError {\n constructor(message: string) {\n super(message);\n this.name = \"PathSafetyError\";\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport async function atomicWriteFile(filePath: string, content: string): Promise<void> {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n const tmp = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);\n const handle = await fs.open(tmp, \"w\");\n try {\n await handle.writeFile(content, \"utf8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n await fs.rename(tmp, filePath);\n}\n","export function ensureTrailingNewline(content: string): string {\n return content.endsWith(\"\\n\") ? content : `${content}\\n`;\n}\n\nexport function upsertLineByPrefix(content: string, prefix: string, nextLine: string): string {\n const lines = ensureTrailingNewline(content).split(\"\\n\");\n const index = lines.findIndex((line) => line.trimStart().startsWith(prefix));\n if (index >= 0) {\n lines[index] = nextLine;\n } else {\n lines.splice(Math.max(0, lines.length - 1), 0, nextLine);\n }\n return ensureTrailingNewline(lines.join(\"\\n\").replace(/\\n{3,}/g, \"\\n\\n\"));\n}\n","import { format, parseISO } from \"date-fns\";\n\nexport function detailFilename(dateInput?: string): string {\n const date = dateInput ? parseISO(dateInput) : new Date();\n if (Number.isNaN(date.getTime())) {\n throw new Error(\"Invalid date. Expected YYYY-MM-DD.\");\n }\n return `L3_${format(date, \"yyyy_MM\")}.md`;\n}\n\nexport function detailHeader(dateInput?: string): string {\n const date = dateInput ? parseISO(dateInput) : new Date();\n if (Number.isNaN(date.getTime())) {\n throw new Error(\"Invalid date. Expected YYYY-MM-DD.\");\n }\n return `# ${format(date, \"MMMM yyyy\")} Detailed Memory\\n\\n`;\n}\n\nexport function detailLine(text: string, dateInput?: string): string {\n const date = dateInput ? parseISO(dateInput) : new Date();\n if (Number.isNaN(date.getTime())) {\n throw new Error(\"Invalid date. Expected YYYY-MM-DD.\");\n }\n const clean = text.replace(/\\s+/g, \" \").trim();\n if (!clean) {\n throw new Error(\"Detail text must be non-empty.\");\n }\n return `* [${format(date, \"yyyy-MM-dd\")}]: ${clean}`;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport async function withLock<T>(root: string, fn: () => Promise<T>): Promise<T> {\n const lockPath = path.join(root, \".lock\");\n const start = Date.now();\n let handle: fs.FileHandle | undefined;\n while (!handle) {\n try {\n await fs.mkdir(root, { recursive: true });\n handle = await fs.open(lockPath, \"wx\");\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\" || Date.now() - start > 5000) {\n throw error;\n }\n await sleep(50);\n }\n }\n try {\n return await fn();\n } finally {\n await handle.close();\n await fs.rm(lockPath, { force: true });\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { CONTEXTLINE_INSTRUCTIONS_FILE, DEFAULT_CONTEXTLINE_INSTRUCTIONS } from \"../core/instructions.js\";\nimport { L1_FILE, L2_DIR, L3_DIR, safeJoin } from \"../paths.js\";\nimport type { InitResult } from \"../core/types.js\";\n\nconst DEFAULT_L1 = `# ContextLine L1 Index\n\nThis is the quick memory cache: compact facts, summaries, and active pointers.\n\n## Quick Facts\n\n`;\n\nexport async function initContextLine(root: string): Promise<InitResult> {\n const created: string[] = [];\n await fs.mkdir(root, { recursive: true });\n\n for (const dir of [\n L2_DIR,\n L3_DIR,\n \"maintenance\",\n \"maintenance/proposals\"\n ]) {\n const target = safeJoin(root, ...dir.split(\"/\"));\n await fs.mkdir(target, { recursive: true });\n created.push(path.relative(root, target));\n }\n\n const files: Array<[string, string]> = [\n [L1_FILE, DEFAULT_L1],\n [CONTEXTLINE_INSTRUCTIONS_FILE, DEFAULT_CONTEXTLINE_INSTRUCTIONS],\n [\"config.json\", `${JSON.stringify({ version: 1, capture: { promotion: \"selective\" } }, null, 2)}\\n`]\n ];\n\n for (const [file, content] of files) {\n const target = safeJoin(root, file);\n try {\n await fs.writeFile(target, content, { encoding: \"utf8\", flag: \"wx\" });\n created.push(path.relative(root, target));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw error;\n }\n }\n }\n\n return { ok: true, root, created };\n}\n","export const CONTEXTLINE_INSTRUCTIONS_FILE = \"contextline_instructions.md\";\n\nexport const DEFAULT_CONTEXTLINE_INSTRUCTIONS = `# ContextLine Instructions\n\nCONTEXTLINE-INSTRUCTIONS-V1\nThis file contains generic operating instructions for ContextLine memory.\nUse the single MCP tool named \"contextline\" for all ContextLine reads and writes.\nDo not use shell commands to read or write .contextline files.\nDo not mention memory saving unless the user asks.\n\n## Operating Policy\n\nUse the single ContextLine MCP tool \"contextline\" silently for durable memory. Do not use shell commands to read or write .contextline files. Do not mention memory saving unless asked.\n\nDurable memory means stable facts likely to help future sessions: user/project facts, preferences, goals, constraints, decisions, relationships, recurring patterns, and important events. Do not save filler, acknowledgements, or one-off chatter.\n\nDo not save every message. Periodically batch-save useful durable memory since the last ContextLine save in this session.\n\nUse ContextLine as a human-like cache:\n\n- L1_Quick.md stores compact facts, summaries, and active pointers.\n- L2_Deep/ stores deeper facts per important object, person, event, project, preference area, or decision thread.\n- L3_Details/ stores timestamped specifics, receipts, and supporting history.\n\nFor V1, use flat L2 filenames with semantic prefixes instead of folders. Prefer names like person_user.md, project_contextline.md, area_work.md, place_san_jose.md, preference_media.md, preference_language.md, decision_memory_architecture.md, event_launch.md, or org_meta.md.\n\nWhen saving durable memory, complete the full chain. Do not stop after writing L3_Details:\n\n1. Append one L3_Details receipt when supporting specifics are useful.\n2. Cross-save extracted meaning into every relevant L2_Deep file.\n3. Update L1_Quick only for compact high-level facts or active pointers.\n4. Link every L1 line to relevant L2 files.\n5. Link every L2 line to relevant L3 details when supporting detail exists.\n\nTypical tool sequence:\n\nCall \"contextline\" once with action \"save_memory\", including:\n\n- detail.text for the L3_Details receipt\n- deep[] entries for every relevant L2_Deep file, such as person_user.md, area_work.md, place_san_jose.md, or preference_media.md\n- quick[] entries for compact L1_Quick facts linked to L2 files\n\nIf you write L3_Details but do not update L2_Deep and L1_Quick, the memory is incomplete. Prefer the bundled \"save_memory\" action so the full chain is saved together.\n`;\n","import fs from \"node:fs/promises\";\nimport { CONTEXTLINE_INSTRUCTIONS_FILE } from \"../core/instructions.js\";\nimport { resolveRoot } from \"../config.js\";\nimport { l1Path, safeJoin } from \"../paths.js\";\nimport { initContextLine } from \"./init.js\";\n\nexport async function hydrate(rootInput?: string) {\n const root = resolveRoot(rootInput);\n await initContextLine(root);\n const l1_path = l1Path(root);\n const instructions_path = safeJoin(root, CONTEXTLINE_INSTRUCTIONS_FILE);\n const content = await fs.readFile(l1_path, \"utf8\");\n const instructions = await fs.readFile(instructions_path, \"utf8\");\n return {\n ok: true,\n root,\n l1_path,\n instructions_path,\n content,\n instructions,\n instruction: \"Use this L1 content as quick memory. Follow the included ContextLine instructions for reading and saving memory.\",\n display: `ContextLine hydrated\\n- Quick memory: ${l1_path}\\n- Instructions: ${instructions_path}`\n };\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { resolveRoot } from \"../config.js\";\nimport { L1_FILE, L2_DIR, L3_DIR, safeJoin } from \"../paths.js\";\n\nasync function countFiles(dir: string): Promise<number> {\n try {\n return (await fs.readdir(dir)).length;\n } catch {\n return 0;\n }\n}\n\nexport async function inspect(rootInput?: string) {\n const root = resolveRoot(rootInput);\n return {\n root,\n l1: path.join(root, L1_FILE),\n l2_deep_files: await countFiles(safeJoin(root, L2_DIR)),\n l3_detail_files: await countFiles(safeJoin(root, L3_DIR))\n };\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { L1_FILE, L2_DIR, L3_DIR, l1Path, safeJoin } from \"../paths.js\";\nimport { extractWikiLinks } from \"./links.js\";\nimport type { ValidationResult } from \"./types.js\";\n\nasync function exists(target: string): Promise<boolean> {\n try {\n await fs.access(target);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function listMarkdown(dir: string): Promise<string[]> {\n const out: string[] = [];\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n const nested = await listMarkdown(full);\n out.push(...nested.map((file) => path.join(entry.name, file)));\n } else if (entry.name.endsWith(\".md\")) {\n out.push(entry.name);\n }\n }\n return out;\n } catch {\n return [];\n }\n}\n\nexport async function validateTopology(root: string): Promise<ValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n const l1 = l1Path(root);\n const l2 = safeJoin(root, L2_DIR);\n const l3 = safeJoin(root, L3_DIR);\n\n if (!(await exists(l1))) errors.push(\"L1_Quick.md is missing.\");\n if (!(await exists(l2))) errors.push(\"L2_Deep directory is missing.\");\n if (!(await exists(l3))) errors.push(\"L3_Details directory is missing.\");\n\n if (errors.length) return { ok: false, errors, warnings };\n\n const l1Content = await fs.readFile(l1, \"utf8\");\n for (const link of extractWikiLinks(l1Content)) {\n if (link.startsWith(\"L3_\")) {\n errors.push(`L1 must not link directly to L3 detail file: ${link}`);\n } else if (!(await exists(path.join(l2, link)))) {\n errors.push(`L1 links to missing L2 deep memory file: ${link}`);\n }\n }\n\n for (const file of await listMarkdown(l2)) {\n const content = await fs.readFile(path.join(l2, file), \"utf8\");\n for (const link of extractWikiLinks(content)) {\n if (link === L1_FILE) {\n errors.push(`L2 deep memory file ${file} must not link upward to L1_Quick.md.`);\n } else if (!link.startsWith(\"L3_\")) {\n warnings.push(`L2 deep memory file ${file} links to non-L3 file: ${link}`);\n } else if (!(await exists(path.join(l3, link)))) {\n errors.push(`L2 deep memory file ${file} links to missing L3 detail file: ${link}`);\n }\n }\n }\n\n for (const file of await listMarkdown(l3)) {\n const content = await fs.readFile(path.join(l3, file), \"utf8\");\n const links = extractWikiLinks(content);\n if (links.length) {\n errors.push(`L3 detail file ${file} must not contain wiki links.`);\n }\n }\n\n return { ok: errors.length === 0, errors, warnings };\n}\n","export const WIKI_LINK_RE = /\\[\\[([^\\]]+)\\]\\]/g;\n\nexport function extractWikiLinks(content: string): string[] {\n return [...content.matchAll(WIKI_LINK_RE)].map((match) => match[1]);\n}\n\nexport function hasWikiLink(line: string, filename: string): boolean {\n return extractWikiLinks(line).includes(filename);\n}\n\nexport function ensureWikiLink(line: string, filename: string): string {\n return hasWikiLink(line, filename) ? line : `${line.trim()} [[${filename}]]`;\n}\n","import { resolveRoot } from \"../config.js\";\nimport { validateTopology as validate } from \"../core/topology.js\";\nimport { initContextLine } from \"./init.js\";\n\nexport async function validateTopology(rootInput?: string) {\n const root = resolveRoot(rootInput);\n await initContextLine(root);\n const result = await validate(root);\n return {\n ...result,\n display: result.ok ? \"ContextLine topology valid\" : `ContextLine topology invalid\\n- Errors: ${result.errors.length}\\n- Warnings: ${result.warnings.length}`\n };\n}\n","import { resolveRoot } from \"./config.js\";\nimport path from \"node:path\";\nimport type { HookEvent } from \"./core/types.js\";\nimport { CONTEXTLINE_INSTRUCTIONS_FILE } from \"./core/instructions.js\";\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));\n return Buffer.concat(chunks).toString(\"utf8\");\n}\n\nexport async function runHook(rootInput?: string): Promise<void> {\n const raw = await readStdin();\n const event = raw.trim() ? (JSON.parse(raw) as HookEvent) : {};\n const root = rootInput ? path.resolve(rootInput) : resolveRoot();\n\n if (event.hook_event_name === \"SessionStart\") {\n process.stdout.write(\n JSON.stringify({\n continue: true,\n hookSpecificOutput: {\n hookEventName: \"SessionStart\",\n additionalContext: `ContextLine memory is available. Call the \"contextline\" MCP tool with action \"hydrate\" and root ${JSON.stringify(root)} to load your memory index. This reads only files inside the .contextline folder at that path — it does not scan or ingest the parent directory. Use that same root for all ContextLine calls this session, then follow the returned instructions.`\n }\n })\n );\n return;\n }\n}\n","import { cwd } from \"node:process\";\nimport { getMemoryRoot } from \"./config.js\";\nimport { detectCodex, setupCodex } from \"./host/codex.js\";\nimport { initContextLine } from \"./tools/init.js\";\nimport { validateTopology } from \"./tools/validateTopology.js\";\n\nexport interface SetupOptions {\n host?: string;\n}\n\nexport async function runSetup(options: SetupOptions = {}) {\n const projectRoot = cwd();\n const root = getMemoryRoot();\n const requestedHost = options.host ?? \"auto\";\n\n let host = requestedHost;\n let detectionDetails: string[] = [];\n if (requestedHost === \"auto\") {\n const detected = await detectCodex(projectRoot);\n host = detected.host === \"codex\" ? \"codex\" : \"none\";\n detectionDetails = detected.details;\n }\n\n await initContextLine(root);\n let hostSetup: unknown = null;\n if (host === \"codex\") {\n hostSetup = await setupCodex(projectRoot, root);\n }\n\n const validation = await validateTopology(root);\n return {\n ok: validation.ok,\n root,\n host,\n detectionDetails,\n hostSetup,\n validation,\n mcpConfig: {\n mcpServers: {\n contextline: {\n command: \"contextline-mcp\",\n args: [],\n env: {\n CONTEXTLINE_ROOT: root\n }\n }\n }\n }\n };\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { initContextLine } from \"../tools/init.js\";\nimport type { HostDetection } from \"../core/types.js\";\n\nfunction contextlineMcpBlock(contextlineRoot: string): string {\n return `[mcp_servers.contextline]\ntype = \"stdio\"\ncommand = \"contextline-mcp\"\nargs = []\nenv = { CONTEXTLINE_ROOT = ${JSON.stringify(contextlineRoot)} }\nstartup_timeout_sec = 10\ntool_timeout_sec = 10\n`;\n}\n\nasync function upsertContextlineMcpConfig(configTomlPath: string, contextlineRoot: string) {\n let existingConfig = \"\";\n try {\n existingConfig = await fs.readFile(configTomlPath, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n const withoutContextLine = existingConfig.replace(/\\n?\\[mcp_servers\\.contextline\\][\\s\\S]*?(?=\\n\\[|$)/, \"\").trim();\n const mcpBlock = contextlineMcpBlock(contextlineRoot);\n await fs.writeFile(configTomlPath, `${withoutContextLine ? `${withoutContextLine}\\n\\n` : \"\"}${mcpBlock}\\n`, \"utf8\");\n}\n\nasync function writeHookScript(scriptPath: string, contextlineRoot: string) {\n const script = `#!/usr/bin/env node\nimport { spawnSync } from \"node:child_process\";\nimport fs from \"node:fs\";\n\nconst input = fs.readFileSync(0, \"utf8\");\nconst result = spawnSync(\"contextline\", [\"hook\", \"--root\", ${JSON.stringify(contextlineRoot)}], {\n input,\n encoding: \"utf8\",\n shell: process.platform === \"win32\"\n});\n\nif (result.stderr) process.stderr.write(result.stderr);\nif (result.stdout) process.stdout.write(result.stdout);\nprocess.exit(result.status ?? 0);\n`;\n await fs.writeFile(scriptPath, script, \"utf8\");\n}\n\nasync function upsertHooksJson(hooksJsonPath: string, hookScriptPath: string) {\n let existing: Record<string, unknown> = {};\n try {\n const raw = await fs.readFile(hooksJsonPath, \"utf8\");\n existing = JSON.parse(raw);\n } catch {\n // missing or unparseable — start fresh\n }\n\n if (!existing.hooks || typeof existing.hooks !== \"object\") {\n existing.hooks = {};\n }\n const hooks = existing.hooks as Record<string, unknown[]>;\n const sessionStart: unknown[] = Array.isArray(hooks.SessionStart) ? hooks.SessionStart : [];\n\n // Remove any existing contextline entry\n const filtered = sessionStart.filter((entry) => {\n if (!entry || typeof entry !== \"object\") return true;\n const e = entry as { hooks?: { command?: string }[] };\n return !e.hooks?.some((h) => h.command?.includes(\"contextline\"));\n });\n\n const contextlineEntry = {\n matcher: \"startup|resume|clear|compact\",\n hooks: [{ type: \"command\", command: `node ${JSON.stringify(hookScriptPath)}` }]\n };\n\n hooks.SessionStart = [contextlineEntry, ...filtered];\n await fs.writeFile(hooksJsonPath, `${JSON.stringify(existing, null, 2)}\\n`, \"utf8\");\n}\n\nexport async function detectCodex(projectRoot: string): Promise<HostDetection> {\n const details: string[] = [];\n const candidates = [\n path.join(os.homedir(), \".codex\"),\n path.join(projectRoot, \".codex\"),\n process.env.CODEX_HOME\n ].filter(Boolean) as string[];\n for (const candidate of candidates) {\n try {\n await fs.access(candidate);\n details.push(`found ${candidate}`);\n } catch {\n // ignore\n }\n }\n if (details.length) return { host: \"codex\", confidence: \"high\", details };\n return { host: \"none\", confidence: \"none\", details: [] };\n}\n\nexport async function setupCodex(projectRoot: string, contextlineRoot: string) {\n await initContextLine(contextlineRoot);\n\n // Project-level setup\n const codexDir = path.join(projectRoot, \".codex\");\n const hooksDir = path.join(codexDir, \"hooks\");\n await fs.mkdir(hooksDir, { recursive: true });\n\n const hookScriptPath = path.join(hooksDir, \"contextline-hook.mjs\");\n await writeHookScript(hookScriptPath, contextlineRoot);\n\n const hooksJsonPath = path.join(codexDir, \"hooks.json\");\n await upsertHooksJson(hooksJsonPath, hookScriptPath);\n\n const configTomlPath = path.join(codexDir, \"config.toml\");\n await upsertContextlineMcpConfig(configTomlPath, contextlineRoot);\n\n // Global setup — configures Codex UI for all sessions\n const globalCodexDir = process.env.CODEX_HOME ?? path.join(os.homedir(), \".codex\");\n const globalHooksDir = path.join(globalCodexDir, \"hooks\");\n await fs.mkdir(globalHooksDir, { recursive: true });\n\n const globalHookScriptPath = path.join(globalHooksDir, \"contextline-hook.mjs\");\n await writeHookScript(globalHookScriptPath, contextlineRoot);\n\n const globalHooksJsonPath = path.join(globalCodexDir, \"hooks.json\");\n await upsertHooksJson(globalHooksJsonPath, globalHookScriptPath);\n\n const globalConfigTomlPath = path.join(globalCodexDir, \"config.toml\");\n if (path.resolve(globalConfigTomlPath) !== path.resolve(configTomlPath)) {\n await upsertContextlineMcpConfig(globalConfigTomlPath, contextlineRoot);\n }\n\n const agentsPath = path.join(projectRoot, \"AGENTS.md\");\n try {\n const existingAgents = await fs.readFile(agentsPath, \"utf8\");\n if (existingAgents.includes(\"# ContextLine Memory\")) {\n const cleanedAgents = existingAgents.slice(0, existingAgents.indexOf(\"# ContextLine Memory\")).trim();\n if (cleanedAgents) {\n await fs.writeFile(agentsPath, `${cleanedAgents}\\n`, \"utf8\");\n } else {\n await fs.rm(agentsPath, { force: true });\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n\n return { hooksJsonPath, hookScriptPath, configTomlPath, globalHooksJsonPath, globalHookScriptPath, globalConfigTomlPath };\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { resolveRoot } from \"../config.js\";\nimport { deepPath, detailPath } from \"../paths.js\";\nimport { atomicWriteFile } from \"../core/atomicWrite.js\";\nimport { ensureWikiLink } from \"../core/links.js\";\nimport { ensureTrailingNewline, upsertLineByPrefix } from \"../core/markdown.js\";\nimport { withLock } from \"../core/lock.js\";\nimport { initContextLine } from \"./init.js\";\n\nexport async function updateDeepPointer(file: string, factText: string, detailFile: string, rootInput?: string) {\n const root = resolveRoot(rootInput);\n await initContextLine(root);\n detailPath(root, detailFile);\n return withLock(root, async () => {\n const filePath = deepPath(root, file);\n let content = \"\";\n try {\n content = await fs.readFile(filePath, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw error;\n }\n content = `# ${path.basename(file, \".md\")}\\n\\n`;\n }\n const updated_line = ensureWikiLink(factText, detailFile);\n const next = upsertLineByPrefix(ensureTrailingNewline(content), factText.trim(), updated_line);\n await atomicWriteFile(filePath, next);\n return { ok: true as const, file, updated_line, display: `ContextLine L2 updated\\n- File: ${file}` };\n });\n}\n","import fs from \"node:fs/promises\";\nimport { resolveRoot } from \"../config.js\";\nimport { deepPath, l1Path } from \"../paths.js\";\nimport { atomicWriteFile } from \"../core/atomicWrite.js\";\nimport { ensureWikiLink } from \"../core/links.js\";\nimport { ensureTrailingNewline, upsertLineByPrefix } from \"../core/markdown.js\";\nimport { withLock } from \"../core/lock.js\";\nimport { initContextLine } from \"./init.js\";\n\nexport async function updateIndexPointer(indexLine: string, files: string[], rootInput?: string) {\n if (files.some((file) => file.startsWith(\"L3_\"))) {\n throw new Error(\"L1 quick memory pointers may only reference L2 deep memory files.\");\n }\n const root = resolveRoot(rootInput);\n await initContextLine(root);\n for (const file of files) {\n deepPath(root, file);\n }\n return withLock(root, async () => {\n const filePath = l1Path(root);\n const content = await fs.readFile(filePath, \"utf8\");\n const updated_line = files.reduce((line, file) => ensureWikiLink(line, file), indexLine);\n const next = upsertLineByPrefix(ensureTrailingNewline(content), indexLine.trim(), updated_line);\n await atomicWriteFile(filePath, next);\n return { ok: true as const, updated_line, display: \"ContextLine L1 updated\" };\n });\n}\n"],"mappings":"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","names":["fs","path","path","fs","path","fs","path","fs","path","fs","path","fs","fs","fs","fs","path","fs","path","fs","path","fs","path","validateTopology","path","fs","path","fs","path","validateTopology","fs","path","fs","path","fs","fs","validateTopology"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/core/instructions.ts","../src/hook.ts","../src/setup.ts","../src/host/claude.ts","../src/tools/createMemorySkeleton.ts","../src/paths.ts","../src/core/errors.ts","../src/host/codex.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { runHook } from \"./hook.js\";\r\nimport { runSetup } from \"./setup.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram.name(\"contextline\").description(\"Persistent memory for AI agents.\").version(\"0.2.0\");\r\n\r\nprogram\r\n .command(\"setup\")\r\n .option(\"--host <host>\", \"Host adapter: auto, codex, claude, claude-code, none\", \"auto\")\r\n .option(\"--verbose\", \"Print full JSON result\")\r\n .description(\"Create memory folder and configure host integrations.\")\r\n .action(async (options: { host?: string; verbose?: boolean }) => {\r\n const result = await runSetup(options);\r\n if (options.verbose) {\r\n console.log(JSON.stringify(result, null, 2));\r\n } else {\r\n process.stdout.write(`ContextLine ready at ${result.root}\\n`);\r\n }\r\n });\r\n\r\nprogram\r\n .command(\"hook\")\r\n .option(\"--root <root>\", \"Memory folder path\")\r\n .description(\"Run ContextLine host hook. Reads hook JSON from stdin.\")\r\n .action(async (options: { root?: string }) => runHook(options.root));\r\n\r\nprogram.parseAsync(process.argv).catch((error) => {\r\n console.error(error instanceof Error ? error.message : error);\r\n process.exit(1);\r\n});\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\nexport function getMemoryRoot(): string {\r\n if (process.env.CONTEXTLINE_ROOT) return path.resolve(process.env.CONTEXTLINE_ROOT);\r\n // v1: memory lives in the current working directory.\r\n // The dream is to support a user-configured global root\r\n // so memory is shared across projects and synced across machines.\r\n // Blocked by host sandbox policies (e.g. Codex auto-review) that deny MCP access\r\n // to paths outside the project folder. Contributions welcome:\r\n // https://github.com/contextline/core/issues\r\n return path.join(process.cwd(), \".contextline\");\r\n}\r\n\r\nexport function resolveRoot(folder?: string): string {\r\n if (folder) return path.resolve(folder);\r\n return getMemoryRoot();\r\n}\r\n\r\nexport function hasMemoryRoot(folder?: string): boolean {\r\n const root = resolveRoot(folder);\r\n try {\r\n return fs.statSync(root).isDirectory();\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\r\n throw error;\r\n }\r\n}\r\n\r\nexport function requireMemoryRoot(folder?: string): string {\r\n const root = resolveRoot(folder);\r\n if (!hasMemoryRoot(root)) {\r\n throw new Error(`ContextLine memory is not set up at ${root}. Run \"contextline setup\" in this folder to create .contextline.`);\r\n }\r\n return root;\r\n}\r\n\r\nexport function ensureGlobalConfigDir(): void {\r\n const dir = getMemoryRoot();\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n}\r\n","export const CONTEXTLINE_INSTRUCTIONS_FILE = \"contextline_instructions.md\";\r\n\r\nexport function memorySaveRules(root?: string): string {\r\n const l2 = root ? `\"${root}/L2_Deep/[topic].md\"` : `L2_Deep/[topic].md`;\r\n const l1 = root ? `\"${root}/L1_Quick.md\"` : `L1_Quick.md`;\r\n\r\n return `SAVE THESE — do not skip any:\r\n- Any decision made, even informal (\"skip this\", \"do it this way\", \"won't fix\")\r\n- Any finding or discovery made by either user or assistant\r\n- Any correction to prior understanding\r\n- Any document reviewed and key feedback given\r\n- Any item resolved, approved, or declined\r\n- Any new direction chosen — save alternatives considered and rationale too\r\n\r\nDO NOT SAVE: transient tool outputs, session navigation, file paths read, intermediate mechanics.\r\n\r\nHOW TO SAVE (use file read/write tools — NOT Bash or shell commands):\r\n1. Save to ${l2} — the DETAIL layer. Write 2–4 sentences per fact:\r\n what happened + why + what was considered + what it means going forward.\r\n Format: \"(YYYY-MM-DD) [full context with rationale and impact]\"\r\n - Read the file, then Write it back with the new entry at the TOP, above existing entries (after any leading # header line)\r\n - Create the file if the topic is new\r\n - Semantic filenames: project_x.md, preference_user.md, person_alice.md, area_work.md\r\n - Save to ALL relevant topic files when a fact involves multiple topics\r\n - If your L2 entry looks like your L1 summary, it is too thin — add the why and context.\r\n2. Save a SHORT pointer to ${l1} — the INDEX layer. ≤15 words, no detail.\r\n \"(YYYY-MM-DD) [subject + what happened]. → [topic].md\"\r\n - Read the file, then Write it back with the new line at the TOP of the entries (after any leading header lines)\r\n - L1 is a scannable index only. If you are writing more than 15 words, move the detail to L2.\r\n\r\nADD ONLY — never modify or delete existing lines. New entries go at the top. Date every L2 entry: (YYYY-MM-DD).\r\n\r\n\"No new facts to save\" should be RARE. If in doubt, save. Missed saves break continuity; redundant saves are harmless.\r\n\r\nBefore responding, confirm on one line — either \"Saved: [what] → [topic].md\" or \"Nothing saved: [reason]\". This line is mandatory. Skipping it means the user cannot verify whether memory was updated.`;\r\n}\r\n\r\nexport const DEFAULT_CONTEXTLINE_INSTRUCTIONS = `# ContextLine Memory\r\n\r\nCONTEXTLINE-INSTRUCTIONS-V2\r\n\r\nMemory lives in two layers inside this folder:\r\n\r\n- **L1_Quick.md** — compact one-line facts and topic pointers. One line per topic, kept brief.\r\n- **L2_Deep/[topic].md** — full dated detail per topic. Always append; never edit existing lines.\r\n\r\n## Rules\r\n\r\nADD ONLY. Every write adds new lines at the top of the file's entry list (after any # header lines). Never modify or delete existing content.\r\n\r\nDate every L2 line: (YYYY-MM-DD). Newer entries win on contradictions — do not remove older ones. New entries go at the top so the LLM encounters the most recent context first.\r\n\r\nUse semantic filenames for L2 files: project_debatrix.md, preference_user.md, person_alice.md, area_work.md, decision_auth.md, event_launch.md.\r\n\r\nCross-save: if a fact involves multiple topics (person + project, place + event), append the relevant angle to each file.\r\n\r\n## Saving\r\n\r\n${memorySaveRules()}\r\n`;\r\n","import { resolveRoot, hasMemoryRoot } from \"./config.js\";\r\nimport { memorySaveRules } from \"./core/instructions.js\";\r\nimport path from \"node:path\";\r\nimport type { HookEvent } from \"./core/types.js\";\r\n\r\nasync function readStdin(): Promise<string> {\r\n const chunks: Buffer[] = [];\r\n for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));\r\n return Buffer.concat(chunks).toString(\"utf8\");\r\n}\r\n\r\nfunction sessionStartInstruction(root: string): string {\r\n return `ContextLine memory is available. Read the file at \"${root}/L1_Quick.md\" to load your memory index. It contains compact facts and topic pointers. If you need detail on a specific topic, read the relevant file from \"${root}/L2_Deep/\". Only read L2 files when needed — do not load the entire directory.`;\r\n}\r\n\r\nfunction checkpointInstruction(root: string): string {\r\n return `ContextLine memory checkpoint. Before responding, append any new durable facts to disk.\\n\\n${memorySaveRules(root)}`;\r\n}\r\n\r\nfunction continueWithoutContext(): void {\r\n process.stdout.write(JSON.stringify({ continue: true }));\r\n}\r\n\r\nexport async function runHook(rootInput?: string): Promise<void> {\r\n const raw = await readStdin();\r\n const event = raw.trim() ? (JSON.parse(raw) as HookEvent) : {};\r\n const root = rootInput ? path.resolve(rootInput) : resolveRoot();\r\n const hasRoot = hasMemoryRoot(root);\r\n\r\n if (event.hook_event_name === \"SessionStart\") {\r\n if (!hasRoot) {\r\n continueWithoutContext();\r\n return;\r\n }\r\n process.stdout.write(\r\n JSON.stringify({\r\n continue: true,\r\n hookSpecificOutput: {\r\n hookEventName: \"SessionStart\",\r\n additionalContext: sessionStartInstruction(root)\r\n }\r\n })\r\n );\r\n return;\r\n }\r\n\r\n if (event.hook_event_name === \"UserPromptSubmit\") {\r\n if (!hasRoot) {\r\n continueWithoutContext();\r\n return;\r\n }\r\n process.stdout.write(\r\n JSON.stringify({\r\n continue: true,\r\n hookSpecificOutput: {\r\n hookEventName: \"UserPromptSubmit\",\r\n additionalContext: checkpointInstruction(root)\r\n }\r\n })\r\n );\r\n return;\r\n }\r\n}\r\n","import { cwd } from \"node:process\";\r\nimport { getMemoryRoot } from \"./config.js\";\r\nimport { detectClaude, setupClaude } from \"./host/claude.js\";\r\nimport { detectCodex, setupCodex } from \"./host/codex.js\";\r\n\r\nexport interface SetupOptions {\r\n host?: string;\r\n}\r\n\r\nexport async function runSetup(options: SetupOptions = {}) {\r\n const projectRoot = cwd();\r\n const root = getMemoryRoot();\r\n const requestedHostInput = (options.host ?? \"auto\").toLowerCase();\r\n const requestedHost = requestedHostInput === \"claude-code\" || requestedHostInput === \"claudecode\" ? \"claude\" : requestedHostInput;\r\n\r\n let hosts: Array<\"codex\" | \"claude\"> = [];\r\n let detectionDetails: string[] = [];\r\n if (requestedHost === \"auto\") {\r\n const [codex, claude] = await Promise.all([detectCodex(projectRoot), detectClaude(projectRoot)]);\r\n if (codex.host === \"codex\") hosts.push(\"codex\");\r\n if (claude.host === \"claude\") hosts.push(\"claude\");\r\n detectionDetails = [\r\n ...codex.details.map((detail) => `codex: ${detail}`),\r\n ...claude.details.map((detail) => `claude: ${detail}`)\r\n ];\r\n } else if (requestedHost === \"codex\" || requestedHost === \"claude\") {\r\n hosts = [requestedHost];\r\n } else if (requestedHost !== \"none\") {\r\n throw new Error(`Unsupported host adapter: ${options.host}`);\r\n }\r\n\r\n const hostSetup: Record<string, unknown> = {};\r\n for (const host of hosts) {\r\n if (host === \"codex\") {\r\n hostSetup.codex = await setupCodex(projectRoot, root);\r\n } else if (host === \"claude\") {\r\n hostSetup.claude = await setupClaude(projectRoot, root);\r\n }\r\n }\r\n\r\n return {\r\n ok: true,\r\n root,\r\n host: hosts.length ? hosts.join(\",\") : \"none\",\r\n hosts,\r\n detectionDetails,\r\n hostSetup\r\n };\r\n}\r\n","import fs from \"node:fs/promises\";\r\nimport os from \"node:os\";\r\nimport path from \"node:path\";\r\nimport { createMemorySkeleton } from \"../tools/createMemorySkeleton.js\";\r\nimport type { HostDetection } from \"../core/types.js\";\r\n\r\nasync function readJsonObject(filePath: string): Promise<Record<string, unknown>> {\r\n try {\r\n const raw = await fs.readFile(filePath, \"utf8\");\r\n const parsed = JSON.parse(raw);\r\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : {};\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\r\n throw error;\r\n }\r\n}\r\n\r\nasync function writeHookScript(scriptPath: string) {\r\n const script = `#!/usr/bin/env node\r\nimport { spawnSync } from \"node:child_process\";\r\nimport fs from \"node:fs\";\r\n\r\nconst input = fs.readFileSync(0, \"utf8\");\r\nconst result = spawnSync(\"contextline\", [\"hook\"], {\r\n input,\r\n encoding: \"utf8\",\r\n shell: process.platform === \"win32\"\r\n});\r\n\r\nif (result.stderr) process.stderr.write(result.stderr);\r\nif (result.stdout) process.stdout.write(result.stdout);\r\nprocess.exit(result.status ?? 0);\r\n`;\r\n await fs.writeFile(scriptPath, script, \"utf8\");\r\n}\r\n\r\nfunction containsContextlineHook(entry: unknown): boolean {\r\n if (!entry || typeof entry !== \"object\") return false;\r\n const hooks = (entry as { hooks?: unknown }).hooks;\r\n if (!Array.isArray(hooks)) return false;\r\n\r\n return hooks.some((hook) => {\r\n if (!hook || typeof hook !== \"object\") return false;\r\n const h = hook as { command?: unknown; args?: unknown };\r\n const command = typeof h.command === \"string\" ? h.command : \"\";\r\n const args = Array.isArray(h.args) ? h.args.filter((arg): arg is string => typeof arg === \"string\") : [];\r\n return command === \"node\" && args.some((arg) => arg.replaceAll(\"\\\\\", \"/\").endsWith(\"/.claude/hooks/contextline-hook.mjs\"));\r\n });\r\n}\r\n\r\nasync function upsertClaudeSettings(settingsPath: string, projectRoot: string, contextlineRoot: string) {\r\n const existing = await readJsonObject(settingsPath);\r\n if (!existing.hooks || typeof existing.hooks !== \"object\" || Array.isArray(existing.hooks)) {\r\n existing.hooks = {};\r\n }\r\n const hooks = existing.hooks as Record<string, unknown>;\r\n const sessionStart = Array.isArray(hooks.SessionStart) ? hooks.SessionStart : [];\r\n const userPromptSubmit = Array.isArray(hooks.UserPromptSubmit) ? hooks.UserPromptSubmit : [];\r\n const filteredSessionStart = sessionStart.filter((entry) => !containsContextlineHook(entry));\r\n const filteredUserPromptSubmit = userPromptSubmit.filter((entry) => !containsContextlineHook(entry));\r\n\r\n const hook = {\r\n type: \"command\",\r\n command: \"node\",\r\n args: [\"${CLAUDE_PROJECT_DIR}/.claude/hooks/contextline-hook.mjs\"]\r\n };\r\n const sessionStartEntry = {\r\n matcher: \"startup|resume|clear|compact\",\r\n hooks: [hook]\r\n };\r\n const userPromptSubmitEntry = {\r\n hooks: [hook]\r\n };\r\n\r\n hooks.SessionStart = [sessionStartEntry, ...filteredSessionStart];\r\n hooks.UserPromptSubmit = [userPromptSubmitEntry, ...filteredUserPromptSubmit];\r\n\r\n const memPath = path.relative(projectRoot, contextlineRoot).replaceAll(\"\\\\\", \"/\");\r\n const memPermissions = [`Read(${memPath}/**)`, `Write(${memPath}/**)`, `Edit(${memPath}/**)`];\r\n if (!existing.permissions || typeof existing.permissions !== \"object\" || Array.isArray(existing.permissions)) {\r\n existing.permissions = {};\r\n }\r\n const permissions = existing.permissions as Record<string, unknown>;\r\n const existingAllow = Array.isArray(permissions.allow) ? (permissions.allow as unknown[]) : [];\r\n const filteredAllow = existingAllow.filter((r) => typeof r !== \"string\" || !memPermissions.includes(r));\r\n permissions.allow = [...memPermissions, ...filteredAllow];\r\n\r\n await fs.writeFile(settingsPath, `${JSON.stringify(existing, null, 2)}\\n`, \"utf8\");\r\n}\r\n\r\nexport async function detectClaude(projectRoot: string): Promise<HostDetection> {\r\n const details: string[] = [];\r\n const candidates = [\r\n path.join(projectRoot, \".claude\"),\r\n path.join(projectRoot, \".mcp.json\"),\r\n path.join(os.homedir(), \".claude\"),\r\n path.join(os.homedir(), \".claude.json\")\r\n ];\r\n\r\n for (const candidate of candidates) {\r\n try {\r\n await fs.access(candidate);\r\n details.push(`found ${candidate}`);\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n if (details.length) return { host: \"claude\", confidence: \"high\", details };\r\n return { host: \"none\", confidence: \"none\", details: [] };\r\n}\r\n\r\nexport async function setupClaude(projectRoot: string, contextlineRoot: string) {\r\n await createMemorySkeleton(contextlineRoot);\r\n\r\n const claudeDir = path.join(projectRoot, \".claude\");\r\n const hooksDir = path.join(claudeDir, \"hooks\");\r\n await fs.mkdir(hooksDir, { recursive: true });\r\n\r\n const hookScriptPath = path.join(hooksDir, \"contextline-hook.mjs\");\r\n await writeHookScript(hookScriptPath);\r\n\r\n const settingsPath = path.join(claudeDir, \"settings.json\");\r\n await upsertClaudeSettings(settingsPath, projectRoot, contextlineRoot);\r\n\r\n return { settingsPath, hookScriptPath };\r\n}\r\n","import fs from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { CONTEXTLINE_INSTRUCTIONS_FILE, DEFAULT_CONTEXTLINE_INSTRUCTIONS } from \"../core/instructions.js\";\r\nimport { L1_FILE, L2_DIR, safeJoin } from \"../paths.js\";\r\nimport type { MemorySkeletonResult } from \"../core/types.js\";\r\n\r\nconst DEFAULT_L1 = `# ContextLine Memory Index\r\n\r\nCompact facts and topic pointers. One line per topic.\r\n\r\n`;\r\n\r\nexport async function createMemorySkeleton(root: string): Promise<MemorySkeletonResult> {\r\n const created: string[] = [];\r\n await fs.mkdir(root, { recursive: true });\r\n\r\n const l2Dir = safeJoin(root, L2_DIR);\r\n await fs.mkdir(l2Dir, { recursive: true });\r\n created.push(L2_DIR);\r\n\r\n // L1 is user data — only create if missing, never overwrite\r\n const l1Target = safeJoin(root, L1_FILE);\r\n try {\r\n await fs.writeFile(l1Target, DEFAULT_L1, { encoding: \"utf8\", flag: \"wx\" });\r\n created.push(L1_FILE);\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\r\n }\r\n\r\n // Instructions are owned by ContextLine — always overwrite to keep current\r\n const instructionsTarget = safeJoin(root, CONTEXTLINE_INSTRUCTIONS_FILE);\r\n await fs.writeFile(instructionsTarget, DEFAULT_CONTEXTLINE_INSTRUCTIONS, { encoding: \"utf8\" });\r\n created.push(CONTEXTLINE_INSTRUCTIONS_FILE);\r\n\r\n return { ok: true, root, created };\r\n}\r\n","import path from \"node:path\";\r\nimport { PathSafetyError } from \"./core/errors.js\";\r\n\r\nexport const L1_FILE = \"L1_Quick.md\";\r\nexport const L2_DIR = \"L2_Deep\";\r\n\r\nexport function assertSafeRelative(input: string): void {\r\n if (!input || input.trim() !== input) {\r\n throw new PathSafetyError(\"Path input must be non-empty and contain no leading/trailing whitespace.\");\r\n }\r\n if (path.isAbsolute(input)) {\r\n throw new PathSafetyError(\"Absolute paths are not allowed in tool inputs.\");\r\n }\r\n const parts = input.split(/[\\\\/]+/);\r\n if (parts.includes(\"..\") || parts.includes(\"\")) {\r\n throw new PathSafetyError(\"Path traversal and empty path segments are not allowed.\");\r\n }\r\n}\r\n\r\nexport function assertSafeRelativeFile(input: string): void {\r\n assertSafeRelative(input);\r\n if (!input.endsWith(\".md\")) {\r\n throw new PathSafetyError(\"Memory file paths must end with .md.\");\r\n }\r\n}\r\n\r\nexport function safeJoin(root: string, ...segments: string[]): string {\r\n for (const segment of segments) {\r\n assertSafeRelative(segment);\r\n }\r\n const resolvedRoot = path.resolve(root);\r\n const target = path.resolve(resolvedRoot, ...segments);\r\n const rel = path.relative(resolvedRoot, target);\r\n if (rel.startsWith(\"..\") || path.isAbsolute(rel)) {\r\n throw new PathSafetyError(\"Resolved path escapes ContextLine root.\");\r\n }\r\n return target;\r\n}\r\n\r\nexport function deepPath(root: string, file: string): string {\r\n assertSafeRelativeFile(file);\r\n if (file.includes(\"/\") || file.includes(\"\\\\\")) {\r\n throw new PathSafetyError(\"L2 memory files must be flat filenames inside L2_Deep for V1.\");\r\n }\r\n return safeJoin(root, L2_DIR, file);\r\n}\r\n\r\nexport function l1Path(root: string): string {\r\n return safeJoin(root, L1_FILE);\r\n}\r\n","export class ContextLineError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ContextLineError\";\n }\n}\n\nexport class PathSafetyError extends ContextLineError {\n constructor(message: string) {\n super(message);\n this.name = \"PathSafetyError\";\n }\n}\n","import fs from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport os from \"node:os\";\r\nimport { createMemorySkeleton } from \"../tools/createMemorySkeleton.js\";\r\nimport type { HostDetection } from \"../core/types.js\";\r\n\r\nfunction filesystemMcpBlock(contextlineRoot: string): string {\r\n return `[mcp_servers.filesystem]\r\ntype = \"stdio\"\r\ncommand = \"npx\"\r\nargs = [\"-y\", \"@modelcontextprotocol/server-filesystem\", ${JSON.stringify(contextlineRoot)}]\r\nstartup_timeout_sec = 10\r\ntool_timeout_sec = 10\r\n`;\r\n}\r\n\r\nasync function upsertFilesystemMcpConfig(configTomlPath: string, contextlineRoot: string) {\r\n let existingConfig = \"\";\r\n try {\r\n existingConfig = await fs.readFile(configTomlPath, \"utf8\");\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\r\n }\r\n const withoutFilesystem = existingConfig.replace(/\\n?\\[mcp_servers\\.filesystem\\][\\s\\S]*?(?=\\n\\[|$)/, \"\").trim();\r\n const mcpBlock = filesystemMcpBlock(contextlineRoot);\r\n await fs.writeFile(configTomlPath, `${withoutFilesystem ? `${withoutFilesystem}\\n\\n` : \"\"}${mcpBlock}\\n`, \"utf8\");\r\n}\r\n\r\nasync function writeHookScript(scriptPath: string) {\r\n const script = `#!/usr/bin/env node\r\nimport { spawnSync } from \"node:child_process\";\r\nimport fs from \"node:fs\";\r\n\r\nconst input = fs.readFileSync(0, \"utf8\");\r\nconst result = spawnSync(\"contextline\", [\"hook\"], {\r\n input,\r\n encoding: \"utf8\",\r\n shell: process.platform === \"win32\"\r\n});\r\n\r\nif (result.stderr) process.stderr.write(result.stderr);\r\nif (result.stdout) process.stdout.write(result.stdout);\r\nprocess.exit(result.status ?? 0);\r\n`;\r\n await fs.writeFile(scriptPath, script, \"utf8\");\r\n}\r\n\r\nasync function upsertHooksJson(hooksJsonPath: string, hookScriptPath: string) {\r\n let existing: Record<string, unknown> = {};\r\n try {\r\n const raw = await fs.readFile(hooksJsonPath, \"utf8\");\r\n existing = JSON.parse(raw);\r\n } catch {\r\n // missing or unparseable — start fresh\r\n }\r\n\r\n if (!existing.hooks || typeof existing.hooks !== \"object\") {\r\n existing.hooks = {};\r\n }\r\n const hooks = existing.hooks as Record<string, unknown[]>;\r\n const sessionStart: unknown[] = Array.isArray(hooks.SessionStart) ? hooks.SessionStart : [];\r\n const userPromptSubmit: unknown[] = Array.isArray(hooks.UserPromptSubmit) ? hooks.UserPromptSubmit : [];\r\n\r\n const containsContextlineHook = (entry: unknown) => {\r\n if (!entry || typeof entry !== \"object\") return false;\r\n const e = entry as { hooks?: { command?: string }[] };\r\n return e.hooks?.some((h) => h.command?.includes(\"contextline\")) ?? false;\r\n };\r\n const filteredSessionStart = sessionStart.filter((entry) => !containsContextlineHook(entry));\r\n const filteredUserPromptSubmit = userPromptSubmit.filter((entry) => !containsContextlineHook(entry));\r\n\r\n const contextlineEntry = {\r\n matcher: \"startup|resume|clear|compact\",\r\n hooks: [{ type: \"command\", command: `node ${JSON.stringify(hookScriptPath)}` }]\r\n };\r\n const contextlinePromptEntry = {\r\n hooks: [{ type: \"command\", command: `node ${JSON.stringify(hookScriptPath)}` }]\r\n };\r\n\r\n hooks.SessionStart = [contextlineEntry, ...filteredSessionStart];\r\n hooks.UserPromptSubmit = [contextlinePromptEntry, ...filteredUserPromptSubmit];\r\n await fs.writeFile(hooksJsonPath, `${JSON.stringify(existing, null, 2)}\\n`, \"utf8\");\r\n}\r\n\r\nexport async function detectCodex(projectRoot: string): Promise<HostDetection> {\r\n const details: string[] = [];\r\n const candidates = [\r\n path.join(os.homedir(), \".codex\"),\r\n path.join(projectRoot, \".codex\"),\r\n process.env.CODEX_HOME\r\n ].filter(Boolean) as string[];\r\n for (const candidate of candidates) {\r\n try {\r\n await fs.access(candidate);\r\n details.push(`found ${candidate}`);\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n if (details.length) return { host: \"codex\", confidence: \"high\", details };\r\n return { host: \"none\", confidence: \"none\", details: [] };\r\n}\r\n\r\nexport async function setupCodex(projectRoot: string, contextlineRoot: string) {\r\n await createMemorySkeleton(contextlineRoot);\r\n\r\n const codexDir = path.join(projectRoot, \".codex\");\r\n const hooksDir = path.join(codexDir, \"hooks\");\r\n await fs.mkdir(hooksDir, { recursive: true });\r\n\r\n const hookScriptPath = path.join(hooksDir, \"contextline-hook.mjs\");\r\n await writeHookScript(hookScriptPath);\r\n\r\n const hooksJsonPath = path.join(codexDir, \"hooks.json\");\r\n await upsertHooksJson(hooksJsonPath, hookScriptPath);\r\n\r\n const configTomlPath = path.join(codexDir, \"config.toml\");\r\n await upsertFilesystemMcpConfig(configTomlPath, contextlineRoot);\r\n\r\n return { hooksJsonPath, hookScriptPath, configTomlPath };\r\n}\r\n"],"mappings":"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","names":["path","fs","path","fs","path","path","path","fs","fs","path","fs","path","os","fs","writeHookScript","containsContextlineHook","path","os"]}
@@ -6,6 +6,6 @@ console.log(`ContextLine installed.
6
6
  Next step:
7
7
  contextline setup
8
8
 
9
- This will choose a memory folder, initialize .contextline, detect supported hosts, and configure MCP/hooks where possible.
9
+ This creates .contextline/, detects supported hosts, and installs memory hooks.
10
10
  `);
11
11
  //# sourceMappingURL=postinstall.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/postinstall.ts"],"sourcesContent":["console.log(`ContextLine installed.\n\nNext step:\n contextline setup\n\nThis will choose a memory folder, initialize .contextline, detect supported hosts, and configure MCP/hooks where possible.\n`);\n"],"mappings":";;;AAAA,QAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAMX;","names":[]}
1
+ {"version":3,"sources":["../src/postinstall.ts"],"sourcesContent":["console.log(`ContextLine installed.\r\n\r\nNext step:\r\n contextline setup\r\n\r\nThis creates .contextline/, detects supported hosts, and installs memory hooks.\r\n`);\r\n"],"mappings":";;;AAAA,QAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAMX;","names":[]}
@@ -1,120 +1,116 @@
1
- # Why ContextLine Exists
2
-
3
- AI assistants are increasingly useful as long-running collaborators, but they still struggle with durable memory. They may remember too little between sessions, or they may rely on opaque chat history that is hard to inspect, edit, move, or trust.
4
-
5
- ContextLine solves this by giving assistants a local, file-backed memory cache that is structured enough to retrieve and maintain, but simple enough for users to inspect in Markdown.
6
-
7
- ## The Problem
8
-
9
- Useful context often appears naturally in conversation:
10
-
11
- - personal preferences
12
- - work constraints
13
- - project decisions
14
- - recurring workflows
15
- - important people, places, and organizations
16
- - facts that should influence future answers
17
-
18
- Without a durable memory layer, the user has to repeat that context in every new session. With naive memory, the assistant may save too much, creating noisy files, stale facts, privacy risk, and bloated prompts.
19
-
20
- The core problem is not just storage. It is deciding where durable context should live so future sessions can find it without replaying entire conversations.
21
-
22
- ## Common Workarounds
23
-
24
- Chat history is convenient, but it is usually host-specific, opaque, difficult to edit, and hard to move between tools.
25
-
26
- Vector memory can retrieve similar text, but similarity alone does not create a clean memory map. It may surface fragments without showing what is current, important, or connected.
27
-
28
- Project docs are useful, but they are manual. They drift unless someone actively maintains them.
29
-
30
- Long system prompts can carry context forward, but they become stale, expensive, and hard to audit.
31
-
32
- Transcript logging captures everything, but most messages are not durable memory. Storing every turn creates noise instead of recall.
33
-
34
- ## ContextLine's Approach
35
-
36
- ContextLine treats memory like a human-style cache:
37
-
38
- - `L1_Quick.md`: compact facts, summaries, and active pointers.
39
- - `L2_Deep/`: deeper fact files by important person, project, place, preference area, event, organization, or decision thread.
40
- - `L3_Details/`: timestamped specifics, receipts, and supporting history.
41
-
42
- The host model decides what matters. ContextLine stores, links, validates, and retrieves the files.
43
-
44
- This separation is intentional. ContextLine does not try to be the semantic brain. It is the durable memory substrate that lets the assistant preserve useful context in a predictable topology.
45
-
46
- ## Example: Personal Preference
47
-
48
- The user says:
49
-
50
- > I prefer concise technical explanations, and I usually want examples in TypeScript.
51
-
52
- The assistant may save:
53
-
54
- - an L1 summary that the user prefers concise technical explanations and TypeScript examples
55
- - L2 entries in `person_user.md`, `preference_writing.md`, and `preference_programming.md`
56
- - an L3 dated detail recording the source conversation
57
-
58
- Later, the user asks:
59
-
60
- > Show me how to debounce this API call.
61
-
62
- The assistant can answer in the user's preferred style without asking them to repeat it.
63
-
64
- ## Example: Project Decision
65
-
66
- The user says:
67
-
68
- > For Project Atlas, we decided to keep Postgres for v1 and defer vector search.
69
-
70
- The assistant may save:
71
-
72
- - an L1 project pointer for Project Atlas
73
- - an L2 entry in `project_atlas.md`
74
- - an L3 dated decision receipt
75
-
76
- Later, the user asks:
77
-
78
- > What did we decide about search for Atlas?
79
-
80
- The assistant can retrieve the project-specific memory and answer from the preserved decision context.
81
-
82
- ## Example: Reusable Work Context
83
-
84
- The user says:
85
-
86
- > When I ask for release notes, keep them short, customer-facing, and grouped by feature area.
87
-
88
- The assistant may save:
89
-
90
- - an L1 preference pointer
91
- - an L2 entry in `preference_writing.md` or `area_release_process.md`
92
- - an L3 detail with the original instruction
93
-
94
- Later release-note tasks can follow the preference without the user restating it.
95
-
96
- ## What ContextLine Is Not
97
-
98
- ContextLine is not:
99
-
100
- - a vector database
101
- - a semantic extractor
102
- - a transcript logger
103
- - cloud sync
104
- - a private data service
105
- - a replacement for source control
106
- - a replacement for canonical project docs
107
-
108
- It is a local memory topology and MCP tool surface for assistants that already know how to reason over context.
109
-
110
- ## Why Local Markdown
111
-
112
- Local Markdown has practical advantages:
113
-
114
- - users can inspect what was saved
115
- - users can edit or delete memory with normal tools
116
- - files can be backed up or versioned if desired
117
- - memory is not locked into one hosted product
118
- - the format is understandable without a database or UI
119
-
120
- The goal is durable context that remains user-owned, portable, and auditable.
1
+ # Why ContextLine Exists
2
+
3
+ AI assistants are increasingly useful as long-running collaborators, but they still struggle with durable memory. They may remember too little between sessions, or they may rely on opaque chat history that is hard to inspect, edit, move, or trust.
4
+
5
+ ContextLine solves this by giving assistants a local, file-backed memory cache that is structured enough to retrieve and maintain, but simple enough for users to inspect in Markdown.
6
+
7
+ ## The Problem
8
+
9
+ Useful context often appears naturally in conversation:
10
+
11
+ - personal preferences
12
+ - work constraints
13
+ - project decisions
14
+ - recurring workflows
15
+ - important people, places, and organizations
16
+ - facts that should influence future answers
17
+
18
+ Without a durable memory layer, the user has to repeat that context in every new session. With naive memory, the assistant may save too much, creating noisy files, stale facts, privacy risk, and bloated prompts.
19
+
20
+ The core problem is not just storage. It is deciding where durable context should live so future sessions can find it without replaying entire conversations.
21
+
22
+ ## Common Workarounds
23
+
24
+ Chat history is convenient, but it is usually host-specific, opaque, difficult to edit, and hard to move between tools.
25
+
26
+ Vector memory can retrieve similar text, but similarity alone does not create a clean memory map. It may surface fragments without showing what is current, important, or connected.
27
+
28
+ Project docs are useful, but they are manual. They drift unless someone actively maintains them.
29
+
30
+ Long system prompts can carry context forward, but they become stale, expensive, and hard to audit.
31
+
32
+ Transcript logging captures everything, but most messages are not durable memory. Storing every turn creates noise instead of recall.
33
+
34
+ ## ContextLine's Approach
35
+
36
+ ContextLine treats memory like a human-style cache:
37
+
38
+ - `L1_Quick.md`: compact facts, summaries, and active pointers.
39
+ - `L2_Deep/`: dated durable memory files by important person, project, place, preference area, event, organization, or decision thread. L2 entries include enough discussion context to reconstruct what was decided and why.
40
+
41
+ The host model decides what matters. ContextLine provides the file structure and hook instructions that tell the assistant how to retrieve and update those files.
42
+
43
+ This separation is intentional. ContextLine does not try to be the semantic brain. It is the durable memory substrate that lets the assistant preserve useful context in a predictable topology.
44
+
45
+ ## Example: Personal Preference
46
+
47
+ The user says:
48
+
49
+ > I prefer concise technical explanations, and I usually want examples in TypeScript.
50
+
51
+ The assistant may save:
52
+
53
+ - an L1 summary that the user prefers concise technical explanations and TypeScript examples
54
+ - dated L2 entries in `person_customer.md`, `preference_writing.md`, and `preference_programming.md`
55
+
56
+ Later, the user asks:
57
+
58
+ > Show me how to debounce this API call.
59
+
60
+ The assistant can answer in the user's preferred style without asking them to repeat it.
61
+
62
+ ## Example: Project Decision
63
+
64
+ The user says:
65
+
66
+ > For the analytics project, we decided to keep Postgres for v1 and defer vector search.
67
+
68
+ The assistant may save:
69
+
70
+ - an L1 project pointer for the analytics project
71
+ - a dated L2 entry in `project_analytics.md` with the decision, rationale, and downstream effect
72
+
73
+ Later, the user asks:
74
+
75
+ > What did we decide about search for Atlas?
76
+
77
+ The assistant can retrieve the project-specific memory and answer from the preserved decision context.
78
+
79
+ ## Example: Reusable Work Context
80
+
81
+ The user says:
82
+
83
+ > When I ask for release notes, keep them short, customer-facing, and grouped by feature area.
84
+
85
+ The assistant may save:
86
+
87
+ - an L1 preference pointer
88
+ - a dated L2 entry in `preference_writing.md` or `area_release_process.md`
89
+
90
+ Later release-note tasks can follow the preference without the user restating it.
91
+
92
+ ## What ContextLine Is Not
93
+
94
+ ContextLine is not:
95
+
96
+ - a vector database
97
+ - a semantic extractor
98
+ - a transcript logger
99
+ - cloud sync
100
+ - a private data service
101
+ - a replacement for source control
102
+ - a replacement for canonical project docs
103
+
104
+ It is a local memory topology and host-integration layer for assistants that already know how to reason over context.
105
+
106
+ ## Why Local Markdown
107
+
108
+ Local Markdown has practical advantages:
109
+
110
+ - users can inspect what was saved
111
+ - users can edit or delete memory with normal tools
112
+ - files can be backed up or versioned if desired
113
+ - memory is not locked into one hosted product
114
+ - the format is understandable without a database or UI
115
+
116
+ The goal is durable context that remains user-owned, portable, and auditable.
package/package.json CHANGED
@@ -1,59 +1,56 @@
1
- {
2
- "name": "@contextline/contextline",
3
- "version": "0.1.1",
4
- "description": "Local-first pointer-chain memory for MCP-capable AI agents.",
5
- "type": "module",
6
- "license": "Apache-2.0",
7
- "author": "",
8
- "homepage": "https://github.com/contextline/core#readme",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/contextline/core.git"
12
- },
13
- "bugs": {
14
- "url": "https://github.com/contextline/core/issues"
15
- },
16
- "keywords": [
17
- "mcp",
18
- "memory",
19
- "codex",
20
- "ai",
21
- "context"
22
- ],
23
- "bin": {
24
- "contextline": "./dist/cli.js",
25
- "contextline-mcp": "./dist/index.js"
26
- },
27
- "files": [
28
- "dist",
29
- "docs",
30
- "README.md",
31
- "LICENSE"
32
- ],
33
- "scripts": {
34
- "build": "tsup",
35
- "clear": "pwsh ../scripts/clear-codex.ps1",
36
- "build:clear": "tsup && pwsh ../scripts/clear-codex.ps1",
37
- "dev": "tsx src/index.ts",
38
- "test": "vitest run",
39
- "lint": "eslint .",
40
- "postinstall": "node -e \"console.log('ContextLine installed.\\n\\nNext step:\\n contextline setup\\n\\nThis will choose a memory folder, initialize .contextline, detect supported hosts, and configure MCP/hooks where possible.\\n')\""
41
- },
42
- "dependencies": {
43
- "@modelcontextprotocol/sdk": "^1.12.1",
44
- "commander": "^12.1.0",
45
- "date-fns": "^3.6.0",
46
- "zod": "^3.25.0"
47
- },
48
- "devDependencies": {
49
- "@types/node": "^20.14.0",
50
- "eslint": "^9.8.0",
51
- "tsup": "^8.2.4",
52
- "tsx": "^4.16.2",
53
- "typescript": "^5.5.4",
54
- "vitest": "^2.0.5"
55
- },
56
- "engines": {
57
- "node": ">=20"
58
- }
59
- }
1
+ {
2
+ "name": "@contextline/contextline",
3
+ "version": "0.2.0",
4
+ "description": "Persistent memory for AI agents. One command installs reliable memory into your AI environment.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "author": "",
8
+ "homepage": "https://github.com/contextline/core#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/contextline/core.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/contextline/core/issues"
15
+ },
16
+ "keywords": [
17
+ "memory",
18
+ "ai",
19
+ "codex",
20
+ "claude",
21
+ "context",
22
+ "agent"
23
+ ],
24
+ "bin": {
25
+ "contextline": "./dist/cli.js"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "docs",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "clear": "pwsh ../scripts/clear-codex.ps1",
36
+ "build:clear": "tsup && pwsh ../scripts/clear-codex.ps1",
37
+ "dev": "tsx src/index.ts",
38
+ "test": "vitest run",
39
+ "lint": "eslint .",
40
+ "postinstall": "node -e \"console.log('ContextLine installed.\\n\\nNext step:\\n contextline setup\\n\\nThis creates .contextline/, detects your AI host, and installs memory hooks.\\n')\""
41
+ },
42
+ "dependencies": {
43
+ "commander": "^12.1.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^20.14.0",
47
+ "eslint": "^9.8.0",
48
+ "tsup": "^8.2.4",
49
+ "tsx": "^4.16.2",
50
+ "typescript": "^5.5.4",
51
+ "vitest": "^2.0.5"
52
+ },
53
+ "engines": {
54
+ "node": ">=20"
55
+ }
56
+ }
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
-
2
- export { }