arcane-cli 0.16.2 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -101,7 +101,8 @@ Rules:
|
|
|
101
101
|
|
|
102
102
|
9. **Stage and commit session-close docs.**
|
|
103
103
|
|
|
104
|
-
**Remote-capability check:** classify the state created by `spell-open-session` before branching, staging, push, or PR operations.
|
|
104
|
+
**Remote-capability check:** classify the state created by `spell-open-session` before branching, staging, push, or PR operations. Check for **not a repository at all** first — every other classification assumes a `.git` directory exists.
|
|
105
|
+
- **Not a Git repository (EF-05):** `git rev-parse --is-inside-work-tree` fails. Do not attempt any git operation (branch, add, commit, push). Fail closed with: `No Git repository detected. Run \`git init -b main\` first (explicit -b avoids landing on "master" on systems where that's the init.defaultBranch default), then re-run spell-close-session.` This mirrors `spell init`'s own next-steps guidance for the same state.
|
|
105
106
|
- **Usable supported remote/merge path:** requires an authenticated supported provider (`github.com`, `dev.azure.com`, or `visualstudio.com`). Keep the current compliant session/PR/worktree branch, resolve the actual remote name and integration branch, and use the provider-neutral PR path below.
|
|
106
107
|
- **No usable remote/merge path:** remain on the current trunk. Do not create a dead session branch, push, pull, or open a PR. Apply the EF-28 interactive commit approval gate, commit locally on trunk after approval, report `Local-only close: committed on <trunk>; no remote PR/pull performed`, and skip steps 9a, 9c, and 10's remote operations.
|
|
107
108
|
- **Read-only session:** if no repository mutation occurred, do not create a branch or commit.
|
package/dist/index.js
CHANGED
|
@@ -1751,13 +1751,50 @@ async function inspectGitRepository(cwd) {
|
|
|
1751
1751
|
uncommittedChanges: lines.length === 0 ? 0 : lines.split("\n").length
|
|
1752
1752
|
};
|
|
1753
1753
|
}
|
|
1754
|
-
async function
|
|
1754
|
+
async function correctUnbornMasterDefault(cwd) {
|
|
1755
|
+
let current;
|
|
1755
1756
|
try {
|
|
1756
|
-
const { stdout } = await runGit(cwd, ["
|
|
1757
|
-
|
|
1758
|
-
return lines.length === 0 ? 0 : lines.split("\n").length;
|
|
1757
|
+
const { stdout } = await runGit(cwd, ["symbolic-ref", "--short", "HEAD"]);
|
|
1758
|
+
current = stdout.trim();
|
|
1759
1759
|
} catch {
|
|
1760
|
-
return
|
|
1760
|
+
return { corrected: false, to: "main" };
|
|
1761
|
+
}
|
|
1762
|
+
if (current !== "master") {
|
|
1763
|
+
return { corrected: false, to: "main" };
|
|
1764
|
+
}
|
|
1765
|
+
try {
|
|
1766
|
+
await runGit(cwd, ["rev-parse", "--verify", "HEAD"]);
|
|
1767
|
+
return { corrected: false, to: "main" };
|
|
1768
|
+
} catch {
|
|
1769
|
+
}
|
|
1770
|
+
try {
|
|
1771
|
+
await runGit(cwd, ["rev-parse", "--verify", "refs/heads/main"]);
|
|
1772
|
+
return { corrected: false, to: "main" };
|
|
1773
|
+
} catch {
|
|
1774
|
+
}
|
|
1775
|
+
await runGit(cwd, ["symbolic-ref", "HEAD", "refs/heads/main"]);
|
|
1776
|
+
return { corrected: true, from: current, to: "main" };
|
|
1777
|
+
}
|
|
1778
|
+
async function ensureLocalPullRebase(cwd) {
|
|
1779
|
+
let rawLocalValue;
|
|
1780
|
+
try {
|
|
1781
|
+
const { stdout } = await runGit(cwd, ["config", "--local", "--get", "pull.rebase"]);
|
|
1782
|
+
rawLocalValue = stdout.trim();
|
|
1783
|
+
} catch {
|
|
1784
|
+
rawLocalValue = void 0;
|
|
1785
|
+
}
|
|
1786
|
+
if (rawLocalValue === void 0) {
|
|
1787
|
+
await runGit(cwd, ["config", "--local", "pull.rebase", "true"]);
|
|
1788
|
+
return { action: "set" };
|
|
1789
|
+
}
|
|
1790
|
+
try {
|
|
1791
|
+
const { stdout } = await runGit(cwd, ["config", "--local", "--type=bool", "--get", "pull.rebase"]);
|
|
1792
|
+
if (stdout.trim() === "false") {
|
|
1793
|
+
return { action: "explicit-false-preserved" };
|
|
1794
|
+
}
|
|
1795
|
+
return { action: "already-set" };
|
|
1796
|
+
} catch {
|
|
1797
|
+
return { action: "already-set" };
|
|
1761
1798
|
}
|
|
1762
1799
|
}
|
|
1763
1800
|
|
|
@@ -1837,21 +1874,42 @@ async function runInit(options, targetDir, assetsDir, packageVersion) {
|
|
|
1837
1874
|
console.log(chalk2.hex("#a855f7")(`
|
|
1838
1875
|
\u2726 Arcane v${packageVersion}
|
|
1839
1876
|
`));
|
|
1877
|
+
let repoNotFound = false;
|
|
1840
1878
|
if (!options.dryRun) {
|
|
1841
|
-
const
|
|
1842
|
-
if (
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1879
|
+
const gitState = await inspectGitRepository(targetDir);
|
|
1880
|
+
if (gitState.status === "not-repository") {
|
|
1881
|
+
repoNotFound = true;
|
|
1882
|
+
} else {
|
|
1883
|
+
if (gitState.status === "no-commits") {
|
|
1884
|
+
const correction = await correctUnbornMasterDefault(targetDir);
|
|
1885
|
+
if (correction.corrected) {
|
|
1886
|
+
printInfo(
|
|
1887
|
+
`Repointed the unborn branch from "${correction.from}" to "${correction.to}" (avoids the Git for Windows default-branch trap).`
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
if (gitState.status === "ready" && gitState.uncommittedChanges > 0) {
|
|
1892
|
+
printWarning(
|
|
1893
|
+
`This repo has ${gitState.uncommittedChanges} uncommitted change${gitState.uncommittedChanges === 1 ? "" : "s"}.`
|
|
1894
|
+
);
|
|
1895
|
+
printInfo("Running spell init will add new files to your working tree.");
|
|
1896
|
+
console.log();
|
|
1897
|
+
const continueAnyway = await confirm({
|
|
1898
|
+
message: "Continue anyway?",
|
|
1899
|
+
default: true
|
|
1900
|
+
});
|
|
1901
|
+
if (!continueAnyway) {
|
|
1902
|
+
console.log("\nCancelled. Commit or stash your changes first.");
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
console.log();
|
|
1906
|
+
}
|
|
1907
|
+
const rebaseResult = await ensureLocalPullRebase(targetDir);
|
|
1908
|
+
if (rebaseResult.action === "explicit-false-preserved") {
|
|
1909
|
+
printWarning(
|
|
1910
|
+
"This repo's pull.rebase is explicitly set to false, but Arcane's governance mandates rebase-and-fast-forward (see .arcane/governance/git-conventions.md). Left as-is since you set it explicitly."
|
|
1911
|
+
);
|
|
1853
1912
|
}
|
|
1854
|
-
console.log();
|
|
1855
1913
|
}
|
|
1856
1914
|
}
|
|
1857
1915
|
let profile = options.profile;
|
|
@@ -2000,6 +2058,12 @@ async function runInit(options, targetDir, assetsDir, packageVersion) {
|
|
|
2000
2058
|
"Review .arcane/governance/ docs and customize for your project"
|
|
2001
2059
|
);
|
|
2002
2060
|
}
|
|
2061
|
+
if (repoNotFound) {
|
|
2062
|
+
printNextStep(
|
|
2063
|
+
step++,
|
|
2064
|
+
'Initialize Git for this project: `git init -b main` (avoids defaulting to "master" on systems where that\'s the system default)'
|
|
2065
|
+
);
|
|
2066
|
+
}
|
|
2003
2067
|
printNextStep(
|
|
2004
2068
|
step++,
|
|
2005
2069
|
"Commit the new files: git add .arcane .github .claude AGENTS.md CLAUDE.md .arcane.json"
|
|
@@ -2680,6 +2744,29 @@ async function checkArcaneManifest(targetDir) {
|
|
|
2680
2744
|
};
|
|
2681
2745
|
}
|
|
2682
2746
|
}
|
|
2747
|
+
async function checkPullRebase(targetDir) {
|
|
2748
|
+
const name = "Git pull.rebase (rebase-and-fast-forward mandate)";
|
|
2749
|
+
try {
|
|
2750
|
+
const { stdout } = await runGit(targetDir, ["config", "--type=bool", "--get", "pull.rebase"]);
|
|
2751
|
+
const value = stdout.trim();
|
|
2752
|
+
if (value === "true") {
|
|
2753
|
+
return { name, passed: true, message: "pull.rebase=true" };
|
|
2754
|
+
}
|
|
2755
|
+
return {
|
|
2756
|
+
name,
|
|
2757
|
+
passed: false,
|
|
2758
|
+
message: `pull.rebase resolves to "${value}", not "true" -- a bare \`git pull\` will create merge commits, contradicting git-conventions.md's rebase-and-fast-forward mandate. Run: git config --local pull.rebase true`,
|
|
2759
|
+
blocking: false
|
|
2760
|
+
};
|
|
2761
|
+
} catch {
|
|
2762
|
+
return {
|
|
2763
|
+
name,
|
|
2764
|
+
passed: false,
|
|
2765
|
+
message: "pull.rebase is unset, not a recognized boolean, or this isn't a Git repository -- effective behavior may not be rebase-and-fast-forward, and Git for Windows defaults this to false when unset. Run: git config --local pull.rebase true",
|
|
2766
|
+
blocking: false
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2683
2770
|
function checkIncidentReleaseGate() {
|
|
2684
2771
|
const result = evaluateIncidentGate(INCIDENT_QUEUE);
|
|
2685
2772
|
if (result.blocked) {
|
|
@@ -2742,7 +2829,8 @@ async function runDoctor(targetDir, options = {}, assetsDir) {
|
|
|
2742
2829
|
checkNodeVersion(),
|
|
2743
2830
|
checkVSCodeExtension("GitHub.copilot-chat", "GitHub Copilot (Chat)"),
|
|
2744
2831
|
checkArcaneManifest(targetDir),
|
|
2745
|
-
Promise.resolve(checkIncidentReleaseGate())
|
|
2832
|
+
Promise.resolve(checkIncidentReleaseGate()),
|
|
2833
|
+
checkPullRebase(targetDir)
|
|
2746
2834
|
]);
|
|
2747
2835
|
const sessionResults = await checkSessionContinuity(targetDir);
|
|
2748
2836
|
results.push(...sessionResults);
|