@moda-ai/cli 1.22.1 → 1.23.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 (118) hide show
  1. package/dist/cli-c4se7a5d.js +762 -0
  2. package/dist/cli-mnqa4rp6.js +1563 -0
  3. package/dist/cli-stmjdgvy.js +2842 -0
  4. package/dist/cli-y55bgg14.js +6980 -0
  5. package/dist/cli-ysegdrm5.js +286 -0
  6. package/dist/cli.js +4779 -1159
  7. package/dist/harness-github-actions-7t3cncnt.js +95 -0
  8. package/dist/index-4ser01zr.js +2769 -0
  9. package/dist/provision-hm1ppwj6.js +74 -0
  10. package/package.json +3 -2
  11. package/skills/integration/index.json +2 -2
  12. package/dist/agent-schemas.d.ts +0 -3
  13. package/dist/agent-schemas.js +0 -358
  14. package/dist/agent.d.ts +0 -5
  15. package/dist/agent.js +0 -477
  16. package/dist/analyst-tui.d.ts +0 -81
  17. package/dist/analyst-tui.js +0 -314
  18. package/dist/api-client.d.ts +0 -46
  19. package/dist/api-client.js +0 -120
  20. package/dist/ask-stream.d.ts +0 -146
  21. package/dist/ask-stream.js +0 -473
  22. package/dist/auth-storage.d.ts +0 -35
  23. package/dist/auth-storage.js +0 -159
  24. package/dist/auth.d.ts +0 -50
  25. package/dist/auth.js +0 -694
  26. package/dist/cli-session.d.ts +0 -26
  27. package/dist/cli-session.js +0 -169
  28. package/dist/cli.d.ts +0 -44
  29. package/dist/command-context.d.ts +0 -62
  30. package/dist/command-context.js +0 -57
  31. package/dist/command-registry.d.ts +0 -22
  32. package/dist/command-registry.js +0 -15
  33. package/dist/config.d.ts +0 -60
  34. package/dist/config.js +0 -110
  35. package/dist/doctor.d.ts +0 -79
  36. package/dist/doctor.js +0 -1182
  37. package/dist/errors.d.ts +0 -50
  38. package/dist/errors.js +0 -64
  39. package/dist/exit-funnel.d.ts +0 -14
  40. package/dist/exit-funnel.js +0 -53
  41. package/dist/harness-artifact-bodies.d.ts +0 -28
  42. package/dist/harness-artifact-bodies.js +0 -276
  43. package/dist/harness-candidates.d.ts +0 -77
  44. package/dist/harness-candidates.js +0 -482
  45. package/dist/harness-remote.d.ts +0 -135
  46. package/dist/harness-remote.js +0 -540
  47. package/dist/harness-report.d.ts +0 -145
  48. package/dist/harness-report.js +0 -1579
  49. package/dist/harness-sync-limits.d.ts +0 -8
  50. package/dist/harness-sync-limits.js +0 -21
  51. package/dist/harness.d.ts +0 -463
  52. package/dist/harness.js +0 -4009
  53. package/dist/init/index.d.ts +0 -78
  54. package/dist/init/index.js +0 -1442
  55. package/dist/init/mcp.d.ts +0 -2
  56. package/dist/init/mcp.js +0 -27
  57. package/dist/init/progress-board.d.ts +0 -80
  58. package/dist/init/progress-board.js +0 -225
  59. package/dist/init/rules.d.ts +0 -6
  60. package/dist/init/rules.js +0 -171
  61. package/dist/init/sdk-catalog.d.ts +0 -10
  62. package/dist/init/sdk-catalog.js +0 -80
  63. package/dist/init/sdk-guide.d.ts +0 -19
  64. package/dist/init/sdk-guide.js +0 -185
  65. package/dist/init/sdk-integration.d.ts +0 -53
  66. package/dist/init/sdk-integration.js +0 -835
  67. package/dist/init/sdk-protocol.d.ts +0 -135
  68. package/dist/init/sdk-protocol.js +0 -152
  69. package/dist/init/sdk-verification.d.ts +0 -27
  70. package/dist/init/sdk-verification.js +0 -123
  71. package/dist/init/tenant.d.ts +0 -67
  72. package/dist/init/tenant.js +0 -421
  73. package/dist/intelligence-schema.d.ts +0 -349
  74. package/dist/intelligence-schema.js +0 -174
  75. package/dist/keys.d.ts +0 -47
  76. package/dist/keys.js +0 -136
  77. package/dist/manifest.d.ts +0 -5
  78. package/dist/manifest.js +0 -114
  79. package/dist/markdown-terminal.d.ts +0 -49
  80. package/dist/markdown-terminal.js +0 -212
  81. package/dist/output.d.ts +0 -191
  82. package/dist/output.js +0 -624
  83. package/dist/production-intelligence.d.ts +0 -175
  84. package/dist/production-intelligence.js +0 -1430
  85. package/dist/profile-commands.d.ts +0 -10
  86. package/dist/profile-commands.js +0 -342
  87. package/dist/profiles.d.ts +0 -112
  88. package/dist/profiles.js +0 -573
  89. package/dist/prompts-ab.d.ts +0 -71
  90. package/dist/prompts-ab.js +0 -313
  91. package/dist/prompts-propose.d.ts +0 -15
  92. package/dist/prompts-propose.js +0 -212
  93. package/dist/prompts.d.ts +0 -58
  94. package/dist/prompts.js +0 -418
  95. package/dist/provision.d.ts +0 -28
  96. package/dist/provision.js +0 -104
  97. package/dist/redact.d.ts +0 -2
  98. package/dist/redact.js +0 -90
  99. package/dist/schemas.d.ts +0 -225
  100. package/dist/schemas.js +0 -126
  101. package/dist/skills-bundled.d.ts +0 -35
  102. package/dist/skills-bundled.js +0 -157
  103. package/dist/skills-manifest.d.ts +0 -33
  104. package/dist/skills-manifest.js +0 -244
  105. package/dist/skills.d.ts +0 -57
  106. package/dist/skills.js +0 -783
  107. package/dist/sync.d.ts +0 -5
  108. package/dist/sync.js +0 -36
  109. package/dist/telemetry.d.ts +0 -56
  110. package/dist/telemetry.js +0 -167
  111. package/dist/transforms.d.ts +0 -25
  112. package/dist/transforms.js +0 -102
  113. package/dist/tui.d.ts +0 -48
  114. package/dist/tui.js +0 -393
  115. package/dist/update-check.d.ts +0 -31
  116. package/dist/update-check.js +0 -141
  117. package/dist/workspace.d.ts +0 -12
  118. package/dist/workspace.js +0 -1294
