@rosenfan/matrix 0.1.4 → 0.1.6

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.
Files changed (54) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +41 -40
  3. package/README.zh-CN.md +39 -38
  4. package/{.claude → assets}/skills/matrix/SKILL.md +79 -78
  5. package/{.claude → assets}/skills/matrix/agents/openai.yaml +4 -4
  6. package/{.claude → assets}/skills/matrix/scripts/matrix_claude.py +11 -11
  7. package/{.claude → assets}/skills/matrix/scripts/matrix_state.py +49 -49
  8. package/assets/skills/matrix/scripts/test_matrix_claude.py +69 -0
  9. package/assets/skills/matrix/scripts/test_matrix_state.py +69 -0
  10. package/{.claude → assets}/skills/matrix-archive/SKILL.md +36 -36
  11. package/{.claude/skills/matrix-hotfix → assets/skills/matrix-archive}/agents/openai.yaml +4 -4
  12. package/{.claude → assets}/skills/matrix-build/SKILL.md +41 -41
  13. package/{.claude/skills/matrix-archive → assets/skills/matrix-build}/agents/openai.yaml +4 -4
  14. package/{.claude → assets}/skills/matrix-design/SKILL.md +71 -65
  15. package/{.claude → assets}/skills/matrix-design/agents/openai.yaml +4 -4
  16. package/assets/skills/matrix-handoff/SKILL.md +89 -0
  17. package/{.claude/skills/matrix-claude → assets/skills/matrix-handoff}/agents/openai.yaml +4 -4
  18. package/{.claude → assets}/skills/matrix-hotfix/SKILL.md +8 -8
  19. package/{.claude/skills/matrix-build → assets/skills/matrix-hotfix}/agents/openai.yaml +4 -4
  20. package/{.claude → assets}/skills/matrix-open/SKILL.md +51 -40
  21. package/assets/skills/matrix-open/agents/openai.yaml +4 -0
  22. package/{.claude → assets}/skills/matrix-status/SKILL.md +15 -15
  23. package/{.claude → assets}/skills/matrix-status/agents/openai.yaml +4 -4
  24. package/{.claude → assets}/skills/matrix-tweak/SKILL.md +8 -8
  25. package/assets/skills/matrix-tweak/agents/openai.yaml +4 -0
  26. package/{.claude → assets}/skills/matrix-verify/SKILL.md +62 -62
  27. package/assets/skills/matrix-verify/agents/openai.yaml +4 -0
  28. package/assets/skills-zh-CN/matrix/SKILL.md +72 -71
  29. package/assets/skills-zh-CN/matrix-build/SKILL.md +4 -4
  30. package/assets/skills-zh-CN/matrix-design/SKILL.md +37 -20
  31. package/assets/skills-zh-CN/matrix-handoff/SKILL.md +79 -0
  32. package/assets/skills-zh-CN/matrix-open/SKILL.md +38 -28
  33. package/assets/skills-zh-CN/matrix-verify/SKILL.md +19 -19
  34. package/bin/matrix.js +15 -15
  35. package/package.json +4 -13
  36. package/src/catalog.js +79 -72
  37. package/src/cli.js +211 -141
  38. package/src/distribution.js +182 -179
  39. package/src/installer.js +10 -10
  40. package/src/matt-catalog.mjs +1 -1
  41. package/src/matt.js +9 -14
  42. package/src/messages.js +32 -23
  43. package/src/npm-invocation.js +23 -0
  44. package/src/project-index.js +43 -0
  45. package/src/runtime-resolver.js +76 -76
  46. package/src/self-update.js +88 -81
  47. package/src/ui.js +97 -89
  48. package/src/workflow-transaction.js +13 -13
  49. package/src/workflow.js +734 -705
  50. package/.claude/skills/matrix-claude/SKILL.md +0 -98
  51. package/.claude/skills/matrix-open/agents/openai.yaml +0 -4
  52. package/.claude/skills/matrix-tweak/agents/openai.yaml +0 -4
  53. package/.claude/skills/matrix-verify/agents/openai.yaml +0 -4
  54. package/assets/skills-zh-CN/matrix-claude/SKILL.md +0 -33
