baldart 3.8.2 → 3.9.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/CHANGELOG.md +25 -0
- package/VERSION +1 -1
- package/framework/.claude/skills/new/SKILL.md +3 -3
- package/framework/.claude/skills/worktree-manager/SKILL.md +91 -20
- package/framework/templates/baldart.config.template.yml +20 -0
- package/package.json +1 -1
- package/src/commands/configure.js +76 -1
- package/src/commands/update.js +14 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to BALDART will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.9.0] - 2026-05-22
|
|
9
|
+
|
|
10
|
+
`worktree-manager` (`/mw`) gains a configurable merge strategy. The previous flow forced every consumer through `gh pr create` + `gh pr merge`; in repos where `develop` is not protected on GitHub, that roundtrip is unnecessary and was observed to stall (PR opened, never merged). The new `git.merge_strategy` key lets each project pick its lane.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`baldart.config.yml` → `git.merge_strategy`** — new top-level config section with one key (default `pr`):
|
|
15
|
+
- `pr` (default, backwards-compatible) — push feature branch, open PR via `gh pr create`, merge via `gh pr merge`. Required if `develop` is protected on origin.
|
|
16
|
+
- `local-push` — after the rebase in `/mw` step 4b, fast-forward push the feature branch directly to `origin/develop` (`git push origin <feat>:develop`). No PR, no `gh` dependency. Fails hard with a clear hint if `develop` is protected on origin.
|
|
17
|
+
- **`framework/.claude/skills/worktree-manager/SKILL.md` step 4c** — fully rewritten into two clearly labelled strategies (A: `pr`, B: `local-push`) plus a shared local-develop-sync block. Both strategies remain strictly worktree-isolated — neither runs `git checkout` / `git switch` / `git branch` on the main repo.
|
|
18
|
+
- **Programmatic API output** — `/mw` now returns `strategy: "pr"|"local-push"`. `prNumber` is `null` in `local-push` mode.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **`framework/.claude/skills/new/SKILL.md`** — Phase 6 description and fail-safe rules reference the configured strategy instead of hard-coding `gh pr merge`.
|
|
23
|
+
- **`worktree-manager` Project Context header** — added at the top of the skill, citing `git.merge_strategy` + `paths.backlog_dir` per the always-ask-never-assume contract in `framework/agents/project-context.md`.
|
|
24
|
+
- **`src/commands/configure.js`** — gained autodetection + interactive prompt for `git.merge_strategy`. The probe runs `gh api repos/:o/:r/branches/develop/protection` to recommend `pr` (when develop is protected) or `local-push` (when not). Falls back to `pr` safely when `gh` is missing or the remote is unreachable. The detection reason is surfaced inline so the user knows *why* a strategy is recommended.
|
|
25
|
+
- **`src/commands/update.js`** — schema-drift detection now also covers new top-level sections (currently `git.*`). When new keys are detected, `update` proactively offers to run `configure` instead of just warning. This means an existing consumer running `npx baldart update` will be **asked** about the new `git.merge_strategy` key, not silently defaulted.
|
|
26
|
+
|
|
27
|
+
### Notes
|
|
28
|
+
|
|
29
|
+
- No migration is required for existing consumers. `git.merge_strategy` defaults to `pr` when the key is absent, preserving the v3.4.0+ behaviour exactly.
|
|
30
|
+
- On `npx baldart update`, existing consumers are **prompted** to run `configure`, where the merge-strategy autodetection probes their repo and recommends `pr` or `local-push` with a one-line reason. The user picks; nothing is auto-applied.
|
|
31
|
+
- `develop` remains the only supported integration branch; configurable `base_branch` is out of scope for this release.
|
|
32
|
+
|
|
8
33
|
## [3.8.2] - 2026-05-22
|
|
9
34
|
|
|
10
35
|
`baldart` (smart doctor) no longer double-confirms safe actions. Previously, running `baldart` with a remote-ahead state would ask *"Run: Pull N commit(s)?"* and then the `update` command would ask *"Proceed with update?"* immediately after — two prompts for the same intent.
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.9.0
|
|
@@ -1176,7 +1176,7 @@ After the final review passes AND all cards are committed in the worktree, deleg
|
|
|
1176
1176
|
- The worktree path and branch from the tracker
|
|
1177
1177
|
- `checksAlreadyPassed: true` (final review + QA already validated the build)
|
|
1178
1178
|
- All card IDs for the commit message
|
|
1179
|
-
3. The skill handles: safety commit of any remaining uncommitted files (step 3), rebasing onto latest develop (step 4b — auto-resolves doc conflicts, stops on code conflicts),
|
|
1179
|
+
3. The skill handles: safety commit of any remaining uncommitted files (step 3), rebasing onto latest develop (step 4b — auto-resolves doc conflicts, stops on code conflicts), merging into develop via the configured `git.merge_strategy` (step 4c — `pr` uses `gh pr merge`, `local-push` does a direct FF push to `origin/develop`; NEITHER runs `git checkout develop` on the main repo, respecting the absolute terminal-isolation rule), post-merge verification, worktree removal, registry cleanup, and remote branch deletion.
|
|
1180
1180
|
4. **If code merge conflicts** → the skill STOPs and reports. Doc-only conflicts (ssot-registry.md, project-status.md, etc.) are auto-resolved by keeping both sides.
|
|
1181
1181
|
5. **If post-merge build fails** → the skill STOPs and keeps the worktree intact for investigation.
|
|
1182
1182
|
6. Record the merge commit hash and result in the tracker.
|
|
@@ -1245,8 +1245,8 @@ The most common failure mode is leaving cards IN_PROGRESS after merge. This crea
|
|
|
1245
1245
|
**Why this exists**: Agents frequently skip the DONE marking in Phase 4 (step 27) due to context compaction, commit failures that interrupt the flow, or team mode where Step D.6 gets lost. This gate is the safety net that catches ALL of these cases.
|
|
1246
1246
|
|
|
1247
1247
|
### Fail-safe rules (enforced by worktree-manager skill)
|
|
1248
|
-
- **Never `git checkout`, `git switch`, `git checkout -b`, or `git branch` on the main repo** from inside this orchestrator. The main repo is shared across parallel terminals. Use
|
|
1249
|
-
- Never merge to `main` — only to `develop`,
|
|
1248
|
+
- **Never `git checkout`, `git switch`, `git checkout -b`, or `git branch` on the main repo** from inside this orchestrator. The main repo is shared across parallel terminals. Use the configured `git.merge_strategy` for develop merges and `git -C <main> pull --ff-only` (only when `HEAD = develop` already) for sync. See `/mw` step 4c.
|
|
1249
|
+
- Never merge to `main` — only to `develop`, via the configured `git.merge_strategy` (NOT local checkout).
|
|
1250
1250
|
- Never force push to `main` or `develop`. `--force-with-lease` on feature branches after rebase is allowed.
|
|
1251
1251
|
- Never delete a branch before successful merge verification.
|
|
1252
1252
|
- Never remove a worktree before confirming develop is stable post-merge.
|
|
@@ -14,6 +14,11 @@ description: >
|
|
|
14
14
|
|
|
15
15
|
# Worktree Manager
|
|
16
16
|
|
|
17
|
+
**Project Context (resolved from `baldart.config.yml`)**:
|
|
18
|
+
- `git.merge_strategy` — `pr` (default, GitHub PR via `gh`) or `local-push` (direct fast-forward to `origin/develop`). Drives `/mw` step 4c.
|
|
19
|
+
- `paths.backlog_dir` — used for syncing untracked backlog cards (`/nw` step 3b).
|
|
20
|
+
- Protocol reference: `framework/agents/project-context.md`. Skills must ASK when a needed key is missing — never assume.
|
|
21
|
+
|
|
17
22
|
**IMMEDIATE EXECUTION**: When invoked via `/nw`, `/mw`, `/lw`, or `/cw`, do NOT explain the process. Start executing the matching command flow immediately:
|
|
18
23
|
|
|
19
24
|
- `/nw` → Ask for **slug** (required) and optionally a **card ID**, then execute all steps (pre-flight → create → install → build → report). If the user provided args, skip questions and use them directly. Supports three forms:
|
|
@@ -38,9 +43,13 @@ Output: `{ path: string, branch: string, port: number }`
|
|
|
38
43
|
### /mw programmatic
|
|
39
44
|
|
|
40
45
|
Input: `{ worktreePath: string, checksAlreadyPassed?: boolean }`
|
|
41
|
-
Output: `{ merged: boolean, developVerified: boolean, prNumber: number, mergeCommit: string }`
|
|
46
|
+
Output: `{ merged: boolean, developVerified: boolean, prNumber: number|null, mergeCommit: string, strategy: "pr"|"local-push" }`
|
|
47
|
+
|
|
48
|
+
The merge path depends on `git.merge_strategy` in `baldart.config.yml`:
|
|
49
|
+
- `pr` (default, since v3.4.0): merge via `gh pr create` + `gh pr merge`. `prNumber` is the PR number, `mergeCommit` is the SHA returned by the GitHub merge API.
|
|
50
|
+
- `local-push` (since v3.9.0): direct fast-forward push of the rebased feature branch to `origin/develop` — no PR, no `gh`. `prNumber` is `null`, `mergeCommit` is the local SHA of the feature branch tip.
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
Neither path runs `git checkout` / `git switch` / `git branch` on the main repo. The orchestrator can branch on `strategy` if it needs PR linking (only meaningful in `pr` mode).
|
|
44
53
|
|
|
45
54
|
### /cw programmatic
|
|
46
55
|
|
|
@@ -435,16 +444,32 @@ npm run build
|
|
|
435
444
|
|
|
436
445
|
If build fails after rebase → STOP and report. The rebase introduced incompatibilities.
|
|
437
446
|
|
|
438
|
-
### 4c.
|
|
447
|
+
### 4c. Land the feature branch onto develop (strategy-branched)
|
|
439
448
|
|
|
440
|
-
**IMPORTANT —
|
|
449
|
+
**IMPORTANT — NO local checkout/switch/branch on the main repo, regardless of strategy.** Consumer projects share the main repo as a parallel resource across multiple terminals; running `git checkout develop` on it preempts whatever the other terminals are doing and breaks worktree-driven workflows. Both strategies below operate from the worktree and the main repo's `HEAD` is never touched.
|
|
441
450
|
|
|
442
|
-
|
|
451
|
+
Read `git.merge_strategy` from `baldart.config.yml` (default: `pr`):
|
|
443
452
|
|
|
444
453
|
```bash
|
|
445
454
|
MAIN=$(git -C "$WORKTREE_PATH" rev-parse --git-common-dir)/..
|
|
446
455
|
MAIN=$(cd "$MAIN" && pwd)
|
|
447
456
|
|
|
457
|
+
# Resolve strategy from baldart.config.yml (default: pr)
|
|
458
|
+
STRATEGY=$(grep -A1 '^git:' "$MAIN/baldart.config.yml" 2>/dev/null \
|
|
459
|
+
| grep 'merge_strategy:' | head -1 \
|
|
460
|
+
| sed -E 's/.*merge_strategy:[[:space:]]*([a-z-]+).*/\1/' || echo "pr")
|
|
461
|
+
STRATEGY=${STRATEGY:-pr}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
If neither `pr` nor `local-push` resolves, ASK the user once which to use and suggest persisting the choice (per the always-ask, never-assume contract in `framework/agents/project-context.md`).
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
#### Strategy A — `pr` (default, GitHub PR via `gh`)
|
|
469
|
+
|
|
470
|
+
Use this when `develop` is protected on origin (required status checks, reviews, merge queue) — a direct push would be rejected.
|
|
471
|
+
|
|
472
|
+
```bash
|
|
448
473
|
# 1. Push the feature branch to origin (force-with-lease in case of rebase in 4b).
|
|
449
474
|
git -C "$WORKTREE_PATH" push --force-with-lease origin "$BRANCH"
|
|
450
475
|
|
|
@@ -464,10 +489,6 @@ fi
|
|
|
464
489
|
echo "PR #$PR_NUMBER ready to merge."
|
|
465
490
|
|
|
466
491
|
# 3. Merge via gh CLI. --delete-branch removes the remote branch on success.
|
|
467
|
-
# The CLI may print a non-fatal warning if the LOCAL branch can't be deleted
|
|
468
|
-
# (e.g. "fatal: 'develop' is already used by worktree at ..." when develop is
|
|
469
|
-
# checked out in another worktree). The REMOTE merge happens regardless — we
|
|
470
|
-
# swallow the local-cleanup warning.
|
|
471
492
|
MERGE_OUTPUT=$(gh pr merge "$PR_NUMBER" --merge --delete-branch 2>&1) || MERGE_RC=$?
|
|
472
493
|
echo "$MERGE_OUTPUT"
|
|
473
494
|
|
|
@@ -488,11 +509,63 @@ if [ "$PR_STATE" != "MERGED" ]; then
|
|
|
488
509
|
exit 1
|
|
489
510
|
fi
|
|
490
511
|
echo "✅ PR #$PR_NUMBER merged to develop."
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
If `gh` is not installed in the project, STOP and ask the user to install GitHub CLI (`brew install gh` or equivalent) and run `gh auth login`. Do NOT fall back to a local checkout — it violates terminal isolation.
|
|
515
|
+
|
|
516
|
+
If both gh CLI and REST API merge fail → STOP and report. Suggest switching to `merge_strategy: local-push` only if `develop` is NOT protected on origin.
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
#### Strategy B — `local-push` (direct FF push, no PR)
|
|
521
|
+
|
|
522
|
+
Use this when `develop` is NOT protected on origin and the user wants to skip the GitHub PR roundtrip. After step 4b the feature branch is rebased onto `origin/develop`, so it can fast-forward `develop` on origin in a single push.
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
# 1. Sanity: confirm the feature branch is strictly ahead of origin/develop
|
|
526
|
+
# (i.e. the rebase in step 4b succeeded and we can FF develop on origin).
|
|
527
|
+
git -C "$WORKTREE_PATH" fetch origin develop --quiet
|
|
528
|
+
AHEAD=$(git -C "$WORKTREE_PATH" rev-list --count "origin/develop..$BRANCH")
|
|
529
|
+
BEHIND=$(git -C "$WORKTREE_PATH" rev-list --count "$BRANCH..origin/develop")
|
|
530
|
+
if [ "$BEHIND" -ne 0 ]; then
|
|
531
|
+
echo "❌ local-push aborted: $BRANCH is $BEHIND commit(s) behind origin/develop." >&2
|
|
532
|
+
echo " Re-run step 4b (rebase onto origin/develop), then retry." >&2
|
|
533
|
+
exit 1
|
|
534
|
+
fi
|
|
535
|
+
if [ "$AHEAD" -eq 0 ]; then
|
|
536
|
+
echo "ℹ️ Nothing to push — $BRANCH is identical to origin/develop. Skipping."
|
|
537
|
+
else
|
|
538
|
+
echo "Fast-forwarding origin/develop by $AHEAD commit(s) via local-push…"
|
|
539
|
+
fi
|
|
540
|
+
|
|
541
|
+
# 2. Direct fast-forward push to develop on origin. No PR.
|
|
542
|
+
# If develop is protected on origin, this fails — surface the hint and stop.
|
|
543
|
+
if ! PUSH_OUTPUT=$(git -C "$WORKTREE_PATH" push origin "$BRANCH:develop" 2>&1); then
|
|
544
|
+
echo "$PUSH_OUTPUT"
|
|
545
|
+
if echo "$PUSH_OUTPUT" | grep -qiE "protected branch|required status|review required"; then
|
|
546
|
+
echo "❌ local-push rejected: 'develop' is protected on origin." >&2
|
|
547
|
+
echo " Switch baldart.config.yml → git.merge_strategy: pr, or remove the protection." >&2
|
|
548
|
+
fi
|
|
549
|
+
exit 1
|
|
550
|
+
fi
|
|
551
|
+
echo "✅ $BRANCH merged into develop on origin (fast-forward, no PR)."
|
|
491
552
|
|
|
492
|
-
#
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
553
|
+
# 3. Delete the remote feature branch (best-effort — never fatal).
|
|
554
|
+
git -C "$WORKTREE_PATH" push origin --delete "$BRANCH" 2>/dev/null || true
|
|
555
|
+
|
|
556
|
+
MERGE_SHA=$(git -C "$WORKTREE_PATH" rev-parse HEAD)
|
|
557
|
+
PR_NUMBER=""
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
#### Common — sync local develop ref (both strategies)
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
# Sync local develop ONLY if the main repo's HEAD is already develop.
|
|
566
|
+
# NEVER `git checkout develop` here. If the user (or another terminal) had
|
|
567
|
+
# develop checked out, ff-only updates it cleanly. Otherwise we leave their
|
|
568
|
+
# HEAD alone and they'll pull next time.
|
|
496
569
|
CURRENT_BRANCH=$(git -C "$MAIN" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
497
570
|
if [ "$CURRENT_BRANCH" = "develop" ]; then
|
|
498
571
|
git -C "$MAIN" pull --ff-only origin develop || \
|
|
@@ -503,10 +576,6 @@ else
|
|
|
503
576
|
fi
|
|
504
577
|
```
|
|
505
578
|
|
|
506
|
-
If `gh` is not installed in the project, STOP and ask the user to install GitHub CLI (`brew install gh` or equivalent) and run `gh auth login`. Do NOT fall back to the legacy local-checkout flow — it violates terminal isolation in worktree-driven projects.
|
|
507
|
-
|
|
508
|
-
If both gh CLI and REST API merge fail → STOP and report. Do NOT attempt a local checkout as fallback.
|
|
509
|
-
|
|
510
579
|
### 5. Post-merge verification
|
|
511
580
|
|
|
512
581
|
```bash
|
|
@@ -554,7 +623,9 @@ Remove the entry from `.worktrees/registry.json`.
|
|
|
554
623
|
Merge complete:
|
|
555
624
|
Card: <CARD-ID>
|
|
556
625
|
Branch: <BRANCH> (deleted on remote; local-cleanup may have skipped if checked out elsewhere)
|
|
557
|
-
|
|
626
|
+
Strategy: <pr | local-push>
|
|
627
|
+
PR: #<PR_NUMBER> merged via `gh pr merge` ← only in `pr` strategy
|
|
628
|
+
Push: fast-forward to origin/develop ← only in `local-push` strategy
|
|
558
629
|
Worktree: <WORKTREE_PATH> (removed)
|
|
559
630
|
develop: remote up to date; local main-repo synced only if HEAD was already develop
|
|
560
631
|
```
|
|
@@ -665,8 +736,8 @@ Cleanup complete:
|
|
|
665
736
|
|
|
666
737
|
## Safety Rules
|
|
667
738
|
|
|
668
|
-
- **NEVER `git checkout`, `git switch`, `git checkout -b`, or `git branch` on the main repo from inside `/mw` / `/nw` / `/cw`.** The main repo is a shared resource across parallel terminals; touching its `HEAD` preempts other agents and breaks worktree-driven workflows. Many consumer projects
|
|
669
|
-
- NEVER merge to `main` — only to `develop`,
|
|
739
|
+
- **NEVER `git checkout`, `git switch`, `git checkout -b`, or `git branch` on the main repo from inside `/mw` / `/nw` / `/cw`.** The main repo is a shared resource across parallel terminals; touching its `HEAD` preempts other agents and breaks worktree-driven workflows. Many consumer projects declare this as an absolute rule in CLAUDE.md and Claude Code's classifier will pattern-match and block these commands preemptively. Use the configured `git.merge_strategy` for develop merges and `git -C <main> pull --ff-only` (only when `HEAD = develop` already) for sync. See `/mw` step 4c.
|
|
740
|
+
- NEVER merge to `main` — only to `develop`, via the configured `git.merge_strategy` (`pr` → `gh pr merge`; `local-push` → `git push origin <feat>:develop`). NEVER via local checkout.
|
|
670
741
|
- NEVER force push from a worktree (`--force-with-lease` on the feature branch after rebase is the only allowed force variant).
|
|
671
742
|
- NEVER `git add .` or `git add -A` — always explicit file names.
|
|
672
743
|
- NEVER remove a worktree with uncommitted changes without explicit user confirmation.
|
|
@@ -112,6 +112,26 @@ features:
|
|
|
112
112
|
# LLM-wiki overlay (paths.wiki_dir + capture/wiki-curator loop).
|
|
113
113
|
has_wiki_overlay: false
|
|
114
114
|
|
|
115
|
+
# ─── GIT ─────────────────────────────────────────────────────────────────
|
|
116
|
+
# Controls how worktree-manager (`/mw`) integrates a worktree's feature
|
|
117
|
+
# branch back into `develop`.
|
|
118
|
+
git:
|
|
119
|
+
# How `/mw` lands the feature branch onto develop:
|
|
120
|
+
#
|
|
121
|
+
# pr (default) — push feature branch, open a PR via `gh pr create`,
|
|
122
|
+
# merge via `gh pr merge`. Required if `develop` is protected
|
|
123
|
+
# on origin (status checks, required reviews, merge queue).
|
|
124
|
+
#
|
|
125
|
+
# local-push — after rebasing the feature branch onto `origin/develop`,
|
|
126
|
+
# fast-forward push the feature branch directly to
|
|
127
|
+
# `origin/develop` (`git push origin <feat>:develop`).
|
|
128
|
+
# No PR, no `gh` dependency. FAILS hard if `develop` is
|
|
129
|
+
# protected on origin — switch back to `pr` in that case.
|
|
130
|
+
#
|
|
131
|
+
# Neither strategy ever touches the main repo's HEAD — both flows are
|
|
132
|
+
# worktree-isolated and respect the terminal-isolation rule.
|
|
133
|
+
merge_strategy: pr
|
|
134
|
+
|
|
115
135
|
# ─── TOOLS ───────────────────────────────────────────────────────────────
|
|
116
136
|
# Which AI CLI tools should the framework target on this machine?
|
|
117
137
|
# Each enabled tool gets its own per-item skill symlinks pointing at the
|
package/package.json
CHANGED
|
@@ -26,6 +26,48 @@ const IGNORED_DIRS = new Set([
|
|
|
26
26
|
'.expo', '.parcel-cache', 'tmp', '.idea', '.vscode', '.DS_Store'
|
|
27
27
|
]);
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Probe origin/develop branch protection to recommend a merge strategy.
|
|
31
|
+
* Returns: { value, protected, reason }
|
|
32
|
+
* - 'pr' when develop is protected (PR is the only viable path)
|
|
33
|
+
* - 'local-push' when develop is NOT protected (FF push is faster, no gh needed)
|
|
34
|
+
* - 'pr' as a safe default if we can't tell (gh missing, network down, no remote)
|
|
35
|
+
*
|
|
36
|
+
* Non-fatal: any failure returns `{ value: 'pr', protected: null }` so configure
|
|
37
|
+
* keeps moving. The user can override in the interactive prompt.
|
|
38
|
+
*/
|
|
39
|
+
function detectMergeStrategy(cwd = process.cwd()) {
|
|
40
|
+
try {
|
|
41
|
+
const { execSync } = require('child_process');
|
|
42
|
+
const opts = { cwd, stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000, encoding: 'utf8' };
|
|
43
|
+
// Need both git + gh. If either is absent or unauthenticated, default 'pr'.
|
|
44
|
+
try { execSync('gh --version', opts); } catch (_) {
|
|
45
|
+
return { value: 'pr', protected: null, reason: 'gh CLI not installed — defaulting to pr.' };
|
|
46
|
+
}
|
|
47
|
+
let nameWithOwner = '';
|
|
48
|
+
try {
|
|
49
|
+
nameWithOwner = execSync('gh repo view --json nameWithOwner -q .nameWithOwner', opts).trim();
|
|
50
|
+
} catch (_) {
|
|
51
|
+
return { value: 'pr', protected: null, reason: 'no GitHub remote detected — defaulting to pr.' };
|
|
52
|
+
}
|
|
53
|
+
// Probe branch protection. 404 means "not protected" → local-push viable.
|
|
54
|
+
try {
|
|
55
|
+
execSync(`gh api repos/${nameWithOwner}/branches/develop/protection`, opts);
|
|
56
|
+
return { value: 'pr', protected: true, reason: 'develop is protected on origin — PR strategy required.' };
|
|
57
|
+
} catch (_) {
|
|
58
|
+
// Could be 404 (not protected) OR branch doesn't exist. Check existence:
|
|
59
|
+
try {
|
|
60
|
+
execSync(`gh api repos/${nameWithOwner}/branches/develop -q .name`, opts);
|
|
61
|
+
return { value: 'local-push', protected: false, reason: 'develop exists on origin and is not protected — local-push viable.' };
|
|
62
|
+
} catch (_) {
|
|
63
|
+
return { value: 'pr', protected: null, reason: 'develop branch not found on origin — defaulting to pr.' };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} catch (_) {
|
|
67
|
+
return { value: 'pr', protected: null, reason: 'probe failed — defaulting to pr.' };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
29
71
|
function detect(cwd = process.cwd()) {
|
|
30
72
|
const exists = (p) => fs.existsSync(path.join(cwd, p));
|
|
31
73
|
const findFirst = (...candidates) => candidates.find(exists) || '';
|
|
@@ -238,6 +280,8 @@ function detect(cwd = process.cwd()) {
|
|
|
238
280
|
: (exists('cypress.config.ts') || exists('cypress.config.js') || hasDep('cypress')) ? 'cypress'
|
|
239
281
|
: '';
|
|
240
282
|
|
|
283
|
+
const mergeStrategyProbe = detectMergeStrategy(cwd);
|
|
284
|
+
|
|
241
285
|
const detected = {
|
|
242
286
|
paths: {
|
|
243
287
|
design_system: designSystemPath,
|
|
@@ -302,6 +346,16 @@ function detect(cwd = process.cwd()) {
|
|
|
302
346
|
tools: {
|
|
303
347
|
enabled: toolAdapters.defaultEnabled(cwd)
|
|
304
348
|
},
|
|
349
|
+
git: {
|
|
350
|
+
// Set to a plain string — the structured probe details (`protected`,
|
|
351
|
+
// `reason`) are kept on a sibling key so mergePreserving stays type-safe.
|
|
352
|
+
merge_strategy: mergeStrategyProbe.value,
|
|
353
|
+
},
|
|
354
|
+
_probes: {
|
|
355
|
+
// Internal scratchpad — never serialized to YAML. Used by
|
|
356
|
+
// interactivePrompts to surface the autodetection reasoning.
|
|
357
|
+
merge_strategy: mergeStrategyProbe,
|
|
358
|
+
},
|
|
305
359
|
};
|
|
306
360
|
|
|
307
361
|
// Reference walkFirst once so the dead-code linter is happy and the helper
|
|
@@ -424,6 +478,25 @@ async function interactivePrompts(merged, detected) {
|
|
|
424
478
|
}
|
|
425
479
|
merged.tools.enabled = currentEnabled;
|
|
426
480
|
|
|
481
|
+
// ---- Git merge strategy (since v3.9.0) --------------------------------
|
|
482
|
+
UI.section('Git merge strategy (how `/mw` lands feature branches onto develop)');
|
|
483
|
+
merged.git = merged.git || {};
|
|
484
|
+
const probe = (detected._probes && detected._probes.merge_strategy)
|
|
485
|
+
|| { value: 'pr', protected: null };
|
|
486
|
+
if (probe.reason) UI.info(probe.reason);
|
|
487
|
+
const currentMerge = merged.git.merge_strategy || probe.value || 'pr';
|
|
488
|
+
const chosenMerge = await UI.select(
|
|
489
|
+
`Choose merge strategy (current: ${currentMerge})`,
|
|
490
|
+
[
|
|
491
|
+
{ name: 'pr — open a GitHub PR, merge via `gh pr merge` (required if develop is protected)', value: 'pr' },
|
|
492
|
+
{ name: 'local-push — direct FF push to origin/develop (no PR, no `gh` dependency)', value: 'local-push' },
|
|
493
|
+
]
|
|
494
|
+
);
|
|
495
|
+
merged.git.merge_strategy = chosenMerge;
|
|
496
|
+
if (chosenMerge === 'local-push' && probe.protected === true) {
|
|
497
|
+
UI.warning('You picked `local-push` but develop appears protected on origin — pushes will be rejected. Switch back to `pr` if it fails.');
|
|
498
|
+
}
|
|
499
|
+
|
|
427
500
|
UI.section('Features (explicit yes/no — option A: always ask)');
|
|
428
501
|
for (const flag of [
|
|
429
502
|
['has_design_system', 'Project has a documented design system?'],
|
|
@@ -535,7 +608,9 @@ function serialize(config) {
|
|
|
535
608
|
# NOTE: features.* flags MUST be explicit (true | false). Any flag absent
|
|
536
609
|
# from this file will prompt the user on first skill invocation.
|
|
537
610
|
`;
|
|
538
|
-
|
|
611
|
+
const clean = stripNullFeatures(config);
|
|
612
|
+
if (clean && clean._probes) delete clean._probes;
|
|
613
|
+
return header + yaml.dump(clean, { lineWidth: 100, noRefs: true });
|
|
539
614
|
}
|
|
540
615
|
|
|
541
616
|
async function configure(opts = {}) {
|
package/src/commands/update.js
CHANGED
|
@@ -413,12 +413,21 @@ async function update(options = {}) {
|
|
|
413
413
|
.filter((k) => !(k in (cur2.features || {})));
|
|
414
414
|
const missingPaths = Object.keys(tpl.paths || {})
|
|
415
415
|
.filter((k) => !(k in (cur2.paths || {})));
|
|
416
|
-
|
|
416
|
+
const missingGit = Object.keys(tpl.git || {})
|
|
417
|
+
.filter((k) => !(k in (cur2.git || {})));
|
|
418
|
+
const allMissing = [...missingPaths, ...missingFeatures, ...missingGit.map((k) => `git.${k}`)];
|
|
419
|
+
if (allMissing.length) {
|
|
417
420
|
UI.newline();
|
|
418
|
-
UI.warning(
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
);
|
|
421
|
+
UI.warning(`New config keys in this version: ${allMissing.join(', ')}.`);
|
|
422
|
+
// Auto-offer configure so the user actually answers, instead of
|
|
423
|
+
// silently falling back to template defaults on first use.
|
|
424
|
+
const runConfigure = await UI.confirm('Run `baldart configure` now to populate them? (existing values are preserved)', true);
|
|
425
|
+
if (runConfigure) {
|
|
426
|
+
const configureCmd = require('./configure');
|
|
427
|
+
await configureCmd();
|
|
428
|
+
} else {
|
|
429
|
+
UI.info('Skipped. Skills will prompt for the missing keys on first use.');
|
|
430
|
+
}
|
|
422
431
|
}
|
|
423
432
|
} catch (_) {
|
|
424
433
|
// Non-fatal — schema drift detection is best-effort.
|