@@ -1,2 +0,0 @@
1
- export declare function detectAgents(): string[];
2
- //# sourceMappingURL=mcp.d.ts.map
package/dist/init/mcp.js DELETED
@@ -1,27 +0,0 @@
1
- // src/init/mcp.ts — Coding agent detection
2
- import { existsSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { execSync } from 'node:child_process';
5
- function isOnPath(cmd) {
6
- try {
7
- execSync(`which ${cmd}`, { stdio: 'pipe' });
8
- return true;
9
- }
10
- catch {
11
- return false;
12
- }
13
- }
14
- export function detectAgents() {
15
- const cwd = process.cwd();
16
- const agents = [];
17
- // Claude Code
18
- if (existsSync(join(cwd, '.claude')) || isOnPath('claude')) {
19
- agents.push('Claude Code');
20
- }
21
- // Cursor
22
- if (existsSync(join(cwd, '.cursor'))) {
23
- agents.push('Cursor');
24
- }
25
- return agents;
26
- }
27
- //# sourceMappingURL=mcp.js.map
@@ -1,80 +0,0 @@
1
- export type ProgressLaneStatus = 'running' | 'done' | 'warning' | 'failed';
2
- export interface ProgressLane {
3
- id: string;
4
- title: string;
5
- status: ProgressLaneStatus;
6
- /** Latest one-line detail while running; frozen summary note when terminal. */
7
- detail?: string;
8
- /** Duration expectation appended to long-running lanes, e.g. "usually 2-4 min". */
9
- expectation?: string;
10
- startedAt: number;
11
- finishedAt?: number;
12
- }
13
- export interface ProgressBoardFrameOptions {
14
- now?: number;
15
- spinnerFrame?: string;
16
- color?: boolean;
17
- /** Max visible line width; longer lines are truncated so in-place rewrites never wrap. */
18
- width?: number;
19
- }
20
- /**
21
- * Pure frame renderer (unit-testable without a TTY): one line per lane, in
22
- * insertion order. Running lanes show spinner, title, elapsed, latest detail,
23
- * and the "(usually X min)" expectation; terminal lanes freeze to a glyph,
24
- * title, and their summary note.
25
- */
26
- export declare function renderProgressBoardFrame(lanes: readonly ProgressLane[], options?: ProgressBoardFrameOptions): string[];
27
- export interface InitProgressBoardOptions {
28
- stream?: NodeJS.WriteStream;
29
- /** Overrides TTY detection (in-place rendering); defaults to stream.isTTY with TERM=dumb treated as non-TTY. */
30
- isTTY?: boolean;
31
- color?: boolean;
32
- now?: () => number;
33
- renderIntervalMs?: number;
34
- fallbackIntervalMs?: number;
35
- }
36
- /**
37
- * Stateful board driver. TTY mode repaints on a ~150ms interval (unref'd so it
38
- * never holds the process open); non-TTY mode prints plain lines on lane
39
- * start/terminal transitions and throttles intermediate updates to one line
40
- * per lane per 10s.
41
- */
42
- export declare class InitProgressBoard {
43
- private readonly stream;
44
- private readonly tty;
45
- private readonly color;
46
- private readonly now;
47
- private readonly renderIntervalMs;
48
- private readonly fallbackIntervalMs;
49
- private readonly laneList;
50
- private readonly lanesById;
51
- private readonly lastFallbackPrint;
52
- private timer;
53
- private renderedLines;
54
- private spinnerIndex;
55
- private stopped;
56
- constructor(options?: InitProgressBoardOptions);
57
- get lanes(): readonly ProgressLane[];
58
- startLane(id: string, title: string, options?: {
59
- detail?: string;
60
- expectation?: string;
61
- }): void;
62
- update(id: string, detail: string): void;
63
- complete(id: string, note?: string): void;
64
- warn(id: string, note?: string): void;
65
- fail(id: string, note?: string): void;
66
- /** Freeze the final frame (TTY) and release the repaint timer. Idempotent. */
67
- stop(): void;
68
- /**
69
- * Terminal transitions are refinable: finishing an already-terminal lane
70
- * updates its glyph/note (keeping the first finish time for elapsed), so a
71
- * lane frozen early (e.g. "analysis finished on Moda; finalizing after
72
- * setup") can be corrected once the final outcome is known. Exact duplicate
73
- * finishes stay no-ops; `update()` still applies to running lanes only.
74
- */
75
- private finish;
76
- private ensureTimer;
77
- private render;
78
- private writeLine;
79
- }
80
- //# sourceMappingURL=progress-board.d.ts.map
@@ -1,225 +0,0 @@
1
- // src/init/progress-board.ts. Multi-lane live progress board for moda init
2
- // Step 4 (Set up), where several activities run at once (hosted harness
3
- // analysis, fast local tasks, the SDK integration agent).
4
- //
5
- // Hand-rolled, no dependencies, stderr only. On a TTY the lane block is
6
- // rewritten in place with cursor-up ANSI, throttled to ~150ms; completed lanes
7
- // freeze to a check line so the board never grows unbounded. Non-TTY human
8
- // mode falls back to plain progress lines at most every 10s per lane.
9
- // Agent/json modes never construct a board (init emits NDJSON progress events
10
- // instead). Color gating follows tui.ts conventions (NO_COLOR / TERM=dumb
11
- // respected); spinner and duration helpers follow analyst-tui.ts.
12
- import { stripAnsi } from '../markdown-terminal.js';
13
- const SPINNER_FRAMES = ['◐', '◓', '◑', '◒'];
14
- const GLYPHS = {
15
- done: '✓',
16
- warning: '!',
17
- failed: '✗',
18
- };
19
- const GLYPH_COLORS = {
20
- done: 32,
21
- warning: 33,
22
- failed: 31,
23
- };
24
- /** Same one-line cap as ask-stream's tool cards. */
25
- const DETAIL_MAX = 100;
26
- const RENDER_INTERVAL_MS = 150;
27
- const NON_TTY_LANE_INTERVAL_MS = 10_000;
28
- /**
29
- * Pure frame renderer (unit-testable without a TTY): one line per lane, in
30
- * insertion order. Running lanes show spinner, title, elapsed, latest detail,
31
- * and the "(usually X min)" expectation; terminal lanes freeze to a glyph,
32
- * title, and their summary note.
33
- */
34
- export function renderProgressBoardFrame(lanes, options = {}) {
35
- const now = options.now ?? Date.now();
36
- const color = options.color ?? false;
37
- const width = Math.max(48, Math.min(options.width ?? 120, 200));
38
- const paint = (code, text) => color ? `\u001b[${code}m${text}\u001b[0m` : text;
39
- const titleWidth = Math.min(32, lanes.reduce((max, lane) => Math.max(max, lane.title.length), 0));
40
- return lanes.map((lane) => {
41
- const title = lane.title.padEnd(titleWidth);
42
- if (lane.status === 'running') {
43
- const elapsed = formatDuration(Math.max(0, Math.round((now - lane.startedAt) / 1000)));
44
- const detail = lane.detail ? ` ${oneLine(lane.detail)}` : '';
45
- const expectation = lane.expectation ? ` ${paint(90, `(${lane.expectation})`)}` : '';
46
- const glyph = paint(36, options.spinnerFrame ?? SPINNER_FRAMES[0]);
47
- return truncateVisible(` ${glyph} ${title} ${paint(90, elapsed.padStart(6))}${detail}${expectation}`, width);
48
- }
49
- const glyph = paint(GLYPH_COLORS[lane.status], GLYPHS[lane.status]);
50
- const note = lane.detail
51
- ? oneLine(lane.detail)
52
- : formatDuration(Math.max(0, Math.round(((lane.finishedAt ?? now) - lane.startedAt) / 1000)));
53
- return truncateVisible(` ${glyph} ${title} ${note}`, width);
54
- });
55
- }
56
- /**
57
- * Stateful board driver. TTY mode repaints on a ~150ms interval (unref'd so it
58
- * never holds the process open); non-TTY mode prints plain lines on lane
59
- * start/terminal transitions and throttles intermediate updates to one line
60
- * per lane per 10s.
61
- */
62
- export class InitProgressBoard {
63
- stream;
64
- tty;
65
- color;
66
- now;
67
- renderIntervalMs;
68
- fallbackIntervalMs;
69
- laneList = [];
70
- lanesById = new Map();
71
- lastFallbackPrint = new Map();
72
- timer;
73
- renderedLines = 0;
74
- spinnerIndex = 0;
75
- stopped = false;
76
- constructor(options = {}) {
77
- this.stream = options.stream ?? process.stderr;
78
- const dumbTerm = process.env.TERM === 'dumb';
79
- this.tty = options.isTTY ?? (this.stream.isTTY === true && !dumbTerm);
80
- // NO_COLOR disables color when set to any value, per no-color.org.
81
- this.color = options.color ?? (this.tty && process.env.NO_COLOR === undefined);
82
- this.now = options.now ?? Date.now;
83
- this.renderIntervalMs = options.renderIntervalMs ?? RENDER_INTERVAL_MS;
84
- this.fallbackIntervalMs = options.fallbackIntervalMs ?? NON_TTY_LANE_INTERVAL_MS;
85
- }
86
- get lanes() {
87
- return this.laneList;
88
- }
89
- startLane(id, title, options = {}) {
90
- if (this.stopped || this.lanesById.has(id))
91
- return;
92
- const lane = {
93
- id,
94
- title,
95
- status: 'running',
96
- detail: options.detail,
97
- expectation: options.expectation,
98
- startedAt: this.now(),
99
- };
100
- this.laneList.push(lane);
101
- this.lanesById.set(id, lane);
102
- if (this.tty) {
103
- this.ensureTimer();
104
- this.render();
105
- }
106
- else {
107
- this.lastFallbackPrint.set(id, this.now());
108
- this.writeLine(` ${title}: ${lane.detail ? oneLine(lane.detail) : 'started'}${lane.expectation ? ` (${lane.expectation})` : ''}`);
109
- }
110
- }
111
- update(id, detail) {
112
- const lane = this.lanesById.get(id);
113
- if (!lane || this.stopped || lane.status !== 'running')
114
- return;
115
- lane.detail = detail;
116
- if (this.tty)
117
- return; // interval repaints
118
- const last = this.lastFallbackPrint.get(id) ?? 0;
119
- if (this.now() - last < this.fallbackIntervalMs)
120
- return;
121
- this.lastFallbackPrint.set(id, this.now());
122
- const elapsed = formatDuration(Math.max(0, Math.round((this.now() - lane.startedAt) / 1000)));
123
- this.writeLine(` ${lane.title}: ${oneLine(detail)} (${elapsed} elapsed)`);
124
- }
125
- complete(id, note) {
126
- this.finish(id, 'done', note);
127
- }
128
- warn(id, note) {
129
- this.finish(id, 'warning', note);
130
- }
131
- fail(id, note) {
132
- this.finish(id, 'failed', note);
133
- }
134
- /** Freeze the final frame (TTY) and release the repaint timer. Idempotent. */
135
- stop() {
136
- if (this.stopped)
137
- return;
138
- this.stopped = true;
139
- if (this.timer) {
140
- clearInterval(this.timer);
141
- this.timer = undefined;
142
- }
143
- if (this.tty && this.laneList.length > 0)
144
- this.render();
145
- }
146
- /**
147
- * Terminal transitions are refinable: finishing an already-terminal lane
148
- * updates its glyph/note (keeping the first finish time for elapsed), so a
149
- * lane frozen early (e.g. "analysis finished on Moda; finalizing after
150
- * setup") can be corrected once the final outcome is known. Exact duplicate
151
- * finishes stay no-ops; `update()` still applies to running lanes only.
152
- */
153
- finish(id, status, note) {
154
- const lane = this.lanesById.get(id);
155
- if (!lane || this.stopped)
156
- return;
157
- if (lane.status !== 'running' && lane.status === status && lane.detail === note)
158
- return;
159
- lane.status = status;
160
- lane.detail = note;
161
- lane.finishedAt ??= this.now();
162
- if (this.tty) {
163
- this.render();
164
- return;
165
- }
166
- const label = status === 'done' ? 'done' : status === 'warning' ? 'warning' : 'failed';
167
- this.writeLine(` ${lane.title}: ${label}${note ? ` (${oneLine(note)})` : ''}`);
168
- }
169
- ensureTimer() {
170
- if (this.timer || !this.tty)
171
- return;
172
- this.timer = setInterval(() => this.render(), this.renderIntervalMs);
173
- // Never keep the process alive just to animate a spinner.
174
- this.timer.unref?.();
175
- }
176
- render() {
177
- if (!this.tty || this.laneList.length === 0)
178
- return;
179
- const lines = renderProgressBoardFrame(this.laneList, {
180
- now: this.now(),
181
- spinnerFrame: SPINNER_FRAMES[this.spinnerIndex++ % SPINNER_FRAMES.length],
182
- color: this.color,
183
- width: this.stream.columns ?? 120,
184
- });
185
- const up = this.renderedLines > 0 ? `\u001b[${this.renderedLines}A` : '';
186
- this.stream.write(`${up}${lines.map((line) => `\u001b[2K${line}`).join('\n')}\n`);
187
- this.renderedLines = lines.length;
188
- }
189
- writeLine(line) {
190
- this.stream.write(`${line}\n`);
191
- }
192
- }
193
- /**
194
- * One-line convention shared with ask-stream tool cards: strip escapes,
195
- * collapse whitespace, cap ~100 chars. Lane details carry backend/agent
196
- * controlled text (hosted progress messages, tool inputs like file paths and
197
- * shell commands), so ANSI/OSC escapes and remaining C0 controls must be
198
- * removed before the text reaches the terminal; an embedded escape would
199
- * otherwise corrupt the in-place cursor-up rewrite or inject control output.
200
- */
201
- function oneLine(text) {
202
- const collapsed = stripAnsi(text)
203
- .replace(/[\u0000-\u001f\u007f]+/g, ' ')
204
- .replace(/\s+/g, ' ')
205
- .trim();
206
- return collapsed.length > DETAIL_MAX ? `${collapsed.slice(0, DETAIL_MAX - 1)}…` : collapsed;
207
- }
208
- /** Same shape as analyst-tui.ts formatDuration. */
209
- function formatDuration(seconds) {
210
- if (seconds < 60)
211
- return `${seconds}s`;
212
- const minutes = Math.floor(seconds / 60);
213
- const remaining = seconds % 60;
214
- return `${minutes}m ${remaining}s`;
215
- }
216
- function truncateVisible(value, width) {
217
- if (visibleLength(value) <= width)
218
- return value;
219
- const stripped = stripAnsi(value);
220
- return `${stripped.slice(0, Math.max(0, width - 3))}...`;
221
- }
222
- function visibleLength(value) {
223
- return stripAnsi(value).length;
224
- }
225
- //# sourceMappingURL=progress-board.js.map
@@ -1,6 +0,0 @@
1
- export declare function claudeSkillToCursorRule(skillContent: string): string;
2
- export declare function installRules(detectedAgents: string[], options?: {
3
- yes?: boolean;
4
- silent?: boolean;
5
- }): Promise<void>;
6
- //# sourceMappingURL=rules.d.ts.map
@@ -1,171 +0,0 @@
1
- // src/init/rules.ts — Install Moda agent skills into the user's project.
2
- //
3
- // Each entry in SKILLS is a directory under <package>/skills/ that contains a
4
- // SKILL.md (Claude Code format). At install time we copy each skill into every
5
- // detected coding agent, translating frontmatter where the agent expects a
6
- // different shape (e.g. Cursor's .mdc rules).
7
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
8
- import { dirname, join } from 'node:path';
9
- import { fileURLToPath } from 'node:url';
10
- import { confirm } from '@inquirer/prompts';
11
- // --- Package path resolution ---
12
- //
13
- // Resolved relative to this module's URL so it works whether we're running
14
- // from src/ in dev or from the published dist/ inside node_modules.
15
- const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
16
- const SKILLS_SOURCE_DIR = join(PACKAGE_ROOT, 'skills');
17
- const SKILLS = [
18
- {
19
- id: 'moda-cli',
20
- label: 'moda-cli — let coding agents query Moda analytics via the CLI',
21
- },
22
- ];
23
- const ADAPTERS = {
24
- 'Claude Code': {
25
- name: 'Claude Code',
26
- targetFor: (skillId) => ({
27
- dir: join('.claude', 'skills', skillId),
28
- filename: 'SKILL.md',
29
- }),
30
- transform: (content) => content,
31
- },
32
- 'Cursor': {
33
- name: 'Cursor',
34
- targetFor: (skillId) => ({
35
- dir: join('.cursor', 'rules'),
36
- filename: `${skillId}.mdc`,
37
- }),
38
- transform: claudeSkillToCursorRule,
39
- },
40
- };
41
- // --- Frontmatter translation ---
42
- //
43
- // Cursor's .mdc rules use a different frontmatter shape:
44
- // ---
45
- // description: "..."
46
- // alwaysApply: false
47
- // ---
48
- // The body markdown is identical, so we strip the Claude frontmatter, lift
49
- // the description out of it, and emit Cursor's variant.
50
- const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
51
- const DESCRIPTION_RE = /^description:\s*(.+?)\s*$/m;
52
- export function claudeSkillToCursorRule(skillContent) {
53
- const match = FRONTMATTER_RE.exec(skillContent);
54
- if (!match) {
55
- // Body without frontmatter — pass through untouched rather than guess.
56
- return skillContent;
57
- }
58
- const [, frontmatter, body] = match;
59
- const descMatch = DESCRIPTION_RE.exec(frontmatter);
60
- const description = descMatch?.[1].trim() ?? '';
61
- // JSON.stringify gives us a YAML-safe double-quoted string with proper
62
- // escaping for any quotes or backslashes inside the description.
63
- const cursorFrontmatter = `---\n` +
64
- `description: ${JSON.stringify(description)}\n` +
65
- `alwaysApply: false\n` +
66
- `---\n`;
67
- return `${cursorFrontmatter}\n${body.replace(/^\s+/, '')}`;
68
- }
69
- // --- I/O helpers ---
70
- function loadSkillSource(skillId) {
71
- const path = join(SKILLS_SOURCE_DIR, skillId, 'SKILL.md');
72
- if (!existsSync(path)) {
73
- throw new Error(`Skill source missing: ${path}`);
74
- }
75
- return readFileSync(path, 'utf8');
76
- }
77
- function installSkillForAgent(skill, adapter, content) {
78
- const { dir, filename } = adapter.targetFor(skill.id);
79
- const dirPath = join(process.cwd(), dir);
80
- const filePath = join(dirPath, filename);
81
- const relativePath = join(dir, filename);
82
- try {
83
- mkdirSync(dirPath, { recursive: true });
84
- if (existsSync(filePath)) {
85
- return { agent: adapter.name, relativePath, status: 'exists' };
86
- }
87
- writeFileSync(filePath, adapter.transform(content));
88
- return { agent: adapter.name, relativePath, status: 'wrote' };
89
- }
90
- catch (err) {
91
- return {
92
- agent: adapter.name,
93
- relativePath,
94
- status: 'failed',
95
- error: err instanceof Error ? err.message : String(err),
96
- };
97
- }
98
- }
99
- // --- Public entry point ---
100
- export async function installRules(detectedAgents, options = {}) {
101
- const adapters = detectedAgents
102
- .map((a) => ADAPTERS[a])
103
- .filter((a) => a !== undefined);
104
- if (adapters.length === 0) {
105
- if (!options.silent)
106
- console.log(' No supported coding agents detected');
107
- return;
108
- }
109
- const agentList = adapters.map((a) => a.name).join(', ');
110
- const skillList = SKILLS.map((s) => `\n • ${s.label}`).join('');
111
- const shouldInstall = options.yes
112
- ? true
113
- : await confirm({
114
- message: `Install Moda skills for ${agentList}?${skillList}\n Continue?`,
115
- default: true,
116
- });
117
- if (!shouldInstall) {
118
- if (!options.silent)
119
- console.log(' Skipped');
120
- return;
121
- }
122
- let wrote = 0;
123
- let skipped = 0;
124
- let failed = 0;
125
- for (const skill of SKILLS) {
126
- let content;
127
- try {
128
- content = loadSkillSource(skill.id);
129
- }
130
- catch (err) {
131
- const message = err instanceof Error ? err.message : String(err);
132
- console.error(` Warning: ${message}`);
133
- failed += adapters.length;
134
- continue;
135
- }
136
- for (const adapter of adapters) {
137
- const outcome = installSkillForAgent(skill, adapter, content);
138
- switch (outcome.status) {
139
- case 'wrote':
140
- if (!options.silent)
141
- console.log(` ${outcome.agent}: wrote ${outcome.relativePath}`);
142
- wrote++;
143
- break;
144
- case 'exists':
145
- if (!options.silent) {
146
- console.log(` ${outcome.agent}: ${outcome.relativePath} already exists — skipping`);
147
- }
148
- skipped++;
149
- break;
150
- case 'failed':
151
- if (!options.silent) {
152
- console.error(` ${outcome.agent}: failed to write ${outcome.relativePath} — ${outcome.error}`);
153
- }
154
- failed++;
155
- break;
156
- }
157
- }
158
- }
159
- const summary = [
160
- wrote && `${wrote} written`,
161
- skipped && `${skipped} already present`,
162
- failed && `${failed} failed`,
163
- ]
164
- .filter(Boolean)
165
- .join(', ');
166
- if (summary) {
167
- if (!options.silent)
168
- console.log(` Done — ${summary}`);
169
- }
170
- }
171
- //# sourceMappingURL=rules.js.map
@@ -1,10 +0,0 @@
1
- import { type BundledSkillIndex } from '../skills-bundled.js';
2
- export interface MaterializedSdkCatalog {
3
- dir: string;
4
- relativeDir: string;
5
- index: BundledSkillIndex;
6
- }
7
- export declare function materializeSdkCatalog(cwd: string, runId: string): MaterializedSdkCatalog;
8
- export declare function cleanupSdkCatalog(dir: string): void;
9
- export declare function renderCatalogListing(index: BundledSkillIndex): string;
10
- //# sourceMappingURL=sdk-catalog.d.ts.map
@@ -1,80 +0,0 @@
1
- // src/init/sdk-catalog.ts — Ephemeral, run-scoped materialization of the
2
- // complete bundled SDK integration-skill catalog for a coding agent to browse.
3
- import { copyFileSync, existsSync, mkdirSync, readdirSync, rmSync, statSync, } from 'node:fs';
4
- import { join, relative, resolve } from 'node:path';
5
- import { bundledSkillsDir, readBundledIndex, } from '../skills-bundled.js';
6
- export function materializeSdkCatalog(cwd, runId) {
7
- if (!/^[a-z0-9-]+$/.test(runId)) {
8
- throw new Error(`Invalid SDK catalog run id: ${JSON.stringify(runId).slice(0, 80)}`);
9
- }
10
- const sourceDir = bundledSkillsDir();
11
- const indexPath = join(sourceDir, 'index.json');
12
- if (!existsSync(sourceDir) || !existsSync(indexPath)) {
13
- throw missingBundleError(sourceDir);
14
- }
15
- const index = readBundledIndex();
16
- if (!index) {
17
- throw missingBundleError(sourceDir);
18
- }
19
- const dir = join(cwd, '.moda', 'tmp', `sdk-catalog-${runId}`);
20
- cleanupSdkCatalog(dir);
21
- copyDirectoryRecursive(sourceDir, dir);
22
- return {
23
- dir,
24
- relativeDir: relative(cwd, dir).replace(/\\/g, '/'),
25
- index,
26
- };
27
- }
28
- export function cleanupSdkCatalog(dir) {
29
- const normalized = resolve(dir).replace(/\\/g, '/');
30
- if (!normalized.includes('.moda/tmp/sdk-catalog-'))
31
- return;
32
- try {
33
- rmSync(dir, { recursive: true, force: true });
34
- }
35
- catch {
36
- // Catalog cleanup is best-effort and must never mask the integration result.
37
- }
38
- }
39
- export function renderCatalogListing(index) {
40
- return index.skills.map((skill) => {
41
- const targets = [`language: ${skill.targets.language}`];
42
- if (skill.targets.frameworks.length > 0) {
43
- targets.push(`frameworks: ${skill.targets.frameworks.join(',')}`);
44
- }
45
- if (skill.targets.providers.length > 0) {
46
- targets.push(`providers: ${skill.targets.providers.join(',')}`);
47
- }
48
- return `- ${skill.id} — ${skill.summary} [${targets.join('; ')}]`;
49
- }).join('\n');
50
- }
51
- function copyDirectoryRecursive(sourceDir, targetDir) {
52
- mkdirSync(targetDir, { recursive: true });
53
- for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
54
- const source = join(sourceDir, entry.name);
55
- const target = join(targetDir, entry.name);
56
- if (entry.isDirectory()) {
57
- copyDirectoryRecursive(source, target);
58
- }
59
- else if (entry.isFile()) {
60
- copyFileSync(source, target);
61
- }
62
- }
63
- }
64
- function missingBundleError(sourceDir) {
65
- let detail = 'is absent';
66
- try {
67
- if (existsSync(sourceDir) && !statSync(sourceDir).isDirectory()) {
68
- detail = 'is not a directory';
69
- }
70
- else if (existsSync(sourceDir)) {
71
- detail = 'has no readable index.json';
72
- }
73
- }
74
- catch {
75
- detail = 'is not readable';
76
- }
77
- return new Error(`Bundled SDK skill catalog ${detail} at ${sourceDir}. ` +
78
- 'Set MODA_BUNDLED_SKILLS_DIR to a built bundle, or run the integration-skill bundle build in a dev tree.');
79
- }
80
- //# sourceMappingURL=sdk-catalog.js.map
@@ -1,19 +0,0 @@
1
- export declare const SDK_GUIDE_PATH = ".moda/SDK_INTEGRATION.md";
2
- type RuntimeSummary = {
3
- runtimeAgents: number;
4
- agentFamilies: number;
5
- frameworks: string[];
6
- providers: string[];
7
- runtimeAgentNames: string[];
8
- };
9
- /**
10
- * Write the human/agent-readable fallback guide (.moda/SDK_INTEGRATION.md).
11
- * Used when automatic SDK integration did not complete successfully.
12
- */
13
- export declare function writeSdkIntegrationGuide(cwd?: string): string;
14
- export declare function renderSdkIntegrationGuideContent(): string;
15
- /** Read optional harness discovery context from `.moda/harness.json`. */
16
- export declare function readRuntimeSummary(cwd: string): RuntimeSummary;
17
- export declare function renderRuntimeSummaryBlock(summary: RuntimeSummary): string;
18
- export {};
19
- //# sourceMappingURL=sdk-guide.d.ts.map