moflo 4.8.9 → 4.8.11
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/agents/core/coder.md +265 -265
- package/.claude/agents/core/planner.md +167 -167
- package/.claude/agents/core/researcher.md +189 -189
- package/.claude/agents/core/reviewer.md +325 -325
- package/.claude/agents/core/tester.md +318 -318
- package/.claude/agents/dual-mode/codex-coordinator.md +224 -224
- package/.claude/agents/dual-mode/codex-worker.md +211 -211
- package/.claude/agents/dual-mode/dual-orchestrator.md +291 -291
- package/.claude/agents/github/code-review-swarm.md +537 -537
- package/.claude/agents/github/github-modes.md +172 -172
- package/.claude/agents/github/issue-tracker.md +318 -318
- package/.claude/agents/github/multi-repo-swarm.md +552 -552
- package/.claude/agents/github/pr-manager.md +190 -190
- package/.claude/agents/github/project-board-sync.md +508 -508
- package/.claude/agents/github/release-manager.md +366 -366
- package/.claude/agents/github/release-swarm.md +582 -582
- package/.claude/agents/github/repo-architect.md +397 -397
- package/.claude/agents/github/swarm-issue.md +572 -572
- package/.claude/agents/github/swarm-pr.md +427 -427
- package/.claude/agents/github/sync-coordinator.md +451 -451
- package/.claude/agents/github/workflow-automation.md +634 -634
- package/.claude/agents/goal/code-goal-planner.md +445 -445
- package/.claude/agents/hive-mind/collective-intelligence-coordinator.md +129 -129
- package/.claude/agents/hive-mind/queen-coordinator.md +202 -202
- package/.claude/agents/hive-mind/scout-explorer.md +241 -241
- package/.claude/agents/hive-mind/swarm-memory-manager.md +192 -192
- package/.claude/agents/hive-mind/worker-specialist.md +216 -216
- package/.claude/agents/neural/safla-neural.md +73 -73
- package/.claude/agents/reasoning/goal-planner.md +72 -72
- package/.claude/agents/swarm/adaptive-coordinator.md +395 -395
- package/.claude/agents/swarm/hierarchical-coordinator.md +326 -326
- package/.claude/agents/swarm/mesh-coordinator.md +391 -391
- package/.claude/agents/templates/migration-plan.md +745 -745
- package/.claude/commands/agents/agent-spawning.md +28 -28
- package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +53 -53
- package/.claude/commands/analysis/bottleneck-detect.md +162 -162
- package/.claude/commands/analysis/performance-bottlenecks.md +58 -58
- package/.claude/commands/analysis/token-efficiency.md +44 -44
- package/.claude/commands/automation/auto-agent.md +122 -122
- package/.claude/commands/automation/self-healing.md +105 -105
- package/.claude/commands/automation/session-memory.md +89 -89
- package/.claude/commands/automation/smart-agents.md +72 -72
- package/.claude/commands/coordination/init.md +44 -44
- package/.claude/commands/coordination/orchestrate.md +43 -43
- package/.claude/commands/coordination/spawn.md +45 -45
- package/.claude/commands/coordination/swarm-init.md +85 -85
- package/.claude/commands/github/github-modes.md +146 -146
- package/.claude/commands/github/github-swarm.md +121 -121
- package/.claude/commands/github/issue-tracker.md +291 -291
- package/.claude/commands/github/pr-manager.md +169 -169
- package/.claude/commands/github/release-manager.md +337 -337
- package/.claude/commands/github/repo-architect.md +366 -366
- package/.claude/commands/github/sync-coordinator.md +300 -300
- package/.claude/commands/memory/neural.md +47 -47
- package/.claude/commands/monitoring/agents.md +44 -44
- package/.claude/commands/monitoring/status.md +46 -46
- package/.claude/commands/optimization/auto-topology.md +61 -61
- package/.claude/commands/optimization/parallel-execution.md +49 -49
- package/.claude/commands/sparc/analyzer.md +51 -51
- package/.claude/commands/sparc/architect.md +53 -53
- package/.claude/commands/sparc/ask.md +97 -97
- package/.claude/commands/sparc/batch-executor.md +54 -54
- package/.claude/commands/sparc/code.md +89 -89
- package/.claude/commands/sparc/coder.md +54 -54
- package/.claude/commands/sparc/debug.md +83 -83
- package/.claude/commands/sparc/debugger.md +54 -54
- package/.claude/commands/sparc/designer.md +53 -53
- package/.claude/commands/sparc/devops.md +109 -109
- package/.claude/commands/sparc/docs-writer.md +80 -80
- package/.claude/commands/sparc/documenter.md +54 -54
- package/.claude/commands/sparc/innovator.md +54 -54
- package/.claude/commands/sparc/integration.md +83 -83
- package/.claude/commands/sparc/mcp.md +117 -117
- package/.claude/commands/sparc/memory-manager.md +54 -54
- package/.claude/commands/sparc/optimizer.md +54 -54
- package/.claude/commands/sparc/orchestrator.md +131 -131
- package/.claude/commands/sparc/post-deployment-monitoring-mode.md +83 -83
- package/.claude/commands/sparc/refinement-optimization-mode.md +83 -83
- package/.claude/commands/sparc/researcher.md +54 -54
- package/.claude/commands/sparc/reviewer.md +54 -54
- package/.claude/commands/sparc/security-review.md +80 -80
- package/.claude/commands/sparc/sparc-modes.md +174 -174
- package/.claude/commands/sparc/sparc.md +111 -111
- package/.claude/commands/sparc/spec-pseudocode.md +80 -80
- package/.claude/commands/sparc/supabase-admin.md +348 -348
- package/.claude/commands/sparc/swarm-coordinator.md +54 -54
- package/.claude/commands/sparc/tdd.md +54 -54
- package/.claude/commands/sparc/tester.md +54 -54
- package/.claude/commands/sparc/tutorial.md +79 -79
- package/.claude/commands/sparc/workflow-manager.md +54 -54
- package/.claude/commands/sparc.md +166 -166
- package/.claude/commands/swarm/analysis.md +95 -95
- package/.claude/commands/swarm/development.md +96 -96
- package/.claude/commands/swarm/examples.md +168 -168
- package/.claude/commands/swarm/maintenance.md +102 -102
- package/.claude/commands/swarm/optimization.md +117 -117
- package/.claude/commands/swarm/research.md +136 -136
- package/.claude/commands/swarm/testing.md +131 -131
- package/.claude/commands/training/neural-patterns.md +73 -73
- package/.claude/commands/training/specialization.md +62 -62
- package/.claude/commands/workflows/development.md +77 -77
- package/.claude/commands/workflows/research.md +62 -62
- package/.claude/guidance/{agent-bootstrap.md → shipped/agent-bootstrap.md} +126 -126
- package/.claude/guidance/{guidance-memory-strategy.md → shipped/guidance-memory-strategy.md} +262 -262
- package/.claude/guidance/{memory-strategy.md → shipped/memory-strategy.md} +204 -204
- package/.claude/guidance/{moflo.md → shipped/moflo.md} +45 -31
- package/.claude/guidance/{task-swarm-integration.md → shipped/task-swarm-integration.md} +441 -348
- package/.claude/helpers/gate.cjs +236 -236
- package/.claude/helpers/hook-handler.cjs +42 -46
- package/.claude/settings.json +2 -2
- package/.claude/settings.local.json +3 -3
- package/.claude/skills/fl/SKILL.md +29 -23
- package/.claude/skills/flo/SKILL.md +29 -23
- package/.claude/skills/github-code-review/SKILL.md +4 -4
- package/.claude/skills/github-multi-repo/SKILL.md +8 -8
- package/.claude/skills/github-project-management/SKILL.md +6 -6
- package/.claude/skills/github-release-management/SKILL.md +12 -12
- package/.claude/skills/github-workflow-automation/SKILL.md +6 -6
- package/.claude/skills/hooks-automation/SKILL.md +1201 -1201
- package/.claude/skills/performance-analysis/SKILL.md +563 -563
- package/.claude/skills/sparc-methodology/SKILL.md +64 -64
- package/.claude/skills/swarm-advanced/SKILL.md +77 -77
- package/.claude-plugin/README.md +3 -3
- package/.claude-plugin/docs/PLUGIN_SUMMARY.md +3 -3
- package/.claude-plugin/docs/QUICKSTART.md +4 -4
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +3 -3
- package/.claude-plugin/scripts/install.sh +9 -9
- package/.claude-plugin/scripts/verify.sh +7 -7
- package/README.md +311 -116
- package/bin/gate-hook.mjs +50 -0
- package/bin/gate.cjs +138 -0
- package/bin/hook-handler.cjs +83 -0
- package/bin/hooks.mjs +72 -12
- package/bin/index-guidance.mjs +28 -34
- package/bin/index-tests.mjs +710 -0
- package/bin/lib/process-manager.mjs +243 -0
- package/bin/lib/registry-cleanup.cjs +41 -0
- package/bin/prompt-hook.mjs +72 -0
- package/bin/semantic-search.mjs +473 -441
- package/bin/session-start-launcher.mjs +81 -31
- package/bin/setup-project.mjs +13 -10
- package/package.json +4 -2
- package/src/@claude-flow/cli/README.md +1 -1
- package/src/@claude-flow/cli/bin/cli.js +175 -175
- package/src/@claude-flow/cli/dist/src/commands/doctor.js +1091 -736
- package/src/@claude-flow/cli/dist/src/commands/github.d.ts +12 -0
- package/src/@claude-flow/cli/dist/src/commands/github.js +505 -0
- package/src/@claude-flow/cli/dist/src/commands/hive-mind.js +90 -90
- package/src/@claude-flow/cli/dist/src/commands/index.d.ts +1 -0
- package/src/@claude-flow/cli/dist/src/commands/index.js +7 -0
- package/src/@claude-flow/cli/dist/src/config-adapter.js +1 -1
- package/src/@claude-flow/cli/dist/src/init/claudemd-generator.js +1 -1
- package/src/@claude-flow/cli/dist/src/init/executor.js +109 -5
- package/src/@claude-flow/cli/dist/src/init/helpers-generator.d.ts +14 -0
- package/src/@claude-flow/cli/dist/src/init/helpers-generator.js +156 -24
- package/src/@claude-flow/cli/dist/src/init/mcp-generator.js +20 -20
- package/src/@claude-flow/cli/dist/src/init/moflo-init.d.ts +7 -0
- package/src/@claude-flow/cli/dist/src/init/moflo-init.js +72 -10
- package/src/@claude-flow/cli/dist/src/init/settings-generator.js +23 -14
- package/src/@claude-flow/cli/dist/src/mcp-server.js +3 -3
- package/src/@claude-flow/cli/dist/src/plugins/manager.js +9 -8
- package/src/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +1 -0
- package/src/@claude-flow/cli/dist/src/services/worker-daemon.js +3 -1
- package/src/@claude-flow/cli/dist/src/services/workflow-gate.js +10 -10
- package/src/@claude-flow/cli/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI GitHub Command
|
|
3
|
+
* Repository setup: CI/CD pipeline generation, branch protection, repo settings.
|
|
4
|
+
*
|
|
5
|
+
* Uses `gh` CLI for GitHub API calls and project detection from moflo.yaml.
|
|
6
|
+
*
|
|
7
|
+
* Created with motailz.com
|
|
8
|
+
*/
|
|
9
|
+
import type { Command } from '../types.js';
|
|
10
|
+
export declare const githubCommand: Command;
|
|
11
|
+
export default githubCommand;
|
|
12
|
+
//# sourceMappingURL=github.d.ts.map
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI GitHub Command
|
|
3
|
+
* Repository setup: CI/CD pipeline generation, branch protection, repo settings.
|
|
4
|
+
*
|
|
5
|
+
* Uses `gh` CLI for GitHub API calls and project detection from moflo.yaml.
|
|
6
|
+
*
|
|
7
|
+
* Created with motailz.com
|
|
8
|
+
*/
|
|
9
|
+
import { output } from '../output.js';
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { execSync } from 'child_process';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Helpers
|
|
15
|
+
// ============================================================================
|
|
16
|
+
function runGh(args, cwd, timeout = 15000, stdin) {
|
|
17
|
+
return execSync(`gh ${args}`, {
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
cwd,
|
|
20
|
+
timeout,
|
|
21
|
+
windowsHide: true,
|
|
22
|
+
input: stdin,
|
|
23
|
+
stdio: stdin ? ['pipe', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe'],
|
|
24
|
+
}).trim();
|
|
25
|
+
}
|
|
26
|
+
function ghAvailable() {
|
|
27
|
+
try {
|
|
28
|
+
execSync('gh --version', { encoding: 'utf8', timeout: 5000, windowsHide: true, stdio: 'pipe' });
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function ghAuthenticated() {
|
|
36
|
+
try {
|
|
37
|
+
execSync('gh auth status', { encoding: 'utf8', timeout: 5000, windowsHide: true, stdio: 'pipe' });
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Read moflo.yaml and extract project info for CI generation */
|
|
45
|
+
function readProjectConfig(cwd) {
|
|
46
|
+
const defaults = {
|
|
47
|
+
name: '',
|
|
48
|
+
extensions: ['.ts', '.js'],
|
|
49
|
+
testDirs: ['tests'],
|
|
50
|
+
srcDirs: ['src'],
|
|
51
|
+
hasTypeScript: false,
|
|
52
|
+
hasTests: false,
|
|
53
|
+
packageManager: 'npm',
|
|
54
|
+
};
|
|
55
|
+
// Detect project name from package.json
|
|
56
|
+
const pkgPath = join(cwd, 'package.json');
|
|
57
|
+
if (existsSync(pkgPath)) {
|
|
58
|
+
try {
|
|
59
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
60
|
+
defaults.name = pkg.name || '';
|
|
61
|
+
}
|
|
62
|
+
catch { /* ignore */ }
|
|
63
|
+
}
|
|
64
|
+
// Detect package manager
|
|
65
|
+
if (existsSync(join(cwd, 'bun.lockb')) || existsSync(join(cwd, 'bun.lock')))
|
|
66
|
+
defaults.packageManager = 'bun';
|
|
67
|
+
else if (existsSync(join(cwd, 'pnpm-lock.yaml')))
|
|
68
|
+
defaults.packageManager = 'pnpm';
|
|
69
|
+
else if (existsSync(join(cwd, 'yarn.lock')))
|
|
70
|
+
defaults.packageManager = 'yarn';
|
|
71
|
+
// Detect TypeScript
|
|
72
|
+
defaults.hasTypeScript = existsSync(join(cwd, 'tsconfig.json'));
|
|
73
|
+
// Read moflo.yaml for detected dirs/extensions
|
|
74
|
+
const yamlPath = join(cwd, 'moflo.yaml');
|
|
75
|
+
if (existsSync(yamlPath)) {
|
|
76
|
+
try {
|
|
77
|
+
const content = readFileSync(yamlPath, 'utf8');
|
|
78
|
+
// Parse extensions
|
|
79
|
+
const extMatch = content.match(/extensions:\s*\[([^\]]+)\]/);
|
|
80
|
+
if (extMatch) {
|
|
81
|
+
defaults.extensions = extMatch[1].split(',').map(e => e.trim().replace(/"/g, ''));
|
|
82
|
+
}
|
|
83
|
+
// Parse test directories
|
|
84
|
+
const testsBlock = content.match(/tests:\s*\n\s+directories:\s*\n((?:\s+-\s+.+\n?)+)/);
|
|
85
|
+
if (testsBlock) {
|
|
86
|
+
const items = testsBlock[1].match(/-\s+(.+)/g);
|
|
87
|
+
if (items)
|
|
88
|
+
defaults.testDirs = items.map(i => i.replace(/^-\s+/, '').trim());
|
|
89
|
+
}
|
|
90
|
+
// Parse source directories
|
|
91
|
+
const codeBlock = content.match(/code_map:\s*\n\s+directories:\s*\n((?:\s+-\s+.+\n?)+)/);
|
|
92
|
+
if (codeBlock) {
|
|
93
|
+
const items = codeBlock[1].match(/-\s+(.+)/g);
|
|
94
|
+
if (items)
|
|
95
|
+
defaults.srcDirs = items.map(i => i.replace(/^-\s+/, '').trim());
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch { /* ignore parse errors */ }
|
|
99
|
+
}
|
|
100
|
+
// Check if test dirs actually exist
|
|
101
|
+
defaults.hasTests = defaults.testDirs.some(d => existsSync(join(cwd, d)));
|
|
102
|
+
return defaults;
|
|
103
|
+
}
|
|
104
|
+
/** Get the default branch name for the current repo */
|
|
105
|
+
function getDefaultBranch(cwd) {
|
|
106
|
+
try {
|
|
107
|
+
return runGh('repo view --json defaultBranchRef --jq .defaultBranchRef.name', cwd);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Fallback: check local git
|
|
111
|
+
try {
|
|
112
|
+
return execSync('git symbolic-ref refs/remotes/origin/HEAD', {
|
|
113
|
+
encoding: 'utf8', cwd, timeout: 5000, windowsHide: true, stdio: 'pipe',
|
|
114
|
+
}).trim().replace('refs/remotes/origin/', '');
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return 'main';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Get the repo owner/name from gh */
|
|
122
|
+
function getRepoSlug(cwd) {
|
|
123
|
+
try {
|
|
124
|
+
return runGh('repo view --json nameWithOwner --jq .nameWithOwner', cwd);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// CI Workflow Generation
|
|
132
|
+
// ============================================================================
|
|
133
|
+
function generateCIWorkflow(config, defaultBranch) {
|
|
134
|
+
const pm = config.packageManager;
|
|
135
|
+
const install = pm === 'pnpm' ? 'pnpm install --frozen-lockfile'
|
|
136
|
+
: pm === 'yarn' ? 'yarn install --frozen-lockfile'
|
|
137
|
+
: pm === 'bun' ? 'bun install --frozen-lockfile'
|
|
138
|
+
: 'npm ci';
|
|
139
|
+
const run = pm === 'pnpm' ? 'pnpm' : pm === 'yarn' ? 'yarn' : pm === 'bun' ? 'bun run' : 'npm run';
|
|
140
|
+
const setupPm = pm === 'pnpm' ? `
|
|
141
|
+
- name: Install pnpm
|
|
142
|
+
uses: pnpm/action-setup@v4
|
|
143
|
+
with:
|
|
144
|
+
version: latest
|
|
145
|
+
` : '';
|
|
146
|
+
const nodeCache = pm === 'pnpm' ? 'pnpm' : pm === 'yarn' ? 'yarn' : pm === 'bun' ? '' : 'npm';
|
|
147
|
+
const setupNode = nodeCache ? `
|
|
148
|
+
- name: Setup Node.js
|
|
149
|
+
uses: actions/setup-node@v4
|
|
150
|
+
with:
|
|
151
|
+
node-version: 20
|
|
152
|
+
cache: '${nodeCache}'
|
|
153
|
+
` : `
|
|
154
|
+
- name: Setup Node.js
|
|
155
|
+
uses: actions/setup-node@v4
|
|
156
|
+
with:
|
|
157
|
+
node-version: 20
|
|
158
|
+
`;
|
|
159
|
+
const setupBun = pm === 'bun' ? `
|
|
160
|
+
- name: Setup Bun
|
|
161
|
+
uses: oven-sh/setup-bun@v2
|
|
162
|
+
` : '';
|
|
163
|
+
const lintStep = `
|
|
164
|
+
- name: Lint
|
|
165
|
+
run: ${run} lint --max-warnings 0
|
|
166
|
+
continue-on-error: true`;
|
|
167
|
+
const typeCheckStep = config.hasTypeScript ? `
|
|
168
|
+
|
|
169
|
+
- name: Type check
|
|
170
|
+
run: ${run} typecheck` : '';
|
|
171
|
+
const testStep = config.hasTests ? `
|
|
172
|
+
|
|
173
|
+
- name: Test
|
|
174
|
+
run: ${run} test` : '';
|
|
175
|
+
return `# CI pipeline — generated by moflo (https://github.com/eric-cielo/moflo)
|
|
176
|
+
# Runs on every push to ${defaultBranch} and on all pull requests.
|
|
177
|
+
|
|
178
|
+
name: CI
|
|
179
|
+
|
|
180
|
+
on:
|
|
181
|
+
push:
|
|
182
|
+
branches: [${defaultBranch}]
|
|
183
|
+
pull_request:
|
|
184
|
+
branches: [${defaultBranch}]
|
|
185
|
+
|
|
186
|
+
concurrency:
|
|
187
|
+
group: \${{ github.workflow }}-\${{ github.ref }}
|
|
188
|
+
cancel-in-progress: true
|
|
189
|
+
|
|
190
|
+
permissions:
|
|
191
|
+
contents: read
|
|
192
|
+
|
|
193
|
+
jobs:
|
|
194
|
+
build:
|
|
195
|
+
runs-on: ubuntu-latest
|
|
196
|
+
timeout-minutes: 15
|
|
197
|
+
|
|
198
|
+
steps:
|
|
199
|
+
- name: Checkout
|
|
200
|
+
uses: actions/checkout@v4
|
|
201
|
+
${setupPm}${setupNode}${setupBun}
|
|
202
|
+
- name: Install dependencies
|
|
203
|
+
run: ${install}
|
|
204
|
+
|
|
205
|
+
- name: Build
|
|
206
|
+
run: ${run} build
|
|
207
|
+
${lintStep}${typeCheckStep}${testStep}
|
|
208
|
+
`;
|
|
209
|
+
}
|
|
210
|
+
// ============================================================================
|
|
211
|
+
// Subcommands
|
|
212
|
+
// ============================================================================
|
|
213
|
+
const ciCommand = {
|
|
214
|
+
name: 'ci',
|
|
215
|
+
description: 'Generate a GitHub Actions CI workflow based on your project',
|
|
216
|
+
options: [
|
|
217
|
+
{ name: 'force', short: 'f', type: 'boolean', description: 'Overwrite existing workflow', default: false },
|
|
218
|
+
{ name: 'dry-run', short: 'd', type: 'boolean', description: 'Print workflow to stdout instead of writing', default: false },
|
|
219
|
+
],
|
|
220
|
+
examples: [
|
|
221
|
+
{ command: 'flo github ci', description: 'Generate .github/workflows/ci.yml' },
|
|
222
|
+
{ command: 'flo github ci --dry-run', description: 'Preview without writing' },
|
|
223
|
+
],
|
|
224
|
+
action: async (ctx) => {
|
|
225
|
+
const force = ctx.flags.force;
|
|
226
|
+
const dryRun = (ctx.flags['dry-run'] || ctx.flags.dryRun);
|
|
227
|
+
const cwd = ctx.cwd;
|
|
228
|
+
const config = readProjectConfig(cwd);
|
|
229
|
+
const defaultBranch = getDefaultBranch(cwd);
|
|
230
|
+
const workflow = generateCIWorkflow(config, defaultBranch);
|
|
231
|
+
if (dryRun) {
|
|
232
|
+
output.writeln(workflow);
|
|
233
|
+
return { success: true };
|
|
234
|
+
}
|
|
235
|
+
const workflowPath = join(cwd, '.github', 'workflows', 'ci.yml');
|
|
236
|
+
if (existsSync(workflowPath) && !force) {
|
|
237
|
+
output.printWarning('.github/workflows/ci.yml already exists (use --force to overwrite)');
|
|
238
|
+
return { success: false, exitCode: 1 };
|
|
239
|
+
}
|
|
240
|
+
mkdirSync(join(cwd, '.github', 'workflows'), { recursive: true });
|
|
241
|
+
writeFileSync(workflowPath, workflow, 'utf8');
|
|
242
|
+
output.writeln();
|
|
243
|
+
output.writeln(output.bold('CI Workflow Generated'));
|
|
244
|
+
output.writeln();
|
|
245
|
+
output.writeln(` ${output.success('✓')} .github/workflows/ci.yml`);
|
|
246
|
+
output.writeln();
|
|
247
|
+
output.writeln(output.dim(` Package manager: ${config.packageManager}`));
|
|
248
|
+
output.writeln(output.dim(` TypeScript: ${config.hasTypeScript ? 'yes' : 'no'}`));
|
|
249
|
+
output.writeln(output.dim(` Tests: ${config.hasTests ? config.testDirs.join(', ') : 'none detected'}`));
|
|
250
|
+
output.writeln(output.dim(` Default branch: ${defaultBranch}`));
|
|
251
|
+
output.writeln();
|
|
252
|
+
output.printInfo('Review the workflow, then commit and push to activate it.');
|
|
253
|
+
return { success: true };
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
const settingsCommand = {
|
|
257
|
+
name: 'settings',
|
|
258
|
+
description: 'Apply recommended repo settings and branch protection via gh CLI',
|
|
259
|
+
options: [
|
|
260
|
+
{ name: 'dry-run', short: 'd', type: 'boolean', description: 'Show what would be applied without making changes', default: false },
|
|
261
|
+
{ name: 'branch', short: 'b', type: 'string', description: 'Branch to protect (default: repo default branch)' },
|
|
262
|
+
{ name: 'skip-protection', type: 'boolean', description: 'Skip branch protection rules', default: false },
|
|
263
|
+
{ name: 'skip-repo', type: 'boolean', description: 'Skip repo-level settings', default: false },
|
|
264
|
+
{ name: 'required-reviews', type: 'string', description: 'Required approving reviews (0 to disable)', default: '1' },
|
|
265
|
+
{ name: 'require-ci', type: 'boolean', description: 'Require CI status checks to pass before merge', default: true },
|
|
266
|
+
],
|
|
267
|
+
examples: [
|
|
268
|
+
{ command: 'flo github settings', description: 'Apply all recommended settings' },
|
|
269
|
+
{ command: 'flo github settings --dry-run', description: 'Preview changes' },
|
|
270
|
+
{ command: 'flo github settings --required-reviews 2', description: 'Require 2 approving reviews' },
|
|
271
|
+
{ command: 'flo github settings --skip-protection', description: 'Only apply repo-level settings' },
|
|
272
|
+
],
|
|
273
|
+
action: async (ctx) => {
|
|
274
|
+
const dryRun = (ctx.flags['dry-run'] || ctx.flags.dryRun);
|
|
275
|
+
const skipProtection = (ctx.flags['skip-protection'] || ctx.flags.skipProtection);
|
|
276
|
+
const skipRepo = (ctx.flags['skip-repo'] || ctx.flags.skipRepo);
|
|
277
|
+
const requiredReviews = parseInt((ctx.flags['required-reviews'] || ctx.flags.requiredReviews) || '1', 10);
|
|
278
|
+
const requireCi = (ctx.flags['require-ci'] ?? ctx.flags.requireCi ?? true);
|
|
279
|
+
const cwd = ctx.cwd;
|
|
280
|
+
if (!ghAvailable()) {
|
|
281
|
+
output.printError('GitHub CLI (gh) is required but not installed.');
|
|
282
|
+
output.printInfo('Install: https://cli.github.com');
|
|
283
|
+
return { success: false, exitCode: 1 };
|
|
284
|
+
}
|
|
285
|
+
if (!ghAuthenticated()) {
|
|
286
|
+
output.printError('Not authenticated with GitHub CLI.');
|
|
287
|
+
output.printInfo('Run: gh auth login');
|
|
288
|
+
return { success: false, exitCode: 1 };
|
|
289
|
+
}
|
|
290
|
+
const slug = getRepoSlug(cwd);
|
|
291
|
+
if (!slug) {
|
|
292
|
+
output.printError('Could not determine repository. Are you in a git repo linked to GitHub?');
|
|
293
|
+
return { success: false, exitCode: 1 };
|
|
294
|
+
}
|
|
295
|
+
const branch = ctx.flags.branch || getDefaultBranch(cwd);
|
|
296
|
+
output.writeln();
|
|
297
|
+
output.writeln(output.bold('GitHub Repository Settings'));
|
|
298
|
+
output.writeln(output.dim(` Repository: ${slug}`));
|
|
299
|
+
output.writeln(output.dim(` Branch: ${branch}`));
|
|
300
|
+
if (dryRun)
|
|
301
|
+
output.writeln(output.warning(' DRY RUN — no changes will be made'));
|
|
302
|
+
output.writeln();
|
|
303
|
+
const applied = [];
|
|
304
|
+
const errors = [];
|
|
305
|
+
// ── Repo-level settings ──────────────────────────────────────────
|
|
306
|
+
if (!skipRepo) {
|
|
307
|
+
// Group settings into batches — some fields must be set together
|
|
308
|
+
const settingsBatches = [
|
|
309
|
+
{
|
|
310
|
+
payload: {
|
|
311
|
+
delete_branch_on_merge: true,
|
|
312
|
+
allow_squash_merge: true,
|
|
313
|
+
allow_merge_commit: true,
|
|
314
|
+
allow_rebase_merge: true,
|
|
315
|
+
allow_auto_merge: true,
|
|
316
|
+
allow_update_branch: true,
|
|
317
|
+
// Title + message must be set together or message fails
|
|
318
|
+
squash_merge_commit_title: 'PR_TITLE',
|
|
319
|
+
squash_merge_commit_message: 'PR_BODY',
|
|
320
|
+
},
|
|
321
|
+
labels: [
|
|
322
|
+
'Delete branch on merge',
|
|
323
|
+
'Allow squash merge',
|
|
324
|
+
'Allow merge commits',
|
|
325
|
+
'Allow rebase merge',
|
|
326
|
+
'Squash defaults: PR title + body',
|
|
327
|
+
'Allow auto-merge',
|
|
328
|
+
'Allow update branch button',
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
];
|
|
332
|
+
for (const batch of settingsBatches) {
|
|
333
|
+
if (dryRun) {
|
|
334
|
+
for (const label of batch.labels) {
|
|
335
|
+
output.writeln(` ${output.dim('○')} ${label}`);
|
|
336
|
+
}
|
|
337
|
+
applied.push(...batch.labels);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
runGh(`api repos/${slug} -X PATCH --input -`, cwd, 15000, JSON.stringify(batch.payload));
|
|
342
|
+
for (const label of batch.labels) {
|
|
343
|
+
output.writeln(` ${output.success('✓')} ${label}`);
|
|
344
|
+
}
|
|
345
|
+
applied.push(...batch.labels);
|
|
346
|
+
}
|
|
347
|
+
catch (e) {
|
|
348
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
349
|
+
for (const label of batch.labels) {
|
|
350
|
+
output.writeln(` ${output.warning('⚠')} ${label} — ${msg.split('\n')[0]}`);
|
|
351
|
+
}
|
|
352
|
+
errors.push(...batch.labels);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
output.writeln();
|
|
356
|
+
}
|
|
357
|
+
// ── Branch protection ────────────────────────────────────────────
|
|
358
|
+
if (!skipProtection) {
|
|
359
|
+
output.writeln(output.bold(' Branch Protection'));
|
|
360
|
+
output.writeln();
|
|
361
|
+
// Build branch protection payload
|
|
362
|
+
const protection = {
|
|
363
|
+
enforce_admins: false,
|
|
364
|
+
required_pull_request_reviews: requiredReviews > 0 ? {
|
|
365
|
+
required_approving_review_count: requiredReviews,
|
|
366
|
+
dismiss_stale_reviews: true,
|
|
367
|
+
require_code_owner_reviews: false,
|
|
368
|
+
} : null,
|
|
369
|
+
required_status_checks: requireCi ? {
|
|
370
|
+
strict: true,
|
|
371
|
+
contexts: [], // Empty = any passing check counts
|
|
372
|
+
} : null,
|
|
373
|
+
restrictions: null,
|
|
374
|
+
required_linear_history: true,
|
|
375
|
+
allow_force_pushes: false,
|
|
376
|
+
allow_deletions: false,
|
|
377
|
+
};
|
|
378
|
+
const protectionItems = [
|
|
379
|
+
requiredReviews > 0 ? `Require ${requiredReviews} approving review${requiredReviews > 1 ? 's' : ''}` : null,
|
|
380
|
+
requiredReviews > 0 ? 'Dismiss stale reviews on new commits' : null,
|
|
381
|
+
requireCi ? 'Require status checks to pass' : null,
|
|
382
|
+
requireCi ? 'Require branch to be up to date' : null,
|
|
383
|
+
'Require linear history',
|
|
384
|
+
'Block force pushes',
|
|
385
|
+
'Block branch deletion',
|
|
386
|
+
].filter(Boolean);
|
|
387
|
+
if (dryRun) {
|
|
388
|
+
for (const item of protectionItems) {
|
|
389
|
+
output.writeln(` ${output.dim('○')} ${item}`);
|
|
390
|
+
}
|
|
391
|
+
applied.push(...protectionItems);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
try {
|
|
395
|
+
const payload = JSON.stringify(protection);
|
|
396
|
+
runGh(`api repos/${slug}/branches/${branch}/protection -X PUT --input -`, cwd, 15000, payload);
|
|
397
|
+
for (const item of protectionItems) {
|
|
398
|
+
output.writeln(` ${output.success('✓')} ${item}`);
|
|
399
|
+
}
|
|
400
|
+
applied.push(...protectionItems);
|
|
401
|
+
}
|
|
402
|
+
catch (e) {
|
|
403
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
404
|
+
if (msg.includes('not found') || msg.includes('404')) {
|
|
405
|
+
output.writeln(` ${output.warning('⚠')} Branch protection requires GitHub Pro, Team, or Enterprise`);
|
|
406
|
+
}
|
|
407
|
+
else if (msg.includes('403')) {
|
|
408
|
+
output.writeln(` ${output.warning('⚠')} Insufficient permissions — need admin access to set branch protection`);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
output.writeln(` ${output.error('✗')} Failed to set branch protection: ${msg.split('\n')[0]}`);
|
|
412
|
+
}
|
|
413
|
+
errors.push('Branch protection');
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// ── Summary ──────────────────────────────────────────────────────
|
|
418
|
+
output.writeln();
|
|
419
|
+
output.writeln(output.dim('─'.repeat(50)));
|
|
420
|
+
if (dryRun) {
|
|
421
|
+
output.writeln();
|
|
422
|
+
output.printInfo(`Would apply ${applied.length} settings. Run without --dry-run to apply.`);
|
|
423
|
+
}
|
|
424
|
+
else if (errors.length === 0) {
|
|
425
|
+
output.writeln();
|
|
426
|
+
output.printSuccess(`Applied ${applied.length} settings to ${slug}`);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
output.writeln();
|
|
430
|
+
output.printWarning(`Applied ${applied.length} settings, ${errors.length} had issues`);
|
|
431
|
+
}
|
|
432
|
+
return { success: errors.length === 0 || dryRun };
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
const setupCommand = {
|
|
436
|
+
name: 'setup',
|
|
437
|
+
description: 'One-shot: generate CI workflow + apply repo settings and branch protection',
|
|
438
|
+
options: [
|
|
439
|
+
{ name: 'dry-run', short: 'd', type: 'boolean', description: 'Preview all changes without applying', default: false },
|
|
440
|
+
{ name: 'force', short: 'f', type: 'boolean', description: 'Overwrite existing CI workflow', default: false },
|
|
441
|
+
{ name: 'required-reviews', type: 'string', description: 'Required approving reviews (0 to disable)', default: '1' },
|
|
442
|
+
{ name: 'skip-ci', type: 'boolean', description: 'Skip CI workflow generation', default: false },
|
|
443
|
+
{ name: 'skip-protection', type: 'boolean', description: 'Skip branch protection rules', default: false },
|
|
444
|
+
{ name: 'skip-repo', type: 'boolean', description: 'Skip repo-level settings', default: false },
|
|
445
|
+
],
|
|
446
|
+
examples: [
|
|
447
|
+
{ command: 'flo github setup', description: 'Generate CI + apply all settings' },
|
|
448
|
+
{ command: 'flo github setup --dry-run', description: 'Preview everything' },
|
|
449
|
+
{ command: 'flo github setup --skip-ci', description: 'Only apply repo settings' },
|
|
450
|
+
],
|
|
451
|
+
action: async (ctx) => {
|
|
452
|
+
const dryRun = (ctx.flags['dry-run'] || ctx.flags.dryRun);
|
|
453
|
+
const skipCi = (ctx.flags['skip-ci'] || ctx.flags.skipCi);
|
|
454
|
+
output.writeln();
|
|
455
|
+
output.writeln(output.bold('GitHub Project Setup'));
|
|
456
|
+
output.writeln(output.dim('CI workflow + repo settings + branch protection'));
|
|
457
|
+
output.writeln();
|
|
458
|
+
let ciOk = true;
|
|
459
|
+
let settingsOk = true;
|
|
460
|
+
// Step 1: CI workflow
|
|
461
|
+
if (!skipCi) {
|
|
462
|
+
output.writeln(output.bold('Step 1: CI Workflow'));
|
|
463
|
+
output.writeln();
|
|
464
|
+
const ciResult = await ciCommand.action(ctx);
|
|
465
|
+
ciOk = ciResult?.success ?? false;
|
|
466
|
+
output.writeln();
|
|
467
|
+
}
|
|
468
|
+
// Step 2: Repo settings + branch protection
|
|
469
|
+
output.writeln(output.bold(skipCi ? 'Repository Settings' : 'Step 2: Repository Settings'));
|
|
470
|
+
output.writeln();
|
|
471
|
+
const settingsResult = await settingsCommand.action(ctx);
|
|
472
|
+
settingsOk = settingsResult?.success ?? false;
|
|
473
|
+
return { success: ciOk && settingsOk };
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
// ============================================================================
|
|
477
|
+
// Main command
|
|
478
|
+
// ============================================================================
|
|
479
|
+
export const githubCommand = {
|
|
480
|
+
name: 'github',
|
|
481
|
+
aliases: ['gh'],
|
|
482
|
+
description: 'GitHub repository setup: CI pipeline, branch protection, repo settings',
|
|
483
|
+
subcommands: [setupCommand, ciCommand, settingsCommand],
|
|
484
|
+
options: [],
|
|
485
|
+
examples: [
|
|
486
|
+
{ command: 'flo github setup', description: 'One-shot: CI + settings + branch protection' },
|
|
487
|
+
{ command: 'flo github ci', description: 'Generate CI workflow from project config' },
|
|
488
|
+
{ command: 'flo github settings', description: 'Apply repo settings and branch protection' },
|
|
489
|
+
{ command: 'flo github setup --dry-run', description: 'Preview all changes' },
|
|
490
|
+
],
|
|
491
|
+
action: async () => {
|
|
492
|
+
output.writeln();
|
|
493
|
+
output.writeln(output.bold('flo github — Repository Setup'));
|
|
494
|
+
output.writeln();
|
|
495
|
+
output.writeln('Subcommands:');
|
|
496
|
+
output.writeln(` ${output.highlight('setup')} One-shot: CI workflow + repo settings + branch protection`);
|
|
497
|
+
output.writeln(` ${output.highlight('ci')} Generate .github/workflows/ci.yml from project config`);
|
|
498
|
+
output.writeln(` ${output.highlight('settings')} Apply repo settings and branch protection via gh CLI`);
|
|
499
|
+
output.writeln();
|
|
500
|
+
output.writeln(output.dim('Run flo github <subcommand> --help for details.'));
|
|
501
|
+
return { success: true };
|
|
502
|
+
},
|
|
503
|
+
};
|
|
504
|
+
export default githubCommand;
|
|
505
|
+
//# sourceMappingURL=github.js.map
|