@spikedpunch/mast 0.3.0 → 0.4.0

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 (81) hide show
  1. package/README.md +180 -9
  2. package/assets/prime.md +18 -0
  3. package/assets/signals.md +51 -0
  4. package/assets/skill.md +6 -47
  5. package/dist/cli/docs-cmd.d.ts +1 -0
  6. package/dist/cli/docs-cmd.d.ts.map +1 -1
  7. package/dist/cli/docs-cmd.js +2 -1
  8. package/dist/cli/docs-cmd.js.map +1 -1
  9. package/dist/cli/hook-cmd.d.ts +3 -0
  10. package/dist/cli/hook-cmd.d.ts.map +1 -0
  11. package/dist/cli/hook-cmd.js +13 -0
  12. package/dist/cli/hook-cmd.js.map +1 -0
  13. package/dist/cli/hook-state-dir.d.ts +24 -0
  14. package/dist/cli/hook-state-dir.d.ts.map +1 -0
  15. package/dist/cli/hook-state-dir.js +57 -0
  16. package/dist/cli/hook-state-dir.js.map +1 -0
  17. package/dist/cli/hook.d.ts +44 -0
  18. package/dist/cli/hook.d.ts.map +1 -0
  19. package/dist/cli/hook.js +200 -0
  20. package/dist/cli/hook.js.map +1 -0
  21. package/dist/cli/index.js +12 -2
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/cli/installed-artifacts.d.ts +23 -0
  24. package/dist/cli/installed-artifacts.d.ts.map +1 -0
  25. package/dist/cli/installed-artifacts.js +39 -0
  26. package/dist/cli/installed-artifacts.js.map +1 -0
  27. package/dist/cli/prime-cmd.d.ts +17 -0
  28. package/dist/cli/prime-cmd.d.ts.map +1 -0
  29. package/dist/cli/prime-cmd.js +70 -0
  30. package/dist/cli/prime-cmd.js.map +1 -0
  31. package/dist/cli/program.d.ts.map +1 -1
  32. package/dist/cli/program.js +6 -0
  33. package/dist/cli/program.js.map +1 -1
  34. package/dist/cli/setup-cmd.d.ts +34 -0
  35. package/dist/cli/setup-cmd.d.ts.map +1 -0
  36. package/dist/cli/setup-cmd.js +205 -0
  37. package/dist/cli/setup-cmd.js.map +1 -0
  38. package/dist/cli/setup-command.d.ts +32 -0
  39. package/dist/cli/setup-command.d.ts.map +1 -0
  40. package/dist/cli/setup-command.js +43 -0
  41. package/dist/cli/setup-command.js.map +1 -0
  42. package/dist/cli/setup-plan.d.ts +52 -0
  43. package/dist/cli/setup-plan.d.ts.map +1 -0
  44. package/dist/cli/setup-plan.js +221 -0
  45. package/dist/cli/setup-plan.js.map +1 -0
  46. package/dist/cli/setup-rules.d.ts +41 -0
  47. package/dist/cli/setup-rules.d.ts.map +1 -0
  48. package/dist/cli/setup-rules.js +76 -0
  49. package/dist/cli/setup-rules.js.map +1 -0
  50. package/dist/cli/setup-static.d.ts +13 -0
  51. package/dist/cli/setup-static.d.ts.map +1 -0
  52. package/dist/cli/setup-static.js +116 -0
  53. package/dist/cli/setup-static.js.map +1 -0
  54. package/dist/cli/skill-install.d.ts +2 -0
  55. package/dist/cli/skill-install.d.ts.map +1 -1
  56. package/dist/cli/skill-install.js +2 -0
  57. package/dist/cli/skill-install.js.map +1 -1
  58. package/dist/cli/upgrade-cmd.d.ts +5 -1
  59. package/dist/cli/upgrade-cmd.d.ts.map +1 -1
  60. package/dist/cli/upgrade-cmd.js +28 -3
  61. package/dist/cli/upgrade-cmd.js.map +1 -1
  62. package/dist/indexer/watcher.d.ts +80 -2
  63. package/dist/indexer/watcher.d.ts.map +1 -1
  64. package/dist/indexer/watcher.js +172 -27
  65. package/dist/indexer/watcher.js.map +1 -1
  66. package/dist/mcp/instructions.d.ts +7 -0
  67. package/dist/mcp/instructions.d.ts.map +1 -0
  68. package/dist/mcp/instructions.js +10 -0
  69. package/dist/mcp/instructions.js.map +1 -0
  70. package/dist/mcp/server.d.ts +8 -0
  71. package/dist/mcp/server.d.ts.map +1 -1
  72. package/dist/mcp/server.js +17 -4
  73. package/dist/mcp/server.js.map +1 -1
  74. package/dist/store/config.d.ts.map +1 -1
  75. package/dist/store/config.js +4 -5
  76. package/dist/store/config.js.map +1 -1
  77. package/dist/store/defaults.d.ts +9 -0
  78. package/dist/store/defaults.d.ts.map +1 -0
  79. package/dist/store/defaults.js +12 -0
  80. package/dist/store/defaults.js.map +1 -0
  81. package/package.json +2 -1
