@openweave/weave-cli 1.0.2

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.
@@ -0,0 +1,170 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { CLIArgs, CommandResult, CliCommand } from '../types';
4
+ import { resolveProjectRoot } from '../utils';
5
+
6
+ interface GraphNode {
7
+ id: string;
8
+ label: string;
9
+ type: string;
10
+ description?: string;
11
+ frequency: number;
12
+ last_accessed?: string;
13
+ }
14
+
15
+ /**
16
+ * Query Command - Search the knowledge graph
17
+ */
18
+ export class QueryCommand implements CliCommand {
19
+ name = 'query';
20
+ description = 'Search the knowledge graph for nodes and relationships';
21
+ usage = 'weave query <search-term> [--limit=10] [--type=all]';
22
+ flags = {
23
+ limit: {
24
+ short: 'l',
25
+ description: 'Maximum results to return (default: 10)',
26
+ default: '10',
27
+ },
28
+ type: {
29
+ short: 't',
30
+ description: 'Filter by node type (default: all)',
31
+ default: 'all',
32
+ },
33
+ json: {
34
+ short: 'j',
35
+ description: 'Output as JSON',
36
+ default: false,
37
+ },
38
+ };
39
+
40
+ async execute(args: CLIArgs): Promise<CommandResult> {
41
+ try {
42
+ const searchTerm = args.args[0];
43
+ if (!searchTerm) {
44
+ return {
45
+ success: false,
46
+ message: 'Error: Search term is required',
47
+ error: 'Usage: weave query <search-term>',
48
+ };
49
+ }
50
+
51
+ const projectRoot = resolveProjectRoot();
52
+ const weaveDir = join(projectRoot, '.weave');
53
+ const graphPath = join(weaveDir, 'graph.json');
54
+
55
+ if (!existsSync(graphPath)) {
56
+ return {
57
+ success: false,
58
+ message: 'Error: Knowledge graph not found',
59
+ error: 'Please run "weave init <project-name>" first',
60
+ };
61
+ }
62
+
63
+ // Mock search results for demonstration
64
+ const mockResults: GraphNode[] = [
65
+ {
66
+ id: 'n1',
67
+ label: 'initializeProject',
68
+ type: 'FUNCTION',
69
+ description: 'Initializes a new project with default settings',
70
+ frequency: 5,
71
+ last_accessed: new Date().toISOString().split('T')[0],
72
+ },
73
+ {
74
+ id: 'n2',
75
+ label: 'ProjectManager',
76
+ type: 'CLASS',
77
+ description: 'Manages project lifecycle and state',
78
+ frequency: 12,
79
+ last_accessed: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000)
80
+ .toISOString()
81
+ .split('T')[0],
82
+ },
83
+ {
84
+ id: 'n3',
85
+ label: 'project_config',
86
+ type: 'VARIABLE',
87
+ description: 'Global configuration object',
88
+ frequency: 3,
89
+ last_accessed: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000)
90
+ .toISOString()
91
+ .split('T')[0],
92
+ },
93
+ ];
94
+
95
+ const limit = parseInt((args.flags.limit as string) || '10', 10);
96
+ const typeFilter = (args.flags.type as string) || 'all';
97
+
98
+ let results = mockResults.filter((r) =>
99
+ r.label.toLowerCase().includes(searchTerm.toLowerCase())
100
+ );
101
+
102
+ if (typeFilter !== 'all') {
103
+ results = results.filter((r) => r.type === typeFilter.toUpperCase());
104
+ }
105
+
106
+ results = results.slice(0, limit);
107
+
108
+ if (args.flags.json) {
109
+ return {
110
+ success: true,
111
+ message: JSON.stringify(results, null, 2),
112
+ data: results,
113
+ };
114
+ }
115
+
116
+ let output = `\nšŸ” Query Results for: "${searchTerm}"\n`;
117
+ output += '='.repeat(60) + '\n';
118
+
119
+ if (results.length === 0) {
120
+ output += 'No results found.\n';
121
+ } else {
122
+ for (const node of results) {
123
+ output += this.formatNode(node);
124
+ }
125
+ }
126
+
127
+ output += '\n' + '='.repeat(60) + '\n';
128
+ output += `Found: ${results.length} result(s)\n`;
129
+
130
+ return {
131
+ success: true,
132
+ message: output,
133
+ data: results,
134
+ };
135
+ } catch (error) {
136
+ return {
137
+ success: false,
138
+ message: 'Error querying knowledge graph',
139
+ error: error instanceof Error ? error.message : String(error),
140
+ };
141
+ }
142
+ }
143
+
144
+ private formatNode(node: GraphNode): string {
145
+ const typeEmoji = {
146
+ FUNCTION: 'šŸ“Œ',
147
+ CLASS: 'šŸ›ļø',
148
+ INTERFACE: 'šŸ“‹',
149
+ ENUM: 'šŸ“Š',
150
+ TYPE: 'šŸ“',
151
+ VARIABLE: 'šŸ“¦',
152
+ METHOD: 'šŸ”§',
153
+ MODULE: 'šŸ“š',
154
+ }[node.type] || 'ā“';
155
+
156
+ let output = `\n${typeEmoji} ${node.label} (${node.type})\n`;
157
+ if (node.description) {
158
+ output += ` Description: ${node.description}\n`;
159
+ }
160
+ output += ` ID: ${node.id}\n`;
161
+ output += ` Frequency: ${node.frequency} times\n`;
162
+ if (node.last_accessed) {
163
+ output += ` Last accessed: ${node.last_accessed}\n`;
164
+ }
165
+
166
+ return output;
167
+ }
168
+ }
169
+
170
+ export const queryCommand = new QueryCommand();
@@ -0,0 +1,161 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { CLIArgs, CommandResult, CliCommand } from '../types';
4
+ import { resolveProjectRoot } from '../utils';
5
+
6
+ interface GraphNode {
7
+ id: string;
8
+ label: string;
9
+ type: string;
10
+ description?: string;
11
+ metadata?: Record<string, unknown>;
12
+ frequency?: number;
13
+ created_at: string;
14
+ updated_at: string;
15
+ }
16
+
17
+ /**
18
+ * SaveNode Command - Manually add or update a node in the knowledge graph
19
+ */
20
+ export class SaveNodeCommand implements CliCommand {
21
+ name = 'save-node';
22
+ description = 'Manually add or update a node in the knowledge graph';
23
+ usage = 'weave save-node --label=<name> --type=<type> [--description=<desc>]';
24
+ flags = {
25
+ label: {
26
+ short: 'l',
27
+ description: 'Node label/name (required)',
28
+ },
29
+ type: {
30
+ short: 't',
31
+ description: 'Node type: function, class, variable, interface, enum, etc.',
32
+ },
33
+ description: {
34
+ short: 'd',
35
+ description: 'Node description/documentation',
36
+ default: '',
37
+ },
38
+ file: {
39
+ short: 'f',
40
+ description: 'Source file location',
41
+ default: '',
42
+ },
43
+ line: {
44
+ description: 'Line number in source file',
45
+ default: '0',
46
+ },
47
+ frequency: {
48
+ description: 'Reference frequency hint',
49
+ default: '1',
50
+ },
51
+ json: {
52
+ short: 'j',
53
+ description: 'Output as JSON',
54
+ default: false,
55
+ },
56
+ };
57
+
58
+ async execute(args: CLIArgs): Promise<CommandResult> {
59
+ try {
60
+ const label = args.flags.label as string;
61
+ const type = args.flags.type as string;
62
+
63
+ if (!label) {
64
+ return {
65
+ success: false,
66
+ message: 'Error: --label is required',
67
+ error: 'Usage: weave save-node --label=<name> --type=<type>',
68
+ };
69
+ }
70
+
71
+ if (!type) {
72
+ return {
73
+ success: false,
74
+ message: 'Error: --type is required',
75
+ error:
76
+ 'Valid types: function, class, variable, interface, enum, module, type, method',
77
+ };
78
+ }
79
+
80
+ const projectRoot = resolveProjectRoot();
81
+ const weaveDir = join(projectRoot, '.weave');
82
+ const graphPath = join(weaveDir, 'graph.json');
83
+
84
+ if (!existsSync(graphPath)) {
85
+ return {
86
+ success: false,
87
+ message: 'Error: Knowledge graph not found',
88
+ error: 'Please run "weave init <project-name>" first',
89
+ };
90
+ }
91
+
92
+ // Read existing graph
93
+ const graphContent = readFileSync(graphPath, 'utf-8');
94
+ const graphData = JSON.parse(graphContent);
95
+
96
+ // Create new node
97
+ const nodeId = `node_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
98
+ const now = new Date().toISOString();
99
+
100
+ const newNode: GraphNode = {
101
+ id: nodeId,
102
+ label,
103
+ type: type.toUpperCase(),
104
+ description: (args.flags.description as string) || undefined,
105
+ frequency: parseInt((args.flags.frequency as string) || '1', 10),
106
+ created_at: now,
107
+ updated_at: now,
108
+ };
109
+
110
+ if (args.flags.file) {
111
+ newNode.metadata = {
112
+ file: args.flags.file,
113
+ line: parseInt((args.flags.line as string) || '0', 10),
114
+ };
115
+ }
116
+
117
+ // Add to graph
118
+ graphData.nodes.push(newNode);
119
+
120
+ // Write back
121
+ writeFileSync(graphPath, JSON.stringify(graphData, null, 2));
122
+
123
+ if (args.flags.json) {
124
+ return {
125
+ success: true,
126
+ message: JSON.stringify(newNode, null, 2),
127
+ data: newNode,
128
+ };
129
+ }
130
+
131
+ const output = `
132
+ āœ… Node created successfully!
133
+
134
+ šŸ“ Node ID: ${newNode.id}
135
+ šŸ“Œ Label: ${newNode.label}
136
+ šŸ·ļø Type: ${newNode.type}
137
+ ${newNode.description ? `šŸ“ Description: ${newNode.description}\n` : ''}
138
+
139
+ Graph Statistics:
140
+ • Total nodes: ${graphData.nodes.length}
141
+ • Total edges: ${graphData.edges.length}
142
+
143
+ Use 'weave query ${newNode.label}' to search for this node.
144
+ `;
145
+
146
+ return {
147
+ success: true,
148
+ message: output,
149
+ data: newNode,
150
+ };
151
+ } catch (error) {
152
+ return {
153
+ success: false,
154
+ message: 'Error saving node',
155
+ error: error instanceof Error ? error.message : String(error),
156
+ };
157
+ }
158
+ }
159
+ }
160
+
161
+ export const saveNodeCommand = new SaveNodeCommand();
@@ -0,0 +1,230 @@
1
+ import { join } from 'node:path';
2
+ import { CLIArgs, CommandResult, CliCommand } from '../types.js';
3
+ import { resolveProjectRoot } from '../utils.js';
4
+ import {
5
+ loadSkillConfig,
6
+ setSkillEnabled,
7
+ CONFIG_FILENAME,
8
+ } from '@openweave/weave-skills';
9
+
10
+ /**
11
+ * SkillsCommand — manage OpenWeave skill modules
12
+ *
13
+ * Subcommands:
14
+ * weave skills list List all skills and their enabled state
15
+ * weave skills enable <id> Enable a skill in .weave.config.json
16
+ * weave skills disable <id> Disable a skill in .weave.config.json
17
+ * weave skills info <id> Show config entry for a skill
18
+ */
19
+ export const skillsCommand: CliCommand = {
20
+ name: 'skills',
21
+ description: 'Manage OpenWeave skill modules',
22
+ usage: 'weave skills <list|enable|disable|info> [skill-id]',
23
+ flags: {
24
+ json: {
25
+ short: 'j',
26
+ description: 'Output as JSON',
27
+ default: false,
28
+ },
29
+ },
30
+
31
+ async execute(args: CLIArgs): Promise<CommandResult> {
32
+ const sub = args.args[0] ?? 'list';
33
+ const skillId = args.args[1];
34
+ const outputJson = Boolean(args.flags.json);
35
+ const projectRoot = resolveProjectRoot();
36
+
37
+ switch (sub) {
38
+ case 'list':
39
+ return handleList(projectRoot, outputJson);
40
+
41
+ case 'enable': {
42
+ if (!skillId) {
43
+ return {
44
+ success: false,
45
+ message: 'āŒ Usage: weave skills enable <skill-id>',
46
+ error: 'Missing skill id',
47
+ };
48
+ }
49
+ return handleSetEnabled(projectRoot, skillId, true, outputJson);
50
+ }
51
+
52
+ case 'disable': {
53
+ if (!skillId) {
54
+ return {
55
+ success: false,
56
+ message: 'āŒ Usage: weave skills disable <skill-id>',
57
+ error: 'Missing skill id',
58
+ };
59
+ }
60
+ return handleSetEnabled(projectRoot, skillId, false, outputJson);
61
+ }
62
+
63
+ case 'info': {
64
+ if (!skillId) {
65
+ return {
66
+ success: false,
67
+ message: 'āŒ Usage: weave skills info <skill-id>',
68
+ error: 'Missing skill id',
69
+ };
70
+ }
71
+ return handleInfo(projectRoot, skillId, outputJson);
72
+ }
73
+
74
+ default:
75
+ return {
76
+ success: false,
77
+ message: `āŒ Unknown subcommand: "${sub}"\n\nUsage: weave skills <list|enable|disable|info>`,
78
+ error: `Unknown subcommand: ${sub}`,
79
+ };
80
+ }
81
+ },
82
+ };
83
+
84
+ // ---------------------------------------------------------------------------
85
+ // Handlers
86
+ // ---------------------------------------------------------------------------
87
+
88
+ function handleList(projectRoot: string, outputJson: boolean): CommandResult {
89
+ const config = loadSkillConfig(projectRoot);
90
+ const entries = Object.entries(config.skills);
91
+
92
+ if (outputJson) {
93
+ return {
94
+ success: true,
95
+ message: JSON.stringify(config.skills, null, 2),
96
+ data: config.skills,
97
+ };
98
+ }
99
+
100
+ const configFile = join(projectRoot, CONFIG_FILENAME);
101
+
102
+ if (entries.length === 0) {
103
+ return {
104
+ success: true,
105
+ message: [
106
+ '',
107
+ 'šŸ”§ OpenWeave Skills',
108
+ '─'.repeat(50),
109
+ '',
110
+ ' No skills configured yet.',
111
+ '',
112
+ ' Skills are enabled/disabled in:',
113
+ ` ${configFile}`,
114
+ '',
115
+ ' Example:',
116
+ ' weave skills enable auto-fix',
117
+ ' weave skills enable code-review',
118
+ '',
119
+ ' Available skill ids (Phase 9):',
120
+ ' auto-fix Ā· code-review Ā· test-gen Ā· docs-gen Ā· refactor',
121
+ ' pipeline-aware Ā· dep-audit Ā· perf-profile Ā· container-advisor Ā· deploy-provision',
122
+ ' onboarding Ā· commit-composer Ā· context-memory Ā· multi-repo Ā· cli-interactive',
123
+ '',
124
+ ].join('\n'),
125
+ };
126
+ }
127
+
128
+ const rows = entries.map(([id, enabled]) => {
129
+ const icon = enabled ? 'āœ…' : '⬜';
130
+ const state = enabled ? 'enabled ' : 'disabled';
131
+ return ` ${icon} ${state} ${id}`;
132
+ });
133
+
134
+ return {
135
+ success: true,
136
+ message: [
137
+ '',
138
+ 'šŸ”§ OpenWeave Skills',
139
+ '─'.repeat(50),
140
+ '',
141
+ ...rows,
142
+ '',
143
+ ` Config: ${configFile}`,
144
+ '',
145
+ ' weave skills enable <id> — activate a skill',
146
+ ' weave skills disable <id> — deactivate a skill',
147
+ '',
148
+ ].join('\n'),
149
+ data: config.skills,
150
+ };
151
+ }
152
+
153
+ function handleSetEnabled(
154
+ projectRoot: string,
155
+ skillId: string,
156
+ enabled: boolean,
157
+ outputJson: boolean
158
+ ): CommandResult {
159
+ try {
160
+ setSkillEnabled(skillId, enabled, projectRoot);
161
+
162
+ const action = enabled ? 'enabled' : 'disabled';
163
+ const icon = enabled ? 'āœ…' : '⬜';
164
+ const data = { skillId, enabled };
165
+
166
+ if (outputJson) {
167
+ return { success: true, message: JSON.stringify(data, null, 2), data };
168
+ }
169
+
170
+ return {
171
+ success: true,
172
+ message: `\n${icon} Skill '${skillId}' ${action} in ${CONFIG_FILENAME}\n`,
173
+ data,
174
+ };
175
+ } catch (err) {
176
+ return {
177
+ success: false,
178
+ message: `āŒ Failed to update skill config`,
179
+ error: err instanceof Error ? err.message : String(err),
180
+ };
181
+ }
182
+ }
183
+
184
+ function handleInfo(projectRoot: string, skillId: string, outputJson: boolean): CommandResult {
185
+ const config = loadSkillConfig(projectRoot);
186
+ const hasEntry = skillId in config.skills;
187
+
188
+ if (!hasEntry) {
189
+ if (outputJson) {
190
+ return {
191
+ success: false,
192
+ message: JSON.stringify({ skillId, configured: false }),
193
+ data: { skillId, configured: false },
194
+ };
195
+ }
196
+ return {
197
+ success: false,
198
+ message: [
199
+ '',
200
+ `āš ļø Skill '${skillId}' is not in ${CONFIG_FILENAME}`,
201
+ '',
202
+ ` To add it:`,
203
+ ` weave skills enable ${skillId}`,
204
+ ` weave skills disable ${skillId}`,
205
+ '',
206
+ ].join('\n'),
207
+ error: `Skill '${skillId}' not configured`,
208
+ };
209
+ }
210
+
211
+ const enabled = config.skills[skillId];
212
+ const data = { skillId, enabled, configured: true };
213
+
214
+ if (outputJson) {
215
+ return { success: true, message: JSON.stringify(data, null, 2), data };
216
+ }
217
+
218
+ return {
219
+ success: true,
220
+ message: [
221
+ '',
222
+ `šŸ”§ Skill: ${skillId}`,
223
+ '─'.repeat(40),
224
+ ` Status : ${enabled ? 'āœ… enabled' : '⬜ disabled'}`,
225
+ ` Config : ${join(projectRoot, CONFIG_FILENAME)}`,
226
+ '',
227
+ ].join('\n'),
228
+ data,
229
+ };
230
+ }
@@ -0,0 +1,122 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { CLIArgs, CommandResult, CliCommand, CLIConfig, ProjectState } from '../types';
4
+ import { resolveProjectRoot } from '../utils';
5
+
6
+ /**
7
+ * Status Command - Display project status
8
+ */
9
+ export class StatusCommand implements CliCommand {
10
+ name = 'status';
11
+ description = 'Show current project status and progress';
12
+ usage = 'weave status [--verbose]';
13
+ flags = {
14
+ verbose: {
15
+ short: 'v',
16
+ description: 'Show detailed information',
17
+ default: false,
18
+ },
19
+ json: {
20
+ short: 'j',
21
+ description: 'Output as JSON',
22
+ default: false,
23
+ },
24
+ };
25
+
26
+ async execute(args: CLIArgs): Promise<CommandResult> {
27
+ try {
28
+ const projectRoot = resolveProjectRoot();
29
+ const weaveDir = join(projectRoot, '.weave');
30
+ const configPath = join(weaveDir, 'config.json');
31
+
32
+ if (!existsSync(configPath)) {
33
+ return {
34
+ success: false,
35
+ message: 'Error: .weave directory not found',
36
+ error: 'Please run "weave init <project-name>" first',
37
+ };
38
+ }
39
+
40
+ const configContent = readFileSync(configPath, 'utf-8');
41
+ const config: CLIConfig = JSON.parse(configContent);
42
+
43
+ const graphPath = join(weaveDir, 'graph.json');
44
+ let graphData = { nodes: [], edges: [], sessions: {} };
45
+
46
+ if (existsSync(graphPath)) {
47
+ const graphContent = readFileSync(graphPath, 'utf-8');
48
+ graphData = JSON.parse(graphContent);
49
+ }
50
+
51
+ const projectState: ProjectState = {
52
+ created_at: new Date(),
53
+ last_updated: new Date(),
54
+ session_id: `session_${Date.now()}`,
55
+ milestones: 0,
56
+ total_nodes: graphData.nodes.length,
57
+ total_edges: graphData.edges.length,
58
+ context_usage_percent: Math.min(
59
+ 100,
60
+ ((graphData.nodes.length + graphData.edges.length) / 10000) * 100
61
+ ),
62
+ };
63
+
64
+ const outputJson = args.flags.json as boolean;
65
+
66
+ if (outputJson) {
67
+ return {
68
+ success: true,
69
+ message: JSON.stringify(projectState, null, 2),
70
+ data: projectState,
71
+ };
72
+ }
73
+
74
+ const verbose = args.flags.verbose as boolean;
75
+
76
+ const statusMessage = `
77
+ šŸ“Š Project Status: ${config.project_name}
78
+ ${'-'.repeat(50)}
79
+
80
+ šŸ“ Location: ${config.project_root}
81
+ šŸ•’ Session: ${projectState.session_id}
82
+
83
+ šŸ“ˆ Graph Statistics:
84
+ • Nodes: ${projectState.total_nodes}
85
+ • Edges: ${projectState.total_edges}
86
+ • Context Usage: ${projectState.context_usage_percent.toFixed(1)}%
87
+
88
+ ${verbose ? this.getVerboseInfo(config) : ''}
89
+
90
+ ✨ Quick Commands:
91
+ • weave query <term> - Search the knowledge graph
92
+ • weave orphans - Analyze code for unused exports
93
+ • weave milestones - View milestone progress
94
+ • weave errors - Show error registry
95
+ `;
96
+
97
+ return {
98
+ success: true,
99
+ message: statusMessage,
100
+ data: projectState,
101
+ };
102
+ } catch (error) {
103
+ return {
104
+ success: false,
105
+ message: 'Error reading project status',
106
+ error: error instanceof Error ? error.message : String(error),
107
+ };
108
+ }
109
+ }
110
+
111
+ private getVerboseInfo(config: CLIConfig): string {
112
+ return `
113
+ āš™ļø Configuration:
114
+ • Include Tests: ${config.include_tests}
115
+ • Max Context Depth: ${config.max_context_depth}
116
+ • Verbose Mode: ${config.verbose}
117
+ • Debug Mode: ${config.debug}
118
+ `;
119
+ }
120
+ }
121
+
122
+ export const statusCommand = new StatusCommand();