specweave 0.23.0 → 0.23.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.
Files changed (76) hide show
  1. package/.claude-plugin/marketplace.json +0 -88
  2. package/CLAUDE.md +100 -0
  3. package/bin/fix-marketplace-errors.sh +8 -8
  4. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  5. package/dist/src/cli/helpers/issue-tracker/index.js +5 -17
  6. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  7. package/dist/src/core/repo-structure/repo-id-generator.d.ts +20 -0
  8. package/dist/src/core/repo-structure/repo-id-generator.d.ts.map +1 -1
  9. package/dist/src/core/repo-structure/repo-id-generator.js +44 -0
  10. package/dist/src/core/repo-structure/repo-id-generator.js.map +1 -1
  11. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  12. package/dist/src/core/repo-structure/repo-structure-manager.js +5 -2
  13. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  14. package/package.json +1 -1
  15. package/plugins/specweave/.claude-plugin/plugin.json +10 -0
  16. package/plugins/specweave/commands/specweave-archive.md +51 -15
  17. package/plugins/specweave/hooks/post-edit-spec.sh +62 -9
  18. package/plugins/specweave/hooks/post-metadata-change.sh +160 -0
  19. package/plugins/specweave/hooks/post-write-spec.sh +62 -8
  20. package/plugins/specweave/lib/hooks/auto-transition.js.bak +50 -0
  21. package/plugins/specweave/lib/hooks/auto-transition.ts.bak +84 -0
  22. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.js.bak +0 -0
  23. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.ts.bak +89 -0
  24. package/plugins/specweave/lib/hooks/git-diff-analyzer.js.bak +142 -0
  25. package/plugins/specweave/lib/hooks/git-diff-analyzer.ts.bak +269 -0
  26. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.js.bak +0 -0
  27. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.ts.bak +60 -0
  28. package/plugins/specweave/lib/hooks/invoke-translator-skill.js.bak +155 -0
  29. package/plugins/specweave/lib/hooks/invoke-translator-skill.ts.bak +264 -0
  30. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.js.bak +0 -0
  31. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.ts.bak +42 -0
  32. package/plugins/specweave/lib/hooks/prepare-reflection-context.js.bak +110 -0
  33. package/plugins/specweave/lib/hooks/prepare-reflection-context.ts.bak +178 -0
  34. package/plugins/specweave/lib/hooks/reflection-config-loader.d.js.bak +0 -0
  35. package/plugins/specweave/lib/hooks/reflection-config-loader.d.ts.bak +45 -0
  36. package/plugins/specweave/lib/hooks/reflection-config-loader.js.bak +92 -0
  37. package/plugins/specweave/lib/hooks/reflection-config-loader.ts.bak +156 -0
  38. package/plugins/specweave/lib/hooks/reflection-parser.d.js.bak +0 -0
  39. package/plugins/specweave/lib/hooks/reflection-parser.d.ts.bak +33 -0
  40. package/plugins/specweave/lib/hooks/reflection-parser.js.bak +301 -0
  41. package/plugins/specweave/lib/hooks/reflection-parser.ts.bak +484 -0
  42. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.js.bak +0 -0
  43. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.ts.bak +56 -0
  44. package/plugins/specweave/lib/hooks/reflection-prompt-builder.js.bak +182 -0
  45. package/plugins/specweave/lib/hooks/reflection-prompt-builder.ts.bak +306 -0
  46. package/plugins/specweave/lib/hooks/reflection-storage.d.js.bak +0 -0
  47. package/plugins/specweave/lib/hooks/reflection-storage.d.ts.bak +64 -0
  48. package/plugins/specweave/lib/hooks/reflection-storage.js.bak +231 -0
  49. package/plugins/specweave/lib/hooks/reflection-storage.ts.bak +369 -0
  50. package/plugins/specweave/lib/hooks/run-self-reflection.d.js.bak +0 -0
  51. package/plugins/specweave/lib/hooks/run-self-reflection.d.ts.bak +43 -0
  52. package/plugins/specweave/lib/hooks/run-self-reflection.js.bak +132 -0
  53. package/plugins/specweave/lib/hooks/run-self-reflection.ts.bak +258 -0
  54. package/plugins/specweave/lib/hooks/sync-cache.js.bak +294 -0
  55. package/plugins/specweave/lib/hooks/sync-living-docs.d.js.bak +1 -0
  56. package/plugins/specweave/lib/hooks/sync-living-docs.d.ts.bak +27 -0
  57. package/plugins/specweave/lib/hooks/sync-living-docs.js.bak +339 -0
  58. package/plugins/specweave/lib/hooks/sync-us-tasks.js.bak +476 -0
  59. package/plugins/specweave/lib/hooks/translate-file.d.js.bak +0 -0
  60. package/plugins/specweave/lib/hooks/translate-file.d.ts.bak +59 -0
  61. package/plugins/specweave/lib/hooks/translate-file.js.bak +289 -0
  62. package/plugins/specweave/lib/hooks/translate-file.ts.bak +428 -0
  63. package/plugins/specweave/lib/hooks/translate-living-docs.d.js.bak +0 -0
  64. package/plugins/specweave/lib/hooks/translate-living-docs.d.ts.bak +13 -0
  65. package/plugins/specweave/lib/hooks/translate-living-docs.js.bak +119 -0
  66. package/plugins/specweave/lib/hooks/translate-living-docs.ts.bak +224 -0
  67. package/plugins/specweave/lib/hooks/update-ac-status.js.bak +51 -0
  68. package/plugins/specweave/lib/hooks/update-ac-status.ts.bak +103 -0
  69. package/plugins/specweave/lib/hooks/update-tasks-md.d.js.bak +1 -0
  70. package/plugins/specweave/lib/hooks/update-tasks-md.d.ts.bak +29 -0
  71. package/plugins/specweave/lib/hooks/update-tasks-md.js.bak +296 -0
  72. package/plugins/specweave/lib/hooks/update-tasks-md.ts.bak +489 -0
  73. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +1 -0
  74. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  75. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  76. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +6225 -0
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { AutoTransitionManager } from "../../../../dist/src/core/increment/auto-transition-manager.js";
3
+ async function main() {
4
+ const event = process.argv[2];
5
+ const incrementId = process.argv[3];
6
+ const force = process.argv.includes("--force");
7
+ if (!event || !incrementId) {
8
+ console.error("Usage: auto-transition.ts <event> <increment-id> [--force]");
9
+ console.error("Events: spec-created, tasks-created, task-started, auto-correct");
10
+ console.error("Example: node auto-transition.ts spec-created 0039-ultra-smart-next-command");
11
+ process.exit(1);
12
+ }
13
+ const projectRoot = process.cwd();
14
+ const manager = new AutoTransitionManager(projectRoot);
15
+ let result;
16
+ try {
17
+ switch (event) {
18
+ case "spec-created":
19
+ result = await manager.handleSpecCreated(incrementId);
20
+ break;
21
+ case "tasks-created":
22
+ result = await manager.handleTasksCreated(incrementId);
23
+ break;
24
+ case "task-started":
25
+ result = await manager.handleTaskStarted(incrementId);
26
+ break;
27
+ case "auto-correct":
28
+ result = await manager.autoCorrect(incrementId, force);
29
+ break;
30
+ default:
31
+ console.error(`\u274C Unknown event: ${event}`);
32
+ console.error("Valid events: spec-created, tasks-created, task-started, auto-correct");
33
+ process.exit(1);
34
+ }
35
+ if (result.transitioned) {
36
+ console.log(`\u2705 Auto-transition: ${result.from} \u2192 ${result.to}`);
37
+ console.log(` Reason: ${result.reason}`);
38
+ } else {
39
+ console.log(`\u2139\uFE0F No transition: ${result.reason}`);
40
+ }
41
+ process.exit(0);
42
+ } catch (error) {
43
+ console.error("\u274C Auto-transition error:", error);
44
+ process.exit(1);
45
+ }
46
+ }
47
+ const isMainModule = import.meta.url === `file://${process.argv[1]}`;
48
+ if (isMainModule) {
49
+ main();
50
+ }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Auto-Transition Hook Handler
5
+ *
6
+ * CLI wrapper for AutoTransitionManager
7
+ * Called by bash hooks to trigger status transitions
8
+ *
9
+ * Events:
10
+ * - spec-created: When spec.md is created (BACKLOG → PLANNING)
11
+ * - tasks-created: When tasks.md is created (PLANNING/BACKLOG → ACTIVE)
12
+ * - task-started: When first task is started (PLANNING → ACTIVE)
13
+ * - auto-correct: Fix status based on artifacts
14
+ *
15
+ * Usage:
16
+ * node auto-transition.ts spec-created 0039-ultra-smart-next-command
17
+ * node auto-transition.ts tasks-created 0039-ultra-smart-next-command
18
+ * node auto-transition.ts task-started 0039-ultra-smart-next-command
19
+ * node auto-transition.ts auto-correct 0039-ultra-smart-next-command
20
+ */
21
+
22
+ import { AutoTransitionManager } from '../../../../dist/src/core/increment/auto-transition-manager.js';
23
+
24
+ async function main() {
25
+ const event = process.argv[2];
26
+ const incrementId = process.argv[3];
27
+ const force = process.argv.includes('--force');
28
+
29
+ if (!event || !incrementId) {
30
+ console.error('Usage: auto-transition.ts <event> <increment-id> [--force]');
31
+ console.error('Events: spec-created, tasks-created, task-started, auto-correct');
32
+ console.error('Example: node auto-transition.ts spec-created 0039-ultra-smart-next-command');
33
+ process.exit(1);
34
+ }
35
+
36
+ const projectRoot = process.cwd();
37
+ const manager = new AutoTransitionManager(projectRoot);
38
+
39
+ let result;
40
+
41
+ try {
42
+ switch (event) {
43
+ case 'spec-created':
44
+ result = await manager.handleSpecCreated(incrementId);
45
+ break;
46
+
47
+ case 'tasks-created':
48
+ result = await manager.handleTasksCreated(incrementId);
49
+ break;
50
+
51
+ case 'task-started':
52
+ result = await manager.handleTaskStarted(incrementId);
53
+ break;
54
+
55
+ case 'auto-correct':
56
+ result = await manager.autoCorrect(incrementId, force);
57
+ break;
58
+
59
+ default:
60
+ console.error(`❌ Unknown event: ${event}`);
61
+ console.error('Valid events: spec-created, tasks-created, task-started, auto-correct');
62
+ process.exit(1);
63
+ }
64
+
65
+ // Display result
66
+ if (result.transitioned) {
67
+ console.log(`✅ Auto-transition: ${result.from} → ${result.to}`);
68
+ console.log(` Reason: ${result.reason}`);
69
+ } else {
70
+ console.log(`ℹ️ No transition: ${result.reason}`);
71
+ }
72
+
73
+ process.exit(0);
74
+ } catch (error) {
75
+ console.error('❌ Auto-transition error:', error);
76
+ process.exit(1);
77
+ }
78
+ }
79
+
80
+ // Run if executed directly
81
+ const isMainModule = import.meta.url === `file://${process.argv[1]}`;
82
+ if (isMainModule) {
83
+ main();
84
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Git Diff Analyzer
3
+ *
4
+ * Extracts modified files from git diff for reflection analysis
5
+ * Parses git diff output to get file changes, line counts, and content
6
+ *
7
+ * @module git-diff-analyzer
8
+ */
9
+ import { GitDiffInfo } from './types/reflection-types';
10
+ /**
11
+ * Check if directory is a git repository
12
+ * @param dir Directory to check (defaults to cwd)
13
+ * @returns True if directory is in a git repository
14
+ */
15
+ export declare function isGitRepository(dir?: string): boolean;
16
+ /**
17
+ * Get list of modified files in the working directory
18
+ * Includes both staged and unstaged changes
19
+ *
20
+ * @param cwd Working directory (optional, defaults to process.cwd())
21
+ * @returns Array of file paths relative to git root
22
+ */
23
+ export declare function getModifiedFilesList(cwd?: string): string[];
24
+ /**
25
+ * Parse git diff numstat output to get line counts
26
+ * Format: <added>\t<removed>\t<filename>
27
+ *
28
+ * @param numstatOutput Output from git diff --numstat
29
+ * @returns Map of filename to {added, removed} counts
30
+ */
31
+ export declare function parseNumstat(numstatOutput: string): Map<string, {
32
+ added: number;
33
+ removed: number;
34
+ }>;
35
+ /**
36
+ * Get diff content for a specific file
37
+ * @param file File path relative to git root
38
+ * @param cwd Working directory (optional)
39
+ * @returns Diff content as string
40
+ */
41
+ export declare function getFileDiff(file: string, cwd?: string): string;
42
+ /**
43
+ * Get current file content
44
+ * @param file File path (can be absolute or relative to cwd)
45
+ * @param cwd Working directory (optional)
46
+ * @returns File content as string, or empty string if file doesn't exist
47
+ */
48
+ export declare function getFileContent(file: string, cwd?: string): string;
49
+ /**
50
+ * Get modified files with diff information
51
+ * Main function for reflection analysis
52
+ *
53
+ * @param cwd Working directory (optional, defaults to process.cwd())
54
+ * @param maxFiles Maximum number of files to return (optional, defaults to 100)
55
+ * @returns Array of GitDiffInfo objects with file changes
56
+ */
57
+ export declare function getModifiedFiles(cwd?: string, maxFiles?: number): GitDiffInfo[];
58
+ /**
59
+ * Get summary statistics for modified files
60
+ * Useful for reflection metadata
61
+ *
62
+ * @param modifiedFiles Array of GitDiffInfo objects
63
+ * @returns Summary with file count, total lines added/removed
64
+ */
65
+ export declare function getModifiedFilesSummary(modifiedFiles: GitDiffInfo[]): {
66
+ count: number;
67
+ linesAdded: number;
68
+ linesRemoved: number;
69
+ totalChanges: number;
70
+ };
71
+ /**
72
+ * Filter files by extension
73
+ * Useful for focusing reflection on specific file types
74
+ *
75
+ * @param modifiedFiles Array of GitDiffInfo objects
76
+ * @param extensions Array of file extensions (e.g., ['.ts', '.js'])
77
+ * @returns Filtered array of GitDiffInfo objects
78
+ */
79
+ export declare function filterFilesByExtension(modifiedFiles: GitDiffInfo[], extensions: string[]): GitDiffInfo[];
80
+ /**
81
+ * Exclude files matching patterns
82
+ * Useful for excluding generated files, test files, etc.
83
+ *
84
+ * @param modifiedFiles Array of GitDiffInfo objects
85
+ * @param patterns Array of glob patterns to exclude
86
+ * @returns Filtered array of GitDiffInfo objects
87
+ */
88
+ export declare function excludeFilesByPattern(modifiedFiles: GitDiffInfo[], patterns: string[]): GitDiffInfo[];
89
+ //# sourceMappingURL=git-diff-analyzer.d.ts.map
@@ -0,0 +1,142 @@
1
+ import { execSync } from "child_process";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ function executeGitCommand(command, cwd) {
5
+ try {
6
+ return execSync(command, {
7
+ cwd: cwd || process.cwd(),
8
+ encoding: "utf-8",
9
+ stdio: ["pipe", "pipe", "pipe"]
10
+ });
11
+ } catch (error) {
12
+ throw new Error(`Git command failed: ${command}. ${error.message}`);
13
+ }
14
+ }
15
+ function isGitRepository(dir = process.cwd()) {
16
+ try {
17
+ executeGitCommand("git rev-parse --git-dir", dir);
18
+ return true;
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+ function getModifiedFilesList(cwd) {
24
+ if (!isGitRepository(cwd)) {
25
+ return [];
26
+ }
27
+ try {
28
+ const output = executeGitCommand("git diff --name-only HEAD", cwd);
29
+ if (!output.trim()) {
30
+ return [];
31
+ }
32
+ return output.trim().split("\n").filter((file) => file.length > 0).filter((file) => !file.startsWith(".git/"));
33
+ } catch {
34
+ return [];
35
+ }
36
+ }
37
+ function parseNumstat(numstatOutput) {
38
+ const stats = /* @__PURE__ */ new Map();
39
+ if (!numstatOutput.trim()) {
40
+ return stats;
41
+ }
42
+ const lines = numstatOutput.trim().split("\n");
43
+ for (const line of lines) {
44
+ const parts = line.split(" ");
45
+ if (parts.length < 3) continue;
46
+ const added = parts[0] === "-" ? 0 : parseInt(parts[0], 10);
47
+ const removed = parts[1] === "-" ? 0 : parseInt(parts[1], 10);
48
+ const filename = parts[2];
49
+ stats.set(filename, { added, removed });
50
+ }
51
+ return stats;
52
+ }
53
+ function getFileDiff(file, cwd) {
54
+ if (!isGitRepository(cwd)) {
55
+ return "";
56
+ }
57
+ try {
58
+ const output = executeGitCommand(`git diff HEAD -- "${file}"`, cwd);
59
+ return output;
60
+ } catch {
61
+ return "";
62
+ }
63
+ }
64
+ function getFileContent(file, cwd) {
65
+ try {
66
+ const workingDir = cwd || process.cwd();
67
+ const absolutePath = path.isAbsolute(file) ? file : path.join(workingDir, file);
68
+ if (!fs.existsSync(absolutePath)) {
69
+ return "";
70
+ }
71
+ return fs.readFileSync(absolutePath, "utf-8");
72
+ } catch {
73
+ return "";
74
+ }
75
+ }
76
+ function getModifiedFiles(cwd, maxFiles = 100) {
77
+ if (!isGitRepository(cwd)) {
78
+ return [];
79
+ }
80
+ const workingDir = cwd || process.cwd();
81
+ const modifiedFiles = getModifiedFilesList(workingDir);
82
+ if (modifiedFiles.length === 0) {
83
+ return [];
84
+ }
85
+ const filesToAnalyze = modifiedFiles.slice(0, maxFiles);
86
+ let numstatOutput = "";
87
+ try {
88
+ numstatOutput = executeGitCommand("git diff --numstat HEAD", workingDir);
89
+ } catch {
90
+ }
91
+ const stats = parseNumstat(numstatOutput);
92
+ const result = [];
93
+ for (const file of filesToAnalyze) {
94
+ const fileStat = stats.get(file) || { added: 0, removed: 0 };
95
+ const diffContent = getFileDiff(file, workingDir);
96
+ if (fileStat.added > 0 || fileStat.removed > 0 || diffContent.length > 0) {
97
+ result.push({
98
+ file,
99
+ linesAdded: fileStat.added,
100
+ linesRemoved: fileStat.removed,
101
+ content: diffContent
102
+ });
103
+ }
104
+ }
105
+ return result;
106
+ }
107
+ function getModifiedFilesSummary(modifiedFiles) {
108
+ return {
109
+ count: modifiedFiles.length,
110
+ linesAdded: modifiedFiles.reduce((sum, file) => sum + file.linesAdded, 0),
111
+ linesRemoved: modifiedFiles.reduce((sum, file) => sum + file.linesRemoved, 0),
112
+ totalChanges: modifiedFiles.reduce(
113
+ (sum, file) => sum + file.linesAdded + file.linesRemoved,
114
+ 0
115
+ )
116
+ };
117
+ }
118
+ function filterFilesByExtension(modifiedFiles, extensions) {
119
+ return modifiedFiles.filter((file) => {
120
+ const ext = path.extname(file.file).toLowerCase();
121
+ return extensions.some((allowedExt) => ext === allowedExt.toLowerCase());
122
+ });
123
+ }
124
+ function excludeFilesByPattern(modifiedFiles, patterns) {
125
+ return modifiedFiles.filter((file) => {
126
+ return !patterns.some((pattern) => {
127
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
128
+ return regex.test(file.file);
129
+ });
130
+ });
131
+ }
132
+ export {
133
+ excludeFilesByPattern,
134
+ filterFilesByExtension,
135
+ getFileContent,
136
+ getFileDiff,
137
+ getModifiedFiles,
138
+ getModifiedFilesList,
139
+ getModifiedFilesSummary,
140
+ isGitRepository,
141
+ parseNumstat
142
+ };
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Git Diff Analyzer
3
+ *
4
+ * Extracts modified files from git diff for reflection analysis
5
+ * Parses git diff output to get file changes, line counts, and content
6
+ *
7
+ * @module git-diff-analyzer
8
+ */
9
+
10
+ import { execSync } from 'child_process';
11
+ import fs from 'fs-extra';
12
+ import path from 'path';
13
+ import { GitDiffInfo } from './types/reflection-types';
14
+
15
+ /**
16
+ * Execute git command safely
17
+ * @param command Git command to execute
18
+ * @param cwd Working directory (optional)
19
+ * @returns Command output as string
20
+ * @throws Error if command fails
21
+ */
22
+ function executeGitCommand(command: string, cwd?: string): string {
23
+ try {
24
+ return execSync(command, {
25
+ cwd: cwd || process.cwd(),
26
+ encoding: 'utf-8',
27
+ stdio: ['pipe', 'pipe', 'pipe']
28
+ });
29
+ } catch (error: any) {
30
+ throw new Error(`Git command failed: ${command}. ${error.message}`);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Check if directory is a git repository
36
+ * @param dir Directory to check (defaults to cwd)
37
+ * @returns True if directory is in a git repository
38
+ */
39
+ export function isGitRepository(dir: string = process.cwd()): boolean {
40
+ try {
41
+ executeGitCommand('git rev-parse --git-dir', dir);
42
+ return true;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Get list of modified files in the working directory
50
+ * Includes both staged and unstaged changes
51
+ *
52
+ * @param cwd Working directory (optional, defaults to process.cwd())
53
+ * @returns Array of file paths relative to git root
54
+ */
55
+ export function getModifiedFilesList(cwd?: string): string[] {
56
+ if (!isGitRepository(cwd)) {
57
+ return [];
58
+ }
59
+
60
+ try {
61
+ // Get both staged and unstaged changes
62
+ const output = executeGitCommand('git diff --name-only HEAD', cwd);
63
+
64
+ if (!output.trim()) {
65
+ return [];
66
+ }
67
+
68
+ return output
69
+ .trim()
70
+ .split('\n')
71
+ .filter(file => file.length > 0)
72
+ .filter(file => !file.startsWith('.git/')); // Exclude .git directory
73
+ } catch {
74
+ return [];
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Parse git diff numstat output to get line counts
80
+ * Format: <added>\t<removed>\t<filename>
81
+ *
82
+ * @param numstatOutput Output from git diff --numstat
83
+ * @returns Map of filename to {added, removed} counts
84
+ */
85
+ export function parseNumstat(numstatOutput: string): Map<string, { added: number; removed: number }> {
86
+ const stats = new Map<string, { added: number; removed: number }>();
87
+
88
+ if (!numstatOutput.trim()) {
89
+ return stats;
90
+ }
91
+
92
+ const lines = numstatOutput.trim().split('\n');
93
+
94
+ for (const line of lines) {
95
+ const parts = line.split('\t');
96
+ if (parts.length < 3) continue;
97
+
98
+ const added = parts[0] === '-' ? 0 : parseInt(parts[0], 10);
99
+ const removed = parts[1] === '-' ? 0 : parseInt(parts[1], 10);
100
+ const filename = parts[2];
101
+
102
+ stats.set(filename, { added, removed });
103
+ }
104
+
105
+ return stats;
106
+ }
107
+
108
+ /**
109
+ * Get diff content for a specific file
110
+ * @param file File path relative to git root
111
+ * @param cwd Working directory (optional)
112
+ * @returns Diff content as string
113
+ */
114
+ export function getFileDiff(file: string, cwd?: string): string {
115
+ if (!isGitRepository(cwd)) {
116
+ return '';
117
+ }
118
+
119
+ try {
120
+ // Get unified diff for the file
121
+ const output = executeGitCommand(`git diff HEAD -- "${file}"`, cwd);
122
+ return output;
123
+ } catch {
124
+ return '';
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Get current file content
130
+ * @param file File path (can be absolute or relative to cwd)
131
+ * @param cwd Working directory (optional)
132
+ * @returns File content as string, or empty string if file doesn't exist
133
+ */
134
+ export function getFileContent(file: string, cwd?: string): string {
135
+ try {
136
+ const workingDir = cwd || process.cwd();
137
+ const absolutePath = path.isAbsolute(file) ? file : path.join(workingDir, file);
138
+
139
+ if (!fs.existsSync(absolutePath)) {
140
+ return '';
141
+ }
142
+
143
+ return fs.readFileSync(absolutePath, 'utf-8');
144
+ } catch {
145
+ return '';
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Get modified files with diff information
151
+ * Main function for reflection analysis
152
+ *
153
+ * @param cwd Working directory (optional, defaults to process.cwd())
154
+ * @param maxFiles Maximum number of files to return (optional, defaults to 100)
155
+ * @returns Array of GitDiffInfo objects with file changes
156
+ */
157
+ export function getModifiedFiles(
158
+ cwd?: string,
159
+ maxFiles: number = 100
160
+ ): GitDiffInfo[] {
161
+ if (!isGitRepository(cwd)) {
162
+ return [];
163
+ }
164
+
165
+ const workingDir = cwd || process.cwd();
166
+
167
+ // Get list of modified files
168
+ const modifiedFiles = getModifiedFilesList(workingDir);
169
+
170
+ if (modifiedFiles.length === 0) {
171
+ return [];
172
+ }
173
+
174
+ // Limit number of files to prevent overwhelming the analysis
175
+ const filesToAnalyze = modifiedFiles.slice(0, maxFiles);
176
+
177
+ // Get line count statistics
178
+ let numstatOutput = '';
179
+ try {
180
+ numstatOutput = executeGitCommand('git diff --numstat HEAD', workingDir);
181
+ } catch {
182
+ // If numstat fails, continue with empty stats
183
+ }
184
+
185
+ const stats = parseNumstat(numstatOutput);
186
+
187
+ // Build GitDiffInfo array
188
+ const result: GitDiffInfo[] = [];
189
+
190
+ for (const file of filesToAnalyze) {
191
+ const fileStat = stats.get(file) || { added: 0, removed: 0 };
192
+ const diffContent = getFileDiff(file, workingDir);
193
+
194
+ // Only include files with actual changes
195
+ if (fileStat.added > 0 || fileStat.removed > 0 || diffContent.length > 0) {
196
+ result.push({
197
+ file,
198
+ linesAdded: fileStat.added,
199
+ linesRemoved: fileStat.removed,
200
+ content: diffContent
201
+ });
202
+ }
203
+ }
204
+
205
+ return result;
206
+ }
207
+
208
+ /**
209
+ * Get summary statistics for modified files
210
+ * Useful for reflection metadata
211
+ *
212
+ * @param modifiedFiles Array of GitDiffInfo objects
213
+ * @returns Summary with file count, total lines added/removed
214
+ */
215
+ export function getModifiedFilesSummary(modifiedFiles: GitDiffInfo[]): {
216
+ count: number;
217
+ linesAdded: number;
218
+ linesRemoved: number;
219
+ totalChanges: number;
220
+ } {
221
+ return {
222
+ count: modifiedFiles.length,
223
+ linesAdded: modifiedFiles.reduce((sum, file) => sum + file.linesAdded, 0),
224
+ linesRemoved: modifiedFiles.reduce((sum, file) => sum + file.linesRemoved, 0),
225
+ totalChanges: modifiedFiles.reduce(
226
+ (sum, file) => sum + file.linesAdded + file.linesRemoved,
227
+ 0
228
+ )
229
+ };
230
+ }
231
+
232
+ /**
233
+ * Filter files by extension
234
+ * Useful for focusing reflection on specific file types
235
+ *
236
+ * @param modifiedFiles Array of GitDiffInfo objects
237
+ * @param extensions Array of file extensions (e.g., ['.ts', '.js'])
238
+ * @returns Filtered array of GitDiffInfo objects
239
+ */
240
+ export function filterFilesByExtension(
241
+ modifiedFiles: GitDiffInfo[],
242
+ extensions: string[]
243
+ ): GitDiffInfo[] {
244
+ return modifiedFiles.filter(file => {
245
+ const ext = path.extname(file.file).toLowerCase();
246
+ return extensions.some(allowedExt => ext === allowedExt.toLowerCase());
247
+ });
248
+ }
249
+
250
+ /**
251
+ * Exclude files matching patterns
252
+ * Useful for excluding generated files, test files, etc.
253
+ *
254
+ * @param modifiedFiles Array of GitDiffInfo objects
255
+ * @param patterns Array of glob patterns to exclude
256
+ * @returns Filtered array of GitDiffInfo objects
257
+ */
258
+ export function excludeFilesByPattern(
259
+ modifiedFiles: GitDiffInfo[],
260
+ patterns: string[]
261
+ ): GitDiffInfo[] {
262
+ return modifiedFiles.filter(file => {
263
+ return !patterns.some(pattern => {
264
+ // Simple pattern matching (supports * wildcard)
265
+ const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
266
+ return regex.test(file.file);
267
+ });
268
+ });
269
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Translator Skill Invocation Utility
3
+ *
4
+ * Provides programmatic invocation of the translator skill for automated translation.
5
+ * Used by hooks and CLI scripts to translate content without manual intervention.
6
+ *
7
+ * @see plugins/specweave/skills/translator/SKILL.md
8
+ * @see plugins/specweave/commands/translate.md
9
+ */
10
+ import { type SupportedLanguage } from '../../../../src/utils/translation.js';
11
+ /**
12
+ * Translation result
13
+ */
14
+ export interface TranslationResult {
15
+ success: boolean;
16
+ sourceLanguage: SupportedLanguage;
17
+ targetLanguage: SupportedLanguage;
18
+ originalContent: string;
19
+ translatedContent?: string;
20
+ error?: string;
21
+ }
22
+ /**
23
+ * Invokes translator skill to translate content
24
+ *
25
+ * This function integrates with the translator skill by:
26
+ * 1. Preparing translation prompt
27
+ * 2. Writing prompt to a temp file
28
+ * 3. Outputting instructions for Claude to process
29
+ * 4. Returning the translated content
30
+ *
31
+ * In an automated context (hooks), this provides clear instructions.
32
+ * In an interactive context, the translator skill can auto-activate.
33
+ *
34
+ * @param content - Content to translate
35
+ * @param sourceLang - Source language
36
+ * @param targetLang - Target language
37
+ * @returns Translation result
38
+ */
39
+ export declare function invokeTranslatorSkill(content: string, sourceLang: SupportedLanguage, targetLang?: SupportedLanguage): Promise<TranslationResult>;
40
+ /**
41
+ * Translate a file using translator skill
42
+ *
43
+ * @param filePath - Path to file to translate
44
+ * @param targetLang - Target language
45
+ * @returns Translation result
46
+ */
47
+ export declare function translateFile(filePath: string, targetLang?: SupportedLanguage): Promise<TranslationResult & {
48
+ filePath: string;
49
+ }>;
50
+ /**
51
+ * Batch translate multiple files
52
+ *
53
+ * @param filePaths - Array of file paths
54
+ * @param targetLang - Target language
55
+ * @returns Array of translation results
56
+ */
57
+ export declare function batchTranslateFiles(filePaths: string[], targetLang?: SupportedLanguage): Promise<Array<TranslationResult & {
58
+ filePath: string;
59
+ }>>;
60
+ //# sourceMappingURL=invoke-translator-skill.d.ts.map