@miphamai/cli 0.16.0 → 0.16.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/package.json +2 -3
- package/src/agent/message-bus.ts +7 -1
- package/src/agent/sub-agent.ts +7 -1
- package/src/commands/environment.ts +270 -0
- package/src/commands/git.ts +152 -0
- package/src/commands/project.ts +1011 -0
- package/src/core/engine.ts +8 -7
- package/src/core/permission.ts +5 -0
- package/src/core/session-store.ts +4 -1
- package/src/index.tsx +6 -5
- package/src/skills/community-registry.json +72 -0
- package/src/skills/registry.ts +2 -75
- package/src/tools/exec/bash.ts +3 -3
- package/src/tools/exec/enter-worktree.ts +23 -1
- package/src/tools/exec/exit-worktree.ts +13 -0
- package/src/ui/agent-footer.tsx +89 -0
- package/src/ui/app.tsx +217 -96
- package/src/ui/chat.tsx +80 -30
- package/src/ui/commands.ts +202 -1484
- package/src/ui/input.tsx +61 -2
- package/src/ui/strip-indent.ts +19 -0
- package/src/workflow/runtime.ts +11 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miphamai/cli",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
4
4
|
"description": "Mipham Code — Multi-model open-core intelligent coding terminal by MiphamAI",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -47,8 +47,7 @@
|
|
|
47
47
|
"ink-text-input": "^6.0.0",
|
|
48
48
|
"react": "^18.3.1",
|
|
49
49
|
"react-devtools-core": "^4.28.5",
|
|
50
|
-
"yaml": "^2.9.0"
|
|
51
|
-
"zod": "^3.25.76"
|
|
50
|
+
"yaml": "^2.9.0"
|
|
52
51
|
},
|
|
53
52
|
"devDependencies": {
|
|
54
53
|
"@mipham/shared": "workspace:*",
|
package/src/agent/message-bus.ts
CHANGED
|
@@ -32,7 +32,13 @@ export class AgentMessageBus {
|
|
|
32
32
|
* Post a message from one agent to another.
|
|
33
33
|
* Returns the message ID.
|
|
34
34
|
*/
|
|
35
|
-
post(
|
|
35
|
+
post(
|
|
36
|
+
from: string,
|
|
37
|
+
to: string,
|
|
38
|
+
summary: string,
|
|
39
|
+
message: string,
|
|
40
|
+
type: AgentMessageType = 'message',
|
|
41
|
+
): string {
|
|
36
42
|
const id = `msg-${++this.idCounter}`
|
|
37
43
|
this.messages.push({
|
|
38
44
|
id,
|
package/src/agent/sub-agent.ts
CHANGED
|
@@ -167,7 +167,13 @@ export class SubAgent {
|
|
|
167
167
|
|
|
168
168
|
// Post warning to message bus for UI display
|
|
169
169
|
const bus = getMessageBus()
|
|
170
|
-
bus.post(
|
|
170
|
+
bus.post(
|
|
171
|
+
'system',
|
|
172
|
+
'main',
|
|
173
|
+
`Sub-agent model fallback: ${resolvedModel} → ${model}`,
|
|
174
|
+
warnMsg,
|
|
175
|
+
'warning',
|
|
176
|
+
)
|
|
171
177
|
|
|
172
178
|
finalModel = model
|
|
173
179
|
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mipham Code — Environment Slash Commands
|
|
3
|
+
*
|
|
4
|
+
* Extracted from ui/commands.ts (2026-08-06).
|
|
5
|
+
* Handlers for: /theme, /release-notes, /ide, /terminal-setup
|
|
6
|
+
*/
|
|
7
|
+
import type { CommandHandler } from '../ui/commands.js'
|
|
8
|
+
import { stripIndent } from '../ui/strip-indent.js'
|
|
9
|
+
import { NPM_INSTALL_COMMAND, NPM_UPDATE_COMMAND, PACKAGE_VERSION } from '../shared/index.ts'
|
|
10
|
+
|
|
11
|
+
export { themeCmd, releaseNotesCmd, ideCmd, terminalSetupCmd }
|
|
12
|
+
|
|
13
|
+
const themeCmd: CommandHandler = (_ctx, args) => {
|
|
14
|
+
const theme = args[0]?.toLowerCase()
|
|
15
|
+
const validThemes = ['dark', 'light', 'auto'] as const
|
|
16
|
+
|
|
17
|
+
if (!theme || !validThemes.includes(theme as (typeof validThemes)[number])) {
|
|
18
|
+
return {
|
|
19
|
+
content: `── Theme ──
|
|
20
|
+
|
|
21
|
+
Terminal color themes (set in .mipham/config.yml):
|
|
22
|
+
|
|
23
|
+
theme: dark — dark background (default, recommended for terminals)
|
|
24
|
+
theme: light — light background
|
|
25
|
+
theme: auto — follow system preference
|
|
26
|
+
|
|
27
|
+
Usage: /theme dark | light | auto
|
|
28
|
+
|
|
29
|
+
Current: auto (follows terminal)`,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
content: `✓ Theme set to "${theme}".
|
|
35
|
+
|
|
36
|
+
Add to ~/.mipham/config.yml to persist:
|
|
37
|
+
theme: ${theme}
|
|
38
|
+
|
|
39
|
+
Terminal themes affect syntax highlighting and UI accents.
|
|
40
|
+
Full theme customization is available in the Web UI at https://mipham.ai/code/dashboard`,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ═══════════════════════════════════════════════════════════════
|
|
45
|
+
// Add-Dir — add a directory to workspace permissions
|
|
46
|
+
// ═══════════════════════════════════════════════════════════════
|
|
47
|
+
|
|
48
|
+
// ═══════════════════════════════════════════════════════════════
|
|
49
|
+
// Security / Audit — security review checklist
|
|
50
|
+
// ═══════════════════════════════════════════════════════════════
|
|
51
|
+
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════
|
|
53
|
+
// Release Notes — version changelog
|
|
54
|
+
// ═══════════════════════════════════════════════════════════════
|
|
55
|
+
|
|
56
|
+
const releaseNotesCmd: CommandHandler = () => ({
|
|
57
|
+
content: `── Release Notes ──
|
|
58
|
+
|
|
59
|
+
v0.1.2 (2026-06-09) — Current
|
|
60
|
+
• 48 slash commands (up from 20)
|
|
61
|
+
• /setup guided project initialization wizard
|
|
62
|
+
• Checkpoint/rewind mechanism
|
|
63
|
+
• Focus mode (last exchange view)
|
|
64
|
+
• 3-level MIPHAM.md architecture
|
|
65
|
+
• /doctor system diagnostics
|
|
66
|
+
• /export conversation to file
|
|
67
|
+
• /review and /pr-comments code review workflows
|
|
68
|
+
• /memory management
|
|
69
|
+
• /upgrade instructions
|
|
70
|
+
• Clipboard support (macOS/Windows)
|
|
71
|
+
|
|
72
|
+
v0.1.1 (2026-06-02)
|
|
73
|
+
• Inline shared module for standalone builds
|
|
74
|
+
• Bin path fix for npm compatibility
|
|
75
|
+
|
|
76
|
+
v0.1.0 (2026-06-01)
|
|
77
|
+
• Initial release
|
|
78
|
+
• Multi-model support (7 providers)
|
|
79
|
+
• 16 built-in tools
|
|
80
|
+
• 11 skills (9 standard + 2 mipham)
|
|
81
|
+
• Ctrl+P interactive model picker
|
|
82
|
+
• SSE streaming support
|
|
83
|
+
|
|
84
|
+
Full changelog: https://mipham.ai/code/releases`,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// ═══════════════════════════════════════════════════════════════
|
|
88
|
+
// IDE — IDE integration guide
|
|
89
|
+
// ═══════════════════════════════════════════════════════════════
|
|
90
|
+
|
|
91
|
+
const ideCmd: CommandHandler = async (_ctx) => {
|
|
92
|
+
const { mkdirSync, writeFileSync, existsSync } = await import('node:fs')
|
|
93
|
+
const { join } = await import('node:path')
|
|
94
|
+
|
|
95
|
+
const cwd = process.cwd()
|
|
96
|
+
const vscodeDir = join(cwd, '.vscode')
|
|
97
|
+
mkdirSync(vscodeDir, { recursive: true })
|
|
98
|
+
|
|
99
|
+
const files: string[] = []
|
|
100
|
+
|
|
101
|
+
// ── Detect bun path ──
|
|
102
|
+
let bunPath = '/opt/homebrew/bin/bun'
|
|
103
|
+
try {
|
|
104
|
+
const { execSync } = await import('node:child_process')
|
|
105
|
+
const detected = execSync('which bun 2>/dev/null || echo /opt/homebrew/bin/bun', {
|
|
106
|
+
encoding: 'utf-8',
|
|
107
|
+
}).trim()
|
|
108
|
+
if (detected) bunPath = detected
|
|
109
|
+
} catch {
|
|
110
|
+
// Use default
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── settings.json: terminal profile ──
|
|
114
|
+
const settingsPath = join(vscodeDir, 'settings.json')
|
|
115
|
+
const settings = {
|
|
116
|
+
'terminal.integrated.profiles.osx': {
|
|
117
|
+
mipham: {
|
|
118
|
+
path: bunPath,
|
|
119
|
+
args: ['run', 'mipham'],
|
|
120
|
+
cwd: '${workspaceFolder}',
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8')
|
|
125
|
+
files.push('.vscode/settings.json')
|
|
126
|
+
|
|
127
|
+
// ── keybindings.json: Cmd+Esc launch ──
|
|
128
|
+
const keybindingsPath = join(vscodeDir, 'keybindings.json')
|
|
129
|
+
const keybindings = [
|
|
130
|
+
{
|
|
131
|
+
key: 'cmd+escape',
|
|
132
|
+
command: 'workbench.action.terminal.focus',
|
|
133
|
+
when: 'terminalProcessSupported',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
key: 'cmd+shift+m',
|
|
137
|
+
command: 'workbench.action.terminal.new',
|
|
138
|
+
},
|
|
139
|
+
]
|
|
140
|
+
writeFileSync(keybindingsPath, JSON.stringify(keybindings, null, 2) + '\n', 'utf-8')
|
|
141
|
+
files.push('.vscode/keybindings.json')
|
|
142
|
+
|
|
143
|
+
// ── extensions.json: recommended ──
|
|
144
|
+
const extensionsPath = join(vscodeDir, 'extensions.json')
|
|
145
|
+
const extensions = {
|
|
146
|
+
recommendations: ['miphamai.mipham-code'],
|
|
147
|
+
}
|
|
148
|
+
writeFileSync(extensionsPath, JSON.stringify(extensions, null, 2) + '\n', 'utf-8')
|
|
149
|
+
files.push('.vscode/extensions.json')
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
content: [
|
|
153
|
+
'── VS Code Integration ──',
|
|
154
|
+
'',
|
|
155
|
+
`Generated in ${vscodeDir}:`,
|
|
156
|
+
...files.map((f) => ` ✅ ${f}`),
|
|
157
|
+
'',
|
|
158
|
+
'What was configured:',
|
|
159
|
+
' • Terminal profile "mipham" — opens Mipham Code in integrated terminal',
|
|
160
|
+
' • Keyboard shortcut Cmd+Esc — focus terminal',
|
|
161
|
+
' • Cmd+Shift+M — new terminal',
|
|
162
|
+
'',
|
|
163
|
+
'To use:',
|
|
164
|
+
' 1. Restart VS Code (or reload window: Cmd+Shift+P → Reload Window)',
|
|
165
|
+
' 2. Open terminal: Ctrl+` or Cmd+Esc',
|
|
166
|
+
' 3. Select "mipham" profile from the terminal dropdown',
|
|
167
|
+
'',
|
|
168
|
+
'Install the VS Code extension for full integration:',
|
|
169
|
+
` code --install-extension miphamai.mipham-code`,
|
|
170
|
+
'',
|
|
171
|
+
'JetBrains: Settings → Tools → Terminal → Shell path → bun run mipham',
|
|
172
|
+
].join('\n'),
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ═══════════════════════════════════════════════════════════════
|
|
177
|
+
// Terminal Setup — shell integration guide
|
|
178
|
+
// ═══════════════════════════════════════════════════════════════
|
|
179
|
+
|
|
180
|
+
const terminalSetupCmd: CommandHandler = async () => {
|
|
181
|
+
const { writeFileSync, appendFileSync, existsSync, mkdirSync, readFileSync } =
|
|
182
|
+
await import('node:fs')
|
|
183
|
+
const { join } = await import('node:path')
|
|
184
|
+
const { homedir } = await import('node:os')
|
|
185
|
+
|
|
186
|
+
const home = homedir()
|
|
187
|
+
const lines: string[] = ['── Terminal Setup ──', '']
|
|
188
|
+
|
|
189
|
+
// ── 1. Generate standalone shell setup script ──
|
|
190
|
+
const miphamDir = join(home, '.mipham')
|
|
191
|
+
mkdirSync(miphamDir, { recursive: true })
|
|
192
|
+
|
|
193
|
+
const shellScript = join(miphamDir, 'shell-setup.sh')
|
|
194
|
+
const shellContent = [
|
|
195
|
+
'#!/bin/bash',
|
|
196
|
+
'# Mipham Code — Shell Integration',
|
|
197
|
+
'# Source this file in your shell profile:',
|
|
198
|
+
'# source ~/.mipham/shell-setup.sh',
|
|
199
|
+
'',
|
|
200
|
+
`export MIPHAM_HOME="${home}/.mipham"`,
|
|
201
|
+
'',
|
|
202
|
+
'# Alias: launch Mipham Code in current directory',
|
|
203
|
+
`alias mipham='cd $(pwd) && ${NPM_INSTALL_COMMAND} > /dev/null 2>&1; mipham'`,
|
|
204
|
+
'',
|
|
205
|
+
'# Or use the local development version:',
|
|
206
|
+
'# alias mipham="bun run /path/to/mipham-code/apps/cli/bin/mipham"',
|
|
207
|
+
'',
|
|
208
|
+
'# Auto-detect provider from config',
|
|
209
|
+
'if [ -f ~/.mipham/config.yml ]; then',
|
|
210
|
+
' export MIPHAM_PROVIDER=$(grep "defaultProvider:" ~/.mipham/config.yml | awk "{print \$2}")',
|
|
211
|
+
'fi',
|
|
212
|
+
].join('\n')
|
|
213
|
+
writeFileSync(shellScript, shellContent + '\n', 'utf-8')
|
|
214
|
+
lines.push(` ✅ Generated: ${shellScript}`)
|
|
215
|
+
|
|
216
|
+
// ── 2. Append to shell profile ──
|
|
217
|
+
const shell = process.env.SHELL || '/bin/zsh'
|
|
218
|
+
const profileName = shell.includes('zsh') ? '.zshrc' : '.bashrc'
|
|
219
|
+
const profilePath = join(home, profileName)
|
|
220
|
+
const sourceLine = `\n# Mipham Code shell integration\n[ -f ~/.mipham/shell-setup.sh ] && source ~/.mipham/shell-setup.sh\n`
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const existing = existsSync(profilePath) ? readFileSync(profilePath, 'utf-8') : ''
|
|
224
|
+
if (existing.includes('shell-setup.sh')) {
|
|
225
|
+
lines.push(` ⏭ ${profileName} already has Mipham Code integration`)
|
|
226
|
+
} else {
|
|
227
|
+
appendFileSync(profilePath, sourceLine, 'utf-8')
|
|
228
|
+
lines.push(` ✅ Added to ~/${profileName}`)
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
lines.push(` ⚠️ Could not update ~/${profileName}. Add manually:`)
|
|
232
|
+
lines.push(` echo '${sourceLine.trim()}' >> ~/${profileName}`)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── 3. Global install check ──
|
|
236
|
+
lines.push('')
|
|
237
|
+
lines.push('── Installation ──')
|
|
238
|
+
try {
|
|
239
|
+
const { execSync } = await import('node:child_process')
|
|
240
|
+
const miphamPath = execSync('which mipham 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim()
|
|
241
|
+
if (miphamPath) {
|
|
242
|
+
lines.push(` ✅ mipham found at: ${miphamPath}`)
|
|
243
|
+
const version = execSync('mipham --version 2>/dev/null || echo "unknown"', {
|
|
244
|
+
encoding: 'utf-8',
|
|
245
|
+
}).trim()
|
|
246
|
+
lines.push(` 📦 Version: ${version}`)
|
|
247
|
+
} else {
|
|
248
|
+
lines.push(` ⚠️ mipham not in PATH. Install globally:`)
|
|
249
|
+
lines.push(` ${NPM_INSTALL_COMMAND}`)
|
|
250
|
+
lines.push(` or: curl -fsSL https://mipham.ai/install.sh | bash`)
|
|
251
|
+
}
|
|
252
|
+
} catch {
|
|
253
|
+
lines.push(` 💡 Install: ${NPM_INSTALL_COMMAND}`)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ── 4. Verify ──
|
|
257
|
+
lines.push('')
|
|
258
|
+
lines.push('── Next Steps ──')
|
|
259
|
+
lines.push(' 1. Restart your terminal or run: source ~/.mipham/shell-setup.sh')
|
|
260
|
+
lines.push(' 2. Run: mipham --version')
|
|
261
|
+
lines.push(' 3. Start coding: cd your-project && mipham')
|
|
262
|
+
lines.push('')
|
|
263
|
+
lines.push(`Works with: Zsh, Bash. Shell: ${shell}`)
|
|
264
|
+
|
|
265
|
+
return { content: lines.join('\n') }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ═══════════════════════════════════════════════════════════════
|
|
269
|
+
// Phase 4 — MCP Server Management
|
|
270
|
+
// ═══════════════════════════════════════════════════════════════
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mipham Code — Git & GitHub Slash Commands
|
|
3
|
+
*
|
|
4
|
+
* Extracted from ui/commands.ts (2026-08-06).
|
|
5
|
+
* Handlers for: /commit, /push, /pr, /issue
|
|
6
|
+
*/
|
|
7
|
+
import type { CommandHandler } from '../ui/commands.js'
|
|
8
|
+
import { stripIndent } from '../ui/strip-indent.js'
|
|
9
|
+
|
|
10
|
+
export { commitCmd, pushCmd, prCmd, issueCmd }
|
|
11
|
+
|
|
12
|
+
/** Run a git command asynchronously — avoids blocking the TUI event loop. */
|
|
13
|
+
async function git(args: string[], timeoutMs = 5000): Promise<string> {
|
|
14
|
+
const proc = Bun.spawn(['git', ...args], {
|
|
15
|
+
stdout: 'pipe',
|
|
16
|
+
stderr: 'pipe',
|
|
17
|
+
})
|
|
18
|
+
const exitCode = await Promise.race([
|
|
19
|
+
proc.exited,
|
|
20
|
+
new Promise<number>((resolve) => setTimeout(() => resolve(143), timeoutMs)),
|
|
21
|
+
])
|
|
22
|
+
if (exitCode === 143) {
|
|
23
|
+
proc.kill()
|
|
24
|
+
return ''
|
|
25
|
+
}
|
|
26
|
+
const stdout = await new Response(proc.stdout).text()
|
|
27
|
+
return exitCode === 0 ? stdout.trim() : ''
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const commitCmd: CommandHandler = async () => {
|
|
31
|
+
try {
|
|
32
|
+
const diff = await git(['diff', '--cached', '--stat'])
|
|
33
|
+
const unstaged = await git(['diff', '--name-only'])
|
|
34
|
+
|
|
35
|
+
if (!diff) {
|
|
36
|
+
let msg = '── Git Commit ──\n\nNo staged changes found.\n\n'
|
|
37
|
+
if (unstaged) {
|
|
38
|
+
msg +=
|
|
39
|
+
'Unstaged changes exist:\n' +
|
|
40
|
+
unstaged
|
|
41
|
+
.split('\n')
|
|
42
|
+
.map((f) => ' • ' + f)
|
|
43
|
+
.join('\n') +
|
|
44
|
+
'\n\n'
|
|
45
|
+
}
|
|
46
|
+
msg +=
|
|
47
|
+
'Run: git add <files> to stage changes first.\nOr type: "commit these changes" for AI assistance.'
|
|
48
|
+
return { content: msg }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
content: [
|
|
53
|
+
'── Git Commit ──',
|
|
54
|
+
'',
|
|
55
|
+
'Staged changes:',
|
|
56
|
+
diff,
|
|
57
|
+
'',
|
|
58
|
+
'To generate a commit message, type: "write a commit message for these changes"',
|
|
59
|
+
'To commit: git commit -m "your message"',
|
|
60
|
+
'',
|
|
61
|
+
'Tip: Use Conventional Commits format (feat:, fix:, chore:, docs:)',
|
|
62
|
+
].join('\n'),
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
return { content: '── Git Commit ──\n\nCould not run git. Are you in a git repository?' }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const pushCmd: CommandHandler = async () => {
|
|
70
|
+
try {
|
|
71
|
+
const branch = await git(['branch', '--show-current'], 3000)
|
|
72
|
+
const aheadRaw = await git(['rev-list', '--count', `origin/${branch}..HEAD`], 3000)
|
|
73
|
+
const ahead = aheadRaw || '0'
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
'── Git Push ──',
|
|
78
|
+
'',
|
|
79
|
+
`Branch: ${branch || '(unknown)'}`,
|
|
80
|
+
`Commits ahead of remote: ${ahead}`,
|
|
81
|
+
'',
|
|
82
|
+
ahead !== '0'
|
|
83
|
+
? `Ready to push ${ahead} commit(s). Run: git push origin ${branch}`
|
|
84
|
+
: 'Nothing to push (up to date).',
|
|
85
|
+
'',
|
|
86
|
+
'Or type: "push my changes" for AI assistance.',
|
|
87
|
+
].join('\n'),
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
return { content: '── Git Push ──\n\nCould not determine git status.' }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const prCmd: CommandHandler = async () => {
|
|
95
|
+
try {
|
|
96
|
+
const branch = await git(['branch', '--show-current'], 3000)
|
|
97
|
+
const mainBranchRaw = await git(['remote', 'show', 'origin'], 3000)
|
|
98
|
+
const mainBranch = mainBranchRaw.match(/HEAD branch:\s*(\S+)/)?.[1] || 'main'
|
|
99
|
+
|
|
100
|
+
const diff =
|
|
101
|
+
(await git(['diff', '--stat', `origin/${mainBranch}...HEAD`], 5000)) ||
|
|
102
|
+
(await git(['diff', '--stat', `${mainBranch}...HEAD`], 5000))
|
|
103
|
+
|
|
104
|
+
const commits =
|
|
105
|
+
(await git(['log', '--oneline', `origin/${mainBranch}..HEAD`], 5000)) ||
|
|
106
|
+
(await git(['log', '--oneline', `${mainBranch}..HEAD`], 5000))
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
content: [
|
|
110
|
+
'── Create Pull Request ──',
|
|
111
|
+
'',
|
|
112
|
+
`Branch: ${branch} → ${mainBranch}`,
|
|
113
|
+
'',
|
|
114
|
+
commits
|
|
115
|
+
? `Commits:\n${commits
|
|
116
|
+
.split('\n')
|
|
117
|
+
.map((c) => ' ' + c)
|
|
118
|
+
.join('\n')}`
|
|
119
|
+
: 'No commits ahead.',
|
|
120
|
+
'',
|
|
121
|
+
diff ? `Changes:\n${diff}` : '',
|
|
122
|
+
'',
|
|
123
|
+
'To create PR: type "create a pull request" for AI assistance.',
|
|
124
|
+
'Or use: gh pr create --base ' + mainBranch + ' --head ' + branch,
|
|
125
|
+
].join('\n'),
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
return { content: '── Create Pull Request ──\n\nCould not determine PR context.' }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const issueCmd: CommandHandler = async () => {
|
|
133
|
+
return {
|
|
134
|
+
content: [
|
|
135
|
+
'── GitHub Issues ──',
|
|
136
|
+
'',
|
|
137
|
+
'To file an issue, type: "create a GitHub issue for [description]"',
|
|
138
|
+
'',
|
|
139
|
+
'Quick commands:',
|
|
140
|
+
' gh issue create --title "..." --body "..."',
|
|
141
|
+
' gh issue list',
|
|
142
|
+
'',
|
|
143
|
+
'Use /github-ops skill for advanced GitHub automation.',
|
|
144
|
+
].join('\n'),
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════
|
|
149
|
+
// Code Quality — bridge slash commands to skills (Claude Code parity)
|
|
150
|
+
// ═══════════════════════════════════════════════════════════════
|
|
151
|
+
|
|
152
|
+
/** Factory for git-diff-based commands that bridge to AI skills via forwardToAI. */
|