package/src/ui.js CHANGED
@@ -1,89 +1,97 @@
1
- import readline from "node:readline";
2
- import { stdin, stdout } from "node:process";
3
-
4
- const codes = { cyan: "\u001b[96m", blue: "\u001b[94m", green: "\u001b[92m", yellow: "\u001b[93m", magenta: "\u001b[95m", dim: "\u001b[2m", reset: "\u001b[0m" };
5
- const pause = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
6
- const logo = String.raw`███╗ ███╗ █████╗ ████████╗██████╗ ██╗██╗ ██╗
7
- ████╗ ████║██╔══██╗╚══██╔══╝██╔══██╗██║╚██╗██╔╝
8
- ██╔████╔██║███████║ ██║ ██████╔╝██║ ╚███╔╝
9
- ██║╚██╔╝██║██╔══██║ ██║ ██╔══██╗██║ ██╔██╗
10
- ██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║██║██╔╝ ██╗
11
- ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝`;
12
- const bannerLines = 9;
13
-
14
- function cancelled() {
15
- const error = new Error("Cancelled.");
16
- error.code = "CANCELLED";
17
- return error;
18
- }
19
-
20
- export function createUi({ color = stdout.isTTY, animate = true } = {}) {
21
- const paint = (name, value) => color ? `${codes[name]}${value}${codes.reset}` : value;
22
- const shouldAnimate = animate && color && stdout.isTTY;
23
- const writeFrame = (stage, status, clearPrevious = false) => {
24
- if (clearPrevious) stdout.write(`\u001b[${bannerLines}A`);
25
- stdout.write("\u001b[2K" + paint("green", `[ MATRIX BOOT // ${status} ]`) + "\n");
26
- for (const line of logo.split("\n")) {
27
- const reveal = Math.floor(line.length * stage);
28
- const stable = line.slice(0, reveal);
29
- const interference = stage === 1 ? "" : "#@%/\\|~".repeat(Math.ceil((line.length - reveal) / 7)).slice(0, line.length - reveal);
30
- stdout.write("\u001b[2K" + paint(stage < 0.7 ? "green" : "cyan", stable + interference) + "\n");
31
- }
32
- stdout.write("\u001b[2K" + paint("yellow", ` ${Math.round(stage * 100)}% ${status}`) + "\n\n");
33
- };
34
- return {
35
- async banner() {
36
- if (shouldAnimate) {
37
- const sequence = [
38
- [0.04, "SIGNAL LOST", 260],
39
- [0.28, "MAGNETIC INTERFERENCE", 320],
40
- [0.62, "NEURAL LINK FORMING", 360],
41
- [1, "SIGNAL LOCKED", 420]
42
- ];
43
- stdout.write("\u001b[?25l");
44
- try {
45
- for (let index = 0; index < sequence.length; index += 1) {
46
- const [stage, status, duration] = sequence[index];
47
- writeFrame(stage, status, index > 0);
48
- await pause(duration);
49
- }
50
- } finally {
51
- stdout.write("\u001b[?25h");
52
- }
53
- stdout.write(`\u001b[${bannerLines}A`);
54
- }
55
- console.log(paint("cyan", logo));
56
- console.log(paint("yellow", "Evidence-driven workflow with Prim and Arch orchestration"));
57
- console.log();
58
- },
59
- info(message) { console.log(`${paint("blue", "> ")} ${message}`); },
60
- success(message) { console.log(`${paint("green", "OK")} ${message}`); },
61
- warn(message) { console.log(`${paint("yellow", "!")} ${message}`); },
62
- muted(message) { console.log(paint("dim", message)); },
63
- async select(question, choices, initial = 0) {
64
- if (!stdin.isTTY || !stdout.isTTY) return choices[initial].value;
65
- let selected = initial;
66
- readline.emitKeypressEvents(stdin); const wasRaw = stdin.isRaw; stdin.setRawMode(true); stdin.resume(); stdout.write(`? ${question}\n`);
67
- const render = (clear = false) => { if (clear) stdout.write(`\u001b[${choices.length}A`); choices.forEach((choice, index) => { stdout.write("\u001b[2K"); stdout.write(`${index === selected ? paint("blue", ">") : " "} ${index === selected ? paint("blue", choice.label) : choice.label}\n`); }); };
68
- render();
69
- return new Promise((resolve, reject) => {
70
- const cleanup = () => { stdin.off("keypress", onKeypress); stdin.setRawMode(Boolean(wasRaw)); stdin.pause(); };
71
- const onKeypress = (_value, key = {}) => { if (key.ctrl && key.name === "c") { cleanup(); reject(cancelled()); } else if (["up", "k"].includes(key.name)) { selected = (selected - 1 + choices.length) % choices.length; render(true); } else if (["down", "j"].includes(key.name)) { selected = (selected + 1) % choices.length; render(true); } else if (key.name === "return") { cleanup(); resolve(choices[selected].value); } };
72
- stdin.on("keypress", onKeypress);
73
- });
74
- },
75
- async selectMany(question, choices) {
76
- if (!stdin.isTTY || !stdout.isTTY) return choices.map((choice) => choice.value);
77
- let selected = 0; const checked = new Set(choices.map((choice) => choice.value));
78
- readline.emitKeypressEvents(stdin); const wasRaw = stdin.isRaw; stdin.setRawMode(true); stdin.resume(); stdout.write(`? ${question}\n`);
79
- const render = (clear = false) => { if (clear) stdout.write(`\u001b[${choices.length}A`); choices.forEach((choice, index) => { stdout.write("\u001b[2K"); const marker = checked.has(choice.value) ? "[x]" : "[ ]"; stdout.write(`${index === selected ? paint("blue", ">") : " "} ${marker} ${index === selected ? paint("blue", choice.label) : choice.label}\n`); }); };
80
- render();
81
- return new Promise((resolve, reject) => {
82
- const cleanup = () => { stdin.off("keypress", onKeypress); stdin.setRawMode(Boolean(wasRaw)); stdin.pause(); };
83
- const onKeypress = (_value, key = {}) => { if (key.ctrl && key.name === "c") { cleanup(); reject(cancelled()); } else if (["up", "k"].includes(key.name)) { selected = (selected - 1 + choices.length) % choices.length; render(true); } else if (["down", "j"].includes(key.name)) { selected = (selected + 1) % choices.length; render(true); } else if (key.name === "space") { const value = choices[selected].value; checked.has(value) ? checked.delete(value) : checked.add(value); render(true); } else if (key.name === "return") { cleanup(); resolve(choices.filter((choice) => checked.has(choice.value)).map((choice) => choice.value)); } };
84
- stdin.on("keypress", onKeypress);
85
- });
86
- },
87
- async confirm(question, defaultValue = true, labels = { yes: "Yes", no: "No" }) { return this.select(question, [{ label: defaultValue ? labels.yes : labels.no, value: defaultValue }, { label: defaultValue ? labels.no : labels.yes, value: !defaultValue }]); }
88
- };
89
- }
1
+ import readline from "node:readline";
2
+ import { stdin, stdout } from "node:process";
3
+
4
+ const codes = { cyan: "\u001b[96m", blue: "\u001b[94m", green: "\u001b[92m", yellow: "\u001b[93m", magenta: "\u001b[95m", dim: "\u001b[2m", reset: "\u001b[0m" };
5
+ const pause = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
6
+ const logo = String.raw`███╗ ███╗ █████╗ ████████╗██████╗ ██╗██╗ ██╗
7
+ ████╗ ████║██╔══██╗╚══██╔══╝██╔══██╗██║╚██╗██╔╝
8
+ ██╔████╔██║███████║ ██║ ██████╔╝██║ ╚███╔╝
9
+ ██║╚██╔╝██║██╔══██║ ██║ ██╔══██╗██║ ██╔██╗
10
+ ██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║██║██╔╝ ██╗
11
+ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝`;
12
+ const bannerLines = 9;
13
+
14
+ function cancelled() {
15
+ const error = new Error("Cancelled.");
16
+ error.code = "CANCELLED";
17
+ return error;
18
+ }
19
+
20
+ export function createUi({ color = stdout.isTTY, animate = true } = {}) {
21
+ const paint = (name, value) => color ? `${codes[name]}${value}${codes.reset}` : value;
22
+ const shouldAnimate = animate && color && stdout.isTTY;
23
+ const writeFrame = (stage, status, clearPrevious = false) => {
24
+ if (clearPrevious) stdout.write(`\u001b[${bannerLines}A`);
25
+ stdout.write("\u001b[2K" + paint("green", `[ MATRIX BOOT // ${status} ]`) + "\n");
26
+ for (const line of logo.split("\n")) {
27
+ const reveal = Math.floor(line.length * stage);
28
+ const stable = line.slice(0, reveal);
29
+ const interference = stage === 1 ? "" : "#@%/\\|~".repeat(Math.ceil((line.length - reveal) / 7)).slice(0, line.length - reveal);
30
+ stdout.write("\u001b[2K" + paint(stage < 0.7 ? "green" : "cyan", stable + interference) + "\n");
31
+ }
32
+ stdout.write("\u001b[2K" + paint("yellow", ` ${Math.round(stage * 100)}% ${status}`) + "\n\n");
33
+ };
34
+ return {
35
+ async banner() {
36
+ if (shouldAnimate) {
37
+ const sequence = [
38
+ [0.04, "SIGNAL LOST", 260],
39
+ [0.28, "MAGNETIC INTERFERENCE", 320],
40
+ [0.62, "NEURAL LINK FORMING", 360],
41
+ [1, "SIGNAL LOCKED", 420]
42
+ ];
43
+ stdout.write("\u001b[?25l");
44
+ try {
45
+ for (let index = 0; index < sequence.length; index += 1) {
46
+ const [stage, status, duration] = sequence[index];
47
+ writeFrame(stage, status, index > 0);
48
+ await pause(duration);
49
+ }
50
+ } finally {
51
+ stdout.write("\u001b[?25h");
52
+ }
53
+ stdout.write(`\u001b[${bannerLines}A`);
54
+ }
55
+ console.log(paint("cyan", logo));
56
+ console.log(paint("yellow", "Evidence-driven workflow with Prim and Arch orchestration"));
57
+ console.log();
58
+ },
59
+ info(message) { console.log(`${paint("blue", "> ")} ${message}`); },
60
+ success(message) { console.log(`${paint("green", "OK")} ${message}`); },
61
+ warn(message) { console.log(`${paint("yellow", "!")} ${message}`); },
62
+ muted(message) { console.log(paint("dim", message)); },
63
+ async select(question, choices, initial = 0) {
64
+ if (!stdin.isTTY || !stdout.isTTY) return choices[initial].value;
65
+ let selected = initial;
66
+ let renderedLines = 0;
67
+ readline.emitKeypressEvents(stdin); const wasRaw = stdin.isRaw; stdin.setRawMode(true); stdin.resume(); stdout.write(`? ${question}\n`);
68
+ const render = (clear = false) => {
69
+ if (clear) stdout.write(`\u001b[${renderedLines}A`);
70
+ renderedLines = 0;
71
+ choices.forEach((choice, index) => {
72
+ stdout.write("\u001b[2K"); stdout.write(`${index === selected ? paint("blue", ">") : " "} ${index === selected ? paint("blue", choice.label) : choice.label}\n`); renderedLines += 1;
73
+ if (choice.detail) { stdout.write("\u001b[2K"); stdout.write(` ${paint("dim", choice.detail)}\n`); renderedLines += 1; }
74
+ });
75
+ };
76
+ render();
77
+ return new Promise((resolve, reject) => {
78
+ const cleanup = () => { stdin.off("keypress", onKeypress); stdin.setRawMode(Boolean(wasRaw)); stdin.pause(); };
79
+ const onKeypress = (_value, key = {}) => { if (key.ctrl && key.name === "c") { cleanup(); reject(cancelled()); } else if (["up", "k"].includes(key.name)) { selected = (selected - 1 + choices.length) % choices.length; render(true); } else if (["down", "j"].includes(key.name)) { selected = (selected + 1) % choices.length; render(true); } else if (key.name === "return") { cleanup(); resolve(choices[selected].value); } };
80
+ stdin.on("keypress", onKeypress);
81
+ });
82
+ },
83
+ async selectMany(question, choices) {
84
+ if (!stdin.isTTY || !stdout.isTTY) return choices.map((choice) => choice.value);
85
+ let selected = 0; const checked = new Set(choices.map((choice) => choice.value));
86
+ readline.emitKeypressEvents(stdin); const wasRaw = stdin.isRaw; stdin.setRawMode(true); stdin.resume(); stdout.write(`? ${question}\n`);
87
+ const render = (clear = false) => { if (clear) stdout.write(`\u001b[${choices.length}A`); choices.forEach((choice, index) => { stdout.write("\u001b[2K"); const marker = checked.has(choice.value) ? "[x]" : "[ ]"; stdout.write(`${index === selected ? paint("blue", ">") : " "} ${marker} ${index === selected ? paint("blue", choice.label) : choice.label}\n`); }); };
88
+ render();
89
+ return new Promise((resolve, reject) => {
90
+ const cleanup = () => { stdin.off("keypress", onKeypress); stdin.setRawMode(Boolean(wasRaw)); stdin.pause(); };
91
+ const onKeypress = (_value, key = {}) => { if (key.ctrl && key.name === "c") { cleanup(); reject(cancelled()); } else if (["up", "k"].includes(key.name)) { selected = (selected - 1 + choices.length) % choices.length; render(true); } else if (["down", "j"].includes(key.name)) { selected = (selected + 1) % choices.length; render(true); } else if (key.name === "space") { const value = choices[selected].value; checked.has(value) ? checked.delete(value) : checked.add(value); render(true); } else if (key.name === "return") { cleanup(); resolve(choices.filter((choice) => checked.has(choice.value)).map((choice) => choice.value)); } };
92
+ stdin.on("keypress", onKeypress);
93
+ });
94
+ },
95
+ async confirm(question, defaultValue = true, labels = { yes: "Yes", no: "No" }) { return this.select(question, [{ label: defaultValue ? labels.yes : labels.no, value: defaultValue }, { label: defaultValue ? labels.no : labels.yes, value: !defaultValue }]); }
96
+ };
97
+ }
@@ -1,18 +1,18 @@
1
1
  import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import crypto from "node:crypto";
