@v1nvn/agentic-core 0.36.1 → 0.37.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/README.md +4 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/vite.js +54 -0
- package/dist/vite.js.map +1 -0
- package/package.json +5 -1
- package/src/cli.ts +11 -0
- package/src/vite.ts +71 -0
package/README.md
CHANGED
|
@@ -13,10 +13,11 @@ User-facing docs: [root README](../../README.md).
|
|
|
13
13
|
| ------------------- | ----------------------------------------------------------------------- |
|
|
14
14
|
| `src/last-reply.ts` | finds the previous assistant reply across local Claude Code transcripts |
|
|
15
15
|
| `src/input.ts` | stdin and markdown-file input with the CLI error contracts |
|
|
16
|
-
| `src/cli.ts` | quiet commander parsing
|
|
16
|
+
| `src/cli.ts` | quiet commander parsing, usage/exit, and the runMain failure tail |
|
|
17
17
|
| `src/logger.ts` | level-filtered stderr logger for the MCP servers |
|
|
18
18
|
| `src/shutdown.ts` | close-then-exit signal handling for the server entrypoints |
|
|
19
19
|
| `src/dev.ts` | the hot-reload watcher wiring the servers' `yarn dev` harnesses share |
|
|
20
|
+
| `src/vite.ts` | the modVite two-mode build behind every mod home's `vite.config.ts` |
|
|
20
21
|
| `src/index.ts` | the public entry — re-exports the modules above |
|
|
21
22
|
|
|
22
23
|
## Develop
|
|
@@ -31,6 +32,8 @@ yarn lint && yarn typecheck # from the repo root
|
|
|
31
32
|
|
|
32
33
|
- No tool-specific logic lives here — a concern lands in core only when two
|
|
33
34
|
or more tools need it.
|
|
35
|
+
- The `./vite` export is dev-only, loaded by the homes' `vite.config.ts` —
|
|
36
|
+
never by the shipped CLIs, which import `.` alone.
|
|
34
37
|
- `last-reply` reads transcripts only; it never writes.
|
|
35
38
|
- The logger and shutdown/dev helpers write to stderr only — stdout carries
|
|
36
39
|
the MCP transport on the servers.
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,14 @@ function printUsageAndExit(program) {
|
|
|
17
17
|
console.error(program.helpInformation());
|
|
18
18
|
process.exit(1);
|
|
19
19
|
}
|
|
20
|
+
async function runMain(main) {
|
|
21
|
+
try {
|
|
22
|
+
await main();
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error(e.message);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
20
28
|
//#endregion
|
|
21
29
|
//#region src/dev.ts
|
|
22
30
|
var RELOAD_DEBOUNCE_MS = 150;
|
|
@@ -180,6 +188,6 @@ function shutdownOnSignals(closables, signals = ["SIGINT", "SIGTERM"]) {
|
|
|
180
188
|
for (const signal of signals) process.on(signal, shutdown);
|
|
181
189
|
}
|
|
182
190
|
//#endregion
|
|
183
|
-
export { Logger, claudeProjectsDir, lastReply, levelEnabled, parseQuietly, printUsageAndExit, readMarkdownFile, readStdin, shutdownOnSignals, watchWithReload };
|
|
191
|
+
export { Logger, claudeProjectsDir, lastReply, levelEnabled, parseQuietly, printUsageAndExit, readMarkdownFile, readStdin, runMain, shutdownOnSignals, watchWithReload };
|
|
184
192
|
|
|
185
193
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/cli.ts","../src/dev.ts","../src/input.ts","../src/last-reply.ts","../src/logger.ts","../src/shutdown.ts"],"sourcesContent":["import type { Command } from 'commander';\n\nexport function parseQuietly<T = never>(\n program: Command,\n args: readonly string[],\n recover?: (err: unknown) => T | undefined,\n): Command | T | undefined {\n try {\n program\n .allowExcessArguments(false)\n .exitOverride()\n .configureOutput({ writeOut: () => undefined, writeErr: () => undefined })\n .parse([...args], { from: 'user' });\n return program;\n } catch (err) {\n return recover?.(err);\n }\n}\n\nexport function printUsageAndExit(program: Command): never {\n console.error(program.helpInformation());\n // CLIs report failure through the exit code; the rule targets libraries.\n // eslint-disable-next-line n/no-process-exit\n process.exit(1);\n}\n","// Structural stand-ins for vite's dev server and module runner — the shared\n// hot-reload wiring below must not pull vite into core's runtime surface; the\n// MCP packages pass their real instances through.\nexport interface ReloadableRunner {\n readonly evaluatedModules: { clear(): void };\n import<T>(entry: string): Promise<T>;\n}\n\nexport interface WatchableDevServer {\n close(): Promise<unknown>;\n readonly watcher: {\n on(event: 'change', cb: (file: string) => void): unknown;\n };\n}\n\nconst RELOAD_DEBOUNCE_MS = 150;\n\nconst IGNORED_SEGMENTS = ['/node_modules/', '/dist/', '/.git/', '/coverage/'];\n\nfunction shouldReload(file: string): boolean {\n return IGNORED_SEGMENTS.every(seg => !file.includes(seg));\n}\n\n/**\n * Debounced file-change reloads, serialized via a promise chain: concurrent\n * saves never overlap, and a mid-reload save still produces a follow-up.\n */\nexport function watchWithReload(\n vite: WatchableDevServer,\n reload: () => Promise<void>,\n): void {\n let chain: Promise<void> = Promise.resolve();\n let timer: NodeJS.Timeout | undefined;\n vite.watcher.on('change', file => {\n if (!shouldReload(file)) {\n return;\n }\n if (timer) {\n clearTimeout(timer);\n }\n timer = setTimeout(() => {\n timer = undefined;\n chain = chain.then(reload);\n }, RELOAD_DEBOUNCE_MS);\n });\n}\n","import { readFileSync, statSync } from 'node:fs';\n\nfunction readAll(stream: NodeJS.ReadableStream): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = '';\n stream.setEncoding('utf8');\n stream.on('data', (chunk: string) => {\n data += chunk;\n });\n stream.on('end', () => {\n resolve(data);\n });\n stream.on('error', reject);\n });\n}\n\nexport function readStdin(\n stream: NodeJS.ReadableStream = process.stdin,\n): Promise<string> {\n return readAll(stream);\n}\n\n/** A markdown file's contents, with the CLI's no-such-file error. */\nexport function readMarkdownFile(path: string): string {\n const stats = statSync(path, { throwIfNoEntry: false });\n if (!stats?.isFile()) {\n throw new Error(`no such file: ${path}`);\n }\n return readFileSync(path, 'utf8');\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nfunction isFile(path: string): boolean {\n try {\n return statSync(path).isFile();\n } catch {\n return false;\n }\n}\n\ninterface ContentBlock {\n text?: string;\n type: string;\n}\n\ninterface TranscriptEntry {\n message?: { content?: unknown };\n type?: string;\n}\n\nfunction textBlocks(entry: TranscriptEntry): ContentBlock[] | undefined {\n const content = entry.message?.content;\n if (entry.type !== 'assistant' || !Array.isArray(content)) {\n return undefined;\n }\n const blocks = content.filter(\n (b): b is ContentBlock =>\n typeof b === 'object' &&\n b !== null &&\n (b as ContentBlock).type === 'text',\n );\n return blocks.length > 0 ? blocks : undefined;\n}\n\nexport function claudeProjectsDir(): string {\n const claudeDir = process.env.CLAUDE_DIR ?? join(homedir(), '.claude');\n return join(claudeDir, 'projects');\n}\n\n/**\n * The last assistant text reply from a Claude Code session, reproducing Claude\n * Code's `/copy` byte-for-byte: the last assistant entry that contains a\n * `text` block, only its `text` block(s) (tool_use / thinking dropped), blocks\n * joined with a blank line, and no trailing newline.\n *\n * @param arg a transcript file, a session UUID under the project dir, or\n * nothing to use the newest session for the current project.\n */\nexport function lastReply(arg?: string): string {\n // Claude Code keys transcripts under the project root with \"/\" → \"-\".\n // Prefer $CLAUDE_PROJECT_DIR (exported to hook processes) over $PWD.\n const proj = (process.env.CLAUDE_PROJECT_DIR ?? process.cwd()).replaceAll(\n '/',\n '-',\n );\n const projDir = join(claudeProjectsDir(), proj);\n\n let file: string | undefined;\n if (arg !== undefined) {\n file = isFile(arg) ? arg : join(projDir, `${arg}.jsonl`);\n } else if (existsSync(projDir)) {\n const newest = readdirSync(projDir)\n .filter(f => f.endsWith('.jsonl'))\n .map(f => ({ f, mtimeMs: statSync(join(projDir, f)).mtimeMs }))\n .sort((a, b) => b.mtimeMs - a.mtimeMs)\n .at(0);\n if (newest) {\n file = join(projDir, newest.f);\n }\n }\n\n if (file === undefined || !isFile(file)) {\n throw new Error(`no session transcript found in ${projDir}`);\n }\n\n let last: ContentBlock[] | undefined;\n for (const line of readFileSync(file, 'utf8').split('\\n')) {\n if (!line.includes('\"assistant\"')) {\n continue;\n }\n let entry: TranscriptEntry;\n try {\n entry = JSON.parse(line) as TranscriptEntry;\n } catch {\n continue;\n }\n const blocks = textBlocks(entry);\n if (blocks) {\n last = blocks;\n }\n }\n return (last ?? []).map(b => b.text ?? '').join('\\n\\n');\n}\n","export type LogLevel = 'debug' | 'error' | 'info' | 'silent' | 'warn';\n\nconst LEVEL_RANK: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n silent: Number.MAX_SAFE_INTEGER,\n};\n\nexport function levelEnabled(\n active: LogLevel,\n wanted: Exclude<LogLevel, 'silent'>,\n): boolean {\n return LEVEL_RANK[wanted] >= LEVEL_RANK[active];\n}\n\nconst LEVEL_LABEL: Record<Exclude<LogLevel, 'silent'>, string> = {\n debug: 'DEBUG',\n error: 'ERROR',\n info: 'INFO',\n warn: 'WARN',\n};\n\ntype Writer = (message: string) => void;\n\n// stdout carries the MCP transport; everything here goes to stderr.\nexport class Logger {\n private readonly stderr: Writer;\n\n constructor(\n readonly activeLevel: LogLevel,\n stderr: Writer = line => process.stderr.write(`${line}\\n`),\n ) {\n this.stderr = stderr;\n }\n\n debug(message: string): void {\n this.log('debug', message);\n }\n\n error(message: string): void {\n this.log('error', message);\n }\n\n info(message: string): void {\n this.log('info', message);\n }\n\n warn(message: string): void {\n this.log('warn', message);\n }\n\n private log(level: Exclude<LogLevel, 'silent'>, message: string): void {\n if (levelEnabled(this.activeLevel, level)) {\n this.stderr(`${LEVEL_LABEL[level]} ${message}`);\n }\n }\n}\n","/** Close each closable in order (best-effort), then force-exit. */\nexport function shutdownOnSignals(\n closables: readonly { close(): Promise<unknown> }[],\n signals: readonly NodeJS.Signals[] = ['SIGINT', 'SIGTERM'],\n): void {\n function shutdown(): void {\n void closables\n .reduce(\n (acc: Promise<void>, closable) =>\n acc.then(async () => {\n try {\n await closable.close();\n } catch {\n // best-effort; exiting regardless\n }\n return;\n }),\n Promise.resolve(),\n )\n .finally(() => {\n // Force-exit on signal; the n/no-process-exit rule targets libraries.\n // eslint-disable-next-line n/no-process-exit\n process.exit(0);\n });\n }\n for (const signal of signals) {\n process.on(signal, shutdown);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/cli.ts","../src/dev.ts","../src/input.ts","../src/last-reply.ts","../src/logger.ts","../src/shutdown.ts"],"sourcesContent":["import type { Command } from 'commander';\n\nexport function parseQuietly<T = never>(\n program: Command,\n args: readonly string[],\n recover?: (err: unknown) => T | undefined,\n): Command | T | undefined {\n try {\n program\n .allowExcessArguments(false)\n .exitOverride()\n .configureOutput({ writeOut: () => undefined, writeErr: () => undefined })\n .parse([...args], { from: 'user' });\n return program;\n } catch (err) {\n return recover?.(err);\n }\n}\n\nexport function printUsageAndExit(program: Command): never {\n console.error(program.helpInformation());\n // CLIs report failure through the exit code; the rule targets libraries.\n // eslint-disable-next-line n/no-process-exit\n process.exit(1);\n}\n\nexport async function runMain(main: () => Promise<void> | void): Promise<void> {\n try {\n await main();\n } catch (e) {\n console.error((e as Error).message);\n // CLIs report failure through the exit code; the rule targets libraries.\n // eslint-disable-next-line n/no-process-exit\n process.exit(1);\n }\n}\n","// Structural stand-ins for vite's dev server and module runner — the shared\n// hot-reload wiring below must not pull vite into core's runtime surface; the\n// MCP packages pass their real instances through.\nexport interface ReloadableRunner {\n readonly evaluatedModules: { clear(): void };\n import<T>(entry: string): Promise<T>;\n}\n\nexport interface WatchableDevServer {\n close(): Promise<unknown>;\n readonly watcher: {\n on(event: 'change', cb: (file: string) => void): unknown;\n };\n}\n\nconst RELOAD_DEBOUNCE_MS = 150;\n\nconst IGNORED_SEGMENTS = ['/node_modules/', '/dist/', '/.git/', '/coverage/'];\n\nfunction shouldReload(file: string): boolean {\n return IGNORED_SEGMENTS.every(seg => !file.includes(seg));\n}\n\n/**\n * Debounced file-change reloads, serialized via a promise chain: concurrent\n * saves never overlap, and a mid-reload save still produces a follow-up.\n */\nexport function watchWithReload(\n vite: WatchableDevServer,\n reload: () => Promise<void>,\n): void {\n let chain: Promise<void> = Promise.resolve();\n let timer: NodeJS.Timeout | undefined;\n vite.watcher.on('change', file => {\n if (!shouldReload(file)) {\n return;\n }\n if (timer) {\n clearTimeout(timer);\n }\n timer = setTimeout(() => {\n timer = undefined;\n chain = chain.then(reload);\n }, RELOAD_DEBOUNCE_MS);\n });\n}\n","import { readFileSync, statSync } from 'node:fs';\n\nfunction readAll(stream: NodeJS.ReadableStream): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = '';\n stream.setEncoding('utf8');\n stream.on('data', (chunk: string) => {\n data += chunk;\n });\n stream.on('end', () => {\n resolve(data);\n });\n stream.on('error', reject);\n });\n}\n\nexport function readStdin(\n stream: NodeJS.ReadableStream = process.stdin,\n): Promise<string> {\n return readAll(stream);\n}\n\n/** A markdown file's contents, with the CLI's no-such-file error. */\nexport function readMarkdownFile(path: string): string {\n const stats = statSync(path, { throwIfNoEntry: false });\n if (!stats?.isFile()) {\n throw new Error(`no such file: ${path}`);\n }\n return readFileSync(path, 'utf8');\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nfunction isFile(path: string): boolean {\n try {\n return statSync(path).isFile();\n } catch {\n return false;\n }\n}\n\ninterface ContentBlock {\n text?: string;\n type: string;\n}\n\ninterface TranscriptEntry {\n message?: { content?: unknown };\n type?: string;\n}\n\nfunction textBlocks(entry: TranscriptEntry): ContentBlock[] | undefined {\n const content = entry.message?.content;\n if (entry.type !== 'assistant' || !Array.isArray(content)) {\n return undefined;\n }\n const blocks = content.filter(\n (b): b is ContentBlock =>\n typeof b === 'object' &&\n b !== null &&\n (b as ContentBlock).type === 'text',\n );\n return blocks.length > 0 ? blocks : undefined;\n}\n\nexport function claudeProjectsDir(): string {\n const claudeDir = process.env.CLAUDE_DIR ?? join(homedir(), '.claude');\n return join(claudeDir, 'projects');\n}\n\n/**\n * The last assistant text reply from a Claude Code session, reproducing Claude\n * Code's `/copy` byte-for-byte: the last assistant entry that contains a\n * `text` block, only its `text` block(s) (tool_use / thinking dropped), blocks\n * joined with a blank line, and no trailing newline.\n *\n * @param arg a transcript file, a session UUID under the project dir, or\n * nothing to use the newest session for the current project.\n */\nexport function lastReply(arg?: string): string {\n // Claude Code keys transcripts under the project root with \"/\" → \"-\".\n // Prefer $CLAUDE_PROJECT_DIR (exported to hook processes) over $PWD.\n const proj = (process.env.CLAUDE_PROJECT_DIR ?? process.cwd()).replaceAll(\n '/',\n '-',\n );\n const projDir = join(claudeProjectsDir(), proj);\n\n let file: string | undefined;\n if (arg !== undefined) {\n file = isFile(arg) ? arg : join(projDir, `${arg}.jsonl`);\n } else if (existsSync(projDir)) {\n const newest = readdirSync(projDir)\n .filter(f => f.endsWith('.jsonl'))\n .map(f => ({ f, mtimeMs: statSync(join(projDir, f)).mtimeMs }))\n .sort((a, b) => b.mtimeMs - a.mtimeMs)\n .at(0);\n if (newest) {\n file = join(projDir, newest.f);\n }\n }\n\n if (file === undefined || !isFile(file)) {\n throw new Error(`no session transcript found in ${projDir}`);\n }\n\n let last: ContentBlock[] | undefined;\n for (const line of readFileSync(file, 'utf8').split('\\n')) {\n if (!line.includes('\"assistant\"')) {\n continue;\n }\n let entry: TranscriptEntry;\n try {\n entry = JSON.parse(line) as TranscriptEntry;\n } catch {\n continue;\n }\n const blocks = textBlocks(entry);\n if (blocks) {\n last = blocks;\n }\n }\n return (last ?? []).map(b => b.text ?? '').join('\\n\\n');\n}\n","export type LogLevel = 'debug' | 'error' | 'info' | 'silent' | 'warn';\n\nconst LEVEL_RANK: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n silent: Number.MAX_SAFE_INTEGER,\n};\n\nexport function levelEnabled(\n active: LogLevel,\n wanted: Exclude<LogLevel, 'silent'>,\n): boolean {\n return LEVEL_RANK[wanted] >= LEVEL_RANK[active];\n}\n\nconst LEVEL_LABEL: Record<Exclude<LogLevel, 'silent'>, string> = {\n debug: 'DEBUG',\n error: 'ERROR',\n info: 'INFO',\n warn: 'WARN',\n};\n\ntype Writer = (message: string) => void;\n\n// stdout carries the MCP transport; everything here goes to stderr.\nexport class Logger {\n private readonly stderr: Writer;\n\n constructor(\n readonly activeLevel: LogLevel,\n stderr: Writer = line => process.stderr.write(`${line}\\n`),\n ) {\n this.stderr = stderr;\n }\n\n debug(message: string): void {\n this.log('debug', message);\n }\n\n error(message: string): void {\n this.log('error', message);\n }\n\n info(message: string): void {\n this.log('info', message);\n }\n\n warn(message: string): void {\n this.log('warn', message);\n }\n\n private log(level: Exclude<LogLevel, 'silent'>, message: string): void {\n if (levelEnabled(this.activeLevel, level)) {\n this.stderr(`${LEVEL_LABEL[level]} ${message}`);\n }\n }\n}\n","/** Close each closable in order (best-effort), then force-exit. */\nexport function shutdownOnSignals(\n closables: readonly { close(): Promise<unknown> }[],\n signals: readonly NodeJS.Signals[] = ['SIGINT', 'SIGTERM'],\n): void {\n function shutdown(): void {\n void closables\n .reduce(\n (acc: Promise<void>, closable) =>\n acc.then(async () => {\n try {\n await closable.close();\n } catch {\n // best-effort; exiting regardless\n }\n return;\n }),\n Promise.resolve(),\n )\n .finally(() => {\n // Force-exit on signal; the n/no-process-exit rule targets libraries.\n // eslint-disable-next-line n/no-process-exit\n process.exit(0);\n });\n }\n for (const signal of signals) {\n process.on(signal, shutdown);\n }\n}\n"],"mappings":"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"}
|
package/dist/vite.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { configDefaults } from "vitest/config";
|
|
2
|
+
//#region src/vite.ts
|
|
3
|
+
/**
|
|
4
|
+
* The one two-mode build every mod home runs — a home's `vite.config.ts` is a
|
|
5
|
+
* thin wrapper handing `modVite` its standalone mode name and extra entries.
|
|
6
|
+
*/
|
|
7
|
+
function modVite(mode, { standalone, entries = {} }) {
|
|
8
|
+
if (mode === standalone.mode) return {
|
|
9
|
+
ssr: { noExternal: true },
|
|
10
|
+
build: {
|
|
11
|
+
outDir: "dist",
|
|
12
|
+
emptyOutDir: false,
|
|
13
|
+
sourcemap: true,
|
|
14
|
+
minify: false,
|
|
15
|
+
ssr: true,
|
|
16
|
+
rollupOptions: {
|
|
17
|
+
input: { [standalone.name]: "src/index.ts" },
|
|
18
|
+
output: {
|
|
19
|
+
banner: "#!/usr/bin/env node",
|
|
20
|
+
entryFileNames: `${standalone.name}.mjs`
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
build: {
|
|
27
|
+
outDir: "dist",
|
|
28
|
+
emptyOutDir: true,
|
|
29
|
+
sourcemap: true,
|
|
30
|
+
minify: false,
|
|
31
|
+
ssr: true,
|
|
32
|
+
rollupOptions: {
|
|
33
|
+
input: {
|
|
34
|
+
index: "src/index.ts",
|
|
35
|
+
...entries
|
|
36
|
+
},
|
|
37
|
+
output: { banner: (chunk) => chunk.isEntry && chunk.facadeModuleId?.endsWith("src/index.ts") ? "#!/usr/bin/env node" : "" }
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
test: {
|
|
41
|
+
globals: true,
|
|
42
|
+
environment: "node",
|
|
43
|
+
exclude: [
|
|
44
|
+
...configDefaults.exclude,
|
|
45
|
+
"dist/**",
|
|
46
|
+
"tests/**"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { modVite };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=vite.js.map
|
package/dist/vite.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["/**\n * The one two-mode build every mod home runs — a home's `vite.config.ts` is a\n * thin wrapper handing `modVite` its standalone mode name and extra entries.\n */\nimport { configDefaults } from 'vitest/config';\n\nexport interface ModViteOptions {\n /** Extra CLI-pass entries beside the CLI itself, e.g. tokens' `text` library chunk. */\n entries?: Record<string, string>;\n /** The `--mode` that builds the standalone artifact, and the `bin/<name>.mjs` it emits. */\n standalone: { mode: string; name: string };\n}\n\nexport function modVite(\n mode: string,\n { standalone, entries = {} }: ModViteOptions,\n) {\n // The standalone pass emits the artifact the plugin folder carries — the\n // mod execs it through the session's Bash tool, where no node_modules\n // exists, so it inlines every dependency. Only ever run under `node`, where\n // the banner is inert.\n if (mode === standalone.mode) {\n return {\n ssr: { noExternal: true },\n build: {\n outDir: 'dist',\n emptyOutDir: false,\n sourcemap: true,\n minify: false,\n ssr: true,\n rollupOptions: {\n input: { [standalone.name]: 'src/index.ts' },\n output: {\n banner: '#!/usr/bin/env node',\n entryFileNames: `${standalone.name}.mjs`,\n },\n },\n },\n };\n }\n // The CLI pass builds the default way: external deps, installed beside it\n // by npx. SSR/Node build: Vite externalizes node built-ins AND package.json\n // `dependencies` (`@v1nvn/agentic-core` stays a real dep), so only source\n // is bundled; ESM out matches \"type\":\"module\".\n return {\n build: {\n outDir: 'dist',\n emptyOutDir: true,\n sourcemap: true,\n minify: false,\n ssr: true,\n rollupOptions: {\n input: { index: 'src/index.ts', ...entries },\n // `bin` (dist/index.js) is exec'd by the kernel; without a shebang\n // the OS runs it under /bin/sh and `npx <bin>` dies parsing `import`.\n // Library chunks carry none.\n output: {\n banner: (chunk: { facadeModuleId?: string; isEntry: boolean }) =>\n chunk.isEntry && chunk.facadeModuleId?.endsWith('src/index.ts')\n ? '#!/usr/bin/env node'\n : '',\n },\n },\n },\n test: {\n globals: true,\n environment: 'node',\n exclude: [...configDefaults.exclude, 'dist/**', 'tests/**'],\n },\n };\n}\n"],"mappings":";;;;;;AAaA,SAAgB,QACd,MACA,EAAE,YAAY,UAAU,CAAC,KACzB;CAKA,IAAI,SAAS,WAAW,MACtB,OAAO;EACL,KAAK,EAAE,YAAY,KAAK;EACxB,OAAO;GACL,QAAQ;GACR,aAAa;GACb,WAAW;GACX,QAAQ;GACR,KAAK;GACL,eAAe;IACb,OAAO,GAAG,WAAW,OAAO,eAAe;IAC3C,QAAQ;KACN,QAAQ;KACR,gBAAgB,GAAG,WAAW,KAAK;IACrC;GACF;EACF;CACF;CAMF,OAAO;EACL,OAAO;GACL,QAAQ;GACR,aAAa;GACb,WAAW;GACX,QAAQ;GACR,KAAK;GACL,eAAe;IACb,OAAO;KAAE,OAAO;KAAgB,GAAG;IAAQ;IAI3C,QAAQ,EACN,SAAS,UACP,MAAM,WAAW,MAAM,gBAAgB,SAAS,cAAc,IAC1D,wBACA,GACR;GACF;EACF;EACA,MAAM;GACJ,SAAS;GACT,aAAa;GACb,SAAS;IAAC,GAAG,eAAe;IAAS;IAAW;GAAU;EAC5D;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@v1nvn/agentic-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Shared runtime for the @v1nvn agent tools — last-reply transcript reader and fixed-width text formatting.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./src/index.ts",
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./vite": {
|
|
13
|
+
"types": "./src/vite.ts",
|
|
14
|
+
"default": "./dist/vite.js"
|
|
11
15
|
}
|
|
12
16
|
},
|
|
13
17
|
"scripts": {
|
package/src/cli.ts
CHANGED
|
@@ -23,3 +23,14 @@ export function printUsageAndExit(program: Command): never {
|
|
|
23
23
|
// eslint-disable-next-line n/no-process-exit
|
|
24
24
|
process.exit(1);
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
export async function runMain(main: () => Promise<void> | void): Promise<void> {
|
|
28
|
+
try {
|
|
29
|
+
await main();
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.error((e as Error).message);
|
|
32
|
+
// CLIs report failure through the exit code; the rule targets libraries.
|
|
33
|
+
// eslint-disable-next-line n/no-process-exit
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/vite.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one two-mode build every mod home runs — a home's `vite.config.ts` is a
|
|
3
|
+
* thin wrapper handing `modVite` its standalone mode name and extra entries.
|
|
4
|
+
*/
|
|
5
|
+
import { configDefaults } from 'vitest/config';
|
|
6
|
+
|
|
7
|
+
export interface ModViteOptions {
|
|
8
|
+
/** Extra CLI-pass entries beside the CLI itself, e.g. tokens' `text` library chunk. */
|
|
9
|
+
entries?: Record<string, string>;
|
|
10
|
+
/** The `--mode` that builds the standalone artifact, and the `bin/<name>.mjs` it emits. */
|
|
11
|
+
standalone: { mode: string; name: string };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function modVite(
|
|
15
|
+
mode: string,
|
|
16
|
+
{ standalone, entries = {} }: ModViteOptions,
|
|
17
|
+
) {
|
|
18
|
+
// The standalone pass emits the artifact the plugin folder carries — the
|
|
19
|
+
// mod execs it through the session's Bash tool, where no node_modules
|
|
20
|
+
// exists, so it inlines every dependency. Only ever run under `node`, where
|
|
21
|
+
// the banner is inert.
|
|
22
|
+
if (mode === standalone.mode) {
|
|
23
|
+
return {
|
|
24
|
+
ssr: { noExternal: true },
|
|
25
|
+
build: {
|
|
26
|
+
outDir: 'dist',
|
|
27
|
+
emptyOutDir: false,
|
|
28
|
+
sourcemap: true,
|
|
29
|
+
minify: false,
|
|
30
|
+
ssr: true,
|
|
31
|
+
rollupOptions: {
|
|
32
|
+
input: { [standalone.name]: 'src/index.ts' },
|
|
33
|
+
output: {
|
|
34
|
+
banner: '#!/usr/bin/env node',
|
|
35
|
+
entryFileNames: `${standalone.name}.mjs`,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// The CLI pass builds the default way: external deps, installed beside it
|
|
42
|
+
// by npx. SSR/Node build: Vite externalizes node built-ins AND package.json
|
|
43
|
+
// `dependencies` (`@v1nvn/agentic-core` stays a real dep), so only source
|
|
44
|
+
// is bundled; ESM out matches "type":"module".
|
|
45
|
+
return {
|
|
46
|
+
build: {
|
|
47
|
+
outDir: 'dist',
|
|
48
|
+
emptyOutDir: true,
|
|
49
|
+
sourcemap: true,
|
|
50
|
+
minify: false,
|
|
51
|
+
ssr: true,
|
|
52
|
+
rollupOptions: {
|
|
53
|
+
input: { index: 'src/index.ts', ...entries },
|
|
54
|
+
// `bin` (dist/index.js) is exec'd by the kernel; without a shebang
|
|
55
|
+
// the OS runs it under /bin/sh and `npx <bin>` dies parsing `import`.
|
|
56
|
+
// Library chunks carry none.
|
|
57
|
+
output: {
|
|
58
|
+
banner: (chunk: { facadeModuleId?: string; isEntry: boolean }) =>
|
|
59
|
+
chunk.isEntry && chunk.facadeModuleId?.endsWith('src/index.ts')
|
|
60
|
+
? '#!/usr/bin/env node'
|
|
61
|
+
: '',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
test: {
|
|
66
|
+
globals: true,
|
|
67
|
+
environment: 'node',
|
|
68
|
+
exclude: [...configDefaults.exclude, 'dist/**', 'tests/**'],
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|