deweyou-cli 0.2.1
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/CHANGELOG.md +18 -0
- package/README.md +221 -0
- package/dist/cache-CBEKVQeD.mjs +231 -0
- package/dist/context-BYyhbv5D.mjs +207 -0
- package/dist/deweyou.mjs +124 -0
- package/dist/doctor-Do7WDnrg.mjs +295 -0
- package/dist/init-ClloZBVB.mjs +353 -0
- package/dist/prompts-DVRcV560.mjs +100 -0
- package/package.json +40 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { cachePaths, n as writeJson, t as readJson } from "./cache-CBEKVQeD.mjs";
|
|
2
|
+
import { cp, lstat, mkdir, readFile, realpath, rename, rm, symlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, relative } from "node:path";
|
|
5
|
+
//#region src/cli/agents-md.ts
|
|
6
|
+
const DEWEYOU_SECTION_START = "<!-- deweyou-agent:start -->";
|
|
7
|
+
const DEWEYOU_SECTION_END = "<!-- deweyou-agent:end -->";
|
|
8
|
+
const DEWEY_SECTION = `${DEWEYOU_SECTION_START}
|
|
9
|
+
## Dewey Workflow
|
|
10
|
+
|
|
11
|
+
This repository uses Dewey's personal agent workflow. Inspect \`.agents/\` before making changes, then run \`deweyou-cli agent context --format markdown\` and follow the returned rules, skill index, asset paths, and runtime notices.
|
|
12
|
+
${DEWEYOU_SECTION_END}`;
|
|
13
|
+
async function upsertAgentsSection(repoRoot) {
|
|
14
|
+
const path = join(repoRoot, "AGENTS.md");
|
|
15
|
+
const next = upsertSection(await readAgentsFile(path));
|
|
16
|
+
await writeFile(path, next);
|
|
17
|
+
return next;
|
|
18
|
+
}
|
|
19
|
+
async function readAgentsFile(path) {
|
|
20
|
+
try {
|
|
21
|
+
return await readFile(path, "utf8");
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
24
|
+
if (error.code === "ENOENT") return "";
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function upsertSection(contents) {
|
|
29
|
+
const markedSection = new RegExp(`${escapeRegex(DEWEYOU_SECTION_START)}[\\s\\S]*?${escapeRegex(DEWEYOU_SECTION_END)}`);
|
|
30
|
+
if (markedSection.test(contents)) return ensureTrailingNewline(contents.replace(markedSection, DEWEY_SECTION));
|
|
31
|
+
const trimmed = contents.trimEnd();
|
|
32
|
+
if (!trimmed) return `${DEWEY_SECTION}\n`;
|
|
33
|
+
return `${trimmed}\n\n${DEWEY_SECTION}\n`;
|
|
34
|
+
}
|
|
35
|
+
function escapeRegex(value) {
|
|
36
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
37
|
+
}
|
|
38
|
+
function ensureTrailingNewline(value) {
|
|
39
|
+
return value.endsWith("\n") ? value : `${value}\n`;
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/cli/init.ts
|
|
43
|
+
const VALID_MODES = new Set([
|
|
44
|
+
"link",
|
|
45
|
+
"copy",
|
|
46
|
+
"pointer"
|
|
47
|
+
]);
|
|
48
|
+
const SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
49
|
+
async function initRepo({ repoRoot = process.cwd(), homeDir = homedir(), mode = "link", selected, force = false, dryRun = false } = {}) {
|
|
50
|
+
if (!selected) throw new Error("selected assets are required");
|
|
51
|
+
validateMode(mode);
|
|
52
|
+
const paths = cachePaths({ homeDir });
|
|
53
|
+
const registry = await readCachedRegistry(paths.assetsRoot);
|
|
54
|
+
const cacheManifest = await readCacheManifest(paths.manifestPath);
|
|
55
|
+
const assets = normalizeSelected(selected);
|
|
56
|
+
const agentsRoot = join(repoRoot, ".agents");
|
|
57
|
+
validateSelectedAssets(registry, assets);
|
|
58
|
+
const plan = await buildInitPlan({
|
|
59
|
+
repoRoot,
|
|
60
|
+
agentsRoot,
|
|
61
|
+
assetsRoot: paths.assetsRoot,
|
|
62
|
+
registry,
|
|
63
|
+
assets,
|
|
64
|
+
mode
|
|
65
|
+
});
|
|
66
|
+
if (mode !== "pointer") await validateDestinationSafety({
|
|
67
|
+
plan,
|
|
68
|
+
manifestPath: join(agentsRoot, "manifest.json"),
|
|
69
|
+
assetsRoot: paths.assetsRoot,
|
|
70
|
+
force
|
|
71
|
+
});
|
|
72
|
+
const manifest = {
|
|
73
|
+
mode,
|
|
74
|
+
source: cacheManifest.source,
|
|
75
|
+
cacheRoot: paths.assetsRoot,
|
|
76
|
+
assets,
|
|
77
|
+
assetSnapshot: snapshotSelectedAssetMetadata(registry, assets),
|
|
78
|
+
initializedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
79
|
+
};
|
|
80
|
+
if (dryRun) return {
|
|
81
|
+
dryRun: true,
|
|
82
|
+
mode,
|
|
83
|
+
source: cacheManifest.source,
|
|
84
|
+
cacheRoot: paths.assetsRoot,
|
|
85
|
+
assets,
|
|
86
|
+
assetSnapshot: snapshotSelectedAssetMetadata(registry, assets),
|
|
87
|
+
files: plan.files
|
|
88
|
+
};
|
|
89
|
+
await mkdir(agentsRoot, { recursive: true });
|
|
90
|
+
if (mode !== "pointer") await installAssets(plan.assets);
|
|
91
|
+
await writeJson(join(agentsRoot, "manifest.json"), manifest);
|
|
92
|
+
await upsertAgentsSection(repoRoot);
|
|
93
|
+
return manifest;
|
|
94
|
+
}
|
|
95
|
+
async function runInit(flags = {}, { promptForInit } = {}) {
|
|
96
|
+
const homeDir = flags.homeDir ?? homedir();
|
|
97
|
+
const repoRoot = flags.repoRoot ?? process.cwd();
|
|
98
|
+
const registry = await readCachedRegistry(cachePaths({ homeDir }).assetsRoot);
|
|
99
|
+
const scripted = hasScriptedSelectionFlags(flags);
|
|
100
|
+
let mode = flags.mode ?? "link";
|
|
101
|
+
let selected;
|
|
102
|
+
validateMode(mode);
|
|
103
|
+
if (flags.yes && !scripted) throw new Error("--yes requires --all, --skills, or --rules");
|
|
104
|
+
if (scripted) selected = flags.all ? selectAll(registry) : {
|
|
105
|
+
skills: flags.skills ?? [],
|
|
106
|
+
rules: flags.rules ?? []
|
|
107
|
+
};
|
|
108
|
+
else {
|
|
109
|
+
const prompted = await (promptForInit ?? await loadPromptForInit())({
|
|
110
|
+
registry,
|
|
111
|
+
repoRoot,
|
|
112
|
+
mode: flags.mode
|
|
113
|
+
});
|
|
114
|
+
mode = prompted.mode;
|
|
115
|
+
selected = prompted.selected;
|
|
116
|
+
}
|
|
117
|
+
if (!hasSelectedAssets(selected)) throw new Error("No assets selected. Pass --all, --skills, or --rules, or run interactive setup.");
|
|
118
|
+
const manifest = await initRepo({
|
|
119
|
+
repoRoot,
|
|
120
|
+
mode,
|
|
121
|
+
selected,
|
|
122
|
+
force: flags.force ?? false,
|
|
123
|
+
dryRun: flags.dryRun ?? false,
|
|
124
|
+
homeDir
|
|
125
|
+
});
|
|
126
|
+
if (flags.dryRun) {
|
|
127
|
+
console.log("Dewey workflow init plan:");
|
|
128
|
+
for (const file of manifest.files) console.log(`- ${file}`);
|
|
129
|
+
} else console.log("Initialized Dewey workflow for this repository.");
|
|
130
|
+
return manifest;
|
|
131
|
+
}
|
|
132
|
+
async function loadPromptForInit() {
|
|
133
|
+
const { promptForInit } = await import("./prompts-DVRcV560.mjs");
|
|
134
|
+
return promptForInit;
|
|
135
|
+
}
|
|
136
|
+
async function readCachedRegistry(assetsRoot) {
|
|
137
|
+
try {
|
|
138
|
+
const registry = JSON.parse(await readFile(join(assetsRoot, "registry.json"), "utf8"));
|
|
139
|
+
return {
|
|
140
|
+
...registry,
|
|
141
|
+
assets: {
|
|
142
|
+
/* v8 ignore next -- legacy cache manifests may omit collections */
|
|
143
|
+
skills: registry.assets?.skills ?? {},
|
|
144
|
+
/* v8 ignore next -- legacy cache manifests may omit collections */
|
|
145
|
+
rules: registry.assets?.rules ?? {}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
} catch (error) {
|
|
149
|
+
/* v8 ignore next -- defensive guard for non-Node filesystem errors */
|
|
150
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
151
|
+
if (error.code === "ENOENT") throw new Error("Dewey asset cache is missing. Run `deweyou-cli agent update` first.");
|
|
152
|
+
/* v8 ignore next -- invalid JSON and non-missing read errors should bubble */
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function readCacheManifest(manifestPath) {
|
|
157
|
+
const manifest = await readJson(manifestPath, null);
|
|
158
|
+
if (!manifest?.source || typeof manifest.source.root !== "string") throw new Error("Dewey asset cache manifest is missing source metadata. Run `deweyou-cli agent update` first.");
|
|
159
|
+
return manifest;
|
|
160
|
+
}
|
|
161
|
+
function normalizeSelected(selected) {
|
|
162
|
+
return {
|
|
163
|
+
skills: [...selected.skills ?? []],
|
|
164
|
+
rules: [...selected.rules ?? []]
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function hasSelectedAssets(selected) {
|
|
168
|
+
if (!selected) return false;
|
|
169
|
+
return selected.skills.length > 0 || selected.rules.length > 0;
|
|
170
|
+
}
|
|
171
|
+
function hasScriptedSelectionFlags(flags) {
|
|
172
|
+
return Boolean(flags.all || flags.skills || flags.rules);
|
|
173
|
+
}
|
|
174
|
+
function validateMode(mode) {
|
|
175
|
+
if (typeof mode !== "string") throw new Error("mode must be one of link, copy, or pointer");
|
|
176
|
+
if (!VALID_MODES.has(mode)) throw new Error("mode must be one of link, copy, or pointer");
|
|
177
|
+
}
|
|
178
|
+
function validateSelectedAssets(registry, selected) {
|
|
179
|
+
for (const skill of selected.skills) {
|
|
180
|
+
if (!registry.assets.skills[skill]) throw new Error(`Unknown Dewey skill: ${skill}`);
|
|
181
|
+
validateAssetId(skill, "skill");
|
|
182
|
+
}
|
|
183
|
+
for (const rule of selected.rules) {
|
|
184
|
+
if (!registry.assets.rules[rule]) throw new Error(`Unknown Dewey rule: ${rule}`);
|
|
185
|
+
validateAssetId(rule, "rule");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function validateAssetId(id, kind) {
|
|
189
|
+
if (!SAFE_ID.test(id)) throw new Error(`Dewey ${kind} id must be kebab-case: ${id}`);
|
|
190
|
+
}
|
|
191
|
+
function selectAll(registry) {
|
|
192
|
+
return {
|
|
193
|
+
skills: Object.keys(registry.assets.skills),
|
|
194
|
+
rules: Object.keys(registry.assets.rules)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function snapshotSelectedAssetMetadata(registry, selected) {
|
|
198
|
+
return {
|
|
199
|
+
skills: Object.fromEntries(selected.skills.map((name) => [name, pickAssetMetadata(registry.assets.skills[name])])),
|
|
200
|
+
rules: Object.fromEntries(selected.rules.map((name) => [name, pickAssetMetadata(registry.assets.rules[name])]))
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function pickAssetMetadata(asset) {
|
|
204
|
+
return {
|
|
205
|
+
description: asset.description,
|
|
206
|
+
hash: asset.hash
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
async function buildInitPlan({ repoRoot, agentsRoot, assetsRoot, registry, assets, mode }) {
|
|
210
|
+
const assetPlans = mode === "pointer" ? [] : await Promise.all([...assets.skills.map((id) => buildAssetPlan({
|
|
211
|
+
kind: "skill",
|
|
212
|
+
id,
|
|
213
|
+
source: join(assetsRoot, registry.assets.skills[id].path),
|
|
214
|
+
destination: join(agentsRoot, "skills", id),
|
|
215
|
+
mode
|
|
216
|
+
})), ...assets.rules.map((id) => buildAssetPlan({
|
|
217
|
+
kind: "rule",
|
|
218
|
+
id,
|
|
219
|
+
source: join(assetsRoot, registry.assets.rules[id].path),
|
|
220
|
+
destination: join(agentsRoot, "rules", `${id}.md`),
|
|
221
|
+
mode
|
|
222
|
+
}))]);
|
|
223
|
+
return {
|
|
224
|
+
assets: assetPlans,
|
|
225
|
+
files: [
|
|
226
|
+
...assetPlans.map((asset) => asset.destination),
|
|
227
|
+
join(agentsRoot, "manifest.json"),
|
|
228
|
+
join(repoRoot, "AGENTS.md")
|
|
229
|
+
]
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
async function buildAssetPlan({ kind, id, source, destination, mode }) {
|
|
233
|
+
return {
|
|
234
|
+
kind,
|
|
235
|
+
id,
|
|
236
|
+
source: await realpath(source),
|
|
237
|
+
destination,
|
|
238
|
+
mode
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async function validateDestinationSafety({ plan, manifestPath, assetsRoot, force }) {
|
|
242
|
+
const previousManifest = await readJson(manifestPath, null);
|
|
243
|
+
const safeManifestDestinations = new Set(manifestDestinations(dirname(manifestPath), previousManifest));
|
|
244
|
+
for (const asset of plan.assets) await validateDestination(asset, {
|
|
245
|
+
assetsRoot,
|
|
246
|
+
force,
|
|
247
|
+
safeManifestDestinations
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
function manifestDestinations(agentsRoot, manifest) {
|
|
251
|
+
if (!manifest?.assets) return [];
|
|
252
|
+
return [...(manifest.assets.skills ?? []).map((id) => join(agentsRoot, "skills", id)), ...(manifest.assets.rules ?? []).map((id) => join(agentsRoot, "rules", `${id}.md`))];
|
|
253
|
+
}
|
|
254
|
+
async function validateDestination(asset, { assetsRoot, force, safeManifestDestinations }) {
|
|
255
|
+
let destinationStat;
|
|
256
|
+
try {
|
|
257
|
+
destinationStat = await lstat(asset.destination);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
/* v8 ignore next -- defensive guard for non-Node filesystem errors */
|
|
260
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
261
|
+
if (error.code === "ENOENT") return;
|
|
262
|
+
/* v8 ignore next -- non-missing lstat errors should bubble to the caller */
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
if (!force) throw new Error(`${asset.destination} already exists. Use --force to replace Dewey-managed asset files.`);
|
|
266
|
+
if (safeManifestDestinations.has(asset.destination) || destinationStat.isSymbolicLink() && await pointsIntoAssetsRoot(asset.destination, assetsRoot)) return;
|
|
267
|
+
throw new Error(`Refusing to replace non-Dewey-managed destination: ${asset.destination}`);
|
|
268
|
+
}
|
|
269
|
+
async function pointsIntoAssetsRoot(destination, assetsRoot) {
|
|
270
|
+
let destinationTarget;
|
|
271
|
+
try {
|
|
272
|
+
destinationTarget = await realpath(destination);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
/* v8 ignore next -- defensive guard for non-Node filesystem errors */
|
|
275
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
276
|
+
if (error.code === "ENOENT") return false;
|
|
277
|
+
/* v8 ignore next -- non-missing realpath errors should bubble to the caller */
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
const pathFromRoot = relative(await realpath(assetsRoot), destinationTarget);
|
|
281
|
+
return pathFromRoot === "" || !pathFromRoot.startsWith("..") && !isAbsolute(pathFromRoot);
|
|
282
|
+
}
|
|
283
|
+
async function installAssets(assets) {
|
|
284
|
+
for (const asset of assets) await installAsset(asset);
|
|
285
|
+
}
|
|
286
|
+
async function installAsset({ source, destination, mode }) {
|
|
287
|
+
const parent = dirname(destination);
|
|
288
|
+
const tempDestination = tempSibling(destination, "tmp");
|
|
289
|
+
const backupDestination = tempSibling(destination, "backup");
|
|
290
|
+
await mkdir(parent, { recursive: true });
|
|
291
|
+
try {
|
|
292
|
+
await rm(tempDestination, {
|
|
293
|
+
recursive: true,
|
|
294
|
+
force: true
|
|
295
|
+
});
|
|
296
|
+
await rm(backupDestination, {
|
|
297
|
+
recursive: true,
|
|
298
|
+
force: true
|
|
299
|
+
});
|
|
300
|
+
if (mode === "link") await symlink(source, tempDestination);
|
|
301
|
+
else await cp(source, tempDestination, {
|
|
302
|
+
recursive: true,
|
|
303
|
+
force: false,
|
|
304
|
+
errorOnExist: true
|
|
305
|
+
});
|
|
306
|
+
const hasExistingDestination = await pathExists(destination);
|
|
307
|
+
if (hasExistingDestination) await rename(destination, backupDestination);
|
|
308
|
+
try {
|
|
309
|
+
await rename(tempDestination, destination);
|
|
310
|
+
} catch (error) {
|
|
311
|
+
/* v8 ignore next -- defensive guard for non-Error filesystem failures */
|
|
312
|
+
if (!(error instanceof Error)) throw error;
|
|
313
|
+
if (hasExistingDestination) await restoreBackup(backupDestination, destination, error);
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
if (hasExistingDestination) await rm(backupDestination, {
|
|
317
|
+
recursive: true,
|
|
318
|
+
force: true
|
|
319
|
+
});
|
|
320
|
+
} catch (error) {
|
|
321
|
+
await rm(tempDestination, {
|
|
322
|
+
recursive: true,
|
|
323
|
+
force: true
|
|
324
|
+
});
|
|
325
|
+
/* v8 ignore next -- cleanup is deterministic; original error is rethrown */
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function tempSibling(destination, label) {
|
|
330
|
+
return join(dirname(destination), `.${basename(destination)}.${label}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
331
|
+
}
|
|
332
|
+
async function pathExists(path) {
|
|
333
|
+
try {
|
|
334
|
+
await lstat(path);
|
|
335
|
+
return true;
|
|
336
|
+
} catch (error) {
|
|
337
|
+
/* v8 ignore next -- defensive guard for non-Node filesystem errors */
|
|
338
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
339
|
+
if (error.code === "ENOENT") return false;
|
|
340
|
+
/* v8 ignore next -- non-missing lstat errors should bubble to the caller */
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async function restoreBackup(backupDestination, destination, originalError) {
|
|
345
|
+
try {
|
|
346
|
+
await rename(backupDestination, destination);
|
|
347
|
+
} catch (restoreError) {
|
|
348
|
+
/* v8 ignore next -- restore failures are attached to the original error */
|
|
349
|
+
originalError.restoreError = restoreError;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
//#endregion
|
|
353
|
+
export { runInit };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { cancel, confirm, intro, isCancel, multiselect, note, select } from "@clack/prompts";
|
|
2
|
+
//#region src/cli/prompts.ts
|
|
3
|
+
const SETUP_MODES = [
|
|
4
|
+
{
|
|
5
|
+
value: "link",
|
|
6
|
+
label: "link",
|
|
7
|
+
hint: "Symlink assets from the Dewey cache."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
value: "copy",
|
|
11
|
+
label: "copy",
|
|
12
|
+
hint: "Copy asset files into this repository."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
value: "pointer",
|
|
16
|
+
label: "pointer",
|
|
17
|
+
hint: "Write only the manifest and AGENTS.md pointers."
|
|
18
|
+
}
|
|
19
|
+
];
|
|
20
|
+
const ASSET_SCOPES = [
|
|
21
|
+
{
|
|
22
|
+
value: "all",
|
|
23
|
+
label: "all",
|
|
24
|
+
hint: "Enable every cached skill and rule."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
value: "custom",
|
|
28
|
+
label: "custom",
|
|
29
|
+
hint: "Choose skills and rules individually."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
value: "skills",
|
|
33
|
+
label: "skills only",
|
|
34
|
+
hint: "Choose skills without installing rules."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
value: "rules",
|
|
38
|
+
label: "rules only",
|
|
39
|
+
hint: "Choose rules without installing skills."
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
async function promptForInit({ registry, repoRoot, mode }) {
|
|
43
|
+
intro("Dewey Agent Setup");
|
|
44
|
+
note(repoRoot, "Repository");
|
|
45
|
+
const selectedMode = mode ?? await promptOrExit(select({
|
|
46
|
+
message: "Select setup mode",
|
|
47
|
+
options: SETUP_MODES
|
|
48
|
+
}));
|
|
49
|
+
const selected = await selectAssets({
|
|
50
|
+
registry,
|
|
51
|
+
scope: await promptOrExit(select({
|
|
52
|
+
message: "Select asset scope",
|
|
53
|
+
options: ASSET_SCOPES
|
|
54
|
+
}))
|
|
55
|
+
});
|
|
56
|
+
if (!await promptOrExit(confirm({ message: `Enable ${selected.skills.length} skill(s) and ${selected.rules.length} rule(s) using ${selectedMode} mode?` }))) exitCancelled();
|
|
57
|
+
return {
|
|
58
|
+
mode: selectedMode,
|
|
59
|
+
selected
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async function selectAssets({ registry, scope }) {
|
|
63
|
+
if (scope === "all") return {
|
|
64
|
+
skills: Object.keys(registry.assets.skills),
|
|
65
|
+
rules: Object.keys(registry.assets.rules)
|
|
66
|
+
};
|
|
67
|
+
const selected = {
|
|
68
|
+
skills: [],
|
|
69
|
+
rules: []
|
|
70
|
+
};
|
|
71
|
+
if (scope === "custom" || scope === "skills") selected.skills = await promptOrExit(multiselect({
|
|
72
|
+
message: "Select skills",
|
|
73
|
+
options: assetOptions(registry.assets.skills),
|
|
74
|
+
required: false
|
|
75
|
+
}));
|
|
76
|
+
if (scope === "custom" || scope === "rules") selected.rules = await promptOrExit(multiselect({
|
|
77
|
+
message: "Select rules",
|
|
78
|
+
options: assetOptions(registry.assets.rules),
|
|
79
|
+
required: false
|
|
80
|
+
}));
|
|
81
|
+
return selected;
|
|
82
|
+
}
|
|
83
|
+
function assetOptions(assets) {
|
|
84
|
+
return Object.entries(assets).map(([name, asset]) => ({
|
|
85
|
+
value: name,
|
|
86
|
+
label: name,
|
|
87
|
+
hint: asset.description
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
async function promptOrExit(prompt) {
|
|
91
|
+
const value = await prompt;
|
|
92
|
+
if (isCancel(value)) exitCancelled();
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
function exitCancelled() {
|
|
96
|
+
cancel("Dewey agent setup cancelled.");
|
|
97
|
+
process.exit(0);
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { promptForInit };
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "deweyou-cli",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Dewey's personal agent workflow bootstrapper",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"README.md",
|
|
9
|
+
"CHANGELOG.md"
|
|
10
|
+
],
|
|
11
|
+
"bin": {
|
|
12
|
+
"deweyou-cli": "./dist/deweyou.mjs"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "vp pack src/bin/deweyou.ts --out-dir dist --clean",
|
|
16
|
+
"prepare": "npm run build",
|
|
17
|
+
"release:prepare": "tsx scripts/prepare-release.ts",
|
|
18
|
+
"test": "vp test run",
|
|
19
|
+
"test:coverage": "vitest run --coverage",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"start": "tsx src/bin/deweyou.ts"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@clack/prompts": "^0.11.0",
|
|
25
|
+
"js-yaml": "^4.1.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/js-yaml": "^4.0.9",
|
|
29
|
+
"@types/node": "^25.5.2",
|
|
30
|
+
"@vitest/coverage-v8": "4.1.5",
|
|
31
|
+
"typescript": "^6.0.2",
|
|
32
|
+
"tsx": "^4.21.0",
|
|
33
|
+
"vitest": "4.1.5",
|
|
34
|
+
"vite-plus": "^0.1.21"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=20"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT"
|
|
40
|
+
}
|