deweyou-cli 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.
- package/CHANGELOG.md +35 -0
- package/README.md +13 -1
- package/dist/deweyou.mjs +10 -4
- package/dist/{init-g2D-URoL.mjs → init-6406tZjR.mjs} +293 -27
- package/dist/prompts-DdaeRkBa.mjs +189 -0
- package/package.json +1 -1
- package/dist/prompts-DVRcV560.mjs +0 -100
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0 - 2026-05-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- add interactive rule install prompts
|
|
8
|
+
- wire rule installs into init
|
|
9
|
+
- add rule install adapters
|
|
10
|
+
- parse rule install init flags
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- refuse agents symlink writes
|
|
15
|
+
- harden rule install safety
|
|
16
|
+
- preserve explicit init mode
|
|
17
|
+
- preserve init flags in wizard
|
|
18
|
+
- omit claude preview without rules
|
|
19
|
+
- include agents preview for claude installs
|
|
20
|
+
- read inline project rules from cache
|
|
21
|
+
- forward init rule install flags
|
|
22
|
+
- handle Claude rule install validation
|
|
23
|
+
- preserve only AGENTS Claude symlink
|
|
24
|
+
- document rule install init flags
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- share managed markdown sections
|
|
29
|
+
|
|
30
|
+
### Documentation
|
|
31
|
+
|
|
32
|
+
- document rule install targets
|
|
33
|
+
## 0.3.1 - 2026-05-17
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- streamline agent skills
|
|
3
38
|
## 0.3.0 - 2026-05-17
|
|
4
39
|
|
|
5
40
|
### Added
|
package/README.md
CHANGED
|
@@ -91,6 +91,12 @@ Initializes the current repository with selected skills and rules.
|
|
|
91
91
|
deweyou-cli agent init
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
Usage:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
deweyou-cli agent init [--all] [--skills a,b] [--rules a,b] [--mode link|copy|pointer] [--scope project|global] [--tools codex,claude|all] [--rule-wiring reference|inline] [--yes] [--dry-run] [--force]
|
|
98
|
+
```
|
|
99
|
+
|
|
94
100
|
Without selection flags, this opens an interactive setup where you choose:
|
|
95
101
|
|
|
96
102
|
- install mode
|
|
@@ -101,7 +107,9 @@ Scripted examples:
|
|
|
101
107
|
|
|
102
108
|
```bash
|
|
103
109
|
deweyou-cli agent init --all --mode link --yes
|
|
104
|
-
deweyou-cli agent init --skills
|
|
110
|
+
deweyou-cli agent init --skills repo-memory,spec-driven-coding,git-delivery --rules code-style
|
|
111
|
+
deweyou-cli agent init --scope project --tools codex,claude --rules code-style --mode link
|
|
112
|
+
deweyou-cli agent init --scope global --tools all --rules code-style --rule-wiring reference --yes
|
|
105
113
|
deweyou-cli agent init --dry-run
|
|
106
114
|
```
|
|
107
115
|
|
|
@@ -182,6 +190,10 @@ AGENTS.md
|
|
|
182
190
|
`AGENTS.md` receives a managed Dewey section that points agents at the selected
|
|
183
191
|
workflow context. Existing content outside that managed section is preserved.
|
|
184
192
|
|
|
193
|
+
Project installs write repository instruction files such as `AGENTS.md` and
|
|
194
|
+
`CLAUDE.md`. Global installs write user-level instruction files such as
|
|
195
|
+
`~/.codex/AGENTS.md` and `~/.claude/CLAUDE.md`.
|
|
196
|
+
|
|
185
197
|
## Safety Notes
|
|
186
198
|
|
|
187
199
|
- Run `deweyou-cli agent update` before `deweyou-cli agent init`.
|
package/dist/deweyou.mjs
CHANGED
|
@@ -11,7 +11,10 @@ const VALUE_FLAGS = new Set([
|
|
|
11
11
|
"mode",
|
|
12
12
|
"skills",
|
|
13
13
|
"rules",
|
|
14
|
-
"format"
|
|
14
|
+
"format",
|
|
15
|
+
"scope",
|
|
16
|
+
"tools",
|
|
17
|
+
"rule-wiring"
|
|
15
18
|
]);
|
|
16
19
|
const FLAGS_BY_COMMAND = {
|
|
17
20
|
init: new Set([
|
|
@@ -19,6 +22,9 @@ const FLAGS_BY_COMMAND = {
|
|
|
19
22
|
"skills",
|
|
20
23
|
"rules",
|
|
21
24
|
"mode",
|
|
25
|
+
"scope",
|
|
26
|
+
"tools",
|
|
27
|
+
"rule-wiring",
|
|
22
28
|
"yes",
|
|
23
29
|
"dry-run",
|
|
24
30
|
"force"
|
|
@@ -69,7 +75,7 @@ function isAllowedForCommand(command, name) {
|
|
|
69
75
|
return FLAGS_BY_COMMAND[command]?.has(name) ?? false;
|
|
70
76
|
}
|
|
71
77
|
function parseValue(name, value) {
|
|
72
|
-
if (name === "skills" || name === "rules") return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
78
|
+
if (name === "skills" || name === "rules" || name === "tools") return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
73
79
|
return value;
|
|
74
80
|
}
|
|
75
81
|
function toCamel(value) {
|
|
@@ -81,7 +87,7 @@ async function main(argv) {
|
|
|
81
87
|
const parsed = parseArgs(argv);
|
|
82
88
|
if (parsed.topic !== "agent") printUsageAndThrow();
|
|
83
89
|
if (parsed.command === "init") {
|
|
84
|
-
const { runInit } = await import("./init-
|
|
90
|
+
const { runInit } = await import("./init-6406tZjR.mjs");
|
|
85
91
|
await runInit(parsed.flags);
|
|
86
92
|
return;
|
|
87
93
|
}
|
|
@@ -104,7 +110,7 @@ async function main(argv) {
|
|
|
104
110
|
}
|
|
105
111
|
function usage() {
|
|
106
112
|
return `Usage:
|
|
107
|
-
deweyou-cli agent init [--all] [--skills a,b] [--rules a,b] [--mode link|copy|pointer] [--yes] [--dry-run] [--force]
|
|
113
|
+
deweyou-cli agent init [--all] [--skills a,b] [--rules a,b] [--mode link|copy|pointer] [--scope project|global] [--tools codex,claude|all] [--rule-wiring reference|inline] [--yes] [--dry-run] [--force]
|
|
108
114
|
deweyou-cli agent update
|
|
109
115
|
deweyou-cli agent context [--format markdown|json]
|
|
110
116
|
deweyou-cli agent doctor`;
|
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
import { cachePaths, n as writeJson, t as readJson } from "./cache-rbXm6Wyh.mjs";
|
|
2
|
-
import { cp, lstat, mkdir, readFile, realpath, rename, rm, symlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { cp, lstat, mkdir, readFile, readlink, realpath, rename, rm, symlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { basename, dirname, isAbsolute, join, relative } from "node:path";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
|
+
//#region src/cli/managed-section.ts
|
|
6
|
+
function upsertManagedSection(contents, { start, end, body }) {
|
|
7
|
+
const section = `${start}\n${body.trimEnd()}\n${end}`;
|
|
8
|
+
const markedSection = new RegExp(`${escapeRegex(start)}[\\s\\S]*?${escapeRegex(end)}`);
|
|
9
|
+
if (markedSection.test(contents)) return ensureTrailingNewline(contents.replace(markedSection, section));
|
|
10
|
+
const trimmed = contents.trimEnd();
|
|
11
|
+
if (!trimmed) return `${section}\n`;
|
|
12
|
+
return `${trimmed}\n\n${section}\n`;
|
|
13
|
+
}
|
|
14
|
+
function escapeRegex(value) {
|
|
15
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16
|
+
}
|
|
17
|
+
function ensureTrailingNewline(value) {
|
|
18
|
+
return value.endsWith("\n") ? value : `${value}\n`;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
5
21
|
//#region src/cli/agents-md.ts
|
|
6
22
|
const DEWEYOU_SECTION_START = "<!-- deweyou-agent:start -->";
|
|
7
23
|
const DEWEYOU_SECTION_END = "<!-- deweyou-agent:end -->";
|
|
8
|
-
const
|
|
9
|
-
## Dewey Workflow
|
|
24
|
+
const DEWEY_SECTION_BODY = `## Dewey Workflow
|
|
10
25
|
|
|
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}`;
|
|
26
|
+
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.`;
|
|
13
27
|
async function upsertAgentsSection(repoRoot) {
|
|
14
28
|
const path = join(repoRoot, "AGENTS.md");
|
|
15
|
-
|
|
29
|
+
await validateAgentsWritePath(path);
|
|
30
|
+
const next = upsertManagedSection(await readAgentsFile(path), {
|
|
31
|
+
start: DEWEYOU_SECTION_START,
|
|
32
|
+
end: DEWEYOU_SECTION_END,
|
|
33
|
+
body: DEWEY_SECTION_BODY
|
|
34
|
+
});
|
|
16
35
|
await writeFile(path, next);
|
|
17
36
|
return next;
|
|
18
37
|
}
|
|
@@ -25,18 +44,160 @@ async function readAgentsFile(path) {
|
|
|
25
44
|
throw error;
|
|
26
45
|
}
|
|
27
46
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
async function validateAgentsWritePath(path) {
|
|
48
|
+
try {
|
|
49
|
+
if ((await lstat(path)).isSymbolicLink()) throw new Error(`Refusing to write Dewey workflow through symlink: ${path}`);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
52
|
+
if (error.code === "ENOENT") return;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
34
55
|
}
|
|
35
|
-
|
|
36
|
-
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/cli/rule-install.ts
|
|
58
|
+
const CODEX_START = "<!-- deweyou-codex-rules:start -->";
|
|
59
|
+
const CODEX_END = "<!-- deweyou-codex-rules:end -->";
|
|
60
|
+
const CLAUDE_START = "<!-- deweyou-claude-rules:start -->";
|
|
61
|
+
const CLAUDE_END = "<!-- deweyou-claude-rules:end -->";
|
|
62
|
+
async function planRuleInstall(input) {
|
|
63
|
+
validateRuleInstallInput(input);
|
|
64
|
+
if (input.selectedRules.length === 0) return {
|
|
65
|
+
files: [],
|
|
66
|
+
operations: []
|
|
67
|
+
};
|
|
68
|
+
const operations = [];
|
|
69
|
+
if (input.tools.includes("codex")) operations.push(await codexOperation(input));
|
|
70
|
+
if (input.tools.includes("claude")) {
|
|
71
|
+
const operation = await claudeOperation(input);
|
|
72
|
+
if (operation) operations.push(operation);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
files: operations.map((operation) => operation.path),
|
|
76
|
+
operations
|
|
77
|
+
};
|
|
37
78
|
}
|
|
38
|
-
function
|
|
39
|
-
|
|
79
|
+
async function applyRuleInstall(plan) {
|
|
80
|
+
for (const operation of plan.operations) {
|
|
81
|
+
await mkdir(dirname(operation.path), { recursive: true });
|
|
82
|
+
await validateInstructionWritePath(operation.path);
|
|
83
|
+
const next = upsertManagedSection(await readTextIfPresent(operation.path), operation);
|
|
84
|
+
await writeFile(operation.path, next);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function codexOperation(input) {
|
|
88
|
+
return {
|
|
89
|
+
path: input.scope === "project" ? join(input.repoRoot, "AGENTS.md") : join(input.homeDir, ".codex", "AGENTS.md"),
|
|
90
|
+
start: CODEX_START,
|
|
91
|
+
end: CODEX_END,
|
|
92
|
+
body: await renderRuleSection(input, "Codex")
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function claudeOperation(input) {
|
|
96
|
+
if (input.scope === "project") {
|
|
97
|
+
const claudePath = join(input.repoRoot, "CLAUDE.md");
|
|
98
|
+
if (await isSymlinkToAgentsMd(claudePath, input.repoRoot)) {
|
|
99
|
+
if (input.tools.includes("codex")) return null;
|
|
100
|
+
return {
|
|
101
|
+
path: join(input.repoRoot, "AGENTS.md"),
|
|
102
|
+
start: CLAUDE_START,
|
|
103
|
+
end: CLAUDE_END,
|
|
104
|
+
body: await renderRuleSection(input, "Claude Code")
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (input.tools.includes("codex") && !await exists(claudePath)) return {
|
|
108
|
+
path: claudePath,
|
|
109
|
+
start: CLAUDE_START,
|
|
110
|
+
end: CLAUDE_END,
|
|
111
|
+
body: "@AGENTS.md"
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
path: claudePath,
|
|
115
|
+
start: CLAUDE_START,
|
|
116
|
+
end: CLAUDE_END,
|
|
117
|
+
body: await renderRuleSection(input, "Claude Code")
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
path: join(input.homeDir, ".claude", "CLAUDE.md"),
|
|
122
|
+
start: CLAUDE_START,
|
|
123
|
+
end: CLAUDE_END,
|
|
124
|
+
body: await renderRuleSection(input, "Claude Code")
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function validateRuleInstallInput(input) {
|
|
128
|
+
if (input.scope !== "project" && input.scope !== "global") throw new Error(`Invalid rule install scope: ${input.scope}`);
|
|
129
|
+
for (const tool of input.tools) if (tool !== "codex" && tool !== "claude") throw new Error(`Invalid rule install tool: ${tool}`);
|
|
130
|
+
if (input.ruleWiring !== "reference" && input.ruleWiring !== "inline") throw new Error(`Invalid rule wiring: ${input.ruleWiring}`);
|
|
131
|
+
if (input.scope === "global") for (const rule of input.selectedRules) {
|
|
132
|
+
const path = requireRulePath(input, rule);
|
|
133
|
+
if (!isPathInside(input.cacheRoot, path)) throw new Error(`Global Dewey rule path must be inside cacheRoot for ${rule}: ${path}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function renderRuleSection(input, toolLabel) {
|
|
137
|
+
if (input.ruleWiring === "inline") return `## Dewey Rules for ${toolLabel}
|
|
138
|
+
|
|
139
|
+
Follow these selected Dewey rules:
|
|
140
|
+
|
|
141
|
+
${(await Promise.all(input.selectedRules.map(async (rule) => {
|
|
142
|
+
return `### ${rule}\n\n${await readFile(requireRulePath(input, rule), "utf8")}`;
|
|
143
|
+
}))).join("\n\n")}`;
|
|
144
|
+
return `## Dewey Rules for ${toolLabel}
|
|
145
|
+
|
|
146
|
+
Follow these selected Dewey rules. Read the referenced files before applying a rule:
|
|
147
|
+
|
|
148
|
+
${input.selectedRules.map((rule) => `- ${rule}: ${displayPath(input, requireRulePath(input, rule))}`).join("\n")}`;
|
|
149
|
+
}
|
|
150
|
+
function requireRulePath(input, rule) {
|
|
151
|
+
const path = input.rulePaths.get(rule);
|
|
152
|
+
if (!path) throw new Error(`Missing path for Dewey rule: ${rule}`);
|
|
153
|
+
return path;
|
|
154
|
+
}
|
|
155
|
+
function displayPath(input, path) {
|
|
156
|
+
if (input.scope === "project") return relative(input.repoRoot, path);
|
|
157
|
+
return path;
|
|
158
|
+
}
|
|
159
|
+
function isPathInside(root, path) {
|
|
160
|
+
const pathFromRoot = relative(resolve(root), resolve(path));
|
|
161
|
+
return pathFromRoot === "" || !pathFromRoot.startsWith("..") && !isAbsolute(pathFromRoot);
|
|
162
|
+
}
|
|
163
|
+
async function readTextIfPresent(path) {
|
|
164
|
+
try {
|
|
165
|
+
return await readFile(path, "utf8");
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
168
|
+
if (error.code === "ENOENT") return "";
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function validateInstructionWritePath(path) {
|
|
173
|
+
try {
|
|
174
|
+
if ((await lstat(path)).isSymbolicLink()) throw new Error(`Refusing to write Dewey rules through symlink: ${path}`);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
177
|
+
if (error.code === "ENOENT") return;
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async function exists(path) {
|
|
182
|
+
try {
|
|
183
|
+
await lstat(path);
|
|
184
|
+
return true;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
187
|
+
if (error.code === "ENOENT") return false;
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async function isSymlinkToAgentsMd(path, repoRoot) {
|
|
192
|
+
try {
|
|
193
|
+
if (!(await lstat(path)).isSymbolicLink()) return false;
|
|
194
|
+
const target = await readlink(path);
|
|
195
|
+
return target === "AGENTS.md" || resolve(dirname(path), target) === resolve(repoRoot, "AGENTS.md");
|
|
196
|
+
} catch (error) {
|
|
197
|
+
if (!(error instanceof Error) || !("code" in error)) throw error;
|
|
198
|
+
if (error.code === "ENOENT") return false;
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
40
201
|
}
|
|
41
202
|
//#endregion
|
|
42
203
|
//#region src/cli/init.ts
|
|
@@ -45,9 +206,23 @@ const VALID_MODES = new Set([
|
|
|
45
206
|
"copy",
|
|
46
207
|
"pointer"
|
|
47
208
|
]);
|
|
209
|
+
const VALID_SCOPES = new Set(["project", "global"]);
|
|
210
|
+
const VALID_TOOLS = new Set(["codex", "claude"]);
|
|
211
|
+
const VALID_TOOL_SELECTIONS = new Set([
|
|
212
|
+
"all",
|
|
213
|
+
"codex",
|
|
214
|
+
"claude"
|
|
215
|
+
]);
|
|
216
|
+
const VALID_RULE_WIRING = new Set(["reference", "inline"]);
|
|
48
217
|
const SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
49
|
-
async function initRepo(
|
|
218
|
+
async function initRepo(options = {}) {
|
|
219
|
+
const { repoRoot = process.cwd(), homeDir = homedir(), mode = "link", selected, force = false, dryRun = false } = options;
|
|
50
220
|
if (!selected) throw new Error("selected assets are required");
|
|
221
|
+
const scope = options.scope ?? "project";
|
|
222
|
+
const ruleWiring = options.ruleWiring ?? "reference";
|
|
223
|
+
validateScope(scope);
|
|
224
|
+
validateRuleWiring(ruleWiring);
|
|
225
|
+
const tools = normalizeTools(options.tools);
|
|
51
226
|
validateMode(mode);
|
|
52
227
|
const paths = cachePaths({ homeDir });
|
|
53
228
|
const registry = await readCachedRegistry(paths.assetsRoot);
|
|
@@ -55,6 +230,17 @@ async function initRepo({ repoRoot = process.cwd(), homeDir = homedir(), mode =
|
|
|
55
230
|
const assets = normalizeSelected(selected);
|
|
56
231
|
const agentsRoot = join(repoRoot, ".agents");
|
|
57
232
|
validateSelectedAssets(registry, assets);
|
|
233
|
+
if (scope === "global") return await initGlobal({
|
|
234
|
+
homeDir,
|
|
235
|
+
repoRoot,
|
|
236
|
+
paths,
|
|
237
|
+
registry,
|
|
238
|
+
cacheManifest,
|
|
239
|
+
assets,
|
|
240
|
+
tools,
|
|
241
|
+
ruleWiring,
|
|
242
|
+
dryRun
|
|
243
|
+
});
|
|
58
244
|
const plan = await buildInitPlan({
|
|
59
245
|
repoRoot,
|
|
60
246
|
agentsRoot,
|
|
@@ -75,21 +261,62 @@ async function initRepo({ repoRoot = process.cwd(), homeDir = homedir(), mode =
|
|
|
75
261
|
cacheRoot: paths.assetsRoot,
|
|
76
262
|
assets,
|
|
77
263
|
assetSnapshot: snapshotSelectedAssetMetadata(registry, assets),
|
|
264
|
+
scope,
|
|
265
|
+
tools,
|
|
266
|
+
ruleWiring,
|
|
78
267
|
initializedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
79
268
|
};
|
|
269
|
+
const rulePlan = await planRuleInstall({
|
|
270
|
+
repoRoot,
|
|
271
|
+
homeDir,
|
|
272
|
+
cacheRoot: paths.assetsRoot,
|
|
273
|
+
scope: "project",
|
|
274
|
+
tools,
|
|
275
|
+
ruleWiring,
|
|
276
|
+
selectedRules: assets.rules,
|
|
277
|
+
rulePaths: projectRulePathMap(repoRoot, paths.assetsRoot, registry, assets, mode, ruleWiring)
|
|
278
|
+
});
|
|
80
279
|
if (dryRun) return {
|
|
280
|
+
...manifest,
|
|
81
281
|
dryRun: true,
|
|
82
|
-
|
|
83
|
-
source: cacheManifest.source,
|
|
84
|
-
cacheRoot: paths.assetsRoot,
|
|
85
|
-
assets,
|
|
86
|
-
assetSnapshot: snapshotSelectedAssetMetadata(registry, assets),
|
|
87
|
-
files: plan.files
|
|
282
|
+
files: uniqueFiles([...plan.files, ...rulePlan.files])
|
|
88
283
|
};
|
|
89
284
|
await mkdir(agentsRoot, { recursive: true });
|
|
90
285
|
if (mode !== "pointer") await installAssets(plan.assets);
|
|
91
286
|
await writeJson(join(agentsRoot, "manifest.json"), manifest);
|
|
92
287
|
await upsertAgentsSection(repoRoot);
|
|
288
|
+
await applyRuleInstall(rulePlan);
|
|
289
|
+
return manifest;
|
|
290
|
+
}
|
|
291
|
+
async function initGlobal({ homeDir, repoRoot, paths, registry, cacheManifest, assets, tools, ruleWiring, dryRun }) {
|
|
292
|
+
if (assets.skills.length > 0) throw new Error("Global installs currently support rules only");
|
|
293
|
+
const rulePlan = await planRuleInstall({
|
|
294
|
+
repoRoot,
|
|
295
|
+
homeDir,
|
|
296
|
+
cacheRoot: paths.assetsRoot,
|
|
297
|
+
scope: "global",
|
|
298
|
+
tools,
|
|
299
|
+
ruleWiring,
|
|
300
|
+
selectedRules: assets.rules,
|
|
301
|
+
rulePaths: rulePathMap(paths.assetsRoot, registry, assets.rules)
|
|
302
|
+
});
|
|
303
|
+
const manifest = {
|
|
304
|
+
scope: "global",
|
|
305
|
+
source: cacheManifest.source,
|
|
306
|
+
cacheRoot: paths.assetsRoot,
|
|
307
|
+
assets,
|
|
308
|
+
assetSnapshot: snapshotSelectedAssetMetadata(registry, assets),
|
|
309
|
+
tools,
|
|
310
|
+
ruleWiring,
|
|
311
|
+
initializedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
312
|
+
};
|
|
313
|
+
if (dryRun) return {
|
|
314
|
+
...manifest,
|
|
315
|
+
dryRun: true,
|
|
316
|
+
files: uniqueFiles([...rulePlan.files, join(homeDir, ".deweyou/agents/global-manifest.json")])
|
|
317
|
+
};
|
|
318
|
+
await applyRuleInstall(rulePlan);
|
|
319
|
+
await writeJson(join(homeDir, ".deweyou/agents/global-manifest.json"), manifest);
|
|
93
320
|
return manifest;
|
|
94
321
|
}
|
|
95
322
|
async function runInit(flags = {}, { promptForInit } = {}) {
|
|
@@ -98,6 +325,9 @@ async function runInit(flags = {}, { promptForInit } = {}) {
|
|
|
98
325
|
const registry = await readCachedRegistry(cachePaths({ homeDir }).assetsRoot);
|
|
99
326
|
const scripted = hasScriptedSelectionFlags(flags);
|
|
100
327
|
let mode = flags.mode ?? "link";
|
|
328
|
+
let scope = flags.scope;
|
|
329
|
+
let tools = flags.tools;
|
|
330
|
+
let ruleWiring = flags.ruleWiring;
|
|
101
331
|
let selected;
|
|
102
332
|
validateMode(mode);
|
|
103
333
|
if (flags.yes && !scripted) throw new Error("--yes requires --all, --skills, or --rules");
|
|
@@ -109,15 +339,25 @@ async function runInit(flags = {}, { promptForInit } = {}) {
|
|
|
109
339
|
const prompted = await (promptForInit ?? await loadPromptForInit())({
|
|
110
340
|
registry,
|
|
111
341
|
repoRoot,
|
|
112
|
-
mode: flags.mode
|
|
342
|
+
mode: flags.mode,
|
|
343
|
+
scope: flags.scope,
|
|
344
|
+
tools: flags.tools,
|
|
345
|
+
ruleWiring: flags.ruleWiring
|
|
113
346
|
});
|
|
114
|
-
mode = prompted.mode;
|
|
347
|
+
mode = flags.mode ?? prompted.mode;
|
|
348
|
+
scope = flags.scope ?? prompted.scope;
|
|
349
|
+
tools = flags.tools ?? prompted.tools;
|
|
350
|
+
ruleWiring = flags.ruleWiring ?? prompted.ruleWiring;
|
|
115
351
|
selected = prompted.selected;
|
|
116
352
|
}
|
|
117
353
|
if (!hasSelectedAssets(selected)) throw new Error("No assets selected. Pass --all, --skills, or --rules, or run interactive setup.");
|
|
354
|
+
if (scripted && scope === "global" && !flags.yes && !flags.dryRun) throw new Error("--scope global with scripted selections requires --yes or --dry-run");
|
|
118
355
|
const manifest = await initRepo({
|
|
119
356
|
repoRoot,
|
|
120
357
|
mode,
|
|
358
|
+
scope,
|
|
359
|
+
tools,
|
|
360
|
+
ruleWiring,
|
|
121
361
|
selected,
|
|
122
362
|
force: flags.force ?? false,
|
|
123
363
|
dryRun: flags.dryRun ?? false,
|
|
@@ -130,7 +370,7 @@ async function runInit(flags = {}, { promptForInit } = {}) {
|
|
|
130
370
|
return manifest;
|
|
131
371
|
}
|
|
132
372
|
async function loadPromptForInit() {
|
|
133
|
-
const { promptForInit } = await import("./prompts-
|
|
373
|
+
const { promptForInit } = await import("./prompts-DdaeRkBa.mjs");
|
|
134
374
|
return promptForInit;
|
|
135
375
|
}
|
|
136
376
|
async function readCachedRegistry(assetsRoot) {
|
|
@@ -175,6 +415,22 @@ function validateMode(mode) {
|
|
|
175
415
|
if (typeof mode !== "string") throw new Error("mode must be one of link, copy, or pointer");
|
|
176
416
|
if (!VALID_MODES.has(mode)) throw new Error("mode must be one of link, copy, or pointer");
|
|
177
417
|
}
|
|
418
|
+
function validateScope(scope) {
|
|
419
|
+
if (typeof scope !== "string") throw new Error("scope must be one of project or global");
|
|
420
|
+
if (!VALID_SCOPES.has(scope)) throw new Error("scope must be one of project or global");
|
|
421
|
+
}
|
|
422
|
+
function normalizeTools(tools) {
|
|
423
|
+
if (tools) {
|
|
424
|
+
for (const tool of tools) if (typeof tool !== "string" || !VALID_TOOL_SELECTIONS.has(tool)) throw new Error(`tool must be one of codex or claude: ${tool}`);
|
|
425
|
+
}
|
|
426
|
+
const selected = !tools || tools.includes("all") ? ["codex", "claude"] : [...tools];
|
|
427
|
+
for (const tool of selected) if (typeof tool !== "string" || !VALID_TOOLS.has(tool)) throw new Error(`tool must be one of codex or claude: ${tool}`);
|
|
428
|
+
return [...new Set(selected)];
|
|
429
|
+
}
|
|
430
|
+
function validateRuleWiring(ruleWiring) {
|
|
431
|
+
if (typeof ruleWiring !== "string") throw new Error("ruleWiring must be one of reference or inline");
|
|
432
|
+
if (!VALID_RULE_WIRING.has(ruleWiring)) throw new Error("ruleWiring must be one of reference or inline");
|
|
433
|
+
}
|
|
178
434
|
function validateSelectedAssets(registry, selected) {
|
|
179
435
|
for (const skill of selected.skills) {
|
|
180
436
|
if (!registry.assets.skills[skill]) throw new Error(`Unknown Dewey skill: ${skill}`);
|
|
@@ -206,6 +462,16 @@ function pickAssetMetadata(asset) {
|
|
|
206
462
|
hash: asset.hash
|
|
207
463
|
};
|
|
208
464
|
}
|
|
465
|
+
function uniqueFiles(files) {
|
|
466
|
+
return [...new Set(files)];
|
|
467
|
+
}
|
|
468
|
+
function rulePathMap(assetsRoot, registry, rules) {
|
|
469
|
+
return new Map(rules.map((rule) => [rule, join(assetsRoot, registry.assets.rules[rule].path)]));
|
|
470
|
+
}
|
|
471
|
+
function projectRulePathMap(repoRoot, assetsRoot, registry, assets, mode, ruleWiring) {
|
|
472
|
+
if (mode === "pointer" || ruleWiring === "inline") return rulePathMap(assetsRoot, registry, assets.rules);
|
|
473
|
+
return new Map(assets.rules.map((rule) => [rule, join(repoRoot, ".agents", "rules", `${rule}.md`)]));
|
|
474
|
+
}
|
|
209
475
|
async function buildInitPlan({ repoRoot, agentsRoot, assetsRoot, registry, assets, mode }) {
|
|
210
476
|
const assetPlans = mode === "pointer" ? [] : await Promise.all([...assets.skills.map((id) => buildAssetPlan({
|
|
211
477
|
kind: "skill",
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { cancel, confirm, intro, isCancel, multiselect, note, select } from "@clack/prompts";
|
|
2
|
+
//#region src/cli/prompts.ts
|
|
3
|
+
const SETUP_SCOPES = [{
|
|
4
|
+
value: "project",
|
|
5
|
+
label: "project",
|
|
6
|
+
hint: "Install into this repository."
|
|
7
|
+
}, {
|
|
8
|
+
value: "global",
|
|
9
|
+
label: "global",
|
|
10
|
+
hint: "Install into Codex and Claude user homes."
|
|
11
|
+
}];
|
|
12
|
+
const TOOL_OPTIONS = [
|
|
13
|
+
{
|
|
14
|
+
value: "both",
|
|
15
|
+
label: "both",
|
|
16
|
+
hint: "Wire Codex and Claude Code."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
value: "codex",
|
|
20
|
+
label: "codex",
|
|
21
|
+
hint: "Wire AGENTS.md only."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: "claude",
|
|
25
|
+
label: "claude",
|
|
26
|
+
hint: "Wire CLAUDE.md only."
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
const SETUP_MODES = [
|
|
30
|
+
{
|
|
31
|
+
value: "link",
|
|
32
|
+
label: "link",
|
|
33
|
+
hint: "Symlink assets from the Dewey cache."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
value: "copy",
|
|
37
|
+
label: "copy",
|
|
38
|
+
hint: "Copy asset files into this repository."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
value: "pointer",
|
|
42
|
+
label: "pointer",
|
|
43
|
+
hint: "Write only the manifest and AGENTS.md pointers."
|
|
44
|
+
}
|
|
45
|
+
];
|
|
46
|
+
const RULE_WIRING_OPTIONS = [{
|
|
47
|
+
value: "reference",
|
|
48
|
+
label: "reference",
|
|
49
|
+
hint: "Reference selected rule files."
|
|
50
|
+
}, {
|
|
51
|
+
value: "inline",
|
|
52
|
+
label: "inline",
|
|
53
|
+
hint: "Inline selected rule bodies."
|
|
54
|
+
}];
|
|
55
|
+
const ASSET_SCOPES = [
|
|
56
|
+
{
|
|
57
|
+
value: "all",
|
|
58
|
+
label: "all",
|
|
59
|
+
hint: "Enable every cached skill and rule."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
value: "custom",
|
|
63
|
+
label: "custom",
|
|
64
|
+
hint: "Choose skills and rules individually."
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
value: "skills",
|
|
68
|
+
label: "skills only",
|
|
69
|
+
hint: "Choose skills without installing rules."
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
value: "rules",
|
|
73
|
+
label: "rules only",
|
|
74
|
+
hint: "Choose rules without installing skills."
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
const GLOBAL_ASSET_SCOPES = [{
|
|
78
|
+
value: "all",
|
|
79
|
+
label: "all rules",
|
|
80
|
+
hint: "Enable every cached rule."
|
|
81
|
+
}, {
|
|
82
|
+
value: "rules",
|
|
83
|
+
label: "choose rules",
|
|
84
|
+
hint: "Choose rules individually."
|
|
85
|
+
}];
|
|
86
|
+
async function promptForInit({ registry, repoRoot, mode, scope, tools, ruleWiring }) {
|
|
87
|
+
intro("Dewey Agent Setup");
|
|
88
|
+
note(repoRoot, "Repository");
|
|
89
|
+
const selectedScope = scope ?? await promptOrExit(select({
|
|
90
|
+
message: "Select install scope",
|
|
91
|
+
options: SETUP_SCOPES
|
|
92
|
+
}));
|
|
93
|
+
const selectedTools = tools === void 0 ? normalizePromptTools(await promptOrExit(select({
|
|
94
|
+
message: "Select tools",
|
|
95
|
+
options: TOOL_OPTIONS
|
|
96
|
+
}))) : normalizeToolSelection(tools);
|
|
97
|
+
const selectedMode = selectedScope === "global" ? "pointer" : mode ?? await promptOrExit(select({
|
|
98
|
+
message: "Select setup mode",
|
|
99
|
+
options: SETUP_MODES
|
|
100
|
+
}));
|
|
101
|
+
const selected = await selectAssets({
|
|
102
|
+
registry,
|
|
103
|
+
scope: await promptOrExit(select({
|
|
104
|
+
message: "Select asset scope",
|
|
105
|
+
options: selectedScope === "global" ? GLOBAL_ASSET_SCOPES : ASSET_SCOPES
|
|
106
|
+
})),
|
|
107
|
+
installScope: selectedScope
|
|
108
|
+
});
|
|
109
|
+
const selectedRuleWiring = ruleWiring ?? (selected.rules.length > 0 ? await promptOrExit(select({
|
|
110
|
+
message: "Select rule wiring",
|
|
111
|
+
options: RULE_WIRING_OPTIONS
|
|
112
|
+
})) : "reference");
|
|
113
|
+
note(plannedFiles({
|
|
114
|
+
repoRoot,
|
|
115
|
+
scope: selectedScope,
|
|
116
|
+
tools: selectedTools,
|
|
117
|
+
selected
|
|
118
|
+
}), "Dewey will update");
|
|
119
|
+
if (!await promptOrExit(confirm({ message: `Enable ${selected.skills.length} skill(s) and ${selected.rules.length} rule(s) using ${selectedMode} mode?` }))) exitCancelled();
|
|
120
|
+
return {
|
|
121
|
+
mode: selectedMode,
|
|
122
|
+
scope: selectedScope,
|
|
123
|
+
tools: selectedTools,
|
|
124
|
+
ruleWiring: selectedRuleWiring,
|
|
125
|
+
selected
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async function selectAssets({ registry, scope, installScope }) {
|
|
129
|
+
if (scope === "all") return {
|
|
130
|
+
skills: installScope === "global" ? [] : Object.keys(registry.assets.skills),
|
|
131
|
+
rules: Object.keys(registry.assets.rules)
|
|
132
|
+
};
|
|
133
|
+
const selected = {
|
|
134
|
+
skills: [],
|
|
135
|
+
rules: []
|
|
136
|
+
};
|
|
137
|
+
if (installScope === "project" && (scope === "custom" || scope === "skills")) selected.skills = await promptOrExit(multiselect({
|
|
138
|
+
message: "Select skills",
|
|
139
|
+
options: assetOptions(registry.assets.skills),
|
|
140
|
+
required: false
|
|
141
|
+
}));
|
|
142
|
+
if (scope === "custom" || scope === "rules") selected.rules = await promptOrExit(multiselect({
|
|
143
|
+
message: "Select rules",
|
|
144
|
+
options: assetOptions(registry.assets.rules),
|
|
145
|
+
required: false
|
|
146
|
+
}));
|
|
147
|
+
return selected;
|
|
148
|
+
}
|
|
149
|
+
function assetOptions(assets) {
|
|
150
|
+
return Object.entries(assets).map(([name, asset]) => ({
|
|
151
|
+
value: name,
|
|
152
|
+
label: name,
|
|
153
|
+
hint: asset.description
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
function normalizePromptTools(selectedTools) {
|
|
157
|
+
if (selectedTools === "both") return ["codex", "claude"];
|
|
158
|
+
return [selectedTools];
|
|
159
|
+
}
|
|
160
|
+
function normalizeToolSelection(tools) {
|
|
161
|
+
if (tools.includes("all")) return ["codex", "claude"];
|
|
162
|
+
return [...new Set(tools)];
|
|
163
|
+
}
|
|
164
|
+
function plannedFiles({ repoRoot, scope, tools, selected }) {
|
|
165
|
+
const files = [];
|
|
166
|
+
if (scope === "global") {
|
|
167
|
+
if (tools.includes("codex")) files.push("~/.codex/AGENTS.md");
|
|
168
|
+
if (tools.includes("claude")) files.push("~/.claude/CLAUDE.md");
|
|
169
|
+
files.push("~/.deweyou/agents/global-manifest.json");
|
|
170
|
+
return files.join("\n");
|
|
171
|
+
}
|
|
172
|
+
files.push("AGENTS.md");
|
|
173
|
+
if (tools.includes("claude") && selected.rules.length > 0) files.push("CLAUDE.md");
|
|
174
|
+
files.push(".agents/manifest.json");
|
|
175
|
+
if (selected.skills.length > 0) files.push(".agents/skills/<skill>/SKILL.md");
|
|
176
|
+
if (selected.rules.length > 0) files.push(".agents/rules/<rule>.md");
|
|
177
|
+
return `${repoRoot}\n\n${files.join("\n")}`;
|
|
178
|
+
}
|
|
179
|
+
async function promptOrExit(prompt) {
|
|
180
|
+
const value = await prompt;
|
|
181
|
+
if (isCancel(value)) exitCancelled();
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
function exitCancelled() {
|
|
185
|
+
cancel("Dewey agent setup cancelled.");
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
export { promptForInit };
|
package/package.json
CHANGED
|
@@ -1,100 +0,0 @@
|
|
|
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 };
|