5
- import { fileURLToPath } from "node:url";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import crypto from "node:crypto";
5
+ import { fileURLToPath } from "node:url";
6
6
 
7
7
  const JOURNAL_SCHEMA = "matrix/workflow-transaction/v1";
8
8
  const RECEIPT_SCHEMA = "matrix/workflow-transaction-receipt/v1";
9
9
  const LOCK_SCHEMA = "matrix/workflow-lock/v1";
10
10
  const MAX_JOURNAL_BYTES = 1024 * 1024;
11
11
  const MAX_TRANSACTIONS = 128;
12
- const RECEIPT_LIMIT = 32;
13
- const quoteCommandPath = (value) => process.platform === "win32" ? `"${value}"` : `'${value.replaceAll("'", "'\\''")}'`;
14
- const workflowRuntimePath = path.join(path.dirname(fileURLToPath(import.meta.url)), "matrix-runtime.mjs");
15
- const workflowCommand = (argumentsText) => `node ${quoteCommandPath(workflowRuntimePath)} ${argumentsText}`;
12
+ const RECEIPT_LIMIT = 32;
13
+ const quoteCommandPath = (value) => process.platform === "win32" ? `"${value}"` : `'${value.replaceAll("'", "'\\''")}'`;
14
+ const workflowRuntimePath = path.join(path.dirname(fileURLToPath(import.meta.url)), "matrix-runtime.mjs");
15
+ const workflowCommand = (argumentsText) => `node ${quoteCommandPath(workflowRuntimePath)} ${argumentsText}`;
16
16
 
