uda-cli 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/LICENSE +21 -0
- package/README.md +85 -0
- package/bin/uda.js +4 -0
- package/package.json +47 -0
- package/src/adapters/agents-md.js +22 -0
- package/src/adapters/base.js +14 -0
- package/src/adapters/claude.js +138 -0
- package/src/adapters/cursor.js +46 -0
- package/src/adapters/raw.js +34 -0
- package/src/adapters/registry.js +18 -0
- package/src/cli.js +106 -0
- package/src/commands/config.js +55 -0
- package/src/commands/export.js +77 -0
- package/src/commands/init.js +62 -0
- package/src/commands/learn.js +82 -0
- package/src/commands/logs.js +70 -0
- package/src/commands/plugin.js +100 -0
- package/src/commands/scan.js +71 -0
- package/src/commands/search.js +69 -0
- package/src/commands/status.js +74 -0
- package/src/commands/sync.js +84 -0
- package/src/core/config.js +47 -0
- package/src/core/constants.js +34 -0
- package/src/core/init.js +86 -0
- package/src/core/knowledge-loader.js +93 -0
- package/src/core/validators.js +123 -0
- package/src/plugins/manager.js +160 -0
- package/src/rag/chunker.js +86 -0
- package/src/rag/embedder.js +29 -0
- package/src/rag/manager.js +56 -0
- package/src/rag/store.js +77 -0
- package/src/workflows/parser.js +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 UDA Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# UDA (Universal Dev AI)
|
|
2
|
+
|
|
3
|
+
AI-agnostic context engineering + RAG CLI tool for game development.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Local RAG**: LanceDB + MiniLM embeddings - no API keys needed
|
|
8
|
+
- **Multi-AI Support**: Claude, Cursor, Windsurf, AGENTS.md, Raw export
|
|
9
|
+
- **Plugin System**: Git-based engine plugins (Unity, Godot, Unreal, etc.)
|
|
10
|
+
- **Workflow Engine**: YAML-defined AI-assisted workflows
|
|
11
|
+
- **Project Context**: Automatic knowledge base management
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g uda
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Initialize UDA in your project
|
|
23
|
+
uda init
|
|
24
|
+
|
|
25
|
+
# Scan and index knowledge
|
|
26
|
+
uda scan
|
|
27
|
+
|
|
28
|
+
# Search knowledge base
|
|
29
|
+
uda search "MonoBehaviour lifecycle"
|
|
30
|
+
|
|
31
|
+
# Export to your AI tool
|
|
32
|
+
uda export --format claude
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Commands
|
|
36
|
+
|
|
37
|
+
- `uda init` - Initialize UDA in current project
|
|
38
|
+
- `uda sync` - Generate AI tool files from knowledge base
|
|
39
|
+
- `uda search <query>` - Search knowledge base
|
|
40
|
+
- `uda learn <source>` - Teach knowledge to RAG
|
|
41
|
+
- `uda scan` - Scan project and index into RAG
|
|
42
|
+
- `uda plugin <action>` - Manage engine plugins
|
|
43
|
+
- `uda export --format <type>` - Export knowledge to specific format
|
|
44
|
+
- `uda status` - Show UDA system status
|
|
45
|
+
- `uda config [key] [value]` - Manage UDA settings
|
|
46
|
+
|
|
47
|
+
## Supported Formats
|
|
48
|
+
|
|
49
|
+
- **claude**: Generates CLAUDE.md and .claude/ skills
|
|
50
|
+
- **cursor**: Generates .cursorrules
|
|
51
|
+
- **agents-md**: Generates AGENTS.md
|
|
52
|
+
- **raw**: Generates full-context.md
|
|
53
|
+
|
|
54
|
+
## Plugin System
|
|
55
|
+
|
|
56
|
+
Install engine-specific knowledge:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uda plugin add https://github.com/user/uda-plugin-unity.git
|
|
60
|
+
uda plugin update unity
|
|
61
|
+
uda plugin update-all
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Project Structure
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
.uda/
|
|
68
|
+
├── config.json # UDA configuration
|
|
69
|
+
├── knowledge/
|
|
70
|
+
│ ├── engine/ # Engine plugins (Unity, Godot, etc.)
|
|
71
|
+
│ ├── project/ # Project-specific knowledge
|
|
72
|
+
│ └── community/ # Community contributions
|
|
73
|
+
├── workflows/ # AI-assisted workflows
|
|
74
|
+
├── agents/ # Specialized AI agents
|
|
75
|
+
├── state/
|
|
76
|
+
│ ├── current.md # Active work state
|
|
77
|
+
│ ├── features/ # Feature specifications
|
|
78
|
+
│ └── history/ # Completed work
|
|
79
|
+
└── rag/
|
|
80
|
+
└── lancedb/ # Vector database
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
MIT
|
package/bin/uda.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "uda-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Universal Dev AI — AI-agnostic context engineering + RAG for game development",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/cli.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"uda-cli": "./bin/uda.js",
|
|
9
|
+
"uda": "./bin/uda.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"src/",
|
|
14
|
+
"!src/**/*.test.js",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "node --test src/**/*.test.js",
|
|
20
|
+
"test:coverage": "node --test --experimental-test-coverage src/**/*.test.js",
|
|
21
|
+
"dev": "node bin/uda.js"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"ai",
|
|
25
|
+
"context-engineering",
|
|
26
|
+
"rag",
|
|
27
|
+
"game-dev",
|
|
28
|
+
"unity",
|
|
29
|
+
"godot",
|
|
30
|
+
"unreal",
|
|
31
|
+
"cli",
|
|
32
|
+
"vector-database",
|
|
33
|
+
"embeddings"
|
|
34
|
+
],
|
|
35
|
+
"author": "UDA Contributors",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@lancedb/lancedb": "^0.26.2",
|
|
42
|
+
"@xenova/transformers": "^2.17.2",
|
|
43
|
+
"commander": "^14.0.3",
|
|
44
|
+
"simple-git": "^3.32.3",
|
|
45
|
+
"yaml": "^2.8.2"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class AgentsMdAdapter {
|
|
2
|
+
get name() { return 'agents-md'; }
|
|
3
|
+
detect() { return true; }
|
|
4
|
+
|
|
5
|
+
generate(knowledge, workflows, agents) {
|
|
6
|
+
const lines = ['# AGENTS.md', '', '> Generated by UDA (Universal Dev AI)', ''];
|
|
7
|
+
|
|
8
|
+
if (knowledge.conventions?.length > 0) {
|
|
9
|
+
lines.push('## Coding Conventions');
|
|
10
|
+
knowledge.conventions.forEach(c => lines.push(`- ${c}`));
|
|
11
|
+
lines.push('');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (knowledge.decisions?.length > 0) {
|
|
15
|
+
lines.push('## Architecture');
|
|
16
|
+
knowledge.decisions.forEach(d => lines.push(`- ${d}`));
|
|
17
|
+
lines.push('');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return { 'AGENTS.md': lines.join('\n') };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/adapters/base.js
|
|
2
|
+
export class BaseAdapter {
|
|
3
|
+
constructor() {
|
|
4
|
+
if (new.target === BaseAdapter) {
|
|
5
|
+
throw new Error('BaseAdapter is abstract');
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
get name() { throw new Error('Not implemented'); }
|
|
10
|
+
|
|
11
|
+
detect(projectRoot) { throw new Error('Not implemented'); }
|
|
12
|
+
|
|
13
|
+
generate(knowledge, workflows, agents, projectRoot) { throw new Error('Not implemented'); }
|
|
14
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// src/adapters/claude.js
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
export class ClaudeAdapter {
|
|
6
|
+
get name() { return 'claude'; }
|
|
7
|
+
|
|
8
|
+
detect(projectRoot) {
|
|
9
|
+
return (
|
|
10
|
+
existsSync(join(projectRoot, 'CLAUDE.md')) ||
|
|
11
|
+
existsSync(join(projectRoot, '.claude'))
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
generate(knowledge, workflows, agents, projectRoot, capabilities = {}) {
|
|
16
|
+
const files = {};
|
|
17
|
+
files['CLAUDE.md'] = this._generateClaudeMd(knowledge, workflows, capabilities);
|
|
18
|
+
|
|
19
|
+
for (const wf of workflows) {
|
|
20
|
+
const skillPath = `.claude/commands/uda/${wf.name}.md`;
|
|
21
|
+
files[skillPath] = this._generateSkill(wf);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (const agent of agents) {
|
|
25
|
+
const agentPath = `.claude/agents/uda-${agent.name}.md`;
|
|
26
|
+
files[agentPath] = this._generateAgent(agent);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return files;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_generateClaudeMd(knowledge, workflows, capabilities) {
|
|
33
|
+
const lines = ['# CLAUDE.md', ''];
|
|
34
|
+
|
|
35
|
+
// Project info
|
|
36
|
+
if (knowledge.project) {
|
|
37
|
+
lines.push('## Project Info');
|
|
38
|
+
if (knowledge.project.name) lines.push(`- **Project**: ${knowledge.project.name}`);
|
|
39
|
+
if (knowledge.project.engine) lines.push(`- **Engine**: ${knowledge.project.engine}`);
|
|
40
|
+
if (knowledge.project.version) lines.push(`- **Version**: ${knowledge.project.version}`);
|
|
41
|
+
lines.push('');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (knowledge.conventions?.length > 0) {
|
|
45
|
+
lines.push('## Conventions');
|
|
46
|
+
for (const conv of knowledge.conventions) lines.push(`- ${conv}`);
|
|
47
|
+
lines.push('');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (knowledge.decisions?.length > 0) {
|
|
51
|
+
lines.push('## Architectural Decisions');
|
|
52
|
+
for (const dec of knowledge.decisions) lines.push(`- ${dec}`);
|
|
53
|
+
lines.push('');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// UDA AI-native instructions
|
|
57
|
+
lines.push('## UDA (Universal Dev AI)');
|
|
58
|
+
lines.push('');
|
|
59
|
+
lines.push('This project uses UDA for AI-assisted development. Follow these rules:');
|
|
60
|
+
lines.push('');
|
|
61
|
+
|
|
62
|
+
// Knowledge base instructions
|
|
63
|
+
lines.push('### Knowledge Base');
|
|
64
|
+
lines.push('- Engine knowledge is in `.uda/knowledge/`');
|
|
65
|
+
lines.push('- For engine questions, check these files FIRST');
|
|
66
|
+
lines.push('- For broader searches: `npx uda-cli search "query"`');
|
|
67
|
+
lines.push('');
|
|
68
|
+
|
|
69
|
+
// Log instructions (only if capability exists)
|
|
70
|
+
if (capabilities.logs) {
|
|
71
|
+
lines.push('### Console Logs');
|
|
72
|
+
lines.push(`- Log file: \`${capabilities.logs.source || '.uda/logs/console.jsonl'}\``);
|
|
73
|
+
lines.push('- Read logs: `npx uda-cli logs --errors --last 50`');
|
|
74
|
+
lines.push('- When user mentions logs, errors, or console issues, run this command');
|
|
75
|
+
lines.push('');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Available commands
|
|
79
|
+
lines.push('### Available Commands');
|
|
80
|
+
lines.push('- `npx uda-cli search "query"` — search knowledge base');
|
|
81
|
+
if (capabilities.logs) {
|
|
82
|
+
lines.push('- `npx uda-cli logs [--errors|--warnings] [--last N]` — read engine logs');
|
|
83
|
+
}
|
|
84
|
+
lines.push('- `npx uda-cli plugin add <repo>` — add new plugin');
|
|
85
|
+
lines.push('- `npx uda-cli sync` — regenerate AI tool files');
|
|
86
|
+
lines.push('');
|
|
87
|
+
|
|
88
|
+
// Workflow skills
|
|
89
|
+
if (workflows.length > 0) {
|
|
90
|
+
lines.push('### UDA Skills');
|
|
91
|
+
for (const wf of workflows) {
|
|
92
|
+
lines.push(`- \`/uda:${wf.name}\` — ${wf.description}`);
|
|
93
|
+
}
|
|
94
|
+
lines.push('');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_generateSkill(workflow) {
|
|
101
|
+
const lines = [
|
|
102
|
+
'---',
|
|
103
|
+
`description: ${workflow.description}`,
|
|
104
|
+
'---',
|
|
105
|
+
'',
|
|
106
|
+
`# ${workflow.name}`,
|
|
107
|
+
'',
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
if (workflow.steps) {
|
|
111
|
+
for (const step of workflow.steps) {
|
|
112
|
+
lines.push(`## ${step.name || step.id}`);
|
|
113
|
+
if (step.questions) {
|
|
114
|
+
for (const q of step.questions) {
|
|
115
|
+
lines.push(`- "${q}"`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
lines.push('');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return lines.join('\n');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
_generateAgent(agent) {
|
|
126
|
+
return [
|
|
127
|
+
'---',
|
|
128
|
+
`name: uda-${agent.name}`,
|
|
129
|
+
`description: ${agent.description}`,
|
|
130
|
+
`tools: ${agent.tools || 'Read, Grep, Glob'}`,
|
|
131
|
+
`model: ${agent.model || 'sonnet'}`,
|
|
132
|
+
'---',
|
|
133
|
+
'',
|
|
134
|
+
agent.prompt || `You are a ${agent.description}.`,
|
|
135
|
+
'',
|
|
136
|
+
].join('\n');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
export class CursorAdapter {
|
|
5
|
+
get name() { return 'cursor'; }
|
|
6
|
+
|
|
7
|
+
detect(projectRoot) {
|
|
8
|
+
return (
|
|
9
|
+
existsSync(join(projectRoot, '.cursorrules')) ||
|
|
10
|
+
existsSync(join(projectRoot, '.cursor'))
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
generate(knowledge, workflows, agents, projectRoot) {
|
|
15
|
+
const files = {};
|
|
16
|
+
files['.cursorrules'] = this._generateRules(knowledge);
|
|
17
|
+
return files;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_generateRules(knowledge) {
|
|
21
|
+
const lines = [];
|
|
22
|
+
|
|
23
|
+
if (knowledge.project) {
|
|
24
|
+
lines.push(`Project: ${knowledge.project.name || 'Unknown'}`);
|
|
25
|
+
lines.push(`Engine: ${knowledge.project.engine || 'Unknown'}`);
|
|
26
|
+
lines.push('');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (knowledge.conventions?.length > 0) {
|
|
30
|
+
lines.push('## Conventions');
|
|
31
|
+
for (const conv of knowledge.conventions) {
|
|
32
|
+
lines.push(`- ${conv}`);
|
|
33
|
+
}
|
|
34
|
+
lines.push('');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (knowledge.decisions?.length > 0) {
|
|
38
|
+
lines.push('## Architecture');
|
|
39
|
+
for (const dec of knowledge.decisions) {
|
|
40
|
+
lines.push(`- ${dec}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class RawAdapter {
|
|
2
|
+
get name() { return 'raw'; }
|
|
3
|
+
detect() { return true; } // always available
|
|
4
|
+
|
|
5
|
+
generate(knowledge, workflows, agents) {
|
|
6
|
+
const lines = ['# Project Context (UDA Generated)', ''];
|
|
7
|
+
|
|
8
|
+
if (knowledge.project) {
|
|
9
|
+
lines.push(`## Project: ${knowledge.project.name || 'Unknown'}`);
|
|
10
|
+
lines.push(`Engine: ${knowledge.project.engine || 'N/A'}`);
|
|
11
|
+
lines.push('');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (knowledge.conventions?.length > 0) {
|
|
15
|
+
lines.push('## Conventions');
|
|
16
|
+
knowledge.conventions.forEach(c => lines.push(`- ${c}`));
|
|
17
|
+
lines.push('');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (knowledge.decisions?.length > 0) {
|
|
21
|
+
lines.push('## Decisions');
|
|
22
|
+
knowledge.decisions.forEach(d => lines.push(`- ${d}`));
|
|
23
|
+
lines.push('');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (workflows.length > 0) {
|
|
27
|
+
lines.push('## Available Workflows');
|
|
28
|
+
workflows.forEach(w => lines.push(`- **${w.name}**: ${w.description}`));
|
|
29
|
+
lines.push('');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { '.uda/.generated/full-context.md': lines.join('\n') };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/adapters/registry.js
|
|
2
|
+
const adapters = [];
|
|
3
|
+
|
|
4
|
+
export function registerAdapter(adapter) {
|
|
5
|
+
adapters.push(adapter);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function getAdapter(name) {
|
|
9
|
+
return adapters.find(a => a.name === name);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function detectAdapters(projectRoot) {
|
|
13
|
+
return adapters.filter(a => a.detect(projectRoot));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getAllAdapters() {
|
|
17
|
+
return [...adapters];
|
|
18
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { handleLearn } from './commands/learn.js';
|
|
3
|
+
import { handleSearch } from './commands/search.js';
|
|
4
|
+
import { handleScan } from './commands/scan.js';
|
|
5
|
+
import { handleSync } from './commands/sync.js';
|
|
6
|
+
import { handleInit } from './commands/init.js';
|
|
7
|
+
import { handlePluginAdd, handlePluginList, handlePluginRemove } from './commands/plugin.js';
|
|
8
|
+
import { handleStatus } from './commands/status.js';
|
|
9
|
+
import { handleConfig } from './commands/config.js';
|
|
10
|
+
import { handlePluginUpdate } from './commands/plugin.js';
|
|
11
|
+
import { handleExport } from './commands/export.js';
|
|
12
|
+
import { handleLogs } from './commands/logs.js';
|
|
13
|
+
|
|
14
|
+
export function createCli() {
|
|
15
|
+
const program = new Command();
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.command('init')
|
|
19
|
+
.description('Initialize UDA in current project')
|
|
20
|
+
.option('-e, --engine <name>', 'Engine plugin to install (e.g. unity)')
|
|
21
|
+
.action(handleInit);
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.command('sync')
|
|
25
|
+
.description('Generate AI tool files from knowledge base')
|
|
26
|
+
.action(handleSync);
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.command('search <query>')
|
|
30
|
+
.description('Search knowledge base')
|
|
31
|
+
.option('-t, --top <number>', 'Number of results', '5')
|
|
32
|
+
.option('-f, --format <format>', 'Output format (terminal, md, clipboard)', 'terminal')
|
|
33
|
+
.action(handleSearch);
|
|
34
|
+
|
|
35
|
+
program
|
|
36
|
+
.command('learn <source>')
|
|
37
|
+
.description('Teach knowledge to RAG')
|
|
38
|
+
.option('--type <type>', 'Knowledge type (bug-fix, feature, pattern, knowledge)', 'knowledge')
|
|
39
|
+
.option('--tags <tags>', 'Comma-separated tags')
|
|
40
|
+
.action(handleLearn);
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.command('scan')
|
|
44
|
+
.description('Scan project and index into RAG')
|
|
45
|
+
.action(handleScan);
|
|
46
|
+
|
|
47
|
+
const pluginCmd = program
|
|
48
|
+
.command('plugin')
|
|
49
|
+
.description('Manage engine plugins');
|
|
50
|
+
|
|
51
|
+
pluginCmd
|
|
52
|
+
.command('add <repo>')
|
|
53
|
+
.description('Install plugin from git repo')
|
|
54
|
+
.action(handlePluginAdd);
|
|
55
|
+
|
|
56
|
+
pluginCmd
|
|
57
|
+
.command('list')
|
|
58
|
+
.description('List installed plugins')
|
|
59
|
+
.action(handlePluginList);
|
|
60
|
+
|
|
61
|
+
pluginCmd
|
|
62
|
+
.command('remove <name>')
|
|
63
|
+
.description('Remove a plugin')
|
|
64
|
+
.action(handlePluginRemove);
|
|
65
|
+
|
|
66
|
+
pluginCmd
|
|
67
|
+
.command('update [name]')
|
|
68
|
+
.description('Update plugin(s)')
|
|
69
|
+
.action(handlePluginUpdate);
|
|
70
|
+
|
|
71
|
+
pluginCmd
|
|
72
|
+
.command('create <name>')
|
|
73
|
+
.description('Scaffold a new plugin')
|
|
74
|
+
.action(async (name) => {
|
|
75
|
+
console.log('uda plugin create — not yet implemented');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
program
|
|
79
|
+
.command('export')
|
|
80
|
+
.description('Export knowledge to specific format')
|
|
81
|
+
.requiredOption('-f, --format <format>', 'Output format (claude, cursor, agents-md, raw)')
|
|
82
|
+
.option('-o, --output <path>', 'Output directory')
|
|
83
|
+
.action(handleExport);
|
|
84
|
+
|
|
85
|
+
program
|
|
86
|
+
.command('status')
|
|
87
|
+
.description('Show UDA system status')
|
|
88
|
+
.action(handleStatus);
|
|
89
|
+
|
|
90
|
+
program
|
|
91
|
+
.command('config')
|
|
92
|
+
.description('Manage UDA settings')
|
|
93
|
+
.argument('[key]', 'Config key to get/set')
|
|
94
|
+
.argument('[value]', 'Value to set')
|
|
95
|
+
.action(handleConfig);
|
|
96
|
+
|
|
97
|
+
program
|
|
98
|
+
.command('logs')
|
|
99
|
+
.description('Read engine console logs')
|
|
100
|
+
.option('-e, --errors', 'Show only errors')
|
|
101
|
+
.option('-w, --warnings', 'Show only warnings')
|
|
102
|
+
.option('-l, --last <count>', 'Show last N entries')
|
|
103
|
+
.action(handleLogs);
|
|
104
|
+
|
|
105
|
+
return program;
|
|
106
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/commands/config.js
|
|
2
|
+
import { loadConfig, saveConfig, getConfigValue, setConfigValue } from '../core/config.js';
|
|
3
|
+
import { validateConfigKey } from '../core/validators.js';
|
|
4
|
+
|
|
5
|
+
export async function handleConfig(key, value) {
|
|
6
|
+
const root = process.cwd();
|
|
7
|
+
|
|
8
|
+
// Validate config key format if provided
|
|
9
|
+
if (key) {
|
|
10
|
+
const v = validateConfigKey(key);
|
|
11
|
+
if (!v.valid) {
|
|
12
|
+
console.error(`✘ ${v.error}`);
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let config;
|
|
19
|
+
try {
|
|
20
|
+
config = await loadConfig(root);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error(`✘ Failed to load config: ${err.message}`);
|
|
23
|
+
console.error(' Run `uda init` to initialize the project.');
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// No key — list all config
|
|
29
|
+
if (!key) {
|
|
30
|
+
console.log(JSON.stringify(config, null, 2));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Key only — get value
|
|
35
|
+
if (value === undefined) {
|
|
36
|
+
const result = getConfigValue(config, key);
|
|
37
|
+
if (result === undefined) {
|
|
38
|
+
console.error(`✘ Key "${key}" not found`);
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
console.log(typeof result === 'object' ? JSON.stringify(result, null, 2) : result);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Key + value — set value
|
|
47
|
+
try {
|
|
48
|
+
setConfigValue(config, key, value);
|
|
49
|
+
await saveConfig(root, config);
|
|
50
|
+
console.log(`✔ ${key} = ${JSON.stringify(getConfigValue(config, key))}`);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error(`✘ Failed to save config: ${err.message}`);
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// src/commands/export.js
|
|
2
|
+
import { writeFile, mkdir } from 'fs/promises';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { udaPaths } from '../core/constants.js';
|
|
5
|
+
import { loadConfig } from '../core/config.js';
|
|
6
|
+
import { loadKnowledge, loadWorkflows, loadAgents } from '../core/knowledge-loader.js';
|
|
7
|
+
import { ClaudeAdapter } from '../adapters/claude.js';
|
|
8
|
+
import { CursorAdapter } from '../adapters/cursor.js';
|
|
9
|
+
import { RawAdapter } from '../adapters/raw.js';
|
|
10
|
+
import { AgentsMdAdapter } from '../adapters/agents-md.js';
|
|
11
|
+
import { validateExportFormat } from '../core/validators.js';
|
|
12
|
+
|
|
13
|
+
const ADAPTER_MAP = {
|
|
14
|
+
claude: () => new ClaudeAdapter(),
|
|
15
|
+
cursor: () => new CursorAdapter(),
|
|
16
|
+
'agents-md': () => new AgentsMdAdapter(),
|
|
17
|
+
raw: () => new RawAdapter(),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export async function handleExport(options) {
|
|
21
|
+
const root = process.cwd();
|
|
22
|
+
const paths = udaPaths(root);
|
|
23
|
+
const format = options.format;
|
|
24
|
+
|
|
25
|
+
// Validate format
|
|
26
|
+
const fv = validateExportFormat(format);
|
|
27
|
+
if (!fv.valid) {
|
|
28
|
+
console.error(`✘ ${fv.error}`);
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const createAdapter = ADAPTER_MAP[format];
|
|
34
|
+
if (!createAdapter) {
|
|
35
|
+
console.error(`✘ Unknown format "${format}". Available: ${Object.keys(ADAPTER_MAP).join(', ')}`);
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let adapter, knowledge, workflows, agents;
|
|
41
|
+
try {
|
|
42
|
+
adapter = createAdapter();
|
|
43
|
+
knowledge = await loadKnowledge(paths);
|
|
44
|
+
workflows = await loadWorkflows(paths);
|
|
45
|
+
agents = await loadAgents(paths);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error(`✘ Failed to load knowledge base: ${err.message}`);
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let files;
|
|
53
|
+
try {
|
|
54
|
+
files = adapter.generate(knowledge, workflows, agents, root);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.error(`✘ Failed to generate ${format} output: ${err.message}`);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Determine output base directory
|
|
62
|
+
const outputBase = options.output || join(paths.generated, format);
|
|
63
|
+
|
|
64
|
+
let totalFiles = 0;
|
|
65
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
66
|
+
const fullPath = join(outputBase, filePath);
|
|
67
|
+
try {
|
|
68
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
69
|
+
await writeFile(fullPath, content);
|
|
70
|
+
totalFiles++;
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error(` ✘ Failed to write ${filePath}: ${err.message}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(`✔ Exported ${totalFiles} files (${format}) → ${outputBase}`);
|
|
77
|
+
}
|