@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
package/src/commands/update.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { loadTemplate } from "../spec.js";
|
|
4
|
-
import { loadAiwsPackage } from "../aiws-package.js";
|
|
5
|
-
import { resolveWorkspaceRoot } from "../workspace.js";
|
|
6
|
-
import { readText, pathExists } from "../fs.js";
|
|
7
|
-
import { UserError } from "../errors.js";
|
|
8
|
-
import { normalizeNewlines } from "../hash.js";
|
|
9
|
-
import { findManagedBlock } from "../managed-blocks.js";
|
|
10
|
-
import { BackupSession } from "../backup.js";
|
|
11
|
-
import { normalizeRel, joinRel } from "../path-utils.js";
|
|
12
|
-
import { copyTemplateFileToWorkspace, applyManagedBlocksFromTemplate } from "../template.js";
|
|
13
|
-
import { detectLegacyWorkspaceTemplateId, writeWorkspaceManifest } from "../manifest.js";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {{ targetPath: string }} options
|
|
17
|
-
*/
|
|
18
|
-
export async function updateCommand(options) {
|
|
19
|
-
const workspaceRoot = await resolveWorkspaceRoot(options.targetPath, { create: false });
|
|
20
|
-
const aiws = await loadAiwsPackage();
|
|
21
|
-
|
|
22
|
-
const manifestPath = path.join(workspaceRoot, ".aiws", "manifest.json");
|
|
23
|
-
const hasManifest = await pathExists(manifestPath);
|
|
24
|
-
const legacyTemplateId = !hasManifest ? await detectLegacyWorkspaceTemplateId(workspaceRoot) : null;
|
|
25
|
-
const legacyMigration = !hasManifest && Boolean(legacyTemplateId);
|
|
26
|
-
if (!hasManifest && !legacyTemplateId) {
|
|
27
|
-
throw new UserError("Missing .aiws/manifest.json. Run `aiws init` first.");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const stored = hasManifest
|
|
31
|
-
? JSON.parse(await readText(manifestPath))
|
|
32
|
-
: { template_id: legacyTemplateId, installed_at: "", tools: [], managed: [] };
|
|
33
|
-
const templateId = String(stored.template_id || legacyTemplateId || "workspace");
|
|
34
|
-
const tpl = await loadTemplate(templateId);
|
|
35
|
-
const storedManagedByPath = new Map(
|
|
36
|
-
(Array.isArray(stored.managed) ? stored.managed : [])
|
|
37
|
-
.filter((entry) => entry && typeof entry === "object")
|
|
38
|
-
.map((entry) => [normalizeRel(String(entry.path || "")), entry]),
|
|
39
|
-
);
|
|
40
|
-
const defaults = tpl.manifest.defaults || {};
|
|
41
|
-
const defaultTools = Array.isArray(defaults.tools) ? defaults.tools.map(String) : ["claude", "opencode", "codex"];
|
|
42
|
-
const allowedTools = new Set(defaultTools);
|
|
43
|
-
|
|
44
|
-
const update = tpl.manifest.update || {};
|
|
45
|
-
const replaceFiles = (update.replace_file || []).map(normalizeRel);
|
|
46
|
-
const managedBlocks = update.managed_blocks && typeof update.managed_blocks === "object" ? update.managed_blocks : {};
|
|
47
|
-
|
|
48
|
-
// Preflight: existing managed-block files must be intact.
|
|
49
|
-
// Missing files that were not previously managed are treated as newly introduced
|
|
50
|
-
// template surfaces and will be created during applyManagedBlocksFromTemplate.
|
|
51
|
-
for (const [fileRelRaw, blockIdsRaw] of Object.entries(managedBlocks)) {
|
|
52
|
-
const fileRel = normalizeRel(fileRelRaw);
|
|
53
|
-
const abs = joinRel(workspaceRoot, fileRel);
|
|
54
|
-
const hadManagedBefore = storedManagedByPath.has(fileRel);
|
|
55
|
-
if (!(await pathExists(abs))) {
|
|
56
|
-
if (hadManagedBefore) {
|
|
57
|
-
throw new UserError("Managed block file missing; run `aiws init` or restore the file.", { details: `Missing: ${fileRel}` });
|
|
58
|
-
}
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
const text = normalizeNewlines(await readText(abs));
|
|
62
|
-
const ids = Array.isArray(blockIdsRaw) ? blockIdsRaw.map(String) : [];
|
|
63
|
-
for (const id of ids) {
|
|
64
|
-
if (!findManagedBlock(text, id)) {
|
|
65
|
-
if (legacyMigration) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
throw new UserError("Managed block markers are missing or broken; refusing to update.", {
|
|
69
|
-
details: `File: ${fileRel}\nBlock: ${id}\nHint: re-run \`aiws init\` or repair markers manually.`,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const removeFiles = (update.remove || []).map(normalizeRel);
|
|
76
|
-
|
|
77
|
-
const backup = new BackupSession({ workspaceRoot, operation: "update" });
|
|
78
|
-
// Backup every file we might touch (including missing, for rollback deletions).
|
|
79
|
-
await backup.recordFile(".aiws/manifest.json", { recordMissing: true });
|
|
80
|
-
for (const f of replaceFiles) await backup.recordFile(f, { recordMissing: true });
|
|
81
|
-
for (const f of Object.keys(managedBlocks)) await backup.recordFile(f, { recordMissing: true });
|
|
82
|
-
for (const f of removeFiles) await backup.recordFile(f);
|
|
83
|
-
await backup.finalize({ extra: { template_id: tpl.templateId } });
|
|
84
|
-
|
|
85
|
-
// Remove deprecated files (backed up above; restorable via `aiws rollback`).
|
|
86
|
-
let removedCount = 0;
|
|
87
|
-
for (const rel of removeFiles) {
|
|
88
|
-
if (!rel) continue;
|
|
89
|
-
const abs = joinRel(workspaceRoot, rel);
|
|
90
|
-
if (await pathExists(abs)) {
|
|
91
|
-
await fs.rm(abs, { force: true });
|
|
92
|
-
removedCount++;
|
|
93
|
-
// Remove empty parent directory (safe: rmdir fails on non-empty).
|
|
94
|
-
try { await fs.rmdir(path.dirname(abs)); } catch { /* non-empty or gone */ }
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (removedCount > 0) {
|
|
98
|
-
console.log(` removed ${removedCount} deprecated file(s)`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Replace files (except manifest, generated later).
|
|
102
|
-
for (const rel of replaceFiles) {
|
|
103
|
-
if (!rel || rel === ".aiws/manifest.json") continue;
|
|
104
|
-
await copyTemplateFileToWorkspace({
|
|
105
|
-
templateDir: tpl.templateDir,
|
|
106
|
-
workspaceRoot,
|
|
107
|
-
relPosix: rel,
|
|
108
|
-
chmod: rel.startsWith(".githooks/") ? 0o755 : undefined,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Update managed blocks only (do not insert missing blocks).
|
|
113
|
-
for (const [fileRelRaw, blockIdsRaw] of Object.entries(managedBlocks)) {
|
|
114
|
-
const fileRel = normalizeRel(fileRelRaw);
|
|
115
|
-
const ids = Array.isArray(blockIdsRaw) ? blockIdsRaw.map(String) : [];
|
|
116
|
-
await applyManagedBlocksFromTemplate({
|
|
117
|
-
templateDir: tpl.templateDir,
|
|
118
|
-
workspaceRoot,
|
|
119
|
-
fileRel,
|
|
120
|
-
blockIds: ids,
|
|
121
|
-
insertIfMissing: legacyMigration,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const now = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
126
|
-
const installedAt = String(stored.installed_at || now);
|
|
127
|
-
const storedTools = Array.isArray(stored.tools) ? stored.tools.map(String).filter((tool) => allowedTools.has(tool)) : [];
|
|
128
|
-
const tools = storedTools.length > 0 ? storedTools : defaultTools;
|
|
129
|
-
|
|
130
|
-
await writeWorkspaceManifest({
|
|
131
|
-
workspaceRoot,
|
|
132
|
-
templateId: tpl.templateId,
|
|
133
|
-
specVersion: tpl.specVersion,
|
|
134
|
-
aiwsVersion: aiws.version,
|
|
135
|
-
installedAt,
|
|
136
|
-
updatedAt: now,
|
|
137
|
-
tools,
|
|
138
|
-
templateManifest: tpl.manifest,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
if (legacyMigration) {
|
|
142
|
-
console.log(` migrated legacy AIWS workspace: generated .aiws/manifest.json for template=${legacyTemplateId}`);
|
|
143
|
-
}
|
|
144
|
-
console.log(`✓ aiws update: ${workspaceRoot}`);
|
|
145
|
-
}
|
package/src/commands/validate.js
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { loadTemplate } from "../spec.js";
|
|
3
|
-
import { resolveWorkspaceRoot } from "../workspace.js";
|
|
4
|
-
import { ensureDir, pathExists, readText, writeText } from "../fs.js";
|
|
5
|
-
import { UserError } from "../errors.js";
|
|
6
|
-
import { detectLegacyWorkspaceTemplateId, validateDrift } from "../manifest.js";
|
|
7
|
-
import { runCommand } from "../exec.js";
|
|
8
|
-
import { expandManifestEntries } from "../template.js";
|
|
9
|
-
import { loadAiwsPackage } from "../aiws-package.js";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Enforce deterministic submodule branch policy:
|
|
13
|
-
* - If `.gitmodules` exists and declares submodules, each submodule must declare `submodule.<name>.branch`.
|
|
14
|
-
* - This avoids guessing which branch to attach/push for workflows like ws-pull/ws-finish.
|
|
15
|
-
*
|
|
16
|
-
* @param {string} workspaceRoot
|
|
17
|
-
*/
|
|
18
|
-
async function validateSubmoduleBranchPolicy(workspaceRoot) {
|
|
19
|
-
const gitmodules = path.join(workspaceRoot, ".gitmodules");
|
|
20
|
-
if (!(await pathExists(gitmodules))) return;
|
|
21
|
-
|
|
22
|
-
const list = await runCommand("git", ["config", "--file", ".gitmodules", "--get-regexp", "^submodule\\..*\\.path$"], {
|
|
23
|
-
cwd: workspaceRoot,
|
|
24
|
-
});
|
|
25
|
-
if (list.code !== 0) {
|
|
26
|
-
// `.gitmodules` exists but no submodule path entries: treat as ok.
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** @type {Array<{ name: string, path: string }>} */
|
|
31
|
-
const subs = [];
|
|
32
|
-
for (const line of String(list.stdout || "").split("\n")) {
|
|
33
|
-
const t = line.trim();
|
|
34
|
-
if (!t) continue;
|
|
35
|
-
const idx = t.indexOf(" ");
|
|
36
|
-
if (idx <= 0) continue;
|
|
37
|
-
const key = t.slice(0, idx).trim();
|
|
38
|
-
const subPath = t.slice(idx + 1).trim();
|
|
39
|
-
const m = key.match(/^submodule\.(.+)\.path$/);
|
|
40
|
-
if (!m) continue;
|
|
41
|
-
const name = m[1] || "";
|
|
42
|
-
if (!name || !subPath) continue;
|
|
43
|
-
subs.push({ name, path: subPath });
|
|
44
|
-
}
|
|
45
|
-
if (subs.length === 0) return;
|
|
46
|
-
|
|
47
|
-
/** @type {Array<{ name: string, path: string }>} */
|
|
48
|
-
const missing = [];
|
|
49
|
-
for (const s of subs) {
|
|
50
|
-
const br = await runCommand("git", ["config", "--file", ".gitmodules", "--get", `submodule.${s.name}.branch`], {
|
|
51
|
-
cwd: workspaceRoot,
|
|
52
|
-
});
|
|
53
|
-
if (br.code !== 0 || !String(br.stdout || "").trim()) {
|
|
54
|
-
missing.push(s);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (missing.length > 0) {
|
|
59
|
-
const lines = missing.map((m) => `- ${m.name} (${m.path}): missing submodule.${m.name}.branch`);
|
|
60
|
-
const hints = missing
|
|
61
|
-
.slice(0, 8)
|
|
62
|
-
.map((m) => `git submodule set-branch --branch main ${m.path}`)
|
|
63
|
-
.join("\n");
|
|
64
|
-
throw new UserError("Submodule branch policy failed: missing `.gitmodules` branch config.", {
|
|
65
|
-
details:
|
|
66
|
-
`${lines.join("\n")}\n\n` +
|
|
67
|
-
"Fix:\n" +
|
|
68
|
-
"- Run `ws-submodule-setup` (recommended), or set per submodule:\n" +
|
|
69
|
-
`${hints}\n\n` +
|
|
70
|
-
"Then commit `.gitmodules` in the superproject.",
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @param {string | undefined} v
|
|
77
|
-
*/
|
|
78
|
-
function envFlag(v) {
|
|
79
|
-
const s = String(v || "")
|
|
80
|
-
.trim()
|
|
81
|
-
.toLowerCase();
|
|
82
|
-
return s === "1" || s === "true" || s === "yes" || s === "y" || s === "on";
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* @returns {string} e.g. 20260204-140026Z
|
|
87
|
-
*/
|
|
88
|
-
function nowStampUtc() {
|
|
89
|
-
const d = new Date();
|
|
90
|
-
const y = String(d.getUTCFullYear());
|
|
91
|
-
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
92
|
-
const day = String(d.getUTCDate()).padStart(2, "0");
|
|
93
|
-
const hh = String(d.getUTCHours()).padStart(2, "0");
|
|
94
|
-
const mm = String(d.getUTCMinutes()).padStart(2, "0");
|
|
95
|
-
const ss = String(d.getUTCSeconds()).padStart(2, "0");
|
|
96
|
-
const ms = String(d.getUTCMilliseconds()).padStart(3, "0");
|
|
97
|
-
return `${y}${m}${day}-${hh}${mm}${ss}${ms}Z`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* @returns {string} e.g. 2026-01-28T14:00:26Z
|
|
102
|
-
*/
|
|
103
|
-
function nowIsoUtc() {
|
|
104
|
-
return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @param {any} error
|
|
109
|
-
* @returns {{ name: string, message: string, details?: string } | null}
|
|
110
|
-
*/
|
|
111
|
-
function serializeError(error) {
|
|
112
|
-
if (!error) return null;
|
|
113
|
-
if (error instanceof UserError) {
|
|
114
|
-
return { name: error.name, message: error.message, details: error.details };
|
|
115
|
-
}
|
|
116
|
-
if (error instanceof Error) {
|
|
117
|
-
return { name: error.name, message: error.message };
|
|
118
|
-
}
|
|
119
|
-
return { name: "Error", message: String(error) };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* @param {{ targetPath: string, stamp?: boolean }} options
|
|
124
|
-
*/
|
|
125
|
-
export async function validateCommand(options) {
|
|
126
|
-
const stampEnabled = options.stamp === true || envFlag(process.env.AIWS_VALIDATE_STAMP);
|
|
127
|
-
const startedAt = nowIsoUtc();
|
|
128
|
-
const startedTs = Math.floor(Date.now() / 1000);
|
|
129
|
-
let workspaceRoot = "";
|
|
130
|
-
let templateId = "";
|
|
131
|
-
let specVersion = "";
|
|
132
|
-
let aiwsVersion = "";
|
|
133
|
-
let status = /** @type {"ok" | "error"} */ ("ok");
|
|
134
|
-
let exitCode = 0;
|
|
135
|
-
let err = null;
|
|
136
|
-
let stampPath = "";
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
workspaceRoot = await resolveWorkspaceRoot(options.targetPath, { create: false });
|
|
140
|
-
const aiws = await loadAiwsPackage();
|
|
141
|
-
aiwsVersion = aiws.version;
|
|
142
|
-
|
|
143
|
-
const manifestPath = path.join(workspaceRoot, ".aiws", "manifest.json");
|
|
144
|
-
if (!(await pathExists(manifestPath))) {
|
|
145
|
-
const legacyTemplateId = await detectLegacyWorkspaceTemplateId(workspaceRoot);
|
|
146
|
-
if (legacyTemplateId) {
|
|
147
|
-
throw new UserError("Missing .aiws/manifest.json in a legacy AIWS workspace.", {
|
|
148
|
-
details: "This workspace looks like an older AIWS install. Run `aiws update .` once to generate the manifest, then rerun `aiws validate`.",
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
throw new UserError("Missing .aiws/manifest.json. Run `aiws init` first.");
|
|
152
|
-
}
|
|
153
|
-
const stored = JSON.parse(await readText(manifestPath));
|
|
154
|
-
templateId = String(stored.template_id || "workspace");
|
|
155
|
-
const tpl = await loadTemplate(templateId);
|
|
156
|
-
specVersion = tpl.specVersion;
|
|
157
|
-
|
|
158
|
-
// Required files/dirs.
|
|
159
|
-
const required = await expandManifestEntries(tpl.templateDir, tpl.manifest.required || []);
|
|
160
|
-
/** @type {string[]} */
|
|
161
|
-
const missing = [];
|
|
162
|
-
for (const rel of required) {
|
|
163
|
-
const abs = path.join(workspaceRoot, ...String(rel).split("/"));
|
|
164
|
-
if (!(await pathExists(abs))) missing.push(rel);
|
|
165
|
-
}
|
|
166
|
-
if (missing.length > 0) {
|
|
167
|
-
throw new UserError("Missing required files.", { details: missing.join("\n") });
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Drift detection.
|
|
171
|
-
await validateDrift({ workspaceRoot, storedManifest: stored, templateManifest: tpl.manifest });
|
|
172
|
-
|
|
173
|
-
// Submodule branch policy (deterministic workflow; avoids guessing target branches).
|
|
174
|
-
await validateSubmoduleBranchPolicy(workspaceRoot);
|
|
175
|
-
|
|
176
|
-
// python3 gate.
|
|
177
|
-
const py = await runCommand("python3", ["--version"], { cwd: workspaceRoot });
|
|
178
|
-
if (py.code !== 0) {
|
|
179
|
-
throw new UserError("python3 is required for validate.", { details: py.stderr || py.stdout });
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Required gate scripts.
|
|
183
|
-
const wsCheck = await runCommand("python3", ["tools/ws_change_check.py", "--strict"], { cwd: workspaceRoot });
|
|
184
|
-
if (wsCheck.code !== 0) {
|
|
185
|
-
throw new UserError("change gate failed.", { details: wsCheck.stderr || wsCheck.stdout });
|
|
186
|
-
}
|
|
187
|
-
const reqCheck = await runCommand("python3", ["tools/requirements_contract.py", "validate"], { cwd: workspaceRoot });
|
|
188
|
-
if (reqCheck.code !== 0) {
|
|
189
|
-
throw new UserError("requirements contract gate failed.", { details: reqCheck.stderr || reqCheck.stdout });
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
console.log(`✓ aiws validate: ${workspaceRoot}`);
|
|
193
|
-
} catch (error) {
|
|
194
|
-
status = "error";
|
|
195
|
-
err = serializeError(error);
|
|
196
|
-
if (error instanceof UserError) {
|
|
197
|
-
exitCode = error.exitCode;
|
|
198
|
-
} else {
|
|
199
|
-
exitCode = 1;
|
|
200
|
-
}
|
|
201
|
-
throw error;
|
|
202
|
-
} finally {
|
|
203
|
-
if (stampEnabled && workspaceRoot) {
|
|
204
|
-
try {
|
|
205
|
-
const stampDir = path.join(workspaceRoot, ".agentdocs", "tmp", "aiws-validate");
|
|
206
|
-
await ensureDir(stampDir);
|
|
207
|
-
stampPath = path.join(stampDir, `${nowStampUtc()}.json`);
|
|
208
|
-
const stamp = {
|
|
209
|
-
timestamp: startedTs,
|
|
210
|
-
ws_root: workspaceRoot,
|
|
211
|
-
template_id: templateId || null,
|
|
212
|
-
aiws_version: aiwsVersion || null,
|
|
213
|
-
spec_version: specVersion || null,
|
|
214
|
-
started_at: startedAt,
|
|
215
|
-
finished_at: nowIsoUtc(),
|
|
216
|
-
status,
|
|
217
|
-
exit_code: status === "ok" ? 0 : exitCode || 1,
|
|
218
|
-
error: err,
|
|
219
|
-
note: "aiws validate stamp; does not include full stdout/stderr; do not put secrets in validation output.",
|
|
220
|
-
};
|
|
221
|
-
await writeText(stampPath, JSON.stringify(stamp, null, 2) + "\n");
|
|
222
|
-
console.log(`stamp: ${path.relative(workspaceRoot, stampPath)}`);
|
|
223
|
-
} catch (e) {
|
|
224
|
-
console.error(`warn: failed to write validate stamp: ${e instanceof Error ? e.message : String(e)}`);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { UserError } from "../errors.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* aiws commit — validate + commit
|
|
7
|
-
* @param {{ cwd?: string, message?: string }} opts
|
|
8
|
-
*/
|
|
9
|
-
export async function wsCommitCommand({ cwd = ".", message } = {}) {
|
|
10
|
-
checkClean(cwd);
|
|
11
|
-
|
|
12
|
-
// Check review artifacts
|
|
13
|
-
const reviewsExist = existsSync(`${cwd}/changes/*/review/spec-review.md`) ||
|
|
14
|
-
existsSync(`${cwd}/changes/*/review/quality-review.md`);
|
|
15
|
-
if (!reviewsExist) {
|
|
16
|
-
console.log("warn: no spec-review or quality-review found");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Run validate
|
|
20
|
-
try {
|
|
21
|
-
execSync("npx -y @aipper/aiws validate . 2>/dev/null || aiws validate . 2>/dev/null || true", { cwd, stdio: "inherit" });
|
|
22
|
-
} catch { /* validate is advisory, not blocking */ }
|
|
23
|
-
|
|
24
|
-
// Get staged changes
|
|
25
|
-
const staged = execSync("git diff --cached --name-only", { cwd, encoding: "utf8" }).trim();
|
|
26
|
-
if (!staged) {
|
|
27
|
-
console.log("info: nothing staged. Use git add first.");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Commit
|
|
31
|
-
if (!message) {
|
|
32
|
-
throw new UserError("commit requires --message (use -m or --message)", { exitCode: 2 });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, { cwd, stdio: "inherit" });
|
|
36
|
-
console.log("ok: commit created");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function checkClean(cwd) {
|
|
40
|
-
const out = execSync("git status --porcelain", { cwd, encoding: "utf8" }).trim();
|
|
41
|
-
if (!out) {
|
|
42
|
-
throw new UserError("Nothing to commit. Working tree clean.", { exitCode: 2 });
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import { UserError } from "../errors.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* aiws deliver — submodule + superproject commit, then finish
|
|
7
|
-
* @param {{ cwd?: string, message?: string, changeId?: string }} opts
|
|
8
|
-
*/
|
|
9
|
-
export async function wsDeliverCommand({ cwd = ".", message, changeId } = {}) {
|
|
10
|
-
if (!changeId) {
|
|
11
|
-
changeId = detectChangeBranch(cwd);
|
|
12
|
-
}
|
|
13
|
-
if (!changeId) {
|
|
14
|
-
throw new UserError("Could not detect change ID.", { exitCode: 2 });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (!message) {
|
|
18
|
-
throw new UserError("deliver requires --message for the superproject commit", { exitCode: 2 });
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
console.log("info: starting delivery...");
|
|
22
|
-
|
|
23
|
-
// Commit submodules first
|
|
24
|
-
if (existsSync(`${cwd}/.gitmodules`)) {
|
|
25
|
-
const out = execSync("git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'", {
|
|
26
|
-
cwd, encoding: "utf8",
|
|
27
|
-
}).trim();
|
|
28
|
-
|
|
29
|
-
for (const line of out.split("\n")) {
|
|
30
|
-
const parts = line.trim().split(/\s+/);
|
|
31
|
-
if (parts.length < 2) continue;
|
|
32
|
-
const subPath = parts[1];
|
|
33
|
-
|
|
34
|
-
const status = execSync(`git -C "${subPath}" status --porcelain`, { cwd, encoding: "utf8" }).trim();
|
|
35
|
-
if (status) {
|
|
36
|
-
console.log(`info: submodule ${subPath} has changes. Committing...`);
|
|
37
|
-
execSync(`git -C "${subPath}" add -A`, { cwd, stdio: "inherit" });
|
|
38
|
-
execSync(`git -C "${subPath}" commit -m "${message}"`, { cwd, stdio: "inherit" });
|
|
39
|
-
} else {
|
|
40
|
-
console.log(`info: submodule ${subPath} has no changes, skipping`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Commit superproject
|
|
46
|
-
const superStatus = execSync("git status --porcelain", { cwd, encoding: "utf8" }).trim();
|
|
47
|
-
if (superStatus) {
|
|
48
|
-
console.log("info: committing superproject...");
|
|
49
|
-
execSync("git add -A", { cwd, stdio: "inherit" });
|
|
50
|
-
execSync(`git commit -m "${message}"`, { cwd, stdio: "inherit" });
|
|
51
|
-
} else {
|
|
52
|
-
console.log("info: superproject has no changes");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Finish
|
|
56
|
-
await import("./ws-finish.js").then(m =>
|
|
57
|
-
m.wsFinishCommand({ cwd, changeId, push: true })
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
console.log("ok: deliver complete");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function detectChangeBranch(cwd) {
|
|
64
|
-
const branch = execSync("git branch --show-current", { cwd, encoding: "utf8" }).trim();
|
|
65
|
-
const match = branch.match(/^(?:change|changes|ws|ws-change)\/(.+)$/);
|
|
66
|
-
return match ? match[1] : null;
|
|
67
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { UserError } from "../errors.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* aiws finish — ff-merge + push + archive
|
|
7
|
-
* @param {{ cwd?: string, changeId?: string, into?: string, push?: boolean, remote?: string }} opts
|
|
8
|
-
*/
|
|
9
|
-
export async function wsFinishCommand({ cwd = ".", changeId, into, push = false, remote = "origin" } = {}) {
|
|
10
|
-
checkClean(cwd);
|
|
11
|
-
|
|
12
|
-
if (!changeId) {
|
|
13
|
-
changeId = detectChangeBranch(cwd);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (!changeId) {
|
|
17
|
-
throw new UserError("Could not detect change ID. Provide as argument or be on a change branch.", { exitCode: 2 });
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const targetBranch = into || "main";
|
|
21
|
-
|
|
22
|
-
// Validate before complete
|
|
23
|
-
try {
|
|
24
|
-
execSync(`npx -y @aipper/aiws change validate ${changeId} --strict 2>/dev/null`, { cwd, stdio: "inherit" });
|
|
25
|
-
} catch {
|
|
26
|
-
throw new UserError(`Validation failed for ${changeId}. Fix issues first.`, { exitCode: 1 });
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// FF merge
|
|
30
|
-
const changeBranch = `change/${changeId}`;
|
|
31
|
-
const branchExists = execSync(`git branch --list '${changeBranch}'`, { cwd, encoding: "utf8" }).trim();
|
|
32
|
-
if (branchExists) {
|
|
33
|
-
console.log(`info: ff-merging ${changeBranch} → ${targetBranch}...`);
|
|
34
|
-
execSync(`git checkout ${targetBranch}`, { cwd, stdio: "inherit" });
|
|
35
|
-
execSync(`git merge --ff-only ${changeBranch}`, { cwd, stdio: "inherit" });
|
|
36
|
-
} else {
|
|
37
|
-
console.log("info: no change branch found, using current branch");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Push
|
|
41
|
-
if (push) {
|
|
42
|
-
const currentBranch = execSync("git branch --show-current", { cwd, encoding: "utf8" }).trim();
|
|
43
|
-
|
|
44
|
-
// Push submodules first
|
|
45
|
-
if (existsSync(`${cwd}/.gitmodules`)) {
|
|
46
|
-
const targetsFile = `changes/${changeId}/submodules.targets`;
|
|
47
|
-
if (existsSync(targetsFile)) {
|
|
48
|
-
const content = execSync(`cat "${targetsFile}"`, { cwd, encoding: "utf8" });
|
|
49
|
-
for (const line of content.split("\n")) {
|
|
50
|
-
const t = line.trim();
|
|
51
|
-
if (!t || t.startsWith("#")) continue;
|
|
52
|
-
const [subPath, targetBranch] = t.split(/\s+/);
|
|
53
|
-
if (subPath && targetBranch) {
|
|
54
|
-
console.log(`info: pushing submodule ${subPath}...`);
|
|
55
|
-
execSync(`git push ${remote} ${targetBranch}`, { cwd: `${cwd}/${subPath}`, stdio: "inherit" });
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
execSync(`git push ${remote} ${currentBranch}`, { cwd, stdio: "inherit" });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Archive
|
|
65
|
-
try {
|
|
66
|
-
execSync(`npx -y @aipper/aiws change archive ${changeId} --force 2>/dev/null || aiws change archive ${changeId} --force 2>/dev/null || true`, { cwd, stdio: "inherit" });
|
|
67
|
-
} catch { /* ignore archive failure — main flow done */ }
|
|
68
|
-
|
|
69
|
-
console.log(`ok: finish complete. ${push ? "Pushed and " : ""}Archived.`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function checkClean(cwd) {
|
|
73
|
-
const out = execSync("git status --porcelain", { cwd, encoding: "utf8" }).trim();
|
|
74
|
-
if (out) {
|
|
75
|
-
throw new UserError("Working tree is not clean. Commit or stash first.", { exitCode: 2 });
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function detectChangeBranch(cwd) {
|
|
80
|
-
const branch = execSync("git branch --show-current", { cwd, encoding: "utf8" }).trim();
|
|
81
|
-
const match = branch.match(/^(?:change|changes|ws|ws-change)\/(.+)$/);
|
|
82
|
-
return match ? match[1] : null;
|
|
83
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* aiws handoff — read/display handoff documents
|
|
5
|
-
* @param {{ cwd?: string }} opts
|
|
6
|
-
*/
|
|
7
|
-
export async function wsHandoffCommand({ cwd = "." } = {}) {
|
|
8
|
-
const changesDir = `${cwd}/changes`;
|
|
9
|
-
if (!existsSync(changesDir)) {
|
|
10
|
-
console.log("info: no changes/ directory found");
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Check archive handoffs first
|
|
15
|
-
const archiveDir = `${changesDir}/archive`;
|
|
16
|
-
if (existsSync(archiveDir)) {
|
|
17
|
-
const archives = readdirSync(archiveDir).filter(f => f !== ".DS_Store");
|
|
18
|
-
for (const a of archives) {
|
|
19
|
-
const hf = `${archiveDir}/${a}/handoff.md`;
|
|
20
|
-
if (existsSync(hf)) {
|
|
21
|
-
console.log(`=== ${a} ===`);
|
|
22
|
-
console.log(readFileSync(hf, "utf8"));
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Check active change handoffs
|
|
28
|
-
const changes = readdirSync(changesDir).filter(f =>
|
|
29
|
-
!f.startsWith(".") && f !== "templates" && f !== "archive" && f !== "README.md"
|
|
30
|
-
);
|
|
31
|
-
for (const c of changes) {
|
|
32
|
-
const hf = `${changesDir}/${c}/handoff.md`;
|
|
33
|
-
if (existsSync(hf)) {
|
|
34
|
-
console.log(`=== ${c} (active) ===`);
|
|
35
|
-
console.log(readFileSync(hf, "utf8"));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
console.log("info: handoffs are generated by aiws change finish --push");
|
|
40
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* aiws migrate — workspace migration (init or update)
|
|
6
|
-
* @param {{ cwd?: string }} opts
|
|
7
|
-
*/
|
|
8
|
-
export async function wsMigrateCommand({ cwd = "." } = {}) {
|
|
9
|
-
const localAiws = `${cwd}/node_modules/.bin/aiws`;
|
|
10
|
-
|
|
11
|
-
if (existsSync(`${cwd}/.aiws/manifest.json`)) {
|
|
12
|
-
console.log("info: workspace detected, running update...");
|
|
13
|
-
runAiws(localAiws, `update ${cwd}`);
|
|
14
|
-
} else {
|
|
15
|
-
console.log("info: no workspace detected, running init...");
|
|
16
|
-
runAiws(localAiws, `init ${cwd}`);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Enable hooks
|
|
20
|
-
try {
|
|
21
|
-
execSync("git config core.hooksPath .githooks", { cwd, stdio: "inherit" });
|
|
22
|
-
console.log("info: hooks enabled (core.hooksPath=.githooks)");
|
|
23
|
-
} catch {
|
|
24
|
-
console.log("warn: could not set hooks (not a git repo?)");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
console.log("ok: migrate complete");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function runAiws(localAiws, args) {
|
|
31
|
-
try {
|
|
32
|
-
if (existsSync(localAiws)) {
|
|
33
|
-
execSync(`${localAiws} ${args}`, { stdio: "inherit" });
|
|
34
|
-
} else {
|
|
35
|
-
execSync(`npx -y @aipper/aiws ${args}`, { stdio: "inherit" });
|
|
36
|
-
}
|
|
37
|
-
} catch (e) {
|
|
38
|
-
console.error(`error: aiws ${args} failed`);
|
|
39
|
-
throw e;
|
|
40
|
-
}
|
|
41
|
-
}
|