@@ -0,0 +1,200 @@
1
+ // Entry module for `mast hook`. Run by the harness before every Grep, so everything
2
+ // reachable through its STATIC imports must be Node built-ins or repo modules that are
3
+ // themselves built-in-only (asserted by hook-import-graph.test.ts, ADR 017 section 5).
4
+ // The session-start branch loads the heavy modules with a dynamic import() instead.
5
+ import { existsSync, readFileSync, statSync } from 'node:fs';
6
+ import { join, resolve } from 'node:path';
7
+ import { resolveHookConfigLight } from './hook-state-dir.js';
8
+ export const SEARCH_REMINDER = 'mast: for TypeScript, JavaScript and Markdown code, try mast_search first (use code tokens such as function and type names); ' +
9
+ 'grep is still right for other languages and for exact-text regex.';
10
+ // Ripgrep type names (what Claude Code's Grep `type` takes) mapped to the extensions
11
+ // they cover. Hand-kept and deliberately small: only the types whose name differs from
12
+ // their extensions. Any other type name is read as its own extension (`py` as `.py`,
13
+ // `vue` as `.vue`), so a project that indexes extra extensions is still matched.
14
+ const RG_TYPE_EXTENSIONS = {
15
+ ts: ['.ts', '.tsx', '.cts', '.mts'],
16
+ js: ['.js', '.jsx', '.cjs', '.mjs'],
17
+ md: ['.md', '.markdown'],
18
+ markdown: ['.md', '.markdown'],
19
+ };
20
+ function isRecord(value) {
21
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
22
+ }
23
+ function isHarness(value) {
24
+ return value === 'claude' || value === 'cursor' || value === 'vscode';
25
+ }
26
+ function isHookEvent(value) {
27
+ return value === 'session-start' || value === 'search';
28
+ }
29
+ function anyIn(indexed, extensions) {
30
+ return extensions.some((e) => indexed.includes(e.toLowerCase()));
31
+ }
32
+ /** A trailing `.ext` of a path segment. All-digit suffixes (`v1.2`) are versions, not extensions. */
33
+ function trailingExtension(segment) {
34
+ const m = /\.([A-Za-z0-9_]*[A-Za-z_][A-Za-z0-9_]*)$/.exec(segment);
35
+ return m?.[1] === undefined ? null : `.${m[1]}`;
36
+ }
37
+ /** Extensions a glob can match, or null when they cannot be determined (`src/**`, `*.[jt]s`). */
38
+ function globExtensions(glob) {
39
+ const segment = glob.slice(glob.lastIndexOf('/') + 1);
40
+ const braces = /\.\{([^{}]*)\}$/.exec(segment);
41
+ if (braces?.[1] !== undefined) {
42
+ const items = braces[1].split(',').map((s) => s.trim());
43
+ return items.every((s) => /^[A-Za-z0-9_]+$/.test(s)) ? items.map((s) => `.${s}`) : null;
44
+ }
45
+ const single = trailingExtension(segment);
46
+ return single === null ? null : [single];
47
+ }
48
+ function stringField(input, key) {
49
+ const value = input[key];
50
+ return typeof value === 'string' ? value : null;
51
+ }
52
+ /**
53
+ * True when the search's own scoping fields restrict it to files mast does not index.
54
+ * Unrecognised fields and shapes count as "not scoped": a missed reminder is cheaper
55
+ * than a wrong one only when we know the language, and here we do not.
56
+ */
57
+ function isScopedAwayFromIndex(input, facts) {
58
+ if (!isRecord(input) || !isRecord(input['tool_input']))
59
+ return false;
60
+ const toolInput = input['tool_input'];
61
+ const indexed = facts.indexedExtensions;
62
+ const type = stringField(toolInput, 'type');
63
+ if (type !== null && !anyIn(indexed, RG_TYPE_EXTENSIONS[type] ?? [`.${type}`]))
64
+ return true;
65
+ // For the Glob tool the `pattern` IS the glob; for Grep it is a regex and says nothing.
66
+ const glob = input['tool_name'] === 'Glob' ? stringField(toolInput, 'pattern') : stringField(toolInput, 'glob');
67
+ const globExts = glob === null ? null : globExtensions(glob);
68
+ if (globExts !== null && !anyIn(indexed, globExts))
69
+ return true;
70
+ const path = facts.searchPathIsDirectory ? null : stringField(toolInput, 'path');
71
+ const pathExt = path === null ? null : trailingExtension(path.slice(path.lastIndexOf('/') + 1));
72
+ if (pathExt !== null && !anyIn(indexed, [pathExt]))
73
+ return true;
74
+ return false;
75
+ }
76
+ function envelope(harness, event, text) {
77
+ if (harness === 'cursor')
78
+ return { additional_context: text };
79
+ return {
80
+ hookSpecificOutput: {
81
+ hookEventName: event === 'session-start' ? 'SessionStart' : 'PreToolUse',
82
+ additionalContext: text,
83
+ },
84
+ };
85
+ }
86
+ /**
87
+ * The whole decision, pure. Returns the harness's envelope, or null for "print nothing".
88
+ * Unknown harness or event is null rather than a throw: see `runHook` for why.
89
+ */
90
+ export function decide(harness, event, input, facts) {
91
+ if (!isHarness(harness) || !isHookEvent(event))
92
+ return null;
93
+ if (event === 'session-start')
94
+ return envelope(harness, event, facts.primeText);
95
+ if (!facts.indexExists || isScopedAwayFromIndex(input, facts))
96
+ return null;
97
+ return envelope(harness, event, SEARCH_REMINDER);
98
+ }
99
+ /** `cwd`, else the first workspace root, else null (caller falls back to process cwd). */
100
+ function projectRootOf(input) {
101
+ if (typeof input['cwd'] === 'string' && input['cwd'] !== '')
102
+ return input['cwd'];
103
+ const roots = input['workspace_roots'];
104
+ if (Array.isArray(roots) && typeof roots[0] === 'string' && roots[0] !== '')
105
+ return roots[0];
106
+ return null;
107
+ }
108
+ function searchPathOf(input) {
109
+ const toolInput = input['tool_input'];
110
+ return isRecord(toolInput) ? stringField(toolInput, 'path') : null;
111
+ }
112
+ /**
113
+ * I/O shell around `decide`.
114
+ *
115
+ * Never throws and never sets a failing exit code. A hook that fails breaks the user's
116
+ * session, and anything on stdout that is not the envelope is a malformed hook response,
117
+ * so every problem here ends as empty stdout plus one stderr line. Ordinary quiet cases
118
+ * (no index, a search scoped to another language) say nothing at all.
119
+ */
120
+ export async function runHook(harness, event, io) {
121
+ if (!isHarness(harness) || !isHookEvent(event)) {
122
+ io.warn(`mast hook: unknown harness "${harness}" or event "${event}"; expected claude|cursor|vscode and session-start|search`);
123
+ return;
124
+ }
125
+ try {
126
+ const raw = await io.readStdin();
127
+ if (raw.trim() === '') {
128
+ io.warn('mast hook: empty stdin, expected the harness hook JSON');
129
+ return;
130
+ }
131
+ let input;
132
+ try {
133
+ input = JSON.parse(raw);
134
+ }
135
+ catch {
136
+ io.warn('mast hook: stdin is not valid JSON');
137
+ return;
138
+ }
139
+ if (!isRecord(input)) {
140
+ io.warn('mast hook: stdin JSON is not an object');
141
+ return;
142
+ }
143
+ const projectRoot = projectRootOf(input) ?? io.cwd();
144
+ const out = decide(harness, event, input, await gatherFacts(event, input, projectRoot, io));
145
+ if (out !== null)
146
+ io.write(JSON.stringify(out));
147
+ }
148
+ catch (error) {
149
+ io.warn(`mast hook ${harness} ${event}: ${error instanceof Error ? error.message : String(error)}`);
150
+ }
151
+ }
152
+ async function gatherFacts(event, input, projectRoot, io) {
153
+ if (event === 'session-start') {
154
+ return {
155
+ indexExists: false,
156
+ primeText: await io.prime(projectRoot),
157
+ searchPathIsDirectory: false,
158
+ indexedExtensions: [],
159
+ };
160
+ }
161
+ const config = resolveHookConfigLight(projectRoot, io.env);
162
+ const searchPath = searchPathOf(input);
163
+ return {
164
+ indexExists: io.fileExists(join(config.stateDir, 'index.json')),
165
+ primeText: '',
166
+ searchPathIsDirectory: searchPath !== null && io.isDirectory(resolve(projectRoot, searchPath)),
167
+ indexedExtensions: config.fileExtensions.map((e) => e.toLowerCase()),
168
+ };
169
+ }
170
+ async function readProcessStdin() {
171
+ // A TTY means someone ran the command by hand; waiting for EOF would just hang.
172
+ if (process.stdin.isTTY)
173
+ return '';
174
+ // Streamed rather than readFileSync(0): that throws EAGAIN on some non-blocking pipes.
175
+ process.stdin.setEncoding('utf8');
176
+ let text = '';
177
+ for await (const chunk of process.stdin)
178
+ text += String(chunk);
179
+ return text;
180
+ }
181
+ /** Production wiring, called from `cli/index.ts` and from the commander registration. */
182
+ export async function runHookFromProcess(harness, event) {
183
+ await runHook(harness, event, {
184
+ readStdin: readProcessStdin,
185
+ write: (text) => { process.stdout.write(text); },
186
+ warn: (line) => { process.stderr.write(`${line}\n`); },
187
+ env: process.env,
188
+ cwd: () => process.cwd(),
189
+ fileExists: existsSync,
190
+ isDirectory: (path) => statSync(path, { throwIfNoEntry: false })?.isDirectory() ?? false,
191
+ prime: async (projectRoot) => {
192
+ // Dynamic so the search path never pays for them (ADR 017 section 5).
193
+ const { buildStatus } = await import('./status.js');
194
+ const { renderPrime, PRIME_ASSET_PATH } = await import('./prime-cmd.js');
195
+ const status = await buildStatus({ path: projectRoot });
196
+ return renderPrime(status, readFileSync(PRIME_ASSET_PATH, 'utf8'), new Date());
197
+ },
198
+ });
199
+ }
200
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/cli/hook.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -1,4 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { buildProgram } from './program.js';
3
- await buildProgram().parseAsync(process.argv);
2
+ // `hook` is dispatched before the program is imported: a hook runs ahead of every Grep,
3
+ // and importing the program (commander, zod, typescript, kysely, the MCP SDK) costs about
4
+ // a second. See ADR 017 section 5 and hook-import-graph.test.ts.
5
+ if (process.argv[2] === 'hook') {
6
+ const { runHookFromProcess } = await import('./hook.js');
7
+ await runHookFromProcess(process.argv[3] ?? '', process.argv[4] ?? '');
8
+ }
9
+ else {
10
+ const { buildProgram } = await import('./program.js');
11
+ await buildProgram().parseAsync(process.argv);
12
+ }
13
+ export {};
4
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,YAAY,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,wFAAwF;AACxF,0FAA0F;AAC1F,iEAAiE;AACjE,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;IAC/B,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACzD,MAAM,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACzE,CAAC;KAAM,CAAC;IACN,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;IACtD,MAAM,YAAY,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { Harness } from './hook.js';
2
+ /**
3
+ * What of mast's own writing `mast upgrade` found installed, so it can say what to re-check
4
+ * after upgrading. `mast upgrade` runs on the OLD binary and cannot know what a newer
5
+ * version's files should contain; it can only know that they exist.
6
+ */
7
+ export interface InstalledArtifacts {
8
+ /** Harnesses whose project-level hook file holds a mast hook command. */
9
+ readonly projectHooks: readonly Harness[];
10
+ /** Harnesses whose user-level hook file holds a mast hook command. */
11
+ readonly globalHooks: readonly Harness[];
12
+ /** Harnesses with a mast-owned rules file in the project. */
13
+ readonly rulesHarnesses: readonly ('cursor' | 'windsurf')[];
14
+ /** Zed rules files, outside the `mast skill --install` set, that carry the marked block. */
15
+ readonly zedOnlyBlockFiles: readonly string[];
16
+ /** Files `mast skill --install` reaches that carry the marked block. */
17
+ readonly skillBlockFiles: readonly string[];
18
+ }
19
+ export declare const NO_INSTALLED_ARTIFACTS: InstalledArtifacts;
20
+ /** Null for a file that is absent or unreadable: an advisory scan cannot fail `mast upgrade`. */
21
+ export declare function readFileOrNull(path: string): string | null;
22
+ export declare function findInstalledArtifacts(projectRoot: string, home: string, readFile: (path: string) => string | null): InstalledArtifacts;
23
+ //# sourceMappingURL=installed-artifacts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installed-artifacts.d.ts","sourceRoot":"","sources":["../../src/cli/installed-artifacts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,SAAS,OAAO,EAAE,CAAC;IAC1C,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,SAAS,OAAO,EAAE,CAAC;IACzC,6DAA6D;IAC7D,QAAQ,CAAC,cAAc,EAAE,SAAS,CAAC,QAAQ,GAAG,UAAU,CAAC,EAAE,CAAC;IAC5D,4FAA4F;IAC5F,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C,wEAAwE;IACxE,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED,eAAO,MAAM,sBAAsB,EAAE,kBAEpC,CAAC;AAIF,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM1D;AAOD,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GACxC,kBAAkB,CAiBpB"}
@@ -0,0 +1,39 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { hookFilePath } from './setup-command.js';
4
+ import { cursorRulesPath, windsurfRulesPaths, ZED_RULES_FILES } from './setup-rules.js';
5
+ import { BEGIN_MARKER, SKILL_TARGET_FILES } from './skill-install.js';
6
+ export const NO_INSTALLED_ARTIFACTS = {
7
+ projectHooks: [], globalHooks: [], rulesHarnesses: [], zedOnlyBlockFiles: [], skillBlockFiles: [],
8
+ };
9
+ const HOOK_HARNESSES = ['claude', 'cursor', 'vscode'];
10
+ /** Null for a file that is absent or unreadable: an advisory scan cannot fail `mast upgrade`. */
11
+ export function readFileOrNull(path) {
12
+ try {
13
+ return readFileSync(path, 'utf8');
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ // A plain substring test on the raw text, not a JSON parse: a file that does not parse is
20
+ // still worth a `--check`, which will report the parse problem itself.
21
+ const hasMastHook = (text, harness) => text !== null && (text.includes(` hook ${harness} session-start`) || text.includes(` hook ${harness} search`));
22
+ export function findInstalledArtifacts(projectRoot, home, readFile) {
23
+ const hooksIn = (scope) => HOOK_HARNESSES.filter((h) => hasMastHook(readFile(hookFilePath(h, scope, projectRoot, home)), h));
24
+ const present = (path) => readFile(path) !== null;
25
+ const carriesBlock = (rel) => readFile(join(projectRoot, rel))?.includes(BEGIN_MARKER) === true;
26
+ const rulesHarnesses = [];
27
+ if (present(cursorRulesPath(projectRoot)))
28
+ rulesHarnesses.push('cursor');
29
+ if (windsurfRulesPaths(projectRoot).some(present))
30
+ rulesHarnesses.push('windsurf');
31
+ return {
32
+ projectHooks: hooksIn('project'),
33
+ globalHooks: hooksIn('global'),
34
+ rulesHarnesses,
35
+ zedOnlyBlockFiles: ZED_RULES_FILES.filter((f) => !SKILL_TARGET_FILES.includes(f) && carriesBlock(f)),
36
+ skillBlockFiles: SKILL_TARGET_FILES.filter(carriesBlock),
37
+ };
38
+ }
39
+ //# sourceMappingURL=installed-artifacts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installed-artifacts.js","sourceRoot":"","sources":["../../src/cli/installed-artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAoBtE,MAAM,CAAC,MAAM,sBAAsB,GAAuB;IACxD,YAAY,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE;CAClG,CAAC;AAEF,MAAM,cAAc,GAAuB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAE1E,iGAAiG;AACjG,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,0FAA0F;AAC1F,uEAAuE;AACvE,MAAM,WAAW,GAAG,CAAC,IAAmB,EAAE,OAAgB,EAAW,EAAE,CACrE,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,OAAO,gBAAgB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,OAAO,SAAS,CAAC,CAAC,CAAC;AAEjH,MAAM,UAAU,sBAAsB,CACpC,WAAmB,EACnB,IAAY,EACZ,QAAyC;IAEzC,MAAM,OAAO,GAAG,CAAC,KAA2B,EAAa,EAAE,CACzD,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpG,MAAM,OAAO,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IACnE,MAAM,YAAY,GAAG,CAAC,GAAW,EAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IAEjH,MAAM,cAAc,GAA8B,EAAE,CAAC;IACrD,IAAI,OAAO,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;QAAE,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzE,IAAI,kBAAkB,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAEnF,OAAO;QACL,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC;QAChC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC;QAC9B,cAAc;QACd,iBAAiB,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;QACpG,eAAe,EAAE,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC;KACzD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Command } from 'commander';
2
+ import { type StatusReport } from './status.js';
3
+ /** Absolute path of the shipped rules text; `assets` is in the package's `files`. */
4
+ export declare const PRIME_ASSET_PATH: string;
5
+ /**
6
+ * Renders the session primer for one of four index states.
7
+ *
8
+ * The rules are printed only when the index can be trusted to answer (fresh, or
9
+ * stale with a reindex instruction). Telling a model to use an index that does not
10
+ * exist, or one that describes a different tree, teaches it to ignore the primer,
11
+ * so those two states say what is wrong instead.
12
+ *
13
+ * Pure: `now` is a parameter so the age line is deterministic.
14
+ */
15
+ export declare function renderPrime(status: StatusReport, rules: string, now: Date): string;
16
+ export declare function registerPrimeCommand(program: Command): void;
17
+ //# sourceMappingURL=prime-cmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prime-cmd.d.ts","sourceRoot":"","sources":["../../src/cli/prime-cmd.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAG7D,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,QAA2C,CAAC;AAEzE;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,MAAM,CAgClF;AAWD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAa3D"}
@@ -0,0 +1,70 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { buildStatus } from './status.js';
4
+ import { PACKAGE_ROOT } from './docs-cmd.js';
5
+ /** Absolute path of the shipped rules text; `assets` is in the package's `files`. */
6
+ export const PRIME_ASSET_PATH = join(PACKAGE_ROOT, 'assets', 'prime.md');
7
+ /**
8
+ * Renders the session primer for one of four index states.
9
+ *
10
+ * The rules are printed only when the index can be trusted to answer (fresh, or
11
+ * stale with a reindex instruction). Telling a model to use an index that does not
12
+ * exist, or one that describes a different tree, teaches it to ignore the primer,
13
+ * so those two states say what is wrong instead.
14
+ *
15
+ * Pure: `now` is a parameter so the age line is deterministic.
16
+ */
17
+ export function renderPrime(status, rules, now) {
18
+ if (!status.initialised) {
19
+ return [
20
+ `mast has no index at ${status.state_dir}.`,
21
+ 'Use ordinary search tools (grep, glob, reading files) here.',
22
+ 'Run `mast init` in the project to create an index.',
23
+ ].join('\n') + '\n';
24
+ }
25
+ if (status.freshness_cause === 'root_mismatch') {
26
+ return [
27
+ `The mast index at ${status.state_dir} describes a different tree than ${status.project_root}.`,
28
+ 'mast results here cannot be trusted, and reindexing will not fix it.',
29
+ 'Use ordinary search tools, and check the project path and --state-dir.',
30
+ ].join('\n') + '\n';
31
+ }
32
+ const health = [
33
+ `mast index: ${String(status.indexed_files ?? 0)} files, last indexed ${describeAge(status.last_indexed, now)}.`,
34
+ ];
35
+ if (!status.index_fresh) {
36
+ const b = status.stale_breakdown;
37
+ const split = b === null
38
+ ? ''
39
+ : ` (changed ${String(b.changed)}, unindexed ${String(b.unindexed)}, deleted ${String(b.deleted)})`;
40
+ health.push(`The index is stale: ${String(status.stale_files ?? 0)} files differ from disk${split}.`, 'Call mast_reindex before relying on mast results.');
41
+ }
42
+ return rules.trimEnd() + '\n\n' + health.join('\n') + '\n';
43
+ }
44
+ function describeAge(lastIndexed, now) {
45
+ if (lastIndexed === null)
46
+ return 'never';
47
+ const seconds = Math.max(0, Math.floor((now.getTime() - new Date(lastIndexed).getTime()) / 1_000));
48
+ if (seconds < 60)
49
+ return `${seconds}s ago`;
50
+ if (seconds < 3_600)
51
+ return `${Math.floor(seconds / 60)}m ago`;
52
+ if (seconds < 86_400)
53
+ return `${Math.floor(seconds / 3_600)}h ago`;
54
+ return `${Math.floor(seconds / 86_400)}d ago`;
55
+ }
56
+ export function registerPrimeCommand(program) {
57
+ program
58
+ .command('prime [path]')
59
+ .description('Print the session primer: how to use mast here, and the live index health')
60
+ .option('--state-dir <dir>', 'State directory')
61
+ .action(async (projectPath, opts) => {
62
+ // Exit 0 in every state, unlike `mast status`: this runs from a session-start
63
+ // hook, where a non-zero exit is a failure the user sees. "No index" is a
64
+ // state the primer reports in its text, not an error of the command.
65
+ const status = await buildStatus({ path: projectPath, stateDir: opts.stateDir });
66
+ const rules = readFileSync(PRIME_ASSET_PATH, 'utf8');
67
+ process.stdout.write(renderPrime(status, rules, new Date()));
68
+ });
69
+ }
70
+ //# sourceMappingURL=prime-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prime-cmd.js","sourceRoot":"","sources":["../../src/cli/prime-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAqB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,qFAAqF;AACrF,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;AAEzE;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,MAAoB,EAAE,KAAa,EAAE,GAAS;IACxE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACxB,OAAO;YACL,wBAAwB,MAAM,CAAC,SAAS,GAAG;YAC3C,6DAA6D;YAC7D,oDAAoD;SACrD,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,IAAI,MAAM,CAAC,eAAe,KAAK,eAAe,EAAE,CAAC;QAC/C,OAAO;YACL,qBAAqB,MAAM,CAAC,SAAS,oCAAoC,MAAM,CAAC,YAAY,GAAG;YAC/F,sEAAsE;YACtE,wEAAwE;SACzE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,MAAM,MAAM,GAAG;QACb,eAAe,MAAM,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC,wBAAwB,WAAW,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,GAAG;KACjH,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,MAAM,CAAC,eAAe,CAAC;QACjC,MAAM,KAAK,GAAG,CAAC,KAAK,IAAI;YACtB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,aAAa,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;QACtG,MAAM,CAAC,IAAI,CACT,uBAAuB,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC,0BAA0B,KAAK,GAAG,EACxF,mDAAmD,CACpD,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC,OAAO,EAAE,GAAG,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAC7D,CAAC;AAED,SAAS,WAAW,CAAC,WAA0B,EAAE,GAAS;IACxD,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IACnG,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,OAAO,CAAC;IAC3C,IAAI,OAAO,GAAG,KAAK;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC;IAC/D,IAAI,OAAO,GAAG,MAAM;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IACnE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,cAAc,CAAC;SACvB,WAAW,CAAC,2EAA2E,CAAC;SACxF,MAAM,CAAC,mBAAmB,EAAE,iBAAiB,CAAC;SAC9C,MAAM,CAAC,KAAK,EAAE,WAA+B,EAAE,IAA2B,EAAE,EAAE;QAC7E,8EAA8E;QAC9E,0EAA0E;QAC1E,qEAAqE;QACrE,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjF,MAAM,KAAK,GAAG,YAAY,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;QACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAmBtC;AAED,0FAA0F;AAC1F,wBAAgB,sBAAsB,IAAI,SAAS,MAAM,EAAE,CAE1D"}
1
+ {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAsBtC;AAED,0FAA0F;AAC1F,wBAAgB,sBAAsB,IAAI,SAAS,MAAM,EAAE,CAE1D"}
@@ -10,6 +10,9 @@ import { registerMetricsCommand } from './metrics-cmd.js';
10
10
  import { registerQueryCommand } from './query.js';
11
11
  import { registerDocsCommand, registerSkillCommand } from './docs-cmd.js';
12
12
  import { registerUpgradeCommand } from './upgrade-cmd.js';
13
+ import { registerPrimeCommand } from './prime-cmd.js';
14
+ import { registerHookCommand } from './hook-cmd.js';
15
+ import { registerSetupCommand } from './setup-cmd.js';
13
16
  /**
14
17
  * Builds the CLI. This is the *only* place commands are registered.
15
18
  *
@@ -34,6 +37,9 @@ export function buildProgram() {
34
37
  registerQueryCommand(program);
35
38
  registerDocsCommand(program);
36
39
  registerSkillCommand(program);
40
+ registerPrimeCommand(program);
41
+ registerHookCommand(program);
42
+ registerSetupCommand(program);
37
43
  registerUpgradeCommand(program);
38
44
  return program;
39
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"program.js","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;SAC1B,IAAI,CAAC,MAAM,CAAC;SACZ,WAAW,CAAC,+FAA+F,CAAC;SAC5G,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,2BAA2B,CAAC,OAAO,CAAC,CAAC;IACrC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAEhC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,sBAAsB;IACpC,OAAO,YAAY,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;SAC1B,IAAI,CAAC,MAAM,CAAC;SACZ,WAAW,CAAC,+FAA+F,CAAC;SAC5G,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,2BAA2B,CAAC,OAAO,CAAC,CAAC;IACrC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAEhC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,sBAAsB;IACpC,OAAO,YAAY,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { Command } from 'commander';
2
+ import { type InstallKind } from './upgrade-cmd.js';
3
+ export interface SetupOptions {
4
+ readonly harness: string;
5
+ readonly projectRoot: string;
6
+ readonly global: boolean;
7
+ readonly check: boolean;
8
+ readonly remove: boolean;
9
+ readonly dryRun: boolean;
10
+ }
11
+ /** Everything about the machine `setup` reads, injected so tests never touch the real home. */
12
+ export interface SetupEnv {
13
+ readonly installKind: InstallKind;
14
+ readonly home: string;
15
+ readonly cliEntry: string;
16
+ /** The text of `assets/skill.md`, which every rules file is rendered from. */
17
+ readonly skillText: string;
18
+ }
19
+ export interface SetupIo {
20
+ /** Null when the file does not exist. Throws on any other failure. */
21
+ readFile(path: string): string | null;
22
+ /** Creates parent directories. Must never leave a half-written file at `path`. */
23
+ writeFileAtomic(path: string, content: string): void;
24
+ removeFile(path: string): void;
25
+ fileExists(path: string): boolean;
26
+ directoryExists(path: string): boolean;
27
+ out(line: string): void;
28
+ err(line: string): void;
29
+ }
30
+ /** Returns the process exit code: 0 done, 1 refused or failed, 2 usage error. */
31
+ export declare function runSetup(opts: SetupOptions, env: SetupEnv, io: SetupIo): number;
32
+ export declare function createNodeSetupIo(): SetupIo;
33
+ export declare function registerSetupCommand(program: Command): void;
34
+ //# sourceMappingURL=setup-cmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-cmd.d.ts","sourceRoot":"","sources":["../../src/cli/setup-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAcvE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,+FAA+F;AAC/F,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,OAAO;IACtB,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACtC,kFAAkF;IAClF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAyDD,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,GAAG,MAAM,CAoB/E;AAsED,wBAAgB,iBAAiB,IAAI,OAAO,CAoB3C;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkB3D"}