@openweave/weave-skills 1.0.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -0
  3. package/dist/config-loader.d.ts +52 -0
  4. package/dist/config-loader.d.ts.map +1 -0
  5. package/dist/config-loader.js +123 -0
  6. package/dist/config-loader.js.map +1 -0
  7. package/dist/index.d.ts +12 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +14 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/skill-registry.d.ts +96 -0
  12. package/dist/skill-registry.d.ts.map +1 -0
  13. package/dist/skill-registry.js +218 -0
  14. package/dist/skill-registry.js.map +1 -0
  15. package/dist/skills/auto-fix.d.ts +35 -0
  16. package/dist/skills/auto-fix.d.ts.map +1 -0
  17. package/dist/skills/auto-fix.js +121 -0
  18. package/dist/skills/auto-fix.js.map +1 -0
  19. package/dist/skills/cli-interactive.d.ts +60 -0
  20. package/dist/skills/cli-interactive.d.ts.map +1 -0
  21. package/dist/skills/cli-interactive.js +264 -0
  22. package/dist/skills/cli-interactive.js.map +1 -0
  23. package/dist/skills/code-review.d.ts +39 -0
  24. package/dist/skills/code-review.d.ts.map +1 -0
  25. package/dist/skills/code-review.js +204 -0
  26. package/dist/skills/code-review.js.map +1 -0
  27. package/dist/skills/commit-composer.d.ts +51 -0
  28. package/dist/skills/commit-composer.d.ts.map +1 -0
  29. package/dist/skills/commit-composer.js +223 -0
  30. package/dist/skills/commit-composer.js.map +1 -0
  31. package/dist/skills/container-advisor.d.ts +43 -0
  32. package/dist/skills/container-advisor.d.ts.map +1 -0
  33. package/dist/skills/container-advisor.js +274 -0
  34. package/dist/skills/container-advisor.js.map +1 -0
  35. package/dist/skills/context-memory.d.ts +44 -0
  36. package/dist/skills/context-memory.d.ts.map +1 -0
  37. package/dist/skills/context-memory.js +160 -0
  38. package/dist/skills/context-memory.js.map +1 -0
  39. package/dist/skills/dep-audit.d.ts +55 -0
  40. package/dist/skills/dep-audit.d.ts.map +1 -0
  41. package/dist/skills/dep-audit.js +248 -0
  42. package/dist/skills/dep-audit.js.map +1 -0
  43. package/dist/skills/deploy-provision.d.ts +47 -0
  44. package/dist/skills/deploy-provision.d.ts.map +1 -0
  45. package/dist/skills/deploy-provision.js +270 -0
  46. package/dist/skills/deploy-provision.js.map +1 -0
  47. package/dist/skills/docs-gen.d.ts +36 -0
  48. package/dist/skills/docs-gen.d.ts.map +1 -0
  49. package/dist/skills/docs-gen.js +187 -0
  50. package/dist/skills/docs-gen.js.map +1 -0
  51. package/dist/skills/index.d.ts +19 -0
  52. package/dist/skills/index.d.ts.map +1 -0
  53. package/dist/skills/index.js +55 -0
  54. package/dist/skills/index.js.map +1 -0
  55. package/dist/skills/multi-repo.d.ts +50 -0
  56. package/dist/skills/multi-repo.d.ts.map +1 -0
  57. package/dist/skills/multi-repo.js +175 -0
  58. package/dist/skills/multi-repo.js.map +1 -0
  59. package/dist/skills/onboarding.d.ts +48 -0
  60. package/dist/skills/onboarding.d.ts.map +1 -0
  61. package/dist/skills/onboarding.js +245 -0
  62. package/dist/skills/onboarding.js.map +1 -0
  63. package/dist/skills/perf-profile.d.ts +36 -0
  64. package/dist/skills/perf-profile.d.ts.map +1 -0
  65. package/dist/skills/perf-profile.js +179 -0
  66. package/dist/skills/perf-profile.js.map +1 -0
  67. package/dist/skills/pipeline-aware.d.ts +33 -0
  68. package/dist/skills/pipeline-aware.d.ts.map +1 -0
  69. package/dist/skills/pipeline-aware.js +226 -0
  70. package/dist/skills/pipeline-aware.js.map +1 -0
  71. package/dist/skills/refactor.d.ts +33 -0
  72. package/dist/skills/refactor.d.ts.map +1 -0
  73. package/dist/skills/refactor.js +210 -0
  74. package/dist/skills/refactor.js.map +1 -0
  75. package/dist/skills/test-gen.d.ts +36 -0
  76. package/dist/skills/test-gen.d.ts.map +1 -0
  77. package/dist/skills/test-gen.js +154 -0
  78. package/dist/skills/test-gen.js.map +1 -0
  79. package/dist/types.d.ts +133 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +8 -0
  82. package/dist/types.js.map +1 -0
  83. package/package.json +39 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Skill: refactor
