@moda-ai/cli 1.55.21 → 1.55.22
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/dist/{cli-4j8nffsm.js → cli-0ef72tqx.js} +53 -24
- package/dist/{cli-sf0g0fjg.js → cli-8dtnwp9r.js} +152 -57
- package/dist/{cli-38p87ykc.js → cli-frhm2wz7.js} +1 -1
- package/dist/cli.js +10 -5
- package/dist/{index-ewqbpnwz.js → index-1wbjz3jc.js} +48 -12
- package/dist/{skills-bundled-r9a2vs07.js → skills-bundled-dq20spzk.js} +2 -2
- package/dist/{skills-br82cpt7.js → skills-gejdvjak.js} +2 -2
- package/dist/{sync-25vn78je.js → sync-57ercmh9.js} +2 -2
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
pruneManagedSkillPaths,
|
|
24
24
|
readSkillsManifest,
|
|
25
25
|
writeSkillsManifest
|
|
26
|
-
} from "./cli-
|
|
26
|
+
} from "./cli-8dtnwp9r.js";
|
|
27
27
|
import {
|
|
28
28
|
__require
|
|
29
29
|
} from "./cli-0v6na3yp.js";
|
|
@@ -80,27 +80,39 @@ function renderFixesBlock() {
|
|
|
80
80
|
].join(`
|
|
81
81
|
`);
|
|
82
82
|
}
|
|
83
|
+
function countOccurrences(text, needle) {
|
|
84
|
+
return text.split(needle).length - 1;
|
|
85
|
+
}
|
|
83
86
|
function upsertManagedBlock(existing, block) {
|
|
84
87
|
if (existing === null) {
|
|
85
|
-
return `${block}
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
const beginIdx = existing.indexOf(FIXES_BLOCK_BEGIN);
|
|
89
|
-
const endIdx = existing.indexOf(FIXES_BLOCK_END);
|
|
90
|
-
if (beginIdx !== -1 && endIdx !== -1 && endIdx >= beginIdx) {
|
|
91
|
-
const before = existing.slice(0, beginIdx);
|
|
92
|
-
const after = existing.slice(endIdx + FIXES_BLOCK_END.length);
|
|
93
|
-
return `${before}${block}${after}`;
|
|
88
|
+
return { ok: true, content: `${block}
|
|
89
|
+
` };
|
|
94
90
|
}
|
|
95
|
-
const
|
|
91
|
+
const begins = countOccurrences(existing, FIXES_BLOCK_BEGIN);
|
|
92
|
+
const ends = countOccurrences(existing, FIXES_BLOCK_END);
|
|
93
|
+
if (begins === 0 && ends === 0) {
|
|
94
|
+
const separator = existing === "" || existing.endsWith(`
|
|
96
95
|
|
|
97
96
|
`) ? "" : existing.endsWith(`
|
|
98
97
|
`) ? `
|
|
99
98
|
` : `
|
|
100
99
|
|
|
101
100
|
`;
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
return { ok: true, content: `${existing}${separator}${block}
|
|
102
|
+
` };
|
|
103
|
+
}
|
|
104
|
+
const beginIdx = existing.indexOf(FIXES_BLOCK_BEGIN);
|
|
105
|
+
const endIdx = existing.indexOf(FIXES_BLOCK_END);
|
|
106
|
+
if (begins === 1 && ends === 1 && endIdx > beginIdx) {
|
|
107
|
+
const before = existing.slice(0, beginIdx);
|
|
108
|
+
const after = existing.slice(endIdx + FIXES_BLOCK_END.length);
|
|
109
|
+
return { ok: true, content: `${before}${block}${after}` };
|
|
110
|
+
}
|
|
111
|
+
const problem = begins === 1 && ends === 1 ? `its ${FIXES_BLOCK_END} marker comes before its ${FIXES_BLOCK_BEGIN} marker` : `it has ${begins} ${FIXES_BLOCK_BEGIN} and ${ends} ${FIXES_BLOCK_END} marker(s) instead of one of each`;
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
reason: `The Moda block in ${AGENTS_MD_FILENAME} was not updated because ${problem}. ` + "The file was left unchanged so no text is lost. Keep exactly one begin marker followed by one end marker " + "(or delete the stray markers), then rerun `moda skills sync`."
|
|
115
|
+
};
|
|
104
116
|
}
|
|
105
117
|
function writeFileIfChanged(filePath, content) {
|
|
106
118
|
try {
|
|
@@ -108,14 +120,14 @@ function writeFileIfChanged(filePath, content) {
|
|
|
108
120
|
if (existsSync(filePath)) {
|
|
109
121
|
const current = readFileSync(filePath, "utf8");
|
|
110
122
|
if (current === content)
|
|
111
|
-
return "unchanged";
|
|
123
|
+
return { status: "unchanged" };
|
|
112
124
|
writeFileSync(filePath, content);
|
|
113
|
-
return "updated";
|
|
125
|
+
return { status: "updated" };
|
|
114
126
|
}
|
|
115
127
|
writeFileSync(filePath, content);
|
|
116
|
-
return "wrote";
|
|
117
|
-
} catch {
|
|
118
|
-
return "failed";
|
|
128
|
+
return { status: "wrote" };
|
|
129
|
+
} catch (err) {
|
|
130
|
+
return { status: "failed", error: err instanceof Error ? err.message : String(err) };
|
|
119
131
|
}
|
|
120
132
|
}
|
|
121
133
|
function upsertFixesAgentsBlock(cwd) {
|
|
@@ -124,12 +136,28 @@ function upsertFixesAgentsBlock(cwd) {
|
|
|
124
136
|
if (existsSync(filePath)) {
|
|
125
137
|
try {
|
|
126
138
|
existing = readFileSync(filePath, "utf8");
|
|
127
|
-
} catch {
|
|
128
|
-
return {
|
|
139
|
+
} catch (err) {
|
|
140
|
+
return {
|
|
141
|
+
path: AGENTS_MD_FILENAME,
|
|
142
|
+
status: "failed",
|
|
143
|
+
reason: `Could not read ${AGENTS_MD_FILENAME}: ${err instanceof Error ? err.message : String(err)}`
|
|
144
|
+
};
|
|
129
145
|
}
|
|
130
146
|
}
|
|
131
147
|
const next = upsertManagedBlock(existing, renderFixesBlock());
|
|
132
|
-
|
|
148
|
+
if (!next.ok)
|
|
149
|
+
return { path: AGENTS_MD_FILENAME, status: "skipped", reason: next.reason };
|
|
150
|
+
const written = writeFileIfChanged(filePath, next.content);
|
|
151
|
+
return {
|
|
152
|
+
path: AGENTS_MD_FILENAME,
|
|
153
|
+
status: written.status,
|
|
154
|
+
...written.error ? { reason: `Could not write ${AGENTS_MD_FILENAME}: ${written.error}` } : {}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function agentsMdProblem(agentsMd) {
|
|
158
|
+
if (!agentsMd || agentsMd.status !== "skipped" && agentsMd.status !== "failed")
|
|
159
|
+
return;
|
|
160
|
+
return agentsMd.reason ?? `The Moda block in ${agentsMd.path} could not be updated.`;
|
|
133
161
|
}
|
|
134
162
|
|
|
135
163
|
// src/skills.ts
|
|
@@ -717,11 +745,12 @@ async function runSkillsCommand(input) {
|
|
|
717
745
|
if (sub === "sync") {
|
|
718
746
|
assertFlags("skills sync", flags, ["dry-run"]);
|
|
719
747
|
const result = await runSkillSync(flags, { profile: context.profile, env: context.env });
|
|
748
|
+
const agentsMdWarning = agentsMdProblem(result.agentsMd);
|
|
720
749
|
context.output.writeData({
|
|
721
750
|
synced: result.synced,
|
|
722
751
|
dryRun: result.dryRun,
|
|
723
752
|
...result.agentsMd ? { agentsMd: result.agentsMd } : {}
|
|
724
|
-
});
|
|
753
|
+
}, agentsMdWarning ? { status: "degraded", warnings: [agentsMdWarning] } : {});
|
|
725
754
|
return 0;
|
|
726
755
|
}
|
|
727
756
|
if (sub === "install") {
|
|
@@ -866,7 +895,7 @@ function sha256(input) {
|
|
|
866
895
|
return createHash("sha256").update(input).digest("hex");
|
|
867
896
|
}
|
|
868
897
|
async function runSkillsInstall(context, ids) {
|
|
869
|
-
const { installIntegrationSkill, readBundledIndex, readBundledSkillMd } = await import("./skills-bundled-
|
|
898
|
+
const { installIntegrationSkill, readBundledIndex, readBundledSkillMd } = await import("./skills-bundled-dq20spzk.js");
|
|
870
899
|
const index = readBundledIndex();
|
|
871
900
|
if (context.flags.list === "true" || ids.length === 0) {
|
|
872
901
|
if (!index) {
|
|
@@ -947,4 +976,4 @@ async function runSkillRegistryCommand(sub, positionals, flags, context) {
|
|
|
947
976
|
return 0;
|
|
948
977
|
}
|
|
949
978
|
|
|
950
|
-
export { pullSkills, runSkillSync, runSkillsCommand, parseScopeInt };
|
|
979
|
+
export { agentsMdProblem, pullSkills, runSkillSync, runSkillsCommand, parseScopeInt };
|
|
@@ -7,8 +7,8 @@ function packageAssetPath(...parts) {
|
|
|
7
7
|
|
|
8
8
|
// src/init/rules.ts
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { dirname as dirname2, join as join2 } from "node:path";
|
|
10
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { dirname as dirname2, join as join2, sep } from "node:path";
|
|
12
12
|
import { confirm } from "@inquirer/prompts";
|
|
13
13
|
var SKILLS_SOURCE_DIR = packageAssetPath("skills");
|
|
14
14
|
var SKILLS = [
|
|
@@ -93,18 +93,68 @@ function skillVersion(skillContent) {
|
|
|
93
93
|
const frontmatter = FRONTMATTER_RE.exec(skillContent)?.[1] ?? "";
|
|
94
94
|
return /^version:\s*(\S+)\s*$/m.exec(frontmatter)?.[1] ?? "unknown";
|
|
95
95
|
}
|
|
96
|
+
function normalizeEol(text) {
|
|
97
|
+
return text.replace(/\r\n/g, `
|
|
98
|
+
`);
|
|
99
|
+
}
|
|
100
|
+
function matchesRecord(existing, recorded) {
|
|
101
|
+
const hash = recorded?.sha256;
|
|
102
|
+
return typeof hash === "string" && (hash === sha256(existing) || hash === sha256(normalizeEol(existing)));
|
|
103
|
+
}
|
|
96
104
|
function looksLikeShippedSkill(existing, skill) {
|
|
97
|
-
|
|
105
|
+
const text = normalizeEol(existing);
|
|
106
|
+
return text.startsWith(`---
|
|
98
107
|
name: ${skill.id}
|
|
99
|
-
`) || /^# Moda CLI$/m.test(
|
|
108
|
+
`) || /^# Moda CLI$/m.test(text);
|
|
109
|
+
}
|
|
110
|
+
function lstatOrUndefined(path) {
|
|
111
|
+
try {
|
|
112
|
+
return lstatSync(path);
|
|
113
|
+
} catch {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function firstSymlinkedComponent(relativePath) {
|
|
118
|
+
let current = "";
|
|
119
|
+
for (const part of relativePath.split(/[\\/]+/).filter(Boolean)) {
|
|
120
|
+
current = current ? join2(current, part) : part;
|
|
121
|
+
const stat = lstatOrUndefined(join2(process.cwd(), current));
|
|
122
|
+
if (!stat)
|
|
123
|
+
return;
|
|
124
|
+
if (stat.isSymbolicLink())
|
|
125
|
+
return current;
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
100
128
|
}
|
|
101
129
|
function unusedBackupPath(relativePath) {
|
|
102
130
|
let candidate = `${relativePath}.bak`;
|
|
103
|
-
for (let n = 1;
|
|
131
|
+
for (let n = 1;lstatOrUndefined(join2(process.cwd(), candidate)); n++) {
|
|
104
132
|
candidate = `${relativePath}.bak.${n}`;
|
|
105
133
|
}
|
|
106
134
|
return candidate;
|
|
107
135
|
}
|
|
136
|
+
function removeStaleReferences(referencesDir, source, record) {
|
|
137
|
+
const removed = [];
|
|
138
|
+
const kept = [];
|
|
139
|
+
const prefix = `${referencesDir}${sep}`;
|
|
140
|
+
for (const key of Object.keys(record)) {
|
|
141
|
+
if (!key.startsWith(prefix))
|
|
142
|
+
continue;
|
|
143
|
+
const name = key.slice(prefix.length);
|
|
144
|
+
if (source.references.has(name) || !/^[^\\/]+\.md$/.test(name))
|
|
145
|
+
continue;
|
|
146
|
+
const path = join2(process.cwd(), key);
|
|
147
|
+
const stat = lstatOrUndefined(path);
|
|
148
|
+
if (stat?.isFile() && matchesRecord(readFileSync(path, "utf8"), record[key])) {
|
|
149
|
+
unlinkSync(path);
|
|
150
|
+
removed.push(key);
|
|
151
|
+
} else if (stat) {
|
|
152
|
+
kept.push(key);
|
|
153
|
+
}
|
|
154
|
+
delete record[key];
|
|
155
|
+
}
|
|
156
|
+
return { removed, kept };
|
|
157
|
+
}
|
|
108
158
|
function installSkillForAgent(skill, adapter, source, record) {
|
|
109
159
|
const { dir, filename } = adapter.targetFor(skill.id);
|
|
110
160
|
const dirPath = join2(process.cwd(), dir);
|
|
@@ -112,6 +162,10 @@ function installSkillForAgent(skill, adapter, source, record) {
|
|
|
112
162
|
const relativePath = join2(dir, filename);
|
|
113
163
|
const referencesDir = adapter.referencesDirFor(skill.id);
|
|
114
164
|
const content = adapter.transform(source.content, referencesDir);
|
|
165
|
+
const symlinkPath = firstSymlinkedComponent(relativePath) ?? firstSymlinkedComponent(referencesDir);
|
|
166
|
+
if (symlinkPath) {
|
|
167
|
+
return { agent: adapter.name, relativePath, status: "symlink", symlinkPath };
|
|
168
|
+
}
|
|
115
169
|
try {
|
|
116
170
|
mkdirSync(dirPath, { recursive: true });
|
|
117
171
|
let status = "wrote";
|
|
@@ -119,9 +173,9 @@ function installSkillForAgent(skill, adapter, source, record) {
|
|
|
119
173
|
if (existsSync(filePath)) {
|
|
120
174
|
const existing = readFileSync(filePath, "utf8");
|
|
121
175
|
const recorded = record[relativePath];
|
|
122
|
-
if (existing === content) {
|
|
176
|
+
if (normalizeEol(existing) === content) {
|
|
123
177
|
status = "current";
|
|
124
|
-
} else if (recorded ?
|
|
178
|
+
} else if (recorded ? matchesRecord(existing, recorded) : looksLikeShippedSkill(existing, skill)) {
|
|
125
179
|
if (!recorded) {
|
|
126
180
|
backupPath = unusedBackupPath(relativePath);
|
|
127
181
|
writeFileSync(join2(process.cwd(), backupPath), existing);
|
|
@@ -138,12 +192,18 @@ function installSkillForAgent(skill, adapter, source, record) {
|
|
|
138
192
|
const referencesPath = join2(process.cwd(), referencesDir);
|
|
139
193
|
mkdirSync(referencesPath, { recursive: true });
|
|
140
194
|
const keptReferences = [];
|
|
195
|
+
const symlinkedReferences = [];
|
|
141
196
|
for (const [name, text] of source.references) {
|
|
142
197
|
const referencePath = join2(referencesPath, name);
|
|
143
198
|
const referenceKey = join2(referencesDir, name);
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
|
|
199
|
+
const stat = lstatOrUndefined(referencePath);
|
|
200
|
+
if (stat?.isSymbolicLink()) {
|
|
201
|
+
symlinkedReferences.push(referenceKey);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const existing = stat ? readFileSync(referencePath, "utf8") : undefined;
|
|
205
|
+
if (existing === undefined || normalizeEol(existing) !== text) {
|
|
206
|
+
if (existing !== undefined && !matchesRecord(existing, record[referenceKey])) {
|
|
147
207
|
keptReferences.push(referenceKey);
|
|
148
208
|
continue;
|
|
149
209
|
}
|
|
@@ -151,12 +211,16 @@ function installSkillForAgent(skill, adapter, source, record) {
|
|
|
151
211
|
}
|
|
152
212
|
record[referenceKey] = { version, sha256: sha256(text) };
|
|
153
213
|
}
|
|
214
|
+
const stale = removeStaleReferences(referencesDir, source, record);
|
|
154
215
|
return {
|
|
155
216
|
agent: adapter.name,
|
|
156
217
|
relativePath,
|
|
157
218
|
status,
|
|
158
219
|
...backupPath ? { backupPath } : {},
|
|
159
|
-
...keptReferences.length > 0 ? { keptReferences } : {}
|
|
220
|
+
...keptReferences.length > 0 ? { keptReferences } : {},
|
|
221
|
+
...symlinkedReferences.length > 0 ? { symlinkedReferences } : {},
|
|
222
|
+
...stale.removed.length > 0 ? { removedReferences: stale.removed } : {},
|
|
223
|
+
...stale.kept.length > 0 ? { keptStaleReferences: stale.kept } : {}
|
|
160
224
|
};
|
|
161
225
|
} catch (err) {
|
|
162
226
|
return {
|
|
@@ -167,30 +231,59 @@ function installSkillForAgent(skill, adapter, source, record) {
|
|
|
167
231
|
};
|
|
168
232
|
}
|
|
169
233
|
}
|
|
234
|
+
function emptyResult(status, agents) {
|
|
235
|
+
return {
|
|
236
|
+
status,
|
|
237
|
+
agents,
|
|
238
|
+
outcomes: [],
|
|
239
|
+
counts: { written: 0, updated: 0, current: 0, kept: 0, skipped: 0, failed: 0 },
|
|
240
|
+
warnings: [],
|
|
241
|
+
notes: []
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function summarizeInstall(result) {
|
|
245
|
+
const { written, updated, current, kept, skipped, failed } = result.counts;
|
|
246
|
+
return [
|
|
247
|
+
written && `${written} written`,
|
|
248
|
+
updated && `${updated} updated`,
|
|
249
|
+
current && `${current} already current`,
|
|
250
|
+
kept && `${kept} kept (edited locally)`,
|
|
251
|
+
skipped && `${skipped} skipped (symlink)`,
|
|
252
|
+
failed && `${failed} failed`
|
|
253
|
+
].filter(Boolean).join(", ");
|
|
254
|
+
}
|
|
170
255
|
async function installRules(detectedAgents, options = {}) {
|
|
171
256
|
const adapters = detectedAgents.map((a) => ADAPTERS[a]).filter((a) => a !== undefined);
|
|
172
257
|
if (adapters.length === 0) {
|
|
173
258
|
if (!options.silent)
|
|
174
259
|
console.log(" No supported coding agents detected");
|
|
175
|
-
return;
|
|
260
|
+
return emptyResult("skipped", []);
|
|
176
261
|
}
|
|
177
|
-
const
|
|
262
|
+
const agentNames = adapters.map((a) => a.name);
|
|
178
263
|
const skillList = SKILLS.map((s) => `
|
|
179
264
|
• ${s.label}`).join("");
|
|
180
265
|
const shouldInstall = options.yes ? true : await confirm({
|
|
181
|
-
message: `Install Moda skills for ${
|
|
266
|
+
message: `Install Moda skills for ${agentNames.join(", ")}?${skillList}
|
|
182
267
|
Continue?`,
|
|
183
268
|
default: true
|
|
184
269
|
});
|
|
185
270
|
if (!shouldInstall) {
|
|
186
271
|
if (!options.silent)
|
|
187
272
|
console.log(" Skipped");
|
|
188
|
-
return;
|
|
273
|
+
return emptyResult("skipped", agentNames);
|
|
189
274
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
275
|
+
const result = emptyResult("ok", agentNames);
|
|
276
|
+
const { counts, warnings, notes } = result;
|
|
277
|
+
const warn = (message) => {
|
|
278
|
+
warnings.push(message);
|
|
279
|
+
if (!options.silent)
|
|
280
|
+
console.error(` ${message}`);
|
|
281
|
+
};
|
|
282
|
+
const note = (message) => {
|
|
283
|
+
notes.push(message);
|
|
284
|
+
if (!options.silent)
|
|
285
|
+
console.log(` ${message}`);
|
|
286
|
+
};
|
|
194
287
|
const record = readInstallRecord();
|
|
195
288
|
const recordBefore = JSON.stringify(record);
|
|
196
289
|
for (const skill of SKILLS) {
|
|
@@ -198,45 +291,54 @@ async function installRules(detectedAgents, options = {}) {
|
|
|
198
291
|
try {
|
|
199
292
|
source = loadSkillSource(skill.id);
|
|
200
293
|
} catch (err) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
failed += adapters.length;
|
|
294
|
+
warn(`Warning: ${err instanceof Error ? err.message : String(err)}`);
|
|
295
|
+
counts.failed += adapters.length;
|
|
204
296
|
continue;
|
|
205
297
|
}
|
|
206
298
|
for (const adapter of adapters) {
|
|
207
299
|
const outcome = installSkillForAgent(skill, adapter, source, record);
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
300
|
+
result.outcomes.push(outcome);
|
|
301
|
+
const agent = outcome.agent;
|
|
302
|
+
for (const kept of outcome.keptReferences ?? []) {
|
|
303
|
+
note(`${agent}: ${kept} was edited locally — skipping (delete it to reinstall)`);
|
|
304
|
+
}
|
|
305
|
+
for (const linked of outcome.symlinkedReferences ?? []) {
|
|
306
|
+
warn(`${agent}: ${linked} is a symlink — skipping so the file it points to is not overwritten`);
|
|
307
|
+
}
|
|
308
|
+
for (const removed of outcome.removedReferences ?? []) {
|
|
309
|
+
note(`${agent}: removed ${removed} (no longer part of the skill)`);
|
|
310
|
+
}
|
|
311
|
+
for (const kept of outcome.keptStaleReferences ?? []) {
|
|
312
|
+
note(`${agent}: ${kept} is no longer part of the skill but was edited locally — leaving it in place`);
|
|
212
313
|
}
|
|
213
314
|
switch (outcome.status) {
|
|
214
315
|
case "wrote":
|
|
215
316
|
if (!options.silent)
|
|
216
|
-
console.log(` ${
|
|
217
|
-
|
|
317
|
+
console.log(` ${agent}: wrote ${outcome.relativePath}`);
|
|
318
|
+
counts.written++;
|
|
218
319
|
break;
|
|
219
320
|
case "updated":
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
321
|
+
if (outcome.backupPath) {
|
|
322
|
+
warn(`${agent}: replaced ${outcome.relativePath} with the current skill (previous copy saved to ${outcome.backupPath})`);
|
|
323
|
+
} else if (!options.silent) {
|
|
324
|
+
console.log(` ${agent}: updated ${outcome.relativePath} to the current skill`);
|
|
223
325
|
}
|
|
224
|
-
updated++;
|
|
326
|
+
counts.updated++;
|
|
225
327
|
break;
|
|
226
328
|
case "current":
|
|
227
|
-
|
|
329
|
+
counts.current++;
|
|
228
330
|
break;
|
|
229
331
|
case "exists":
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
332
|
+
note(`${agent}: ${outcome.relativePath} was edited locally — skipping (delete it to reinstall)`);
|
|
333
|
+
counts.kept++;
|
|
334
|
+
break;
|
|
335
|
+
case "symlink":
|
|
336
|
+
warn(`${agent}: skipped ${outcome.relativePath} because ${outcome.symlinkPath} is a symlink ` + "(install never writes through a link, which could overwrite files outside this project)");
|
|
337
|
+
counts.skipped++;
|
|
234
338
|
break;
|
|
235
339
|
case "failed":
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
failed++;
|
|
340
|
+
warn(`${agent}: failed to write ${outcome.relativePath} — ${outcome.error}`);
|
|
341
|
+
counts.failed++;
|
|
240
342
|
break;
|
|
241
343
|
}
|
|
242
344
|
}
|
|
@@ -245,21 +347,14 @@ async function installRules(detectedAgents, options = {}) {
|
|
|
245
347
|
try {
|
|
246
348
|
writeInstallRecord(record);
|
|
247
349
|
} catch (err) {
|
|
248
|
-
|
|
249
|
-
console.error(` Could not save ${INSTALL_RECORD_PATH} — ${err instanceof Error ? err.message : String(err)}. ` + "A later moda init may overwrite local edits to these skill files.");
|
|
250
|
-
}
|
|
350
|
+
warn(`Could not save ${INSTALL_RECORD_PATH} — ${err instanceof Error ? err.message : String(err)}. ` + "A later moda init may overwrite local edits to these skill files.");
|
|
251
351
|
}
|
|
252
352
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
].filter(Boolean).join(", ");
|
|
259
|
-
if (summary) {
|
|
260
|
-
if (!options.silent)
|
|
261
|
-
console.log(` Done — ${summary}`);
|
|
262
|
-
}
|
|
353
|
+
result.status = counts.failed > 0 ? "failed" : warnings.length > 0 ? "warning" : "ok";
|
|
354
|
+
const summary = summarizeInstall(result);
|
|
355
|
+
if (summary && !options.silent)
|
|
356
|
+
console.log(` Done — ${summary}`);
|
|
357
|
+
return result;
|
|
263
358
|
}
|
|
264
359
|
|
|
265
360
|
// src/init/mcp.ts
|
|
@@ -293,7 +388,7 @@ import {
|
|
|
293
388
|
readdirSync as readdirSync2,
|
|
294
389
|
readFileSync as readFileSync2,
|
|
295
390
|
rmdirSync,
|
|
296
|
-
unlinkSync,
|
|
391
|
+
unlinkSync as unlinkSync2,
|
|
297
392
|
writeFileSync as writeFileSync2
|
|
298
393
|
} from "node:fs";
|
|
299
394
|
import { dirname as dirname3, isAbsolute, join as join4, normalize, relative, resolve } from "node:path";
|
|
@@ -376,7 +471,7 @@ function pruneManagedSkillPaths(cwd, managedPaths, log) {
|
|
|
376
471
|
if (!absolutePath || !existsSync3(absolutePath))
|
|
377
472
|
continue;
|
|
378
473
|
try {
|
|
379
|
-
|
|
474
|
+
unlinkSync2(absolutePath);
|
|
380
475
|
pruned++;
|
|
381
476
|
log(` pruned ${safePath}`);
|
|
382
477
|
pruneEmptyClaudeSkillDir(cwd, safePath);
|
|
@@ -505,4 +600,4 @@ function yamlQuote(value) {
|
|
|
505
600
|
return JSON.stringify(value);
|
|
506
601
|
}
|
|
507
602
|
|
|
508
|
-
export { packageAssetPath, claudeSkillToCursorRule, installRules, detectAgents, SKILL_SOURCE_HARNESS, SKILL_SOURCE_INTEGRATION_BUNDLED, readSkillsManifest, writeSkillsManifest, upsertSkillManifestRecord, pruneManagedSkillPaths };
|
|
603
|
+
export { packageAssetPath, claudeSkillToCursorRule, summarizeInstall, installRules, detectAgents, SKILL_SOURCE_HARNESS, SKILL_SOURCE_INTEGRATION_BUNDLED, readSkillsManifest, writeSkillsManifest, upsertSkillManifestRecord, pruneManagedSkillPaths };
|
package/dist/cli.js
CHANGED
|
@@ -1853,7 +1853,7 @@ async function runPromptsCommand(...args) {
|
|
|
1853
1853
|
return (await import("./prompts-1tkhrxkq.js")).runPromptsCommand(...args);
|
|
1854
1854
|
}
|
|
1855
1855
|
async function runSkillsCommand(...args) {
|
|
1856
|
-
return (await import("./skills-
|
|
1856
|
+
return (await import("./skills-gejdvjak.js")).runSkillsCommand(...args);
|
|
1857
1857
|
}
|
|
1858
1858
|
async function runFixCommand(...args) {
|
|
1859
1859
|
return (await import("./fix-9fmy6p2x.js")).runFixCommand(...args);
|
|
@@ -1865,7 +1865,7 @@ async function runHarnessCommand(...args) {
|
|
|
1865
1865
|
return (await import("./harness-xvg6ws6x.js")).runHarnessCommand(...args);
|
|
1866
1866
|
}
|
|
1867
1867
|
async function runSyncCommand(...args) {
|
|
1868
|
-
return (await import("./sync-
|
|
1868
|
+
return (await import("./sync-57ercmh9.js")).runSyncCommand(...args);
|
|
1869
1869
|
}
|
|
1870
1870
|
async function runRegistryCommand(...args) {
|
|
1871
1871
|
return (await import("./registry-atqyb5fa.js")).runRegistryCommand(...args);
|
|
@@ -4473,7 +4473,9 @@ function initAgentWriteOptions(result) {
|
|
|
4473
4473
|
warnings: [
|
|
4474
4474
|
...manualActions.filter((action) => action.reason).map((action) => `${action.title}: ${action.reason}`),
|
|
4475
4475
|
...result.promptSync?.status === "failed" ? [`Prompt sync failed: ${result.promptSync.error}. Retry with 'moda prompts sync'.`] : [],
|
|
4476
|
-
...result.skillSync?.status === "failed" ? [`Skill sync failed: ${result.skillSync.error}. Retry with 'moda skills sync'.`] : []
|
|
4476
|
+
...result.skillSync?.status === "failed" ? [`Skill sync failed: ${result.skillSync.error}. Retry with 'moda skills sync'.`] : [],
|
|
4477
|
+
...result.skillSync?.warnings ?? [],
|
|
4478
|
+
...(result.skillInstall?.warnings ?? []).map((warning) => `moda-cli skill install: ${warning}`)
|
|
4477
4479
|
]
|
|
4478
4480
|
};
|
|
4479
4481
|
}
|
|
@@ -4533,7 +4535,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
4533
4535
|
resetApiRequestCountBeforeRun: true,
|
|
4534
4536
|
telemetry: "result-and-error",
|
|
4535
4537
|
handler: async (context) => {
|
|
4536
|
-
const { runInit } = await import("./index-
|
|
4538
|
+
const { runInit } = await import("./index-1wbjz3jc.js");
|
|
4537
4539
|
if (context.outputMode === "agent-stream") {
|
|
4538
4540
|
context.output.writeEvent({
|
|
4539
4541
|
event: "started",
|
|
@@ -4553,7 +4555,10 @@ var commandRegistry = createCommandRegistry([
|
|
|
4553
4555
|
}
|
|
4554
4556
|
});
|
|
4555
4557
|
if (context.outputMode !== "human") {
|
|
4556
|
-
context.output.writeData(result,
|
|
4558
|
+
context.output.writeData(result, {
|
|
4559
|
+
...initAgentWriteOptions(result),
|
|
4560
|
+
...result.skillInstall?.status === "failed" ? { status: "degraded" } : {}
|
|
4561
|
+
});
|
|
4557
4562
|
}
|
|
4558
4563
|
return { exitCode: 0, apiKey: result.apiKey };
|
|
4559
4564
|
}
|
|
@@ -42,10 +42,11 @@ import {
|
|
|
42
42
|
bundledSkillsDir,
|
|
43
43
|
installIntegrationSkill,
|
|
44
44
|
readBundledIndex
|
|
45
|
-
} from "./cli-
|
|
45
|
+
} from "./cli-frhm2wz7.js";
|
|
46
46
|
import {
|
|
47
|
+
agentsMdProblem,
|
|
47
48
|
runSkillSync
|
|
48
|
-
} from "./cli-
|
|
49
|
+
} from "./cli-0ef72tqx.js";
|
|
49
50
|
import {
|
|
50
51
|
createCommandContext
|
|
51
52
|
} from "./cli-569hj0bj.js";
|
|
@@ -62,8 +63,9 @@ import {
|
|
|
62
63
|
} from "./cli-5ke3xe6y.js";
|
|
63
64
|
import {
|
|
64
65
|
detectAgents,
|
|
65
|
-
installRules
|
|
66
|
-
|
|
66
|
+
installRules,
|
|
67
|
+
summarizeInstall
|
|
68
|
+
} from "./cli-8dtnwp9r.js";
|
|
67
69
|
import"./cli-0v6na3yp.js";
|
|
68
70
|
|
|
69
71
|
// src/init/index.ts
|
|
@@ -2022,13 +2024,33 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2022
2024
|
}
|
|
2023
2025
|
emit("apply", "Applying approved setup changes.");
|
|
2024
2026
|
const laneBoard = startBoard();
|
|
2027
|
+
const setupWarnings = [];
|
|
2028
|
+
const addSetupWarning = (message) => {
|
|
2029
|
+
setupWarnings.push(message);
|
|
2030
|
+
emit("apply", message, "warning");
|
|
2031
|
+
};
|
|
2025
2032
|
const agents = detectAgents();
|
|
2026
2033
|
laneBoard?.startLane("agent-skills", "Agent skills");
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2034
|
+
const skillInstallResult = agents.length > 0 ? await installRules(agents, { yes: true, silent: true }) : undefined;
|
|
2035
|
+
const skillInstall = {
|
|
2036
|
+
status: skillInstallResult?.status ?? "skipped",
|
|
2037
|
+
agents: skillInstallResult?.agents ?? [],
|
|
2038
|
+
summary: skillInstallResult ? summarizeInstall(skillInstallResult) : "",
|
|
2039
|
+
warnings: skillInstallResult?.warnings ?? []
|
|
2040
|
+
};
|
|
2041
|
+
skillInstall.warnings.forEach(addSetupWarning);
|
|
2042
|
+
if (skillInstall.agents.length === 0) {
|
|
2043
|
+
laneBoard?.complete("agent-skills", agents.length > 0 ? "no supported coding agents detected" : "no coding agents detected");
|
|
2044
|
+
} else if (skillInstall.status === "ok") {
|
|
2045
|
+
laneBoard?.complete("agent-skills", `moda-cli skill installed for ${skillInstall.agents.join(", ")}`);
|
|
2030
2046
|
} else {
|
|
2031
|
-
|
|
2047
|
+
const counts = skillInstallResult?.counts;
|
|
2048
|
+
const nothingInstalled = counts !== undefined && counts.written + counts.updated + counts.current === 0;
|
|
2049
|
+
const note = `moda-cli skill: ${skillInstall.summary}; ${skillInstall.warnings.length} warning(s), details below`;
|
|
2050
|
+
if (skillInstall.status === "failed" && nothingInstalled)
|
|
2051
|
+
laneBoard?.fail("agent-skills", note);
|
|
2052
|
+
else
|
|
2053
|
+
laneBoard?.warn("agent-skills", note);
|
|
2032
2054
|
}
|
|
2033
2055
|
await initPrompts({ silent: true });
|
|
2034
2056
|
let promptSyncOutcome;
|
|
@@ -2046,8 +2068,19 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2046
2068
|
laneBoard?.startLane("skills", "Skills");
|
|
2047
2069
|
try {
|
|
2048
2070
|
const skillSync = await runSkillSync({}, { profile });
|
|
2049
|
-
|
|
2050
|
-
|
|
2071
|
+
const agentsMdWarning = agentsMdProblem(skillSync.agentsMd);
|
|
2072
|
+
skillSyncOutcome = {
|
|
2073
|
+
status: "ok",
|
|
2074
|
+
synced: skillSync.synced.map((skill) => skill.key),
|
|
2075
|
+
...agentsMdWarning ? { warnings: [agentsMdWarning] } : {}
|
|
2076
|
+
};
|
|
2077
|
+
const syncedNote = skillSync.synced.length > 0 ? `${skillSync.synced.length} skill(s) synced` : "no local skills to sync yet";
|
|
2078
|
+
if (agentsMdWarning) {
|
|
2079
|
+
addSetupWarning(agentsMdWarning);
|
|
2080
|
+
laneBoard?.warn("skills", `${syncedNote}; AGENTS.md block not updated, details below`);
|
|
2081
|
+
} else {
|
|
2082
|
+
laneBoard?.complete("skills", syncedNote);
|
|
2083
|
+
}
|
|
2051
2084
|
} catch (err) {
|
|
2052
2085
|
const message = err instanceof Error ? err.message : String(err);
|
|
2053
2086
|
skillSyncOutcome = { status: "failed", synced: [], error: message };
|
|
@@ -2160,11 +2193,13 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2160
2193
|
}
|
|
2161
2194
|
}
|
|
2162
2195
|
board?.stop();
|
|
2196
|
+
for (const message of setupWarnings)
|
|
2197
|
+
warn(renderInitNote(message, "warning"));
|
|
2163
2198
|
if (!harnessDeferred)
|
|
2164
2199
|
markApplied(setupPlan, "harness");
|
|
2165
2200
|
phases.push(harnessPhase);
|
|
2166
|
-
emit("apply", "Approved changes applied.", "completed");
|
|
2167
|
-
phases.push({ id: "apply", title: "Set up", status: "completed" });
|
|
2201
|
+
emit("apply", "Approved changes applied.", setupWarnings.length > 0 ? "warning" : "completed");
|
|
2202
|
+
phases.push(setupWarnings.length > 0 ? { id: "apply", title: "Set up", status: "warning", details: `${setupWarnings.length} setup warning(s); see warnings.` } : { id: "apply", title: "Set up", status: "completed" });
|
|
2168
2203
|
const offlineValidation = isTruthyFlag(flagValue(flags, "offline"));
|
|
2169
2204
|
log(renderInitStep(5, 5, "Verify", offlineValidation ? "Running local doctor checks so the setup leaves a clear state." : "Running Moda doctor online as a tenant health check (not proof of SDK integration)."));
|
|
2170
2205
|
emit("validate", `Running Moda doctor diagnostics (${offlineValidation ? "offline" : "online"}).`);
|
|
@@ -2205,6 +2240,7 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2205
2240
|
setupPlan,
|
|
2206
2241
|
promptSync: promptSyncOutcome,
|
|
2207
2242
|
skillSync: skillSyncOutcome,
|
|
2243
|
+
skillInstall,
|
|
2208
2244
|
...sdkIntegration ? { sdk_integration: sdkIntegration } : {}
|
|
2209
2245
|
};
|
|
2210
2246
|
}
|
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
pullSkills,
|
|
4
4
|
runSkillSync,
|
|
5
5
|
runSkillsCommand
|
|
6
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-0ef72tqx.js";
|
|
7
7
|
import"./cli-569hj0bj.js";
|
|
8
8
|
import"./cli-cehskjde.js";
|
|
9
9
|
import"./cli-rqk6f6n6.js";
|
|
10
10
|
import"./cli-7ea02wca.js";
|
|
11
11
|
import"./cli-5ke3xe6y.js";
|
|
12
|
-
import"./cli-
|
|
12
|
+
import"./cli-8dtnwp9r.js";
|
|
13
13
|
import"./cli-0v6na3yp.js";
|
|
14
14
|
export {
|
|
15
15
|
runSkillsCommand,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import"./cli-97fnztpy.js";
|
|
9
9
|
import {
|
|
10
10
|
runSkillsCommand
|
|
11
|
-
} from "./cli-
|
|
11
|
+
} from "./cli-0ef72tqx.js";
|
|
12
12
|
import"./cli-569hj0bj.js";
|
|
13
13
|
import"./cli-cehskjde.js";
|
|
14
14
|
import"./cli-rqk6f6n6.js";
|
|
@@ -16,7 +16,7 @@ import"./cli-7ea02wca.js";
|
|
|
16
16
|
import {
|
|
17
17
|
CliInputError
|
|
18
18
|
} from "./cli-5ke3xe6y.js";
|
|
19
|
-
import"./cli-
|
|
19
|
+
import"./cli-8dtnwp9r.js";
|
|
20
20
|
import"./cli-0v6na3yp.js";
|
|
21
21
|
|
|
22
22
|
// src/sync.ts
|
package/package.json
CHANGED