triflux 10.9.19 → 10.9.21
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/CLAUDE.md +212 -0
- package/hub/lib/bash-path.mjs +73 -0
- package/hub/team/dashboard-open.mjs +1 -68
- package/hub/team/native-supervisor.mjs +9 -2
- package/hub/team/psmux.mjs +5 -13
- package/hub/team/session.mjs +6 -26
- package/hub/team/swarm-hypervisor.mjs +205 -27
- package/hub/team/synapse-http.mjs +1 -0
- package/hub/team/tui-core.mjs +292 -0
- package/hub/team/tui-lite.mjs +20 -154
- package/hub/team/tui-synapse.mjs +213 -0
- package/hub/team/tui-widgets.mjs +262 -0
- package/hub/team/tui.mjs +159 -255
- package/hub/workers/delegator-mcp.mjs +2 -2
- package/package.json +21 -62
- package/references/hosts.json +46 -0
- package/scripts/__tests__/keyword-detector.test.mjs +4 -4
- package/scripts/cross-review-gate.mjs +13 -0
- package/scripts/remote-spawn.mjs +11 -46
- package/scripts/session-spawn-helper.mjs +8 -21
- package/scripts/test-tfx-route-no-claude-native.mjs +4 -2
- package/scripts/tfx-route.sh +13 -0
- package/skills/tfx-deep-interview/SKILL.md +6 -6
- package/skills/tfx-deep-interview/SKILL.md.tmpl +6 -6
- package/skills/tfx-index/SKILL.md +1 -1
- package/skills/tfx-index/SKILL.md.tmpl +1 -1
- package/skills/tfx-interview/SKILL.md +9 -9
- package/skills/tfx-interview/SKILL.md.tmpl +9 -9
- package/skills/tfx-plan/SKILL.md +1 -1
- package/skills/tfx-plan/SKILL.md.tmpl +1 -1
- package/skills/tfx-research/SKILL.md +1 -1
- package/skills/tfx-research/SKILL.md.tmpl +1 -1
- package/skills/tfx-workspace/async-tests/run-tests.sh +203 -0
- package/skills/tfx-workspace/evals/evals.json +79 -0
- package/skills/tfx-workspace/iteration-1/benchmark.json +524 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +25 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +154 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +25 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +126 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +25 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +119 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +25 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +115 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +10 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +20 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +86 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +20 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +81 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +316 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +352 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/review.html +1325 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +97 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +94 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +209 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +193 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/benchmark.json +144 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +13 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +35 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +382 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +35 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +333 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/review.html +1325 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +217 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +77 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +65 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +94 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +82 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +133 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +426 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +101 -0
- package/.claude-plugin/marketplace.json +0 -34
- package/.claude-plugin/plugin.json +0 -22
- package/config/mcp-registry.json +0 -29
- package/scripts/__tests__/release-governance.test.mjs +0 -148
- package/scripts/release/bump-version.mjs +0 -77
- package/scripts/release/check-sync.mjs +0 -51
- package/scripts/release/lib.mjs +0 -303
- package/scripts/release/prepare.mjs +0 -85
- package/scripts/release/publish.mjs +0 -87
- package/scripts/release/verify.mjs +0 -81
- package/scripts/release/version-manifest.json +0 -26
- package/tui/codex-profile.mjs +0 -457
- package/tui/core.mjs +0 -266
- package/tui/doctor.mjs +0 -375
- package/tui/gemini-profile.mjs +0 -299
- package/tui/monitor-data.mjs +0 -152
- package/tui/monitor.mjs +0 -339
- package/tui/setup.mjs +0 -598
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { describe, it } from "node:test";
|
|
6
|
-
|
|
7
|
-
import { bumpVersion } from "../release/bump-version.mjs";
|
|
8
|
-
import { assertVersionSync, writeJson } from "../release/lib.mjs";
|
|
9
|
-
import { prepareRelease } from "../release/prepare.mjs";
|
|
10
|
-
import { publishRelease } from "../release/publish.mjs";
|
|
11
|
-
import { verifyRelease } from "../release/verify.mjs";
|
|
12
|
-
|
|
13
|
-
function makeRepo() {
|
|
14
|
-
const root = mkdtempSync(join(tmpdir(), "tfx-release-"));
|
|
15
|
-
mkdirSync(join(root, "scripts", "release"), { recursive: true });
|
|
16
|
-
mkdirSync(join(root, ".claude-plugin"), { recursive: true });
|
|
17
|
-
mkdirSync(join(root, "packages", "triflux"), { recursive: true });
|
|
18
|
-
mkdirSync(join(root, ".omx", "plans"), { recursive: true });
|
|
19
|
-
|
|
20
|
-
writeJson(join(root, "package.json"), { name: "triflux", version: "1.2.3" });
|
|
21
|
-
writeJson(join(root, "packages", "triflux", "package.json"), {
|
|
22
|
-
name: "triflux",
|
|
23
|
-
version: "1.2.0",
|
|
24
|
-
});
|
|
25
|
-
writeJson(join(root, ".claude-plugin", "plugin.json"), {
|
|
26
|
-
name: "triflux",
|
|
27
|
-
version: "1.1.0",
|
|
28
|
-
});
|
|
29
|
-
writeJson(join(root, ".claude-plugin", "marketplace.json"), {
|
|
30
|
-
version: "1.2.0",
|
|
31
|
-
plugins: [{ name: "triflux", version: "1.0.0" }],
|
|
32
|
-
});
|
|
33
|
-
writeJson(join(root, "package-lock.json"), {
|
|
34
|
-
name: "triflux",
|
|
35
|
-
version: "1.2.0",
|
|
36
|
-
packages: {
|
|
37
|
-
"": {
|
|
38
|
-
version: "1.0.0",
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
writeJson(join(root, "scripts", "release", "version-manifest.json"), {
|
|
43
|
-
canonicalFile: "package.json",
|
|
44
|
-
canonicalPath: ["version"],
|
|
45
|
-
targets: [
|
|
46
|
-
{ file: "package.json", paths: [["version"]] },
|
|
47
|
-
{ file: "packages/triflux/package.json", paths: [["version"]] },
|
|
48
|
-
{ file: ".claude-plugin/plugin.json", paths: [["version"]] },
|
|
49
|
-
{
|
|
50
|
-
file: ".claude-plugin/marketplace.json",
|
|
51
|
-
paths: [["version"], ["plugins", 0, "version"]],
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
file: "package-lock.json",
|
|
55
|
-
paths: [["version"], ["packages", "", "version"]],
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
});
|
|
59
|
-
return root;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
describe("release governance scripts", () => {
|
|
63
|
-
it("assertVersionSync detects mismatches and fixes them", () => {
|
|
64
|
-
const root = makeRepo();
|
|
65
|
-
try {
|
|
66
|
-
const before = assertVersionSync({ rootDir: root });
|
|
67
|
-
assert.equal(before.ok, false);
|
|
68
|
-
assert.ok(before.mismatches.length >= 4);
|
|
69
|
-
|
|
70
|
-
const after = assertVersionSync({ rootDir: root, fix: true });
|
|
71
|
-
assert.equal(after.ok, true);
|
|
72
|
-
assert.deepEqual(
|
|
73
|
-
JSON.parse(readFileSync(join(root, ".claude-plugin", "plugin.json"))),
|
|
74
|
-
{ name: "triflux", version: "1.2.3" },
|
|
75
|
-
);
|
|
76
|
-
} finally {
|
|
77
|
-
rmSync(root, { recursive: true, force: true });
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("bumpVersion writes canonical version and syncs targets", async () => {
|
|
82
|
-
const root = makeRepo();
|
|
83
|
-
try {
|
|
84
|
-
const result = await bumpVersion({
|
|
85
|
-
rootDir: root,
|
|
86
|
-
nextVersion: "2.0.0",
|
|
87
|
-
write: true,
|
|
88
|
-
});
|
|
89
|
-
assert.equal(result.ok, true);
|
|
90
|
-
assert.equal(
|
|
91
|
-
JSON.parse(readFileSync(join(root, "package.json"), "utf8")).version,
|
|
92
|
-
"2.0.0",
|
|
93
|
-
);
|
|
94
|
-
assert.equal(
|
|
95
|
-
JSON.parse(
|
|
96
|
-
readFileSync(
|
|
97
|
-
join(root, "packages", "triflux", "package.json"),
|
|
98
|
-
"utf8",
|
|
99
|
-
),
|
|
100
|
-
).version,
|
|
101
|
-
"2.0.0",
|
|
102
|
-
);
|
|
103
|
-
} finally {
|
|
104
|
-
rmSync(root, { recursive: true, force: true });
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("prepare/publish/verify support dry-run planning", async () => {
|
|
109
|
-
const root = makeRepo();
|
|
110
|
-
try {
|
|
111
|
-
assertVersionSync({ rootDir: root, fix: true });
|
|
112
|
-
const execStub = (command, args) => {
|
|
113
|
-
if (command === "git" && args[0] === "status") return "";
|
|
114
|
-
if (command === "git" && args[0] === "describe") return "v1.2.2";
|
|
115
|
-
if (command === "git" && args[0] === "log")
|
|
116
|
-
return "abc1234 feat: sample\n";
|
|
117
|
-
return "";
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const prepare = await prepareRelease({
|
|
121
|
-
rootDir: root,
|
|
122
|
-
version: "1.2.3",
|
|
123
|
-
allowDirty: true,
|
|
124
|
-
dryRun: true,
|
|
125
|
-
execFileSyncFn: execStub,
|
|
126
|
-
});
|
|
127
|
-
assert.equal(prepare.ok, true);
|
|
128
|
-
assert.equal(prepare.commands.length, 3);
|
|
129
|
-
|
|
130
|
-
const publish = await publishRelease({
|
|
131
|
-
rootDir: root,
|
|
132
|
-
version: "1.2.3",
|
|
133
|
-
dryRun: true,
|
|
134
|
-
});
|
|
135
|
-
assert.equal(publish.steps.length >= 3, true);
|
|
136
|
-
|
|
137
|
-
const verify = await verifyRelease({
|
|
138
|
-
rootDir: root,
|
|
139
|
-
version: "1.2.3",
|
|
140
|
-
dryRun: true,
|
|
141
|
-
});
|
|
142
|
-
assert.equal(verify.ok, true);
|
|
143
|
-
assert.equal(verify.checks[1].name, "npm-view");
|
|
144
|
-
} finally {
|
|
145
|
-
rmSync(root, { recursive: true, force: true });
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
});
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import {
|
|
5
|
-
assertVersionSync,
|
|
6
|
-
isSemver,
|
|
7
|
-
parseArgs,
|
|
8
|
-
ROOT,
|
|
9
|
-
readJson,
|
|
10
|
-
syncVersionTargets,
|
|
11
|
-
writeJson,
|
|
12
|
-
} from "./lib.mjs";
|
|
13
|
-
|
|
14
|
-
export async function bumpVersion({
|
|
15
|
-
nextVersion,
|
|
16
|
-
rootDir = ROOT,
|
|
17
|
-
write = false,
|
|
18
|
-
} = {}) {
|
|
19
|
-
if (!isSemver(nextVersion)) {
|
|
20
|
-
throw new Error(`Invalid semver version: ${nextVersion}`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const packagePath = join(rootDir, "package.json");
|
|
24
|
-
const packageJson = readJson(packagePath);
|
|
25
|
-
const previousVersion = packageJson.version;
|
|
26
|
-
packageJson.version = nextVersion;
|
|
27
|
-
|
|
28
|
-
if (write) {
|
|
29
|
-
writeJson(packagePath, packageJson);
|
|
30
|
-
const syncedFiles = syncVersionTargets({
|
|
31
|
-
rootDir,
|
|
32
|
-
expectedVersion: nextVersion,
|
|
33
|
-
});
|
|
34
|
-
const post = assertVersionSync({ rootDir, expectedVersion: nextVersion });
|
|
35
|
-
return {
|
|
36
|
-
ok: post.ok,
|
|
37
|
-
previousVersion,
|
|
38
|
-
nextVersion,
|
|
39
|
-
updatedFiles: ["package.json", ...syncedFiles],
|
|
40
|
-
targets: post.targets,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const preview = assertVersionSync({
|
|
45
|
-
rootDir,
|
|
46
|
-
expectedVersion: nextVersion,
|
|
47
|
-
});
|
|
48
|
-
return {
|
|
49
|
-
ok: true,
|
|
50
|
-
previousVersion,
|
|
51
|
-
nextVersion,
|
|
52
|
-
updatedFiles: ["package.json"],
|
|
53
|
-
targets: preview.targets.map((target) => ({
|
|
54
|
-
...target,
|
|
55
|
-
expected: nextVersion,
|
|
56
|
-
inSync: target.file === "package.json",
|
|
57
|
-
})),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
62
|
-
const args = parseArgs(process.argv.slice(2));
|
|
63
|
-
const nextVersion = args.next || args.version;
|
|
64
|
-
const result = await bumpVersion({
|
|
65
|
-
nextVersion,
|
|
66
|
-
rootDir: args.root,
|
|
67
|
-
write: Boolean(args.write),
|
|
68
|
-
});
|
|
69
|
-
if (args.json) {
|
|
70
|
-
console.log(JSON.stringify(result, null, 2));
|
|
71
|
-
} else {
|
|
72
|
-
console.log(
|
|
73
|
-
`${args.write ? "Bumped" : "Planned"} version ${result.previousVersion} -> ${result.nextVersion}`,
|
|
74
|
-
);
|
|
75
|
-
console.log(`Updated files: ${result.updatedFiles.join(", ")}`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
|
|
4
|
-
import { assertVersionSync, formatPathSegments, parseArgs } from "./lib.mjs";
|
|
5
|
-
|
|
6
|
-
export { assertVersionSync } from "./lib.mjs";
|
|
7
|
-
|
|
8
|
-
function toJson(result) {
|
|
9
|
-
return {
|
|
10
|
-
ok: result.ok,
|
|
11
|
-
rootVersion: result.rootVersion,
|
|
12
|
-
fixedFiles: result.fixedFiles,
|
|
13
|
-
mismatches: result.mismatches.map((target) => ({
|
|
14
|
-
file: target.file,
|
|
15
|
-
path: formatPathSegments(target.path),
|
|
16
|
-
found: target.found ?? null,
|
|
17
|
-
expected: target.expected,
|
|
18
|
-
missing: target.missing,
|
|
19
|
-
})),
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function printHuman(result) {
|
|
24
|
-
if (result.ok) {
|
|
25
|
-
console.log(`Version sync OK (${result.rootVersion})`);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
console.log(`Version sync mismatch (${result.rootVersion})`);
|
|
29
|
-
for (const target of result.mismatches) {
|
|
30
|
-
console.log(
|
|
31
|
-
`- ${target.file} :: ${formatPathSegments(target.path)} => found=${target.found ?? "missing"}, expected=${target.expected}`,
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
if (result.fixedFiles.length) {
|
|
35
|
-
console.log(`Fixed files: ${result.fixedFiles.join(", ")}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
40
|
-
const args = parseArgs(process.argv.slice(2));
|
|
41
|
-
const result = assertVersionSync({
|
|
42
|
-
rootDir: args.root,
|
|
43
|
-
fix: Boolean(args.fix),
|
|
44
|
-
});
|
|
45
|
-
if (args.json) {
|
|
46
|
-
console.log(JSON.stringify(toJson(result), null, 2));
|
|
47
|
-
} else {
|
|
48
|
-
printHuman(result);
|
|
49
|
-
}
|
|
50
|
-
process.exitCode = result.ok ? 0 : 1;
|
|
51
|
-
}
|
package/scripts/release/lib.mjs
DELETED
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join, resolve } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
|
|
6
|
-
export const ROOT = resolve(
|
|
7
|
-
dirname(fileURLToPath(import.meta.url)),
|
|
8
|
-
"..",
|
|
9
|
-
"..",
|
|
10
|
-
);
|
|
11
|
-
export const DEFAULT_MANIFEST_PATH = join(
|
|
12
|
-
ROOT,
|
|
13
|
-
"scripts",
|
|
14
|
-
"release",
|
|
15
|
-
"version-manifest.json",
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
export function parseArgs(argv) {
|
|
19
|
-
const args = {};
|
|
20
|
-
for (let i = 0; i < argv.length; i++) {
|
|
21
|
-
const token = argv[i];
|
|
22
|
-
if (!token.startsWith("--")) continue;
|
|
23
|
-
const key = token.slice(2);
|
|
24
|
-
const next = argv[i + 1];
|
|
25
|
-
if (!next || next.startsWith("--")) {
|
|
26
|
-
args[key] = true;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
args[key] = next;
|
|
30
|
-
i++;
|
|
31
|
-
}
|
|
32
|
-
return args;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function readJson(filePath) {
|
|
36
|
-
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function writeJson(filePath, value) {
|
|
40
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
41
|
-
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function getValueAtPath(obj, pathSegments) {
|
|
45
|
-
return pathSegments.reduce((acc, segment) => {
|
|
46
|
-
if (acc === undefined || acc === null) return undefined;
|
|
47
|
-
return acc[segment];
|
|
48
|
-
}, obj);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function setValueAtPath(obj, pathSegments, value) {
|
|
52
|
-
if (!pathSegments.length) {
|
|
53
|
-
throw new Error("pathSegments must not be empty");
|
|
54
|
-
}
|
|
55
|
-
let cursor = obj;
|
|
56
|
-
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
57
|
-
const segment = pathSegments[i];
|
|
58
|
-
const nextSegment = pathSegments[i + 1];
|
|
59
|
-
if (
|
|
60
|
-
cursor[segment] === undefined ||
|
|
61
|
-
cursor[segment] === null ||
|
|
62
|
-
typeof cursor[segment] !== "object"
|
|
63
|
-
) {
|
|
64
|
-
cursor[segment] = typeof nextSegment === "number" ? [] : {};
|
|
65
|
-
}
|
|
66
|
-
cursor = cursor[segment];
|
|
67
|
-
}
|
|
68
|
-
cursor[pathSegments.at(-1)] = value;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function formatPathSegments(pathSegments) {
|
|
72
|
-
return pathSegments
|
|
73
|
-
.map((segment) =>
|
|
74
|
-
typeof segment === "number"
|
|
75
|
-
? `[${segment}]`
|
|
76
|
-
: segment === ""
|
|
77
|
-
? '[""]'
|
|
78
|
-
: `.${segment}`,
|
|
79
|
-
)
|
|
80
|
-
.join("")
|
|
81
|
-
.replace(/^\./, "");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function isSemver(value) {
|
|
85
|
-
return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(String(value || "").trim());
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function loadVersionManifest({
|
|
89
|
-
rootDir = ROOT,
|
|
90
|
-
manifestPath = join(rootDir, "scripts", "release", "version-manifest.json"),
|
|
91
|
-
} = {}) {
|
|
92
|
-
const manifest = readJson(manifestPath);
|
|
93
|
-
if (!manifest.canonicalFile || !Array.isArray(manifest.targets)) {
|
|
94
|
-
throw new Error(`Invalid version manifest: ${manifestPath}`);
|
|
95
|
-
}
|
|
96
|
-
return manifest;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function getCanonicalVersion({
|
|
100
|
-
rootDir = ROOT,
|
|
101
|
-
manifest = loadVersionManifest({ rootDir }),
|
|
102
|
-
} = {}) {
|
|
103
|
-
const canonicalPath = join(rootDir, manifest.canonicalFile);
|
|
104
|
-
const canonicalJson = readJson(canonicalPath);
|
|
105
|
-
const value = getValueAtPath(
|
|
106
|
-
canonicalJson,
|
|
107
|
-
manifest.canonicalPath || ["version"],
|
|
108
|
-
);
|
|
109
|
-
if (!isSemver(value)) {
|
|
110
|
-
throw new Error(
|
|
111
|
-
`Canonical version is missing or invalid at ${manifest.canonicalFile}`,
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
return value;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function collectVersionTargets({
|
|
118
|
-
rootDir = ROOT,
|
|
119
|
-
manifest = loadVersionManifest({ rootDir }),
|
|
120
|
-
expectedVersion = getCanonicalVersion({ rootDir, manifest }),
|
|
121
|
-
} = {}) {
|
|
122
|
-
return manifest.targets.flatMap((target) => {
|
|
123
|
-
const absolutePath = join(rootDir, target.file);
|
|
124
|
-
if (!existsSync(absolutePath)) {
|
|
125
|
-
return target.paths.map((pathSegments) => ({
|
|
126
|
-
file: target.file,
|
|
127
|
-
absolutePath,
|
|
128
|
-
path: pathSegments,
|
|
129
|
-
found: undefined,
|
|
130
|
-
expected: expectedVersion,
|
|
131
|
-
inSync: false,
|
|
132
|
-
missing: true,
|
|
133
|
-
}));
|
|
134
|
-
}
|
|
135
|
-
const json = readJson(absolutePath);
|
|
136
|
-
return target.paths.map((pathSegments) => {
|
|
137
|
-
const found = getValueAtPath(json, pathSegments);
|
|
138
|
-
return {
|
|
139
|
-
file: target.file,
|
|
140
|
-
absolutePath,
|
|
141
|
-
path: pathSegments,
|
|
142
|
-
found,
|
|
143
|
-
expected: expectedVersion,
|
|
144
|
-
inSync: found === expectedVersion,
|
|
145
|
-
missing: false,
|
|
146
|
-
};
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export function syncVersionTargets({
|
|
152
|
-
rootDir = ROOT,
|
|
153
|
-
manifest = loadVersionManifest({ rootDir }),
|
|
154
|
-
expectedVersion = getCanonicalVersion({ rootDir, manifest }),
|
|
155
|
-
} = {}) {
|
|
156
|
-
const touched = new Set();
|
|
157
|
-
for (const target of manifest.targets) {
|
|
158
|
-
const absolutePath = join(rootDir, target.file);
|
|
159
|
-
if (!existsSync(absolutePath)) {
|
|
160
|
-
throw new Error(`Cannot sync missing target: ${target.file}`);
|
|
161
|
-
}
|
|
162
|
-
const json = readJson(absolutePath);
|
|
163
|
-
let changed = false;
|
|
164
|
-
for (const pathSegments of target.paths) {
|
|
165
|
-
if (getValueAtPath(json, pathSegments) !== expectedVersion) {
|
|
166
|
-
setValueAtPath(json, pathSegments, expectedVersion);
|
|
167
|
-
changed = true;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
if (changed) {
|
|
171
|
-
writeJson(absolutePath, json);
|
|
172
|
-
touched.add(target.file);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return [...touched];
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function assertVersionSync({
|
|
179
|
-
rootDir = ROOT,
|
|
180
|
-
manifestPath = join(rootDir, "scripts", "release", "version-manifest.json"),
|
|
181
|
-
expectedVersion,
|
|
182
|
-
fix = false,
|
|
183
|
-
} = {}) {
|
|
184
|
-
const manifest = loadVersionManifest({ rootDir, manifestPath });
|
|
185
|
-
const rootVersion =
|
|
186
|
-
expectedVersion || getCanonicalVersion({ rootDir, manifest });
|
|
187
|
-
let targets = collectVersionTargets({
|
|
188
|
-
rootDir,
|
|
189
|
-
manifest,
|
|
190
|
-
expectedVersion: rootVersion,
|
|
191
|
-
});
|
|
192
|
-
const mismatches = targets.filter((target) => !target.inSync);
|
|
193
|
-
let fixedFiles = [];
|
|
194
|
-
|
|
195
|
-
if (fix && mismatches.length) {
|
|
196
|
-
fixedFiles = syncVersionTargets({
|
|
197
|
-
rootDir,
|
|
198
|
-
manifest,
|
|
199
|
-
expectedVersion: rootVersion,
|
|
200
|
-
});
|
|
201
|
-
targets = collectVersionTargets({
|
|
202
|
-
rootDir,
|
|
203
|
-
manifest,
|
|
204
|
-
expectedVersion: rootVersion,
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
ok: targets.every((target) => target.inSync),
|
|
210
|
-
rootVersion,
|
|
211
|
-
targets,
|
|
212
|
-
mismatches: targets.filter((target) => !target.inSync),
|
|
213
|
-
fixedFiles,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export function ensureGitClean({
|
|
218
|
-
rootDir = ROOT,
|
|
219
|
-
execFileSyncFn = execFileSync,
|
|
220
|
-
} = {}) {
|
|
221
|
-
const output = execFileSyncFn("git", ["status", "--porcelain"], {
|
|
222
|
-
cwd: rootDir,
|
|
223
|
-
encoding: "utf8",
|
|
224
|
-
}).trim();
|
|
225
|
-
return { clean: output.length === 0, output };
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export function getPreviousTag({
|
|
229
|
-
rootDir = ROOT,
|
|
230
|
-
execFileSyncFn = execFileSync,
|
|
231
|
-
} = {}) {
|
|
232
|
-
try {
|
|
233
|
-
return execFileSyncFn("git", ["describe", "--tags", "--abbrev=0"], {
|
|
234
|
-
cwd: rootDir,
|
|
235
|
-
encoding: "utf8",
|
|
236
|
-
}).trim();
|
|
237
|
-
} catch {
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export function getCommitSummaries({
|
|
243
|
-
rootDir = ROOT,
|
|
244
|
-
previousTag,
|
|
245
|
-
execFileSyncFn = execFileSync,
|
|
246
|
-
} = {}) {
|
|
247
|
-
const range = previousTag ? `${previousTag}..HEAD` : "HEAD~10..HEAD";
|
|
248
|
-
try {
|
|
249
|
-
return execFileSyncFn("git", ["log", "--oneline", range], {
|
|
250
|
-
cwd: rootDir,
|
|
251
|
-
encoding: "utf8",
|
|
252
|
-
})
|
|
253
|
-
.trim()
|
|
254
|
-
.split(/\r?\n/)
|
|
255
|
-
.filter(Boolean);
|
|
256
|
-
} catch {
|
|
257
|
-
return [];
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export function buildReleaseNotes({
|
|
262
|
-
version,
|
|
263
|
-
rootDir = ROOT,
|
|
264
|
-
execFileSyncFn = execFileSync,
|
|
265
|
-
} = {}) {
|
|
266
|
-
const previousTag = getPreviousTag({ rootDir, execFileSyncFn });
|
|
267
|
-
const commits = getCommitSummaries({
|
|
268
|
-
rootDir,
|
|
269
|
-
previousTag,
|
|
270
|
-
execFileSyncFn,
|
|
271
|
-
});
|
|
272
|
-
const heading = previousTag
|
|
273
|
-
? `Changes since ${previousTag}`
|
|
274
|
-
: "Recent changes (no prior tag found)";
|
|
275
|
-
const lines = commits.length
|
|
276
|
-
? commits.map((commit) => `- ${commit}`)
|
|
277
|
-
: ["- No commit summary available"];
|
|
278
|
-
|
|
279
|
-
return [
|
|
280
|
-
`# Release v${version}`,
|
|
281
|
-
"",
|
|
282
|
-
`## ${heading}`,
|
|
283
|
-
...lines,
|
|
284
|
-
"",
|
|
285
|
-
"## Install",
|
|
286
|
-
`- npm: \`npm install -g triflux@${version}\``,
|
|
287
|
-
"- Claude Code:",
|
|
288
|
-
" - `/plugin marketplace add tellang/triflux`",
|
|
289
|
-
" - `/plugin install triflux@tellang`",
|
|
290
|
-
"",
|
|
291
|
-
].join("\n");
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export function runCommand(
|
|
295
|
-
command,
|
|
296
|
-
args,
|
|
297
|
-
{ cwd = ROOT, execFileSyncFn = execFileSync } = {},
|
|
298
|
-
) {
|
|
299
|
-
execFileSyncFn(command, args, {
|
|
300
|
-
cwd,
|
|
301
|
-
stdio: "inherit",
|
|
302
|
-
});
|
|
303
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { writeFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import {
|
|
6
|
-
assertVersionSync,
|
|
7
|
-
buildReleaseNotes,
|
|
8
|
-
ensureGitClean,
|
|
9
|
-
parseArgs,
|
|
10
|
-
ROOT,
|
|
11
|
-
runCommand,
|
|
12
|
-
} from "./lib.mjs";
|
|
13
|
-
|
|
14
|
-
export async function prepareRelease({
|
|
15
|
-
version,
|
|
16
|
-
rootDir = ROOT,
|
|
17
|
-
allowDirty = false,
|
|
18
|
-
dryRun = true,
|
|
19
|
-
execFileSyncFn,
|
|
20
|
-
} = {}) {
|
|
21
|
-
const sync = assertVersionSync({ rootDir });
|
|
22
|
-
if (!sync.ok) {
|
|
23
|
-
throw new Error(
|
|
24
|
-
"Version sync failed. Run scripts/release/check-sync.mjs first.",
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const gitState = ensureGitClean({ rootDir, execFileSyncFn });
|
|
29
|
-
if (!gitState.clean && !allowDirty) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
"Working tree is dirty. Re-run with --allow-dirty only for scaffolding.",
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const releaseVersion = version || sync.rootVersion;
|
|
36
|
-
const commands = [
|
|
37
|
-
["npm", ["test"]],
|
|
38
|
-
["npm", ["run", "lint"]],
|
|
39
|
-
["npm", ["pack", "--dry-run"]],
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
if (!dryRun) {
|
|
43
|
-
for (const [command, args] of commands) {
|
|
44
|
-
runCommand(command, args, { cwd: rootDir, execFileSyncFn });
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const notes = buildReleaseNotes({
|
|
49
|
-
version: releaseVersion,
|
|
50
|
-
rootDir,
|
|
51
|
-
execFileSyncFn,
|
|
52
|
-
});
|
|
53
|
-
const notesPath = join(
|
|
54
|
-
rootDir,
|
|
55
|
-
".omx",
|
|
56
|
-
"plans",
|
|
57
|
-
`release-notes-v${releaseVersion}.md`,
|
|
58
|
-
);
|
|
59
|
-
writeFileSync(notesPath, notes, "utf8");
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
ok: true,
|
|
63
|
-
version: releaseVersion,
|
|
64
|
-
clean: gitState.clean,
|
|
65
|
-
allowDirty,
|
|
66
|
-
dryRun,
|
|
67
|
-
commands: commands.map(([command, args]) => [command, ...args].join(" ")),
|
|
68
|
-
releaseNotesPath: notesPath,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
73
|
-
const args = parseArgs(process.argv.slice(2));
|
|
74
|
-
const result = await prepareRelease({
|
|
75
|
-
version: args.version,
|
|
76
|
-
rootDir: args.root,
|
|
77
|
-
allowDirty: Boolean(args["allow-dirty"]),
|
|
78
|
-
dryRun: !args.execute,
|
|
79
|
-
});
|
|
80
|
-
if (args.json) {
|
|
81
|
-
console.log(JSON.stringify(result, null, 2));
|
|
82
|
-
} else {
|
|
83
|
-
console.log(JSON.stringify(result, null, 2));
|
|
84
|
-
}
|
|
85
|
-
}
|