3
+ *
4
+ * Detects common code smells in TypeScript source files and produces a prioritised
5
+ * list of refactoring suggestions. Purely static / heuristic — no AST required.
6
+ *
7
+ * Detects:
8
+ * - Long functions (> LONG_FUNCTION_LINES lines between opening { and matching })
9
+ * - Large files (> LARGE_FILE_LINES total lines)
10
+ * - Magic numbers (numeric literals that are not 0, 1, -1, 2, 100)
11
+ * - Deep nesting (indentation depth > MAX_NESTING spaces / 2)
12
+ * - Long parameter lists (> MAX_PARAMS parameters in a function signature)
13
+ * - TODO / FIXME / HACK comments
14
+ * - Duplicate imports (same module imported more than once)
15
+ */
16
+ import { readFileSync, existsSync } from 'node:fs';
17
+ import { join } from 'node:path';
18
+ // ---------------------------------------------------------------------------
19
+ // Constants (overridable via ctx.graph)
20
+ // ---------------------------------------------------------------------------
21
+ const LONG_FUNCTION_LINES = 40;
22
+ const LARGE_FILE_LINES = 300;
23
+ const MAX_NESTING = 4; // levels (each ~2 spaces, so 8 spaces indent)
24
+ const MAX_PARAMS = 4;
25
+ // ---------------------------------------------------------------------------
26
+ // Helpers
27
+ // ---------------------------------------------------------------------------
28
+ export function detectSmells(source, filePath) {
29
+ const lines = source.split('\n');
30
+ const smells = [];
31
+ // ── Large file ──────────────────────────────────────────────────────────
32
+ if (lines.length > LARGE_FILE_LINES) {
33
+ smells.push({
34
+ file: filePath,
35
+ line: 1,
36
+ kind: 'large-file',
37
+ severity: 'warning',
38
+ message: `File has ${lines.length} lines (threshold: ${LARGE_FILE_LINES})`,
39
+ });
40
+ }
41
+ // ── Duplicate imports ────────────────────────────────────────────────────
42
+ const importSeen = new Map(); // module → first line
43
+ const IMPORT_RE = /^\s*import\s+.+\s+from\s+['"]([^'"]+)['"]/;
44
+ for (let i = 0; i < lines.length; i++) {
45
+ const m = IMPORT_RE.exec(lines[i]);
46
+ if (!m)
47
+ continue;
48
+ const mod = m[1];
49
+ if (importSeen.has(mod)) {
50
+ smells.push({
51
+ file: filePath,
52
+ line: i + 1,
53
+ kind: 'duplicate-import',
54
+ severity: 'warning',
55
+ message: `Duplicate import of '${mod}' (first seen at line ${importSeen.get(mod)})`,
56
+ });
57
+ }
58
+ else {
59
+ importSeen.set(mod, i + 1);
60
+ }
61
+ }
62
+ // ── Per-line checks ──────────────────────────────────────────────────────
63
+ const funcStack = [];
64
+ let braceDepth = 0;
65
+ for (let i = 0; i < lines.length; i++) {
66
+ const line = lines[i];
67
+ const lineNo = i + 1;
68
+ const trimmed = line.trimStart();
69
+ // TODO / FIXME / HACK
70
+ if (/\b(TODO|FIXME|HACK|XXX)\b/.test(trimmed)) {
71
+ smells.push({
72
+ file: filePath,
73
+ line: lineNo,
74
+ kind: 'todo-comment',
75
+ severity: 'info',
76
+ message: `TODO/FIXME comment at line ${lineNo}`,
77
+ detail: trimmed.slice(0, 80),
78
+ });
79
+ }
80
+ // Magic numbers
81
+ // Exclude: 0, 1, -1, 2, 100, numbers inside imports/require, pure number literals in tests
82
+ const MAGIC_RE = /(?<![.\w])(-?\b(?:[3-9]\d*|1\d+|2\d+)\b)(?!\s*[%,]?\s*\/\*)/g;
83
+ const isCommentLine = trimmed.startsWith('//') || trimmed.startsWith('*');
84
+ const isImportLine = /^\s*import\s/.test(line) || /require\(/.test(line);
85
+ if (!isCommentLine && !isImportLine) {
86
+ let mm;
87
+ while ((mm = MAGIC_RE.exec(line)) !== null) {
88
+ const num = Number(mm[1]);
89
+ if (Number.isFinite(num) && Math.abs(num) > 1 && num !== 100) {
90
+ smells.push({
91
+ file: filePath,
92
+ line: lineNo,
93
+ kind: 'magic-number',
94
+ severity: 'info',
95
+ message: `Magic number ${mm[1]} — consider extracting to a named constant`,
96
+ });
97
+ }
98
+ }
99
+ }
100
+ // Deep nesting — count leading spaces (2 spaces = 1 level)
101
+ const indent = line.length - trimmed.length;
102
+ const nestLevel = Math.floor(indent / 2);
103
+ if (nestLevel > MAX_NESTING && trimmed.length > 0 && !isCommentLine) {
104
+ smells.push({
105
+ file: filePath,
106
+ line: lineNo,
107
+ kind: 'deep-nesting',
108
+ severity: 'warning',
109
+ message: `Nesting depth ${nestLevel} exceeds threshold (${MAX_NESTING})`,
110
+ });
111
+ }
112
+ // Long parameter lists in function/method signatures
113
+ const PARAM_RE = /(?:function\s+\w+|(?:\w+)\s*=\s*(?:async\s+)?\()\s*([^)]{30,})\)/;
114
+ const paramMatch = PARAM_RE.exec(line);
115
+ if (paramMatch) {
116
+ const params = paramMatch[1].split(',').filter((p) => p.trim().length > 0);
117
+ if (params.length > MAX_PARAMS) {
118
+ smells.push({
119
+ file: filePath,
120
+ line: lineNo,
121
+ kind: 'long-param-list',
122
+ severity: 'warning',
123
+ message: `Function has ${params.length} parameters (threshold: ${MAX_PARAMS}) — consider an options object`,
124
+ });
125
+ }
126
+ }
127
+ // Brace tracking for long-function detection
128
+ const opens = (line.match(/\{/g) ?? []).length;
129
+ const closes = (line.match(/\}/g) ?? []).length;
130
+ // Detect function start
131
+ const FUNC_START_RE = /(?:(?:export\s+)?(?:async\s+)?function\s+(\w+)|(?:(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*(?:=>\s*)?\{))/;
132
+ const funcMatch = FUNC_START_RE.exec(line);
133
+ if (funcMatch && line.includes('{')) {
134
+ funcStack.push({ startLine: lineNo, name: funcMatch[1] ?? funcMatch[2] ?? '(anonymous)', depth: braceDepth });
135
+ }
136
+ braceDepth += opens - closes;
137
+ // Check if function closed
138
+ while (funcStack.length > 0 && braceDepth <= funcStack[funcStack.length - 1].depth) {
139
+ const fn = funcStack.pop();
140
+ const length = lineNo - fn.startLine + 1;
141
+ if (length > LONG_FUNCTION_LINES) {
142
+ smells.push({
143
+ file: filePath,
144
+ line: fn.startLine,
145
+ kind: 'long-function',
146
+ severity: 'warning',
147
+ message: `Function '${fn.name}' is ${length} lines long (threshold: ${LONG_FUNCTION_LINES})`,
148
+ });
149
+ }
150
+ }
151
+ }
152
+ return smells;
153
+ }
154
+ // ---------------------------------------------------------------------------
155
+ // Skill
156
+ // ---------------------------------------------------------------------------
157
+ export const refactorSkill = {
158
+ id: 'refactor',
159
+ name: 'Refactor Advisor',
160
+ description: 'Detects code smells (long functions, magic numbers, deep nesting, large files, etc.) and suggests refactoring',
161
+ version: '1.0.0',
162
+ enabled: false,
163
+ tags: ['dev', 'quality'],
164
+ async execute(ctx) {
165
+ const tsFiles = ctx.files.filter((f) => f.endsWith('.ts') && !f.endsWith('.test.ts') && !f.endsWith('.d.ts') && !f.includes('/dist/'));
166
+ const smells = [];
167
+ for (const relPath of tsFiles) {
168
+ const absPath = join(ctx.projectRoot, relPath);
169
+ if (!existsSync(absPath))
170
+ continue;
171
+ try {
172
+ const content = readFileSync(absPath, 'utf-8');
173
+ smells.push(...detectSmells(content, relPath));
174
+ }
175
+ catch { /* skip unreadable */ }
176
+ }
177
+ // Summary
178
+ const summary = {};
179
+ for (const s of smells) {
180
+ summary[s.kind] = (summary[s.kind] ?? 0) + 1;
181
+ }
182
+ const errors = smells.filter((s) => s.severity === 'error').length;
183
+ const warnings = smells.filter((s) => s.severity === 'warning').length;
184
+ const infos = smells.filter((s) => s.severity === 'info').length;
185
+ const lines = [
186
+ `♻️ Refactor Advisor — ${tsFiles.length} files analyzed`,
187
+ ` ${errors} error(s), ${warnings} warning(s), ${infos} info(s)`,
188
+ '',
189
+ ];
190
+ const SEVERITY_ORDER = ['error', 'warning', 'info'];
191
+ for (const sev of SEVERITY_ORDER) {
192
+ const group = smells.filter((s) => s.severity === sev);
193
+ if (group.length === 0)
194
+ continue;
195
+ lines.push(` ${sev.toUpperCase()}S:`);
196
+ for (const s of group.slice(0, 15)) {
197
+ lines.push(` ${s.file}:${s.line} [${s.kind}] ${s.message}`);
198
+ }
199
+ if (group.length > 15)
200
+ lines.push(` … and ${group.length - 15} more`);
201
+ lines.push('');
202
+ }
203
+ return {
204
+ success: true,
205
+ output: lines.join('\n'),
206
+ data: { smells, filesAnalyzed: tsFiles.length, summary },
207
+ };
208
+ },
209
+ };
210
+ //# sourceMappingURL=refactor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refactor.js","sourceRoot":"","sources":["../../src/skills/refactor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Skill: test-gen
3
+ *
4
+ * Detects exported TypeScript functions and classes that have no corresponding
5
+ * test file, then generates Vitest-compatible test stubs.
6
+ *
7
+ * Strategy:
8
+ * 1. Walk ctx.files for *.ts files (excluding *.test.ts and *.d.ts)
9
+ * 2. For each source file, check if a <name>.test.ts exists next to it
10
+ * 3. Parse exported function/class names via regex (fast, no full AST)
11
+ * 4. Generate a stub test file matching the project's Vitest patterns
12
+ *
13
+ * Output data:
14
+ * - untested: UncoveredFile[] — files with no test counterpart
15
+ * - stubs: TestStub[] — generated test stubs (not written to disk)
16
+ */
17
+ import type { SkillModule } from '../types.js';
18
+ export interface UncoveredFile {
19
+ sourcePath: string;
20
+ exports: string[];
21
+ }
22
+ export interface TestStub {
23
+ testFilePath: string;
24
+ sourcePath: string;
25
+ content: string;
26
+ }
27
+ export interface TestGenResult {
28
+ untested: UncoveredFile[];
29
+ stubs: TestStub[];
30
+ totalSourceFiles: number;
31
+ testedCount: number;
32
+ }
33
+ export declare function extractExports(content: string): string[];
34
+ export declare function generateStub(sourcePath: string, exportNames: string[]): string;
35
+ export declare const testGenSkill: SkillModule;
36
+ //# sourceMappingURL=test-gen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-gen.d.ts","sourceRoot":"","sources":["../../src/skills/test-gen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,WAAW,EAA6B,MAAM,aAAa,CAAC;AAM1E,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAsBxD;AAQD,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CA8B9E;AAMD,eAAO,MAAM,YAAY,EAAE,WAkF1B,CAAC"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Skill: test-gen
3
+ *
4
+ * Detects exported TypeScript functions and classes that have no corresponding
5
+ * test file, then generates Vitest-compatible test stubs.
6
+ *
7
+ * Strategy:
8
+ * 1. Walk ctx.files for *.ts files (excluding *.test.ts and *.d.ts)
9
+ * 2. For each source file, check if a <name>.test.ts exists next to it
10
+ * 3. Parse exported function/class names via regex (fast, no full AST)
11
+ * 4. Generate a stub test file matching the project's Vitest patterns
12
+ *
13
+ * Output data:
14
+ * - untested: UncoveredFile[] — files with no test counterpart
15
+ * - stubs: TestStub[] — generated test stubs (not written to disk)
16
+ */
17
+ import { existsSync, readFileSync } from 'node:fs';
18
+ import { join, dirname, basename, extname } from 'node:path';
19
+ // ---------------------------------------------------------------------------
20
+ // Helpers
21
+ // ---------------------------------------------------------------------------
22
+ const EXPORT_FN_RE = /^export\s+(?:async\s+)?function\s+(\w+)/gm;
23
+ const EXPORT_CLASS_RE = /^export\s+(?:abstract\s+)?class\s+(\w+)/gm;
24
+ const EXPORT_CONST_FN_RE = /^export\s+(?:const|let)\s+(\w+)\s*[=:]/gm;
25
+ const EXPORT_DEFAULT_RE = /^export\s+default\s+(?:function\s+)?(\w+)/gm;
26
+ const REEXPORT_RE = /^export\s*\{[^}]+\}\s*from/m;
27
+ export function extractExports(content) {
28
+ // Skip pure re-export files
29
+ if (REEXPORT_RE.test(content) && !EXPORT_FN_RE.test(content))
30
+ return [];
31
+ const names = new Set();
32
+ let m;
33
+ EXPORT_FN_RE.lastIndex = 0;
34
+ while ((m = EXPORT_FN_RE.exec(content)) !== null)
35
+ names.add(m[1]);
36
+ EXPORT_CLASS_RE.lastIndex = 0;
37
+ while ((m = EXPORT_CLASS_RE.exec(content)) !== null)
38
+ names.add(m[1]);
39
+ EXPORT_CONST_FN_RE.lastIndex = 0;
40
+ while ((m = EXPORT_CONST_FN_RE.exec(content)) !== null) {
41
+ if (m[1] !== 'type' && m[1] !== 'interface')
42
+ names.add(m[1]);
43
+ }
44
+ EXPORT_DEFAULT_RE.lastIndex = 0;
45
+ while ((m = EXPORT_DEFAULT_RE.exec(content)) !== null)
46
+ names.add(m[1]);
47
+ return Array.from(names);
48
+ }
49
+ function testFilePath(sourcePath) {
50
+ const dir = dirname(sourcePath);
51
+ const base = basename(sourcePath, extname(sourcePath));
52
+ return join(dir, `${base}.test.ts`);
53
+ }
54
+ export function generateStub(sourcePath, exportNames) {
55
+ const base = basename(sourcePath, '.ts');
56
+ const relPath = `./${base}.js`;
57
+ const imports = exportNames.length > 0
58
+ ? `import { ${exportNames.join(', ')} } from '${relPath}';\n`
59
+ : `// import { ... } from '${relPath}';\n`;
60
+ const describes = exportNames.map((name) => [
61
+ `describe('${name}', () => {`,
62
+ ` it('should be defined', () => {`,
63
+ ` expect(${name}).toBeDefined();`,
64
+ ` });`,
65
+ ``,
66
+ ` it('TODO: add meaningful test', () => {`,
67
+ ` // Arrange`,
68
+ ` // Act`,
69
+ ` // Assert`,
70
+ ` expect(true).toBe(true);`,
71
+ ` });`,
72
+ `});`,
73
+ ].join('\n'));
74
+ return [
75
+ `import { describe, it, expect } from 'vitest';`,
76
+ imports,
77
+ ...describes,
78
+ ].join('\n');
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Skill
82
+ // ---------------------------------------------------------------------------
83
+ export const testGenSkill = {
84
+ id: 'test-gen',
85
+ name: 'Test Generator',
86
+ description: 'Detects untested exported functions/classes and generates Vitest test stubs',
87
+ version: '1.0.0',
88
+ enabled: false,
89
+ tags: ['dev', 'quality'],
90
+ async execute(ctx) {
91
+ const tsFiles = ctx.files
92
+ .filter((f) => f.endsWith('.ts') &&
93
+ !f.endsWith('.test.ts') &&
94
+ !f.endsWith('.d.ts') &&
95
+ !f.includes('node_modules') &&
96
+ !f.includes('/dist/'));
97
+ if (tsFiles.length === 0) {
98
+ return {
99
+ success: true,
100
+ output: 'No TypeScript source files found in context.',
101
+ data: { untested: [], stubs: [], totalSourceFiles: 0, testedCount: 0 },
102
+ };
103
+ }
104
+ const untested = [];
105
+ let testedCount = 0;
106
+ for (const relPath of tsFiles) {
107
+ const absPath = join(ctx.projectRoot, relPath);
108
+ const testPath = testFilePath(absPath);
109
+ if (existsSync(testPath)) {
110
+ testedCount++;
111
+ continue;
112
+ }
113
+ let content = '';
114
+ try {
115
+ content = readFileSync(absPath, 'utf-8');
116
+ }
117
+ catch {
118
+ continue;
119
+ }
120
+ const exports = extractExports(content);
121
+ if (exports.length > 0) {
122
+ untested.push({ sourcePath: relPath, exports });
123
+ }
124
+ }
125
+ const stubs = untested.map((u) => ({
126
+ testFilePath: testFilePath(join(ctx.projectRoot, u.sourcePath)),
127
+ sourcePath: u.sourcePath,
128
+ content: generateStub(u.sourcePath, u.exports),
129
+ }));
130
+ const lines = [
131
+ `🧪 Test Generator`,
132
+ `Source files: ${tsFiles.length} | Tested: ${testedCount} | Missing tests: ${untested.length}`,
133
+ '',
134
+ ];
135
+ if (untested.length === 0) {
136
+ lines.push(' ✅ All exported files have test counterparts.');
137
+ }
138
+ else {
139
+ lines.push(' Files missing tests:');
140
+ for (const u of untested) {
141
+ lines.push(` ⬜ ${u.sourcePath}`);
142
+ lines.push(` Exports: ${u.exports.slice(0, 5).join(', ')}${u.exports.length > 5 ? '…' : ''}`);
143
+ }
144
+ lines.push('');
145
+ lines.push(` 💡 ${stubs.length} test stub(s) generated (see data.stubs)`);
146
+ }
147
+ return {
148
+ success: true,
149
+ output: lines.join('\n'),
150
+ data: { untested, stubs, totalSourceFiles: tsFiles.length, testedCount },
151
+ };
152
+ },
153
+ };
154
+ //# sourceMappingURL=test-gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-gen.js","sourceRoot":"","sources":["../../src/skills/test-gen.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * WeaveSkills — Type Definitions
3
+ *
4
+ * Core contracts for the Skill Module system (M19).
5
+ * All skill modules implement SkillModule; the registry manages their lifecycle.
6
+ */
7
+ /** Git state available to skills that need to inspect staged/unstaged changes. */
8
+ export interface SkillGitContext {
9
+ /** Current branch name */
10
+ branch: string;
11
+ /** Paths of staged files (`git diff --name-only --cached`) */
12
+ stagedFiles: string[];
13
+ /** Paths of unstaged modified files */
14
+ unstagedFiles: string[];
15
+ /** Full diff of staged changes (may be truncated) */
16
+ stagedDiff: string;
17
+ }
18
+ /**
19
+ * Context object injected into every skill at execution time.
20
+ * Skills may use any subset — unused fields can be null when unavailable.
21
+ */
22
+ export interface SkillContext {
23
+ /** Absolute path to the project root */
24
+ projectRoot: string;
25
+ /** File paths in the project (relative to projectRoot) */
26
+ files: string[];
27
+ /**
28
+ * Opaque knowledge-graph snapshot.
29
+ * Typed as unknown to avoid a circular dependency on @openweave/weave-graph.
30
+ * Skills that need the graph should cast to the WeaveGraph types they import.
31
+ */
32
+ graph: unknown | null;
33
+ /** Active session metadata */
34
+ session: {
35
+ id: string;
36
+ chatId?: string;
37
+ } | null;
38
+ /** Git workspace state — null if not a git repository */
39
+ git: SkillGitContext | null;
40
+ }
41
+ /** Structured result returned by every skill execution. */
42
+ export interface SkillResult {
43
+ /** Whether the skill completed successfully */
44
+ success: boolean;
45
+ /** Human-readable summary of the outcome (for CLI / agent logging) */
46
+ output: string;
47
+ /** Arbitrary structured payload — skill-specific */
48
+ data?: unknown;
49
+ /** Error message when success === false */
50
+ error?: string;
51
+ }
52
+ /**
53
+ * Base contract every skill module must implement.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import type { SkillModule, SkillContext, SkillResult } from '@openweave/weave-skills';
58
+ *
59
+ * export const mySkill: SkillModule = {
60
+ * id: 'my-skill',
61
+ * name: 'My Skill',
62
+ * description: 'Does something useful',
63
+ * version: '1.0.0',
64
+ * enabled: true,
65
+ * tags: ['dev'],
66
+ * async execute(ctx: SkillContext): Promise<SkillResult> {
67
+ * return { success: true, output: 'done' };
68
+ * },
69
+ * };
70
+ * ```
71
+ */
72
+ export interface SkillModule {
73
+ /** Unique kebab-case identifier (e.g. 'auto-fix', 'code-review') */
74
+ id: string;
75
+ /** Display name */
76
+ name: string;
77
+ /** Short description shown in `weave skills list` */
78
+ description: string;
79
+ /** Semver string */
80
+ version: string;
81
+ /**
82
+ * Whether this skill is active by default when first registered.
83
+ * The registry always defers to the loaded SkillConfig over this value.
84
+ */
85
+ enabled: boolean;
86
+ /** Optional categorisation tags ('dev' | 'devops' | 'dx' | 'custom') */
87
+ tags?: string[];
88
+ /**
89
+ * Execute the skill.
90
+ * @param context - Runtime context (files, graph, session, git)
91
+ * @returns Structured result with success flag, output message and optional data
92
+ */
93
+ execute(context: SkillContext): Promise<SkillResult>;
94
+ }
95
+ /** Internal record maintained by SkillRegistry for each registered skill. */
96
+ export interface RegisteredSkill {
97
+ module: SkillModule;
98
+ /** Current enabled state — authoritative source of truth at runtime */
99
+ enabled: boolean;
100
+ /** When the skill was registered in this process */
101
+ registeredAt: Date;
102
+ }
103
+ /**
104
+ * Persisted skill configuration stored in `.weave.config.json`.
105
+ *
106
+ * @example
107
+ * ```json
108
+ * {
109
+ * "skills": {
110
+ * "auto-fix": true,
111
+ * "code-review": false,
112
+ * "test-gen": true
113
+ * }
114
+ * }
115
+ * ```
116
+ */
117
+ export interface SkillConfig {
118
+ /** Map of skill id → enabled flag */
119
+ skills: Record<string, boolean>;
120
+ }
121
+ /**
122
+ * Shape of the full `.weave.config.json` file.
123
+ * Only the `skills` section is managed by weave-skills; other properties are preserved.
124
+ */
125
+ export interface WeaveConfig {
126
+ /** Project name */
127
+ projectName?: string;
128
+ /** Skill enable/disable flags */
129
+ skills?: Record<string, boolean>;
130
+ /** Other config keys are preserved verbatim */
131
+ [key: string]: unknown;
132
+ }
133
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,uCAAuC;IACvC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB,8BAA8B;IAC9B,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,yDAAyD;IACzD,GAAG,EAAE,eAAe,GAAG,IAAI,CAAC;CAC7B;AAMD,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CACtD;AAMD,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,YAAY,EAAE,IAAI,CAAC;CACpB;AAMD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,mBAAmB;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,+CAA+C;IAC/C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * WeaveSkills — Type Definitions
3
+ *
4
+ * Core contracts for the Skill Module system (M19).
5
+ * All skill modules implement SkillModule; the registry manages their lifecycle.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@openweave/weave-skills",
3
+ "version": "1.0.0",
4
+ "description": "WeaveSkills — Skill Module Registry for OpenWeave agent extensibility",
5
+ "private": false,
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "import": "./dist/index.js",
17
+ "types": "./dist/index.d.ts"
18
+ }
19
+ },
20
+ "keywords": [
21
+ "openweave",
22
+ "skills",
23
+ "plugins",
24
+ "agent",
25
+ "extensibility"
26
+ ],
27
+ "devDependencies": {
28
+ "@types/node": "22.10.2",
29
+ "typescript": "5.7.2",
30
+ "vitest": "3.0.0"
31
+ },
32
+ "scripts": {
33
+ "build": "tsc",
34
+ "dev": "tsc --watch",
35
+ "test": "vitest",
36
+ "lint": "eslint src --max-warnings 0",
37
+ "clean": "rm -rf dist"
38
+ }
39
+ }