switchman-dev 0.1.2 → 0.1.3
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/README.md +301 -7
- package/examples/README.md +18 -2
- package/examples/walkthrough.sh +12 -16
- package/package.json +1 -1
- package/src/cli/index.js +1699 -37
- package/src/core/ci.js +114 -0
- package/src/core/db.js +1417 -26
- package/src/core/detector.js +109 -7
- package/src/core/enforcement.js +966 -0
- package/src/core/git.js +34 -4
- package/src/core/ignore.js +47 -0
- package/src/core/mcp.js +47 -0
- package/src/core/merge-gate.js +305 -0
- package/src/core/monitor.js +39 -0
- package/src/core/outcome.js +153 -0
- package/src/core/pipeline.js +1113 -0
- package/src/core/planner.js +508 -0
- package/src/core/semantic.js +311 -0
- package/src/mcp/server.js +321 -1
package/src/core/ci.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
|
|
4
|
+
function summarizeUnclaimedChanges(result) {
|
|
5
|
+
if (!result.unclaimed_changes?.length) return ['- None'];
|
|
6
|
+
return result.unclaimed_changes.map((entry) => `- ${entry.worktree}: ${entry.files.join(', ')}`);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function summarizeFileConflicts(result) {
|
|
10
|
+
if (!result.file_conflicts?.length) return ['- None'];
|
|
11
|
+
return result.file_conflicts.map((entry) => `- ${entry.file}: ${entry.worktrees.join(', ')}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function summarizeBranchConflicts(result) {
|
|
15
|
+
if (!result.branch_conflicts?.length) return ['- None'];
|
|
16
|
+
return result.branch_conflicts.map((entry) => `- ${entry.worktreeA} vs ${entry.worktreeB}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function formatCiGateMarkdown(result) {
|
|
20
|
+
return [
|
|
21
|
+
'# Switchman CI Gate',
|
|
22
|
+
'',
|
|
23
|
+
`- Status: ${result.ok ? 'pass' : 'blocked'}`,
|
|
24
|
+
`- Summary: ${result.summary}`,
|
|
25
|
+
'',
|
|
26
|
+
'## Compliance',
|
|
27
|
+
`- Managed: ${result.compliance?.managed ?? 0}`,
|
|
28
|
+
`- Observed: ${result.compliance?.observed ?? 0}`,
|
|
29
|
+
`- Non-compliant: ${result.compliance?.non_compliant ?? 0}`,
|
|
30
|
+
`- Stale: ${result.compliance?.stale ?? 0}`,
|
|
31
|
+
'',
|
|
32
|
+
'## Unclaimed Changes',
|
|
33
|
+
...summarizeUnclaimedChanges(result),
|
|
34
|
+
'',
|
|
35
|
+
'## File Conflicts',
|
|
36
|
+
...summarizeFileConflicts(result),
|
|
37
|
+
'',
|
|
38
|
+
'## Branch Conflicts',
|
|
39
|
+
...summarizeBranchConflicts(result),
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function writeGitHubCiStatus({ result, stepSummaryPath = null, outputPath = null }) {
|
|
44
|
+
const markdown = formatCiGateMarkdown(result);
|
|
45
|
+
|
|
46
|
+
if (stepSummaryPath) {
|
|
47
|
+
writeFileSync(stepSummaryPath, `${markdown}\n`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (outputPath) {
|
|
51
|
+
const lines = [
|
|
52
|
+
`switchman_ok=${result.ok ? 'true' : 'false'}`,
|
|
53
|
+
`switchman_summary=${JSON.stringify(result.summary)}`,
|
|
54
|
+
`switchman_non_compliant=${result.compliance?.non_compliant ?? 0}`,
|
|
55
|
+
`switchman_stale=${result.compliance?.stale ?? 0}`,
|
|
56
|
+
`switchman_unclaimed_changes=${result.unclaimed_changes?.length ?? 0}`,
|
|
57
|
+
`switchman_file_conflicts=${result.file_conflicts?.length ?? 0}`,
|
|
58
|
+
`switchman_branch_conflicts=${result.branch_conflicts?.length ?? 0}`,
|
|
59
|
+
];
|
|
60
|
+
writeFileSync(outputPath, `${lines.join('\n')}\n`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
markdown,
|
|
65
|
+
wrote_step_summary: Boolean(stepSummaryPath),
|
|
66
|
+
wrote_output: Boolean(outputPath),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function installGitHubActionsWorkflow(repoRoot, workflowName = 'switchman-gate.yml') {
|
|
71
|
+
const workflowsDir = join(repoRoot, '.github', 'workflows');
|
|
72
|
+
if (!existsSync(workflowsDir)) {
|
|
73
|
+
mkdirSync(workflowsDir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const workflowPath = join(workflowsDir, workflowName);
|
|
77
|
+
const workflow = `name: Switchman Gate
|
|
78
|
+
|
|
79
|
+
on:
|
|
80
|
+
pull_request:
|
|
81
|
+
push:
|
|
82
|
+
branches:
|
|
83
|
+
- main
|
|
84
|
+
|
|
85
|
+
jobs:
|
|
86
|
+
switchman-gate:
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
steps:
|
|
89
|
+
- name: Checkout
|
|
90
|
+
uses: actions/checkout@v4
|
|
91
|
+
|
|
92
|
+
- name: Setup Node
|
|
93
|
+
uses: actions/setup-node@v4
|
|
94
|
+
with:
|
|
95
|
+
node-version: 20
|
|
96
|
+
|
|
97
|
+
- name: Install Switchman
|
|
98
|
+
run: npm install -g switchman-dev
|
|
99
|
+
|
|
100
|
+
- name: Run Switchman CI gate
|
|
101
|
+
run: switchman gate ci --github
|
|
102
|
+
`;
|
|
103
|
+
|
|
104
|
+
writeFileSync(workflowPath, workflow);
|
|
105
|
+
return workflowPath;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function resolveGitHubOutputTargets(opts = {}, env = process.env) {
|
|
109
|
+
const githubMode = Boolean(opts.github);
|
|
110
|
+
return {
|
|
111
|
+
stepSummaryPath: opts.githubStepSummary || (githubMode ? env.GITHUB_STEP_SUMMARY || null : null),
|
|
112
|
+
outputPath: opts.githubOutput || (githubMode ? env.GITHUB_OUTPUT || null : null),
|
|
113
|
+
};
|
|
114
|
+
}
|