@uniqbit/mate-core 0.15.3 → 0.15.4-canary.10
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-plugin/.claude-plugin/plugin.json +4 -0
- package/claude-plugin/hooks/artifact-finish-nudge.mjs +8 -0
- package/claude-plugin/hooks/hooks.json +37 -0
- package/claude-plugin/hooks/session-banner.mjs +8 -0
- package/claude-plugin/hooks/ts-loader.mjs +28 -0
- package/claude-plugin/hooks/validate-artifact-path.mjs +8 -0
- package/package.json +4 -2
- package/src/cli/commands/artifact/finish/openspec.ts +1 -5
- package/src/cli/commands/companion/hub.ts +99 -0
- package/src/cli/commands/companion/link.ts +0 -5
- package/src/cli/commands/companion/tui.ts +3 -2
- package/src/cli/commands/doctor.ts +254 -170
- package/src/cli/commands/install.ts +18 -14
- package/src/cli/commands/launch/shared.ts +4 -4
- package/src/cli/commands/plugin/install.ts +94 -0
- package/src/cli/commands/plugin/plugin.ts +17 -0
- package/src/cli/commands/setup.ts +4 -6
- package/src/cli/commands/update.ts +9 -11
- package/src/cli/companion-link-wizard.tsx +22 -78
- package/src/cli/main.ts +57 -13
- package/src/cli/plugin-commands.ts +4 -4
- package/src/cli/repo-list-table.tsx +2 -7
- package/src/cli/usage.ts +7 -3
- package/src/distribution.ts +3 -5
- package/src/framework.ts +4 -36
- package/src/hooks/artifact-finish-nudge.ts +212 -0
- package/src/hooks/session-banner.ts +25 -0
- package/src/hooks/validate-artifact-path.ts +236 -0
- package/src/index.ts +4 -0
- package/src/lib/context-mode-package.ts +5 -3
- package/src/lib/install.ts +31 -43
- package/src/lib/orchestrator/adapters/base.ts +7 -8
- package/src/lib/orchestrator/adapters/claude.ts +16 -0
- package/src/lib/orchestrator/adapters/opencode.ts +10 -60
- package/src/lib/orchestrator/companion-hub.ts +380 -0
- package/src/lib/orchestrator/companion-store.ts +1 -35
- package/src/lib/orchestrator/config-store.ts +94 -7
- package/src/lib/orchestrator/editor.ts +4 -20
- package/src/lib/orchestrator/framework-context.ts +119 -24
- package/src/lib/orchestrator/global-config-store.ts +1 -5
- package/src/lib/orchestrator/launcher.ts +3 -9
- package/src/lib/orchestrator/migration.ts +0 -23
- package/src/lib/orchestrator/opencode-guidance.ts +1 -2
- package/src/lib/orchestrator/repo-local-registry.ts +3 -1
- package/src/lib/orchestrator/root-context.ts +117 -0
- package/src/lib/orchestrator/setup-compatibilities.ts +1 -1
- package/src/lib/orchestrator/setup-preflight.ts +5 -18
- package/src/lib/orchestrator/types.ts +27 -19
- package/src/lib/orchestrator/working-repo-store.ts +9 -3
- package/src/lib/package-paths.ts +37 -0
- package/src/lib/update-checker.ts +5 -5
- package/src/playbooks/companion-guidance.ts +7 -7
- package/src/runtime/env.ts +0 -4
- package/src/templates/capabilities/openspec-cap/mate-skills/{mate-artifact-finish → agents/mate-artifact-finish}/SKILL.md +8 -8
- package/src/templates/capabilities/openspec-cap/mate-skills/{mate-artifact-finish → agents/mate-artifact-finish}/references/openspec.md +3 -3
- package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/SKILL.md +58 -0
- package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/references/openspec.md +139 -0
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +6 -5
- package/src/templates/root/TEMPLATE_AGENTS.md +2 -0
- package/src/templates/root/TEMPLATE_CLAUDE.md +2 -0
- package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +3541 -0
- package/src/tools/setup/capabilities/context-mode.ts +57 -83
- package/src/tools/setup/capabilities/graphify-shared.ts +27 -0
- package/src/tools/setup/capabilities/graphify.ts +86 -295
- package/src/tools/setup/capabilities/openspec.ts +27 -30
- package/src/tools/setup/capabilities/react-doctor.ts +37 -37
- package/src/tools/setup/capabilities/rtk.ts +4 -1
- package/src/tools/setup/capabilities/tokensave-shared.ts +6 -0
- package/src/tools/setup/capabilities/tokensave.ts +50 -71
- package/src/tools/setup/context-services.ts +29 -0
- package/src/tools/setup/dynamic-plugins/host.ts +2 -1
- package/src/tools/setup/dynamic-plugins/hydrate.ts +3 -13
- package/src/tools/setup/dynamic-plugins/install.ts +124 -109
- package/src/tools/setup/dynamic-plugins/loader.ts +23 -58
- package/src/tools/setup/dynamic-plugins/paths.ts +8 -19
- package/src/tools/setup/dynamic-plugins/registry-hint.ts +14 -0
- package/src/tools/setup/engine.ts +65 -1
- package/src/tools/setup/mate.ts +28 -31
- package/src/tools/setup/plugin.ts +81 -0
- package/src/tools/setup/plugins/gitignore.ts +19 -9
- package/src/tools/setup/plugins/guidance.ts +2 -6
- package/src/tools/setup/policy.ts +4 -7
- package/src/tools/setup/providers/agent-file-sections.ts +78 -0
- package/src/tools/setup/providers/claude-format.ts +159 -0
- package/src/tools/setup/providers/claude.ts +283 -292
- package/src/tools/setup/providers/opencode-format.ts +146 -0
- package/src/tools/setup/providers/opencode.ts +171 -66
- package/src/tools/setup/providers/skill-tree.ts +38 -0
- package/src/tools/setup.ts +12 -7
- package/src/tui.ts +5 -0
- package/src/templates/capabilities/openspec-cap/claude/hooks/mate-artifact-finish.sh +0 -161
- package/src/templates/providers/claude/.claude/hooks/mate-session-banner +0 -28
- package/src/templates/providers/claude/.claude/hooks/validate-artifact-path +0 -242
- package/src/tools/setup/dynamic-plugins/pin-store.ts +0 -30
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin shim: hook logic lives in src/hooks/artifact-finish-nudge.ts, loaded
|
|
3
|
+
// through ts-loader.mjs because the installed plugin sits inside node_modules
|
|
4
|
+
// where node's native type stripping is disabled.
|
|
5
|
+
import "./ts-loader.mjs";
|
|
6
|
+
|
|
7
|
+
const { run } = await import("../../src/hooks/artifact-finish-nudge.ts");
|
|
8
|
+
process.exitCode = await run();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Mate hooks — PreToolUse artifact-path guardrail, SessionStart banner, PostToolUse OpenSpec archive-finish nudge (self-gated at runtime)",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"PreToolUse": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "Write|Edit|MultiEdit|Bash",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/validate-artifact-path.mjs\""
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"SessionStart": [
|
|
16
|
+
{
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-banner.mjs\""
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"PostToolUse": [
|
|
26
|
+
{
|
|
27
|
+
"matcher": "Bash",
|
|
28
|
+
"hooks": [
|
|
29
|
+
{
|
|
30
|
+
"type": "command",
|
|
31
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/artifact-finish-nudge.mjs\""
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin shim: hook logic lives in src/hooks/session-banner.ts, loaded through
|
|
3
|
+
// ts-loader.mjs because the installed plugin sits inside node_modules where
|
|
4
|
+
// node's native type stripping is disabled.
|
|
5
|
+
import "./ts-loader.mjs";
|
|
6
|
+
|
|
7
|
+
const { run } = await import("../../src/hooks/session-banner.ts");
|
|
8
|
+
process.exitCode = run();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Synchronous module loader that strips TypeScript types itself. Node's
|
|
2
|
+
// built-in .ts loading refuses files under node_modules
|
|
3
|
+
// (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), which is exactly where the
|
|
4
|
+
// published plugin lives — so the hook shims register this loader before
|
|
5
|
+
// importing their src/hooks/*.ts implementation.
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import { registerHooks, stripTypeScriptTypes } from "node:module";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
// stripTypeScriptTypes emits an ExperimentalWarning; hooks must keep stderr
|
|
11
|
+
// clean because Claude Code surfaces it to the user on non-zero exits.
|
|
12
|
+
const emitWarning = process.emitWarning;
|
|
13
|
+
process.emitWarning = (warning, ...args) => {
|
|
14
|
+
if (String(warning).includes("stripTypeScriptTypes")) return;
|
|
15
|
+
emitWarning.call(process, warning, ...args);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
registerHooks({
|
|
19
|
+
load(url, context, next) {
|
|
20
|
+
if (!url.endsWith(".ts")) return next(url, context);
|
|
21
|
+
const source = fs.readFileSync(fileURLToPath(url), "utf8");
|
|
22
|
+
return {
|
|
23
|
+
format: "module",
|
|
24
|
+
source: stripTypeScriptTypes(source),
|
|
25
|
+
shortCircuit: true,
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin shim: hook logic lives in src/hooks/validate-artifact-path.ts, loaded
|
|
3
|
+
// through ts-loader.mjs because the installed plugin sits inside node_modules
|
|
4
|
+
// where node's native type stripping is disabled.
|
|
5
|
+
import "./ts-loader.mjs";
|
|
6
|
+
|
|
7
|
+
const { run } = await import("../../src/hooks/validate-artifact-path.ts");
|
|
8
|
+
process.exitCode = await run();
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniqbit/mate-core",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.4-canary.10",
|
|
4
4
|
"description": "Core framework and plugin APIs for Mate.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
7
7
|
"src/",
|
|
8
8
|
"wrappers/",
|
|
9
|
+
"claude-plugin/",
|
|
9
10
|
"!src/**/*.test.ts",
|
|
10
11
|
"!src/**/*.test.tsx"
|
|
11
12
|
],
|
|
@@ -14,7 +15,8 @@
|
|
|
14
15
|
".": "./src/index.ts",
|
|
15
16
|
"./plugins": "./src/plugins.ts",
|
|
16
17
|
"./runtime": "./src/runtime/index.ts",
|
|
17
|
-
"./opencode": "./src/opencode/index.ts"
|
|
18
|
+
"./opencode": "./src/opencode/index.ts",
|
|
19
|
+
"./tui": "./src/tui.ts"
|
|
18
20
|
},
|
|
19
21
|
"publishConfig": {
|
|
20
22
|
"access": "public",
|
|
@@ -61,10 +61,7 @@ async function capSync(context: FinishContext): Promise<boolean> {
|
|
|
61
61
|
const previous = process.exitCode;
|
|
62
62
|
process.exitCode = 0;
|
|
63
63
|
const previousEnv = new Map(
|
|
64
|
-
["MATE_ARTIFACT_PATH", "MATE_REPO_PATH", "MATE_REPO_ID"
|
|
65
|
-
key,
|
|
66
|
-
process.env[key],
|
|
67
|
-
]),
|
|
64
|
+
["MATE_ARTIFACT_PATH", "MATE_REPO_PATH", "MATE_REPO_ID"].map((key) => [key, process.env[key]]),
|
|
68
65
|
);
|
|
69
66
|
|
|
70
67
|
// Pass both sides of the companion/working-repo boundary explicitly. Capability
|
|
@@ -73,7 +70,6 @@ async function capSync(context: FinishContext): Promise<boolean> {
|
|
|
73
70
|
if (context.repository) {
|
|
74
71
|
process.env.MATE_REPO_PATH = context.repository.path;
|
|
75
72
|
process.env.MATE_REPO_ID = context.repository.id;
|
|
76
|
-
process.env.MATE_REPO_PROFILE = context.repository.profile;
|
|
77
73
|
}
|
|
78
74
|
try {
|
|
79
75
|
await runIndexCapCommand([]);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import readline from "node:readline/promises";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
addHubMember,
|
|
5
|
+
discoverHubSource,
|
|
6
|
+
initializeCompanionHub,
|
|
7
|
+
syncHub,
|
|
8
|
+
updateHubPlugins,
|
|
9
|
+
} from "../../../lib/orchestrator/companion-hub";
|
|
10
|
+
import { GlobalConfigStore } from "../../../lib/orchestrator/global-config-store";
|
|
11
|
+
|
|
12
|
+
function positionalArgs(argv: string[]): string[] {
|
|
13
|
+
const values: string[] = [];
|
|
14
|
+
const valueFlags = new Set(["id", "path", "url", "companion", "ref"]);
|
|
15
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
16
|
+
const value = argv[index]!;
|
|
17
|
+
if (!value.startsWith("--")) {
|
|
18
|
+
values.push(value);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (valueFlags.has(value.slice(2))) index += 1;
|
|
22
|
+
}
|
|
23
|
+
return values;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function flagValue(argv: string[], name: string): string | undefined {
|
|
27
|
+
const index = argv.indexOf(`--${name}`);
|
|
28
|
+
return index >= 0 ? argv[index + 1] : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function selectRegisteredCompanion(): Promise<string> {
|
|
32
|
+
const companions = await new GlobalConfigStore().list();
|
|
33
|
+
if (companions.length === 0) throw new Error("No registered companions are available.");
|
|
34
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
35
|
+
throw new Error("hub add requires a source path or Git URL outside a TTY.");
|
|
36
|
+
}
|
|
37
|
+
const prompt = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
38
|
+
try {
|
|
39
|
+
console.log("Registered companions:");
|
|
40
|
+
companions.forEach((candidate, index) => console.log(` ${index + 1}. ${candidate}`));
|
|
41
|
+
const answer = await prompt.question("Select a companion: ");
|
|
42
|
+
const selected = companions[Number.parseInt(answer, 10) - 1];
|
|
43
|
+
if (!selected) throw new Error("Invalid companion selection.");
|
|
44
|
+
return selected;
|
|
45
|
+
} finally {
|
|
46
|
+
prompt.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function runHubInit(argv: string[]): Promise<void> {
|
|
51
|
+
const folder = positionalArgs(argv)[0] ?? process.cwd();
|
|
52
|
+
const hubPath = await initializeCompanionHub(folder);
|
|
53
|
+
console.log(`Initialized companion hub: ${hubPath}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function runHubAdd(argv: string[]): Promise<void> {
|
|
57
|
+
const sourceArg =
|
|
58
|
+
positionalArgs(argv)[0] ?? flagValue(argv, "url") ?? flagValue(argv, "companion");
|
|
59
|
+
const source = discoverHubSource(sourceArg ?? (await selectRegisteredCompanion()));
|
|
60
|
+
const ref = flagValue(argv, "ref");
|
|
61
|
+
if (ref && source.kind === "git") source.ref = ref;
|
|
62
|
+
const member = await addHubMember(process.cwd(), source, {
|
|
63
|
+
id: flagValue(argv, "id"),
|
|
64
|
+
memberPath: flagValue(argv, "path"),
|
|
65
|
+
});
|
|
66
|
+
console.log(`Added hub member ${member.id}: ${member.path}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function runHubSync(argv: string[]): Promise<void> {
|
|
70
|
+
const results = await syncHub(process.cwd());
|
|
71
|
+
const plugins = await updateHubPlugins(process.cwd());
|
|
72
|
+
if (argv.includes("--json")) {
|
|
73
|
+
console.log(JSON.stringify({ companions: results, plugins }));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const result of results) console.log(`${result.id}: ${result.status} (${result.message})`);
|
|
77
|
+
for (const plugin of plugins) {
|
|
78
|
+
const detail = plugin.status === "failed" ? `: ${plugin.error ?? "failed"}` : "";
|
|
79
|
+
console.log(`hub plugin ${plugin.package}: ${plugin.status}${detail}`);
|
|
80
|
+
}
|
|
81
|
+
if (plugins.some((plugin) => plugin.status === "failed")) process.exitCode = 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function runHubCommand(argv: string[]): Promise<void> {
|
|
85
|
+
const [subcommand, ...rest] = argv;
|
|
86
|
+
switch (subcommand) {
|
|
87
|
+
case "init":
|
|
88
|
+
await runHubInit(rest);
|
|
89
|
+
return;
|
|
90
|
+
case "add":
|
|
91
|
+
await runHubAdd(rest);
|
|
92
|
+
return;
|
|
93
|
+
case "sync":
|
|
94
|
+
await runHubSync(rest);
|
|
95
|
+
return;
|
|
96
|
+
default:
|
|
97
|
+
throw new Error("Usage: mate hub <init|add|sync>");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
type CompanionLinkWizardOptions,
|
|
11
11
|
} from "../../companion-link-wizard";
|
|
12
12
|
import { extractRepoName } from "../artifact/extract-repo-name";
|
|
13
|
-
import { defaultConfig } from "../../../lib/orchestrator/config-store";
|
|
14
13
|
import { GlobalConfigStore } from "../../../lib/orchestrator/global-config-store";
|
|
15
14
|
import { CompanionResolver } from "../../../lib/orchestrator/companion-resolver";
|
|
16
15
|
import { inspectSetupPreflight } from "../../../lib/orchestrator/setup-preflight";
|
|
@@ -131,7 +130,6 @@ export async function runCompanionLinkCommandWithDeps(
|
|
|
131
130
|
|
|
132
131
|
const cwd = path.resolve(process.cwd());
|
|
133
132
|
const existingMatch = await resolveCompanion(cwd);
|
|
134
|
-
const profiles = Object.keys(defaultConfig().profiles);
|
|
135
133
|
const companionsDir = companionsHomeDir();
|
|
136
134
|
const candidatePaths = new Set(await listCompanions());
|
|
137
135
|
if (existingMatch) candidatePaths.add(existingMatch.companionPath);
|
|
@@ -148,7 +146,6 @@ export async function runCompanionLinkCommandWithDeps(
|
|
|
148
146
|
).filter((candidate): candidate is string => candidate !== null);
|
|
149
147
|
|
|
150
148
|
const inputs = await selectInputs({
|
|
151
|
-
profiles,
|
|
152
149
|
existingCompanions,
|
|
153
150
|
});
|
|
154
151
|
if (!inputs) {
|
|
@@ -241,7 +238,6 @@ export async function runCompanionLinkCommandWithDeps(
|
|
|
241
238
|
const repository: LinkedRepository = {
|
|
242
239
|
id: path.basename(cwd),
|
|
243
240
|
path: cwd,
|
|
244
|
-
profile: inputs.profile,
|
|
245
241
|
};
|
|
246
242
|
|
|
247
243
|
const registerRepository =
|
|
@@ -292,6 +288,5 @@ export async function runCompanionLinkCommandWithDeps(
|
|
|
292
288
|
console.log(` Companion: ${companionPath}`);
|
|
293
289
|
}
|
|
294
290
|
console.log(` Source: ${persistedSource}`);
|
|
295
|
-
console.log(` Profile: ${repository.profile}`);
|
|
296
291
|
console.log(" Workspace: run `mate companion open` to open the workspace in your editor");
|
|
297
292
|
}
|
|
@@ -2,12 +2,14 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { accessSync } from "node:fs";
|
|
3
3
|
|
|
4
4
|
import { getActiveDistribution } from "../../../distribution";
|
|
5
|
+
import { FRAMEWORK_NAME } from "../../../framework";
|
|
5
6
|
import { resolveFrameworkContext } from "../../../lib/orchestrator/framework-context";
|
|
6
7
|
import { findRepoLocalLinkedRepository } from "../../../lib/orchestrator/repo-local-registry";
|
|
7
8
|
import { ensureUnambiguousCompanion } from "../launch/shared";
|
|
8
9
|
|
|
9
10
|
export function renderBanner(repoPath: string, companionPath: string): string {
|
|
10
|
-
const {
|
|
11
|
+
const { version } = getActiveDistribution().config;
|
|
12
|
+
const name = FRAMEWORK_NAME;
|
|
11
13
|
const lines = [`${name} v${version}`, "", `repo: ${repoPath}`, `${name}: ${companionPath}`];
|
|
12
14
|
const contentWidth = Math.max(...lines.map((l) => l.length));
|
|
13
15
|
const pad = (text: string) => `│ ${text.padEnd(contentWidth)} │`;
|
|
@@ -79,7 +81,6 @@ export async function runCompanionTuiCommand(): Promise<void> {
|
|
|
79
81
|
if (repository) {
|
|
80
82
|
process.env.MATE_REPO_PATH = repository.path;
|
|
81
83
|
process.env.MATE_REPO_ID = repository.id;
|
|
82
|
-
process.env.MATE_REPO_PROFILE = repository.profile;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
console.log(renderBanner(repoPath, companionPath));
|