17
17
  const stamp = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
18
18
  const fail = (code, message, extra = {}) => ({ ok: false, code, message, ...extra });
@@ -434,11 +434,11 @@ export function workflowMutationGate(cwd) {
434
434
  let pending;
435
435
  try { pending = pendingTransactions(cwd); }
436
436
  catch (error) { return recoverError(error, null); }
437
- if (pending.length) return fail("WORKFLOW_RECOVERY_REQUIRED", "An unfinished Workflow transaction must be resolved before starting another mutation.", { transactions: pending.map(({ id }) => id), recovery_command: workflowCommand("doctor") });
437
+ if (pending.length) return fail("WORKFLOW_RECOVERY_REQUIRED", "An unfinished Workflow transaction must be resolved before starting another mutation.", { transactions: pending.map(({ id }) => id), recovery_command: workflowCommand("doctor") });
438
438
  const currentLock = readLock(cwd);
439
439
  if (!currentLock) return null;
440
440
  const owner = lockOwner(currentLock);
441
- return fail(owner === "active" ? "WORKFLOW_BUSY" : owner === "absent" ? "WORKFLOW_RECOVERY_REQUIRED" : "WORKFLOW_LOCK_CONFLICT", owner === "active" ? "An active Workflow mutation owns the project boundary." : "A Workflow lock requires explicit doctor recovery.", { lock_id: currentLock.id, transaction_id: currentLock.transaction_id, recovery_command: workflowCommand("doctor") });
441
+ return fail(owner === "active" ? "WORKFLOW_BUSY" : owner === "absent" ? "WORKFLOW_RECOVERY_REQUIRED" : "WORKFLOW_LOCK_CONFLICT", owner === "active" ? "An active Workflow mutation owns the project boundary." : "A Workflow lock requires explicit doctor recovery.", { lock_id: currentLock.id, transaction_id: currentLock.transaction_id, recovery_command: workflowCommand("doctor") });
442
442
  }
