@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/codex-prompts.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import os from "node:os";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { listFilesRecursive, readText } from "./fs.js";
|
|
4
|
-
import { normalizeNewlines } from "./hash.js";
|
|
5
|
-
import { extractTemplateBlock } from "./managed-blocks.js";
|
|
6
|
-
import { UserError } from "./errors.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Resolve Codex global prompts directory:
|
|
10
|
-
* - If `--dir` provided, use it directly.
|
|
11
|
-
* - Else if `CODEX_HOME` is set, use `$CODEX_HOME/prompts`.
|
|
12
|
-
* - Else use `~/.codex/prompts`.
|
|
13
|
-
*
|
|
14
|
-
* @param {string | undefined} dirOption
|
|
15
|
-
*/
|
|
16
|
-
export function resolveCodexPromptsDir(dirOption) {
|
|
17
|
-
if (dirOption) return path.resolve(dirOption);
|
|
18
|
-
const envHome = String(process.env.CODEX_HOME || "").trim();
|
|
19
|
-
if (envHome) return path.join(path.resolve(envHome), "prompts");
|
|
20
|
-
return path.join(os.homedir(), ".codex", "prompts");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function timestamp() {
|
|
24
|
-
const d = new Date();
|
|
25
|
-
// 2026-01-28T12:34:56Z -> 20260128-123456Z
|
|
26
|
-
return d.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z").replace("T", "-");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function codexBackupStamp() {
|
|
30
|
-
return timestamp();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @param {string} promptsDir
|
|
35
|
-
* @param {string} filename
|
|
36
|
-
* @param {string=} stamp
|
|
37
|
-
*/
|
|
38
|
-
export function codexBackupPathFor(promptsDir, filename, stamp) {
|
|
39
|
-
return path.join(promptsDir, ".aiws", "backups", "codex-prompts", stamp || timestamp(), filename);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* List Codex prompt files in a template and extract their managed block inner text.
|
|
44
|
-
*
|
|
45
|
-
* @param {{ templateId: string, templateDir: string }} tpl
|
|
46
|
-
* @returns {Promise<Array<{ rel: string, filename: string, name: string, blockId: string, srcAbs: string, templateText: string, innerText: string }>>}
|
|
47
|
-
*/
|
|
48
|
-
export async function listTemplateCodexPrompts(tpl) {
|
|
49
|
-
const relFiles = await listFilesRecursive(tpl.templateDir, ".codex/prompts");
|
|
50
|
-
const promptFiles = relFiles
|
|
51
|
-
.filter((rel) => rel.startsWith(".codex/prompts/"))
|
|
52
|
-
.filter((rel) => rel.split("/").length === 3)
|
|
53
|
-
.filter((rel) => rel.toLowerCase().endsWith(".md"));
|
|
54
|
-
|
|
55
|
-
if (promptFiles.length === 0) {
|
|
56
|
-
throw new UserError(`Template has no Codex prompt files: ${tpl.templateId}`, {
|
|
57
|
-
details: `Missing under: ${path.join(tpl.templateDir, ".codex", "prompts")}`,
|
|
58
|
-
exitCode: 1,
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** @type {Array<{ rel: string, filename: string, name: string, blockId: string, srcAbs: string, templateText: string, innerText: string }>} */
|
|
63
|
-
const out = [];
|
|
64
|
-
for (const rel of promptFiles) {
|
|
65
|
-
const filename = path.posix.basename(rel);
|
|
66
|
-
const name = filename.replace(/\.md$/i, "");
|
|
67
|
-
const blockId = `codex:${name}`;
|
|
68
|
-
const srcAbs = path.join(tpl.templateDir, ...rel.split("/"));
|
|
69
|
-
const templateText = normalizeNewlines(await readText(srcAbs));
|
|
70
|
-
const { innerText } = extractTemplateBlock(templateText, blockId);
|
|
71
|
-
out.push({ rel, filename, name, blockId, srcAbs, templateText, innerText });
|
|
72
|
-
}
|
|
73
|
-
return out;
|
|
74
|
-
}
|
package/src/codex-skills.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import os from "node:os";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { listFilesRecursive, readText } from "./fs.js";
|
|
4
|
-
import { normalizeNewlines } from "./hash.js";
|
|
5
|
-
import { UserError } from "./errors.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Resolve Codex global skills directory:
|
|
9
|
-
* - If `--dir` provided, use it directly.
|
|
10
|
-
* - Else if `CODEX_HOME` is set, use `$CODEX_HOME/.agents/skills` as a compatibility/test override root.
|
|
11
|
-
* - Else use `~/.agents/skills`.
|
|
12
|
-
*
|
|
13
|
-
* @param {string | undefined} dirOption
|
|
14
|
-
*/
|
|
15
|
-
export function resolveCodexSkillsDir(dirOption) {
|
|
16
|
-
if (dirOption) return path.resolve(dirOption);
|
|
17
|
-
const envHome = String(process.env.CODEX_HOME || "").trim();
|
|
18
|
-
if (envHome) return path.join(path.resolve(envHome), ".agents", "skills");
|
|
19
|
-
return path.join(os.homedir(), ".agents", "skills");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function timestamp() {
|
|
23
|
-
const d = new Date();
|
|
24
|
-
// 2026-01-28T12:34:56Z -> 20260128-123456Z
|
|
25
|
-
return d.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z").replace("T", "-");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function codexSkillsBackupStamp() {
|
|
29
|
-
return timestamp();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @param {string} skillsDir
|
|
34
|
-
* @param {string} skillName
|
|
35
|
-
* @param {string=} stamp
|
|
36
|
-
*/
|
|
37
|
-
export function codexSkillsBackupPathFor(skillsDir, skillName, stamp) {
|
|
38
|
-
return path.join(skillsDir, ".aiws", "backups", "codex-skills", stamp || timestamp(), skillName, "SKILL.md");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @param {string} text normalized
|
|
43
|
-
* @returns {{ frontMatter: string, body: string }}
|
|
44
|
-
*/
|
|
45
|
-
function splitYamlFrontMatter(text) {
|
|
46
|
-
const t = normalizeNewlines(text);
|
|
47
|
-
const lines = t.split("\n");
|
|
48
|
-
if ((lines[0] ?? "") !== "---") {
|
|
49
|
-
throw new UserError("Invalid SKILL.md: missing YAML front matter at top", { exitCode: 1 });
|
|
50
|
-
}
|
|
51
|
-
let end = -1;
|
|
52
|
-
for (let i = 1; i < lines.length; i++) {
|
|
53
|
-
if ((lines[i] ?? "") === "---") {
|
|
54
|
-
end = i;
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (end === -1) {
|
|
59
|
-
throw new UserError("Invalid SKILL.md: YAML front matter not closed", { exitCode: 1 });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const frontMatter = `${lines.slice(0, end + 1).join("\n")}\n`;
|
|
63
|
-
const body = lines.slice(end + 1).join("\n");
|
|
64
|
-
return { frontMatter, body };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* List Codex repo skills in a template and prepare a managed-file wrapper for global installation.
|
|
69
|
-
*
|
|
70
|
-
* The wrapper adds an AIWS managed block after YAML front matter so updates can preserve user-added
|
|
71
|
-
* text outside the managed block.
|
|
72
|
-
*
|
|
73
|
-
* @param {{ templateId: string, templateDir: string }} tpl
|
|
74
|
-
* @returns {Promise<Array<{ rel: string, skillName: string, blockId: string, srcAbs: string, templateText: string, managedText: string, innerText: string }>>}
|
|
75
|
-
*/
|
|
76
|
-
export async function listTemplateCodexSkills(tpl) {
|
|
77
|
-
const relFiles = await listFilesRecursive(tpl.templateDir, ".agents/skills");
|
|
78
|
-
const skillFiles = relFiles
|
|
79
|
-
.filter((rel) => rel.startsWith(".agents/skills/"))
|
|
80
|
-
.filter((rel) => rel.split("/").length === 4)
|
|
81
|
-
.filter((rel) => rel.toLowerCase().endsWith("/skill.md"));
|
|
82
|
-
|
|
83
|
-
if (skillFiles.length === 0) {
|
|
84
|
-
throw new UserError(`Template has no Codex skills: ${tpl.templateId}`, {
|
|
85
|
-
details: `Missing under: ${path.join(tpl.templateDir, ".agents", "skills")}`,
|
|
86
|
-
exitCode: 1,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** @type {Array<{ rel: string, skillName: string, blockId: string, srcAbs: string, templateText: string, managedText: string, innerText: string }>} */
|
|
91
|
-
const out = [];
|
|
92
|
-
for (const rel of skillFiles) {
|
|
93
|
-
const parts = rel.split("/");
|
|
94
|
-
const skillName = parts[2] ?? "";
|
|
95
|
-
if (!skillName) continue;
|
|
96
|
-
const blockId = `codex-skill:${skillName}`;
|
|
97
|
-
const srcAbs = path.join(tpl.templateDir, ...rel.split("/"));
|
|
98
|
-
const templateText = normalizeNewlines(await readText(srcAbs));
|
|
99
|
-
const { frontMatter, body } = splitYamlFrontMatter(templateText);
|
|
100
|
-
|
|
101
|
-
const innerTextRaw = normalizeNewlines(body);
|
|
102
|
-
const innerText = innerTextRaw.endsWith("\n") ? innerTextRaw : `${innerTextRaw}\n`;
|
|
103
|
-
|
|
104
|
-
const begin = `<!-- AIWS_MANAGED_BEGIN:${blockId} -->`;
|
|
105
|
-
const end = `<!-- AIWS_MANAGED_END:${blockId} -->`;
|
|
106
|
-
const managedText = `${frontMatter}\n${begin}\n${innerText}${end}\n`;
|
|
107
|
-
|
|
108
|
-
out.push({ rel, skillName, blockId, srcAbs, templateText, managedText, innerText });
|
|
109
|
-
}
|
|
110
|
-
return out;
|
|
111
|
-
}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param {string} gitRoot
|
|
3
|
-
* @param {string} changeId
|
|
4
|
-
* @param {{
|
|
5
|
-
* computeChangeStatus: (gitRoot: string, changeId: string) => Promise<any>,
|
|
6
|
-
* currentBranch: (gitRoot: string) => Promise<string>,
|
|
7
|
-
* inferChangeIdFromBranch: (branch: string) => string,
|
|
8
|
-
* changeDirAbs: (gitRoot: string, changeId: string) => string,
|
|
9
|
-
* governanceGuidanceLines: (status: any) => Promise<string[]>,
|
|
10
|
-
* scopeGateFailureAction: (changeId: string) => string,
|
|
11
|
-
* scopeGatePendingRecommendation: (changeId: string) => string,
|
|
12
|
-
* scopeGatePassedRecommendation: () => string
|
|
13
|
-
* }} deps
|
|
14
|
-
*/
|
|
15
|
-
export async function computeChangeNextAdvice(gitRoot, changeId, deps) {
|
|
16
|
-
const status = await deps.computeChangeStatus(gitRoot, changeId);
|
|
17
|
-
const branch = await deps.currentBranch(gitRoot);
|
|
18
|
-
const inferred = deps.inferChangeIdFromBranch(branch);
|
|
19
|
-
const advice = {
|
|
20
|
-
changeId,
|
|
21
|
-
branch: branch || "",
|
|
22
|
-
inferredChangeId: inferred || "",
|
|
23
|
-
phase: status.phase,
|
|
24
|
-
governance: status.governance || null,
|
|
25
|
-
scopeGate: status.scopeGate || null,
|
|
26
|
-
blockersStrict: status.blockersStrict,
|
|
27
|
-
evidence: status.evidence?.counts || null,
|
|
28
|
-
collaboration: status.collaboration?.counts || null,
|
|
29
|
-
actions: [],
|
|
30
|
-
prohibitions: [],
|
|
31
|
-
recommended: [],
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
if (!branch) {
|
|
35
|
-
advice.actions.push("当前为 detached HEAD:建议切到 `change/<change-id>` 分支后再继续(或显式传入 `<change-id>`)");
|
|
36
|
-
} else if (inferred && inferred !== changeId) {
|
|
37
|
-
advice.actions.push(`当前分支为 \`${branch}\`(推断 change=${inferred}),但你在查看 change=${changeId}:建议先切换到 \`change/${changeId}\` 再执行开发/交付`);
|
|
38
|
-
} else if (!inferred) {
|
|
39
|
-
advice.actions.push(`当前分支为 \`${branch}\`(非 change 分支):建议 \`aiws change start ${changeId} --hooks --switch\` 或 \`git switch change/${changeId}\``);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (status.metaState !== "ok") advice.actions.push(`补齐元信息并建立真值基线:\`aiws change sync ${changeId}\``);
|
|
43
|
-
if (status.driftFiles.length > 0) {
|
|
44
|
-
advice.actions.push("真值/合同已变化:先对齐 `AI_PROJECT.md` / `AI_WORKSPACE.md` / `REQUIREMENTS.md` 与 proposal/tasks");
|
|
45
|
-
advice.actions.push(`确认后同步基线:\`aiws change sync ${changeId}\``);
|
|
46
|
-
}
|
|
47
|
-
if (status.terminatedReuse) {
|
|
48
|
-
advice.actions.push(status.terminatedReuse.message);
|
|
49
|
-
advice.prohibitions.push(`不要继续在 change/${changeId} 上开发或提交新改动`);
|
|
50
|
-
advice.recommended.push(`优先续跑收尾:\`aiws change finish ${changeId} --push\``);
|
|
51
|
-
advice.recommended.push(`仅在你明确只需要本地归档恢复时,才手工运行 \`aiws change archive ${changeId}\``);
|
|
52
|
-
return advice;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const proposalPath = path.join(deps.changeDirAbs(gitRoot, changeId), "proposal.md");
|
|
56
|
-
const tasksPath = path.join(deps.changeDirAbs(gitRoot, changeId), "tasks.md");
|
|
57
|
-
if (status.blockersStrict.some((item) => item.includes("proposal.md") && item.includes("WS:TODO"))) {
|
|
58
|
-
advice.actions.push(`完善 proposal:\`$EDITOR ${proposalPath}\``);
|
|
59
|
-
}
|
|
60
|
-
if (status.blockersStrict.some((item) => item.includes("tasks.md") && item.includes("WS:TODO"))) {
|
|
61
|
-
advice.actions.push(`完善 tasks:\`$EDITOR ${tasksPath}\``);
|
|
62
|
-
}
|
|
63
|
-
if (status.blockersStrict.some((item) => item.includes("proposal.md missing attribution"))) {
|
|
64
|
-
advice.actions.push("补齐归因:在 proposal.md 填写非空 `Req_ID` 或 `Problem_ID`(严格校验需要)");
|
|
65
|
-
}
|
|
66
|
-
if (status.blockersStrict.some((item) => item.includes("missing Contract_Row"))) {
|
|
67
|
-
advice.actions.push("补齐绑定:在 proposal.md 填写非空 `Contract_Row`(严格校验需要)");
|
|
68
|
-
}
|
|
69
|
-
if (status.blockersStrict.some((item) => item.includes("Plan_File"))) {
|
|
70
|
-
advice.actions.push("补齐绑定:修正 proposal.md 的 `Plan_File`(严格校验需要;必须存在且仅 1 个)");
|
|
71
|
-
}
|
|
72
|
-
if (status.blockersStrict.some((item) => item.includes("missing Evidence_Path"))) {
|
|
73
|
-
advice.actions.push("补齐绑定:在 proposal.md 填写非空 `Evidence_Path`(严格校验需要;建议优先写 `changes/<id>/evidence/...`)");
|
|
74
|
-
}
|
|
75
|
-
if (status.scopeGate?.status === "failed") {
|
|
76
|
-
advice.actions.push(deps.scopeGateFailureAction(changeId));
|
|
77
|
-
}
|
|
78
|
-
if (status.tasks.unchecked > 0) advice.actions.push(`完成未勾选任务(${status.tasks.unchecked} 项)`);
|
|
79
|
-
if (status.blockersStrict.length === 0 && status.tasks.unchecked > 0) {
|
|
80
|
-
advice.actions.push("质量门通过后继续开发:simple/local 单点修复优先 `$ws-dev-lite`;否则 `$ws-dev`");
|
|
81
|
-
}
|
|
82
|
-
if (
|
|
83
|
-
status.blockersStrict.length === 0 &&
|
|
84
|
-
status.tasks.unchecked === 0 &&
|
|
85
|
-
Number(status?.reviewSignals?.effectiveCount || 0) > 0 &&
|
|
86
|
-
Number(status?.reviewGates?.summary?.dualReviewMissingCount || 0) > 0
|
|
87
|
-
) {
|
|
88
|
-
advice.actions.push("已有通用 review,但 dual review gate 未完成:继续补齐 `$ws-spec-review` / `$ws-quality-review`,不要把 `codex-review.md` 当成 finish gate");
|
|
89
|
-
}
|
|
90
|
-
if ((status.collaboration?.counts?.analysis || 0) > 0 && (status.collaboration?.counts?.review || 0) === 0) {
|
|
91
|
-
advice.actions.push("已有委托分析但还没有 review 汇总结论:建议在 `changes/<id>/review/` 落盘结论后再交付");
|
|
92
|
-
}
|
|
93
|
-
if ((status.collaboration?.counts?.patches || 0) > 0) {
|
|
94
|
-
advice.actions.push("存在委托 patch 草案:提交/交付前先人工审查 `changes/<id>/patches/`,不要直接无审查应用");
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (status.blockersStrict.length > 0 && advice.actions.length === 0) {
|
|
98
|
-
advice.actions.push("先修复 Blockers (strict) 后复跑质量门,再进入编码:simple/local 单点修复优先 `$ws-dev-lite`;否则 `$ws-dev`");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (status.blockersStrict.length === 0 && status.tasks.unchecked === 0) {
|
|
102
|
-
for (const line of await deps.governanceGuidanceLines(status)) advice.recommended.push(line);
|
|
103
|
-
if (advice.evidence && advice.evidence.persistent === 0) {
|
|
104
|
-
advice.recommended.push("交付前建议生成持久证据并回填:`aiws change evidence <change-id>`");
|
|
105
|
-
}
|
|
106
|
-
if (status.scopeGate?.status === "not_run") {
|
|
107
|
-
advice.recommended.push(deps.scopeGatePendingRecommendation(changeId));
|
|
108
|
-
} else if (status.scopeGate?.status === "passed") {
|
|
109
|
-
advice.recommended.push(deps.scopeGatePassedRecommendation());
|
|
110
|
-
}
|
|
111
|
-
if ((status.governance?.recommendedStage || "") === "ws-finish" || (status.governance?.currentStage || "") === "ws-commit") {
|
|
112
|
-
advice.recommended.push("交付前门禁(推荐落盘):`aiws validate . --stamp`");
|
|
113
|
-
}
|
|
114
|
-
if ((status.collaboration?.counts?.review || 0) > 1) advice.recommended.push("存在多份 review:交付前建议在 handoff 或 review 汇总里收敛最终结论");
|
|
115
|
-
} else if (advice.actions.length === 0) {
|
|
116
|
-
advice.recommended.push("继续开发:simple/local 单点修复优先 `$ws-dev-lite`;否则 `$ws-dev`(小步实现 + 可复现验证)");
|
|
117
|
-
if ((status.collaboration?.counts?.analysis || 0) > 0) advice.recommended.push("已有委托分析:进入实现前先收敛 `changes/<id>/analysis/` 结论");
|
|
118
|
-
if ((status.collaboration?.counts?.patches || 0) > 0) advice.recommended.push("已有 patch 草案:应用前先人工审查 `changes/<id>/patches/`");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return advice;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* @param {{
|
|
126
|
-
* changeId: string,
|
|
127
|
-
* gitRoot: string,
|
|
128
|
-
* now: string,
|
|
129
|
-
* status: any,
|
|
130
|
-
* advice: any,
|
|
131
|
-
* planVerifyLine: string
|
|
132
|
-
* }} input
|
|
133
|
-
*/
|
|
134
|
-
export function renderChangeStateMarkdown(input) {
|
|
135
|
-
/** @type {string[]} */
|
|
136
|
-
const lines = [];
|
|
137
|
-
lines.push(`# AIWS STATE: ${input.changeId}`);
|
|
138
|
-
lines.push("");
|
|
139
|
-
lines.push(`Generated: ${input.now}`);
|
|
140
|
-
lines.push("");
|
|
141
|
-
lines.push("## Context");
|
|
142
|
-
lines.push(`- Worktree: \`${input.gitRoot}\``);
|
|
143
|
-
lines.push(`- Branch: \`${input.advice.branch || "(detached)"}\``);
|
|
144
|
-
lines.push("");
|
|
145
|
-
lines.push("## Phase");
|
|
146
|
-
lines.push(`- Phase: \`${input.status.phase}\``);
|
|
147
|
-
lines.push(
|
|
148
|
-
`- Tasks: ${input.status.tasks.done}/${input.status.tasks.total} (unchecked=${input.status.tasks.unchecked}, optional_open=${input.status.tasks.optionalUnchecked || 0}, n_a=${input.status.tasks.naCount || 0})`,
|
|
149
|
-
);
|
|
150
|
-
lines.push("");
|
|
151
|
-
lines.push("## Bindings");
|
|
152
|
-
if (input.status.reqId) lines.push(`- Req_ID: \`${input.status.reqId}\``);
|
|
153
|
-
if (input.status.probId) lines.push(`- Problem_ID: \`${input.status.probId}\``);
|
|
154
|
-
if (input.status.bindings?.contractRow) lines.push(`- Contract_Row: \`${input.status.bindings.contractRow}\``);
|
|
155
|
-
if (input.status.bindings?.planFile) lines.push(`- Plan_File: \`${input.status.bindings.planFile}\``);
|
|
156
|
-
lines.push("");
|
|
157
|
-
lines.push("## Evidence");
|
|
158
|
-
if (input.status.evidence?.counts) {
|
|
159
|
-
const counts = input.status.evidence.counts;
|
|
160
|
-
lines.push(
|
|
161
|
-
`- Declared: \`${counts.total}\` (exists=\`${counts.exists}\`, missing=\`${counts.missing}\`, persistent=\`${counts.persistent}\`, tmp=\`${counts.tmp}\`)`,
|
|
162
|
-
);
|
|
163
|
-
} else {
|
|
164
|
-
lines.push("- (unknown)");
|
|
165
|
-
}
|
|
166
|
-
lines.push("");
|
|
167
|
-
lines.push("## Blockers (strict)");
|
|
168
|
-
if (input.status.blockersStrict.length === 0) lines.push("- (none)");
|
|
169
|
-
else for (const blocker of input.status.blockersStrict) lines.push(`- ${blocker}`);
|
|
170
|
-
lines.push("");
|
|
171
|
-
lines.push("## Next");
|
|
172
|
-
lines.push(`- ${input.planVerifyLine}`);
|
|
173
|
-
for (const action of input.advice.actions) lines.push(`- ${action}`);
|
|
174
|
-
for (const recommendation of input.advice.recommended) lines.push(`- ${recommendation}`);
|
|
175
|
-
if (input.advice.prohibitions.length > 0) {
|
|
176
|
-
lines.push("");
|
|
177
|
-
lines.push("## Do Not");
|
|
178
|
-
for (const prohibition of input.advice.prohibitions) lines.push(`- ${prohibition}`);
|
|
179
|
-
}
|
|
180
|
-
return lines.join("\n") + "\n";
|
|
181
|
-
}
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import {
|
|
4
|
-
buildDeliverySummary,
|
|
5
|
-
collectCollaborationArtifacts,
|
|
6
|
-
findLatestFileByMtime,
|
|
7
|
-
formatDeclaredValues,
|
|
8
|
-
mergeDeclaredValues,
|
|
9
|
-
relFromRoot,
|
|
10
|
-
upsertIdLine,
|
|
11
|
-
upsertProposalEvidencePath,
|
|
12
|
-
} from "./change-evidence.js";
|
|
13
|
-
import { TMP_REVIEW_FALLBACKS, tmpReviewFallbackPath } from "./change-review-gates.js";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {{
|
|
17
|
-
* gitRoot: string,
|
|
18
|
-
* changeId: string,
|
|
19
|
-
* changeDir: string,
|
|
20
|
-
* proposalText: string,
|
|
21
|
-
* options: { noValidate: boolean, allowFail: boolean }
|
|
22
|
-
* }} input
|
|
23
|
-
* @param {{
|
|
24
|
-
* appendMetricsEvent: (gitRoot: string, changeId: string, type: string, payload: any) => Promise<void>,
|
|
25
|
-
* computeChangeStatus: (gitRoot: string, changeId: string) => Promise<any>,
|
|
26
|
-
* currentBranch: (gitRoot: string) => Promise<string>,
|
|
27
|
-
* ensureDir: (path: string) => Promise<void>,
|
|
28
|
-
* extractId: (label: string, text: string) => string,
|
|
29
|
-
* nowIsoUtc: () => string,
|
|
30
|
-
* nowStampUtc: () => string,
|
|
31
|
-
* pathExists: (path: string) => Promise<boolean>,
|
|
32
|
-
* planVerifyHint: (changeId: string) => string,
|
|
33
|
-
* readText: (path: string) => Promise<string>,
|
|
34
|
-
* splitDeclaredValues: (s: string) => string[],
|
|
35
|
-
* validateChangeArtifacts: (gitRoot: string, changeId: string, options: any) => Promise<any>,
|
|
36
|
-
* writeText: (path: string, content: string) => Promise<void>,
|
|
37
|
-
* UserError: typeof import("../errors.js").UserError
|
|
38
|
-
* }} deps
|
|
39
|
-
*/
|
|
40
|
-
export async function runChangeEvidenceWorkflow(input, deps) {
|
|
41
|
-
const { gitRoot, changeId, changeDir, proposalText, options } = input;
|
|
42
|
-
const planDecl = deps.extractId("Plan_File", proposalText) || deps.extractId("Plan file", proposalText);
|
|
43
|
-
const planFiles = deps.splitDeclaredValues(planDecl).map((item) => String(item || "").replace(/^\.\//, ""));
|
|
44
|
-
const planRel = planFiles.length === 1 ? planFiles[0] : "";
|
|
45
|
-
const planAbs = planRel ? path.join(gitRoot, planRel) : "";
|
|
46
|
-
const planText = planAbs && (await deps.pathExists(planAbs)) ? await deps.readText(planAbs) : "";
|
|
47
|
-
|
|
48
|
-
const now = deps.nowStampUtc();
|
|
49
|
-
const evidenceDir = path.join(changeDir, "evidence");
|
|
50
|
-
const reviewDir = path.join(changeDir, "review");
|
|
51
|
-
const analysisDir = path.join(changeDir, "analysis");
|
|
52
|
-
const patchesDir = path.join(changeDir, "patches");
|
|
53
|
-
await deps.ensureDir(evidenceDir);
|
|
54
|
-
await deps.ensureDir(reviewDir);
|
|
55
|
-
await deps.ensureDir(analysisDir);
|
|
56
|
-
await deps.ensureDir(patchesDir);
|
|
57
|
-
|
|
58
|
-
/** @type {string[]} */
|
|
59
|
-
const created = [];
|
|
60
|
-
const branch = await deps.currentBranch(gitRoot);
|
|
61
|
-
const status = await deps.computeChangeStatus(gitRoot, changeId);
|
|
62
|
-
|
|
63
|
-
const statusPath = path.join(evidenceDir, `change-status-${now}.json`);
|
|
64
|
-
await deps.writeText(
|
|
65
|
-
statusPath,
|
|
66
|
-
JSON.stringify(
|
|
67
|
-
{
|
|
68
|
-
generated_at: deps.nowIsoUtc(),
|
|
69
|
-
ws_root: gitRoot,
|
|
70
|
-
change_id: changeId,
|
|
71
|
-
branch: branch || "",
|
|
72
|
-
phase: status.phase,
|
|
73
|
-
tasks: status.tasks,
|
|
74
|
-
bindings: status.bindings,
|
|
75
|
-
truth: {
|
|
76
|
-
baseline: { label: status.baselineLabel, at: status.baselineAt },
|
|
77
|
-
drift_files: status.driftFiles,
|
|
78
|
-
},
|
|
79
|
-
evidence: status.evidence?.counts || null,
|
|
80
|
-
collaboration: status.collaboration?.counts || null,
|
|
81
|
-
note: "aiws change evidence status snapshot; does not contain secrets.",
|
|
82
|
-
},
|
|
83
|
-
null,
|
|
84
|
-
2,
|
|
85
|
-
) + "\n",
|
|
86
|
-
);
|
|
87
|
-
created.push(relFromRoot(gitRoot, statusPath));
|
|
88
|
-
|
|
89
|
-
/** @type {any | null} */
|
|
90
|
-
let validateRes = null;
|
|
91
|
-
let validateFailed = false;
|
|
92
|
-
let validatePathForError = "";
|
|
93
|
-
if (!options.noValidate) {
|
|
94
|
-
validateRes = await deps.validateChangeArtifacts(gitRoot, changeId, { strict: true, allowTruthDrift: false });
|
|
95
|
-
const validatePath = path.join(evidenceDir, `change-validate-strict-${now}.json`);
|
|
96
|
-
await deps.writeText(validatePath, JSON.stringify(validateRes, null, 2) + "\n");
|
|
97
|
-
created.push(relFromRoot(gitRoot, validatePath));
|
|
98
|
-
validateFailed = validateRes.ok !== true;
|
|
99
|
-
validatePathForError = validatePath;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const validateStampDir = path.join(gitRoot, ".agentdocs", "tmp", "aiws-validate");
|
|
103
|
-
const latestValidateStamp = await findLatestFileByMtime(validateStampDir, (name) => name.endsWith(".json"));
|
|
104
|
-
if (latestValidateStamp) {
|
|
105
|
-
const dest = path.join(evidenceDir, `aiws-validate-stamp-${now}.json`);
|
|
106
|
-
await fs.copyFile(latestValidateStamp, dest);
|
|
107
|
-
created.push(relFromRoot(gitRoot, dest));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const syncStampDir = path.join(gitRoot, ".agentdocs", "tmp", "change-sync");
|
|
111
|
-
const latestSyncStamp = await findLatestFileByMtime(syncStampDir, (name) => name.endsWith(`-${changeId}.json`));
|
|
112
|
-
if (latestSyncStamp) {
|
|
113
|
-
const dest = path.join(evidenceDir, `change-sync-stamp-${now}.json`);
|
|
114
|
-
await fs.copyFile(latestSyncStamp, dest);
|
|
115
|
-
created.push(relFromRoot(gitRoot, dest));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
for (const { fileName, persistentKind } of TMP_REVIEW_FALLBACKS) {
|
|
119
|
-
const src = tmpReviewFallbackPath(gitRoot, fileName);
|
|
120
|
-
if (!(await deps.pathExists(src))) continue;
|
|
121
|
-
const dest = persistentKind === "evidence" ? path.join(evidenceDir, fileName) : path.join(reviewDir, fileName);
|
|
122
|
-
if (!(await deps.pathExists(dest))) {
|
|
123
|
-
await fs.copyFile(src, dest);
|
|
124
|
-
}
|
|
125
|
-
const rel = relFromRoot(gitRoot, dest);
|
|
126
|
-
if (!created.includes(rel)) created.push(rel);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (await deps.pathExists(reviewDir)) {
|
|
130
|
-
try {
|
|
131
|
-
const reviewEntries = await fs.readdir(reviewDir, { withFileTypes: true });
|
|
132
|
-
for (const entry of reviewEntries) {
|
|
133
|
-
if (!entry.isFile()) continue;
|
|
134
|
-
if (entry.name === "codex-review.md") continue;
|
|
135
|
-
if (!entry.name.endsWith(".md")) continue;
|
|
136
|
-
const rel = relFromRoot(gitRoot, path.join(reviewDir, entry.name));
|
|
137
|
-
if (!created.includes(rel)) created.push(rel);
|
|
138
|
-
}
|
|
139
|
-
} catch {
|
|
140
|
-
// ignore
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const collaboration = await collectCollaborationArtifacts(gitRoot, changeDir);
|
|
145
|
-
const collaborationPath = path.join(evidenceDir, `collaboration-summary-${now}.json`);
|
|
146
|
-
await deps.writeText(
|
|
147
|
-
collaborationPath,
|
|
148
|
-
JSON.stringify(
|
|
149
|
-
{
|
|
150
|
-
generated_at: deps.nowIsoUtc(),
|
|
151
|
-
ws_root: gitRoot,
|
|
152
|
-
change_id: changeId,
|
|
153
|
-
counts: collaboration.counts,
|
|
154
|
-
dirs: collaboration.dirs,
|
|
155
|
-
note: "aiws collaboration summary; patch files are drafts and must be reviewed before apply.",
|
|
156
|
-
},
|
|
157
|
-
null,
|
|
158
|
-
2,
|
|
159
|
-
) + "\n",
|
|
160
|
-
);
|
|
161
|
-
created.push(relFromRoot(gitRoot, collaborationPath));
|
|
162
|
-
|
|
163
|
-
const summaryAbs = path.join(evidenceDir, `delivery-summary-${now}.md`);
|
|
164
|
-
await deps.writeText(
|
|
165
|
-
summaryAbs,
|
|
166
|
-
buildDeliverySummary({
|
|
167
|
-
gitRoot,
|
|
168
|
-
changeId,
|
|
169
|
-
branch: branch || "",
|
|
170
|
-
status,
|
|
171
|
-
collaboration,
|
|
172
|
-
created,
|
|
173
|
-
validateRes,
|
|
174
|
-
validateFailed,
|
|
175
|
-
}),
|
|
176
|
-
);
|
|
177
|
-
created.push(relFromRoot(gitRoot, summaryAbs));
|
|
178
|
-
|
|
179
|
-
const declaredEvidence = deps.extractId("Evidence_Path", proposalText) || deps.extractId("Evidence_Path(s)", proposalText);
|
|
180
|
-
const existingProposalEvidence = deps.splitDeclaredValues(declaredEvidence).map((item) => String(item || "").replace(/^\.\//, ""));
|
|
181
|
-
const mergedEvidence = mergeDeclaredValues(existingProposalEvidence, created);
|
|
182
|
-
const mergedEvidenceText = formatDeclaredValues(mergedEvidence);
|
|
183
|
-
|
|
184
|
-
const updatedProposal = upsertProposalEvidencePath(proposalText, mergedEvidenceText);
|
|
185
|
-
if (updatedProposal.changed) {
|
|
186
|
-
await deps.writeText(path.join(changeDir, "proposal.md"), updatedProposal.text);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
let planUpdated = false;
|
|
190
|
-
if (planAbs && planText) {
|
|
191
|
-
const existingPlanEvidence = deps
|
|
192
|
-
.splitDeclaredValues(deps.extractId("Evidence_Path", planText) || deps.extractId("Evidence_Path(s)", planText))
|
|
193
|
-
.map((item) => String(item || "").replace(/^\.\//, ""));
|
|
194
|
-
const mergedPlanEvidence = mergeDeclaredValues(existingPlanEvidence, mergedEvidence);
|
|
195
|
-
const mergedPlanEvidenceText = formatDeclaredValues(mergedPlanEvidence);
|
|
196
|
-
const updatedPlan = upsertIdLine(planText, ["Evidence_Path", "Evidence_Path(s)"], mergedPlanEvidenceText, { insertAfterLabels: ["Plan_File", "Contract_Row"] });
|
|
197
|
-
if (updatedPlan.changed) {
|
|
198
|
-
await deps.writeText(planAbs, updatedPlan.text);
|
|
199
|
-
planUpdated = true;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
await deps.appendMetricsEvent(gitRoot, changeId, "evidence", {
|
|
204
|
-
generated_at: deps.nowIsoUtc(),
|
|
205
|
-
created: created.length,
|
|
206
|
-
strict_validate: options.noValidate ? "skipped" : validateRes && validateRes.ok === true ? "ok" : "failed",
|
|
207
|
-
allow_fail: options.allowFail === true,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
/** @type {string[]} */
|
|
211
|
-
const outputLines = [];
|
|
212
|
-
outputLines.push(`✓ aiws change evidence: ${changeId}`);
|
|
213
|
-
outputLines.push(`dir: ${path.relative(gitRoot, changeDir)}`);
|
|
214
|
-
if (planRel) outputLines.push(`Plan_File: ${planRel}`);
|
|
215
|
-
outputLines.push("created:");
|
|
216
|
-
if (created.length > 0) {
|
|
217
|
-
for (const item of created) outputLines.push(`- ${item}`);
|
|
218
|
-
} else {
|
|
219
|
-
outputLines.push("- (none)");
|
|
220
|
-
}
|
|
221
|
-
outputLines.push("updated:");
|
|
222
|
-
outputLines.push(`- ${path.relative(gitRoot, path.join(changeDir, "proposal.md"))} (Evidence_Path)`);
|
|
223
|
-
if (planRel && planText && planUpdated) outputLines.push(`- ${planRel} (Evidence_Path)`);
|
|
224
|
-
else if (planRel && planText) outputLines.push(`- ${planRel} (Evidence_Path)`);
|
|
225
|
-
outputLines.push("");
|
|
226
|
-
outputLines.push("next:");
|
|
227
|
-
outputLines.push(`- ${deps.planVerifyHint(changeId)}`);
|
|
228
|
-
if (validateFailed) outputLines.push("- fix validation errors, then re-run evidence (or run with --allow-fail to proceed)");
|
|
229
|
-
else outputLines.push(`- verify evidence gate: \`aiws change validate ${changeId} --strict --check-evidence\``);
|
|
230
|
-
|
|
231
|
-
let errorToThrow = null;
|
|
232
|
-
if (validateFailed && !options.allowFail) {
|
|
233
|
-
const details = (validateRes && validateRes.raw ? String(validateRes.raw) : "").trim() || "Validation failed (see evidence JSON for details).";
|
|
234
|
-
const hint = validatePathForError ? `\n\nevidence:\n- ${path.relative(gitRoot, validatePathForError)}` : "";
|
|
235
|
-
errorToThrow = new deps.UserError("Failed to generate evidence due to strict validation errors.", { details: `${details}${hint}` });
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return {
|
|
239
|
-
output: outputLines.join("\n") + "\n",
|
|
240
|
-
errorToThrow,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @param {{
|
|
5
|
-
* gitRoot: string,
|
|
6
|
-
* changeId: string,
|
|
7
|
-
* options: { noValidate: boolean, allowFail: boolean }
|
|
8
|
-
* }} input
|
|
9
|
-
* @param {{
|
|
10
|
-
* changeDirAbs: (gitRoot: string, changeId: string) => string,
|
|
11
|
-
* fileState: (changeDir: string, rel: string) => Promise<any>,
|
|
12
|
-
* pathExists: (path: string) => Promise<boolean>,
|
|
13
|
-
* runChangeEvidenceWorkflow: (input: any, deps: any) => Promise<{ output: string, errorToThrow: any }>,
|
|
14
|
-
* workflowDeps: Record<string, any>,
|
|
15
|
-
* UserError: typeof import("../errors.js").UserError
|
|
16
|
-
* }} deps
|
|
17
|
-
*/
|
|
18
|
-
export async function runChangeEvidenceCommand(input, deps) {
|
|
19
|
-
const { gitRoot, changeId, options } = input;
|
|
20
|
-
const changeDir = deps.changeDirAbs(gitRoot, changeId);
|
|
21
|
-
if (!(await deps.pathExists(changeDir))) {
|
|
22
|
-
throw new deps.UserError(`Missing change dir: ${path.relative(gitRoot, changeDir)}`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const proposal = await deps.fileState(changeDir, "proposal.md");
|
|
26
|
-
if (proposal.state !== "ok") {
|
|
27
|
-
throw new deps.UserError("proposal.md is required for evidence generation.", { details: path.relative(gitRoot, proposal.abs) });
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return deps.runChangeEvidenceWorkflow(
|
|
31
|
-
{
|
|
32
|
-
gitRoot,
|
|
33
|
-
changeId,
|
|
34
|
-
changeDir,
|
|
35
|
-
proposalText: proposal.text,
|
|
36
|
-
options,
|
|
37
|
-
},
|
|
38
|
-
deps.workflowDeps,
|
|
39
|
-
);
|
|
40
|
-
}
|