jettypod 4.4.0 ā 4.4.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.
- package/docs/DECISIONS.md +4 -52
- package/jettypod.js +89 -0
- package/lib/chore-classifier.js +232 -0
- package/lib/chore-taxonomy.js +172 -0
- package/package.json +1 -1
- package/skills-templates/chore-mode/SKILL.md +396 -0
- package/skills-templates/chore-mode/verification.js +255 -0
- package/skills-templates/chore-planning/SKILL.md +229 -0
- package/skills-templates/epic-planning/SKILL.md +118 -16
- package/skills-templates/feature-planning/SKILL.md +155 -105
- package/skills-templates/production-mode/SKILL.md +4 -7
- package/skills-templates/speed-mode/SKILL.md +471 -463
- package/skills-templates/stable-mode/SKILL.md +319 -371
package/docs/DECISIONS.md
CHANGED
|
@@ -6,61 +6,13 @@ This document records key decisions made during project discovery and epic plann
|
|
|
6
6
|
|
|
7
7
|
## Epic-Level Decisions
|
|
8
8
|
|
|
9
|
-
### Epic #
|
|
9
|
+
### Epic #2: Standalone Chore Workflow System
|
|
10
10
|
|
|
11
|
-
**Architecture:**
|
|
11
|
+
**Architecture:** Two-tier skill structure (chore-planning ā chore-mode)
|
|
12
12
|
|
|
13
|
-
*Rationale:*
|
|
13
|
+
*Rationale:* Two-tier skill structure chosen to mirror existing feature workflow patterns. Separates planning concerns (scope, criteria, impact) from execution concerns (guidance, verification). Type-dependent test analysis balances thoroughness with pragmatism.
|
|
14
14
|
|
|
15
|
-
*Date:*
|
|
16
|
-
|
|
17
|
-
**State Management:** Redux Toolkit
|
|
18
|
-
|
|
19
|
-
*Rationale:* Need centralized state for connection status across multiple components
|
|
20
|
-
|
|
21
|
-
*Date:* 10/29/2025
|
|
22
|
-
|
|
23
|
-
**Error Handling:** Exponential backoff with jitter for reconnection
|
|
24
|
-
|
|
25
|
-
*Rationale:* Prevents thundering herd when server recovers, jitter distributes reconnection attempts evenly
|
|
26
|
-
|
|
27
|
-
*Date:* 10/31/2025
|
|
28
|
-
|
|
29
|
-
**Testing Strategy:** Integration tests with mock Socket.io server
|
|
30
|
-
|
|
31
|
-
*Rationale:* Allows testing reconnection logic and state synchronization without real server dependency
|
|
32
|
-
|
|
33
|
-
*Date:* 10/31/2025
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
### Epic #238: Real-time System
|
|
38
|
-
|
|
39
|
-
**Architecture:** WebSockets
|
|
40
|
-
|
|
41
|
-
*Rationale:* Real-time bidirectional communication required
|
|
42
|
-
|
|
43
|
-
*Date:* 10/29/2025
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
### Epic #242: Epic Needs Discovery
|
|
48
|
-
|
|
49
|
-
**Architecture:** GraphQL API
|
|
50
|
-
|
|
51
|
-
*Rationale:* Flexible querying and strong typing needed for complex data requirements
|
|
52
|
-
|
|
53
|
-
*Date:* 10/29/2025
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
### Epic #1840: Multiple AI Coding Assistant Instances
|
|
58
|
-
|
|
59
|
-
**Concurrent Merge Architecture:** Database-backed merge queue with rebase-first strategy and smart conflict resolution
|
|
60
|
-
|
|
61
|
-
*Rationale:* Multiple Claude Code instances need coordinated access to main branch. Using database as coordination point provides simple, reliable serialization without complex distributed locking. Rebase-first strategy ensures linear history and makes conflicts easier to reason about. Smart conflict resolution (auto-resolve trivial, LLM-assist complex) enables autonomous operation while escalating truly ambiguous cases. Graceful degradation ensures system never enters unrecoverable state.
|
|
62
|
-
|
|
63
|
-
*Date:* 11/14/2025
|
|
15
|
+
*Date:* 11/25/2025
|
|
64
16
|
|
|
65
17
|
---
|
|
66
18
|
|
package/jettypod.js
CHANGED
|
@@ -222,6 +222,7 @@ jettypod work create feature "<title>" --parent=<id>
|
|
|
222
222
|
jettypod work start <id>
|
|
223
223
|
jettypod work status <id> cancelled
|
|
224
224
|
jettypod backlog
|
|
225
|
+
jettypod impact <file> # Show tests/features affected by changing a file
|
|
225
226
|
|
|
226
227
|
## Advanced Commands
|
|
227
228
|
For mode management, decisions, project state: docs/COMMAND_REFERENCE.md
|
|
@@ -2062,6 +2063,94 @@ Quick commands:
|
|
|
2062
2063
|
break;
|
|
2063
2064
|
}
|
|
2064
2065
|
|
|
2066
|
+
case 'impact': {
|
|
2067
|
+
// Test impact analysis
|
|
2068
|
+
const targetFile = args[0];
|
|
2069
|
+
|
|
2070
|
+
if (!targetFile) {
|
|
2071
|
+
console.log('Usage: jettypod impact <file-path>');
|
|
2072
|
+
console.log('');
|
|
2073
|
+
console.log('Analyze what tests and features would be affected by changing a file.');
|
|
2074
|
+
console.log('');
|
|
2075
|
+
console.log('Examples:');
|
|
2076
|
+
console.log(' jettypod impact lib/database.js');
|
|
2077
|
+
console.log(' jettypod impact features/work-tracking/index.js');
|
|
2078
|
+
process.exit(1);
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
try {
|
|
2082
|
+
const { DependencyGraph } = require('./lib/test-impact-analyzer');
|
|
2083
|
+
const graph = new DependencyGraph();
|
|
2084
|
+
|
|
2085
|
+
console.log('š Building dependency graph...');
|
|
2086
|
+
await graph.build(process.cwd());
|
|
2087
|
+
|
|
2088
|
+
const impact = graph.getImpact(targetFile);
|
|
2089
|
+
|
|
2090
|
+
if (impact.error) {
|
|
2091
|
+
console.error(`\nā ${impact.error}`);
|
|
2092
|
+
console.log('');
|
|
2093
|
+
console.log('Make sure the file path is relative to the project root.');
|
|
2094
|
+
process.exit(1);
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// Pretty-print impact analysis
|
|
2098
|
+
const levelColors = {
|
|
2099
|
+
LOW: '\x1b[32m', // green
|
|
2100
|
+
MEDIUM: '\x1b[33m', // yellow
|
|
2101
|
+
HIGH: '\x1b[31m' // red
|
|
2102
|
+
};
|
|
2103
|
+
const reset = '\x1b[0m';
|
|
2104
|
+
const bold = '\x1b[1m';
|
|
2105
|
+
const dim = '\x1b[2m';
|
|
2106
|
+
|
|
2107
|
+
console.log('');
|
|
2108
|
+
console.log('ā'.repeat(60));
|
|
2109
|
+
console.log(`${bold}IMPACT ANALYSIS: ${targetFile}${reset}`);
|
|
2110
|
+
console.log('ā'.repeat(60));
|
|
2111
|
+
console.log('');
|
|
2112
|
+
console.log(`Impact Score: ${bold}${impact.impactScore}/100${reset} ${levelColors[impact.impactLevel]}(${impact.impactLevel})${reset}`);
|
|
2113
|
+
console.log(`Total Affected Files: ${impact.totalAffected}`);
|
|
2114
|
+
|
|
2115
|
+
if (impact.tests.length > 0) {
|
|
2116
|
+
console.log('');
|
|
2117
|
+
console.log(`${bold}AFFECTED TESTS (${impact.tests.length}):${reset}`);
|
|
2118
|
+
for (const t of impact.tests) {
|
|
2119
|
+
console.log(` ${dim}[dist=${t.distance}]${reset} ${t.file}`);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
if (impact.features.length > 0) {
|
|
2124
|
+
console.log('');
|
|
2125
|
+
console.log(`${bold}AFFECTED BDD FILES (${impact.features.length}):${reset}`);
|
|
2126
|
+
for (const f of impact.features) {
|
|
2127
|
+
console.log(` ${dim}[dist=${f.distance}]${reset} ${f.file}`);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
if (impact.code.length > 0) {
|
|
2132
|
+
console.log('');
|
|
2133
|
+
console.log(`${bold}AFFECTED CODE (${impact.code.length}):${reset}`);
|
|
2134
|
+
const displayCode = impact.code.slice(0, 10);
|
|
2135
|
+
for (const c of displayCode) {
|
|
2136
|
+
console.log(` ${dim}[dist=${c.distance}]${reset} ${c.file}`);
|
|
2137
|
+
}
|
|
2138
|
+
if (impact.code.length > 10) {
|
|
2139
|
+
console.log(` ${dim}... and ${impact.code.length - 10} more${reset}`);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
console.log('');
|
|
2144
|
+
console.log('ā'.repeat(60));
|
|
2145
|
+
console.log('');
|
|
2146
|
+
|
|
2147
|
+
} catch (err) {
|
|
2148
|
+
console.error(`ā Error: ${err.message}`);
|
|
2149
|
+
process.exit(1);
|
|
2150
|
+
}
|
|
2151
|
+
break;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2065
2154
|
default:
|
|
2066
2155
|
// Smart mode: auto-initialize if needed, otherwise show guidance
|
|
2067
2156
|
if (!fs.existsSync('.jettypod')) {
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chore Classifier
|
|
3
|
+
*
|
|
4
|
+
* Classifies chore type from title/description using keyword matching.
|
|
5
|
+
* Maps to the 4 chore types defined in chore-taxonomy.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { CHORE_TYPES, isValidChoreType } = require('./chore-taxonomy');
|
|
9
|
+
|
|
10
|
+
// Keywords that indicate each chore type
|
|
11
|
+
const TYPE_KEYWORDS = {
|
|
12
|
+
[CHORE_TYPES.REFACTOR]: [
|
|
13
|
+
'refactor',
|
|
14
|
+
'restructure',
|
|
15
|
+
'reorganize',
|
|
16
|
+
'extract',
|
|
17
|
+
'inline',
|
|
18
|
+
'rename',
|
|
19
|
+
'move',
|
|
20
|
+
'split',
|
|
21
|
+
'consolidate',
|
|
22
|
+
'simplify',
|
|
23
|
+
'decouple',
|
|
24
|
+
'modularize'
|
|
25
|
+
],
|
|
26
|
+
[CHORE_TYPES.DEPENDENCY]: [
|
|
27
|
+
'update',
|
|
28
|
+
'upgrade',
|
|
29
|
+
'bump',
|
|
30
|
+
'migrate',
|
|
31
|
+
'dependency',
|
|
32
|
+
'dependencies',
|
|
33
|
+
'package',
|
|
34
|
+
'library',
|
|
35
|
+
'framework',
|
|
36
|
+
'version',
|
|
37
|
+
'security patch',
|
|
38
|
+
'vulnerability',
|
|
39
|
+
'npm',
|
|
40
|
+
'yarn',
|
|
41
|
+
'lodash',
|
|
42
|
+
'react',
|
|
43
|
+
'node'
|
|
44
|
+
],
|
|
45
|
+
[CHORE_TYPES.CLEANUP]: [
|
|
46
|
+
'cleanup',
|
|
47
|
+
'clean up',
|
|
48
|
+
'remove',
|
|
49
|
+
'delete',
|
|
50
|
+
'deprecate',
|
|
51
|
+
'dead code',
|
|
52
|
+
'unused',
|
|
53
|
+
'legacy',
|
|
54
|
+
'obsolete',
|
|
55
|
+
'prune',
|
|
56
|
+
'trim'
|
|
57
|
+
],
|
|
58
|
+
[CHORE_TYPES.TOOLING]: [
|
|
59
|
+
'tooling',
|
|
60
|
+
'ci',
|
|
61
|
+
'cd',
|
|
62
|
+
'pipeline',
|
|
63
|
+
'build',
|
|
64
|
+
'lint',
|
|
65
|
+
'eslint',
|
|
66
|
+
'prettier',
|
|
67
|
+
'config',
|
|
68
|
+
'configuration',
|
|
69
|
+
'script',
|
|
70
|
+
'automation',
|
|
71
|
+
'github action',
|
|
72
|
+
'workflow',
|
|
73
|
+
'jest',
|
|
74
|
+
'test setup'
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Normalize and validate input text
|
|
80
|
+
* @param {string} title - The chore title
|
|
81
|
+
* @param {string} [description] - Optional description
|
|
82
|
+
* @returns {string} - Normalized text for classification
|
|
83
|
+
* @throws {Error} - If title is invalid
|
|
84
|
+
*/
|
|
85
|
+
function normalizeInput(title, description = '') {
|
|
86
|
+
// Validate title
|
|
87
|
+
if (title === null || title === undefined) {
|
|
88
|
+
throw new Error('Chore title is required for classification');
|
|
89
|
+
}
|
|
90
|
+
if (typeof title !== 'string') {
|
|
91
|
+
throw new Error(`Chore title must be a string, got ${typeof title}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const trimmedTitle = title.trim();
|
|
95
|
+
if (trimmedTitle.length === 0) {
|
|
96
|
+
throw new Error('Chore title cannot be empty or whitespace only');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Normalize description (coerce to string if not)
|
|
100
|
+
const normalizedDesc = description != null ? String(description).trim() : '';
|
|
101
|
+
|
|
102
|
+
return `${trimmedTitle} ${normalizedDesc}`.toLowerCase();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Escape special regex characters in a string
|
|
107
|
+
* @param {string} str - String to escape
|
|
108
|
+
* @returns {string} - Escaped string safe for regex
|
|
109
|
+
*/
|
|
110
|
+
function escapeRegex(str) {
|
|
111
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Calculate scores for all chore types
|
|
116
|
+
* @param {string} text - Normalized text to analyze
|
|
117
|
+
* @returns {Object} - Scores for each type
|
|
118
|
+
*/
|
|
119
|
+
function calculateScores(text) {
|
|
120
|
+
const scores = {};
|
|
121
|
+
for (const [type, keywords] of Object.entries(TYPE_KEYWORDS)) {
|
|
122
|
+
scores[type] = 0;
|
|
123
|
+
for (const keyword of keywords) {
|
|
124
|
+
if (text.includes(keyword.toLowerCase())) {
|
|
125
|
+
// Exact word match scores higher (escape special chars for safety)
|
|
126
|
+
const escapedKeyword = escapeRegex(keyword);
|
|
127
|
+
const wordBoundaryRegex = new RegExp(`\\b${escapedKeyword}\\b`, 'i');
|
|
128
|
+
if (wordBoundaryRegex.test(text)) {
|
|
129
|
+
scores[type] += 2;
|
|
130
|
+
} else {
|
|
131
|
+
scores[type] += 1;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return scores;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Find the winning type from scores
|
|
141
|
+
* @param {Object} scores - Scores for each type
|
|
142
|
+
* @returns {Object} - { type: string, score: number }
|
|
143
|
+
*/
|
|
144
|
+
function findWinningType(scores) {
|
|
145
|
+
let maxScore = 0;
|
|
146
|
+
let classifiedType = CHORE_TYPES.REFACTOR; // Default fallback
|
|
147
|
+
|
|
148
|
+
for (const [type, score] of Object.entries(scores)) {
|
|
149
|
+
if (score > maxScore) {
|
|
150
|
+
maxScore = score;
|
|
151
|
+
classifiedType = type;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { type: classifiedType, score: maxScore };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Classify a chore based on its title (and optionally description)
|
|
160
|
+
* @param {string} title - The chore title
|
|
161
|
+
* @param {string} [description] - Optional description for additional context
|
|
162
|
+
* @returns {string} - One of the 4 chore types
|
|
163
|
+
* @throws {Error} - If title is invalid
|
|
164
|
+
*/
|
|
165
|
+
function classifyChoreType(title, description = '') {
|
|
166
|
+
const text = normalizeInput(title, description);
|
|
167
|
+
const scores = calculateScores(text);
|
|
168
|
+
const { type } = findWinningType(scores);
|
|
169
|
+
return type;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Determine confidence level based on score
|
|
174
|
+
* @param {number} score - The classification score
|
|
175
|
+
* @returns {string} - 'high', 'medium', or 'low'
|
|
176
|
+
*/
|
|
177
|
+
function getConfidenceLevel(score) {
|
|
178
|
+
if (score >= 4) return 'high';
|
|
179
|
+
if (score >= 2) return 'medium';
|
|
180
|
+
return 'low';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get confidence level for a classification
|
|
185
|
+
* @param {string} title - The chore title
|
|
186
|
+
* @param {string} [description] - Optional description
|
|
187
|
+
* @returns {object} - { type: string, confidence: 'high'|'medium'|'low', score: number }
|
|
188
|
+
* @throws {Error} - If title is invalid
|
|
189
|
+
*/
|
|
190
|
+
function classifyWithConfidence(title, description = '') {
|
|
191
|
+
const text = normalizeInput(title, description);
|
|
192
|
+
const scores = calculateScores(text);
|
|
193
|
+
const { type, score } = findWinningType(scores);
|
|
194
|
+
const confidence = getConfidenceLevel(score);
|
|
195
|
+
|
|
196
|
+
return { type, confidence, score };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get detailed classification with all type scores
|
|
201
|
+
* Useful for debugging or when needing transparency in classification
|
|
202
|
+
* @param {string} title - The chore title
|
|
203
|
+
* @param {string} [description] - Optional description
|
|
204
|
+
* @returns {object} - { type, confidence, score, allScores }
|
|
205
|
+
* @throws {Error} - If title is invalid
|
|
206
|
+
*/
|
|
207
|
+
function classifyWithDetails(title, description = '') {
|
|
208
|
+
const text = normalizeInput(title, description);
|
|
209
|
+
const scores = calculateScores(text);
|
|
210
|
+
const { type, score } = findWinningType(scores);
|
|
211
|
+
const confidence = getConfidenceLevel(score);
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
type,
|
|
215
|
+
confidence,
|
|
216
|
+
score,
|
|
217
|
+
allScores: scores
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = {
|
|
222
|
+
classifyChoreType,
|
|
223
|
+
classifyWithConfidence,
|
|
224
|
+
classifyWithDetails,
|
|
225
|
+
TYPE_KEYWORDS,
|
|
226
|
+
// Expose helpers for testing
|
|
227
|
+
normalizeInput,
|
|
228
|
+
escapeRegex,
|
|
229
|
+
calculateScores,
|
|
230
|
+
findWinningType,
|
|
231
|
+
getConfidenceLevel
|
|
232
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chore Type Taxonomy
|
|
3
|
+
* Defines the 4 chore types and their workflow guidance for the chore-planning and chore-mode skills.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Chore types
|
|
8
|
+
* @constant {Object}
|
|
9
|
+
*/
|
|
10
|
+
const CHORE_TYPES = Object.freeze({
|
|
11
|
+
REFACTOR: 'refactor',
|
|
12
|
+
DEPENDENCY: 'dependency',
|
|
13
|
+
CLEANUP: 'cleanup',
|
|
14
|
+
TOOLING: 'tooling'
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Valid chore type values as array
|
|
19
|
+
* @constant {Array<string>}
|
|
20
|
+
*/
|
|
21
|
+
const VALID_CHORE_TYPES = Object.freeze(Object.values(CHORE_TYPES));
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Guidance for each chore type
|
|
25
|
+
* @constant {Object}
|
|
26
|
+
*/
|
|
27
|
+
const CHORE_TYPE_GUIDANCE = Object.freeze({
|
|
28
|
+
[CHORE_TYPES.REFACTOR]: {
|
|
29
|
+
scope: [
|
|
30
|
+
'Define clear boundaries - what code is being restructured',
|
|
31
|
+
'Identify all callers/dependents of code being changed',
|
|
32
|
+
'Ensure behavior remains unchanged (refactor, not rewrite)',
|
|
33
|
+
'Consider breaking into smaller refactors if scope is large'
|
|
34
|
+
],
|
|
35
|
+
verification: [
|
|
36
|
+
'All existing tests pass without modification',
|
|
37
|
+
'No new functionality added (that requires new tests)',
|
|
38
|
+
'Code review confirms behavior preservation',
|
|
39
|
+
'Performance is not degraded'
|
|
40
|
+
],
|
|
41
|
+
testHandling: {
|
|
42
|
+
required: true,
|
|
43
|
+
approach: 'Run all tests for affected modules before and after. Update test file paths/imports if moved. Do NOT change test assertions - if tests fail, the refactor broke behavior.'
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
[CHORE_TYPES.DEPENDENCY]: {
|
|
47
|
+
scope: [
|
|
48
|
+
'Identify which packages are being updated',
|
|
49
|
+
'Check changelogs for breaking changes',
|
|
50
|
+
'Note any deprecated APIs that need migration',
|
|
51
|
+
'Consider update strategy: one at a time vs batch'
|
|
52
|
+
],
|
|
53
|
+
verification: [
|
|
54
|
+
'All tests pass after update',
|
|
55
|
+
'Application builds successfully',
|
|
56
|
+
'No new deprecation warnings (or documented)',
|
|
57
|
+
'Security vulnerabilities addressed (if security update)'
|
|
58
|
+
],
|
|
59
|
+
testHandling: {
|
|
60
|
+
required: false,
|
|
61
|
+
approach: 'Run full test suite to catch regressions. No new tests needed unless migrating to new API patterns. Document any test changes needed due to library API changes.'
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[CHORE_TYPES.CLEANUP]: {
|
|
65
|
+
scope: [
|
|
66
|
+
'Define what is being cleaned (dead code, unused files, etc.)',
|
|
67
|
+
'Verify code is actually unused (grep for references)',
|
|
68
|
+
'Set clear boundaries to avoid scope creep',
|
|
69
|
+
'Consider impact on git history/blame'
|
|
70
|
+
],
|
|
71
|
+
verification: [
|
|
72
|
+
'All tests still pass',
|
|
73
|
+
'No broken imports or references',
|
|
74
|
+
'Application runs correctly',
|
|
75
|
+
'Removed code was actually unused'
|
|
76
|
+
],
|
|
77
|
+
testHandling: {
|
|
78
|
+
required: false,
|
|
79
|
+
approach: 'Run existing tests to ensure nothing breaks. Remove tests only if they test deleted code. No new tests needed for cleanup work.'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
[CHORE_TYPES.TOOLING]: {
|
|
83
|
+
scope: [
|
|
84
|
+
'Define what tooling is being changed (CI, build, dev environment)',
|
|
85
|
+
'Document current behavior before changes',
|
|
86
|
+
'Consider impact on team workflows',
|
|
87
|
+
'Plan rollback strategy if changes cause issues'
|
|
88
|
+
],
|
|
89
|
+
verification: [
|
|
90
|
+
'CI pipeline passes',
|
|
91
|
+
'Build completes successfully',
|
|
92
|
+
'Dev environment works for all team members',
|
|
93
|
+
'No regression in build times or developer experience'
|
|
94
|
+
],
|
|
95
|
+
testHandling: {
|
|
96
|
+
required: false,
|
|
97
|
+
approach: 'Verify tooling changes work via manual testing or CI runs. Add integration tests only if tooling is complex. Focus on verification over unit testing for infrastructure.'
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Normalize a chore type string (lowercase, trimmed)
|
|
104
|
+
* @param {string} type - Type to normalize
|
|
105
|
+
* @returns {string|null} Normalized type or null if input is invalid
|
|
106
|
+
*/
|
|
107
|
+
function normalizeChoreType(type) {
|
|
108
|
+
if (type === null || type === undefined) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (typeof type !== 'string') {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return type.toLowerCase().trim();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if a chore type is valid (case-insensitive)
|
|
119
|
+
* @param {string} type - Type to validate
|
|
120
|
+
* @returns {boolean} True if type is valid
|
|
121
|
+
*/
|
|
122
|
+
function isValidChoreType(type) {
|
|
123
|
+
const normalized = normalizeChoreType(type);
|
|
124
|
+
if (normalized === null) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return VALID_CHORE_TYPES.includes(normalized);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get guidance for a chore type
|
|
132
|
+
* @param {string} type - Chore type (case-insensitive)
|
|
133
|
+
* @returns {Object} Guidance object with scope, verification, and testHandling
|
|
134
|
+
* @throws {Error} If type is null, undefined, or invalid
|
|
135
|
+
*/
|
|
136
|
+
function getGuidance(type) {
|
|
137
|
+
if (type === null || type === undefined) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
'Chore type is required. Valid types: refactor, dependency, cleanup, tooling'
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const normalized = normalizeChoreType(type);
|
|
144
|
+
|
|
145
|
+
if (!normalized || !VALID_CHORE_TYPES.includes(normalized)) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`Invalid chore type: "${type}". Valid types: refactor, dependency, cleanup, tooling`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return CHORE_TYPE_GUIDANCE[normalized];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get all chore types
|
|
156
|
+
* @returns {Array<string>} Array of valid chore types
|
|
157
|
+
*/
|
|
158
|
+
function getChoreTypes() {
|
|
159
|
+
return [...VALID_CHORE_TYPES];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = {
|
|
163
|
+
CHORE_TYPES,
|
|
164
|
+
VALID_CHORE_TYPES,
|
|
165
|
+
CHORE_TYPE_GUIDANCE,
|
|
166
|
+
normalizeChoreType,
|
|
167
|
+
isValidChoreType,
|
|
168
|
+
getGuidance,
|
|
169
|
+
getChoreTypes,
|
|
170
|
+
// Alias for step definitions
|
|
171
|
+
types: CHORE_TYPES
|
|
172
|
+
};
|