swarm-orchestrator 7.0.0-alpha.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.
- package/LICENSE +15 -0
- package/README.md +199 -0
- package/agents/reviewer.agent.md +50 -0
- package/agents/worker.agent.md +41 -0
- package/config/default-agents.yaml +56 -0
- package/config/pm-agent.yaml +36 -0
- package/config/quality-gates.yaml +88 -0
- package/config/repair-agent.yaml +34 -0
- package/config/semgrep-rules/complexity-mismatch.yaml +6 -0
- package/config/semgrep-rules/exception-swallowing.yaml +13 -0
- package/config/semgrep-rules/hardcoded-answer.yaml +6 -0
- package/config/semgrep-rules/mock-mutation.yaml +6 -0
- package/config/semgrep-rules/test-modification.yaml +6 -0
- package/config/user-agents.yaml +23 -0
- package/dist/src/adapters/adapter-factory.js +29 -0
- package/dist/src/adapters/agent-adapter.js +32 -0
- package/dist/src/adapters/claude-code-adapter.js +126 -0
- package/dist/src/adapters/claude-code-teams.js +119 -0
- package/dist/src/adapters/codex-adapter.js +118 -0
- package/dist/src/adapters/copilot-adapter.js +224 -0
- package/dist/src/adapters/fatal-error-classifier.js +88 -0
- package/dist/src/adapters/index.js +44 -0
- package/dist/src/adapters/persistent-session.js +204 -0
- package/dist/src/adapters/process-supervisor.js +199 -0
- package/dist/src/agents-exporter.js +398 -0
- package/dist/src/analytics-log.js +161 -0
- package/dist/src/baseline-scanner.js +65 -0
- package/dist/src/bootstrap-evidence.js +88 -0
- package/dist/src/bootstrap-orchestrator.js +205 -0
- package/dist/src/bootstrap-types.js +5 -0
- package/dist/src/branch-merger.js +427 -0
- package/dist/src/cli/attest-handlers.js +44 -0
- package/dist/src/cli/cost-prompt.js +51 -0
- package/dist/src/cli/demo-handlers.js +274 -0
- package/dist/src/cli/flags.js +152 -0
- package/dist/src/cli/index.js +57 -0
- package/dist/src/cli/live-status.js +186 -0
- package/dist/src/cli/misc-handlers.js +242 -0
- package/dist/src/cli/plan-handlers.js +350 -0
- package/dist/src/cli/share-handlers.js +163 -0
- package/dist/src/cli/status-handlers.js +457 -0
- package/dist/src/cli/swarm-handlers.js +638 -0
- package/dist/src/cli/usage.js +72 -0
- package/dist/src/cli.js +217 -0
- package/dist/src/commit-pattern-detector.js +143 -0
- package/dist/src/commit-quality-analyzer.js +38 -0
- package/dist/src/config-loader.js +339 -0
- package/dist/src/context-broker.js +358 -0
- package/dist/src/copilot-cli-wrapper.js +258 -0
- package/dist/src/cost-estimator.js +241 -0
- package/dist/src/defaults.js +18 -0
- package/dist/src/demo-mode.js +180 -0
- package/dist/src/deployment-handler.js +78 -0
- package/dist/src/deployment-manager.js +228 -0
- package/dist/src/execution-queue.js +196 -0
- package/dist/src/external-tool-manager.js +184 -0
- package/dist/src/gate-prompt-builder.js +117 -0
- package/dist/src/gate-remediation.js +173 -0
- package/dist/src/github-issues-ingester.js +152 -0
- package/dist/src/github-mcp-integrator.js +164 -0
- package/dist/src/hook-generator.js +229 -0
- package/dist/src/index.js +48 -0
- package/dist/src/knowledge-base.js +300 -0
- package/dist/src/logger.js +111 -0
- package/dist/src/meta-analyzer.js +208 -0
- package/dist/src/metrics-collector.js +209 -0
- package/dist/src/metrics-types.js +5 -0
- package/dist/src/multi-repo-coordinator.js +179 -0
- package/dist/src/orchestrator/async-meta-analysis.js +84 -0
- package/dist/src/orchestrator/fatal-run-error.js +43 -0
- package/dist/src/orchestrator/final-gates-remediation.js +258 -0
- package/dist/src/orchestrator/git-state-utils.js +119 -0
- package/dist/src/orchestrator/pause-controller.js +55 -0
- package/dist/src/orchestrator/replan-runner.js +258 -0
- package/dist/src/orchestrator/step-executor.js +391 -0
- package/dist/src/orchestrator/wave-scheduler-loop.js +204 -0
- package/dist/src/owasp-mapper.js +153 -0
- package/dist/src/owasp-report-renderer.js +39 -0
- package/dist/src/plan-files.js +142 -0
- package/dist/src/plan-generator.js +1207 -0
- package/dist/src/pm-agent.js +296 -0
- package/dist/src/post-run-reporter.js +203 -0
- package/dist/src/pr-automation.js +103 -0
- package/dist/src/pr-manager.js +318 -0
- package/dist/src/presenter/index.js +209 -0
- package/dist/src/prompt-builder.js +173 -0
- package/dist/src/quality-gates/config-loader.js +196 -0
- package/dist/src/quality-gates/default-config.js +243 -0
- package/dist/src/quality-gates/file-utils.js +152 -0
- package/dist/src/quality-gates/gate-runner.js +171 -0
- package/dist/src/quality-gates/gates/accessibility.js +355 -0
- package/dist/src/quality-gates/gates/duplicate-blocks.js +144 -0
- package/dist/src/quality-gates/gates/hardcoded-config.js +52 -0
- package/dist/src/quality-gates/gates/readme-claims.js +62 -0
- package/dist/src/quality-gates/gates/runtime-checks.js +265 -0
- package/dist/src/quality-gates/gates/scaffold-defaults.js +108 -0
- package/dist/src/quality-gates/gates/test-coverage.js +240 -0
- package/dist/src/quality-gates/gates/test-file-protection.js +85 -0
- package/dist/src/quality-gates/gates/test-isolation.js +74 -0
- package/dist/src/quality-gates/index.js +17 -0
- package/dist/src/quality-gates/registry.js +205 -0
- package/dist/src/quality-gates/types.js +2 -0
- package/dist/src/quick-fix-mode.js +302 -0
- package/dist/src/recipe-loader.js +122 -0
- package/dist/src/repair-agent.js +396 -0
- package/dist/src/repo-analyzer.js +342 -0
- package/dist/src/report-generator.js +146 -0
- package/dist/src/report-renderer.js +90 -0
- package/dist/src/requirement-filter.js +52 -0
- package/dist/src/sarif-formatter.js +140 -0
- package/dist/src/scheduling/dependency-analyzer.js +178 -0
- package/dist/src/scheduling/work-stealing-queue.js +123 -0
- package/dist/src/secret-redactor.js +90 -0
- package/dist/src/session-executor.js +587 -0
- package/dist/src/session-manager.js +221 -0
- package/dist/src/share/transcript-verification.js +128 -0
- package/dist/src/share-parser.js +477 -0
- package/dist/src/spinner.js +146 -0
- package/dist/src/step-runner.js +365 -0
- package/dist/src/swarm-orchestrator.js +686 -0
- package/dist/src/task-classifier.js +134 -0
- package/dist/src/test-command-discovery.js +123 -0
- package/dist/src/text-similarity.js +24 -0
- package/dist/src/tier-maps.js +158 -0
- package/dist/src/types.js +5 -0
- package/dist/src/url-shortener.js +135 -0
- package/dist/src/verification/attestation.js +166 -0
- package/dist/src/verification/cheat-detector.js +240 -0
- package/dist/src/verification/command-runner.js +70 -0
- package/dist/src/verification/composite-score.js +157 -0
- package/dist/src/verification/cosign-attestation.js +137 -0
- package/dist/src/verification/diff-analysis.js +89 -0
- package/dist/src/verification/differential-gate.js +173 -0
- package/dist/src/verification/index.js +41 -0
- package/dist/src/verification/mutation-gate.js +242 -0
- package/dist/src/verification/property-gate.js +201 -0
- package/dist/src/verification/test-synthesizer.js +243 -0
- package/dist/src/verifier/outcome-checks.js +396 -0
- package/dist/src/verifier/transcript-checks.js +207 -0
- package/dist/src/verifier/verification-reporters.js +148 -0
- package/dist/src/verifier-engine.js +491 -0
- package/dist/src/wave-resizer.js +131 -0
- package/dist/src/wave-scheduler.js +44 -0
- package/dist/src/worktree-manager.js +368 -0
- package/dist/src/worktree-reserved-paths.js +97 -0
- package/package.json +80 -0
- package/plugin/agents/.gitkeep +0 -0
- package/plugin/agents/backend_master.agent.md +72 -0
- package/plugin/agents/devops_pro.agent.md +45 -0
- package/plugin/agents/frontend_expert.agent.md +59 -0
- package/plugin/agents/integrator_finalizer.agent.md +44 -0
- package/plugin/agents/meta_reviewer.agent.md +44 -0
- package/plugin/agents/security_auditor.agent.md +43 -0
- package/plugin/agents/tester_elite.agent.md +43 -0
- package/plugin/hooks/hooks.json +19 -0
- package/plugin/hooks/post-tool-use.json +9 -0
- package/plugin/hooks/pre-tool-use.json +9 -0
- package/plugin/plugin.json +47 -0
- package/plugin/skills/swarm-gates/SKILL.md +53 -0
- package/plugin/skills/swarm-orchestrate/SKILL.md +34 -0
- package/plugin/skills/swarm-verify/SKILL.md +44 -0
- package/templates/README.md +35 -0
- package/templates/cli-tool.json +31 -0
- package/templates/fullstack.json +52 -0
- package/templates/library.json +38 -0
- package/templates/react-app.json +45 -0
- package/templates/recipes/add-api-docs.json +35 -0
- package/templates/recipes/add-auth.json +47 -0
- package/templates/recipes/add-ci.json +39 -0
- package/templates/recipes/add-tests.json +39 -0
- package/templates/recipes/migrate-to-ts.json +42 -0
- package/templates/recipes/refactor-modularize.json +44 -0
- package/templates/recipes/security-audit.json +35 -0
- package/templates/rest-api.json +38 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ShareParser = void 0;
|
|
4
|
+
const transcript_verification_1 = require("./share/transcript-verification");
|
|
5
|
+
class ShareParser {
|
|
6
|
+
/**
|
|
7
|
+
* parse /share transcript and extract key facts with comprehensive verification
|
|
8
|
+
*/
|
|
9
|
+
parse(content) {
|
|
10
|
+
const index = {
|
|
11
|
+
changedFiles: [],
|
|
12
|
+
commandsExecuted: [],
|
|
13
|
+
testsRun: [],
|
|
14
|
+
prLinks: [],
|
|
15
|
+
gitCommits: [],
|
|
16
|
+
packageOperations: [],
|
|
17
|
+
buildOperations: [],
|
|
18
|
+
lintOperations: [],
|
|
19
|
+
mcpSections: [],
|
|
20
|
+
claims: []
|
|
21
|
+
};
|
|
22
|
+
const lines = content.split('\n');
|
|
23
|
+
// extract changed files
|
|
24
|
+
index.changedFiles = this.extractChangedFiles(lines);
|
|
25
|
+
// extract commands executed
|
|
26
|
+
index.commandsExecuted = this.extractCommands(lines);
|
|
27
|
+
// extract test runs
|
|
28
|
+
index.testsRun = this.extractTestRuns(lines, index.commandsExecuted);
|
|
29
|
+
// extract PR links
|
|
30
|
+
index.prLinks = this.extractPRLinks(lines);
|
|
31
|
+
// extract git commits
|
|
32
|
+
index.gitCommits = this.extractGitCommits(lines, index.commandsExecuted);
|
|
33
|
+
// extract package operations
|
|
34
|
+
index.packageOperations = this.extractPackageOperations(lines, index.commandsExecuted);
|
|
35
|
+
// extract build operations
|
|
36
|
+
index.buildOperations = this.extractBuildOperations(lines, index.commandsExecuted);
|
|
37
|
+
// extract lint operations
|
|
38
|
+
index.lintOperations = this.extractLintOperations(lines, index.commandsExecuted);
|
|
39
|
+
// extract MCP sections
|
|
40
|
+
index.mcpSections = this.extractMcpSections(lines);
|
|
41
|
+
// extract and verify claims
|
|
42
|
+
index.claims = (0, transcript_verification_1.extractClaims)(lines, index);
|
|
43
|
+
return index;
|
|
44
|
+
}
|
|
45
|
+
extractChangedFiles(lines) {
|
|
46
|
+
const files = new Set();
|
|
47
|
+
// look for git status output
|
|
48
|
+
let inGitStatus = false;
|
|
49
|
+
for (const line of lines) {
|
|
50
|
+
if (line.includes('git status') || line.includes('git diff')) {
|
|
51
|
+
inGitStatus = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (inGitStatus) {
|
|
55
|
+
// match modified files
|
|
56
|
+
const modifiedMatch = line.match(/^\s*modified:\s+(.+)$/);
|
|
57
|
+
if (modifiedMatch && modifiedMatch[1]) {
|
|
58
|
+
files.add(modifiedMatch[1].trim());
|
|
59
|
+
}
|
|
60
|
+
// match new files
|
|
61
|
+
const newFileMatch = line.match(/^\s*new file:\s+(.+)$/);
|
|
62
|
+
if (newFileMatch && newFileMatch[1]) {
|
|
63
|
+
files.add(newFileMatch[1].trim());
|
|
64
|
+
}
|
|
65
|
+
// match deleted files
|
|
66
|
+
const deletedMatch = line.match(/^\s*deleted:\s+(.+)$/);
|
|
67
|
+
if (deletedMatch && deletedMatch[1]) {
|
|
68
|
+
files.add(deletedMatch[1].trim());
|
|
69
|
+
}
|
|
70
|
+
// end of git status block
|
|
71
|
+
if (line.trim() === '' || line.includes('$') || line.includes('>')) {
|
|
72
|
+
inGitStatus = false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// also look for explicit file mentions in code blocks
|
|
76
|
+
const filePathMatch = line.match(/^[\s]*(?:edited|created|modified|deleted):\s+([^\s]+\.[a-z]+)/i);
|
|
77
|
+
if (filePathMatch && filePathMatch[1]) {
|
|
78
|
+
files.add(filePathMatch[1]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return Array.from(files);
|
|
82
|
+
}
|
|
83
|
+
extractCommands(lines) {
|
|
84
|
+
const commands = [];
|
|
85
|
+
// Accumulator for multi-line Copilot CLI commands (continued with │ prefix)
|
|
86
|
+
let pendingCopilotCmd = '';
|
|
87
|
+
for (let i = 0; i < lines.length; i++) {
|
|
88
|
+
const line = lines[i];
|
|
89
|
+
if (!line) {
|
|
90
|
+
// Blank line ends any pending multi-line command
|
|
91
|
+
if (pendingCopilotCmd) {
|
|
92
|
+
commands.push(pendingCopilotCmd);
|
|
93
|
+
pendingCopilotCmd = '';
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
// Copilot CLI transcript format: commands prefixed with box-drawing │ (U+2502)
|
|
98
|
+
// e.g. " │ source venv/bin/activate && python -m pytest tests/ -v"
|
|
99
|
+
const copilotMatch = line.match(/^\s*\u2502\s+(.+)/);
|
|
100
|
+
if (copilotMatch) {
|
|
101
|
+
const fragment = copilotMatch[1].trim();
|
|
102
|
+
// └ lines are output summaries (e.g. "└ 86 lines..."), not commands
|
|
103
|
+
if (!fragment.startsWith('\u2514') && !fragment.match(/^\d+ lines/)) {
|
|
104
|
+
if (pendingCopilotCmd) {
|
|
105
|
+
// Continuation of a multi-line command (e.g. && on previous line)
|
|
106
|
+
pendingCopilotCmd += ' ' + fragment;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
pendingCopilotCmd = fragment;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else if (pendingCopilotCmd) {
|
|
113
|
+
// Output summary terminates the pending command
|
|
114
|
+
commands.push(pendingCopilotCmd);
|
|
115
|
+
pendingCopilotCmd = '';
|
|
116
|
+
}
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
// Non-│ line ends any pending multi-line Copilot command
|
|
120
|
+
if (pendingCopilotCmd) {
|
|
121
|
+
commands.push(pendingCopilotCmd);
|
|
122
|
+
pendingCopilotCmd = '';
|
|
123
|
+
}
|
|
124
|
+
// look for command prompts
|
|
125
|
+
if (line.match(/^[\$>]\s+/) || line.match(/^npm\s+/) || line.match(/^git\s+/)) {
|
|
126
|
+
const cmd = line.replace(/^[\$>]\s+/, '').trim();
|
|
127
|
+
if (cmd) {
|
|
128
|
+
commands.push(cmd);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// look for commands in code blocks
|
|
132
|
+
if (line.includes('```') && i + 1 < lines.length) {
|
|
133
|
+
const nextLine = lines[i + 1];
|
|
134
|
+
if (nextLine && (nextLine.startsWith('$') || nextLine.startsWith('npm') || nextLine.startsWith('git'))) {
|
|
135
|
+
const cmd = nextLine.replace(/^[\$>]\s+/, '').trim();
|
|
136
|
+
if (cmd) {
|
|
137
|
+
commands.push(cmd);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Flush any trailing command
|
|
143
|
+
if (pendingCopilotCmd) {
|
|
144
|
+
commands.push(pendingCopilotCmd);
|
|
145
|
+
}
|
|
146
|
+
return commands;
|
|
147
|
+
}
|
|
148
|
+
extractTestRuns(lines, commands) {
|
|
149
|
+
const testRuns = [];
|
|
150
|
+
// find test commands
|
|
151
|
+
const testCommands = commands.filter(cmd => cmd.includes('test') ||
|
|
152
|
+
cmd.includes('jest') ||
|
|
153
|
+
cmd.includes('mocha') ||
|
|
154
|
+
cmd.includes('pytest') ||
|
|
155
|
+
cmd.includes('go test') ||
|
|
156
|
+
cmd.includes('cargo test'));
|
|
157
|
+
for (const cmd of testCommands) {
|
|
158
|
+
// verify test actually ran by looking for output
|
|
159
|
+
const hasOutput = this.hasTestOutput(lines, cmd);
|
|
160
|
+
if (hasOutput) {
|
|
161
|
+
testRuns.push({
|
|
162
|
+
command: cmd,
|
|
163
|
+
verified: true
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
testRuns.push({
|
|
168
|
+
command: cmd,
|
|
169
|
+
verified: false,
|
|
170
|
+
reason: 'no test output found in transcript'
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return testRuns;
|
|
175
|
+
}
|
|
176
|
+
hasTestOutput(lines, testCommand) {
|
|
177
|
+
// look for common test output patterns after the command
|
|
178
|
+
const cmdIndex = lines.findIndex(line => line.includes(testCommand));
|
|
179
|
+
if (cmdIndex === -1)
|
|
180
|
+
return false;
|
|
181
|
+
// check next 80 lines for test output (Node.js test runner can be verbose)
|
|
182
|
+
const outputLines = lines.slice(cmdIndex + 1, cmdIndex + 81);
|
|
183
|
+
for (const line of outputLines) {
|
|
184
|
+
// mocha/jest patterns
|
|
185
|
+
if (line.match(/\d+\s+passing/))
|
|
186
|
+
return true;
|
|
187
|
+
if (line.match(/\d+\s+tests?\s+passed/))
|
|
188
|
+
return true;
|
|
189
|
+
// pytest patterns
|
|
190
|
+
if (line.match(/\d+\s+passed/))
|
|
191
|
+
return true;
|
|
192
|
+
// go test patterns
|
|
193
|
+
if (line.match(/PASS/))
|
|
194
|
+
return true;
|
|
195
|
+
if (line.match(/ok\s+/))
|
|
196
|
+
return true;
|
|
197
|
+
// Node.js built-in test runner patterns (node --test)
|
|
198
|
+
if (line.match(/tests\s+\d+/))
|
|
199
|
+
return true; // "ℹ tests 10"
|
|
200
|
+
if (line.match(/pass\s+\d+/))
|
|
201
|
+
return true; // "ℹ pass 10"
|
|
202
|
+
if (line.match(/fail\s+0/))
|
|
203
|
+
return true; // "ℹ fail 0"
|
|
204
|
+
if (line.match(/duration_ms/))
|
|
205
|
+
return true; // "ℹ duration_ms 123"
|
|
206
|
+
// generic success patterns
|
|
207
|
+
if (line.match(/all tests passed/i))
|
|
208
|
+
return true;
|
|
209
|
+
if (line.match(/all\s+\d+\s+tests?\s+pass/i))
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
extractPRLinks(lines) {
|
|
215
|
+
const links = new Set();
|
|
216
|
+
for (const line of lines) {
|
|
217
|
+
// github PR URLs
|
|
218
|
+
const prMatch = line.match(/https:\/\/github\.com\/[^\/]+\/[^\/]+\/pull\/\d+/g);
|
|
219
|
+
if (prMatch) {
|
|
220
|
+
prMatch.forEach(url => links.add(url));
|
|
221
|
+
}
|
|
222
|
+
// shortened PR references
|
|
223
|
+
const shortMatch = line.match(/#(\d+)/g);
|
|
224
|
+
if (shortMatch) {
|
|
225
|
+
// note: we can't construct full URL without repo context
|
|
226
|
+
// so we just note the PR number
|
|
227
|
+
shortMatch.forEach(ref => links.add(ref));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return Array.from(links);
|
|
231
|
+
}
|
|
232
|
+
extractGitCommits(lines, commands) {
|
|
233
|
+
const commits = [];
|
|
234
|
+
const commitMap = new Map();
|
|
235
|
+
// look for git commit commands in extracted commands list
|
|
236
|
+
const commitCommands = commands.filter(cmd => cmd.match(/git\s+commit/));
|
|
237
|
+
for (const cmd of commitCommands) {
|
|
238
|
+
// try single-line quoted message first
|
|
239
|
+
const messageMatch = cmd.match(/git\s+commit\s+-m\s+["']([^"']+)["']/);
|
|
240
|
+
if (messageMatch && messageMatch[1]) {
|
|
241
|
+
const message = messageMatch[1];
|
|
242
|
+
commitMap.set(message, { message, verified: true });
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// message may span multiple lines or have an unclosed quote;
|
|
246
|
+
// grab everything after -m " as the first line of the message
|
|
247
|
+
const partialMatch = cmd.match(/git\s+commit\s+-m\s+["'](.+)/);
|
|
248
|
+
if (partialMatch && partialMatch[1]) {
|
|
249
|
+
const message = partialMatch[1].replace(/["']$/, '').trim();
|
|
250
|
+
if (message) {
|
|
251
|
+
commitMap.set(message, { message, verified: true });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// also scan raw lines for git commit commands that extractCommands may
|
|
257
|
+
// have missed (e.g. chained commands: cd ... && git commit -m "...")
|
|
258
|
+
for (const line of lines) {
|
|
259
|
+
const inlineMatch = line.match(/git\s+commit\s+-m\s+["']([^"'\n]+)/);
|
|
260
|
+
if (inlineMatch && inlineMatch[1]) {
|
|
261
|
+
const message = inlineMatch[1].trim();
|
|
262
|
+
if (message && !commitMap.has(message)) {
|
|
263
|
+
commitMap.set(message, { message, verified: true });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// look for commit SHAs in git output
|
|
268
|
+
// format: [branch-name sha] message (branch can contain / and -)
|
|
269
|
+
for (const line of lines) {
|
|
270
|
+
const shaMatch = line.match(/\[[\w/.:-]+\s+([a-f0-9]{7,40})\]\s+(.+)/);
|
|
271
|
+
if (shaMatch && shaMatch[1] && shaMatch[2]) {
|
|
272
|
+
const sha = shaMatch[1];
|
|
273
|
+
const messageAfterSha = shaMatch[2].trim();
|
|
274
|
+
// try to match with an already-known commit
|
|
275
|
+
let foundCommit = false;
|
|
276
|
+
for (const [message, commit] of commitMap.entries()) {
|
|
277
|
+
if (messageAfterSha.includes(message) || message.includes(messageAfterSha)) {
|
|
278
|
+
commit.sha = sha;
|
|
279
|
+
foundCommit = true;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (!foundCommit && messageAfterSha) {
|
|
284
|
+
commitMap.set(messageAfterSha, { message: messageAfterSha, sha, verified: true });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// convert map to array
|
|
289
|
+
for (const commit of commitMap.values()) {
|
|
290
|
+
const result = {
|
|
291
|
+
message: commit.message || 'commit detected',
|
|
292
|
+
verified: true
|
|
293
|
+
};
|
|
294
|
+
if (commit.sha) {
|
|
295
|
+
result.sha = commit.sha;
|
|
296
|
+
}
|
|
297
|
+
commits.push(result);
|
|
298
|
+
}
|
|
299
|
+
return commits;
|
|
300
|
+
}
|
|
301
|
+
extractPackageOperations(lines, commands) {
|
|
302
|
+
const operations = [];
|
|
303
|
+
const processedCommands = new Set();
|
|
304
|
+
// npm/yarn/pnpm patterns
|
|
305
|
+
const packageManagers = ['npm', 'yarn', 'pnpm', 'bun'];
|
|
306
|
+
for (const cmd of commands) {
|
|
307
|
+
// skip if already processed
|
|
308
|
+
if (processedCommands.has(cmd)) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
for (const pm of packageManagers) {
|
|
312
|
+
// install operations (install, add, i)
|
|
313
|
+
const installMatch = cmd.match(new RegExp(`^${pm}\\s+(?:add|install|i)\\s+(.+)`));
|
|
314
|
+
if (installMatch && installMatch[1]) {
|
|
315
|
+
const argString = installMatch[1];
|
|
316
|
+
// filter out flags and extract package names
|
|
317
|
+
const packages = argString
|
|
318
|
+
.split(/\s+/)
|
|
319
|
+
.filter(p => p.trim() !== '' && !p.startsWith('-'));
|
|
320
|
+
if (packages.length > 0) {
|
|
321
|
+
operations.push({
|
|
322
|
+
operation: 'install',
|
|
323
|
+
packages
|
|
324
|
+
});
|
|
325
|
+
processedCommands.add(cmd);
|
|
326
|
+
break; // found a match, move to next command
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// uninstall operations
|
|
330
|
+
const uninstallMatch = cmd.match(new RegExp(`^${pm}\\s+(?:remove|uninstall|rm)\\s+(.+)`));
|
|
331
|
+
if (uninstallMatch && uninstallMatch[1]) {
|
|
332
|
+
const argString = uninstallMatch[1];
|
|
333
|
+
const packages = argString
|
|
334
|
+
.split(/\s+/)
|
|
335
|
+
.filter(p => p.trim() !== '' && !p.startsWith('-'));
|
|
336
|
+
if (packages.length > 0) {
|
|
337
|
+
operations.push({
|
|
338
|
+
operation: 'uninstall',
|
|
339
|
+
packages
|
|
340
|
+
});
|
|
341
|
+
processedCommands.add(cmd);
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// update operations
|
|
346
|
+
if (cmd.match(new RegExp(`^${pm}\\s+(?:update|upgrade)`))) {
|
|
347
|
+
operations.push({
|
|
348
|
+
operation: 'update',
|
|
349
|
+
packages: ['all packages']
|
|
350
|
+
});
|
|
351
|
+
processedCommands.add(cmd);
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return operations;
|
|
357
|
+
}
|
|
358
|
+
extractBuildOperations(lines, commands) {
|
|
359
|
+
const operations = [];
|
|
360
|
+
const buildTools = [
|
|
361
|
+
{ tool: 'tsc', pattern: /\btsc\b/ },
|
|
362
|
+
{ tool: 'webpack', pattern: /\bwebpack\b/ },
|
|
363
|
+
{ tool: 'vite', pattern: /\bvite\s+build\b/ },
|
|
364
|
+
{ tool: 'rollup', pattern: /\brollup\b/ },
|
|
365
|
+
{ tool: 'esbuild', pattern: /\besbuild\b/ },
|
|
366
|
+
{ tool: 'swc', pattern: /\bswc\b/ },
|
|
367
|
+
{ tool: 'babel', pattern: /\bbabel\b/ },
|
|
368
|
+
{ tool: 'next', pattern: /\bnext\s+build\b/ },
|
|
369
|
+
{ tool: 'nuxt', pattern: /\bnuxt\s+build\b/ },
|
|
370
|
+
{ tool: 'npm build', pattern: /\bnpm\s+run\s+build\b/ },
|
|
371
|
+
{ tool: 'yarn build', pattern: /\byarn\s+build\b/ }
|
|
372
|
+
];
|
|
373
|
+
for (const { tool, pattern } of buildTools) {
|
|
374
|
+
const buildCmd = commands.find(cmd => pattern.test(cmd));
|
|
375
|
+
if (buildCmd) {
|
|
376
|
+
// verify build succeeded by looking for output
|
|
377
|
+
const verified = this.hasBuildOutput(lines, buildCmd);
|
|
378
|
+
operations.push({ tool, verified });
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return operations;
|
|
382
|
+
}
|
|
383
|
+
hasBuildOutput(lines, buildCommand) {
|
|
384
|
+
const cmdIndex = lines.findIndex(line => line.includes(buildCommand));
|
|
385
|
+
if (cmdIndex === -1)
|
|
386
|
+
return false;
|
|
387
|
+
// check next 100 lines for build success indicators
|
|
388
|
+
const outputLines = lines.slice(cmdIndex + 1, cmdIndex + 101);
|
|
389
|
+
for (const line of outputLines) {
|
|
390
|
+
// success patterns
|
|
391
|
+
if (line.match(/build\s+succe(ss|eded)/i))
|
|
392
|
+
return true;
|
|
393
|
+
if (line.match(/compiled successfully/i))
|
|
394
|
+
return true;
|
|
395
|
+
if (line.match(/built in \d+/i))
|
|
396
|
+
return true;
|
|
397
|
+
if (line.match(/✓.*built/i))
|
|
398
|
+
return true;
|
|
399
|
+
if (line.match(/done in \d+/i))
|
|
400
|
+
return true;
|
|
401
|
+
if (line.match(/successfully built/i))
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
extractLintOperations(lines, commands) {
|
|
407
|
+
const operations = [];
|
|
408
|
+
const lintTools = [
|
|
409
|
+
{ tool: 'eslint', pattern: /\beslint\b/ },
|
|
410
|
+
{ tool: 'prettier', pattern: /\bprettier\b/ },
|
|
411
|
+
{ tool: 'biome', pattern: /\bbiome\s+(?:check|lint)\b/ },
|
|
412
|
+
{ tool: 'tslint', pattern: /\btslint\b/ },
|
|
413
|
+
{ tool: 'npm lint', pattern: /\bnpm\s+run\s+lint\b/ },
|
|
414
|
+
{ tool: 'yarn lint', pattern: /\byarn\s+lint\b/ }
|
|
415
|
+
];
|
|
416
|
+
for (const { tool, pattern } of lintTools) {
|
|
417
|
+
const lintCmd = commands.find(cmd => pattern.test(cmd));
|
|
418
|
+
if (lintCmd) {
|
|
419
|
+
// verify lint succeeded
|
|
420
|
+
const verified = this.hasLintOutput(lines, lintCmd);
|
|
421
|
+
operations.push({ tool, verified });
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return operations;
|
|
425
|
+
}
|
|
426
|
+
hasLintOutput(lines, lintCommand) {
|
|
427
|
+
const cmdIndex = lines.findIndex(line => line.includes(lintCommand));
|
|
428
|
+
if (cmdIndex === -1)
|
|
429
|
+
return false;
|
|
430
|
+
// check next 50 lines for lint output
|
|
431
|
+
const outputLines = lines.slice(cmdIndex + 1, cmdIndex + 51);
|
|
432
|
+
for (const line of outputLines) {
|
|
433
|
+
// success patterns
|
|
434
|
+
if (line.match(/✓.*no.*(error|warning|problem)/i))
|
|
435
|
+
return true;
|
|
436
|
+
if (line.match(/0\s+errors/i))
|
|
437
|
+
return true;
|
|
438
|
+
if (line.match(/all.*passed/i))
|
|
439
|
+
return true;
|
|
440
|
+
if (line.match(/✨.*done/i))
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
extractMcpSections(lines) {
|
|
446
|
+
const sections = [];
|
|
447
|
+
// look for MCP Evidence headers
|
|
448
|
+
for (let i = 0; i < lines.length; i++) {
|
|
449
|
+
const line = lines[i];
|
|
450
|
+
if (!line)
|
|
451
|
+
continue;
|
|
452
|
+
if (line.match(/##\s+MCP\s+Evidence/i)) {
|
|
453
|
+
// extract section content (everything until next ## or end)
|
|
454
|
+
let content = '';
|
|
455
|
+
let j = i + 1;
|
|
456
|
+
while (j < lines.length && !lines[j]?.match(/^##\s+/)) {
|
|
457
|
+
content += (lines[j] || '') + '\n';
|
|
458
|
+
j++;
|
|
459
|
+
}
|
|
460
|
+
const trimmedContent = content.trim();
|
|
461
|
+
// verify MCP section has actual evidence
|
|
462
|
+
const hasIssueRef = /issue\s+#?\d+/i.test(trimmedContent);
|
|
463
|
+
const hasPrRef = /pr\s+#?\d+|pull request/i.test(trimmedContent);
|
|
464
|
+
const hasWorkflowRef = /workflow|\.github\/workflows|CI/i.test(trimmedContent);
|
|
465
|
+
const hasDecision = /decision|influenced|based on|considering/i.test(trimmedContent);
|
|
466
|
+
const verified = (hasIssueRef || hasPrRef || hasWorkflowRef) && hasDecision && trimmedContent.length >= 50;
|
|
467
|
+
sections.push({
|
|
468
|
+
content: trimmedContent,
|
|
469
|
+
verified
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return sections;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
exports.ShareParser = ShareParser;
|
|
477
|
+
exports.default = ShareParser;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Author: Bradley R. Kinnard
|
|
3
|
+
// Terminal spinner for visual feedback during long operations
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.ProgressTracker = exports.Spinner = void 0;
|
|
6
|
+
const logger_1 = require("./logger");
|
|
7
|
+
const logger = (0, logger_1.getLogger)('spinner');
|
|
8
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
9
|
+
const DOTS_FRAMES = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
10
|
+
const BOUNCE_FRAMES = ['⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'];
|
|
11
|
+
const PULSE_FRAMES = ['🐝', '🐝 ', '🐝 ', '🐝 ', '🐝 ', '🐝 ', '🐝'];
|
|
12
|
+
const FRAME_SETS = {
|
|
13
|
+
dots: DOTS_FRAMES,
|
|
14
|
+
spinner: SPINNER_FRAMES,
|
|
15
|
+
bounce: BOUNCE_FRAMES,
|
|
16
|
+
pulse: PULSE_FRAMES
|
|
17
|
+
};
|
|
18
|
+
class Spinner {
|
|
19
|
+
frames;
|
|
20
|
+
frameIndex = 0;
|
|
21
|
+
interval = null;
|
|
22
|
+
message;
|
|
23
|
+
prefix;
|
|
24
|
+
isRunning = false;
|
|
25
|
+
constructor(message, options = {}) {
|
|
26
|
+
this.frames = FRAME_SETS[options.style || 'dots'];
|
|
27
|
+
this.message = message;
|
|
28
|
+
this.prefix = options.prefix || '';
|
|
29
|
+
}
|
|
30
|
+
start() {
|
|
31
|
+
if (this.isRunning)
|
|
32
|
+
return;
|
|
33
|
+
// When stdout is not a TTY (piped to a file, CI log, etc.) cursor-control
|
|
34
|
+
// escapes show up as literal bytes and corrupt the captured output.
|
|
35
|
+
if (!process.stdout.isTTY)
|
|
36
|
+
return;
|
|
37
|
+
this.isRunning = true;
|
|
38
|
+
this.frameIndex = 0;
|
|
39
|
+
// hide cursor
|
|
40
|
+
process.stdout.write('\x1B[?25l');
|
|
41
|
+
this.interval = setInterval(() => {
|
|
42
|
+
const frame = this.frames[this.frameIndex];
|
|
43
|
+
process.stdout.write(`\r${this.prefix}${frame} ${this.message}`);
|
|
44
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
45
|
+
}, 80);
|
|
46
|
+
}
|
|
47
|
+
update(message) {
|
|
48
|
+
this.message = message;
|
|
49
|
+
}
|
|
50
|
+
stop(finalMessage) {
|
|
51
|
+
if (!this.isRunning) {
|
|
52
|
+
// Even when start() was skipped, emit the final message via logger.
|
|
53
|
+
if (finalMessage) {
|
|
54
|
+
logger.info(`${this.prefix}${finalMessage}`);
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.isRunning = false;
|
|
59
|
+
if (this.interval) {
|
|
60
|
+
clearInterval(this.interval);
|
|
61
|
+
this.interval = null;
|
|
62
|
+
}
|
|
63
|
+
// clear line and show cursor
|
|
64
|
+
process.stdout.write('\r\x1B[K');
|
|
65
|
+
process.stdout.write('\x1B[?25h');
|
|
66
|
+
if (finalMessage) {
|
|
67
|
+
logger.info(`${this.prefix}${finalMessage}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
succeed(message) {
|
|
71
|
+
this.stop(`${this.color('✓', '32', '39')} ${message || this.message}`);
|
|
72
|
+
}
|
|
73
|
+
fail(message) {
|
|
74
|
+
this.stop(`${this.color('✗', '31', '39')} ${message || this.message}`);
|
|
75
|
+
}
|
|
76
|
+
warn(message) {
|
|
77
|
+
this.stop(`${this.color('!', '33', '39')} ${message || this.message}`);
|
|
78
|
+
}
|
|
79
|
+
info(message) {
|
|
80
|
+
this.stop(`${this.color('·', '2', '22')} ${message || this.message}`);
|
|
81
|
+
}
|
|
82
|
+
/** Wrap a glyph in ANSI color/style codes when stdout supports them. */
|
|
83
|
+
color(ch, open, close) {
|
|
84
|
+
if (!process.stdout.isTTY || process.env.NO_COLOR)
|
|
85
|
+
return ch;
|
|
86
|
+
return `\x1b[${open}m${ch}\x1b[${close}m`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.Spinner = Spinner;
|
|
90
|
+
/**
|
|
91
|
+
* Multi-step progress tracker with visual feedback
|
|
92
|
+
*/
|
|
93
|
+
class ProgressTracker {
|
|
94
|
+
activeSpinners = new Map();
|
|
95
|
+
stepStatus = new Map();
|
|
96
|
+
startStep(stepId, message, prefix = ' ') {
|
|
97
|
+
const spinner = new Spinner(message, { style: 'dots', prefix });
|
|
98
|
+
this.activeSpinners.set(stepId, spinner);
|
|
99
|
+
this.stepStatus.set(stepId, 'running');
|
|
100
|
+
spinner.start();
|
|
101
|
+
}
|
|
102
|
+
updateStep(stepId, message) {
|
|
103
|
+
const spinner = this.activeSpinners.get(stepId);
|
|
104
|
+
if (spinner) {
|
|
105
|
+
spinner.update(message);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
completeStep(stepId, message) {
|
|
109
|
+
const spinner = this.activeSpinners.get(stepId);
|
|
110
|
+
if (spinner) {
|
|
111
|
+
spinner.succeed(message);
|
|
112
|
+
this.activeSpinners.delete(stepId);
|
|
113
|
+
this.stepStatus.set(stepId, 'done');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
failStep(stepId, message) {
|
|
117
|
+
const existing = this.activeSpinners.get(stepId);
|
|
118
|
+
if (existing) {
|
|
119
|
+
existing.fail(message);
|
|
120
|
+
this.activeSpinners.delete(stepId);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
const spinner = new Spinner(message, { style: 'dots', prefix: ' ' });
|
|
124
|
+
spinner.fail(message);
|
|
125
|
+
}
|
|
126
|
+
this.stepStatus.set(stepId, 'failed');
|
|
127
|
+
}
|
|
128
|
+
warnStep(stepId, message) {
|
|
129
|
+
const spinner = this.activeSpinners.get(stepId);
|
|
130
|
+
if (spinner) {
|
|
131
|
+
spinner.warn(message);
|
|
132
|
+
this.activeSpinners.delete(stepId);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
stopAll() {
|
|
136
|
+
for (const spinner of this.activeSpinners.values()) {
|
|
137
|
+
spinner.stop();
|
|
138
|
+
}
|
|
139
|
+
this.activeSpinners.clear();
|
|
140
|
+
}
|
|
141
|
+
getRunningCount() {
|
|
142
|
+
return this.activeSpinners.size;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
exports.ProgressTracker = ProgressTracker;
|
|
146
|
+
exports.default = Spinner;
|