awesome-slash 2.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/.claude-plugin/marketplace.json +54 -0
- package/.claude-plugin/plugin.json +11 -0
- package/.mcp.json +8 -0
- package/CHANGELOG.md +261 -0
- package/LICENSE +21 -0
- package/README.md +363 -0
- package/SECURITY.md +101 -0
- package/adapters/README.md +256 -0
- package/adapters/codex/README.md +272 -0
- package/adapters/codex/install.sh +179 -0
- package/adapters/opencode/README.md +301 -0
- package/adapters/opencode/install.sh +223 -0
- package/lib/patterns/review-patterns.js +511 -0
- package/lib/patterns/slop-patterns.js +647 -0
- package/lib/platform/detect-platform.js +535 -0
- package/lib/platform/verify-tools.js +235 -0
- package/lib/state/workflow-state.js +635 -0
- package/lib/state/workflow-state.schema.json +282 -0
- package/lib/utils/context-optimizer.js +227 -0
- package/mcp-server/index.js +303 -0
- package/mcp-server/package.json +23 -0
- package/package.json +63 -0
- package/plugins/deslop-around/.claude-plugin/plugin.json +20 -0
- package/plugins/deslop-around/commands/deslop-around.md +220 -0
- package/plugins/deslop-around/lib/patterns/review-patterns.js +511 -0
- package/plugins/deslop-around/lib/patterns/slop-patterns.js +641 -0
- package/plugins/deslop-around/lib/platform/detect-platform.js +514 -0
- package/plugins/deslop-around/lib/platform/verify-tools.js +235 -0
- package/plugins/deslop-around/lib/state/workflow-state.js +635 -0
- package/plugins/deslop-around/lib/state/workflow-state.schema.json +282 -0
- package/plugins/deslop-around/lib/utils/context-optimizer.js +222 -0
- package/plugins/next-task/.claude-plugin/plugin.json +24 -0
- package/plugins/next-task/agents/ci-fixer.md +236 -0
- package/plugins/next-task/agents/ci-monitor.md +291 -0
- package/plugins/next-task/agents/delivery-validator.md +451 -0
- package/plugins/next-task/agents/deslop-work.md +272 -0
- package/plugins/next-task/agents/docs-updater.md +506 -0
- package/plugins/next-task/agents/exploration-agent.md +277 -0
- package/plugins/next-task/agents/implementation-agent.md +427 -0
- package/plugins/next-task/agents/planning-agent.md +236 -0
- package/plugins/next-task/agents/policy-selector.md +248 -0
- package/plugins/next-task/agents/review-orchestrator.md +521 -0
- package/plugins/next-task/agents/simple-fixer.md +136 -0
- package/plugins/next-task/agents/task-discoverer.md +357 -0
- package/plugins/next-task/agents/test-coverage-checker.md +447 -0
- package/plugins/next-task/agents/worktree-manager.md +419 -0
- package/plugins/next-task/commands/delivery-approval.md +331 -0
- package/plugins/next-task/commands/next-task.md +627 -0
- package/plugins/next-task/commands/update-docs-around.md +418 -0
- package/plugins/next-task/hooks/hooks.json +14 -0
- package/plugins/next-task/lib/patterns/review-patterns.js +511 -0
- package/plugins/next-task/lib/patterns/slop-patterns.js +641 -0
- package/plugins/next-task/lib/platform/detect-platform.js +514 -0
- package/plugins/next-task/lib/platform/verify-tools.js +235 -0
- package/plugins/next-task/lib/state/tasks-registry.schema.json +85 -0
- package/plugins/next-task/lib/state/workflow-state.js +635 -0
- package/plugins/next-task/lib/state/workflow-state.schema.json +282 -0
- package/plugins/next-task/lib/state/worktree-status.schema.json +219 -0
- package/plugins/next-task/lib/utils/context-optimizer.js +222 -0
- package/plugins/project-review/.claude-plugin/plugin.json +20 -0
- package/plugins/project-review/commands/project-review-agents.md +286 -0
- package/plugins/project-review/commands/project-review-github.md +142 -0
- package/plugins/project-review/commands/project-review.md +273 -0
- package/plugins/project-review/lib/patterns/review-patterns.js +511 -0
- package/plugins/project-review/lib/patterns/slop-patterns.js +641 -0
- package/plugins/project-review/lib/platform/detect-platform.js +514 -0
- package/plugins/project-review/lib/platform/verify-tools.js +235 -0
- package/plugins/project-review/lib/state/workflow-state.js +635 -0
- package/plugins/project-review/lib/state/workflow-state.schema.json +282 -0
- package/plugins/project-review/lib/utils/context-optimizer.js +222 -0
- package/plugins/reality-check/.claude-plugin/plugin.json +23 -0
- package/plugins/reality-check/README.md +156 -0
- package/plugins/reality-check/agents/code-explorer.md +353 -0
- package/plugins/reality-check/agents/doc-analyzer.md +337 -0
- package/plugins/reality-check/agents/issue-scanner.md +231 -0
- package/plugins/reality-check/agents/plan-synthesizer.md +479 -0
- package/plugins/reality-check/commands/scan.md +242 -0
- package/plugins/reality-check/commands/set.md +203 -0
- package/plugins/reality-check/lib/state/reality-check-state.js +509 -0
- package/plugins/reality-check/skills/reality-analysis/SKILL.md +317 -0
- package/plugins/ship/.claude-plugin/plugin.json +21 -0
- package/plugins/ship/commands/ship-ci-review-loop.md +443 -0
- package/plugins/ship/commands/ship-deployment.md +330 -0
- package/plugins/ship/commands/ship-error-handling.md +254 -0
- package/plugins/ship/commands/ship.md +370 -0
- package/plugins/ship/lib/patterns/review-patterns.js +511 -0
- package/plugins/ship/lib/patterns/slop-patterns.js +641 -0
- package/plugins/ship/lib/platform/detect-platform.js +514 -0
- package/plugins/ship/lib/platform/verify-tools.js +235 -0
- package/plugins/ship/lib/state/workflow-state.js +635 -0
- package/plugins/ship/lib/state/workflow-state.schema.json +282 -0
- package/plugins/ship/lib/utils/context-optimizer.js +222 -0
- package/scripts/install/claude.sh +50 -0
- package/scripts/install/codex.sh +181 -0
- package/scripts/install/opencode.sh +211 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reality Check State Management
|
|
3
|
+
*
|
|
4
|
+
* Persistent state management for reality-check workflow orchestration.
|
|
5
|
+
* Enables parallel agent coordination and scan result tracking.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
|
|
12
|
+
const SCHEMA_VERSION = '1.0.0';
|
|
13
|
+
const STATE_DIR = '.claude';
|
|
14
|
+
const STATE_FILE = 'reality-check-state.json';
|
|
15
|
+
const SETTINGS_FILE = 'reality-check.local.md';
|
|
16
|
+
|
|
17
|
+
const PHASES = [
|
|
18
|
+
'settings-check',
|
|
19
|
+
'parallel-scan',
|
|
20
|
+
'synthesis',
|
|
21
|
+
'report-generation',
|
|
22
|
+
'complete'
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const DEFAULT_SETTINGS = {
|
|
26
|
+
sources: {
|
|
27
|
+
github_issues: true,
|
|
28
|
+
linear: false,
|
|
29
|
+
docs_paths: ['docs/', 'README.md', 'CLAUDE.md', 'PLAN.md'],
|
|
30
|
+
code_exploration: true
|
|
31
|
+
},
|
|
32
|
+
scan_depth: 'thorough',
|
|
33
|
+
output: {
|
|
34
|
+
write_to_file: true,
|
|
35
|
+
file_path: 'reality-check-report.md',
|
|
36
|
+
display_summary: true
|
|
37
|
+
},
|
|
38
|
+
priority_weights: {
|
|
39
|
+
security: 10,
|
|
40
|
+
bugs: 8,
|
|
41
|
+
features: 5,
|
|
42
|
+
docs: 3
|
|
43
|
+
},
|
|
44
|
+
exclusions: {
|
|
45
|
+
paths: ['node_modules/', 'dist/', '.git/'],
|
|
46
|
+
labels: ['wontfix', 'duplicate']
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Generate a unique scan ID
|
|
52
|
+
* @returns {string} Scan ID in format: scan-YYYYMMDD-HHMMSS-random
|
|
53
|
+
*/
|
|
54
|
+
function generateScanId() {
|
|
55
|
+
const now = new Date();
|
|
56
|
+
const date = now.toISOString().slice(0, 10).replace(/-/g, '');
|
|
57
|
+
const time = now.toISOString().slice(11, 19).replace(/:/g, '');
|
|
58
|
+
const random = crypto.randomBytes(4).toString('hex');
|
|
59
|
+
return `scan-${date}-${time}-${random}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get the state file path
|
|
64
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
65
|
+
* @returns {string} Full path to state file
|
|
66
|
+
*/
|
|
67
|
+
function getStatePath(baseDir = process.cwd()) {
|
|
68
|
+
return path.join(baseDir, STATE_DIR, STATE_FILE);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get the settings file path
|
|
73
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
74
|
+
* @returns {string} Full path to settings file
|
|
75
|
+
*/
|
|
76
|
+
function getSettingsPath(baseDir = process.cwd()) {
|
|
77
|
+
return path.join(baseDir, STATE_DIR, SETTINGS_FILE);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Ensure state directory exists
|
|
82
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
83
|
+
*/
|
|
84
|
+
function ensureStateDir(baseDir = process.cwd()) {
|
|
85
|
+
const stateDir = path.join(baseDir, STATE_DIR);
|
|
86
|
+
if (!fs.existsSync(stateDir)) {
|
|
87
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check if settings file exists
|
|
93
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
94
|
+
* @returns {boolean} True if settings exist
|
|
95
|
+
*/
|
|
96
|
+
function hasSettings(baseDir = process.cwd()) {
|
|
97
|
+
return fs.existsSync(getSettingsPath(baseDir));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Read settings from .local.md file
|
|
102
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
103
|
+
* @returns {Object} Settings object (defaults if not found)
|
|
104
|
+
*/
|
|
105
|
+
function readSettings(baseDir = process.cwd()) {
|
|
106
|
+
const settingsPath = getSettingsPath(baseDir);
|
|
107
|
+
|
|
108
|
+
if (!fs.existsSync(settingsPath)) {
|
|
109
|
+
return { ...DEFAULT_SETTINGS };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const content = fs.readFileSync(settingsPath, 'utf8');
|
|
114
|
+
// Parse YAML frontmatter
|
|
115
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
116
|
+
if (match) {
|
|
117
|
+
// Simple YAML parsing for our known structure
|
|
118
|
+
const yaml = match[1];
|
|
119
|
+
const settings = parseSimpleYaml(yaml);
|
|
120
|
+
return { ...DEFAULT_SETTINGS, ...settings };
|
|
121
|
+
}
|
|
122
|
+
return { ...DEFAULT_SETTINGS };
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(`Error reading settings: ${error.message}`);
|
|
125
|
+
return { ...DEFAULT_SETTINGS };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Simple YAML parser for settings
|
|
131
|
+
* @param {string} yaml - YAML content
|
|
132
|
+
* @returns {Object} Parsed settings
|
|
133
|
+
*/
|
|
134
|
+
function parseSimpleYaml(yaml) {
|
|
135
|
+
const settings = {};
|
|
136
|
+
const lines = yaml.split('\n');
|
|
137
|
+
let currentSection = null;
|
|
138
|
+
let currentSubSection = null;
|
|
139
|
+
|
|
140
|
+
for (const line of lines) {
|
|
141
|
+
const trimmed = line.trim();
|
|
142
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
143
|
+
|
|
144
|
+
// Check indentation level
|
|
145
|
+
const indent = line.search(/\S/);
|
|
146
|
+
|
|
147
|
+
if (indent === 0 && trimmed.endsWith(':')) {
|
|
148
|
+
// Top-level section
|
|
149
|
+
currentSection = trimmed.slice(0, -1);
|
|
150
|
+
settings[currentSection] = {};
|
|
151
|
+
currentSubSection = null;
|
|
152
|
+
} else if (indent === 2 && trimmed.endsWith(':')) {
|
|
153
|
+
// Sub-section
|
|
154
|
+
currentSubSection = trimmed.slice(0, -1);
|
|
155
|
+
if (currentSection) {
|
|
156
|
+
settings[currentSection][currentSubSection] = {};
|
|
157
|
+
}
|
|
158
|
+
} else if (trimmed.includes(':')) {
|
|
159
|
+
// Key-value pair
|
|
160
|
+
const [key, ...valueParts] = trimmed.split(':');
|
|
161
|
+
let value = valueParts.join(':').trim();
|
|
162
|
+
|
|
163
|
+
// Parse value type
|
|
164
|
+
if (value === 'true') value = true;
|
|
165
|
+
else if (value === 'false') value = false;
|
|
166
|
+
else if (!isNaN(value) && value !== '') value = Number(value);
|
|
167
|
+
else if (value.startsWith('[') && value.endsWith(']')) {
|
|
168
|
+
// Array
|
|
169
|
+
value = value.slice(1, -1).split(',').map(v => v.trim().replace(/['"]/g, ''));
|
|
170
|
+
} else {
|
|
171
|
+
value = value.replace(/['"]/g, '');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (currentSubSection && currentSection) {
|
|
175
|
+
settings[currentSection][currentSubSection][key.trim()] = value;
|
|
176
|
+
} else if (currentSection) {
|
|
177
|
+
settings[currentSection][key.trim()] = value;
|
|
178
|
+
} else {
|
|
179
|
+
settings[key.trim()] = value;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return settings;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Write settings to .local.md file
|
|
189
|
+
* @param {Object} settings - Settings object
|
|
190
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
191
|
+
* @returns {boolean} Success status
|
|
192
|
+
*/
|
|
193
|
+
function writeSettings(settings, baseDir = process.cwd()) {
|
|
194
|
+
ensureStateDir(baseDir);
|
|
195
|
+
const settingsPath = getSettingsPath(baseDir);
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const content = `---
|
|
199
|
+
sources:
|
|
200
|
+
github_issues: ${settings.sources?.github_issues ?? true}
|
|
201
|
+
linear: ${settings.sources?.linear ?? false}
|
|
202
|
+
docs_paths: [${(settings.sources?.docs_paths || DEFAULT_SETTINGS.sources.docs_paths).map(p => `"${p}"`).join(', ')}]
|
|
203
|
+
code_exploration: ${settings.sources?.code_exploration ?? true}
|
|
204
|
+
scan_depth: ${settings.scan_depth || 'thorough'}
|
|
205
|
+
output:
|
|
206
|
+
write_to_file: ${settings.output?.write_to_file ?? true}
|
|
207
|
+
file_path: "${settings.output?.file_path || 'reality-check-report.md'}"
|
|
208
|
+
display_summary: ${settings.output?.display_summary ?? true}
|
|
209
|
+
priority_weights:
|
|
210
|
+
security: ${settings.priority_weights?.security ?? 10}
|
|
211
|
+
bugs: ${settings.priority_weights?.bugs ?? 8}
|
|
212
|
+
features: ${settings.priority_weights?.features ?? 5}
|
|
213
|
+
docs: ${settings.priority_weights?.docs ?? 3}
|
|
214
|
+
exclusions:
|
|
215
|
+
paths: [${(settings.exclusions?.paths || DEFAULT_SETTINGS.exclusions.paths).map(p => `"${p}"`).join(', ')}]
|
|
216
|
+
labels: [${(settings.exclusions?.labels || DEFAULT_SETTINGS.exclusions.labels).map(l => `"${l}"`).join(', ')}]
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
# Reality Check Settings
|
|
220
|
+
|
|
221
|
+
Configuration for the reality-check plugin. Edit the YAML frontmatter above to customize behavior.
|
|
222
|
+
|
|
223
|
+
## Sources
|
|
224
|
+
- **github_issues**: Scan GitHub issues and PRs
|
|
225
|
+
- **linear**: Scan Linear issues (requires Linear MCP)
|
|
226
|
+
- **docs_paths**: Paths to documentation files
|
|
227
|
+
- **code_exploration**: Enable deep codebase analysis
|
|
228
|
+
|
|
229
|
+
## Scan Depth
|
|
230
|
+
- **quick**: Fast scan, surface-level analysis
|
|
231
|
+
- **medium**: Balanced scan depth
|
|
232
|
+
- **thorough**: Deep analysis (recommended)
|
|
233
|
+
|
|
234
|
+
## Output
|
|
235
|
+
- **write_to_file**: Save report to file
|
|
236
|
+
- **file_path**: Report file location
|
|
237
|
+
- **display_summary**: Show summary in conversation
|
|
238
|
+
|
|
239
|
+
## Priority Weights
|
|
240
|
+
Higher numbers = higher priority in the final plan
|
|
241
|
+
|
|
242
|
+
## Exclusions
|
|
243
|
+
- **paths**: Directories to skip during code exploration
|
|
244
|
+
- **labels**: Issue labels to exclude from scanning
|
|
245
|
+
`;
|
|
246
|
+
|
|
247
|
+
fs.writeFileSync(settingsPath, content, 'utf8');
|
|
248
|
+
return true;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error(`Error writing settings: ${error.message}`);
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Create a new scan state
|
|
257
|
+
* @param {Object} [settings={}] - Settings overrides
|
|
258
|
+
* @returns {Object} New scan state
|
|
259
|
+
*/
|
|
260
|
+
function createState(settings = {}) {
|
|
261
|
+
const now = new Date().toISOString();
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
version: SCHEMA_VERSION,
|
|
265
|
+
scan: {
|
|
266
|
+
id: generateScanId(),
|
|
267
|
+
status: 'pending',
|
|
268
|
+
startedAt: now,
|
|
269
|
+
lastUpdatedAt: now,
|
|
270
|
+
completedAt: null
|
|
271
|
+
},
|
|
272
|
+
settings: { ...DEFAULT_SETTINGS, ...settings },
|
|
273
|
+
phases: {
|
|
274
|
+
current: 'settings-check',
|
|
275
|
+
history: []
|
|
276
|
+
},
|
|
277
|
+
agents: {
|
|
278
|
+
issueScanner: null,
|
|
279
|
+
docAnalyzer: null,
|
|
280
|
+
codeExplorer: null,
|
|
281
|
+
planSynthesizer: null
|
|
282
|
+
},
|
|
283
|
+
findings: {
|
|
284
|
+
issues: [],
|
|
285
|
+
docs: [],
|
|
286
|
+
code: [],
|
|
287
|
+
drift: [],
|
|
288
|
+
gaps: []
|
|
289
|
+
},
|
|
290
|
+
report: null
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Read scan state from file
|
|
296
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
297
|
+
* @returns {Object|null} Scan state or null if not found
|
|
298
|
+
*/
|
|
299
|
+
function readState(baseDir = process.cwd()) {
|
|
300
|
+
const statePath = getStatePath(baseDir);
|
|
301
|
+
|
|
302
|
+
if (!fs.existsSync(statePath)) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const content = fs.readFileSync(statePath, 'utf8');
|
|
308
|
+
return JSON.parse(content);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.error(`Error reading state: ${error.message}`);
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Write scan state to file
|
|
317
|
+
* @param {Object} state - Scan state
|
|
318
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
319
|
+
* @returns {boolean} Success status
|
|
320
|
+
*/
|
|
321
|
+
function writeState(state, baseDir = process.cwd()) {
|
|
322
|
+
ensureStateDir(baseDir);
|
|
323
|
+
const statePath = getStatePath(baseDir);
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
state.scan.lastUpdatedAt = new Date().toISOString();
|
|
327
|
+
const content = JSON.stringify(state, null, 2);
|
|
328
|
+
fs.writeFileSync(statePath, content, 'utf8');
|
|
329
|
+
return true;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.error(`Error writing state: ${error.message}`);
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Update agent results in state
|
|
338
|
+
* @param {string} agentName - Agent identifier (issueScanner, docAnalyzer, codeExplorer)
|
|
339
|
+
* @param {Object} result - Agent result
|
|
340
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
341
|
+
* @returns {Object|null} Updated state or null on error
|
|
342
|
+
*/
|
|
343
|
+
function updateAgentResult(agentName, result, baseDir = process.cwd()) {
|
|
344
|
+
const state = readState(baseDir);
|
|
345
|
+
if (!state) return null;
|
|
346
|
+
|
|
347
|
+
state.agents[agentName] = {
|
|
348
|
+
status: 'completed',
|
|
349
|
+
completedAt: new Date().toISOString(),
|
|
350
|
+
result
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
if (writeState(state, baseDir)) {
|
|
354
|
+
return state;
|
|
355
|
+
}
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Add findings to state
|
|
361
|
+
* @param {string} category - Finding category (issues, docs, code, drift, gaps)
|
|
362
|
+
* @param {Array} items - Finding items to add
|
|
363
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
364
|
+
* @returns {Object|null} Updated state or null on error
|
|
365
|
+
*/
|
|
366
|
+
function addFindings(category, items, baseDir = process.cwd()) {
|
|
367
|
+
const state = readState(baseDir);
|
|
368
|
+
if (!state) return null;
|
|
369
|
+
|
|
370
|
+
state.findings[category] = [
|
|
371
|
+
...(state.findings[category] || []),
|
|
372
|
+
...items
|
|
373
|
+
];
|
|
374
|
+
|
|
375
|
+
if (writeState(state, baseDir)) {
|
|
376
|
+
return state;
|
|
377
|
+
}
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Start a phase
|
|
383
|
+
* @param {string} phaseName - Phase name
|
|
384
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
385
|
+
* @returns {Object|null} Updated state or null on error
|
|
386
|
+
*/
|
|
387
|
+
function startPhase(phaseName, baseDir = process.cwd()) {
|
|
388
|
+
if (!PHASES.includes(phaseName)) {
|
|
389
|
+
console.error(`Invalid phase: ${phaseName}`);
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const state = readState(baseDir);
|
|
394
|
+
if (!state) return null;
|
|
395
|
+
|
|
396
|
+
state.phases.current = phaseName;
|
|
397
|
+
state.phases.history.push({
|
|
398
|
+
phase: phaseName,
|
|
399
|
+
status: 'in_progress',
|
|
400
|
+
startedAt: new Date().toISOString()
|
|
401
|
+
});
|
|
402
|
+
state.scan.status = 'in_progress';
|
|
403
|
+
|
|
404
|
+
if (writeState(state, baseDir)) {
|
|
405
|
+
return state;
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Complete the current phase
|
|
412
|
+
* @param {Object} [result={}] - Phase result
|
|
413
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
414
|
+
* @returns {Object|null} Updated state or null on error
|
|
415
|
+
*/
|
|
416
|
+
function completePhase(result = {}, baseDir = process.cwd()) {
|
|
417
|
+
const state = readState(baseDir);
|
|
418
|
+
if (!state) return null;
|
|
419
|
+
|
|
420
|
+
const currentEntry = state.phases.history[state.phases.history.length - 1];
|
|
421
|
+
if (currentEntry) {
|
|
422
|
+
currentEntry.status = 'completed';
|
|
423
|
+
currentEntry.completedAt = new Date().toISOString();
|
|
424
|
+
currentEntry.result = result;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const currentIndex = PHASES.indexOf(state.phases.current);
|
|
428
|
+
state.phases.current = currentIndex < PHASES.length - 1
|
|
429
|
+
? PHASES[currentIndex + 1]
|
|
430
|
+
: 'complete';
|
|
431
|
+
|
|
432
|
+
if (state.phases.current === 'complete') {
|
|
433
|
+
state.scan.status = 'completed';
|
|
434
|
+
state.scan.completedAt = new Date().toISOString();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (writeState(state, baseDir)) {
|
|
438
|
+
return state;
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Set the final report
|
|
445
|
+
* @param {Object} report - Report data
|
|
446
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
447
|
+
* @returns {Object|null} Updated state or null on error
|
|
448
|
+
*/
|
|
449
|
+
function setReport(report, baseDir = process.cwd()) {
|
|
450
|
+
const state = readState(baseDir);
|
|
451
|
+
if (!state) return null;
|
|
452
|
+
|
|
453
|
+
state.report = report;
|
|
454
|
+
|
|
455
|
+
if (writeState(state, baseDir)) {
|
|
456
|
+
return state;
|
|
457
|
+
}
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Delete scan state (cleanup)
|
|
463
|
+
* @param {string} [baseDir=process.cwd()] - Base directory
|
|
464
|
+
* @returns {boolean} Success status
|
|
465
|
+
*/
|
|
466
|
+
function deleteState(baseDir = process.cwd()) {
|
|
467
|
+
const statePath = getStatePath(baseDir);
|
|
468
|
+
|
|
469
|
+
try {
|
|
470
|
+
if (fs.existsSync(statePath)) {
|
|
471
|
+
fs.unlinkSync(statePath);
|
|
472
|
+
}
|
|
473
|
+
return true;
|
|
474
|
+
} catch (error) {
|
|
475
|
+
console.error(`Error deleting state: ${error.message}`);
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
module.exports = {
|
|
481
|
+
// Constants
|
|
482
|
+
SCHEMA_VERSION,
|
|
483
|
+
PHASES,
|
|
484
|
+
DEFAULT_SETTINGS,
|
|
485
|
+
|
|
486
|
+
// Path functions
|
|
487
|
+
getStatePath,
|
|
488
|
+
getSettingsPath,
|
|
489
|
+
ensureStateDir,
|
|
490
|
+
|
|
491
|
+
// Settings
|
|
492
|
+
hasSettings,
|
|
493
|
+
readSettings,
|
|
494
|
+
writeSettings,
|
|
495
|
+
|
|
496
|
+
// State CRUD
|
|
497
|
+
generateScanId,
|
|
498
|
+
createState,
|
|
499
|
+
readState,
|
|
500
|
+
writeState,
|
|
501
|
+
deleteState,
|
|
502
|
+
|
|
503
|
+
// State updates
|
|
504
|
+
updateAgentResult,
|
|
505
|
+
addFindings,
|
|
506
|
+
startPhase,
|
|
507
|
+
completePhase,
|
|
508
|
+
setReport
|
|
509
|
+
};
|