@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.
Files changed (278) hide show
  1. package/bin/aiws.js +2 -2
  2. package/dist/aiws-package.d.ts +4 -0
  3. package/dist/aiws-package.js +11 -0
  4. package/dist/aiws-package.js.map +1 -0
  5. package/dist/aiws-paths.d.ts +48 -0
  6. package/dist/aiws-paths.js +129 -0
  7. package/dist/aiws-paths.js.map +1 -0
  8. package/dist/backup.d.ts +27 -0
  9. package/dist/backup.js +125 -0
  10. package/dist/backup.js.map +1 -0
  11. package/dist/cli.d.ts +1 -0
  12. package/dist/cli.js +773 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/codex-prompts.d.ts +17 -0
  15. package/dist/codex-prompts.js +49 -0
  16. package/dist/codex-prompts.js.map +1 -0
  17. package/dist/codex-skills.d.ts +17 -0
  18. package/dist/codex-skills.js +75 -0
  19. package/dist/codex-skills.js.map +1 -0
  20. package/dist/commands/change-advice.d.ts +25 -0
  21. package/dist/commands/change-advice.js +197 -0
  22. package/dist/commands/change-advice.js.map +1 -0
  23. package/dist/commands/change-evidence-command.d.ts +29 -0
  24. package/dist/commands/change-evidence-command.js +213 -0
  25. package/dist/commands/change-evidence-command.js.map +1 -0
  26. package/dist/commands/change-evidence-entry.d.ts +16 -0
  27. package/dist/commands/change-evidence-entry.js +35 -0
  28. package/dist/commands/change-evidence-entry.js.map +1 -0
  29. package/dist/commands/change-evidence.d.ts +108 -0
  30. package/dist/commands/change-evidence.js +341 -0
  31. package/dist/commands/change-evidence.js.map +1 -0
  32. package/dist/commands/change-finish.d.ts +71 -0
  33. package/dist/commands/change-finish.js +387 -0
  34. package/dist/commands/change-finish.js.map +1 -0
  35. package/dist/commands/change-git-status.d.ts +22 -0
  36. package/dist/commands/change-git-status.js +50 -0
  37. package/dist/commands/change-git-status.js.map +1 -0
  38. package/dist/commands/change-lifecycle-entry.d.ts +29 -0
  39. package/{src → dist}/commands/change-lifecycle-entry.js +21 -28
  40. package/dist/commands/change-lifecycle-entry.js.map +1 -0
  41. package/dist/commands/change-lifecycle.d.ts +56 -0
  42. package/dist/commands/change-lifecycle.js +211 -0
  43. package/dist/commands/change-lifecycle.js.map +1 -0
  44. package/dist/commands/change-metrics-command.d.ts +12 -0
  45. package/dist/commands/change-metrics-command.js +110 -0
  46. package/dist/commands/change-metrics-command.js.map +1 -0
  47. package/dist/commands/change-next-command.d.ts +18 -0
  48. package/dist/commands/change-next-command.js +138 -0
  49. package/dist/commands/change-next-command.js.map +1 -0
  50. package/dist/commands/change-review-gates.d.ts +107 -0
  51. package/dist/commands/change-review-gates.js +329 -0
  52. package/dist/commands/change-review-gates.js.map +1 -0
  53. package/dist/commands/change-scope-gate.d.ts +21 -0
  54. package/dist/commands/change-scope-gate.js +32 -0
  55. package/dist/commands/change-scope-gate.js.map +1 -0
  56. package/dist/commands/change-start.d.ts +30 -0
  57. package/dist/commands/change-start.js +91 -0
  58. package/dist/commands/change-start.js.map +1 -0
  59. package/dist/commands/change-state-command.d.ts +3 -0
  60. package/dist/commands/change-state-command.js +32 -0
  61. package/dist/commands/change-state-command.js.map +1 -0
  62. package/dist/commands/change-status-command.d.ts +13 -0
  63. package/dist/commands/change-status-command.js +126 -0
  64. package/dist/commands/change-status-command.js.map +1 -0
  65. package/dist/commands/change-status-context.d.ts +58 -0
  66. package/dist/commands/change-status-context.js +166 -0
  67. package/dist/commands/change-status-context.js.map +1 -0
  68. package/dist/commands/change-task-stats.d.ts +24 -0
  69. package/dist/commands/change-task-stats.js +66 -0
  70. package/dist/commands/change-task-stats.js.map +1 -0
  71. package/dist/commands/change-validate-entry.d.ts +17 -0
  72. package/dist/commands/change-validate-entry.js +44 -0
  73. package/dist/commands/change-validate-entry.js.map +1 -0
  74. package/dist/commands/change-validate.d.ts +32 -0
  75. package/dist/commands/change-validate.js +128 -0
  76. package/dist/commands/change-validate.js.map +1 -0
  77. package/dist/commands/change.d.ts +322 -0
  78. package/dist/commands/change.js +1731 -0
  79. package/dist/commands/change.js.map +1 -0
  80. package/dist/commands/codex-install-prompts.d.ts +4 -0
  81. package/dist/commands/codex-install-prompts.js +68 -0
  82. package/dist/commands/codex-install-prompts.js.map +1 -0
  83. package/dist/commands/codex-install-skills.d.ts +4 -0
  84. package/dist/commands/codex-install-skills.js +74 -0
  85. package/dist/commands/codex-install-skills.js.map +1 -0
  86. package/dist/commands/codex-status-prompts.d.ts +4 -0
  87. package/dist/commands/codex-status-prompts.js +44 -0
  88. package/dist/commands/codex-status-prompts.js.map +1 -0
  89. package/dist/commands/codex-status-skills.d.ts +4 -0
  90. package/dist/commands/codex-status-skills.js +54 -0
  91. package/dist/commands/codex-status-skills.js.map +1 -0
  92. package/dist/commands/codex-uninstall-prompts.d.ts +4 -0
  93. package/dist/commands/codex-uninstall-prompts.js +51 -0
  94. package/dist/commands/codex-uninstall-prompts.js.map +1 -0
  95. package/dist/commands/codex-uninstall-skills.d.ts +4 -0
  96. package/dist/commands/codex-uninstall-skills.js +59 -0
  97. package/dist/commands/codex-uninstall-skills.js.map +1 -0
  98. package/dist/commands/dashboard.d.ts +222 -0
  99. package/dist/commands/dashboard.js +193 -0
  100. package/dist/commands/dashboard.js.map +1 -0
  101. package/dist/commands/hooks-install.d.ts +4 -0
  102. package/dist/commands/hooks-install.js +90 -0
  103. package/dist/commands/hooks-install.js.map +1 -0
  104. package/dist/commands/hooks-status.d.ts +4 -0
  105. package/dist/commands/hooks-status.js +89 -0
  106. package/dist/commands/hooks-status.js.map +1 -0
  107. package/dist/commands/init.d.ts +4 -0
  108. package/dist/commands/init.js +100 -0
  109. package/dist/commands/init.js.map +1 -0
  110. package/dist/commands/opencode-auto.d.ts +11 -0
  111. package/dist/commands/opencode-auto.js +158 -0
  112. package/dist/commands/opencode-auto.js.map +1 -0
  113. package/dist/commands/opencode-status.d.ts +4 -0
  114. package/dist/commands/opencode-status.js +75 -0
  115. package/dist/commands/opencode-status.js.map +1 -0
  116. package/dist/commands/opencode-supervise.d.ts +24 -0
  117. package/dist/commands/opencode-supervise.js +188 -0
  118. package/dist/commands/opencode-supervise.js.map +1 -0
  119. package/dist/commands/rollback.d.ts +4 -0
  120. package/dist/commands/rollback.js +12 -0
  121. package/dist/commands/rollback.js.map +1 -0
  122. package/dist/commands/update.d.ts +4 -0
  123. package/dist/commands/update.js +212 -0
  124. package/dist/commands/update.js.map +1 -0
  125. package/dist/commands/validate.d.ts +4 -0
  126. package/dist/commands/validate.js +222 -0
  127. package/dist/commands/validate.js.map +1 -0
  128. package/dist/commands/ws-commit.d.ts +5 -0
  129. package/dist/commands/ws-commit.js +39 -0
  130. package/dist/commands/ws-commit.js.map +1 -0
  131. package/dist/commands/ws-deliver.d.ts +5 -0
  132. package/dist/commands/ws-deliver.js +59 -0
  133. package/dist/commands/ws-deliver.js.map +1 -0
  134. package/dist/commands/ws-finish.d.ts +5 -0
  135. package/dist/commands/ws-finish.js +142 -0
  136. package/dist/commands/ws-finish.js.map +1 -0
  137. package/dist/commands/ws-handoff.d.ts +5 -0
  138. package/dist/commands/ws-handoff.js +35 -0
  139. package/dist/commands/ws-handoff.js.map +1 -0
  140. package/dist/commands/ws-migrate.d.ts +5 -0
  141. package/dist/commands/ws-migrate.js +41 -0
  142. package/dist/commands/ws-migrate.js.map +1 -0
  143. package/dist/commands/ws-plan-verify.d.ts +5 -0
  144. package/dist/commands/ws-plan-verify.js +38 -0
  145. package/dist/commands/ws-plan-verify.js.map +1 -0
  146. package/dist/commands/ws-pull.d.ts +5 -0
  147. package/dist/commands/ws-pull.js +26 -0
  148. package/dist/commands/ws-pull.js.map +1 -0
  149. package/dist/commands/ws-push.d.ts +5 -0
  150. package/dist/commands/ws-push.js +68 -0
  151. package/dist/commands/ws-push.js.map +1 -0
  152. package/dist/commands/ws-submodule-setup.d.ts +5 -0
  153. package/dist/commands/ws-submodule-setup.js +55 -0
  154. package/dist/commands/ws-submodule-setup.js.map +1 -0
  155. package/dist/commands/ws-verify-before-complete.d.ts +5 -0
  156. package/dist/commands/ws-verify-before-complete.js +35 -0
  157. package/dist/commands/ws-verify-before-complete.js.map +1 -0
  158. package/dist/dashboard/app.d.ts +1 -0
  159. package/dist/dashboard/app.js +370 -0
  160. package/dist/dashboard/app.js.map +1 -0
  161. package/dist/delegation/cli.d.ts +6 -0
  162. package/dist/delegation/cli.js +14 -0
  163. package/dist/delegation/cli.js.map +1 -0
  164. package/dist/delegation/index.d.ts +35 -0
  165. package/dist/delegation/index.js +118 -0
  166. package/dist/delegation/index.js.map +1 -0
  167. package/dist/errors.d.ts +8 -0
  168. package/dist/errors.js +11 -0
  169. package/dist/errors.js.map +1 -0
  170. package/dist/exec.d.ts +9 -0
  171. package/dist/exec.js +22 -0
  172. package/dist/exec.js.map +1 -0
  173. package/dist/fs.d.ts +9 -0
  174. package/dist/fs.js +62 -0
  175. package/dist/fs.js.map +1 -0
  176. package/dist/governance.d.ts +10 -0
  177. package/dist/governance.js +135 -0
  178. package/dist/governance.js.map +1 -0
  179. package/dist/hash.d.ts +3 -0
  180. package/dist/hash.js +13 -0
  181. package/dist/hash.js.map +1 -0
  182. package/dist/hooks/deploy-claude.d.ts +13 -0
  183. package/dist/hooks/deploy-claude.js +67 -0
  184. package/dist/hooks/deploy-claude.js.map +1 -0
  185. package/dist/journal.d.ts +22 -0
  186. package/dist/journal.js +112 -0
  187. package/dist/journal.js.map +1 -0
  188. package/dist/json-schema-lite.d.ts +22 -0
  189. package/dist/json-schema-lite.js +148 -0
  190. package/dist/json-schema-lite.js.map +1 -0
  191. package/dist/managed-blocks.d.ts +24 -0
  192. package/dist/managed-blocks.js +84 -0
  193. package/dist/managed-blocks.js.map +1 -0
  194. package/dist/manifest.d.ts +24 -0
  195. package/dist/manifest.js +145 -0
  196. package/dist/manifest.js.map +1 -0
  197. package/dist/opencode-env.d.ts +62 -0
  198. package/dist/opencode-env.js +178 -0
  199. package/dist/opencode-env.js.map +1 -0
  200. package/dist/path-utils.d.ts +2 -0
  201. package/dist/path-utils.js +8 -0
  202. package/dist/path-utils.js.map +1 -0
  203. package/dist/spec.d.ts +17 -0
  204. package/dist/spec.js +174 -0
  205. package/dist/spec.js.map +1 -0
  206. package/dist/template.d.ts +20 -0
  207. package/dist/template.js +123 -0
  208. package/dist/template.js.map +1 -0
  209. package/dist/workspace.d.ts +4 -0
  210. package/dist/workspace.js +19 -0
  211. package/dist/workspace.js.map +1 -0
  212. package/package.json +11 -3
  213. package/src/aiws-package.js +0 -15
  214. package/src/backup.js +0 -149
  215. package/src/cli.js +0 -760
  216. package/src/codex-prompts.js +0 -74
  217. package/src/codex-skills.js +0 -111
  218. package/src/commands/change-advice.js +0 -181
  219. package/src/commands/change-evidence-command.js +0 -242
  220. package/src/commands/change-evidence-entry.js +0 -40
  221. package/src/commands/change-evidence.js +0 -340
  222. package/src/commands/change-finish.js +0 -413
  223. package/src/commands/change-git-status.js +0 -53
  224. package/src/commands/change-lifecycle.js +0 -236
  225. package/src/commands/change-metrics-command.js +0 -110
  226. package/src/commands/change-next-command.js +0 -133
  227. package/src/commands/change-review-gates.js +0 -331
  228. package/src/commands/change-scope-gate.js +0 -34
  229. package/src/commands/change-start.js +0 -85
  230. package/src/commands/change-state-command.js +0 -40
  231. package/src/commands/change-status-command.js +0 -125
  232. package/src/commands/change-status-context.js +0 -173
  233. package/src/commands/change-task-stats.js +0 -64
  234. package/src/commands/change-validate-entry.js +0 -45
  235. package/src/commands/change-validate.js +0 -127
  236. package/src/commands/change.js +0 -1829
  237. package/src/commands/codex-install-prompts.js +0 -68
  238. package/src/commands/codex-install-skills.js +0 -74
  239. package/src/commands/codex-status-prompts.js +0 -55
  240. package/src/commands/codex-status-skills.js +0 -63
  241. package/src/commands/codex-uninstall-prompts.js +0 -55
  242. package/src/commands/codex-uninstall-skills.js +0 -62
  243. package/src/commands/dashboard.js +0 -205
  244. package/src/commands/hooks-install.js +0 -92
  245. package/src/commands/hooks-status.js +0 -95
  246. package/src/commands/init.js +0 -101
  247. package/src/commands/opencode-auto.js +0 -167
  248. package/src/commands/opencode-status.js +0 -103
  249. package/src/commands/opencode-supervise.js +0 -200
  250. package/src/commands/rollback.js +0 -13
  251. package/src/commands/update.js +0 -145
  252. package/src/commands/validate.js +0 -228
  253. package/src/commands/ws-commit.js +0 -44
  254. package/src/commands/ws-deliver.js +0 -67
  255. package/src/commands/ws-finish.js +0 -83
  256. package/src/commands/ws-handoff.js +0 -40
  257. package/src/commands/ws-migrate.js +0 -41
  258. package/src/commands/ws-plan-verify.js +0 -44
  259. package/src/commands/ws-pull.js +0 -30
  260. package/src/commands/ws-push.js +0 -71
  261. package/src/commands/ws-submodule-setup.js +0 -65
  262. package/src/commands/ws-verify-before-complete.js +0 -39
  263. package/src/dashboard/app.js +0 -371
  264. package/src/errors.js +0 -15
  265. package/src/exec.js +0 -34
  266. package/src/fs.js +0 -91
  267. package/src/governance.js +0 -159
  268. package/src/hash.js +0 -25
  269. package/src/json-schema-lite.js +0 -164
  270. package/src/managed-blocks.js +0 -131
  271. package/src/manifest.js +0 -177
  272. package/src/opencode-env.js +0 -208
  273. package/src/path-utils.js +0 -20
  274. package/src/spec.js +0 -195
  275. package/src/template.js +0 -187
  276. package/src/workspace.js +0 -23
  277. /package/{src → dist}/dashboard/index.html +0 -0
  278. /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
- }