nova-terminal-assistant 0.1.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.
Potentially problematic release.
This version of nova-terminal-assistant might be problematic. Click here for more details.
- package/README.md +358 -0
- package/bin/nova +38 -0
- package/bin/nova.js +12 -0
- package/package.json +67 -0
- package/src/cli/commands/SmartCompletion.ts +458 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/startup/IFlowRepl.ts +212 -0
- package/src/cli/startup/InkBasedRepl.ts +1056 -0
- package/src/cli/startup/InteractiveRepl.ts +2833 -0
- package/src/cli/startup/NovaApp.ts +1861 -0
- package/src/cli/startup/index.ts +4 -0
- package/src/cli/startup/parseArgs.ts +293 -0
- package/src/cli/test-modules.ts +27 -0
- package/src/cli/ui/IFlowDropdown.ts +425 -0
- package/src/cli/ui/ModernReplUI.ts +276 -0
- package/src/cli/ui/SimpleSelector2.ts +215 -0
- package/src/cli/ui/components/ConfirmDialog.ts +176 -0
- package/src/cli/ui/components/ErrorPanel.ts +364 -0
- package/src/cli/ui/components/InkAppRunner.tsx +67 -0
- package/src/cli/ui/components/InkComponents.tsx +613 -0
- package/src/cli/ui/components/NovaInkApp.tsx +312 -0
- package/src/cli/ui/components/ProgressBar.ts +177 -0
- package/src/cli/ui/components/ProgressIndicator.ts +298 -0
- package/src/cli/ui/components/QuickActions.ts +396 -0
- package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
- package/src/cli/ui/components/StatusBar.ts +194 -0
- package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
- package/src/cli/ui/components/index.ts +27 -0
- package/src/cli/ui/ink-prototype.tsx +347 -0
- package/src/cli/utils/CliUI.ts +336 -0
- package/src/cli/utils/CompletionHelper.ts +388 -0
- package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
- package/src/cli/utils/EnhancedCompleter.ts +513 -0
- package/src/cli/utils/ErrorEnhancer.ts +429 -0
- package/src/cli/utils/OutputFormatter.ts +193 -0
- package/src/cli/utils/index.ts +9 -0
- package/src/core/agents/AgentOrchestrator.ts +515 -0
- package/src/core/agents/index.ts +17 -0
- package/src/core/audit/AuditLogger.ts +509 -0
- package/src/core/audit/index.ts +11 -0
- package/src/core/auth/AuthManager.d.ts.map +1 -0
- package/src/core/auth/AuthManager.ts +138 -0
- package/src/core/auth/index.d.ts.map +1 -0
- package/src/core/auth/index.ts +2 -0
- package/src/core/config/ConfigManager.d.ts.map +1 -0
- package/src/core/config/ConfigManager.test.ts +183 -0
- package/src/core/config/ConfigManager.ts +1219 -0
- package/src/core/config/index.d.ts.map +1 -0
- package/src/core/config/index.ts +1 -0
- package/src/core/context/ContextBuilder.d.ts.map +1 -0
- package/src/core/context/ContextBuilder.ts +171 -0
- package/src/core/context/ContextCompressor.d.ts.map +1 -0
- package/src/core/context/ContextCompressor.ts +642 -0
- package/src/core/context/LayeredMemoryManager.ts +657 -0
- package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
- package/src/core/context/MemoryDiscovery.ts +175 -0
- package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
- package/src/core/context/defaultSystemPrompt.ts +35 -0
- package/src/core/context/index.d.ts.map +1 -0
- package/src/core/context/index.ts +22 -0
- package/src/core/extensions/SkillGenerator.ts +421 -0
- package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
- package/src/core/extensions/SkillInstaller.ts +257 -0
- package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
- package/src/core/extensions/SkillRegistry.ts +361 -0
- package/src/core/extensions/SkillValidator.ts +525 -0
- package/src/core/extensions/index.ts +15 -0
- package/src/core/index.d.ts.map +1 -0
- package/src/core/index.ts +42 -0
- package/src/core/mcp/McpManager.d.ts.map +1 -0
- package/src/core/mcp/McpManager.ts +632 -0
- package/src/core/mcp/index.d.ts.map +1 -0
- package/src/core/mcp/index.ts +2 -0
- package/src/core/model/ModelClient.d.ts.map +1 -0
- package/src/core/model/ModelClient.ts +217 -0
- package/src/core/model/ModelConnectionTester.ts +363 -0
- package/src/core/model/ModelValidator.ts +348 -0
- package/src/core/model/index.d.ts.map +1 -0
- package/src/core/model/index.ts +6 -0
- package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
- package/src/core/model/providers/AnthropicProvider.ts +279 -0
- package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
- package/src/core/model/providers/CodingPlanProvider.ts +210 -0
- package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
- package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
- package/src/core/model/providers/OllamaManager.ts +201 -0
- package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaProvider.ts +73 -0
- package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
- package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAIProvider.ts +29 -0
- package/src/core/model/providers/index.d.ts.map +1 -0
- package/src/core/model/providers/index.ts +12 -0
- package/src/core/model/types.d.ts.map +1 -0
- package/src/core/model/types.ts +77 -0
- package/src/core/security/ApprovalManager.d.ts.map +1 -0
- package/src/core/security/ApprovalManager.ts +174 -0
- package/src/core/security/FileFilter.d.ts.map +1 -0
- package/src/core/security/FileFilter.ts +141 -0
- package/src/core/security/HookExecutor.d.ts.map +1 -0
- package/src/core/security/HookExecutor.ts +178 -0
- package/src/core/security/SandboxExecutor.ts +447 -0
- package/src/core/security/index.d.ts.map +1 -0
- package/src/core/security/index.ts +8 -0
- package/src/core/session/AgentLoop.d.ts.map +1 -0
- package/src/core/session/AgentLoop.ts +501 -0
- package/src/core/session/SessionManager.d.ts.map +1 -0
- package/src/core/session/SessionManager.test.ts +183 -0
- package/src/core/session/SessionManager.ts +460 -0
- package/src/core/session/index.d.ts.map +1 -0
- package/src/core/session/index.ts +3 -0
- package/src/core/telemetry/Telemetry.d.ts.map +1 -0
- package/src/core/telemetry/Telemetry.ts +90 -0
- package/src/core/telemetry/TelemetryService.ts +531 -0
- package/src/core/telemetry/index.d.ts.map +1 -0
- package/src/core/telemetry/index.ts +12 -0
- package/src/core/testing/AutoFixer.ts +385 -0
- package/src/core/testing/ErrorAnalyzer.ts +499 -0
- package/src/core/testing/TestRunner.ts +265 -0
- package/src/core/testing/agent-cli-tests.ts +538 -0
- package/src/core/testing/index.ts +11 -0
- package/src/core/tools/ToolRegistry.d.ts.map +1 -0
- package/src/core/tools/ToolRegistry.test.ts +206 -0
- package/src/core/tools/ToolRegistry.ts +260 -0
- package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/EditFileTool.ts +97 -0
- package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
- package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/MemoryTool.ts +102 -0
- package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/ReadFileTool.ts +58 -0
- package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchContentTool.ts +94 -0
- package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchFileTool.ts +61 -0
- package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
- package/src/core/tools/impl/ShellTool.ts +118 -0
- package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
- package/src/core/tools/impl/TaskTool.ts +207 -0
- package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
- package/src/core/tools/impl/TodoTool.ts +122 -0
- package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebFetchTool.ts +103 -0
- package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebSearchTool.ts +89 -0
- package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/WriteFileTool.ts +49 -0
- package/src/core/tools/impl/index.d.ts.map +1 -0
- package/src/core/tools/impl/index.ts +16 -0
- package/src/core/tools/index.d.ts.map +1 -0
- package/src/core/tools/index.ts +7 -0
- package/src/core/tools/schemas/execution.d.ts.map +1 -0
- package/src/core/tools/schemas/execution.ts +42 -0
- package/src/core/tools/schemas/file.d.ts.map +1 -0
- package/src/core/tools/schemas/file.ts +119 -0
- package/src/core/tools/schemas/index.d.ts.map +1 -0
- package/src/core/tools/schemas/index.ts +11 -0
- package/src/core/tools/schemas/memory.d.ts.map +1 -0
- package/src/core/tools/schemas/memory.ts +52 -0
- package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
- package/src/core/tools/schemas/orchestration.ts +44 -0
- package/src/core/tools/schemas/search.d.ts.map +1 -0
- package/src/core/tools/schemas/search.ts +112 -0
- package/src/core/tools/schemas/todo.d.ts.map +1 -0
- package/src/core/tools/schemas/todo.ts +32 -0
- package/src/core/tools/schemas/web.d.ts.map +1 -0
- package/src/core/tools/schemas/web.ts +86 -0
- package/src/core/types/config.d.ts.map +1 -0
- package/src/core/types/config.ts +200 -0
- package/src/core/types/errors.d.ts.map +1 -0
- package/src/core/types/errors.ts +204 -0
- package/src/core/types/index.d.ts.map +1 -0
- package/src/core/types/index.ts +8 -0
- package/src/core/types/session.d.ts.map +1 -0
- package/src/core/types/session.ts +216 -0
- package/src/core/types/tools.d.ts.map +1 -0
- package/src/core/types/tools.ts +157 -0
- package/src/core/utils/CheckpointManager.d.ts.map +1 -0
- package/src/core/utils/CheckpointManager.ts +327 -0
- package/src/core/utils/Logger.d.ts.map +1 -0
- package/src/core/utils/Logger.ts +98 -0
- package/src/core/utils/RetryManager.ts +471 -0
- package/src/core/utils/TokenCounter.d.ts.map +1 -0
- package/src/core/utils/TokenCounter.ts +414 -0
- package/src/core/utils/VectorMemoryStore.ts +440 -0
- package/src/core/utils/helpers.d.ts.map +1 -0
- package/src/core/utils/helpers.ts +89 -0
- package/src/core/utils/index.d.ts.map +1 -0
- package/src/core/utils/index.ts +19 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// SkillValidator - Validate skill definitions for quality and completeness
|
|
3
|
+
// Reference: WorkBuddy skill validation pipeline
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
import type { SkillDefinition, SkillMetadata } from './SkillRegistry.js';
|
|
7
|
+
import { readFile, stat, access } from 'node:fs/promises';
|
|
8
|
+
import { join, resolve } from 'node:path';
|
|
9
|
+
|
|
10
|
+
export interface ValidationIssue {
|
|
11
|
+
severity: 'error' | 'warning' | 'info';
|
|
12
|
+
code: string;
|
|
13
|
+
message: string;
|
|
14
|
+
suggestion?: string;
|
|
15
|
+
line?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ValidationResult {
|
|
19
|
+
valid: boolean;
|
|
20
|
+
issues: ValidationIssue[];
|
|
21
|
+
score: number; // 0-100 quality score
|
|
22
|
+
checkedAt: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Validation rule definitions
|
|
26
|
+
const REQUIRED_METADATA_FIELDS: (keyof SkillMetadata)[] = ['name', 'description', 'version'];
|
|
27
|
+
const RECOMMENDED_METADATA_FIELDS: (keyof SkillMetadata)[] = ['tags', 'author', 'createdAt'];
|
|
28
|
+
const MIN_CONTENT_LENGTH = 100;
|
|
29
|
+
const RECOMMENDED_CONTENT_LENGTH = 500;
|
|
30
|
+
const MAX_CONTENT_LENGTH = 50000;
|
|
31
|
+
|
|
32
|
+
export class SkillValidator {
|
|
33
|
+
/**
|
|
34
|
+
* Validate a skill definition comprehensively.
|
|
35
|
+
*/
|
|
36
|
+
async validate(skill: SkillDefinition): Promise<ValidationResult> {
|
|
37
|
+
const issues: ValidationIssue[] = [];
|
|
38
|
+
|
|
39
|
+
// Step 1: Metadata validation
|
|
40
|
+
this.validateMetadata(skill.metadata, issues);
|
|
41
|
+
|
|
42
|
+
// Step 2: Content validation
|
|
43
|
+
this.validateContent(skill.content, issues);
|
|
44
|
+
|
|
45
|
+
// Step 3: Structural validation
|
|
46
|
+
this.validateStructure(skill.content, issues);
|
|
47
|
+
|
|
48
|
+
// Step 4: Security validation
|
|
49
|
+
this.validateSecurity(skill.content, issues);
|
|
50
|
+
|
|
51
|
+
// Step 5: Quality scoring
|
|
52
|
+
this.validateQuality(skill, issues);
|
|
53
|
+
|
|
54
|
+
const hasErrors = issues.some((i) => i.severity === 'error');
|
|
55
|
+
const score = this.calculateScore(issues);
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
valid: !hasErrors,
|
|
59
|
+
issues,
|
|
60
|
+
score,
|
|
61
|
+
checkedAt: new Date().toISOString(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Quick validation - only checks for blocking errors.
|
|
67
|
+
*/
|
|
68
|
+
quickValidate(skill: SkillDefinition): ValidationIssue[] {
|
|
69
|
+
const issues: ValidationIssue[] = [];
|
|
70
|
+
|
|
71
|
+
// Only check critical fields
|
|
72
|
+
if (!skill.metadata.name) {
|
|
73
|
+
issues.push({
|
|
74
|
+
severity: 'error',
|
|
75
|
+
code: 'MISSING_NAME',
|
|
76
|
+
message: 'Skill name is required',
|
|
77
|
+
suggestion: 'Add a unique kebab-case name for this skill',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!skill.metadata.description) {
|
|
82
|
+
issues.push({
|
|
83
|
+
severity: 'error',
|
|
84
|
+
code: 'MISSING_DESCRIPTION',
|
|
85
|
+
message: 'Skill description is required',
|
|
86
|
+
suggestion: 'Add a one-line description of what this skill does',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!skill.content || skill.content.length < 20) {
|
|
91
|
+
issues.push({
|
|
92
|
+
severity: 'error',
|
|
93
|
+
code: 'EMPTY_CONTENT',
|
|
94
|
+
message: 'Skill content is too short or empty',
|
|
95
|
+
suggestion: 'Add detailed instructions for the AI agent to follow',
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return issues;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Validate a skill file on disk.
|
|
104
|
+
*/
|
|
105
|
+
async validateFile(filePath: string): Promise<ValidationResult> {
|
|
106
|
+
try {
|
|
107
|
+
const content = await readFile(filePath, 'utf-8');
|
|
108
|
+
|
|
109
|
+
// Parse as markdown - extract YAML frontmatter if present
|
|
110
|
+
const { metadata, body } = this.parseSkillFile(content, filePath);
|
|
111
|
+
|
|
112
|
+
const skill: SkillDefinition = {
|
|
113
|
+
metadata,
|
|
114
|
+
content: body,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return this.validate(skill);
|
|
118
|
+
} catch (err: any) {
|
|
119
|
+
return {
|
|
120
|
+
valid: false,
|
|
121
|
+
issues: [
|
|
122
|
+
{
|
|
123
|
+
severity: 'error',
|
|
124
|
+
code: 'FILE_READ_ERROR',
|
|
125
|
+
message: `Cannot read skill file: ${err.message}`,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
score: 0,
|
|
129
|
+
checkedAt: new Date().toISOString(),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Compare two versions of a skill and report differences.
|
|
136
|
+
*/
|
|
137
|
+
async diff(oldSkill: SkillDefinition, newSkill: SkillDefinition): Promise<ValidationIssue[]> {
|
|
138
|
+
const issues: ValidationIssue[] = [];
|
|
139
|
+
|
|
140
|
+
// Version check
|
|
141
|
+
if (oldSkill.metadata.version && newSkill.metadata.version) {
|
|
142
|
+
if (this.compareVersions(oldSkill.metadata.version, newSkill.metadata.version) > 0) {
|
|
143
|
+
issues.push({
|
|
144
|
+
severity: 'warning',
|
|
145
|
+
code: 'VERSION_DOWNGRADE',
|
|
146
|
+
message: `Version decreased from ${oldSkill.metadata.version} to ${newSkill.metadata.version}`,
|
|
147
|
+
suggestion: 'Version should only increase. Consider bumping the version number.',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Content size change
|
|
153
|
+
const sizeDiff = newSkill.content.length - oldSkill.content.length;
|
|
154
|
+
if (Math.abs(sizeDiff) > oldSkill.content.length * 0.5 && sizeDiff < 0) {
|
|
155
|
+
issues.push({
|
|
156
|
+
severity: 'warning',
|
|
157
|
+
code: 'SIGNIFICANT_SHRINKAGE',
|
|
158
|
+
message: `Content reduced by ${Math.abs(sizeDiff)} characters (${Math.round(Math.abs(sizeDiff) / oldSkill.content.length * 100)}%)`,
|
|
159
|
+
suggestion: 'Verify that important content was not accidentally removed',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Name change
|
|
164
|
+
if (oldSkill.metadata.name !== newSkill.metadata.name) {
|
|
165
|
+
issues.push({
|
|
166
|
+
severity: 'info',
|
|
167
|
+
code: 'NAME_CHANGED',
|
|
168
|
+
message: `Skill renamed from "${oldSkill.metadata.name}" to "${newSkill.metadata.name}"`,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Tag changes
|
|
173
|
+
const oldTags = new Set(oldSkill.metadata.tags || []);
|
|
174
|
+
const newTags = new Set(newSkill.metadata.tags || []);
|
|
175
|
+
const removedTags = [...oldTags].filter((t) => !newTags.has(t));
|
|
176
|
+
const addedTags = [...newTags].filter((t) => !oldTags.has(t));
|
|
177
|
+
|
|
178
|
+
if (removedTags.length > 0) {
|
|
179
|
+
issues.push({
|
|
180
|
+
severity: 'info',
|
|
181
|
+
code: 'TAGS_REMOVED',
|
|
182
|
+
message: `Tags removed: ${removedTags.join(', ')}`,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (addedTags.length > 0) {
|
|
186
|
+
issues.push({
|
|
187
|
+
severity: 'info',
|
|
188
|
+
code: 'TAGS_ADDED',
|
|
189
|
+
message: `Tags added: ${addedTags.join(', ')}`,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return issues;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// --- Private validation methods ---
|
|
197
|
+
|
|
198
|
+
private validateMetadata(metadata: SkillMetadata, issues: ValidationIssue[]): void {
|
|
199
|
+
// Required fields
|
|
200
|
+
for (const field of REQUIRED_METADATA_FIELDS) {
|
|
201
|
+
if (!metadata[field]) {
|
|
202
|
+
issues.push({
|
|
203
|
+
severity: 'error',
|
|
204
|
+
code: `MISSING_${field.toUpperCase()}`,
|
|
205
|
+
message: `Required metadata field "${field}" is missing or empty`,
|
|
206
|
+
suggestion: `Add "${field}" to the skill metadata`,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Recommended fields
|
|
212
|
+
for (const field of RECOMMENDED_METADATA_FIELDS) {
|
|
213
|
+
if (!metadata[field]) {
|
|
214
|
+
issues.push({
|
|
215
|
+
severity: 'info',
|
|
216
|
+
code: `MISSING_RECOMMENDED_${field.toUpperCase()}`,
|
|
217
|
+
message: `Recommended metadata field "${field}" is missing`,
|
|
218
|
+
suggestion: `Consider adding "${field}" for better discoverability`,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Name format validation
|
|
224
|
+
if (metadata.name) {
|
|
225
|
+
if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(metadata.name) && metadata.name.length > 1) {
|
|
226
|
+
issues.push({
|
|
227
|
+
severity: 'warning',
|
|
228
|
+
code: 'INVALID_NAME_FORMAT',
|
|
229
|
+
message: `Skill name "${metadata.name}" should be lowercase kebab-case`,
|
|
230
|
+
suggestion: 'Use only lowercase letters, numbers, and hyphens. Example: "react-component-generator"',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (metadata.name.length > 50) {
|
|
235
|
+
issues.push({
|
|
236
|
+
severity: 'warning',
|
|
237
|
+
code: 'NAME_TOO_LONG',
|
|
238
|
+
message: `Skill name is ${metadata.name.length} characters, max 50 recommended`,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Description length
|
|
244
|
+
if (metadata.description) {
|
|
245
|
+
if (metadata.description.length < 10) {
|
|
246
|
+
issues.push({
|
|
247
|
+
severity: 'warning',
|
|
248
|
+
code: 'DESCRIPTION_TOO_SHORT',
|
|
249
|
+
message: 'Description should be at least 10 characters for better discoverability',
|
|
250
|
+
});
|
|
251
|
+
} else if (metadata.description.length > 200) {
|
|
252
|
+
issues.push({
|
|
253
|
+
severity: 'info',
|
|
254
|
+
code: 'DESCRIPTION_TOO_LONG',
|
|
255
|
+
message: 'Description is long, consider keeping it under 200 characters for display',
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Version format
|
|
261
|
+
if (metadata.version) {
|
|
262
|
+
if (!/^\d+\.\d+\.\d+/.test(metadata.version)) {
|
|
263
|
+
issues.push({
|
|
264
|
+
severity: 'warning',
|
|
265
|
+
code: 'INVALID_VERSION_FORMAT',
|
|
266
|
+
message: `Version "${metadata.version}" does not follow semver (x.y.z)`,
|
|
267
|
+
suggestion: 'Use semantic versioning like "1.0.0", "0.2.1"',
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private validateContent(content: string, issues: ValidationIssue[]): void {
|
|
274
|
+
if (!content || content.length === 0) {
|
|
275
|
+
issues.push({
|
|
276
|
+
severity: 'error',
|
|
277
|
+
code: 'EMPTY_CONTENT',
|
|
278
|
+
message: 'Skill content is empty',
|
|
279
|
+
suggestion: 'Add instructions, patterns, and examples for the AI agent',
|
|
280
|
+
});
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Minimum content length
|
|
285
|
+
if (content.length < MIN_CONTENT_LENGTH) {
|
|
286
|
+
issues.push({
|
|
287
|
+
severity: 'warning',
|
|
288
|
+
code: 'CONTENT_TOO_SHORT',
|
|
289
|
+
message: `Content is ${content.length} characters, minimum recommended is ${MIN_CONTENT_LENGTH}`,
|
|
290
|
+
suggestion: 'Add more detailed instructions and examples to make the skill more useful',
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Maximum content length
|
|
295
|
+
if (content.length > MAX_CONTENT_LENGTH) {
|
|
296
|
+
issues.push({
|
|
297
|
+
severity: 'warning',
|
|
298
|
+
code: 'CONTENT_TOO_LONG',
|
|
299
|
+
message: `Content is ${content.length} characters, max recommended is ${MAX_CONTENT_LENGTH}`,
|
|
300
|
+
suggestion: 'Consider splitting into multiple specialized skills or trimming verbose sections',
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Check for placeholder content
|
|
305
|
+
const placeholderPatterns = [
|
|
306
|
+
/TODO.*implement/i,
|
|
307
|
+
/placeholder/i,
|
|
308
|
+
/FIXME/i,
|
|
309
|
+
/\[insert.*here\]/i,
|
|
310
|
+
/lorem ipsum/i,
|
|
311
|
+
/your content here/i,
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
for (const pattern of placeholderPatterns) {
|
|
315
|
+
if (pattern.test(content)) {
|
|
316
|
+
issues.push({
|
|
317
|
+
severity: 'error',
|
|
318
|
+
code: 'PLACEHOLDER_CONTENT',
|
|
319
|
+
message: 'Content contains placeholder text that should be filled in',
|
|
320
|
+
suggestion: 'Replace all TODO/placeholder markers with actual instructions',
|
|
321
|
+
});
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private validateStructure(content: string, issues: ValidationIssue[]): void {
|
|
328
|
+
// Check for markdown headers (indicates good structure)
|
|
329
|
+
const headerMatches = content.match(/^#{1,3}\s+.+$/gm);
|
|
330
|
+
if (!headerMatches || headerMatches.length < 2) {
|
|
331
|
+
issues.push({
|
|
332
|
+
severity: 'info',
|
|
333
|
+
code: 'POOR_STRUCTURE',
|
|
334
|
+
message: 'Content lacks section headers for organization',
|
|
335
|
+
suggestion: 'Use markdown headers (##) to organize content into clear sections',
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Check for code examples
|
|
340
|
+
const codeBlockMatches = content.match(/```[\s\S]*?```/g);
|
|
341
|
+
if (!codeBlockMatches || codeBlockMatches.length === 0) {
|
|
342
|
+
issues.push({
|
|
343
|
+
severity: 'info',
|
|
344
|
+
code: 'NO_CODE_EXAMPLES',
|
|
345
|
+
message: 'No code examples found in the skill',
|
|
346
|
+
suggestion: 'Include code examples to help the agent understand the expected patterns',
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Check for numbered or bullet lists (indicates step-by-step instructions)
|
|
351
|
+
const listMatches = content.match(/(?:^[\d]+\.\s|^\s*[-*]\s)/gm);
|
|
352
|
+
if (!listMatches || listMatches.length < 2) {
|
|
353
|
+
issues.push({
|
|
354
|
+
severity: 'info',
|
|
355
|
+
code: 'NO_PROCEDURAL_STEPS',
|
|
356
|
+
message: 'No procedural steps found (numbered lists or bullet points)',
|
|
357
|
+
suggestion: 'Add step-by-step instructions for the agent to follow',
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Check for trigger/activation conditions
|
|
362
|
+
const triggerPatterns = [
|
|
363
|
+
/(?:when to use|trigger|activation|use this skill|when.*should)/i,
|
|
364
|
+
/(?:applicable|适用|触发)/i,
|
|
365
|
+
];
|
|
366
|
+
const hasTrigger = triggerPatterns.some((p) => p.test(content));
|
|
367
|
+
if (!hasTrigger) {
|
|
368
|
+
issues.push({
|
|
369
|
+
severity: 'info',
|
|
370
|
+
code: 'NO_TRIGGER_CONDITIONS',
|
|
371
|
+
message: 'No trigger/activation conditions specified',
|
|
372
|
+
suggestion: 'Add a section describing when this skill should be activated (e.g., "Use this skill when...")',
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private validateSecurity(content: string, issues: ValidationIssue[]): void {
|
|
378
|
+
// Check for potentially dangerous instructions
|
|
379
|
+
const dangerousPatterns = [
|
|
380
|
+
{ pattern: /rm\s+-rf\s+\/|del\s+\/S\s+\/Q/i, message: 'Contains destructive file system commands', code: 'DANGEROUS_FS_COMMAND' },
|
|
381
|
+
{ pattern: /DROP\s+TABLE|DELETE\s+FROM\s+\w+(?!\s+WHERE)/i, message: 'Contains potentially dangerous SQL without WHERE clause', code: 'DANGEROUS_SQL' },
|
|
382
|
+
{ pattern: /eval\s*\(|Function\s*\(/, message: 'Contains eval() or Function() constructor usage', code: 'DANGEROUS_EVAL' },
|
|
383
|
+
{ pattern: /curl.*\|\s*(?:bash|sh)/i, message: 'Contains pipe-to-shell pattern (curl | bash)', code: 'PIPE_TO_SHELL' },
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
for (const { pattern, message, code } of dangerousPatterns) {
|
|
387
|
+
if (pattern.test(content)) {
|
|
388
|
+
issues.push({
|
|
389
|
+
severity: 'warning',
|
|
390
|
+
code,
|
|
391
|
+
message: `Security: ${message}`,
|
|
392
|
+
suggestion: 'Review and add safety guards. If intentional, document why this is needed.',
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private validateQuality(skill: SkillDefinition, issues: ValidationIssue[]): void {
|
|
399
|
+
// Check for auto-generated flag without human review
|
|
400
|
+
if (skill.metadata.autoGenerated && !skill.metadata.reviewedAt) {
|
|
401
|
+
issues.push({
|
|
402
|
+
severity: 'info',
|
|
403
|
+
code: 'AUTO_GENERATED_UNREVIEWED',
|
|
404
|
+
message: 'Auto-generated skill has not been reviewed',
|
|
405
|
+
suggestion: 'Have a human review and update the "reviewedAt" field after verification',
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Calculate a quality score (0-100) based on issues.
|
|
412
|
+
*/
|
|
413
|
+
private calculateScore(issues: ValidationIssue[]): number {
|
|
414
|
+
let score = 100;
|
|
415
|
+
|
|
416
|
+
for (const issue of issues) {
|
|
417
|
+
switch (issue.severity) {
|
|
418
|
+
case 'error':
|
|
419
|
+
score -= 25;
|
|
420
|
+
break;
|
|
421
|
+
case 'warning':
|
|
422
|
+
score -= 10;
|
|
423
|
+
break;
|
|
424
|
+
case 'info':
|
|
425
|
+
score -= 3;
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Bonus for having good content length
|
|
431
|
+
const contentIssues = issues.filter(
|
|
432
|
+
(i) => i.code.includes('CONTENT') && i.severity !== 'error'
|
|
433
|
+
);
|
|
434
|
+
if (contentIssues.length === 0) {
|
|
435
|
+
score += 5; // Bonus for good content sizing
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return Math.max(0, Math.min(100, score));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Parse a SKILL.md file with optional YAML frontmatter.
|
|
443
|
+
*/
|
|
444
|
+
private parseSkillFile(
|
|
445
|
+
content: string,
|
|
446
|
+
filePath: string
|
|
447
|
+
): { metadata: SkillMetadata; body: string } {
|
|
448
|
+
const defaultMetadata: SkillMetadata = {
|
|
449
|
+
name: filePath ? filePath.replace(/\.md$/, '').split(/[\\/]/).pop() || 'unknown' : 'unknown',
|
|
450
|
+
description: '',
|
|
451
|
+
version: '0.1.0',
|
|
452
|
+
tags: [],
|
|
453
|
+
createdAt: new Date().toISOString(),
|
|
454
|
+
updatedAt: new Date().toISOString(),
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// Check for YAML frontmatter
|
|
458
|
+
const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
459
|
+
if (frontmatterMatch) {
|
|
460
|
+
const yamlContent = frontmatterMatch[1];
|
|
461
|
+
const body = frontmatterMatch[2].trim();
|
|
462
|
+
|
|
463
|
+
const metadata: Record<string, any> = {};
|
|
464
|
+
for (const line of yamlContent.split('\n')) {
|
|
465
|
+
const colonIndex = line.indexOf(':');
|
|
466
|
+
if (colonIndex === -1) continue;
|
|
467
|
+
|
|
468
|
+
const key = line.slice(0, colonIndex).trim();
|
|
469
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
470
|
+
|
|
471
|
+
// Remove quotes
|
|
472
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
473
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
474
|
+
value = value.slice(1, -1);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Parse arrays
|
|
478
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
479
|
+
try {
|
|
480
|
+
metadata[key] = JSON.parse(value);
|
|
481
|
+
} catch {
|
|
482
|
+
metadata[key] = value;
|
|
483
|
+
}
|
|
484
|
+
} else {
|
|
485
|
+
metadata[key] = value;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
metadata: {
|
|
491
|
+
...defaultMetadata,
|
|
492
|
+
...metadata,
|
|
493
|
+
name: metadata.name || defaultMetadata.name,
|
|
494
|
+
tags: metadata.tags || [],
|
|
495
|
+
},
|
|
496
|
+
body,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// No frontmatter - use defaults
|
|
501
|
+
return {
|
|
502
|
+
metadata: defaultMetadata,
|
|
503
|
+
body: content.trim(),
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Compare two semver version strings.
|
|
509
|
+
* Returns: -1 (a < b), 0 (equal), 1 (a > b)
|
|
510
|
+
*/
|
|
511
|
+
private compareVersions(a: string, b: string): number {
|
|
512
|
+
const parseVersion = (v: string): number[] =>
|
|
513
|
+
v.split('.').map((n) => parseInt(n, 10) || 0);
|
|
514
|
+
|
|
515
|
+
const va = parseVersion(a);
|
|
516
|
+
const vb = parseVersion(b);
|
|
517
|
+
|
|
518
|
+
for (let i = 0; i < 3; i++) {
|
|
519
|
+
if (va[i] < vb[i]) return -1;
|
|
520
|
+
if (va[i] > vb[i]) return 1;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return 0;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// extensions - Skill registry, auto-generation, validation, and installation
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
export { SkillRegistry } from './SkillRegistry.js';
|
|
6
|
+
export type { SkillMetadata, SkillDefinition, SkillSearchParams } from './SkillRegistry.js';
|
|
7
|
+
export { SkillGenerator } from './SkillGenerator.js';
|
|
8
|
+
export type { GenerationResult, GenerationOptions } from './SkillGenerator.js';
|
|
9
|
+
export { SkillValidator } from './SkillValidator.js';
|
|
10
|
+
export type { ValidationResult, ValidationIssue } from './SkillValidator.js';
|
|
11
|
+
export { SkillInstaller, POPULAR_SKILL_REPOS, installSuperpowers } from './SkillInstaller.js';
|
|
12
|
+
export type { SkillInstallOptions, InstalledSkill } from './SkillInstaller.js';
|
|
13
|
+
|
|
14
|
+
/** Skill type alias for convenience */
|
|
15
|
+
export type Skill = import('./SkillRegistry.js').SkillDefinition;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAKA,cAAc,kBAAkB,CAAC;AAGjC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// @nova-cli/core - Main barrel export
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
// Types
|
|
6
|
+
export * from './types/index.js';
|
|
7
|
+
|
|
8
|
+
// Tools
|
|
9
|
+
export * from './tools/index.js';
|
|
10
|
+
|
|
11
|
+
// Model
|
|
12
|
+
export * from './model/index.js';
|
|
13
|
+
|
|
14
|
+
// Session
|
|
15
|
+
export * from './session/index.js';
|
|
16
|
+
|
|
17
|
+
// Context
|
|
18
|
+
export * from './context/index.js';
|
|
19
|
+
|
|
20
|
+
// Security
|
|
21
|
+
export * from './security/index.js';
|
|
22
|
+
|
|
23
|
+
// MCP
|
|
24
|
+
export * from './mcp/index.js';
|
|
25
|
+
|
|
26
|
+
// Config
|
|
27
|
+
export * from './config/index.js';
|
|
28
|
+
|
|
29
|
+
// Auth
|
|
30
|
+
export * from './auth/index.js';
|
|
31
|
+
|
|
32
|
+
// Telemetry
|
|
33
|
+
export * from './telemetry/index.js';
|
|
34
|
+
|
|
35
|
+
// Utils
|
|
36
|
+
export * from './utils/index.js';
|
|
37
|
+
|
|
38
|
+
// Agents (Multi-agent orchestration)
|
|
39
|
+
export * from './agents/index.js';
|
|
40
|
+
|
|
41
|
+
// Audit (Audit logging)
|
|
42
|
+
export * from './audit/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpManager.d.ts","sourceRoot":"","sources":["McpManager.ts"],"names":[],"mappings":"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"}
|