@uniqbit/mate-core 0.15.1-canary.3 → 0.15.1-canary.4
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/package.json +1 -1
- package/src/cli/main.ts +71 -40
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +1 -0
- package/src/templates/root/TEMPLATE_AGENTS.md +1 -0
- package/src/templates/root/TEMPLATE_CLAUDE.md +1 -0
- package/src/tools/setup/dynamic-plugins/paths.ts +0 -3
- package/src/tools/setup/plugins/gitignore.ts +17 -11
package/package.json
CHANGED
package/src/cli/main.ts
CHANGED
|
@@ -22,16 +22,13 @@ import { hydrateDynamicPlugins } from "../tools/setup/dynamic-plugins/hydrate";
|
|
|
22
22
|
import { findPluginCliCommand } from "./plugin-commands";
|
|
23
23
|
import { usage } from "./usage";
|
|
24
24
|
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
command === "-h" ||
|
|
33
|
-
(command === "companion" && (subcommand === "setup" || subcommand === "link"))
|
|
34
|
-
);
|
|
25
|
+
export interface GateNeeds {
|
|
26
|
+
/** Block the command while an enforced update is pending. */
|
|
27
|
+
updateGuard?: boolean;
|
|
28
|
+
/** Require an unambiguous companion (selection wizard on ambiguity) before dispatch. */
|
|
29
|
+
companion?: boolean;
|
|
30
|
+
/** Require a complete installation (install preflight) before dispatch. */
|
|
31
|
+
install?: boolean;
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
export interface MainDeps {
|
|
@@ -54,6 +51,11 @@ export async function main(argv = process.argv, deps: MainDeps = mainDeps): Prom
|
|
|
54
51
|
return;
|
|
55
52
|
}
|
|
56
53
|
|
|
54
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
55
|
+
console.log(usage());
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
57
59
|
// Companion-declared plugins register before cap-command detection so
|
|
58
60
|
// their commands route like compiled-in ones (including MCP servers whose
|
|
59
61
|
// command is their own cap subcommand). Diagnostics stay on stderr; a
|
|
@@ -65,73 +67,102 @@ export async function main(argv = process.argv, deps: MainDeps = mainDeps): Prom
|
|
|
65
67
|
// are suppressed for them.
|
|
66
68
|
const isPluginCommand =
|
|
67
69
|
command === "cap" && findPluginCliCommand(subcommand, rest[0]) !== undefined;
|
|
68
|
-
if (!isPluginCommand) {
|
|
69
|
-
const updateStore = new UpdateStateStore();
|
|
70
|
-
scheduleBackgroundCheck(updateStore);
|
|
71
|
-
if (
|
|
72
|
-
!isInstallRecoveryCommand(command, subcommand) &&
|
|
73
|
-
(await enforceUpdateIfRequired(updateStore))
|
|
74
|
-
) {
|
|
75
|
-
process.exitCode = 1;
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
await showUpdateBannerIfAvailable(updateStore);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
82
|
-
console.log(usage());
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
70
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
71
|
+
// Every dispatch case opens with a gate() call declaring what the command
|
|
72
|
+
// needs before it may run. Declared gates run in fixed order — update
|
|
73
|
+
// enforcement, companion selection, install preflight — and a blocked gate
|
|
74
|
+
// sets the exit code, so callers only need `if (!(await gate(...))) return`.
|
|
75
|
+
const gate = async (needs: GateNeeds): Promise<boolean> => {
|
|
76
|
+
if (!isPluginCommand) {
|
|
77
|
+
const updateStore = new UpdateStateStore();
|
|
78
|
+
scheduleBackgroundCheck(updateStore);
|
|
79
|
+
if (needs.updateGuard && (await enforceUpdateIfRequired(updateStore))) {
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
await showUpdateBannerIfAvailable(updateStore);
|
|
84
|
+
}
|
|
90
85
|
|
|
91
|
-
|
|
92
|
-
const preflight = await deps.inspectInstallPreflight();
|
|
93
|
-
if (!preflight.ok) {
|
|
94
|
-
console.error(
|
|
95
|
-
`${frameworkCommandName()}: ${preflight.reason ?? "installation is incomplete"}`,
|
|
96
|
-
);
|
|
97
|
-
console.error(`Run \`${frameworkCommandName()} install\` before continuing.`);
|
|
86
|
+
if (needs.companion && !(await deps.ensureUnambiguousCompanion())) {
|
|
98
87
|
process.exitCode = 1;
|
|
99
|
-
return;
|
|
88
|
+
return false;
|
|
100
89
|
}
|
|
101
|
-
|
|
90
|
+
|
|
91
|
+
if (needs.install) {
|
|
92
|
+
const preflight = await deps.inspectInstallPreflight();
|
|
93
|
+
if (!preflight.ok) {
|
|
94
|
+
console.error(
|
|
95
|
+
`${frameworkCommandName()}: ${preflight.reason ?? "installation is incomplete"}`,
|
|
96
|
+
);
|
|
97
|
+
console.error(`Run \`${frameworkCommandName()} install\` before continuing.`);
|
|
98
|
+
process.exitCode = 1;
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return true;
|
|
104
|
+
};
|
|
102
105
|
|
|
103
106
|
switch (command) {
|
|
104
107
|
case "install":
|
|
108
|
+
// Builds a companion-scoped context, so it needs an unambiguous
|
|
109
|
+
// companion — but must stay runnable when installation is incomplete.
|
|
110
|
+
if (!(await gate({ companion: true }))) return;
|
|
105
111
|
await runInstallCommand(argv.slice(3));
|
|
106
112
|
return;
|
|
107
113
|
case "artifact":
|
|
114
|
+
if (!(await gate({ updateGuard: true, companion: true, install: true }))) return;
|
|
108
115
|
await runArtifactCommand(subcommand, rest);
|
|
109
116
|
return;
|
|
110
117
|
case "companion":
|
|
118
|
+
switch (subcommand) {
|
|
119
|
+
// setup/link are recovery paths and must never be gated on the
|
|
120
|
+
// state they exist to repair.
|
|
121
|
+
case "setup":
|
|
122
|
+
case "link":
|
|
123
|
+
if (!(await gate({}))) return;
|
|
124
|
+
break;
|
|
125
|
+
// open/tui consume a companion context.
|
|
126
|
+
case "open":
|
|
127
|
+
case "tui":
|
|
128
|
+
if (!(await gate({ updateGuard: true, companion: true }))) return;
|
|
129
|
+
break;
|
|
130
|
+
// list and unknown subcommands (which fail inside the command).
|
|
131
|
+
default:
|
|
132
|
+
if (!(await gate({ updateGuard: true }))) return;
|
|
133
|
+
}
|
|
111
134
|
await runCompanionCommand(subcommand, rest);
|
|
112
135
|
return;
|
|
113
136
|
case "claude":
|
|
137
|
+
if (!(await gate({ updateGuard: true, companion: true, install: true }))) return;
|
|
114
138
|
await runLaunchClaudeCommand(argv.slice(3), { directPassthrough: true });
|
|
115
139
|
return;
|
|
116
140
|
case "opencode":
|
|
141
|
+
if (!(await gate({ updateGuard: true, companion: true, install: true }))) return;
|
|
117
142
|
await runLaunchOpenCodeCommand(argv.slice(3), { directPassthrough: true });
|
|
118
143
|
return;
|
|
119
144
|
case "report":
|
|
145
|
+
if (!(await gate({ updateGuard: true }))) return;
|
|
120
146
|
await runReportCommand(argv.slice(3));
|
|
121
147
|
return;
|
|
122
148
|
case "config":
|
|
149
|
+
if (!(await gate({ updateGuard: true }))) return;
|
|
123
150
|
await runConfigCommand(argv.slice(3));
|
|
124
151
|
return;
|
|
125
152
|
case "doctor":
|
|
153
|
+
if (!(await gate({}))) return;
|
|
126
154
|
await runDoctorCommand(argv.slice(3));
|
|
127
155
|
return;
|
|
128
156
|
case "cap":
|
|
157
|
+
if (!(await gate({ updateGuard: true, companion: true, install: true }))) return;
|
|
129
158
|
await runCapCommand(subcommand, rest);
|
|
130
159
|
return;
|
|
131
160
|
case "update":
|
|
161
|
+
if (!(await gate({}))) return;
|
|
132
162
|
await runUpdateCommand(argv.slice(3));
|
|
133
163
|
return;
|
|
134
164
|
default:
|
|
165
|
+
// Unknown commands fail fast: no case matched, so no gate ever ran.
|
|
135
166
|
console.error(`Unknown command: ${command}`);
|
|
136
167
|
console.error(usage());
|
|
137
168
|
process.exitCode = 1;
|
|
@@ -272,4 +272,5 @@ apply:
|
|
|
272
272
|
tracks: tasks.md
|
|
273
273
|
instruction: |
|
|
274
274
|
Read context files and work through pending tasks only within the proposal's affected Areas. Mark complete as you go.
|
|
275
|
+
When reporting information to me, be extremely concise and sacrifice grammar for the sake of concision. Apply this same preference to JSDoc.
|
|
275
276
|
Pause if you hit blockers or need clarification.
|
|
@@ -8,6 +8,7 @@ This block is kept for AGENTS.md compatibility and must not restate that policy.
|
|
|
8
8
|
## Agent Notes
|
|
9
9
|
|
|
10
10
|
- Use absolute paths when a tool needs a file path. Do not create literal `$MATE_REPO_PATH` or `$MATE_ARTIFACT_PATH` directories.
|
|
11
|
+
- When reporting information to me, be extremely concise and sacrifice grammar for the sake of concision. Apply this same preference to JSDoc
|
|
11
12
|
- Never add a `Co-Authored-By: <model>` trailer or model-attribution footer to commit messages.
|
|
12
13
|
- Never commit, push, or open a pull request in the working repo (`$MATE_REPO_PATH`) unless the user explicitly asks for it.
|
|
13
14
|
|
|
@@ -8,6 +8,7 @@ This block is kept for Claude/AGENTS.md compatibility and must not restate that
|
|
|
8
8
|
## Claude Notes
|
|
9
9
|
|
|
10
10
|
- Use absolute paths when a tool needs a file path. Do not create literal `$MATE_REPO_PATH` or `$MATE_ARTIFACT_PATH` directories.
|
|
11
|
+
- When reporting information to me, be extremely concise and sacrifice grammar for the sake of concision. Apply this same preference to JSDoc.
|
|
11
12
|
- Claude Code receives `$MATE_ARTIFACT_PATH` via `--add-dir`; if `@` autocomplete does not show companion artifacts, reference them by absolute path.
|
|
12
13
|
- The root `CLAUDE.md` in the companion repo is intentional and loaded by Mate. Do not create another project-level `CLAUDE.md` in the working repo unless the user explicitly asks for one.
|
|
13
14
|
- Never add a `Co-Authored-By: <model>` trailer or model-attribution footer to commit messages.
|
|
@@ -37,6 +37,3 @@ export function pluginLocalOverridesPath(companionPath: string): string {
|
|
|
37
37
|
|
|
38
38
|
/** Companion-relative gitignore entry for the local override file. */
|
|
39
39
|
export const PLUGIN_LOCAL_OVERRIDES_GITIGNORE_ENTRY = `.${FRAMEWORK_NAME}/config/plugins.local.yaml`;
|
|
40
|
-
|
|
41
|
-
/** Companion-relative gitignore entry for installed plugin trees (regenerated by install). */
|
|
42
|
-
export const PLUGIN_DEPENDENCIES_GITIGNORE_ENTRY = `.${FRAMEWORK_NAME}/dependencies/plugins/`;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { getActiveDistribution } from "../../../distribution";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
PLUGIN_LOCAL_OVERRIDES_GITIGNORE_ENTRY,
|
|
5
|
-
} from "../dynamic-plugins/paths";
|
|
2
|
+
import { FRAMEWORK_NAME } from "../../../framework";
|
|
3
|
+
import { PLUGIN_LOCAL_OVERRIDES_GITIGNORE_ENTRY } from "../dynamic-plugins/paths";
|
|
6
4
|
import type { Plugin, SetupContext } from "../plugin";
|
|
7
5
|
|
|
8
6
|
const MANAGED_START = (name: string) => `# ${name} managed: start`;
|
|
@@ -59,14 +57,22 @@ export async function writeManagedGitignoreBlock(
|
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
export function collectManagedGitignoreEntries(ctx: SetupContext, plugins: Plugin[]): string[] {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
// Baseline for every companion: node_modules never versions wherever a tool
|
|
61
|
+
// materializes it, and everything under the dependencies tree (dynamic
|
|
62
|
+
// plugins, context-mode, future consumers) is regenerated by setup/install —
|
|
63
|
+
// version pins live in .mate/config and in code, never in the tree itself.
|
|
64
|
+
const entries = [
|
|
65
|
+
"node_modules/",
|
|
66
|
+
`.${FRAMEWORK_NAME}/dependencies/`,
|
|
67
|
+
...plugins
|
|
68
|
+
.filter((p) => p.kind !== "root" && (p.isEnabled(ctx.config) || p.persistGitignoreEntries))
|
|
69
|
+
.flatMap((p) => p.gitignoreEntries?.(ctx) ?? []),
|
|
70
|
+
];
|
|
71
|
+
// Dynamic-plugin loading artifacts: the local override file never versions;
|
|
72
|
+
// the pin file (plugins.lock.yaml) stays committed and is deliberately not
|
|
73
|
+
// listed here.
|
|
68
74
|
if (ctx.config.plugins?.length) {
|
|
69
|
-
entries.push(PLUGIN_LOCAL_OVERRIDES_GITIGNORE_ENTRY
|
|
75
|
+
entries.push(PLUGIN_LOCAL_OVERRIDES_GITIGNORE_ENTRY);
|
|
70
76
|
}
|
|
71
77
|
return entries;
|
|
72
78
|
}
|