@otomus/nerva-cli 0.1.1
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/commands/dev.d.ts +42 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +257 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/generate.d.ts +24 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +162 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/list.d.ts +19 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +96 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new.d.ts +23 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +138 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/plugin.d.ts +79 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +319 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/test.d.ts +52 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +110 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/trace-ui.d.ts +16 -0
- package/dist/commands/trace-ui.d.ts.map +1 -0
- package/dist/commands/trace-ui.js +117 -0
- package/dist/commands/trace-ui.js.map +1 -0
- package/dist/config/nerva-yaml.d.ts +70 -0
- package/dist/config/nerva-yaml.d.ts.map +1 -0
- package/dist/config/nerva-yaml.js +185 -0
- package/dist/config/nerva-yaml.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/plugin-registry.d.ts +30 -0
- package/dist/registry/plugin-registry.d.ts.map +1 -0
- package/dist/registry/plugin-registry.js +134 -0
- package/dist/registry/plugin-registry.js.map +1 -0
- package/dist/templates/render.d.ts +36 -0
- package/dist/templates/render.d.ts.map +1 -0
- package/dist/templates/render.js +52 -0
- package/dist/templates/render.js.map +1 -0
- package/package.json +32 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva new <name>` command.
|
|
3
|
+
*
|
|
4
|
+
* Scaffolds a new Nerva project directory with the correct structure
|
|
5
|
+
* for either Python or TypeScript.
|
|
6
|
+
*/
|
|
7
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { serializeNervaConfig } from '../config/nerva-yaml.js';
|
|
10
|
+
/** Directories created for a Python project. */
|
|
11
|
+
const PYTHON_DIRS = ['agents', 'tools', 'memory', 'middleware', 'tests'];
|
|
12
|
+
/** Directories created for a TypeScript project. */
|
|
13
|
+
const TS_DIRS = ['src/agents', 'src/tools', 'src/memory', 'src/middleware', 'tests'];
|
|
14
|
+
/**
|
|
15
|
+
* Creates a directory and all parents, silently succeeding if it already exists.
|
|
16
|
+
*
|
|
17
|
+
* @param dirPath - Absolute path to create
|
|
18
|
+
*/
|
|
19
|
+
async function ensureDir(dirPath) {
|
|
20
|
+
await mkdir(dirPath, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Builds a default NervaConfig for a new project.
|
|
24
|
+
*
|
|
25
|
+
* @param name - Project name
|
|
26
|
+
* @param lang - Project language
|
|
27
|
+
* @returns Default configuration
|
|
28
|
+
*/
|
|
29
|
+
function buildDefaultConfig(name, lang) {
|
|
30
|
+
return {
|
|
31
|
+
name,
|
|
32
|
+
version: '0.1.0',
|
|
33
|
+
lang,
|
|
34
|
+
agents: [],
|
|
35
|
+
tools: [],
|
|
36
|
+
middleware: [],
|
|
37
|
+
routers: [],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Scaffolds the Python-specific files in the project directory.
|
|
42
|
+
*
|
|
43
|
+
* @param projectDir - Absolute path to the project root
|
|
44
|
+
* @param name - Project name
|
|
45
|
+
*/
|
|
46
|
+
async function scaffoldPython(projectDir, name) {
|
|
47
|
+
for (const dir of PYTHON_DIRS) {
|
|
48
|
+
await ensureDir(join(projectDir, dir));
|
|
49
|
+
}
|
|
50
|
+
await writeFile(join(projectDir, 'main.py'), `"""${name} — entry point."""\n\nfrom nerva import Runtime\n\n\ndef main() -> None:\n """Start the Nerva runtime."""\n runtime = Runtime.from_config("nerva.yaml")\n runtime.start()\n\n\nif __name__ == "__main__":\n main()\n`, 'utf-8');
|
|
51
|
+
await writeFile(join(projectDir, 'requirements.txt'), 'nerva>=0.1.0\n', 'utf-8');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Scaffolds the TypeScript-specific files in the project directory.
|
|
55
|
+
*
|
|
56
|
+
* @param projectDir - Absolute path to the project root
|
|
57
|
+
* @param name - Project name
|
|
58
|
+
*/
|
|
59
|
+
async function scaffoldTypeScript(projectDir, name) {
|
|
60
|
+
for (const dir of TS_DIRS) {
|
|
61
|
+
await ensureDir(join(projectDir, dir));
|
|
62
|
+
}
|
|
63
|
+
await writeFile(join(projectDir, 'src/index.ts'), `/**\n * ${name} — entry point.\n */\n\nimport { Runtime } from 'nerva';\n\n/** Start the Nerva runtime. */\nasync function main(): Promise<void> {\n const runtime = await Runtime.fromConfig('nerva.yaml');\n await runtime.start();\n}\n\nmain();\n`, 'utf-8');
|
|
64
|
+
await writeFile(join(projectDir, 'package.json'), JSON.stringify({
|
|
65
|
+
name,
|
|
66
|
+
version: '0.1.0',
|
|
67
|
+
type: 'module',
|
|
68
|
+
scripts: {
|
|
69
|
+
build: 'tsc',
|
|
70
|
+
start: 'node dist/index.js',
|
|
71
|
+
test: 'vitest run',
|
|
72
|
+
},
|
|
73
|
+
dependencies: {
|
|
74
|
+
nerva: '^0.1.0',
|
|
75
|
+
},
|
|
76
|
+
devDependencies: {
|
|
77
|
+
typescript: '^5.4.0',
|
|
78
|
+
vitest: '^1.6.0',
|
|
79
|
+
'@types/node': '^20.14.0',
|
|
80
|
+
},
|
|
81
|
+
}, null, 2) + '\n', 'utf-8');
|
|
82
|
+
await writeFile(join(projectDir, 'tsconfig.json'), JSON.stringify({
|
|
83
|
+
compilerOptions: {
|
|
84
|
+
strict: true,
|
|
85
|
+
target: 'ES2022',
|
|
86
|
+
module: 'ES2022',
|
|
87
|
+
moduleResolution: 'node16',
|
|
88
|
+
outDir: 'dist',
|
|
89
|
+
rootDir: 'src',
|
|
90
|
+
declaration: true,
|
|
91
|
+
esModuleInterop: true,
|
|
92
|
+
skipLibCheck: true,
|
|
93
|
+
},
|
|
94
|
+
include: ['src/**/*.ts'],
|
|
95
|
+
exclude: ['node_modules', 'dist'],
|
|
96
|
+
}, null, 2) + '\n', 'utf-8');
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Registers the `nerva new` command with the CLI program.
|
|
100
|
+
*
|
|
101
|
+
* @param program - The root commander program
|
|
102
|
+
*/
|
|
103
|
+
export function registerNewCommand(program) {
|
|
104
|
+
program
|
|
105
|
+
.command('new')
|
|
106
|
+
.description('Create a new Nerva project')
|
|
107
|
+
.argument('<name>', 'Project name')
|
|
108
|
+
.option('-l, --lang <language>', 'Project language (python or typescript)', 'python')
|
|
109
|
+
.action(async (name, options) => {
|
|
110
|
+
await executeNew(name, options.lang);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Executes the project scaffolding logic.
|
|
115
|
+
*
|
|
116
|
+
* @param name - Project name (used as directory name)
|
|
117
|
+
* @param lang - Target language
|
|
118
|
+
* @throws {Error} If the language is unsupported
|
|
119
|
+
*/
|
|
120
|
+
export async function executeNew(name, lang) {
|
|
121
|
+
if (lang !== 'python' && lang !== 'typescript') {
|
|
122
|
+
throw new Error(`Unsupported language: "${lang}". Use "python" or "typescript".`);
|
|
123
|
+
}
|
|
124
|
+
const projectDir = join(process.cwd(), name);
|
|
125
|
+
await ensureDir(projectDir);
|
|
126
|
+
// Write nerva.yaml
|
|
127
|
+
const config = buildDefaultConfig(name, lang);
|
|
128
|
+
await writeFile(join(projectDir, 'nerva.yaml'), serializeNervaConfig(config), 'utf-8');
|
|
129
|
+
// Scaffold language-specific files
|
|
130
|
+
if (lang === 'python') {
|
|
131
|
+
await scaffoldPython(projectDir, name);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
await scaffoldTypeScript(projectDir, name);
|
|
135
|
+
}
|
|
136
|
+
console.log(`Created project "${name}" with ${lang} template at ./${name}`);
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva plugin` command group.
|
|
3
|
+
*
|
|
4
|
+
* Manages template plugins that extend `nerva generate` with custom
|
|
5
|
+
* component types. Plugins are stored in `.nerva/plugins/` and expose
|
|
6
|
+
* a `nerva-plugin.json` manifest.
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
/** A single template entry inside a plugin manifest. */
|
|
10
|
+
export interface PluginTemplate {
|
|
11
|
+
type: string;
|
|
12
|
+
files: string[];
|
|
13
|
+
}
|
|
14
|
+
/** Schema of the `nerva-plugin.json` manifest file. */
|
|
15
|
+
export interface PluginManifest {
|
|
16
|
+
name: string;
|
|
17
|
+
version: string;
|
|
18
|
+
templates: PluginTemplate[];
|
|
19
|
+
}
|
|
20
|
+
/** Summary of an installed plugin returned by `listPlugins`. */
|
|
21
|
+
export interface InstalledPlugin {
|
|
22
|
+
name: string;
|
|
23
|
+
version: string;
|
|
24
|
+
types: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parses and validates a raw JSON string as a PluginManifest.
|
|
28
|
+
*
|
|
29
|
+
* @param raw - Raw JSON string from nerva-plugin.json
|
|
30
|
+
* @returns Validated manifest
|
|
31
|
+
* @throws {Error} If the JSON is malformed or required fields are missing
|
|
32
|
+
*/
|
|
33
|
+
export declare function parsePluginManifest(raw: string): PluginManifest;
|
|
34
|
+
/**
|
|
35
|
+
* Returns the absolute path to the plugins directory for a project.
|
|
36
|
+
*
|
|
37
|
+
* @param projectDir - Absolute path to the project root
|
|
38
|
+
* @returns Absolute path to `.nerva/plugins/`
|
|
39
|
+
*/
|
|
40
|
+
export declare function pluginsDir(projectDir: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Installs a plugin from a local path or npm package name.
|
|
43
|
+
*
|
|
44
|
+
* For local paths: copies the directory into `.nerva/plugins/<name>`.
|
|
45
|
+
* For npm packages: runs `npm pack` in a temp dir, extracts, and copies.
|
|
46
|
+
*
|
|
47
|
+
* @param source - Local directory path or npm package name
|
|
48
|
+
* @param projectDir - Absolute path to the project root
|
|
49
|
+
* @returns The parsed manifest of the installed plugin
|
|
50
|
+
* @throws {Error} If the source has no valid manifest or the plugin is already installed
|
|
51
|
+
*/
|
|
52
|
+
export declare function installPlugin(source: string, projectDir: string): Promise<PluginManifest>;
|
|
53
|
+
/**
|
|
54
|
+
* Lists all installed plugins by reading `.nerva/plugins/`.
|
|
55
|
+
*
|
|
56
|
+
* @param projectDir - Absolute path to the project root
|
|
57
|
+
* @returns Array of installed plugin summaries
|
|
58
|
+
*/
|
|
59
|
+
export declare function listPlugins(projectDir: string): Promise<InstalledPlugin[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Removes an installed plugin by name.
|
|
62
|
+
*
|
|
63
|
+
* @param name - Plugin name to remove
|
|
64
|
+
* @param projectDir - Absolute path to the project root
|
|
65
|
+
* @throws {Error} If the plugin is not installed
|
|
66
|
+
*/
|
|
67
|
+
export declare function removePlugin(name: string, projectDir: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Registers the `nerva plugin` command group with the CLI program.
|
|
70
|
+
*
|
|
71
|
+
* Sub-commands:
|
|
72
|
+
* - `nerva plugin install <source>` — install from npm or local path
|
|
73
|
+
* - `nerva plugin list` — list installed plugins
|
|
74
|
+
* - `nerva plugin remove <name>` — remove an installed plugin
|
|
75
|
+
*
|
|
76
|
+
* @param program - The root commander program
|
|
77
|
+
*/
|
|
78
|
+
export declare function registerPluginCommand(program: Command): void;
|
|
79
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/commands/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAgBD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAiC/D;AA0CD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAErD;AAiBD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAazB;AAsFD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CA0BhF;AA2BD;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQlF;AAMD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4C5D"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva plugin` command group.
|
|
3
|
+
*
|
|
4
|
+
* Manages template plugins that extend `nerva generate` with custom
|
|
5
|
+
* component types. Plugins are stored in `.nerva/plugins/` and expose
|
|
6
|
+
* a `nerva-plugin.json` manifest.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdir, readFile, readdir, rm, cp } from 'node:fs/promises';
|
|
9
|
+
import { join, resolve } from 'node:path';
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { execSync } from 'node:child_process';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Constants
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/** Directory name where plugins are stored, relative to project root. */
|
|
16
|
+
const PLUGINS_DIR = '.nerva/plugins';
|
|
17
|
+
/** Name of the manifest file inside each plugin directory. */
|
|
18
|
+
const MANIFEST_FILENAME = 'nerva-plugin.json';
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Manifest parsing
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
/**
|
|
23
|
+
* Parses and validates a raw JSON string as a PluginManifest.
|
|
24
|
+
*
|
|
25
|
+
* @param raw - Raw JSON string from nerva-plugin.json
|
|
26
|
+
* @returns Validated manifest
|
|
27
|
+
* @throws {Error} If the JSON is malformed or required fields are missing
|
|
28
|
+
*/
|
|
29
|
+
export function parsePluginManifest(raw) {
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new Error('nerva-plugin.json contains invalid JSON');
|
|
36
|
+
}
|
|
37
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
38
|
+
throw new Error('nerva-plugin.json must be a JSON object');
|
|
39
|
+
}
|
|
40
|
+
const obj = parsed;
|
|
41
|
+
if (typeof obj['name'] !== 'string' || obj['name'].trim() === '') {
|
|
42
|
+
throw new Error('nerva-plugin.json: "name" must be a non-empty string');
|
|
43
|
+
}
|
|
44
|
+
if (typeof obj['version'] !== 'string' || obj['version'].trim() === '') {
|
|
45
|
+
throw new Error('nerva-plugin.json: "version" must be a non-empty string');
|
|
46
|
+
}
|
|
47
|
+
if (!Array.isArray(obj['templates'])) {
|
|
48
|
+
throw new Error('nerva-plugin.json: "templates" must be an array');
|
|
49
|
+
}
|
|
50
|
+
const templates = validateTemplates(obj['templates']);
|
|
51
|
+
return {
|
|
52
|
+
name: obj['name'],
|
|
53
|
+
version: obj['version'],
|
|
54
|
+
templates,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validates the templates array from a plugin manifest.
|
|
59
|
+
*
|
|
60
|
+
* @param raw - Unvalidated templates array
|
|
61
|
+
* @returns Validated PluginTemplate array
|
|
62
|
+
* @throws {Error} If any entry is malformed
|
|
63
|
+
*/
|
|
64
|
+
function validateTemplates(raw) {
|
|
65
|
+
return raw.map((entry, index) => {
|
|
66
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
|
|
67
|
+
throw new Error(`nerva-plugin.json: templates[${index}] must be an object`);
|
|
68
|
+
}
|
|
69
|
+
const item = entry;
|
|
70
|
+
if (typeof item['type'] !== 'string' || item['type'].trim() === '') {
|
|
71
|
+
throw new Error(`nerva-plugin.json: templates[${index}].type must be a non-empty string`);
|
|
72
|
+
}
|
|
73
|
+
if (!Array.isArray(item['files']) || item['files'].length === 0) {
|
|
74
|
+
throw new Error(`nerva-plugin.json: templates[${index}].files must be a non-empty array`);
|
|
75
|
+
}
|
|
76
|
+
const files = item['files'].map((f, fi) => {
|
|
77
|
+
if (typeof f !== 'string' || f.trim() === '') {
|
|
78
|
+
throw new Error(`nerva-plugin.json: templates[${index}].files[${fi}] must be a non-empty string`);
|
|
79
|
+
}
|
|
80
|
+
return f;
|
|
81
|
+
});
|
|
82
|
+
return { type: item['type'], files };
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Plugin directory helpers
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
/**
|
|
89
|
+
* Returns the absolute path to the plugins directory for a project.
|
|
90
|
+
*
|
|
91
|
+
* @param projectDir - Absolute path to the project root
|
|
92
|
+
* @returns Absolute path to `.nerva/plugins/`
|
|
93
|
+
*/
|
|
94
|
+
export function pluginsDir(projectDir) {
|
|
95
|
+
return join(projectDir, PLUGINS_DIR);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Returns the absolute path to a specific plugin's directory.
|
|
99
|
+
*
|
|
100
|
+
* @param projectDir - Absolute path to the project root
|
|
101
|
+
* @param name - Plugin name
|
|
102
|
+
* @returns Absolute path to the plugin directory
|
|
103
|
+
*/
|
|
104
|
+
function pluginPath(projectDir, name) {
|
|
105
|
+
return join(pluginsDir(projectDir), name);
|
|
106
|
+
}
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Install
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
/**
|
|
111
|
+
* Installs a plugin from a local path or npm package name.
|
|
112
|
+
*
|
|
113
|
+
* For local paths: copies the directory into `.nerva/plugins/<name>`.
|
|
114
|
+
* For npm packages: runs `npm pack` in a temp dir, extracts, and copies.
|
|
115
|
+
*
|
|
116
|
+
* @param source - Local directory path or npm package name
|
|
117
|
+
* @param projectDir - Absolute path to the project root
|
|
118
|
+
* @returns The parsed manifest of the installed plugin
|
|
119
|
+
* @throws {Error} If the source has no valid manifest or the plugin is already installed
|
|
120
|
+
*/
|
|
121
|
+
export async function installPlugin(source, projectDir) {
|
|
122
|
+
const resolvedSource = resolvePluginSource(source);
|
|
123
|
+
const manifest = await readManifestFrom(resolvedSource);
|
|
124
|
+
const dest = pluginPath(projectDir, manifest.name);
|
|
125
|
+
if (existsSync(dest)) {
|
|
126
|
+
throw new Error(`Plugin "${manifest.name}" is already installed. Remove it first to reinstall.`);
|
|
127
|
+
}
|
|
128
|
+
await mkdir(dest, { recursive: true });
|
|
129
|
+
await copyPluginFiles(resolvedSource, dest);
|
|
130
|
+
return manifest;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Resolves a source argument to an absolute local path.
|
|
134
|
+
*
|
|
135
|
+
* If the source looks like a local path (starts with `.`, `/`, or contains
|
|
136
|
+
* path separators), it is resolved against cwd. Otherwise it is treated as
|
|
137
|
+
* an npm package name and fetched via `npm pack`.
|
|
138
|
+
*
|
|
139
|
+
* @param source - CLI argument for plugin source
|
|
140
|
+
* @returns Absolute path to the plugin directory
|
|
141
|
+
*/
|
|
142
|
+
function resolvePluginSource(source) {
|
|
143
|
+
const isLocalPath = source.startsWith('.') || source.startsWith('/') || source.includes('/');
|
|
144
|
+
if (isLocalPath) {
|
|
145
|
+
return resolve(source);
|
|
146
|
+
}
|
|
147
|
+
return fetchNpmPackage(source);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Downloads an npm package using `npm pack` and extracts it to a temp directory.
|
|
151
|
+
*
|
|
152
|
+
* @param packageName - npm package name
|
|
153
|
+
* @returns Absolute path to the extracted package directory
|
|
154
|
+
* @throws {Error} If `npm pack` fails
|
|
155
|
+
*/
|
|
156
|
+
function fetchNpmPackage(packageName) {
|
|
157
|
+
const tmpDir = join(process.cwd(), '.nerva', '.tmp', `npm-${Date.now()}`);
|
|
158
|
+
try {
|
|
159
|
+
execSync(`mkdir -p "${tmpDir}" && cd "${tmpDir}" && npm pack "${packageName}" --silent 2>/dev/null`, {
|
|
160
|
+
stdio: 'pipe',
|
|
161
|
+
});
|
|
162
|
+
// npm pack creates a tarball — extract it
|
|
163
|
+
const tgzFiles = execSync(`ls "${tmpDir}"/*.tgz 2>/dev/null`, { encoding: 'utf-8' }).trim();
|
|
164
|
+
if (!tgzFiles) {
|
|
165
|
+
throw new Error(`Failed to download npm package "${packageName}"`);
|
|
166
|
+
}
|
|
167
|
+
const tgzPath = tgzFiles.split('\n')[0];
|
|
168
|
+
execSync(`cd "${tmpDir}" && tar xzf "${tgzPath}" --strip-components=1`, { stdio: 'pipe' });
|
|
169
|
+
return tmpDir;
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
throw new Error(`Failed to install npm package "${packageName}": ${err instanceof Error ? err.message : String(err)}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Reads and parses the manifest from a plugin source directory.
|
|
177
|
+
*
|
|
178
|
+
* @param sourceDir - Absolute path to the plugin source
|
|
179
|
+
* @returns Parsed manifest
|
|
180
|
+
* @throws {Error} If the manifest file is missing or invalid
|
|
181
|
+
*/
|
|
182
|
+
async function readManifestFrom(sourceDir) {
|
|
183
|
+
const manifestPath = join(sourceDir, MANIFEST_FILENAME);
|
|
184
|
+
if (!existsSync(manifestPath)) {
|
|
185
|
+
throw new Error(`No ${MANIFEST_FILENAME} found in ${sourceDir}`);
|
|
186
|
+
}
|
|
187
|
+
const raw = await readFile(manifestPath, 'utf-8');
|
|
188
|
+
return parsePluginManifest(raw);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Copies all plugin files from source to destination.
|
|
192
|
+
*
|
|
193
|
+
* @param sourceDir - Source directory
|
|
194
|
+
* @param destDir - Destination directory
|
|
195
|
+
*/
|
|
196
|
+
async function copyPluginFiles(sourceDir, destDir) {
|
|
197
|
+
await cp(sourceDir, destDir, { recursive: true });
|
|
198
|
+
}
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// List
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
/**
|
|
203
|
+
* Lists all installed plugins by reading `.nerva/plugins/`.
|
|
204
|
+
*
|
|
205
|
+
* @param projectDir - Absolute path to the project root
|
|
206
|
+
* @returns Array of installed plugin summaries
|
|
207
|
+
*/
|
|
208
|
+
export async function listPlugins(projectDir) {
|
|
209
|
+
const dir = pluginsDir(projectDir);
|
|
210
|
+
if (!existsSync(dir)) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
214
|
+
const plugins = [];
|
|
215
|
+
for (const entry of entries) {
|
|
216
|
+
if (!entry.isDirectory()) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const manifest = await readManifestSafe(join(dir, entry.name));
|
|
220
|
+
if (manifest) {
|
|
221
|
+
plugins.push({
|
|
222
|
+
name: manifest.name,
|
|
223
|
+
version: manifest.version,
|
|
224
|
+
types: manifest.templates.map((t) => t.type),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return plugins;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Reads a manifest from a plugin directory, returning null on failure.
|
|
232
|
+
*
|
|
233
|
+
* @param pluginDir - Absolute path to a plugin directory
|
|
234
|
+
* @returns Parsed manifest or null if unreadable
|
|
235
|
+
*/
|
|
236
|
+
async function readManifestSafe(pluginDir) {
|
|
237
|
+
const manifestPath = join(pluginDir, MANIFEST_FILENAME);
|
|
238
|
+
if (!existsSync(manifestPath)) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const raw = await readFile(manifestPath, 'utf-8');
|
|
243
|
+
return parsePluginManifest(raw);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// Remove
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
/**
|
|
253
|
+
* Removes an installed plugin by name.
|
|
254
|
+
*
|
|
255
|
+
* @param name - Plugin name to remove
|
|
256
|
+
* @param projectDir - Absolute path to the project root
|
|
257
|
+
* @throws {Error} If the plugin is not installed
|
|
258
|
+
*/
|
|
259
|
+
export async function removePlugin(name, projectDir) {
|
|
260
|
+
const dest = pluginPath(projectDir, name);
|
|
261
|
+
if (!existsSync(dest)) {
|
|
262
|
+
throw new Error(`Plugin "${name}" is not installed`);
|
|
263
|
+
}
|
|
264
|
+
await rm(dest, { recursive: true, force: true });
|
|
265
|
+
}
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
// Command registration
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
/**
|
|
270
|
+
* Registers the `nerva plugin` command group with the CLI program.
|
|
271
|
+
*
|
|
272
|
+
* Sub-commands:
|
|
273
|
+
* - `nerva plugin install <source>` — install from npm or local path
|
|
274
|
+
* - `nerva plugin list` — list installed plugins
|
|
275
|
+
* - `nerva plugin remove <name>` — remove an installed plugin
|
|
276
|
+
*
|
|
277
|
+
* @param program - The root commander program
|
|
278
|
+
*/
|
|
279
|
+
export function registerPluginCommand(program) {
|
|
280
|
+
const pluginCmd = program
|
|
281
|
+
.command('plugin')
|
|
282
|
+
.description('Manage template plugins for nerva generate');
|
|
283
|
+
pluginCmd
|
|
284
|
+
.command('install')
|
|
285
|
+
.description('Install a plugin from npm or a local path')
|
|
286
|
+
.argument('<source>', 'npm package name or local directory path')
|
|
287
|
+
.action(async (source) => {
|
|
288
|
+
const manifest = await installPlugin(source, process.cwd());
|
|
289
|
+
const types = manifest.templates.map((t) => t.type).join(', ');
|
|
290
|
+
console.log(`Installed plugin "${manifest.name}" v${manifest.version}`);
|
|
291
|
+
console.log(`Available types: ${types}`);
|
|
292
|
+
});
|
|
293
|
+
pluginCmd
|
|
294
|
+
.command('list')
|
|
295
|
+
.alias('ls')
|
|
296
|
+
.description('List installed plugins')
|
|
297
|
+
.action(async () => {
|
|
298
|
+
const plugins = await listPlugins(process.cwd());
|
|
299
|
+
if (plugins.length === 0) {
|
|
300
|
+
console.log('No plugins installed.');
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
console.log(`\nInstalled plugins:\n`);
|
|
304
|
+
for (const plugin of plugins) {
|
|
305
|
+
console.log(` ${plugin.name} (v${plugin.version})`);
|
|
306
|
+
console.log(` types: ${plugin.types.join(', ')}`);
|
|
307
|
+
}
|
|
308
|
+
console.log('');
|
|
309
|
+
});
|
|
310
|
+
pluginCmd
|
|
311
|
+
.command('remove')
|
|
312
|
+
.description('Remove an installed plugin')
|
|
313
|
+
.argument('<name>', 'Plugin name to remove')
|
|
314
|
+
.action(async (name) => {
|
|
315
|
+
await removePlugin(name, process.cwd());
|
|
316
|
+
console.log(`Removed plugin "${name}"`);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/commands/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva test` command.
|
|
3
|
+
*
|
|
4
|
+
* Detects the project language from nerva.yaml and runs the appropriate
|
|
5
|
+
* test runner (pytest for Python, vitest for TypeScript).
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { type ProjectLang } from '../config/nerva-yaml.js';
|
|
9
|
+
/**
|
|
10
|
+
* Builds the full argument list for the test runner, including optional coverage flag.
|
|
11
|
+
*
|
|
12
|
+
* @param lang - Project language
|
|
13
|
+
* @param coverage - Whether to enable coverage reporting
|
|
14
|
+
* @returns Tuple of [command, args]
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildTestCommand(lang: ProjectLang, coverage: boolean): {
|
|
17
|
+
cmd: string;
|
|
18
|
+
args: string[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Detects the project language from nerva.yaml in the given directory.
|
|
22
|
+
*
|
|
23
|
+
* @param projectDir - Absolute path to project root
|
|
24
|
+
* @returns Detected project language
|
|
25
|
+
* @throws {Error} If nerva.yaml is missing or invalid
|
|
26
|
+
*/
|
|
27
|
+
export declare function detectLanguage(projectDir: string): Promise<ProjectLang>;
|
|
28
|
+
/**
|
|
29
|
+
* Runs the test command as a child process and returns its exit code.
|
|
30
|
+
*
|
|
31
|
+
* @param projectDir - Working directory for the test runner
|
|
32
|
+
* @param cmd - Command to execute
|
|
33
|
+
* @param args - Arguments to pass
|
|
34
|
+
* @returns Exit code from the test runner (0 = success)
|
|
35
|
+
*/
|
|
36
|
+
export declare function runTestProcess(projectDir: string, cmd: string, args: string[]): Promise<number>;
|
|
37
|
+
/**
|
|
38
|
+
* Executes the test command: detects language, runs the appropriate test runner,
|
|
39
|
+
* and prints a trace summary.
|
|
40
|
+
*
|
|
41
|
+
* @param projectDir - Absolute path to project root
|
|
42
|
+
* @param coverage - Whether to enable coverage
|
|
43
|
+
* @returns Exit code from the test runner
|
|
44
|
+
*/
|
|
45
|
+
export declare function executeTest(projectDir: string, coverage: boolean): Promise<number>;
|
|
46
|
+
/**
|
|
47
|
+
* Registers the `nerva test` command with the CLI program.
|
|
48
|
+
*
|
|
49
|
+
* @param program - The root commander program
|
|
50
|
+
*/
|
|
51
|
+
export declare function registerTestCommand(program: Command): void;
|
|
52
|
+
//# sourceMappingURL=test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/commands/test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAa5E;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,OAAO,GAChB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAYjC;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAG7E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,MAAM,CAAC,CAsBjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAU1D"}
|