443
443
 
444
444
  function compactReceipt(cwd, journal, disposition) {
@@ -478,7 +478,7 @@ function recoverError(error, transactionId) {
478
478
  const code = error.code && String(error.code).startsWith("WORKFLOW_") ? error.code : "WORKFLOW_PERSISTENCE_FAILED";
479
479
  return fail(code, error.message, {
480
480
  transaction_id: transactionId,
481
- recovery_command: workflowCommand(`doctor --repair --transaction ${transactionId} --strategy continue`)
481
+ recovery_command: workflowCommand(`doctor --repair --transaction ${transactionId} --strategy continue`)
482
482
  });
483
483
  }
484
484
 
@@ -563,7 +563,7 @@ function repairTransaction(cwd, id, strategy) {
563
563
  }
564
564
 
565
565
  function exactCommands(transaction) {
566
- return transaction.strategies.map((strategy) => workflowCommand(`doctor --repair --transaction ${transaction.id} --strategy ${strategy}`));
566
+ return transaction.strategies.map((strategy) => workflowCommand(`doctor --repair --transaction ${transaction.id} --strategy ${strategy}`));
567
567
  }
568
568
 
569
569
  export function workflowDoctor(cwd, { repair = false, transaction = null, strategy = null, lock = null } = {}) {
@@ -589,7 +589,7 @@ export function workflowDoctor(cwd, { repair = false, transaction = null, strate
589
589
  transaction_id: currentLock.transaction_id,
590
590
  owner,
591
591
  commands: owner === "absent" && !transactions.some((item) => item.id === currentLock.transaction_id && (item.corrupt || !["committed", "rolled-back"].includes(item.status)))
592
- ? [workflowCommand(`doctor --repair --lock ${currentLock.id}`)]
592
+ ? [workflowCommand(`doctor --repair --lock ${currentLock.id}`)]
593
593
  : []
594
594
  }] : [])
595
595
  ];