@rosenfan/matrix 0.1.4 → 0.1.5
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/LICENSE +21 -21
- package/README.md +29 -29
- package/README.zh-CN.md +27 -27
- package/{.claude → assets}/skills/matrix/SKILL.md +78 -78
- package/{.claude → assets}/skills/matrix/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix/scripts/matrix_claude.py +11 -11
- package/{.claude → assets}/skills/matrix/scripts/matrix_state.py +49 -49
- package/assets/skills/matrix/scripts/test_matrix_claude.py +69 -0
- package/assets/skills/matrix/scripts/test_matrix_state.py +69 -0
- package/{.claude → assets}/skills/matrix-archive/SKILL.md +36 -36
- package/{.claude/skills/matrix-hotfix → assets/skills/matrix-archive}/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-build/SKILL.md +41 -41
- package/{.claude/skills/matrix-archive → assets/skills/matrix-build}/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-claude/SKILL.md +98 -98
- package/{.claude → assets}/skills/matrix-claude/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-design/SKILL.md +65 -65
- package/{.claude → assets}/skills/matrix-design/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-hotfix/SKILL.md +8 -8
- package/{.claude/skills/matrix-build → assets/skills/matrix-hotfix}/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-open/SKILL.md +39 -39
- package/assets/skills/matrix-open/agents/openai.yaml +4 -0
- package/{.claude → assets}/skills/matrix-status/SKILL.md +15 -15
- package/{.claude → assets}/skills/matrix-status/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-tweak/SKILL.md +8 -8
- package/assets/skills/matrix-tweak/agents/openai.yaml +4 -0
- package/{.claude → assets}/skills/matrix-verify/SKILL.md +62 -62
- package/assets/skills/matrix-verify/agents/openai.yaml +4 -0
- package/assets/skills-zh-CN/matrix/SKILL.md +14 -14
- package/assets/skills-zh-CN/matrix-build/SKILL.md +4 -4
- package/assets/skills-zh-CN/matrix-design/SKILL.md +4 -4
- package/assets/skills-zh-CN/matrix-open/SKILL.md +3 -3
- package/assets/skills-zh-CN/matrix-verify/SKILL.md +19 -19
- package/bin/matrix.js +15 -15
- package/package.json +4 -13
- package/src/catalog.js +79 -72
- package/src/cli.js +100 -100
- package/src/distribution.js +181 -178
- package/src/installer.js +10 -10
- package/src/matt-catalog.mjs +1 -1
- package/src/matt.js +115 -115
- package/src/messages.js +14 -14
- package/src/runtime-resolver.js +76 -76
- package/src/self-update.js +81 -81
- package/src/workflow-transaction.js +13 -13
- package/src/workflow.js +711 -705
- package/.claude/skills/matrix-open/agents/openai.yaml +0 -4
- package/.claude/skills/matrix-tweak/agents/openai.yaml +0 -4
- package/.claude/skills/matrix-verify/agents/openai.yaml +0 -4
|
@@ -5,28 +5,28 @@ description: 依据计划验证 Matrix 变更并审查差异。
|
|
|
5
5
|
|
|
6
6
|
# Matrix Verify
|
|
7
7
|
|
|
8
|
-
运行已批准 Contract 的验证命令,在 `verification.md` 写入 `## Test evidence`。hotfix 另写 `## Regression evidence`;tweak 另写 `## Scope review evidence`。所有模式都必须写入 `## Review evidence`,并在其中保留内容充分的 `### Standards` 与 `### Spec` 两个子节。读取冻结 `orchestration`:
|
|
8
|
+
运行已批准 Contract 的验证命令,在 `verification.md` 写入 `## Test evidence`。hotfix 另写 `## Regression evidence`;tweak 另写 `## Scope review evidence`。所有模式都必须写入 `## Review evidence`,并在其中保留内容充分的 `### Standards` 与 `### Spec` 两个子节。读取冻结 `orchestration`:
|
|
9
9
|
|
|
10
10
|
验证正文使用 `artifact_language`;上述 Runtime 所需英文证据标题保持不变。
|
|
11
11
|
|
|
12
12
|
- **Prim**:Matrix 自行完成 Standards 与 Spec 双轴审查。
|
|
13
13
|
- **Arch**:调用 `code-review` 产生双轴证据,并说明调用原因。
|
|
14
14
|
|
|
15
|
-
审查结果只是 Matrix Verify 的输入;companion 不得决定阶段结果、扩大已批准架构、推进或归档。不得调用事后架构改进 wrapper。正确安装的审查能力失败时最多执行一次同能力 Matrix fallback;安装完整性问题必须停止。
|
|
16
|
-
|
|
17
|
-
Arch 正常执行 `code-review` 后,使用项目 Runtime 把 fixed point、HEAD/diff、Contract、审查正文和工作区绑定为当前 revision 的回执:
|
|
18
|
-
|
|
19
|
-
```powershell
|
|
20
|
-
node <matrix-skill-directory>/scripts/matrix-runtime.mjs review --source code-review --fixed-point <ref> --standards <passed|failed> --spec <passed|failed>
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
只有在无 Git 基线、已提交的 HEAD diff 为空、仍存在 staged/unstaged/untracked 候选,或 capability 真实执行失败时,才记录限制并使用一次有界 fallback:
|
|
24
|
-
|
|
25
|
-
```powershell
|
|
26
|
-
node <matrix-skill-directory>/scripts/matrix-runtime.mjs review --source matrix-fallback --reason <git-baseline-unavailable|empty-head-diff|uncommitted-worktree|capability-execution-failed> [--fixed-point <ref-for-empty-head-diff-or-uncommitted-worktree>] --standards <passed|failed> --spec <passed|failed>
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
不得把 fallback 写成 `code-review` 成功。`empty-head-diff` 必须提供 fixed point,由 Runtime 核实三点 diff 确实为空。`uncommitted-worktree` 也必须提供 fixed point,Runtime 会把 staged、unstaged 与 untracked 候选内容绑定为 `candidate_hash`,之后任意变化都会使回执过期。当前 revision 已有回执或任一审查轴失败时,必须先受控 Return,不能覆盖回执后继续。
|
|
15
|
+
审查结果只是 Matrix Verify 的输入;companion 不得决定阶段结果、扩大已批准架构、推进或归档。不得调用事后架构改进 wrapper。正确安装的审查能力失败时最多执行一次同能力 Matrix fallback;安装完整性问题必须停止。
|
|
16
|
+
|
|
17
|
+
Arch 正常执行 `code-review` 后,使用项目 Runtime 把 fixed point、HEAD/diff、Contract、审查正文和工作区绑定为当前 revision 的回执:
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs review --source code-review --fixed-point <ref> --standards <passed|failed> --spec <passed|failed>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
只有在无 Git 基线、已提交的 HEAD diff 为空、仍存在 staged/unstaged/untracked 候选,或 capability 真实执行失败时,才记录限制并使用一次有界 fallback:
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs review --source matrix-fallback --reason <git-baseline-unavailable|empty-head-diff|uncommitted-worktree|capability-execution-failed> [--fixed-point <ref-for-empty-head-diff-or-uncommitted-worktree>] --standards <passed|failed> --spec <passed|failed>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
不得把 fallback 写成 `code-review` 成功。`empty-head-diff` 必须提供 fixed point,由 Runtime 核实三点 diff 确实为空。`uncommitted-worktree` 也必须提供 fixed point,Runtime 会把 staged、unstaged 与 untracked 候选内容绑定为 `candidate_hash`,之后任意变化都会使回执过期。当前 revision 已有回执或任一审查轴失败时,必须先受控 Return,不能覆盖回执后继续。
|
|
30
30
|
|
|
31
31
|
失败时使用受控 Return:
|
|
32
32
|
|
|
@@ -35,9 +35,9 @@ node <matrix-skill-directory>/scripts/matrix-runtime.mjs return build --reason v
|
|
|
35
35
|
node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason acceptance-or-design-gap
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
Return 会轮换旧 `verification.md`;不得把旧结论直接复制为新的当前证据。
|
|
39
|
-
|
|
40
|
-
Arch 的 Return 也会轮换 `review-receipt.json`;新 revision 必须重新审查并生成新回执。仅有 Markdown 标题、旧回执或与当前工作区不匹配的回执都不能通过 guard。
|
|
38
|
+
Return 会轮换旧 `verification.md`;不得把旧结论直接复制为新的当前证据。
|
|
39
|
+
|
|
40
|
+
Arch 的 Return 也会轮换 `review-receipt.json`;新 revision 必须重新审查并生成新回执。仅有 Markdown 标题、旧回执或与当前工作区不匹配的回执都不能通过 guard。
|
|
41
41
|
|
|
42
42
|
```powershell
|
|
43
43
|
node <matrix-skill-directory>/scripts/matrix-runtime.mjs guard verify
|
package/bin/matrix.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { main } from "../src/cli.js";
|
|
4
|
-
|
|
5
|
-
main(process.argv.slice(2)).then((exitCode) => {
|
|
6
|
-
if (Number.isInteger(exitCode) && exitCode !== 0) process.exitCode = exitCode;
|
|
7
|
-
}).catch((error) => {
|
|
8
|
-
if (error.code === "CANCELLED") {
|
|
9
|
-
console.log("\nCancelled.");
|
|
10
|
-
process.exitCode = 0;
|
|
11
|
-
} else {
|
|
12
|
-
console.error(`\nMatrix error: ${error.message}`);
|
|
13
|
-
process.exitCode = 1;
|
|
14
|
-
}
|
|
15
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { main } from "../src/cli.js";
|
|
4
|
+
|
|
5
|
+
main(process.argv.slice(2)).then((exitCode) => {
|
|
6
|
+
if (Number.isInteger(exitCode) && exitCode !== 0) process.exitCode = exitCode;
|
|
7
|
+
}).catch((error) => {
|
|
8
|
+
if (error.code === "CANCELLED") {
|
|
9
|
+
console.log("\nCancelled.");
|
|
10
|
+
process.exitCode = 0;
|
|
11
|
+
} else {
|
|
12
|
+
console.error(`\nMatrix error: ${error.message}`);
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
}
|
|
15
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rosenfan/matrix",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Evidence-driven development workflow with Prim and Arch agent-skill orchestration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -10,19 +10,10 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"bin/",
|
|
12
12
|
"src/",
|
|
13
|
+
"assets/skills",
|
|
13
14
|
"assets/skills-zh-CN",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
".claude/skills/matrix-design",
|
|
17
|
-
".claude/skills/matrix-build",
|
|
18
|
-
".claude/skills/matrix-verify",
|
|
19
|
-
".claude/skills/matrix-archive",
|
|
20
|
-
".claude/skills/matrix-hotfix",
|
|
21
|
-
".claude/skills/matrix-tweak",
|
|
22
|
-
".claude/skills/matrix-status",
|
|
23
|
-
".claude/skills/matrix-claude",
|
|
24
|
-
"!.claude/skills/**/__pycache__",
|
|
25
|
-
"!.claude/skills/**/scripts/test_*.py",
|
|
15
|
+
"!assets/skills/**/__pycache__",
|
|
16
|
+
"!assets/skills-zh-CN/**/__pycache__",
|
|
26
17
|
"README.md",
|
|
27
18
|
"README.zh-CN.md",
|
|
28
19
|
"LICENSE"
|
package/src/catalog.js
CHANGED
|
@@ -1,87 +1,94 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { MATT_INSTALLABLE_SKILLS, MATT_ROLES } from "./matt-catalog.mjs";
|
|
7
|
-
|
|
8
|
-
export const LANGUAGE_IDS = ["en", "zh-CN"];
|
|
9
|
-
export const PLATFORM_IDS = ["claude-code", "codex"];
|
|
10
|
-
export const MATRIX_SKILLS = [
|
|
11
|
-
"matrix", "matrix-open", "matrix-design", "matrix-build", "matrix-verify",
|
|
12
|
-
"matrix-archive", "matrix-hotfix", "matrix-tweak", "matrix-status", "matrix-claude"
|
|
13
|
-
];
|
|
7
|
+
|
|
8
|
+
export const LANGUAGE_IDS = ["en", "zh-CN"];
|
|
9
|
+
export const PLATFORM_IDS = ["claude-code", "codex"];
|
|
10
|
+
export const MATRIX_SKILLS = [
|
|
11
|
+
"matrix", "matrix-open", "matrix-design", "matrix-build", "matrix-verify",
|
|
12
|
+
"matrix-archive", "matrix-hotfix", "matrix-tweak", "matrix-status", "matrix-claude"
|
|
13
|
+
];
|
|
14
14
|
export const MATT_SKILLS = MATT_INSTALLABLE_SKILLS;
|
|
15
15
|
export const UNMANAGED_MATT_SKILLS = MATT_ROLES.incompatible;
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
18
18
|
const packageManifest = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
19
19
|
export const MATRIX_VERSION = packageManifest.version;
|
|
20
|
-
|
|
21
|
-
export const PLATFORMS = {
|
|
22
|
-
"claude-code": {
|
|
23
|
-
id: "claude-code", name: "Claude Code", skillsCliAgent: "claude-code",
|
|
24
|
-
skillRoot(base) { return path.join(base, ".claude", "skills"); },
|
|
25
|
-
legacyRoots() { return []; }
|
|
26
|
-
},
|
|
27
|
-
codex: {
|
|
28
|
-
id: "codex", name: "Codex", skillsCliAgent: "codex",
|
|
29
|
-
skillRoot(base) { return path.join(base, ".agents", "skills"); },
|
|
30
|
-
legacyRoots(base) { return [path.join(base, ".codex", "skills")]; }
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export function sourceSkillsRoot(language = "en") {
|
|
35
|
-
const localized = language === "zh-CN"
|
|
36
|
-
? path.join(packageRoot, "assets", "skills-zh-CN")
|
|
37
|
-
: path.join(packageRoot, "
|
|
38
|
-
if (!fs.existsSync(localized)) throw new Error(`Matrix package is incomplete: ${language} assets are missing.`);
|
|
39
|
-
return localized;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function scopeBase(scope, projectRoot, home = os.homedir()) {
|
|
43
|
-
return scope === "global" ? home : projectRoot;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function installationRoot(scope, projectRoot, home) {
|
|
47
|
-
return path.join(scopeBase(scope, projectRoot, home), ".matrix");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function manifestPath(scope, projectRoot, home) {
|
|
51
|
-
return path.join(installationRoot(scope, projectRoot, home), "installation.json");
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
20
|
+
|
|
21
|
+
export const PLATFORMS = {
|
|
22
|
+
"claude-code": {
|
|
23
|
+
id: "claude-code", name: "Claude Code", skillsCliAgent: "claude-code",
|
|
24
|
+
skillRoot(base) { return path.join(base, ".claude", "skills"); },
|
|
25
|
+
legacyRoots() { return []; }
|
|
26
|
+
},
|
|
27
|
+
codex: {
|
|
28
|
+
id: "codex", name: "Codex", skillsCliAgent: "codex",
|
|
29
|
+
skillRoot(base) { return path.join(base, ".agents", "skills"); },
|
|
30
|
+
legacyRoots(base) { return [path.join(base, ".codex", "skills")]; }
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function sourceSkillsRoot(language = "en") {
|
|
35
|
+
const localized = language === "zh-CN"
|
|
36
|
+
? path.join(packageRoot, "assets", "skills-zh-CN")
|
|
37
|
+
: path.join(packageRoot, "assets", "skills");
|
|
38
|
+
if (!fs.existsSync(localized)) throw new Error(`Matrix package is incomplete: ${language} assets are missing.`);
|
|
39
|
+
return localized;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function scopeBase(scope, projectRoot, home = os.homedir()) {
|
|
43
|
+
return scope === "global" ? home : projectRoot;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function installationRoot(scope, projectRoot, home) {
|
|
47
|
+
return path.join(scopeBase(scope, projectRoot, home), ".matrix");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function manifestPath(scope, projectRoot, home) {
|
|
51
|
+
return path.join(installationRoot(scope, projectRoot, home), "installation.json");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Content digests must identify logical file content, not checkout line endings:
|
|
55
|
+
// a git working tree (autocrlf CRLF), the npm tarball and a local tgz must agree.
|
|
56
|
+
// Generated caches (__pycache__) are skipped so dirty working trees cannot poison digests.
|
|
57
|
+
const normalizeEol = (contents) => Buffer.from(contents.toString("utf8").replaceAll("\r\n", "\n").replaceAll("\r", "\n"), "utf8");
|
|
58
|
+
const GENERATED_DIRS = new Set(["__pycache__"]);
|
|
59
|
+
|
|
60
|
+
export function hashDirectory(directory, extraFiles = []) {
|
|
61
|
+
const hash = crypto.createHash("sha256");
|
|
62
|
+
const files = new Map();
|
|
63
|
+
const visit = (current, relative = "") => {
|
|
64
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
65
|
+
if (entry.isDirectory() && GENERATED_DIRS.has(entry.name)) continue;
|
|
66
|
+
const child = path.join(current, entry.name);
|
|
67
|
+
const childRelative = path.join(relative, entry.name).replaceAll("\\", "/");
|
|
68
|
+
if (entry.isDirectory()) visit(child, childRelative);
|
|
69
|
+
else if (entry.isFile()) {
|
|
70
|
+
files.set(childRelative, fs.readFileSync(child));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
visit(directory);
|
|
75
|
+
for (const extra of extraFiles) files.set(extra.path, Buffer.from(extra.contents));
|
|
76
|
+
for (const [relative, contents] of [...files.entries()].sort(([left], [right]) => left.localeCompare(right))) {
|
|
77
|
+
hash.update(`${relative}\0`);
|
|
78
|
+
hash.update(normalizeEol(contents));
|
|
79
|
+
}
|
|
80
|
+
return hash.digest("hex");
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
export function workflowRuntimeSource() { return path.join(packageRoot, "src", "workflow.js"); }
|
|
77
84
|
export function workflowTransactionSource() { return path.join(packageRoot, "src", "workflow-transaction.js"); }
|
|
78
85
|
export function mattCatalogSource() { return path.join(packageRoot, "src", "matt-catalog.mjs"); }
|
|
79
|
-
|
|
86
|
+
|
|
80
87
|
export function releaseCatalog(language = "en") {
|
|
81
|
-
if (!LANGUAGE_IDS.includes(language)) throw new Error(`Unsupported language: ${language}`);
|
|
82
|
-
const root = sourceSkillsRoot(language);
|
|
83
|
-
const missing = MATRIX_SKILLS.filter((skill) => !fs.existsSync(path.join(root, skill, "SKILL.md")));
|
|
84
|
-
if (missing.length) throw new Error(`Matrix package is incomplete. Missing: ${missing.join(", ")}`);
|
|
88
|
+
if (!LANGUAGE_IDS.includes(language)) throw new Error(`Unsupported language: ${language}`);
|
|
89
|
+
const root = sourceSkillsRoot(language);
|
|
90
|
+
const missing = MATRIX_SKILLS.filter((skill) => !fs.existsSync(path.join(root, skill, "SKILL.md")));
|
|
91
|
+
if (missing.length) throw new Error(`Matrix package is incomplete. Missing: ${missing.join(", ")}`);
|
|
85
92
|
const runtime = fs.readFileSync(workflowRuntimeSource());
|
|
86
93
|
const transaction = fs.readFileSync(workflowTransactionSource());
|
|
87
94
|
const mattCatalog = fs.readFileSync(mattCatalogSource());
|
package/src/cli.js
CHANGED
|
@@ -2,22 +2,22 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { PLATFORMS } from "./catalog.js";
|
|
6
|
-
import { createDistribution, installedIntent } from "./distribution.js";
|
|
7
|
-
import { createMattAdapter, inspectMattInstallation } from "./matt.js";
|
|
5
|
+
import { PLATFORMS } from "./catalog.js";
|
|
6
|
+
import { createDistribution, installedIntent } from "./distribution.js";
|
|
7
|
+
import { createMattAdapter, inspectMattInstallation } from "./matt.js";
|
|
8
8
|
import { text } from "./messages.js";
|
|
9
9
|
import { createUi } from "./ui.js";
|
|
10
10
|
import { invoke } from "./workflow.js";
|
|
11
|
-
import { compareVersions, globalBinPath, latestVersion, selfUpdate } from "./self-update.js";
|
|
12
|
-
import { resolveProjectRuntime } from "./runtime-resolver.js";
|
|
13
|
-
import { MATT_COMPATIBILITY } from "./matt-catalog.mjs";
|
|
11
|
+
import { compareVersions, globalBinPath, latestVersion, selfUpdate } from "./self-update.js";
|
|
12
|
+
import { resolveProjectRuntime } from "./runtime-resolver.js";
|
|
13
|
+
import { MATT_COMPATIBILITY } from "./matt-catalog.mjs";
|
|
14
14
|
|
|
15
15
|
const packagePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
16
|
-
const version = JSON.parse(fs.readFileSync(packagePath, "utf8")).version;
|
|
17
|
-
const HELP = `Matrix ${version}\n\nUsage: matrix init [directory] [options]\n matrix update [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-matt\n --default-orchestration <prim|arch>\n --skip-self-update --force --yes, -y --dry-run --json --no-color`;
|
|
16
|
+
const version = JSON.parse(fs.readFileSync(packagePath, "utf8")).version;
|
|
17
|
+
const HELP = `Matrix ${version}\n\nUsage: matrix init [directory] [options]\n matrix update [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-matt\n --default-orchestration <prim|arch>\n --skip-self-update --force --yes, -y --dry-run --json --no-color`;
|
|
18
18
|
|
|
19
|
-
function parse(argv) {
|
|
20
|
-
const result = { command: null, directory: null, scope: null, language: null, platforms: [], mode: "copy", matt: null, mattPolicy: "preserve", defaultOrchestration: null, matrixPolicy: "safe", yes: false, dryRun: false, json: false, color: true, skipSelfUpdate: false, reexec: false };
|
|
19
|
+
function parse(argv) {
|
|
20
|
+
const result = { command: null, directory: null, scope: null, language: null, platforms: [], mode: "copy", matt: null, mattPolicy: "preserve", defaultOrchestration: null, matrixPolicy: "safe", yes: false, dryRun: false, json: false, color: true, skipSelfUpdate: false, reexec: false };
|
|
21
21
|
const values = [...argv];
|
|
22
22
|
if (values[0] && !values[0].startsWith("-")) result.command = values.shift();
|
|
23
23
|
while (values.length) {
|
|
@@ -27,9 +27,9 @@ function parse(argv) {
|
|
|
27
27
|
else if (token === "--language") result.language = values.shift();
|
|
28
28
|
else if (token === "--platform") result.platforms.push(values.shift());
|
|
29
29
|
else if (token === "--mode") result.mode = values.shift();
|
|
30
|
-
else if (token === "--with-mattpocock") result.matt = "missing";
|
|
31
|
-
else if (token === "--without-mattpocock") result.matt = "none";
|
|
32
|
-
else if (token === "--force-matt") result.mattPolicy = "replace";
|
|
30
|
+
else if (token === "--with-mattpocock") result.matt = "missing";
|
|
31
|
+
else if (token === "--without-mattpocock") result.matt = "none";
|
|
32
|
+
else if (token === "--force-matt") result.mattPolicy = "replace";
|
|
33
33
|
else if (token === "--default-orchestration") result.defaultOrchestration = values.shift();
|
|
34
34
|
else if (token === "--force") result.matrixPolicy = "replace";
|
|
35
35
|
else if (["--yes", "-y"].includes(token)) result.yes = true;
|
|
@@ -53,8 +53,8 @@ function projectRoot(directory) {
|
|
|
53
53
|
|
|
54
54
|
function labelsFor(language) { return { yes: text(language, "yes"), no: text(language, "no") }; }
|
|
55
55
|
|
|
56
|
-
function localizedState(language, state) {
|
|
57
|
-
return text(language, ({ missing: "stateMissing", matching: "stateMatching", complete: "stateComplete", "user-modified": "stateUserModified", partial: "statePartial", damaged: "statePartial", newer: "stateMatching", "outdated-unchanged": "stateOutdated", "safe-update": "stateSafeUpdate", adoptable: "stateAdoptable", unreadable: "stateUnreadable", compatible: "stateCompatible", "update-required": "stateUpdateRequired", "unsupported-newer": "stateUnsupportedNewer", unverified: "stateUnverified", unknown: "stateUnknown", incomplete: "statePartial" })[state] ?? state);
|
|
56
|
+
function localizedState(language, state) {
|
|
57
|
+
return text(language, ({ missing: "stateMissing", matching: "stateMatching", complete: "stateComplete", "user-modified": "stateUserModified", partial: "statePartial", damaged: "statePartial", newer: "stateMatching", "outdated-unchanged": "stateOutdated", "safe-update": "stateSafeUpdate", adoptable: "stateAdoptable", unreadable: "stateUnreadable", compatible: "stateCompatible", "update-required": "stateUpdateRequired", "unsupported-newer": "stateUnsupportedNewer", unverified: "stateUnverified", unknown: "stateUnknown", incomplete: "statePartial" })[state] ?? state);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
function localizedAction(language, action) {
|
|
@@ -71,20 +71,20 @@ async function intentFrom(options, ui) {
|
|
|
71
71
|
const defaultOrchestration = options.defaultOrchestration ?? (interactive && !options.yes && matt === "missing"
|
|
72
72
|
? await ui.select(text(language, "orchestrationQuestion"), [{ label: "Arch", value: "arch" }, { label: "Prim", value: "prim" }])
|
|
73
73
|
: null);
|
|
74
|
-
return { projectRoot: projectRoot(options.directory), language, scope, platforms, mode: options.mode, matt, mattPolicy: options.mattPolicy, defaultOrchestration, nonInteractive: options.yes || !interactive, matrixPolicy: options.matrixPolicy, dryRun: options.dryRun };
|
|
74
|
+
return { projectRoot: projectRoot(options.directory), language, scope, platforms, mode: options.mode, matt, mattPolicy: options.mattPolicy, defaultOrchestration, nonInteractive: options.yes || !interactive, matrixPolicy: options.matrixPolicy, dryRun: options.dryRun };
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function render(ui, evaluation) {
|
|
78
78
|
const language = evaluation.intent?.language ?? "en";
|
|
79
79
|
const actions = (evaluation.actions ?? []).map((item) => `${item.platform}: ${localizedAction(language, item.matrix)}`).join(", ");
|
|
80
80
|
ui.info(actions ? `${text(language, "preview")}: ${actions}` : evaluation.code);
|
|
81
|
-
if (evaluation.intent?.defaultOrchestration) ui.muted(`${text(language, "defaultOrchestration")}: ${evaluation.intent.defaultOrchestration}`);
|
|
82
|
-
for (const item of evaluation.observations ?? []) {
|
|
83
|
-
const mattStates = Object.entries(item.matt.classificationCounts ?? {}).map(([state, count]) => `${localizedState(language, state)}=${count}`).join(", ");
|
|
84
|
-
ui.muted(`${item.name}: ${text(language, "matrix")} ${localizedState(language, item.matrix)};${text(language, "matt")} ${localizedState(language, item.matt.state)}${mattStates ? ` (${mattStates})` : ""}`);
|
|
85
|
-
}
|
|
81
|
+
if (evaluation.intent?.defaultOrchestration) ui.muted(`${text(language, "defaultOrchestration")}: ${evaluation.intent.defaultOrchestration}`);
|
|
82
|
+
for (const item of evaluation.observations ?? []) {
|
|
83
|
+
const mattStates = Object.entries(item.matt.classificationCounts ?? {}).map(([state, count]) => `${localizedState(language, state)}=${count}`).join(", ");
|
|
84
|
+
ui.muted(`${item.name}: ${text(language, "matrix")} ${localizedState(language, item.matrix)};${text(language, "matt")} ${localizedState(language, item.matt.state)}${mattStates ? ` (${mattStates})` : ""}`);
|
|
85
|
+
}
|
|
86
86
|
for (const item of evaluation.diagnostics ?? []) ui.warn(item.code === "USER_MODIFIED_BLOCKED" ? text(language, "replaceQuestion") : item.message ?? item.code);
|
|
87
|
-
for (const item of evaluation.diagnosis ?? []) (item.code === "MATT_GLOBAL_IGNORED" ? ui.muted : ui.warn)(item.message ?? item.code);
|
|
87
|
+
for (const item of evaluation.diagnosis ?? []) (item.code === "MATT_GLOBAL_IGNORED" ? ui.muted : ui.warn)(item.message ?? item.code);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
async function init(options) {
|
|
@@ -94,42 +94,42 @@ async function init(options) {
|
|
|
94
94
|
return refresh(intent, options, ui);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
async function refresh(intent, options, ui) {
|
|
98
|
-
const distribution = createDistribution({ mattAdapter: createMattAdapter({ stdio: options.json ? "pipe" : "inherit" }) });
|
|
99
|
-
let evaluation = distribution.evaluate(intent);
|
|
100
|
-
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && !options.json);
|
|
101
|
-
let reevaluated = false;
|
|
102
|
-
if (!options.json) render(ui, evaluation);
|
|
103
|
-
if (!evaluation.ok && evaluation.code === "USER_MODIFIED_BLOCKED" && interactive && !options.yes && await ui.confirm(text(intent.language, "replaceQuestion"), false, labelsFor(intent.language))) {
|
|
104
|
-
evaluation = distribution.evaluate({ ...intent, matrixPolicy: "replace" }); reevaluated = true;
|
|
105
|
-
}
|
|
106
|
-
if (!evaluation.ok && evaluation.code === "MATT_USER_MODIFIED" && interactive && !options.yes
|
|
107
|
-
&& await ui.confirm(text(intent.language, "mattReplaceQuestion"), false, labelsFor(intent.language))) {
|
|
108
|
-
evaluation = distribution.evaluate({ ...intent, mattPolicy: "replace" }); reevaluated = true;
|
|
109
|
-
}
|
|
110
|
-
if (options.json) {
|
|
111
|
-
if (!evaluation.ok || options.dryRun) {
|
|
112
|
-
const output = options.updateMode ? { ...evaluation, matt: inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms }) } : evaluation;
|
|
113
|
-
console.log(JSON.stringify(output)); return evaluation.ok ? 0 : 2;
|
|
114
|
-
}
|
|
115
|
-
const result = distribution.commit(evaluation.plan);
|
|
116
|
-
const output = { ...evaluation, ...result, intent: evaluation.intent, observations: evaluation.observations, actions: evaluation.actions,
|
|
117
|
-
...(options.updateMode ? { matt: inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms }) } : {}) };
|
|
118
|
-
console.log(JSON.stringify(output));
|
|
119
|
-
return !result.ok ? 2 : result.code === "PARTIAL" ? 1 : 0;
|
|
120
|
-
}
|
|
121
|
-
if (reevaluated) render(ui, evaluation);
|
|
97
|
+
async function refresh(intent, options, ui) {
|
|
98
|
+
const distribution = createDistribution({ mattAdapter: createMattAdapter({ stdio: options.json ? "pipe" : "inherit" }) });
|
|
99
|
+
let evaluation = distribution.evaluate(intent);
|
|
100
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && !options.json);
|
|
101
|
+
let reevaluated = false;
|
|
102
|
+
if (!options.json) render(ui, evaluation);
|
|
103
|
+
if (!evaluation.ok && evaluation.code === "USER_MODIFIED_BLOCKED" && interactive && !options.yes && await ui.confirm(text(intent.language, "replaceQuestion"), false, labelsFor(intent.language))) {
|
|
104
|
+
evaluation = distribution.evaluate({ ...intent, matrixPolicy: "replace" }); reevaluated = true;
|
|
105
|
+
}
|
|
106
|
+
if (!evaluation.ok && evaluation.code === "MATT_USER_MODIFIED" && interactive && !options.yes
|
|
107
|
+
&& await ui.confirm(text(intent.language, "mattReplaceQuestion"), false, labelsFor(intent.language))) {
|
|
108
|
+
evaluation = distribution.evaluate({ ...intent, mattPolicy: "replace" }); reevaluated = true;
|
|
109
|
+
}
|
|
110
|
+
if (options.json) {
|
|
111
|
+
if (!evaluation.ok || options.dryRun) {
|
|
112
|
+
const output = options.updateMode ? { ...evaluation, matt: inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms }) } : evaluation;
|
|
113
|
+
console.log(JSON.stringify(output)); return evaluation.ok ? 0 : 2;
|
|
114
|
+
}
|
|
115
|
+
const result = distribution.commit(evaluation.plan);
|
|
116
|
+
const output = { ...evaluation, ...result, intent: evaluation.intent, observations: evaluation.observations, actions: evaluation.actions,
|
|
117
|
+
...(options.updateMode ? { matt: inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms }) } : {}) };
|
|
118
|
+
console.log(JSON.stringify(output));
|
|
119
|
+
return !result.ok ? 2 : result.code === "PARTIAL" ? 1 : 0;
|
|
120
|
+
}
|
|
121
|
+
if (reevaluated) render(ui, evaluation);
|
|
122
122
|
if (!evaluation.ok) return 2;
|
|
123
123
|
if (!options.yes && interactive && !options.dryRun && !await ui.confirm(text(intent.language, "confirm"), true, labelsFor(intent.language))) return 0;
|
|
124
|
-
const result = distribution.commit(evaluation.plan);
|
|
124
|
+
const result = distribution.commit(evaluation.plan);
|
|
125
125
|
if (!result.ok) { ui.warn(result.error ?? result.code); return 2; }
|
|
126
126
|
if (result.code === "PARTIAL") { ui.warn(result.recovery); return 1; }
|
|
127
|
-
ui.success(options.dryRun ? text(intent.language, "dryRunComplete") : text(intent.language, "ready"));
|
|
128
|
-
if (!options.updateMode && intent.matt === "missing" && !options.dryRun) ui.muted(text(intent.language, "mattSetupHint").replace("{release}", MATT_COMPATIBILITY.release));
|
|
129
|
-
if (options.updateMode) {
|
|
130
|
-
const matt = inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms });
|
|
131
|
-
ui.muted(`${text(intent.language, "mattCompatibilityStatus").replace("{release}", matt.supported.release).replace("{status}", localizedState(intent.language, matt.status))}${matt.action ? `; ${matt.action}` : ""}`);
|
|
132
|
-
}
|
|
127
|
+
ui.success(options.dryRun ? text(intent.language, "dryRunComplete") : text(intent.language, "ready"));
|
|
128
|
+
if (!options.updateMode && intent.matt === "missing" && !options.dryRun) ui.muted(text(intent.language, "mattSetupHint").replace("{release}", MATT_COMPATIBILITY.release));
|
|
129
|
+
if (options.updateMode) {
|
|
130
|
+
const matt = inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms });
|
|
131
|
+
ui.muted(`${text(intent.language, "mattCompatibilityStatus").replace("{release}", matt.supported.release).replace("{status}", localizedState(intent.language, matt.status))}${matt.action ? `; ${matt.action}` : ""}`);
|
|
132
|
+
}
|
|
133
133
|
ui.muted(evaluation.intent.platforms.map((id) => `${PLATFORMS[id].name}: $matrix <request>`).join("\n"));
|
|
134
134
|
return 0;
|
|
135
135
|
}
|
|
@@ -143,7 +143,7 @@ async function update(options) {
|
|
|
143
143
|
if (options.json) console.log(JSON.stringify(result)); else ui.warn(result.message);
|
|
144
144
|
return 2;
|
|
145
145
|
}
|
|
146
|
-
const forbidden = options.language || options.scope || options.platforms.length || options.matt || options.mattPolicy === "replace" || options.defaultOrchestration || options.mode !== "copy";
|
|
146
|
+
const forbidden = options.language || options.scope || options.platforms.length || options.matt || options.mattPolicy === "replace" || options.defaultOrchestration || options.mode !== "copy";
|
|
147
147
|
if (forbidden) throw new Error("matrix update inherits installation settings. Use matrix init to change language, scope, platform, mode, Matt skills, or orchestration.");
|
|
148
148
|
const intent = { ...installed.intent, dryRun: options.dryRun };
|
|
149
149
|
let npm = { status: "skipped", current: version, target: version, reason: options.skipSelfUpdate ? "disabled by --skip-self-update" : null };
|
|
@@ -158,7 +158,7 @@ async function update(options) {
|
|
|
158
158
|
return 2;
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
-
if (options.json && options.dryRun) { console.log(JSON.stringify({ ok: true, code: "DRY_RUN", npm, intent, matt: inspectMattInstallation({ projectRoot: root, home: intent.home, platforms: intent.platforms }) })); return 0; }
|
|
161
|
+
if (options.json && options.dryRun) { console.log(JSON.stringify({ ok: true, code: "DRY_RUN", npm, intent, matt: inspectMattInstallation({ projectRoot: root, home: intent.home, platforms: intent.platforms }) })); return 0; }
|
|
162
162
|
if (!options.json) {
|
|
163
163
|
await ui.banner(); ui.info(`Matrix update: CLI ${npm.current}${npm.status === "available" ? ` → ${npm.target}` : ""}`);
|
|
164
164
|
ui.muted(`Assets: ${intent.scope}, ${intent.language}, ${intent.platforms.join(", ")}`);
|
|
@@ -166,7 +166,7 @@ async function update(options) {
|
|
|
166
166
|
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && !options.json);
|
|
167
167
|
if (npm.status === "available") {
|
|
168
168
|
if (!options.yes && interactive && !await ui.confirm(`Upgrade Matrix CLI ${npm.current} → ${npm.target} and then refresh these assets?`, true, labelsFor(intent.language))) return 0;
|
|
169
|
-
if (options.dryRun) { if (options.json) console.log(JSON.stringify({ ok: true, code: "DRY_RUN", npm, intent, matt: inspectMattInstallation({ projectRoot: root, home: intent.home, platforms: intent.platforms }) })); else ui.success(text(intent.language, "dryRunComplete")); return 0; }
|
|
169
|
+
if (options.dryRun) { if (options.json) console.log(JSON.stringify({ ok: true, code: "DRY_RUN", npm, intent, matt: inspectMattInstallation({ projectRoot: root, home: intent.home, platforms: intent.platforms }) })); else ui.success(text(intent.language, "dryRunComplete")); return 0; }
|
|
170
170
|
const updated = selfUpdate(version, npm.target);
|
|
171
171
|
if (!updated.ok) { const result = { ok: false, code: "SELF_UPDATE_FAILED", npm: { ...npm, status: "failed", reason: updated.reason }, recovery_command: "matrix update --skip-self-update" }; if (options.json) console.log(JSON.stringify(result)); else ui.warn(`${result.code}: ${updated.reason}`); return 2; }
|
|
172
172
|
const args = [globalBinPath(), "update", root, "--skip-self-update", "--_reexec", "--yes"];
|
|
@@ -174,51 +174,51 @@ async function update(options) {
|
|
|
174
174
|
const rerun = spawnSync(process.execPath, args, { stdio: "inherit" });
|
|
175
175
|
return Number.isInteger(rerun.status) ? rerun.status : 2;
|
|
176
176
|
}
|
|
177
|
-
if (options.json) return refresh(intent, { ...options, yes: true, updateMode: true }, ui);
|
|
178
|
-
return refresh(intent, { ...options, updateMode: true }, ui);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function recoveryArgument(value) {
|
|
182
|
-
const argument = String(value);
|
|
183
|
-
if (/^[A-Za-z0-9_./:@=-]+$/.test(argument)) return argument;
|
|
184
|
-
if (process.platform === "win32") return `"${argument.replaceAll('"', '\\"')}"`;
|
|
185
|
-
return `'${argument.replaceAll("'", "'\\''")}'`;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function runtimeRecovery(projectRuntime, argv) {
|
|
189
|
-
const argumentsWithoutJson = argv.slice(1).filter((value) => value !== "--json");
|
|
190
|
-
return { ...projectRuntime, recovery_command: [projectRuntime.recovery_command, ...argumentsWithoutJson.map(recoveryArgument)].join(" ") };
|
|
191
|
-
}
|
|
177
|
+
if (options.json) return refresh(intent, { ...options, yes: true, updateMode: true }, ui);
|
|
178
|
+
return refresh(intent, { ...options, updateMode: true }, ui);
|
|
179
|
+
}
|
|
192
180
|
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
: await intentFrom({ ...options, directory: root, yes: true }, ui);
|
|
200
|
-
const evaluation = createDistribution().diagnose(intent);
|
|
201
|
-
const matt = inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms });
|
|
202
|
-
if (options.json) console.log(JSON.stringify({ ...evaluation, matt }));
|
|
203
|
-
else { await ui.banner(); ui.info(text(intent.language, "doctor")); render(ui, evaluation); ui.muted(`${text(intent.language, "mattCompatibilityStatus").replace("{release}", matt.supported.release).replace("{status}", localizedState(intent.language, matt.status))}${matt.action ? `; ${matt.action}` : ""}`); }
|
|
204
|
-
return evaluation.ok ? 0 : 2;
|
|
205
|
-
}
|
|
181
|
+
function recoveryArgument(value) {
|
|
182
|
+
const argument = String(value);
|
|
183
|
+
if (/^[A-Za-z0-9_./:@=-]+$/.test(argument)) return argument;
|
|
184
|
+
if (process.platform === "win32") return `"${argument.replaceAll('"', '\\"')}"`;
|
|
185
|
+
return `'${argument.replaceAll("'", "'\\''")}'`;
|
|
186
|
+
}
|
|
206
187
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
188
|
+
function runtimeRecovery(projectRuntime, argv) {
|
|
189
|
+
const argumentsWithoutJson = argv.slice(1).filter((value) => value !== "--json");
|
|
190
|
+
return { ...projectRuntime, recovery_command: [projectRuntime.recovery_command, ...argumentsWithoutJson.map(recoveryArgument)].join(" ") };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function doctor(options) {
|
|
194
|
+
const ui = createUi({ color: options.color && !options.json });
|
|
195
|
+
const root = projectRoot(options.directory);
|
|
196
|
+
const installed = installedIntent({ projectRoot: root });
|
|
197
|
+
const intent = installed.ok
|
|
198
|
+
? { ...installed.intent, dryRun: true }
|
|
199
|
+
: await intentFrom({ ...options, directory: root, yes: true }, ui);
|
|
200
|
+
const evaluation = createDistribution().diagnose(intent);
|
|
201
|
+
const matt = inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms });
|
|
202
|
+
if (options.json) console.log(JSON.stringify({ ...evaluation, matt }));
|
|
203
|
+
else { await ui.banner(); ui.info(text(intent.language, "doctor")); render(ui, evaluation); ui.muted(`${text(intent.language, "mattCompatibilityStatus").replace("{release}", matt.supported.release).replace("{status}", localizedState(intent.language, matt.status))}${matt.action ? `; ${matt.action}` : ""}`); }
|
|
204
|
+
return evaluation.ok ? 0 : 2;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export async function main(argv) {
|
|
208
|
+
if (argv[0] === "workflow") {
|
|
209
|
+
const json = argv.includes("--json");
|
|
210
|
+
const projectRuntime = resolveProjectRuntime({ projectRoot: process.cwd() });
|
|
211
|
+
if (!projectRuntime.ok) {
|
|
212
|
+
const failure = projectRuntime.recovery_command ? runtimeRecovery(projectRuntime, argv) : projectRuntime;
|
|
213
|
+
if (json) console.log(JSON.stringify(failure));
|
|
214
|
+
else console.log(`MATRIX ERROR [${failure.code}]: ${failure.message}${failure.recovery_command ? `\nRun ${failure.recovery_command}` : ""}`);
|
|
215
|
+
return 2;
|
|
216
|
+
}
|
|
217
|
+
if (projectRuntime.runtime) {
|
|
218
|
+
const delegated = spawnSync(process.execPath, [projectRuntime.runtime, ...argv.slice(1).filter((value) => value !== "--json")], { stdio: "inherit" });
|
|
219
|
+
return Number.isInteger(delegated.status) ? delegated.status : 2;
|
|
220
|
+
}
|
|
221
|
+
const result = invoke(argv.slice(1).filter((value) => value !== "--json"));
|
|
222
222
|
if (json) console.log(JSON.stringify(result));
|
|
223
223
|
else console.log(result.ok ? JSON.stringify(result, null, 2) : `MATRIX ERROR [${result.code}]: ${result.message}`);
|
|
224
224
|
return result.ok ? 0 : 2;
|