@aipper/aiws 0.0.29 → 0.0.30
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/bin/aiws.js +2 -2
- package/dist/aiws-package.d.ts +4 -0
- package/dist/aiws-package.js +11 -0
- package/dist/aiws-package.js.map +1 -0
- package/dist/aiws-paths.d.ts +48 -0
- package/dist/aiws-paths.js +129 -0
- package/dist/aiws-paths.js.map +1 -0
- package/dist/backup.d.ts +27 -0
- package/dist/backup.js +125 -0
- package/dist/backup.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +773 -0
- package/dist/cli.js.map +1 -0
- package/dist/codex-prompts.d.ts +17 -0
- package/dist/codex-prompts.js +49 -0
- package/dist/codex-prompts.js.map +1 -0
- package/dist/codex-skills.d.ts +17 -0
- package/dist/codex-skills.js +75 -0
- package/dist/codex-skills.js.map +1 -0
- package/dist/commands/change-advice.d.ts +25 -0
- package/dist/commands/change-advice.js +197 -0
- package/dist/commands/change-advice.js.map +1 -0
- package/dist/commands/change-evidence-command.d.ts +29 -0
- package/dist/commands/change-evidence-command.js +213 -0
- package/dist/commands/change-evidence-command.js.map +1 -0
- package/dist/commands/change-evidence-entry.d.ts +16 -0
- package/dist/commands/change-evidence-entry.js +35 -0
- package/dist/commands/change-evidence-entry.js.map +1 -0
- package/dist/commands/change-evidence.d.ts +108 -0
- package/dist/commands/change-evidence.js +341 -0
- package/dist/commands/change-evidence.js.map +1 -0
- package/dist/commands/change-finish.d.ts +71 -0
- package/dist/commands/change-finish.js +387 -0
- package/dist/commands/change-finish.js.map +1 -0
- package/dist/commands/change-git-status.d.ts +22 -0
- package/dist/commands/change-git-status.js +50 -0
- package/dist/commands/change-git-status.js.map +1 -0
- package/dist/commands/change-lifecycle-entry.d.ts +29 -0
- package/{src → dist}/commands/change-lifecycle-entry.js +21 -28
- package/dist/commands/change-lifecycle-entry.js.map +1 -0
- package/dist/commands/change-lifecycle.d.ts +56 -0
- package/dist/commands/change-lifecycle.js +211 -0
- package/dist/commands/change-lifecycle.js.map +1 -0
- package/dist/commands/change-metrics-command.d.ts +12 -0
- package/dist/commands/change-metrics-command.js +110 -0
- package/dist/commands/change-metrics-command.js.map +1 -0
- package/dist/commands/change-next-command.d.ts +18 -0
- package/dist/commands/change-next-command.js +138 -0
- package/dist/commands/change-next-command.js.map +1 -0
- package/dist/commands/change-review-gates.d.ts +107 -0
- package/dist/commands/change-review-gates.js +329 -0
- package/dist/commands/change-review-gates.js.map +1 -0
- package/dist/commands/change-scope-gate.d.ts +21 -0
- package/dist/commands/change-scope-gate.js +32 -0
- package/dist/commands/change-scope-gate.js.map +1 -0
- package/dist/commands/change-start.d.ts +30 -0
- package/dist/commands/change-start.js +91 -0
- package/dist/commands/change-start.js.map +1 -0
- package/dist/commands/change-state-command.d.ts +3 -0
- package/dist/commands/change-state-command.js +32 -0
- package/dist/commands/change-state-command.js.map +1 -0
- package/dist/commands/change-status-command.d.ts +13 -0
- package/dist/commands/change-status-command.js +126 -0
- package/dist/commands/change-status-command.js.map +1 -0
- package/dist/commands/change-status-context.d.ts +58 -0
- package/dist/commands/change-status-context.js +166 -0
- package/dist/commands/change-status-context.js.map +1 -0
- package/dist/commands/change-task-stats.d.ts +24 -0
- package/dist/commands/change-task-stats.js +66 -0
- package/dist/commands/change-task-stats.js.map +1 -0
- package/dist/commands/change-validate-entry.d.ts +17 -0
- package/dist/commands/change-validate-entry.js +44 -0
- package/dist/commands/change-validate-entry.js.map +1 -0
- package/dist/commands/change-validate.d.ts +32 -0
- package/dist/commands/change-validate.js +128 -0
- package/dist/commands/change-validate.js.map +1 -0
- package/dist/commands/change.d.ts +322 -0
- package/dist/commands/change.js +1731 -0
- package/dist/commands/change.js.map +1 -0
- package/dist/commands/codex-install-prompts.d.ts +4 -0
- package/dist/commands/codex-install-prompts.js +68 -0
- package/dist/commands/codex-install-prompts.js.map +1 -0
- package/dist/commands/codex-install-skills.d.ts +4 -0
- package/dist/commands/codex-install-skills.js +74 -0
- package/dist/commands/codex-install-skills.js.map +1 -0
- package/dist/commands/codex-status-prompts.d.ts +4 -0
- package/dist/commands/codex-status-prompts.js +44 -0
- package/dist/commands/codex-status-prompts.js.map +1 -0
- package/dist/commands/codex-status-skills.d.ts +4 -0
- package/dist/commands/codex-status-skills.js +54 -0
- package/dist/commands/codex-status-skills.js.map +1 -0
- package/dist/commands/codex-uninstall-prompts.d.ts +4 -0
- package/dist/commands/codex-uninstall-prompts.js +51 -0
- package/dist/commands/codex-uninstall-prompts.js.map +1 -0
- package/dist/commands/codex-uninstall-skills.d.ts +4 -0
- package/dist/commands/codex-uninstall-skills.js +59 -0
- package/dist/commands/codex-uninstall-skills.js.map +1 -0
- package/dist/commands/dashboard.d.ts +222 -0
- package/dist/commands/dashboard.js +193 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/hooks-install.d.ts +4 -0
- package/dist/commands/hooks-install.js +90 -0
- package/dist/commands/hooks-install.js.map +1 -0
- package/dist/commands/hooks-status.d.ts +4 -0
- package/dist/commands/hooks-status.js +89 -0
- package/dist/commands/hooks-status.js.map +1 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +100 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/opencode-auto.d.ts +11 -0
- package/dist/commands/opencode-auto.js +158 -0
- package/dist/commands/opencode-auto.js.map +1 -0
- package/dist/commands/opencode-status.d.ts +4 -0
- package/dist/commands/opencode-status.js +75 -0
- package/dist/commands/opencode-status.js.map +1 -0
- package/dist/commands/opencode-supervise.d.ts +24 -0
- package/dist/commands/opencode-supervise.js +188 -0
- package/dist/commands/opencode-supervise.js.map +1 -0
- package/dist/commands/rollback.d.ts +4 -0
- package/dist/commands/rollback.js +12 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/update.d.ts +4 -0
- package/dist/commands/update.js +212 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.d.ts +4 -0
- package/dist/commands/validate.js +222 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/ws-commit.d.ts +5 -0
- package/dist/commands/ws-commit.js +39 -0
- package/dist/commands/ws-commit.js.map +1 -0
- package/dist/commands/ws-deliver.d.ts +5 -0
- package/dist/commands/ws-deliver.js +59 -0
- package/dist/commands/ws-deliver.js.map +1 -0
- package/dist/commands/ws-finish.d.ts +5 -0
- package/dist/commands/ws-finish.js +142 -0
- package/dist/commands/ws-finish.js.map +1 -0
- package/dist/commands/ws-handoff.d.ts +5 -0
- package/dist/commands/ws-handoff.js +35 -0
- package/dist/commands/ws-handoff.js.map +1 -0
- package/dist/commands/ws-migrate.d.ts +5 -0
- package/dist/commands/ws-migrate.js +41 -0
- package/dist/commands/ws-migrate.js.map +1 -0
- package/dist/commands/ws-plan-verify.d.ts +5 -0
- package/dist/commands/ws-plan-verify.js +38 -0
- package/dist/commands/ws-plan-verify.js.map +1 -0
- package/dist/commands/ws-pull.d.ts +5 -0
- package/dist/commands/ws-pull.js +26 -0
- package/dist/commands/ws-pull.js.map +1 -0
- package/dist/commands/ws-push.d.ts +5 -0
- package/dist/commands/ws-push.js +68 -0
- package/dist/commands/ws-push.js.map +1 -0
- package/dist/commands/ws-submodule-setup.d.ts +5 -0
- package/dist/commands/ws-submodule-setup.js +55 -0
- package/dist/commands/ws-submodule-setup.js.map +1 -0
- package/dist/commands/ws-verify-before-complete.d.ts +5 -0
- package/dist/commands/ws-verify-before-complete.js +35 -0
- package/dist/commands/ws-verify-before-complete.js.map +1 -0
- package/dist/dashboard/app.d.ts +1 -0
- package/dist/dashboard/app.js +370 -0
- package/dist/dashboard/app.js.map +1 -0
- package/dist/delegation/cli.d.ts +6 -0
- package/dist/delegation/cli.js +14 -0
- package/dist/delegation/cli.js.map +1 -0
- package/dist/delegation/index.d.ts +35 -0
- package/dist/delegation/index.js +118 -0
- package/dist/delegation/index.js.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +11 -0
- package/dist/errors.js.map +1 -0
- package/dist/exec.d.ts +9 -0
- package/dist/exec.js +22 -0
- package/dist/exec.js.map +1 -0
- package/dist/fs.d.ts +9 -0
- package/dist/fs.js +62 -0
- package/dist/fs.js.map +1 -0
- package/dist/governance.d.ts +10 -0
- package/dist/governance.js +135 -0
- package/dist/governance.js.map +1 -0
- package/dist/hash.d.ts +3 -0
- package/dist/hash.js +13 -0
- package/dist/hash.js.map +1 -0
- package/dist/hooks/deploy-claude.d.ts +13 -0
- package/dist/hooks/deploy-claude.js +67 -0
- package/dist/hooks/deploy-claude.js.map +1 -0
- package/dist/journal.d.ts +22 -0
- package/dist/journal.js +112 -0
- package/dist/journal.js.map +1 -0
- package/dist/json-schema-lite.d.ts +22 -0
- package/dist/json-schema-lite.js +148 -0
- package/dist/json-schema-lite.js.map +1 -0
- package/dist/managed-blocks.d.ts +24 -0
- package/dist/managed-blocks.js +84 -0
- package/dist/managed-blocks.js.map +1 -0
- package/dist/manifest.d.ts +24 -0
- package/dist/manifest.js +145 -0
- package/dist/manifest.js.map +1 -0
- package/dist/opencode-env.d.ts +62 -0
- package/dist/opencode-env.js +178 -0
- package/dist/opencode-env.js.map +1 -0
- package/dist/path-utils.d.ts +2 -0
- package/dist/path-utils.js +8 -0
- package/dist/path-utils.js.map +1 -0
- package/dist/spec.d.ts +17 -0
- package/dist/spec.js +174 -0
- package/dist/spec.js.map +1 -0
- package/dist/template.d.ts +20 -0
- package/dist/template.js +123 -0
- package/dist/template.js.map +1 -0
- package/dist/workspace.d.ts +4 -0
- package/dist/workspace.js +19 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +11 -3
- package/src/aiws-package.js +0 -15
- package/src/backup.js +0 -149
- package/src/cli.js +0 -760
- package/src/codex-prompts.js +0 -74
- package/src/codex-skills.js +0 -111
- package/src/commands/change-advice.js +0 -181
- package/src/commands/change-evidence-command.js +0 -242
- package/src/commands/change-evidence-entry.js +0 -40
- package/src/commands/change-evidence.js +0 -340
- package/src/commands/change-finish.js +0 -413
- package/src/commands/change-git-status.js +0 -53
- package/src/commands/change-lifecycle.js +0 -236
- package/src/commands/change-metrics-command.js +0 -110
- package/src/commands/change-next-command.js +0 -133
- package/src/commands/change-review-gates.js +0 -331
- package/src/commands/change-scope-gate.js +0 -34
- package/src/commands/change-start.js +0 -85
- package/src/commands/change-state-command.js +0 -40
- package/src/commands/change-status-command.js +0 -125
- package/src/commands/change-status-context.js +0 -173
- package/src/commands/change-task-stats.js +0 -64
- package/src/commands/change-validate-entry.js +0 -45
- package/src/commands/change-validate.js +0 -127
- package/src/commands/change.js +0 -1829
- package/src/commands/codex-install-prompts.js +0 -68
- package/src/commands/codex-install-skills.js +0 -74
- package/src/commands/codex-status-prompts.js +0 -55
- package/src/commands/codex-status-skills.js +0 -63
- package/src/commands/codex-uninstall-prompts.js +0 -55
- package/src/commands/codex-uninstall-skills.js +0 -62
- package/src/commands/dashboard.js +0 -205
- package/src/commands/hooks-install.js +0 -92
- package/src/commands/hooks-status.js +0 -95
- package/src/commands/init.js +0 -101
- package/src/commands/opencode-auto.js +0 -167
- package/src/commands/opencode-status.js +0 -103
- package/src/commands/opencode-supervise.js +0 -200
- package/src/commands/rollback.js +0 -13
- package/src/commands/update.js +0 -145
- package/src/commands/validate.js +0 -228
- package/src/commands/ws-commit.js +0 -44
- package/src/commands/ws-deliver.js +0 -67
- package/src/commands/ws-finish.js +0 -83
- package/src/commands/ws-handoff.js +0 -40
- package/src/commands/ws-migrate.js +0 -41
- package/src/commands/ws-plan-verify.js +0 -44
- package/src/commands/ws-pull.js +0 -30
- package/src/commands/ws-push.js +0 -71
- package/src/commands/ws-submodule-setup.js +0 -65
- package/src/commands/ws-verify-before-complete.js +0 -39
- package/src/dashboard/app.js +0 -371
- package/src/errors.js +0 -15
- package/src/exec.js +0 -34
- package/src/fs.js +0 -91
- package/src/governance.js +0 -159
- package/src/hash.js +0 -25
- package/src/json-schema-lite.js +0 -164
- package/src/managed-blocks.js +0 -131
- package/src/manifest.js +0 -177
- package/src/opencode-env.js +0 -208
- package/src/path-utils.js +0 -20
- package/src/spec.js +0 -195
- package/src/template.js +0 -187
- package/src/workspace.js +0 -23
- /package/{src → dist}/dashboard/index.html +0 -0
- /package/{src → dist}/dashboard/style.css +0 -0
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @param {string} gitRoot
|
|
5
|
-
* @param {string} branch
|
|
6
|
-
* @param {string | undefined} preferredRemote
|
|
7
|
-
* @param {{ runCommand: typeof import("../exec.js").runCommand }} deps
|
|
8
|
-
*/
|
|
9
|
-
export async function resolvePushTarget(gitRoot, branch, preferredRemote, deps) {
|
|
10
|
-
const remoteRaw = String(preferredRemote || "").trim();
|
|
11
|
-
const branchRemoteRes = await deps.runCommand("git", ["config", "--get", `branch.${branch}.remote`], { cwd: gitRoot });
|
|
12
|
-
const trackedRemote = String(branchRemoteRes.stdout || "").trim();
|
|
13
|
-
|
|
14
|
-
const branchMergeRes = await deps.runCommand("git", ["config", "--get", `branch.${branch}.merge`], { cwd: gitRoot });
|
|
15
|
-
const trackedMergeRef = String(branchMergeRes.stdout || "").trim();
|
|
16
|
-
const trackedBranch = trackedMergeRef.startsWith("refs/heads/") ? trackedMergeRef.slice("refs/heads/".length) : "";
|
|
17
|
-
|
|
18
|
-
const remote = remoteRaw || trackedRemote || "origin";
|
|
19
|
-
const remoteBranch = trackedBranch || branch;
|
|
20
|
-
const refspec = remoteBranch === branch ? branch : `${branch}:${remoteBranch}`;
|
|
21
|
-
|
|
22
|
-
return { remote, remoteBranch, refspec, trackedRemote, trackedMergeRef };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @param {string} gitRoot
|
|
27
|
-
* @param {{ pathExists: (path: string) => Promise<boolean>, runCommand: typeof import("../exec.js").runCommand }} deps
|
|
28
|
-
* @returns {Promise<Array<{ name: string, path: string }>>}
|
|
29
|
-
*/
|
|
30
|
-
export async function listSubmodulesFromGitmodules(gitRoot, deps) {
|
|
31
|
-
const gitmodules = path.join(gitRoot, ".gitmodules");
|
|
32
|
-
if (!(await deps.pathExists(gitmodules))) return [];
|
|
33
|
-
|
|
34
|
-
const list = await deps.runCommand("git", ["config", "--file", ".gitmodules", "--get-regexp", "^submodule\\..*\\.path$"], { cwd: gitRoot });
|
|
35
|
-
if (list.code !== 0) return [];
|
|
36
|
-
|
|
37
|
-
/** @type {Array<{ name: string, path: string }>} */
|
|
38
|
-
const subs = [];
|
|
39
|
-
for (const raw of String(list.stdout || "").split("\n")) {
|
|
40
|
-
const line = raw.trim();
|
|
41
|
-
if (!line) continue;
|
|
42
|
-
const idx = line.indexOf(" ");
|
|
43
|
-
if (idx <= 0) continue;
|
|
44
|
-
const key = line.slice(0, idx).trim();
|
|
45
|
-
const subPath = line.slice(idx + 1).trim();
|
|
46
|
-
const match = key.match(/^submodule\.(.+)\.path$/);
|
|
47
|
-
if (!match) continue;
|
|
48
|
-
const name = String(match[1] || "").trim();
|
|
49
|
-
if (!name || !subPath) continue;
|
|
50
|
-
subs.push({ name, path: subPath });
|
|
51
|
-
}
|
|
52
|
-
return subs;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* @param {string} targetsPath
|
|
57
|
-
* @param {{ pathExists: (path: string) => Promise<boolean>, readText: (path: string) => Promise<string> }} deps
|
|
58
|
-
*/
|
|
59
|
-
export async function parseSubmoduleTargetsFile(targetsPath, deps) {
|
|
60
|
-
/** @type {Map<string, { branch: string, remote: string }>} */
|
|
61
|
-
const parsed = new Map();
|
|
62
|
-
/** @type {string[]} */
|
|
63
|
-
const errors = [];
|
|
64
|
-
|
|
65
|
-
if (!(await deps.pathExists(targetsPath))) {
|
|
66
|
-
return { parsed, errors };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const text = await deps.readText(targetsPath);
|
|
70
|
-
const lines = String(text || "").split(/\r?\n/);
|
|
71
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
72
|
-
const raw = lines[i] || "";
|
|
73
|
-
const line = raw.trim();
|
|
74
|
-
if (!line || line.startsWith("#")) continue;
|
|
75
|
-
const parts = line.split(/\s+/).filter(Boolean);
|
|
76
|
-
if (parts.length < 2) {
|
|
77
|
-
errors.push(`${path.basename(targetsPath)}:${i + 1} invalid line (expected: <path> <target_branch> [remote])`);
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const subPath = String(parts[0] || "").trim();
|
|
81
|
-
const targetBranch = String(parts[1] || "").trim();
|
|
82
|
-
const remote = String(parts[2] || "origin").trim() || "origin";
|
|
83
|
-
if (!subPath) {
|
|
84
|
-
errors.push(`${path.basename(targetsPath)}:${i + 1} missing submodule path`);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (parsed.has(subPath)) {
|
|
88
|
-
errors.push(`${path.basename(targetsPath)}:${i + 1} duplicate submodule path: ${subPath}`);
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (!targetBranch || ["<fill-me>", "<fill_me>", "tbd", "TBD"].includes(targetBranch)) {
|
|
92
|
-
errors.push(`${path.basename(targetsPath)}:${i + 1} missing/placeholder target_branch for ${subPath}`);
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
parsed.set(subPath, { branch: targetBranch, remote });
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return { parsed, errors };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* @param {string} repoPath
|
|
103
|
-
* @param {string} rev
|
|
104
|
-
* @param {{ pathExists: (path: string) => Promise<boolean>, runCommand: typeof import("../exec.js").runCommand }} deps
|
|
105
|
-
*/
|
|
106
|
-
async function gitHasRevision(repoPath, rev, deps) {
|
|
107
|
-
if (!(await deps.pathExists(repoPath))) return false;
|
|
108
|
-
const res = await deps.runCommand("git", ["-C", repoPath, "cat-file", "-e", `${rev}^{commit}`], { cwd: repoPath });
|
|
109
|
-
return res.code === 0;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* @param {string} repoPath
|
|
114
|
-
* @param {{ pathExists: (path: string) => Promise<boolean>, runCommand: typeof import("../exec.js").runCommand }} deps
|
|
115
|
-
* @returns {Promise<boolean>}
|
|
116
|
-
*/
|
|
117
|
-
export async function isGitRepository(repoPath, deps) {
|
|
118
|
-
if (!(await deps.pathExists(repoPath))) return false;
|
|
119
|
-
const res = await deps.runCommand("git", ["-C", repoPath, "rev-parse", "--git-dir"], { cwd: repoPath });
|
|
120
|
-
return res.code === 0;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* @param {string} targetRepoPath
|
|
125
|
-
* @param {string} sha
|
|
126
|
-
* @param {string | undefined} fallbackRepoPath
|
|
127
|
-
* @param {{
|
|
128
|
-
* pathExists: (path: string) => Promise<boolean>,
|
|
129
|
-
* runCommand: typeof import("../exec.js").runCommand,
|
|
130
|
-
* UserError: typeof import("../errors.js").UserError
|
|
131
|
-
* }} deps
|
|
132
|
-
*/
|
|
133
|
-
async function ensureRevisionAvailable(targetRepoPath, sha, fallbackRepoPath, deps) {
|
|
134
|
-
if (await gitHasRevision(targetRepoPath, sha, deps)) return;
|
|
135
|
-
const fallback = String(fallbackRepoPath || "").trim();
|
|
136
|
-
if (!fallback) {
|
|
137
|
-
throw new deps.UserError("Required submodule commit is not available locally.", {
|
|
138
|
-
details: `repo: ${targetRepoPath}\ncommit: ${sha}`,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
if (!(await gitHasRevision(fallback, sha, deps))) {
|
|
142
|
-
throw new deps.UserError("Required submodule commit is missing in both target and fallback repos.", {
|
|
143
|
-
details: `target_repo: ${targetRepoPath}\nfallback_repo: ${fallback}\ncommit: ${sha}`,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
const fetch = await deps.runCommand("git", ["-C", targetRepoPath, "fetch", fallback, sha], { cwd: targetRepoPath });
|
|
147
|
-
if (fetch.code !== 0 || !(await gitHasRevision(targetRepoPath, sha, deps))) {
|
|
148
|
-
throw new deps.UserError("Failed to import submodule commit from fallback repo.", {
|
|
149
|
-
details: `target_repo: ${targetRepoPath}\nfallback_repo: ${fallback}\ncommit: ${sha}\n\n${fetch.stderr || fetch.stdout}`,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* @param {string} repoPath
|
|
156
|
-
* @param {string} remote
|
|
157
|
-
* @param {string} branch
|
|
158
|
-
* @param {string} sha
|
|
159
|
-
* @param {{ runCommand: typeof import("../exec.js").runCommand, UserError: typeof import("../errors.js").UserError }} deps
|
|
160
|
-
*/
|
|
161
|
-
async function pushSubmoduleBranch(repoPath, remote, branch, sha, deps) {
|
|
162
|
-
const fetch = await deps.runCommand("git", ["-C", repoPath, "fetch", remote, "--prune"], { cwd: repoPath });
|
|
163
|
-
if (fetch.code !== 0) {
|
|
164
|
-
throw new deps.UserError("Failed to fetch submodule remote before push.", {
|
|
165
|
-
details: `repo: ${repoPath}\nremote: ${remote}\n\n${fetch.stderr || fetch.stdout}`,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const remoteRef = `refs/remotes/${remote}/${branch}`;
|
|
170
|
-
const hasRemoteBranch = await deps
|
|
171
|
-
.runCommand("git", ["-C", repoPath, "show-ref", "--verify", "--quiet", remoteRef], { cwd: repoPath })
|
|
172
|
-
.then((result) => result.code === 0);
|
|
173
|
-
if (!hasRemoteBranch) {
|
|
174
|
-
throw new deps.UserError("Submodule target branch does not exist on remote.", {
|
|
175
|
-
details: `repo: ${repoPath}\nremote: ${remote}\nbranch: ${branch}`,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const remoteAncestor = await deps
|
|
180
|
-
.runCommand("git", ["-C", repoPath, "merge-base", "--is-ancestor", remoteRef, sha], { cwd: repoPath })
|
|
181
|
-
.then((result) => result.code === 0);
|
|
182
|
-
if (remoteAncestor) {
|
|
183
|
-
const push = await deps.runCommand("git", ["-C", repoPath, "push", remote, `${sha}:refs/heads/${branch}`], { cwd: repoPath });
|
|
184
|
-
if (push.code !== 0) {
|
|
185
|
-
throw new deps.UserError("Failed to fast-forward push submodule target branch.", {
|
|
186
|
-
details: `repo: ${repoPath}\nremote: ${remote}\nbranch: ${branch}\ncommit: ${sha}\n\n${push.stderr || push.stdout}`,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
return { status: "pushed", remoteRef };
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const shaContained = await deps
|
|
193
|
-
.runCommand("git", ["-C", repoPath, "merge-base", "--is-ancestor", sha, remoteRef], { cwd: repoPath })
|
|
194
|
-
.then((result) => result.code === 0);
|
|
195
|
-
if (shaContained) {
|
|
196
|
-
return { status: "already-contained", remoteRef };
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
throw new deps.UserError("Submodule target branch diverged from gitlink commit.", {
|
|
200
|
-
details: `repo: ${repoPath}\nremote: ${remote}\nbranch: ${branch}\ncommit: ${sha}\n\nHint: reconcile the submodule branch manually, then retry.`,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* @param {string} gitRoot
|
|
206
|
-
* @param {string} changeId
|
|
207
|
-
* @param {string} baseBranch
|
|
208
|
-
* @param {{
|
|
209
|
-
* pathExists: (path: string) => Promise<boolean>,
|
|
210
|
-
* readText: (path: string) => Promise<string>,
|
|
211
|
-
* runCommand: typeof import("../exec.js").runCommand,
|
|
212
|
-
* UserError: typeof import("../errors.js").UserError
|
|
213
|
-
* }} deps
|
|
214
|
-
*/
|
|
215
|
-
export async function pushSubmodulesForFinish(gitRoot, changeId, baseBranch, deps) {
|
|
216
|
-
const subs = await listSubmodulesFromGitmodules(gitRoot, deps);
|
|
217
|
-
if (subs.length === 0) return { processed: 0 };
|
|
218
|
-
|
|
219
|
-
const targetsPath = path.join(gitRoot, "changes", changeId, "submodules.targets");
|
|
220
|
-
if (!(await deps.pathExists(targetsPath))) {
|
|
221
|
-
throw new deps.UserError("Missing submodules.targets for submodule-aware finish.", {
|
|
222
|
-
details: `required: ${path.relative(gitRoot, targetsPath)}\n\nHint: add one entry per submodule path before running \`aiws change finish --push\`.`,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const parsedTargets = await parseSubmoduleTargetsFile(targetsPath, deps);
|
|
227
|
-
if (parsedTargets.errors.length > 0) {
|
|
228
|
-
throw new deps.UserError("Invalid submodules.targets.", { details: parsedTargets.errors.join("\n") });
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const missingPaths = subs.filter((submodule) => !parsedTargets.parsed.has(submodule.path)).map((submodule) => submodule.path);
|
|
232
|
-
if (missingPaths.length > 0) {
|
|
233
|
-
throw new deps.UserError("submodules.targets missing declared submodule paths.", {
|
|
234
|
-
details: `file: ${path.relative(gitRoot, targetsPath)}\nmissing: ${missingPaths.join(", ")}`,
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/** @type {Array<{ name: string, path: string, sha: string, branch: string, remote: string, pinBranch: string }>} */
|
|
239
|
-
const specs = [];
|
|
240
|
-
for (const sub of subs) {
|
|
241
|
-
const target = parsedTargets.parsed.get(sub.path);
|
|
242
|
-
if (!target) continue;
|
|
243
|
-
const targetBranch = target.branch === "." ? baseBranch : target.branch;
|
|
244
|
-
const gitlink = await deps.runCommand("git", ["rev-parse", `HEAD:${sub.path}`], { cwd: gitRoot });
|
|
245
|
-
if (gitlink.code !== 0) {
|
|
246
|
-
throw new deps.UserError("Failed to resolve submodule gitlink commit from merged superproject.", {
|
|
247
|
-
details: `path: ${sub.path}\n\n${gitlink.stderr || gitlink.stdout}`,
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
const sha = String(gitlink.stdout || "").trim();
|
|
251
|
-
specs.push({
|
|
252
|
-
name: sub.name,
|
|
253
|
-
path: sub.path,
|
|
254
|
-
sha,
|
|
255
|
-
branch: targetBranch,
|
|
256
|
-
remote: target.remote,
|
|
257
|
-
pinBranch: `aiws/pin/${targetBranch}`,
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
for (const spec of specs) {
|
|
262
|
-
const targetRepoPath = path.join(gitRoot, spec.path);
|
|
263
|
-
if (!(await isGitRepository(targetRepoPath, deps))) {
|
|
264
|
-
throw new deps.UserError("Submodule repository is not initialized.", {
|
|
265
|
-
details: `path: ${spec.path}\nHint: initialize submodules before finish.`,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (!(await gitHasRevision(targetRepoPath, spec.sha, deps))) {
|
|
270
|
-
throw new deps.UserError("Merged submodule gitlink commit is not available locally.", {
|
|
271
|
-
details: `path: ${spec.path}\ncommit: ${spec.sha}`,
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const pushResult = await pushSubmoduleBranch(targetRepoPath, spec.remote, spec.branch, spec.sha, deps);
|
|
276
|
-
console.log(
|
|
277
|
-
`submodule push: ${spec.path} (${spec.remote} ${spec.branch}${pushResult.status === "pushed" ? ` <= ${spec.sha}` : ", already contained"})`,
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const sync = await deps.runCommand("git", ["submodule", "update", "--init", "--recursive"], { cwd: gitRoot });
|
|
282
|
-
if (sync.code !== 0) {
|
|
283
|
-
throw new deps.UserError("Submodule push succeeded, but failed to sync submodules in target repo.", {
|
|
284
|
-
details: sync.stderr || sync.stdout,
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
for (const spec of specs) {
|
|
289
|
-
const targetRepoPath = path.join(gitRoot, spec.path);
|
|
290
|
-
if (!(await isGitRepository(targetRepoPath, deps))) {
|
|
291
|
-
throw new deps.UserError("Submodule repo missing after sync.", { details: `path: ${spec.path}` });
|
|
292
|
-
}
|
|
293
|
-
await ensureRevisionAvailable(targetRepoPath, spec.sha, "", deps);
|
|
294
|
-
const fetch = await deps.runCommand("git", ["-C", targetRepoPath, "fetch", spec.remote, "--prune"], { cwd: targetRepoPath });
|
|
295
|
-
if (fetch.code !== 0) {
|
|
296
|
-
throw new deps.UserError("Failed to refresh target submodule remote.", {
|
|
297
|
-
details: `path: ${spec.path}\nremote: ${spec.remote}\n\n${fetch.stderr || fetch.stdout}`,
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
const checkout = await deps.runCommand("git", ["-C", targetRepoPath, "checkout", "-B", spec.pinBranch, spec.sha], { cwd: targetRepoPath });
|
|
301
|
-
if (checkout.code !== 0) {
|
|
302
|
-
throw new deps.UserError("Failed to attach target submodule to pin branch.", {
|
|
303
|
-
details: `path: ${spec.path}\npin_branch: ${spec.pinBranch}\ncommit: ${spec.sha}\n\n${checkout.stderr || checkout.stdout}`,
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
await deps.runCommand("git", ["-C", targetRepoPath, "branch", "--set-upstream-to", `${spec.remote}/${spec.branch}`, spec.pinBranch], {
|
|
307
|
-
cwd: targetRepoPath,
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return { processed: specs.length };
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* @param {string} gitRoot
|
|
316
|
-
* @param {{ changeId?: string, into?: string, base?: string }} options
|
|
317
|
-
* @param {{
|
|
318
|
-
* assertValidChangeId: (changeId: string) => void,
|
|
319
|
-
* checkGitClean: (gitRoot: string) => Promise<{ clean: true } | { clean: false, details: string }>,
|
|
320
|
-
* currentBranch: (gitRoot: string) => Promise<string>,
|
|
321
|
-
* pathExists: (path: string) => Promise<boolean>,
|
|
322
|
-
* readText: (path: string) => Promise<string>,
|
|
323
|
-
* resolveChangeId: (gitRoot: string, changeId: string | undefined, options: { command: string }) => Promise<string>,
|
|
324
|
-
* runCommand: typeof import("../exec.js").runCommand,
|
|
325
|
-
* UserError: typeof import("../errors.js").UserError
|
|
326
|
-
* }} deps
|
|
327
|
-
*/
|
|
328
|
-
export async function resolveFinishContext(gitRoot, options, deps) {
|
|
329
|
-
const changeId = await deps.resolveChangeId(gitRoot, options.changeId, { command: "finish" });
|
|
330
|
-
deps.assertValidChangeId(changeId);
|
|
331
|
-
|
|
332
|
-
const changeBranch = `change/${changeId}`;
|
|
333
|
-
const changeBranchRef = `refs/heads/${changeBranch}`;
|
|
334
|
-
|
|
335
|
-
const hasChangeBranch = await deps
|
|
336
|
-
.runCommand("git", ["show-ref", "--verify", "--quiet", changeBranchRef], { cwd: gitRoot })
|
|
337
|
-
.then((result) => result.code === 0);
|
|
338
|
-
if (!hasChangeBranch) {
|
|
339
|
-
throw new deps.UserError(`Missing change branch: ${changeBranch}`, {
|
|
340
|
-
details: "Hint: create it with `aiws change start <change-id>` (or `git switch -c change/<id>`).",
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const clean = await deps.checkGitClean(gitRoot);
|
|
345
|
-
if (!clean.clean) {
|
|
346
|
-
throw new deps.UserError("Refusing to finish with a dirty working tree.", {
|
|
347
|
-
details: `${clean.details}\n\nHint: commit or stash your changes, then retry.`,
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
const cur = await deps.currentBranch(gitRoot);
|
|
352
|
-
const intoRaw = String(options.into || "").trim();
|
|
353
|
-
const baseRaw = String(options.base || "").trim();
|
|
354
|
-
if (intoRaw && baseRaw && intoRaw !== baseRaw) {
|
|
355
|
-
throw new deps.UserError("change finish: cannot combine --into with --base (values differ).", {
|
|
356
|
-
details: `--into=${intoRaw}\n--base=${baseRaw}`,
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/** @type {string} */
|
|
361
|
-
let into = intoRaw || baseRaw;
|
|
362
|
-
|
|
363
|
-
if (!into) {
|
|
364
|
-
if (!cur) {
|
|
365
|
-
throw new deps.UserError("Detached HEAD: cannot infer target branch for finish.", {
|
|
366
|
-
details: "Hint: switch to the target branch (e.g. main) and retry, or pass `aiws change finish <id> --into <branch>`.",
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
if (cur !== changeBranch) {
|
|
370
|
-
into = cur;
|
|
371
|
-
} else {
|
|
372
|
-
const metaPath = path.join(gitRoot, "changes", changeId, ".ws-change.json");
|
|
373
|
-
/** @type {any} */
|
|
374
|
-
let meta = null;
|
|
375
|
-
if (await deps.pathExists(metaPath)) {
|
|
376
|
-
try {
|
|
377
|
-
meta = JSON.parse(await deps.readText(metaPath));
|
|
378
|
-
} catch {
|
|
379
|
-
meta = null;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
if (!meta) {
|
|
383
|
-
const show = await deps.runCommand("git", ["show", `${changeBranch}:changes/${changeId}/.ws-change.json`], { cwd: gitRoot });
|
|
384
|
-
if (show.code === 0) {
|
|
385
|
-
try {
|
|
386
|
-
meta = JSON.parse(String(show.stdout || ""));
|
|
387
|
-
} catch {
|
|
388
|
-
meta = null;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
const inferred = meta && typeof meta === "object" ? String(meta.base_branch || "").trim() : "";
|
|
393
|
-
if (!inferred) {
|
|
394
|
-
throw new deps.UserError("Cannot infer base branch for finish.", {
|
|
395
|
-
details: "Hint: run from the target branch (e.g. main), or pass: `aiws change finish <id> --into <branch>`.",
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
into = inferred;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (into === changeBranch) {
|
|
403
|
-
throw new deps.UserError("change finish: target branch cannot be the change branch.", { details: `target=${into}` });
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
const intoRef = `refs/heads/${into}`;
|
|
407
|
-
const hasIntoBranch = await deps.runCommand("git", ["show-ref", "--verify", "--quiet", intoRef], { cwd: gitRoot }).then((result) => result.code === 0);
|
|
408
|
-
if (!hasIntoBranch) {
|
|
409
|
-
throw new deps.UserError("Target branch does not exist.", { details: `branch: ${into}` });
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return { changeId, changeBranch, changeBranchRef, into, cur };
|
|
413
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { runCommand } from "../exec.js";
|
|
2
|
-
import { UserError } from "../errors.js";
|
|
3
|
-
|
|
4
|
-
const CONFLICT_STATUS_RE = /^(DD|AU|UD|UA|DU|AA|UU)$/;
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @param {string} output
|
|
8
|
-
*/
|
|
9
|
-
export function parseGitStatusPorcelain(output) {
|
|
10
|
-
const lines = String(output || "")
|
|
11
|
-
.split(/\r?\n/)
|
|
12
|
-
.map((line) => line.trimEnd())
|
|
13
|
-
.filter(Boolean);
|
|
14
|
-
|
|
15
|
-
let staged = 0;
|
|
16
|
-
let unstaged = 0;
|
|
17
|
-
let untracked = 0;
|
|
18
|
-
let conflicted = 0;
|
|
19
|
-
|
|
20
|
-
for (const line of lines) {
|
|
21
|
-
const xy = line.slice(0, 2);
|
|
22
|
-
if (xy === "??") {
|
|
23
|
-
untracked += 1;
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
if (CONFLICT_STATUS_RE.test(xy)) {
|
|
27
|
-
conflicted += 1;
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (xy[0] && xy[0] !== " " && xy[0] !== "?") staged += 1;
|
|
31
|
-
if (xy[1] && xy[1] !== " ") unstaged += 1;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
clean: lines.length === 0,
|
|
36
|
-
total: lines.length,
|
|
37
|
-
staged,
|
|
38
|
-
unstaged,
|
|
39
|
-
untracked,
|
|
40
|
-
conflicted,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @param {string} gitRoot
|
|
46
|
-
*/
|
|
47
|
-
export async function collectGitStatus(gitRoot) {
|
|
48
|
-
const res = await runCommand("git", ["status", "--porcelain", "--untracked-files=all"], { cwd: gitRoot });
|
|
49
|
-
if (res.code !== 0) {
|
|
50
|
-
throw new UserError("Failed to inspect git status for change governance.", { details: res.stderr || res.stdout });
|
|
51
|
-
}
|
|
52
|
-
return parseGitStatusPorcelain(res.stdout || "");
|
|
53
|
-
}
|