@rosenfan/matrix 0.1.1 → 0.1.2
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/skills/matrix/SKILL.md +62 -21
- package/.claude/skills/matrix/agents/openai.yaml +4 -4
- package/.claude/skills/matrix/scripts/matrix_claude.py +11 -166
- package/.claude/skills/matrix/scripts/matrix_state.py +60 -114
- package/.claude/skills/matrix-archive/SKILL.md +33 -26
- package/.claude/skills/matrix-build/SKILL.md +26 -39
- package/.claude/skills/matrix-claude/SKILL.md +22 -22
- package/.claude/skills/matrix-design/SKILL.md +28 -29
- package/.claude/skills/matrix-hotfix/SKILL.md +8 -8
- package/.claude/skills/matrix-open/SKILL.md +17 -20
- package/.claude/skills/matrix-status/SKILL.md +4 -3
- package/.claude/skills/matrix-tweak/SKILL.md +8 -8
- package/.claude/skills/matrix-verify/SKILL.md +18 -29
- package/LICENSE +21 -21
- package/README.md +255 -242
- package/README.zh-CN.md +254 -242
- package/assets/skills-zh-CN/matrix/SKILL.md +57 -5
- package/assets/skills-zh-CN/matrix-archive/SKILL.md +23 -2
- package/assets/skills-zh-CN/matrix-build/SKILL.md +26 -2
- package/assets/skills-zh-CN/matrix-claude/SKILL.md +12 -11
- package/assets/skills-zh-CN/matrix-design/SKILL.md +16 -11
- package/assets/skills-zh-CN/matrix-hotfix/SKILL.md +8 -8
- package/assets/skills-zh-CN/matrix-open/SKILL.md +16 -11
- package/assets/skills-zh-CN/matrix-status/SKILL.md +12 -11
- package/assets/skills-zh-CN/matrix-tweak/SKILL.md +8 -8
- package/assets/skills-zh-CN/matrix-verify/SKILL.md +22 -8
- package/bin/matrix.js +15 -15
- package/package.json +56 -56
- package/src/catalog.js +85 -80
- package/src/cli.js +116 -111
- package/src/distribution.js +216 -152
- package/src/installer.js +45 -45
- package/src/matt.js +45 -45
- package/src/messages.js +33 -33
- package/src/ui.js +89 -89
- package/src/workflow-transaction.js +622 -0
- package/src/workflow.js +547 -74
package/src/cli.js
CHANGED
|
@@ -1,117 +1,122 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { PLATFORMS } from "./catalog.js";
|
|
5
|
-
import { createDistribution } from "./distribution.js";
|
|
6
|
-
import { text } from "./messages.js";
|
|
7
|
-
import { createUi } from "./ui.js";
|
|
8
|
-
import { invoke } from "./workflow.js";
|
|
9
|
-
|
|
10
|
-
const packagePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
11
|
-
const version = JSON.parse(fs.readFileSync(packagePath, "utf8")).version;
|
|
12
|
-
const HELP = `Matrix ${version}\n\nUsage: matrix init [directory] [options]\n matrix doctor [directory] [options]\n\nOptions:\n --language <en|zh-CN>\n --scope <project|global>\n --platform <claude-code|codex> (repeatable)\n --mode <copy|symlink>\n --with-mattpocock | --without-mattpocock\n --force --yes, -y --dry-run --json --no-color`;
|
|
13
|
-
|
|
14
|
-
function parse(argv) {
|
|
15
|
-
const result = { command: null, directory: null, scope: null, language: null, platforms: [], mode: "copy", matt: null, matrixPolicy: "safe", yes: false, dryRun: false, json: false, color: true };
|
|
16
|
-
const values = [...argv];
|
|
17
|
-
if (values[0] && !values[0].startsWith("-")) result.command = values.shift();
|
|
18
|
-
while (values.length) {
|
|
19
|
-
const token = values.shift();
|
|
20
|
-
if (!token.startsWith("-") && !result.directory) result.directory = token;
|
|
21
|
-
else if (token === "--scope") result.scope = values.shift();
|
|
22
|
-
else if (token === "--language") result.language = values.shift();
|
|
23
|
-
else if (token === "--platform") result.platforms.push(values.shift());
|
|
24
|
-
else if (token === "--mode") result.mode = values.shift();
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { PLATFORMS } from "./catalog.js";
|
|
5
|
+
import { createDistribution } from "./distribution.js";
|
|
6
|
+
import { text } from "./messages.js";
|
|
7
|
+
import { createUi } from "./ui.js";
|
|
8
|
+
import { invoke } from "./workflow.js";
|
|
9
|
+
|
|
10
|
+
const packagePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
11
|
+
const version = JSON.parse(fs.readFileSync(packagePath, "utf8")).version;
|
|
12
|
+
const HELP = `Matrix ${version}\n\nUsage: matrix init [directory] [options]\n matrix doctor [directory] [options]\n\nOptions:\n --language <en|zh-CN>\n --scope <project|global>\n --platform <claude-code|codex> (repeatable)\n --mode <copy|symlink>\n --with-mattpocock | --without-mattpocock\n --default-orchestration <prim|arch>\n --force --yes, -y --dry-run --json --no-color`;
|
|
13
|
+
|
|
14
|
+
function parse(argv) {
|
|
15
|
+
const result = { command: null, directory: null, scope: null, language: null, platforms: [], mode: "copy", matt: null, defaultOrchestration: null, matrixPolicy: "safe", yes: false, dryRun: false, json: false, color: true };
|
|
16
|
+
const values = [...argv];
|
|
17
|
+
if (values[0] && !values[0].startsWith("-")) result.command = values.shift();
|
|
18
|
+
while (values.length) {
|
|
19
|
+
const token = values.shift();
|
|
20
|
+
if (!token.startsWith("-") && !result.directory) result.directory = token;
|
|
21
|
+
else if (token === "--scope") result.scope = values.shift();
|
|
22
|
+
else if (token === "--language") result.language = values.shift();
|
|
23
|
+
else if (token === "--platform") result.platforms.push(values.shift());
|
|
24
|
+
else if (token === "--mode") result.mode = values.shift();
|
|
25
25
|
else if (token === "--with-mattpocock") result.matt = "missing";
|
|
26
26
|
else if (token === "--without-mattpocock") result.matt = "none";
|
|
27
|
-
else if (token === "--
|
|
28
|
-
else if (
|
|
29
|
-
else if (
|
|
30
|
-
else if (token === "--
|
|
31
|
-
else if (token === "--
|
|
32
|
-
else if (
|
|
33
|
-
else if (["--
|
|
34
|
-
else
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
27
|
+
else if (token === "--default-orchestration") result.defaultOrchestration = values.shift();
|
|
28
|
+
else if (token === "--force") result.matrixPolicy = "replace";
|
|
29
|
+
else if (["--yes", "-y"].includes(token)) result.yes = true;
|
|
30
|
+
else if (token === "--dry-run") result.dryRun = true;
|
|
31
|
+
else if (token === "--json") result.json = true;
|
|
32
|
+
else if (token === "--no-color") result.color = false;
|
|
33
|
+
else if (["--help", "-h"].includes(token)) result.help = true;
|
|
34
|
+
else if (["--version", "-v"].includes(token)) result.version = true;
|
|
35
|
+
else throw new Error(`Unknown option: ${token}`);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function projectRoot(directory) {
|
|
41
|
+
const value = path.resolve(directory ?? process.cwd());
|
|
42
|
+
if (!fs.existsSync(value) || !fs.statSync(value).isDirectory()) throw new Error(`Not a directory: ${value}`);
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function labelsFor(language) { return { yes: text(language, "yes"), no: text(language, "no") }; }
|
|
47
|
+
|
|
48
|
+
function localizedState(language, state) {
|
|
49
|
+
return text(language, ({ missing: "stateMissing", matching: "stateMatching", complete: "stateComplete", "user-modified": "stateUserModified", partial: "statePartial", damaged: "statePartial", newer: "stateMatching", "outdated-unchanged": "stateOutdated" })[state] ?? state);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function localizedAction(language, action) {
|
|
53
|
+
return text(language, ({ install: "actionInstall", keep: "actionKeep", "backup-replace": "actionBackupReplace", repair: "actionRepair", blocked: "actionBlocked" })[action] ?? action);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function intentFrom(options, ui) {
|
|
57
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && !options.json);
|
|
58
|
+
const language = options.language ?? (options.yes ? "en" : await ui.select("Language / 语言", [{ label: "English", value: "en" }, { label: "中文", value: "zh-CN" }]));
|
|
59
|
+
const scope = options.scope ?? (options.yes ? "project" : await ui.select(text(language, "scopeQuestion"), [{ label: text(language, "project"), value: "project" }, { label: text(language, "global"), value: "global" }]));
|
|
60
|
+
let platforms = options.platforms;
|
|
60
61
|
if (!platforms.length && interactive && !options.yes) platforms = await ui.selectMany(text(language, "platformsQuestion"), [{ label: "Claude Code", value: "claude-code" }, { label: "Codex", value: "codex" }]);
|
|
61
62
|
const matt = options.matt ?? (interactive && !options.yes ? (await ui.confirm(text(language, "mattQuestion"), true, labelsFor(language)) ? "missing" : "none") : "missing");
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
const defaultOrchestration = options.defaultOrchestration ?? (interactive && !options.yes && matt === "missing"
|
|
64
|
+
? await ui.select(text(language, "orchestrationQuestion"), [{ label: "Arch", value: "arch" }, { label: "Prim", value: "prim" }])
|
|
65
|
+
: null);
|
|
66
|
+
return { projectRoot: projectRoot(options.directory), language, scope, platforms, mode: options.mode, matt, defaultOrchestration, nonInteractive: options.yes || !interactive, matrixPolicy: options.matrixPolicy, dryRun: options.dryRun };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function render(ui, evaluation) {
|
|
70
|
+
const language = evaluation.intent?.language ?? "en";
|
|
67
71
|
const actions = (evaluation.actions ?? []).map((item) => `${item.platform}: ${localizedAction(language, item.matrix)}`).join(", ");
|
|
68
72
|
ui.info(actions ? `${text(language, "preview")}: ${actions}` : evaluation.code);
|
|
69
|
-
|
|
70
|
-
for (const item of evaluation.
|
|
71
|
-
for (const item of evaluation.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (options.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (!
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (result.
|
|
89
|
-
|
|
90
|
-
ui.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (options.
|
|
114
|
-
if (options.
|
|
115
|
-
if (options.command === "
|
|
116
|
-
|
|
117
|
-
}
|
|
73
|
+
if (evaluation.intent?.defaultOrchestration) ui.muted(`Default orchestration: ${evaluation.intent.defaultOrchestration}`);
|
|
74
|
+
for (const item of evaluation.observations ?? []) ui.muted(`${item.name}: ${text(language, "matrix")} ${localizedState(language, item.matrix)};${text(language, "matt")} ${localizedState(language, item.matt.state)}${item.matt.inherited ? ` (${text(language, "global")})` : ""}`);
|
|
75
|
+
for (const item of evaluation.diagnostics ?? []) ui.warn(item.code === "USER_MODIFIED_BLOCKED" ? text(language, "replaceQuestion") : item.message ?? item.code);
|
|
76
|
+
for (const item of evaluation.diagnosis ?? []) (item.code === "MATT_INHERITED" ? ui.muted : ui.warn)(item.message ?? item.code);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function init(options) {
|
|
80
|
+
const ui = createUi({ color: options.color && !options.json });
|
|
81
|
+
if (!options.json) await ui.banner();
|
|
82
|
+
const intent = await intentFrom(options, ui);
|
|
83
|
+
const distribution = createDistribution();
|
|
84
|
+
let evaluation = distribution.evaluate(intent);
|
|
85
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && !options.json);
|
|
86
|
+
if (!evaluation.ok && evaluation.code === "USER_MODIFIED_BLOCKED" && interactive && !options.yes && await ui.confirm(text(intent.language, "replaceQuestion"), false, labelsFor(intent.language))) evaluation = distribution.evaluate({ ...intent, matrixPolicy: "replace" });
|
|
87
|
+
if (options.json) { console.log(JSON.stringify(evaluation)); return evaluation.ok ? 0 : 2; }
|
|
88
|
+
render(ui, evaluation);
|
|
89
|
+
if (!evaluation.ok) return 2;
|
|
90
|
+
if (!options.yes && interactive && !options.dryRun && !await ui.confirm(text(intent.language, "confirm"), true, labelsFor(intent.language))) return 0;
|
|
91
|
+
const result = distribution.commit(evaluation.plan);
|
|
92
|
+
if (!result.ok) { ui.warn(result.error ?? result.code); return 2; }
|
|
93
|
+
if (result.code === "PARTIAL") { ui.warn(result.recovery); return 1; }
|
|
94
|
+
ui.success(options.dryRun ? text(intent.language, "dryRunComplete") : text(intent.language, "ready"));
|
|
95
|
+
ui.muted(evaluation.intent.platforms.map((id) => `${PLATFORMS[id].name}: $matrix <request>`).join("\n"));
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function doctor(options) {
|
|
100
|
+
const ui = createUi({ color: options.color && !options.json });
|
|
101
|
+
const intent = await intentFrom({ ...options, yes: true }, ui);
|
|
102
|
+
const evaluation = createDistribution().diagnose(intent);
|
|
103
|
+
if (options.json) console.log(JSON.stringify(evaluation));
|
|
104
|
+
else { await ui.banner(); ui.info(text(intent.language, "doctor")); render(ui, evaluation); }
|
|
105
|
+
return evaluation.ok ? 0 : 2;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function main(argv) {
|
|
109
|
+
if (argv[0] === "workflow") {
|
|
110
|
+
const json = argv.includes("--json");
|
|
111
|
+
const result = invoke(argv.slice(1).filter((value) => value !== "--json"));
|
|
112
|
+
if (json) console.log(JSON.stringify(result));
|
|
113
|
+
else console.log(result.ok ? JSON.stringify(result, null, 2) : `MATRIX ERROR [${result.code}]: ${result.message}`);
|
|
114
|
+
return result.ok ? 0 : 2;
|
|
115
|
+
}
|
|
116
|
+
const options = parse(argv);
|
|
117
|
+
if (options.version) { console.log(version); return 0; }
|
|
118
|
+
if (options.help || !options.command) { console.log(HELP); return 0; }
|
|
119
|
+
if (options.command === "init") return init(options);
|
|
120
|
+
if (options.command === "doctor") return doctor(options);
|
|
121
|
+
throw new Error(`Unknown command: ${options.command}. Run matrix --help.`);
|
|
122
|
+
}
|