ultimate-pi 0.19.1 → 0.22.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/.agents/skills/harness-decisions/SKILL.md +68 -2
- package/.agents/skills/harness-git-commit/SKILL.md +72 -0
- package/.agents/skills/harness-governor/SKILL.md +2 -2
- package/.agents/skills/harness-ls-lint-setup/SKILL.md +59 -0
- package/.agents/skills/harness-plan/SKILL.md +13 -11
- package/.agents/skills/harness-review/SKILL.md +1 -1
- package/.agents/skills/harness-sentrux-repair/SKILL.md +48 -0
- package/.agents/skills/sentrux/SKILL.md +4 -2
- package/.agents/skills/wiki-save/SKILL.md +1 -1
- package/.pi/PACKAGING.md +6 -0
- package/.pi/SYSTEM.md +21 -3
- package/.pi/agents/harness/ls-lint-steward.md +49 -0
- package/.pi/agents/harness/planning/decompose.md +4 -4
- package/.pi/agents/harness/reviewing/evaluator.md +1 -1
- package/.pi/agents/harness/running/executor.md +43 -2
- package/.pi/agents/harness/sentrux-repair-advisor.md +50 -0
- package/.pi/agents/pi-pi/prompt-expert.md +17 -2
- package/.pi/auto-commit.json +9 -2
- package/.pi/extensions/debate-orchestrator.ts +3 -0
- package/.pi/extensions/harness-anchored-edit.ts +139 -0
- package/.pi/extensions/harness-ask-user.ts +13 -34
- package/.pi/extensions/harness-debate-tools.ts +43 -4
- package/.pi/extensions/harness-live-widget.ts +28 -19
- package/.pi/extensions/harness-run-context.ts +278 -115
- package/.pi/extensions/harness-web-tools.ts +598 -471
- package/.pi/extensions/ls-lint-rules-sync.ts +103 -0
- package/.pi/extensions/observation-bus.ts +4 -0
- package/.pi/extensions/policy-gate.ts +270 -229
- package/.pi/extensions/sentrux-rules-sync.ts +2 -0
- package/.pi/extensions/soundboard.ts +48 -48
- package/.pi/harness/README.md +4 -0
- package/.pi/harness/agents.manifest.json +15 -7
- package/.pi/harness/agents.policy.yaml +47 -81
- package/.pi/harness/docs/adrs/0051-hash-anchored-executor-edits.md +41 -0
- package/.pi/harness/docs/adrs/0052-ls-lint-naming-lifecycle.md +45 -0
- package/.pi/harness/docs/adrs/0052-sentrux-structured-repair.md +38 -0
- package/.pi/harness/docs/adrs/0053-plan-task-clarification-gate.md +39 -0
- package/.pi/harness/docs/adrs/0054-harness-native-ask-user.md +40 -0
- package/.pi/harness/docs/adrs/0055-auto-commit-coauthor-lifecycle.md +40 -0
- package/.pi/harness/docs/adrs/README.md +7 -0
- package/.pi/harness/docs/practice-map.md +21 -5
- package/.pi/harness/evals/smoke/ls-lint-stub.json +10 -0
- package/.pi/harness/evolution/self-healing-rules.json +16 -0
- package/.pi/harness/ls-lint/naming.manifest.json +128 -0
- package/.pi/harness/sentrux/architecture.manifest.json +1 -1
- package/.pi/harness/specs/auto-commit.schema.json +63 -0
- package/.pi/harness/specs/ls-lint-manifest-proposal.schema.json +80 -0
- package/.pi/harness/specs/ls-lint-signal.schema.json +47 -0
- package/.pi/harness/specs/naming-manifest.schema.json +54 -0
- package/.pi/harness/specs/plan-task-clarification.schema.json +88 -0
- package/.pi/harness/specs/sentrux-diagnostics.schema.json +173 -0
- package/.pi/harness/specs/sentrux-repair-plan.schema.json +133 -0
- package/.pi/harness/specs/sentrux-report.schema.json +119 -0
- package/.pi/harness/specs/sentrux-signal.schema.json +34 -1
- package/.pi/lib/agents-policy.d.mts +26 -47
- package/.pi/lib/agents-policy.mjs +84 -29
- package/.pi/lib/agents-policy.ts +1 -0
- package/.pi/lib/agt/build-evaluation-context.ts +136 -64
- package/.pi/lib/ask-user/constants.mjs +3 -0
- package/.pi/lib/ask-user/constants.ts +4 -0
- package/.pi/lib/ask-user/contracts/glimpse-parse.ts +56 -0
- package/.pi/lib/ask-user/contracts/glimpse-payload-build.ts +58 -0
- package/.pi/lib/ask-user/contracts/glimpse-payload.ts +38 -0
- package/.pi/lib/ask-user/core/questionnaire.ts +74 -0
- package/.pi/lib/ask-user/dialog.ts +2 -314
- package/.pi/lib/ask-user/fallback.ts +2 -78
- package/.pi/lib/ask-user/format.ts +85 -0
- package/.pi/lib/ask-user/glimpseui.d.ts +10 -0
- package/.pi/lib/ask-user/index.ts +114 -0
- package/.pi/lib/ask-user/merge-task-clarification.ts +98 -0
- package/.pi/lib/ask-user/policy.mjs +43 -0
- package/.pi/lib/ask-user/policy.ts +104 -0
- package/.pi/lib/ask-user/presenters/glimpse.ts +130 -0
- package/.pi/lib/ask-user/presenters/headless.ts +131 -0
- package/.pi/lib/ask-user/presenters/select.ts +60 -0
- package/.pi/lib/ask-user/presenters/tui.ts +373 -0
- package/.pi/lib/ask-user/presenters/types.ts +13 -0
- package/.pi/lib/ask-user/render.ts +40 -9
- package/.pi/lib/ask-user/schema.ts +66 -13
- package/.pi/lib/ask-user/types.ts +60 -3
- package/.pi/lib/ask-user/validate-core.mjs +193 -7
- package/.pi/lib/ask-user/validate.ts +53 -34
- package/.pi/lib/harness-anchored-edit/.hash_anchors +1721 -0
- package/.pi/lib/harness-anchored-edit/anchor-state.ts +320 -0
- package/.pi/lib/harness-anchored-edit/apply-anchored-edits.ts +161 -0
- package/.pi/lib/harness-anchored-edit/edit-executor.ts +146 -0
- package/.pi/lib/harness-anchored-edit/index.ts +9 -0
- package/.pi/lib/harness-anchored-edit/line-protocol.ts +38 -0
- package/.pi/lib/harness-anchored-edit/package.json +3 -0
- package/.pi/lib/harness-anchored-edit/settings.ts +1 -0
- package/.pi/lib/harness-anchored-edit/task-id.ts +8 -0
- package/.pi/lib/harness-anchored-edit/types.ts +19 -0
- package/.pi/lib/harness-artifact-gate.ts +75 -21
- package/.pi/lib/harness-auto-commit-config.mjs +321 -0
- package/.pi/lib/harness-lens/clients/anchored-edit-autopatch.ts +158 -0
- package/.pi/lib/harness-lens/clients/lsp/client.ts +62 -39
- package/.pi/lib/harness-lens/clients/tool-policy.ts +73 -181
- package/.pi/lib/harness-lens/index.ts +246 -96
- package/.pi/lib/harness-lens/tools/lsp-navigation.ts +10 -8
- package/.pi/lib/harness-repair-brief.ts +84 -25
- package/.pi/lib/harness-run-context.ts +42 -52
- package/.pi/lib/harness-sentrux-parse.mjs +272 -0
- package/.pi/lib/harness-sentrux-root.mjs +78 -0
- package/.pi/lib/harness-slash-completions.ts +116 -0
- package/.pi/lib/harness-spawn-topology.ts +121 -87
- package/.pi/lib/harness-subagent-submit-registry.ts +10 -0
- package/.pi/lib/harness-subagents-bridge.ts +11 -6
- package/.pi/lib/harness-ui-state.ts +95 -48
- package/.pi/lib/plan-approval/dialog.ts +5 -0
- package/.pi/lib/plan-approval/validate.ts +1 -1
- package/.pi/lib/plan-approval-readiness.ts +32 -0
- package/.pi/lib/plan-debate-gate.ts +154 -114
- package/.pi/lib/plan-task-clarification.ts +158 -0
- package/.pi/prompts/harness-auto.md +2 -2
- package/.pi/prompts/harness-ls-lint-steward.md +43 -0
- package/.pi/prompts/harness-plan.md +58 -8
- package/.pi/prompts/harness-review.md +40 -6
- package/.pi/prompts/harness-run.md +33 -11
- package/.pi/prompts/harness-setup.md +72 -3
- package/.pi/prompts/harness-steer.md +3 -2
- package/.pi/prompts/wiki-save.md +5 -4
- package/.pi/scripts/README.md +8 -0
- package/.pi/scripts/generate-agents-policy-yaml.mjs +14 -2
- package/.pi/scripts/harness-anchored-edit-smoke.mjs +45 -0
- package/.pi/scripts/harness-auto-commit-bootstrap.mjs +96 -0
- package/.pi/scripts/harness-cli-verify.sh +47 -0
- package/.pi/scripts/harness-git-churn.mjs +77 -0
- package/.pi/scripts/harness-git-commit.mjs +173 -0
- package/.pi/scripts/harness-ls-lint-bootstrap.mjs +142 -0
- package/.pi/scripts/harness-ls-lint-cli.mjs +184 -0
- package/.pi/scripts/harness-seed-project-contracts.mjs +47 -0
- package/.pi/scripts/harness-sentrux-diagnostics.mjs +230 -0
- package/.pi/scripts/harness-sentrux-report.mjs +256 -0
- package/.pi/scripts/harness-verify.mjs +347 -117
- package/.pi/scripts/ls-lint-rules-sync.mjs +265 -0
- package/.pi/scripts/run-tests.mjs +65 -0
- package/.pi/settings.example.json +1 -0
- package/.sentrux/rules.toml +1 -1
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +31 -0
- package/README.md +13 -4
- package/THIRD_PARTY_NOTICES.md +7 -0
- package/package.json +8 -3
- package/vendor/pi-subagents/src/agents.ts +5 -0
- package/vendor/pi-subagents/src/subagents.ts +22 -3
- package/vendor/pi-vcc/src/hooks/before-compact.ts +86 -60
- package/.pi/scripts/release.sh +0 -338
|
@@ -137,6 +137,87 @@ const REASON_MESSAGES: Record<OwnCutCancelReason, string> = {
|
|
|
137
137
|
too_few_live_messages: "pi-vcc: Too few messages to compact",
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
+
|
|
141
|
+
function collectLiveRolesForDiagnostics(
|
|
142
|
+
branchEntries: any[],
|
|
143
|
+
lastCompIdx: number,
|
|
144
|
+
lastKeptId: string | undefined,
|
|
145
|
+
): string[] {
|
|
146
|
+
const hasPriorCompaction = lastCompIdx >= 0;
|
|
147
|
+
const hasValidKeptId = !!lastKeptId && branchEntries.some((e: any) => e.id === lastKeptId);
|
|
148
|
+
const diagOrphan = hasPriorCompaction && !hasValidKeptId;
|
|
149
|
+
const liveRoles: string[] = [];
|
|
150
|
+
if (diagOrphan) {
|
|
151
|
+
for (let i = lastCompIdx + 1; i < branchEntries.length; i++) {
|
|
152
|
+
const e = branchEntries[i];
|
|
153
|
+
if (e.type === "compaction") continue;
|
|
154
|
+
if (e.type === "message" && e.message) liveRoles.push(e.message.role);
|
|
155
|
+
}
|
|
156
|
+
return liveRoles;
|
|
157
|
+
}
|
|
158
|
+
let foundKept = !lastKeptId;
|
|
159
|
+
for (const e of branchEntries) {
|
|
160
|
+
if (!foundKept && e.id === lastKeptId) foundKept = true;
|
|
161
|
+
if (!foundKept || e.type === "compaction") continue;
|
|
162
|
+
if (e.type === "message" && e.message) liveRoles.push(e.message.role);
|
|
163
|
+
}
|
|
164
|
+
return liveRoles;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function handleOwnCutCancellation(args: {
|
|
168
|
+
ownCut: Extract<OwnCutResult, { ok: false }>;
|
|
169
|
+
isPiVcc: boolean;
|
|
170
|
+
settings: PiVccSettings;
|
|
171
|
+
branchEntries: any[];
|
|
172
|
+
ctx: any;
|
|
173
|
+
}): { cancel: true } {
|
|
174
|
+
const { ownCut, isPiVcc, settings, branchEntries, ctx } = args;
|
|
175
|
+
const lastComp = [...branchEntries].reverse().find((e: any) => e.type === "compaction");
|
|
176
|
+
const lastCompIdx = lastComp ? branchEntries.indexOf(lastComp) : -1;
|
|
177
|
+
const lastKeptId: string | undefined = lastComp?.firstKeptEntryId;
|
|
178
|
+
const liveRoles = collectLiveRolesForDiagnostics(branchEntries, lastCompIdx, lastKeptId);
|
|
179
|
+
const userIndices = liveRoles.reduce<number[]>((acc, r, i) => (r === "user" ? (acc.push(i), acc) : acc), []);
|
|
180
|
+
|
|
181
|
+
dbg(settings, {
|
|
182
|
+
cancelled: true,
|
|
183
|
+
reason: ownCut.reason,
|
|
184
|
+
isPiVcc,
|
|
185
|
+
counts: {
|
|
186
|
+
total: branchEntries.length,
|
|
187
|
+
messages: branchEntries.filter((e: any) => e.type === "message").length,
|
|
188
|
+
compactions: branchEntries.filter((e: any) => e.type === "compaction").length,
|
|
189
|
+
entriesAfterLastCompaction: lastCompIdx >= 0 ? branchEntries.length - lastCompIdx - 1 : null,
|
|
190
|
+
},
|
|
191
|
+
liveMessages: {
|
|
192
|
+
count: liveRoles.length,
|
|
193
|
+
userCount: userIndices.length,
|
|
194
|
+
firstUserIdx: userIndices[0] ?? null,
|
|
195
|
+
lastUserIdx: userIndices[userIndices.length - 1] ?? null,
|
|
196
|
+
roleSequence: liveRoles.length <= 30
|
|
197
|
+
? liveRoles
|
|
198
|
+
: [...liveRoles.slice(0, 10), "...", ...liveRoles.slice(-10)],
|
|
199
|
+
},
|
|
200
|
+
lastCompaction: lastComp
|
|
201
|
+
? {
|
|
202
|
+
hasFirstKeptEntryId: !!lastComp.firstKeptEntryId,
|
|
203
|
+
foundInBranch: lastComp.firstKeptEntryId
|
|
204
|
+
? branchEntries.some((e: any) => e.id === lastComp.firstKeptEntryId)
|
|
205
|
+
: null,
|
|
206
|
+
}
|
|
207
|
+
: null,
|
|
208
|
+
tail: branchEntries.slice(-5).map((e: any) => ({
|
|
209
|
+
type: e.type,
|
|
210
|
+
role: e.type === "message" ? e.message?.role : undefined,
|
|
211
|
+
hasContent: e.type === "message" ? e.message?.content != null : undefined,
|
|
212
|
+
})),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
ctx?.ui?.notify?.(REASON_MESSAGES[ownCut.reason], "warning");
|
|
217
|
+
} catch {}
|
|
218
|
+
return { cancel: true };
|
|
219
|
+
}
|
|
220
|
+
|
|
140
221
|
export const registerBeforeCompactHook = (pi: ExtensionAPI) => {
|
|
141
222
|
pi.on("session_before_compact", (event, ctx) => {
|
|
142
223
|
const { preparation, branchEntries, customInstructions } = event;
|
|
@@ -149,68 +230,13 @@ export const registerBeforeCompactHook = (pi: ExtensionAPI) => {
|
|
|
149
230
|
|
|
150
231
|
const ownCut = buildOwnCut(branchEntries as any[]);
|
|
151
232
|
if (!ownCut.ok) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// Recompute liveMessages view (same logic as buildOwnCut) for diagnostic
|
|
156
|
-
const lastKeptId: string | undefined = lastComp?.firstKeptEntryId;
|
|
157
|
-
const hasPriorCompaction = lastCompIdx >= 0;
|
|
158
|
-
const hasValidKeptId = !!lastKeptId && (branchEntries as any[]).some((e: any) => e.id === lastKeptId);
|
|
159
|
-
const diagOrphan = hasPriorCompaction && !hasValidKeptId;
|
|
160
|
-
const liveRoles: string[] = [];
|
|
161
|
-
if (diagOrphan) {
|
|
162
|
-
for (let i = lastCompIdx + 1; i < branchEntries.length; i++) {
|
|
163
|
-
const e = (branchEntries as any[])[i];
|
|
164
|
-
if (e.type === "compaction") continue;
|
|
165
|
-
if (e.type === "message" && e.message) liveRoles.push(e.message.role);
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
let foundKept = !lastKeptId;
|
|
169
|
-
for (const e of branchEntries as any[]) {
|
|
170
|
-
if (!foundKept && e.id === lastKeptId) foundKept = true;
|
|
171
|
-
if (!foundKept) continue;
|
|
172
|
-
if (e.type === "compaction") continue;
|
|
173
|
-
if (e.type === "message" && e.message) liveRoles.push(e.message.role);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
const userIndices = liveRoles.reduce<number[]>((acc, r, i) => (r === "user" ? (acc.push(i), acc) : acc), []);
|
|
177
|
-
|
|
178
|
-
dbg(settings, {
|
|
179
|
-
cancelled: true,
|
|
180
|
-
reason: ownCut.reason,
|
|
233
|
+
return handleOwnCutCancellation({
|
|
234
|
+
ownCut,
|
|
181
235
|
isPiVcc,
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
compactions: (branchEntries as any[]).filter((e: any) => e.type === "compaction").length,
|
|
186
|
-
entriesAfterLastCompaction: lastCompIdx >= 0 ? branchEntries.length - lastCompIdx - 1 : null,
|
|
187
|
-
},
|
|
188
|
-
liveMessages: {
|
|
189
|
-
count: liveRoles.length,
|
|
190
|
-
userCount: userIndices.length,
|
|
191
|
-
firstUserIdx: userIndices[0] ?? null,
|
|
192
|
-
lastUserIdx: userIndices[userIndices.length - 1] ?? null,
|
|
193
|
-
roleSequence: liveRoles.length <= 30
|
|
194
|
-
? liveRoles
|
|
195
|
-
: [...liveRoles.slice(0, 10), "...", ...liveRoles.slice(-10)],
|
|
196
|
-
},
|
|
197
|
-
lastCompaction: lastComp ? {
|
|
198
|
-
hasFirstKeptEntryId: !!lastComp.firstKeptEntryId,
|
|
199
|
-
foundInBranch: lastComp.firstKeptEntryId
|
|
200
|
-
? (branchEntries as any[]).some((e: any) => e.id === lastComp.firstKeptEntryId)
|
|
201
|
-
: null,
|
|
202
|
-
} : null,
|
|
203
|
-
tail: (branchEntries as any[]).slice(-5).map((e: any) => ({
|
|
204
|
-
type: e.type,
|
|
205
|
-
role: e.type === "message" ? e.message?.role : undefined,
|
|
206
|
-
hasContent: e.type === "message" ? e.message?.content != null : undefined,
|
|
207
|
-
})),
|
|
236
|
+
settings,
|
|
237
|
+
branchEntries: branchEntries as any[],
|
|
238
|
+
ctx,
|
|
208
239
|
});
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
ctx?.ui?.notify?.(REASON_MESSAGES[ownCut.reason], "warning");
|
|
212
|
-
} catch {}
|
|
213
|
-
return { cancel: true };
|
|
214
240
|
}
|
|
215
241
|
|
|
216
242
|
const agentMessages = ownCut.messages;
|
package/.pi/scripts/release.sh
DELETED
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# release.sh — Version bump, changelog, tag, and push
|
|
4
|
-
# Usage: ./.pi/scripts/release.sh [patch|minor|major] [--dry-run]
|
|
5
|
-
#
|
|
6
|
-
set -euo pipefail
|
|
7
|
-
|
|
8
|
-
# ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
9
|
-
warn() { echo "⚠ $*" >&2; }
|
|
10
|
-
abort() { echo "✗ $*" >&2; exit 1; }
|
|
11
|
-
ok() { echo "✓ $*"; }
|
|
12
|
-
|
|
13
|
-
# ─── Step 0 — Parse arguments ─────────────────────────────────────────────────
|
|
14
|
-
BUMP_TYPE=""
|
|
15
|
-
DRY_RUN=false
|
|
16
|
-
|
|
17
|
-
for arg in "$@"; do
|
|
18
|
-
case "$arg" in
|
|
19
|
-
patch|minor|major) BUMP_TYPE="$arg" ;;
|
|
20
|
-
--dry-run) DRY_RUN=true ;;
|
|
21
|
-
*) abort "Unknown argument: $arg" ;;
|
|
22
|
-
esac
|
|
23
|
-
done
|
|
24
|
-
|
|
25
|
-
# ─── Step 1 — Infer bump type from commits if not provided ────────────────────
|
|
26
|
-
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
27
|
-
|
|
28
|
-
if [ -z "$BUMP_TYPE" ]; then
|
|
29
|
-
ok "No bump type provided. Scanning commits since last tag…"
|
|
30
|
-
|
|
31
|
-
if [ -z "$LAST_TAG" ]; then
|
|
32
|
-
COMMIT_LOG=$(git log --format="%s" HEAD 2>/dev/null || true)
|
|
33
|
-
else
|
|
34
|
-
COMMIT_LOG=$(git log --format="%s" "${LAST_TAG}..HEAD" 2>/dev/null || true)
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
if [ -z "$COMMIT_LOG" ]; then
|
|
38
|
-
abort "No commits since last tag. Nothing to release."
|
|
39
|
-
fi
|
|
40
|
-
|
|
41
|
-
# Inference rules
|
|
42
|
-
if echo "$COMMIT_LOG" | grep -qE '^feat!:|BREAKING CHANGE'; then
|
|
43
|
-
BUMP_TYPE="major"
|
|
44
|
-
elif echo "$COMMIT_LOG" | grep -qE '^feat:'; then
|
|
45
|
-
BUMP_TYPE="minor"
|
|
46
|
-
else
|
|
47
|
-
BUMP_TYPE="patch"
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
ok "Inferred bump type: $BUMP_TYPE"
|
|
51
|
-
fi
|
|
52
|
-
|
|
53
|
-
# ─── Step 2 — Read current version and validate semver ────────────────────────
|
|
54
|
-
CURRENT_VERSION=$(node -e "console.log(require('./package.json').version)" 2>/dev/null) \
|
|
55
|
-
|| abort "Failed to read version from package.json"
|
|
56
|
-
|
|
57
|
-
if ! [[ "$CURRENT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
58
|
-
abort "Invalid semver in package.json: $CURRENT_VERSION"
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
NEW_VERSION=$(node -e "
|
|
62
|
-
const [maj, min, pat] = '$CURRENT_VERSION'.split('.').map(Number);
|
|
63
|
-
const bump = '$BUMP_TYPE';
|
|
64
|
-
if (bump === 'major') console.log((maj + 1) + '.0.0');
|
|
65
|
-
else if (bump === 'minor') console.log(maj + '.' + (min + 1) + '.0');
|
|
66
|
-
else console.log(maj + '.' + min + '.' + (pat + 1));
|
|
67
|
-
")
|
|
68
|
-
|
|
69
|
-
ok "Version: $CURRENT_VERSION → $NEW_VERSION"
|
|
70
|
-
|
|
71
|
-
# ─── Step 3 — Pre-flight checks ───────────────────────────────────────────────
|
|
72
|
-
|
|
73
|
-
# Must be in a git repo
|
|
74
|
-
git rev-parse --is-inside-work-tree >/dev/null 2>&1 \
|
|
75
|
-
|| abort "Not a git repository."
|
|
76
|
-
|
|
77
|
-
# Must have origin remote
|
|
78
|
-
git remote -v | grep -q origin \
|
|
79
|
-
|| abort "No 'origin' remote configured."
|
|
80
|
-
|
|
81
|
-
# Must be on a branch (not detached HEAD)
|
|
82
|
-
BRANCH=$(git symbolic-ref -q HEAD 2>/dev/null | sed 's|^refs/heads/||') \
|
|
83
|
-
|| abort "Detached HEAD. Switch to a branch first."
|
|
84
|
-
|
|
85
|
-
# Must have clean working tree (warn only in dry-run)
|
|
86
|
-
git diff --quiet && git diff --cached --quiet
|
|
87
|
-
if [ $? -ne 0 ]; then
|
|
88
|
-
if [ "$DRY_RUN" = true ]; then
|
|
89
|
-
warn "Working tree is dirty — actual release would be blocked."
|
|
90
|
-
else
|
|
91
|
-
abort "Working tree is dirty. Commit or stash changes first."
|
|
92
|
-
fi
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
# No duplicate tag locally or on remote
|
|
96
|
-
if git rev-parse "v$NEW_VERSION" >/dev/null 2>&1; then
|
|
97
|
-
abort "Tag v$NEW_VERSION already exists locally."
|
|
98
|
-
fi
|
|
99
|
-
if git ls-remote --tags origin "refs/tags/v$NEW_VERSION" >/dev/null 2>&1; then
|
|
100
|
-
abort "Tag v$NEW_VERSION already exists on remote."
|
|
101
|
-
fi
|
|
102
|
-
|
|
103
|
-
# ─── Step 4 — Gather commits since last tag ───────────────────────────────────
|
|
104
|
-
if [ -z "$LAST_TAG" ]; then
|
|
105
|
-
COMMITS=$(git log --oneline --no-merges HEAD)
|
|
106
|
-
else
|
|
107
|
-
COMMITS=$(git log --oneline --no-merges "${LAST_TAG}..HEAD")
|
|
108
|
-
fi
|
|
109
|
-
|
|
110
|
-
COMMIT_COUNT=$(echo "$COMMITS" | grep -c '^' || echo 0)
|
|
111
|
-
|
|
112
|
-
if [ "$COMMIT_COUNT" -eq 0 ]; then
|
|
113
|
-
abort "No commits since last tag. Nothing to release."
|
|
114
|
-
fi
|
|
115
|
-
|
|
116
|
-
# ─── Step 5 — Generate changelog entry ────────────────────────────────────────
|
|
117
|
-
|
|
118
|
-
# Map conventional commit prefix → section
|
|
119
|
-
map_prefix() {
|
|
120
|
-
local msg="$1"
|
|
121
|
-
case "$msg" in
|
|
122
|
-
feat!:*|*"BREAKING CHANGE"*) echo "breaking" ;;
|
|
123
|
-
feat:*) echo "features" ;;
|
|
124
|
-
fix:*) echo "fixes" ;;
|
|
125
|
-
perf:*) echo "perf" ;;
|
|
126
|
-
refactor:*) echo "refactor" ;;
|
|
127
|
-
docs:*) echo "docs" ;;
|
|
128
|
-
style:*) echo "style" ;;
|
|
129
|
-
test:*) echo "tests" ;;
|
|
130
|
-
chore:*) echo "chores" ;;
|
|
131
|
-
ci:*) echo "ci" ;;
|
|
132
|
-
build:*) echo "build" ;;
|
|
133
|
-
*) echo "chores" ;;
|
|
134
|
-
esac
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
declare -A SECTIONS=(
|
|
138
|
-
[breaking]="⚠️ Breaking Changes"
|
|
139
|
-
[features]="✨ Features"
|
|
140
|
-
[fixes]="🐛 Fixes"
|
|
141
|
-
[perf]="⚡ Performance"
|
|
142
|
-
[refactor]="♻️ Refactoring"
|
|
143
|
-
[docs]="📖 Documentation"
|
|
144
|
-
[style]="🎨 Style"
|
|
145
|
-
[tests]="✅ Tests"
|
|
146
|
-
[chores]="🔧 Chores"
|
|
147
|
-
[ci]="🔄 CI/CD"
|
|
148
|
-
[build]="📦 Build"
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
# Build per-section entries
|
|
152
|
-
entries_breaking=""
|
|
153
|
-
entries_features=""
|
|
154
|
-
entries_fixes=""
|
|
155
|
-
entries_perf=""
|
|
156
|
-
entries_refactor=""
|
|
157
|
-
entries_docs=""
|
|
158
|
-
entries_style=""
|
|
159
|
-
entries_tests=""
|
|
160
|
-
entries_chores=""
|
|
161
|
-
entries_ci=""
|
|
162
|
-
entries_build=""
|
|
163
|
-
|
|
164
|
-
while IFS= read -r line; do
|
|
165
|
-
[ -z "$line" ] && continue
|
|
166
|
-
# Strip the short sha prefix (first word)
|
|
167
|
-
msg="${line#* }"
|
|
168
|
-
# Strip conventional commit prefix for display
|
|
169
|
-
display="$msg"
|
|
170
|
-
display=$(echo "$display" | sed -E 's/^[a-z]+(\([a-z0-9_-]+\))?!?:\s*//')
|
|
171
|
-
prefix=$(map_prefix "$msg")
|
|
172
|
-
case "$prefix" in
|
|
173
|
-
breaking) entries_breaking="${entries_breaking}- $display
|
|
174
|
-
" ;;
|
|
175
|
-
features) entries_features="${entries_features}- $display
|
|
176
|
-
" ;;
|
|
177
|
-
fixes) entries_fixes="${entries_fixes}- $display
|
|
178
|
-
" ;;
|
|
179
|
-
perf) entries_perf="${entries_perf}- $display
|
|
180
|
-
" ;;
|
|
181
|
-
refactor) entries_refactor="${entries_refactor}- $display
|
|
182
|
-
" ;;
|
|
183
|
-
docs) entries_docs="${entries_docs}- $display
|
|
184
|
-
" ;;
|
|
185
|
-
style) entries_style="${entries_style}- $display
|
|
186
|
-
" ;;
|
|
187
|
-
tests) entries_tests="${entries_tests}- $display
|
|
188
|
-
" ;;
|
|
189
|
-
ci) entries_ci="${entries_ci}- $display
|
|
190
|
-
" ;;
|
|
191
|
-
build) entries_build="${entries_build}- $display
|
|
192
|
-
" ;;
|
|
193
|
-
*) entries_chores="${entries_chores}- $display
|
|
194
|
-
" ;;
|
|
195
|
-
esac
|
|
196
|
-
done <<< "$COMMITS"
|
|
197
|
-
|
|
198
|
-
# Assemble the changelog block
|
|
199
|
-
TODAY=$(date +%Y-%m-%d)
|
|
200
|
-
CHANGELOG_BLOCK="## [v$NEW_VERSION] — $TODAY
|
|
201
|
-
"
|
|
202
|
-
|
|
203
|
-
for key in breaking features fixes perf refactor docs style tests ci build chores; do
|
|
204
|
-
eval "content=\"\$entries_$key\""
|
|
205
|
-
if [ -n "$content" ]; then
|
|
206
|
-
CHANGELOG_BLOCK="${CHANGELOG_BLOCK}
|
|
207
|
-
### ${SECTIONS[$key]}
|
|
208
|
-
|
|
209
|
-
$content"
|
|
210
|
-
fi
|
|
211
|
-
done
|
|
212
|
-
|
|
213
|
-
# ─── Step 6 — Dry run check ───────────────────────────────────────────────────
|
|
214
|
-
if [ "$DRY_RUN" = true ]; then
|
|
215
|
-
echo ""
|
|
216
|
-
echo "═══════════════════════════════════════════════════════════════"
|
|
217
|
-
echo " DRY RUN — no changes made"
|
|
218
|
-
echo "═══════════════════════════════════════════════════════════════"
|
|
219
|
-
echo " Version: $CURRENT_VERSION → $NEW_VERSION"
|
|
220
|
-
echo " Bump: $BUMP_TYPE"
|
|
221
|
-
echo " Commits: $COMMIT_COUNT since ${LAST_TAG:-<none>}"
|
|
222
|
-
echo " Branch: $BRANCH"
|
|
223
|
-
echo ""
|
|
224
|
-
echo " Files that would change:"
|
|
225
|
-
echo " - package.json (version → $NEW_VERSION)"
|
|
226
|
-
echo " - CHANGELOG.md (new entry below)"
|
|
227
|
-
echo ""
|
|
228
|
-
echo " Tag that would be created: v$NEW_VERSION"
|
|
229
|
-
echo ""
|
|
230
|
-
echo " Changelog entry:"
|
|
231
|
-
echo "───────────────────────────────────────────────────────────────"
|
|
232
|
-
echo "$CHANGELOG_BLOCK"
|
|
233
|
-
echo "───────────────────────────────────────────────────────────────"
|
|
234
|
-
exit 0
|
|
235
|
-
fi
|
|
236
|
-
|
|
237
|
-
# ─── Step 7 — Bump version in package.json ────────────────────────────────────
|
|
238
|
-
npm pkg set version="$NEW_VERSION"
|
|
239
|
-
|
|
240
|
-
node -e "
|
|
241
|
-
const v = require('./package.json').version;
|
|
242
|
-
if (v !== '$NEW_VERSION') {
|
|
243
|
-
console.error('✗ version mismatch: expected $NEW_VERSION, got ' + v);
|
|
244
|
-
process.exit(1);
|
|
245
|
-
}
|
|
246
|
-
console.log('✓ version bumped to $NEW_VERSION');
|
|
247
|
-
"
|
|
248
|
-
|
|
249
|
-
# ─── Step 8 — Write CHANGELOG.md ──────────────────────────────────────────────
|
|
250
|
-
if [ -f CHANGELOG.md ]; then
|
|
251
|
-
# Prepend after the first heading line
|
|
252
|
-
{
|
|
253
|
-
head -n 1 CHANGELOG.md
|
|
254
|
-
echo ""
|
|
255
|
-
echo "$CHANGELOG_BLOCK"
|
|
256
|
-
tail -n +2 CHANGELOG.md
|
|
257
|
-
} > CHANGELOG.md.tmp && mv CHANGELOG.md.tmp CHANGELOG.md
|
|
258
|
-
else
|
|
259
|
-
{
|
|
260
|
-
echo "# Changelog"
|
|
261
|
-
echo ""
|
|
262
|
-
echo "All notable changes to this project are documented in this file."
|
|
263
|
-
echo ""
|
|
264
|
-
echo "$CHANGELOG_BLOCK"
|
|
265
|
-
} > CHANGELOG.md
|
|
266
|
-
fi
|
|
267
|
-
|
|
268
|
-
ok "CHANGELOG.md updated"
|
|
269
|
-
|
|
270
|
-
# ─── Step 9 — Read co-author config ───────────────────────────────────────────
|
|
271
|
-
CO_AUTHOR="pi-mono <261679550+pi-mono@users.noreply.github.com>"
|
|
272
|
-
if [ -f .pi/auto-commit.json ]; then
|
|
273
|
-
CO_AUTHOR=$(node -e "
|
|
274
|
-
const fs = require('fs');
|
|
275
|
-
const cfg = JSON.parse(fs.readFileSync('.pi/auto-commit.json', 'utf8'));
|
|
276
|
-
const ca = cfg.coAuthor || {};
|
|
277
|
-
console.log((ca.login || 'pi-mono') + ' <' + (ca.email || '261679550+pi-mono@users.noreply.github.com') + '>');
|
|
278
|
-
" 2>/dev/null) || true
|
|
279
|
-
fi
|
|
280
|
-
|
|
281
|
-
# ─── Step 10 — Commit ─────────────────────────────────────────────────────────
|
|
282
|
-
git add package.json CHANGELOG.md
|
|
283
|
-
|
|
284
|
-
COMMIT_BODY=$(cat <<EOF
|
|
285
|
-
- Bump version in package.json
|
|
286
|
-
- Add changelog entry for v$NEW_VERSION
|
|
287
|
-
|
|
288
|
-
Commits included:
|
|
289
|
-
$(echo "$COMMITS" | sed 's/^/- /')
|
|
290
|
-
EOF
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
git commit -m "chore(release): bump to v$NEW_VERSION" \
|
|
294
|
-
-m "$COMMIT_BODY" \
|
|
295
|
-
-m "Co-authored-by: $CO_AUTHOR"
|
|
296
|
-
|
|
297
|
-
ok "Committed version bump + changelog"
|
|
298
|
-
|
|
299
|
-
# ─── Step 11 — Create and push tag ────────────────────────────────────────────
|
|
300
|
-
TAG_BODY=$(cat <<EOF
|
|
301
|
-
Release v$NEW_VERSION — $BUMP_TYPE bump
|
|
302
|
-
|
|
303
|
-
$COMMITS
|
|
304
|
-
EOF
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
git tag -a "v$NEW_VERSION" -m "$TAG_BODY"
|
|
308
|
-
ok "Created tag v$NEW_VERSION"
|
|
309
|
-
|
|
310
|
-
git push origin "v$NEW_VERSION"
|
|
311
|
-
ok "Pushed tag v$NEW_VERSION to origin"
|
|
312
|
-
|
|
313
|
-
# ─── Step 12 — Optionally push branch commit ──────────────────────────────────
|
|
314
|
-
echo ""
|
|
315
|
-
read -rp "Push the version-bump commit to the current branch ($BRANCH) too? [Y/n] " PUSH_BRANCH
|
|
316
|
-
if [[ "$PUSH_BRANCH" =~ ^[Yy]?$ ]]; then
|
|
317
|
-
git push origin "$BRANCH"
|
|
318
|
-
ok "Pushed commit to $BRANCH"
|
|
319
|
-
else
|
|
320
|
-
echo "Skipped branch push."
|
|
321
|
-
fi
|
|
322
|
-
|
|
323
|
-
# ─── Step 13 — Report ─────────────────────────────────────────────────────────
|
|
324
|
-
echo ""
|
|
325
|
-
echo "═══════════════════════════════════════════════════════════════"
|
|
326
|
-
echo " ✓ Released v$NEW_VERSION ($BUMP_TYPE)"
|
|
327
|
-
echo "═══════════════════════════════════════════════════════════════"
|
|
328
|
-
echo " Tag: v$NEW_VERSION — pushed to origin"
|
|
329
|
-
echo " Commit: $(git rev-parse --short HEAD)"
|
|
330
|
-
echo " Branch: $BRANCH"
|
|
331
|
-
echo ""
|
|
332
|
-
echo " Workflows triggered:"
|
|
333
|
-
echo " - .github/workflows/publish-github-packages.yml"
|
|
334
|
-
echo " - .github/workflows/publish-npm.yml"
|
|
335
|
-
echo ""
|
|
336
|
-
echo " Changelog: CHANGELOG.md updated"
|
|
337
|
-
echo " Monitor: https://github.com/aryaniyaps/ultimate-pi/actions"
|
|
338
|
-
echo "═══════════════════════════════════════════════════════════════"
|