@dzhechkov/harness-cli 0.2.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/README.md +27 -0
- package/dist/bin.d.ts +6 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +358 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
- package/src/bin.ts +8 -0
- package/src/cli.ts +404 -0
- package/src/index.ts +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @dzhechkov/harness-cli
|
|
2
|
+
|
|
3
|
+
The **`dz`** command-line interface for the DZ cross-platform harness — a thin
|
|
4
|
+
argv shell over [`@dzhechkov/harness-core`](../harness-core).
|
|
5
|
+
|
|
6
|
+
## Commands
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
dz init --target <name> [--skills-dir <dir>] [--project <dir>] [--force]
|
|
10
|
+
dz verify [--skills-dir <dir>] [--target <name>]
|
|
11
|
+
dz sync [--canonical <dir>] [--project <dir>] [--dry-run] [--force]
|
|
12
|
+
dz update (alias of sync)
|
|
13
|
+
dz doctor [--project <dir>]
|
|
14
|
+
dz help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- `--target` — one of `claude-code`, `codex`, `opencode`, `hermes`
|
|
18
|
+
- `init` compiles a skills directory for a target and writes it **additively**
|
|
19
|
+
(existing files are never overwritten without `--force`)
|
|
20
|
+
- `sync` compares the canonical pack to the legacy `.claude/skills` tree
|
|
21
|
+
- `doctor` reports environment diagnostics
|
|
22
|
+
|
|
23
|
+
## Status
|
|
24
|
+
|
|
25
|
+
`0.1.0` — alpha, part of the `extended-a-migration` feature (Phase 6).
|
|
26
|
+
`--preset <name>` (Phase 10) selects a named skill set — see
|
|
27
|
+
`@dzhechkov/harness-presets`. The `paperclip` target is planned for a later phase.
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;GAEG"}
|
package/dist/bin.js
ADDED
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `dz` CLI — argv parsing + dispatch over `@dzhechkov/harness-core`.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
/** Output sink + working directory — injectable so the CLI is testable. */
|
|
7
|
+
export interface CliIo {
|
|
8
|
+
readonly cwd?: string;
|
|
9
|
+
readonly write?: (line: string) => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Run the `dz` CLI. Returns a process exit code; never calls `process.exit`,
|
|
13
|
+
* so it is safe to call from tests. Output and working directory are injectable.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runCli(argv: string[], io?: CliIo): Promise<number>;
|
|
16
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0CH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAqTD;;;GAGG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAwC5E"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `dz` CLI — argv parsing + dispatch over `@dzhechkov/harness-core`.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readlinkSync, renameSync, rmdirSync, symlinkSync } from 'node:fs';
|
|
7
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { getSkillInfo, getWorkflow, isTargetName, listSkills, runDoctor, runInit, runMigrate, runSync, runVerify, TARGET_NAMES, WORKFLOW_NAMES, } from '@dzhechkov/harness-core';
|
|
11
|
+
import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
|
|
12
|
+
const USAGE = `dz - DZ cross-platform harness CLI
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
dz init --target <name> [--skills-dir <dir>] [--project <dir>] [--preset <name>] [--select id,id,...] [--force]
|
|
16
|
+
dz verify [--skills-dir <dir>] [--target <name>]
|
|
17
|
+
dz sync [--canonical <dir>] [--project <dir>] [--dry-run] [--force]
|
|
18
|
+
dz update (alias of sync)
|
|
19
|
+
dz list [--skills-dir <dir>]
|
|
20
|
+
dz info <skill-id> [--skills-dir <dir>]
|
|
21
|
+
dz migrate [--project <dir>]
|
|
22
|
+
dz doctor [--project <dir>]
|
|
23
|
+
dz workflow <task> [--dry-run]
|
|
24
|
+
dz roam [--apply] [--slug <slug>]
|
|
25
|
+
dz help
|
|
26
|
+
|
|
27
|
+
Workflows: coverage-lift, mutation-kill, canonicalize, security-audit
|
|
28
|
+
|
|
29
|
+
Targets: ${TARGET_NAMES.join(', ')}
|
|
30
|
+
Presets: ${PRESET_NAMES.join(', ')}`;
|
|
31
|
+
/** Parse `<command> [--key value] [--flag]` argv. */
|
|
32
|
+
function parseArgs(argv) {
|
|
33
|
+
const options = new Map();
|
|
34
|
+
const flags = new Set();
|
|
35
|
+
const positional = [];
|
|
36
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
37
|
+
const arg = argv[index] ?? '';
|
|
38
|
+
if (arg.startsWith('--')) {
|
|
39
|
+
const key = arg.slice(2);
|
|
40
|
+
const next = argv[index + 1];
|
|
41
|
+
if (next !== undefined && !next.startsWith('--')) {
|
|
42
|
+
options.set(key, next);
|
|
43
|
+
index += 1;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
flags.add(key);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
positional.push(arg);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { command: positional[0] ?? '', options, flags };
|
|
54
|
+
}
|
|
55
|
+
async function cmdInit(options, flags, cwd, write) {
|
|
56
|
+
const target = options.get('target');
|
|
57
|
+
if (target === undefined || !isTargetName(target)) {
|
|
58
|
+
write(`dz init: --target must be one of: ${TARGET_NAMES.join(', ')}`);
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
const skillsDir = resolve(cwd, options.get('skills-dir') ?? '.claude/skills');
|
|
62
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
63
|
+
const presetName = options.get('preset');
|
|
64
|
+
const selectArg = options.get('select');
|
|
65
|
+
let select;
|
|
66
|
+
if (presetName !== undefined) {
|
|
67
|
+
const preset = getPreset(presetName);
|
|
68
|
+
if (preset === undefined) {
|
|
69
|
+
write(`dz init: --preset must be one of: ${PRESET_NAMES.join(', ')}`);
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
select = preset.skills;
|
|
73
|
+
}
|
|
74
|
+
else if (selectArg !== undefined) {
|
|
75
|
+
// --select skill1,skill2,skill3 — comma-separated skill ids
|
|
76
|
+
select = selectArg.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
|
|
77
|
+
if (select.length === 0) {
|
|
78
|
+
write('dz init: --select requires comma-separated skill ids');
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const report = await runInit({
|
|
83
|
+
target,
|
|
84
|
+
skillsDir,
|
|
85
|
+
projectRoot,
|
|
86
|
+
force: flags.has('force'),
|
|
87
|
+
...(select !== undefined ? { select } : {}),
|
|
88
|
+
});
|
|
89
|
+
let written = 0;
|
|
90
|
+
let skipped = 0;
|
|
91
|
+
for (const skill of report.skills) {
|
|
92
|
+
written += skill.written.length;
|
|
93
|
+
skipped += skill.skipped.length;
|
|
94
|
+
}
|
|
95
|
+
write(`dz init --target ${target}: ${report.skills.length} skill(s), ${written} file(s) written, ${skipped} skipped`);
|
|
96
|
+
for (const skill of report.skills) {
|
|
97
|
+
for (const warning of skill.warnings)
|
|
98
|
+
write(` warning [${skill.id}]: ${warning}`);
|
|
99
|
+
}
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
async function cmdVerify(options, cwd, write) {
|
|
103
|
+
const skillsDir = resolve(cwd, options.get('skills-dir') ?? '.claude/skills');
|
|
104
|
+
const targetOpt = options.get('target');
|
|
105
|
+
if (targetOpt !== undefined && !isTargetName(targetOpt)) {
|
|
106
|
+
write(`dz verify: --target must be one of: ${TARGET_NAMES.join(', ')}`);
|
|
107
|
+
return 1;
|
|
108
|
+
}
|
|
109
|
+
const report = await runVerify({
|
|
110
|
+
skillsDir,
|
|
111
|
+
...(targetOpt !== undefined ? { target: targetOpt } : {}),
|
|
112
|
+
});
|
|
113
|
+
write(`dz verify (${report.target}): ${report.valid}/${report.total} skill(s) valid`);
|
|
114
|
+
for (const skill of report.skills) {
|
|
115
|
+
if (!skill.ok)
|
|
116
|
+
write(` FAIL ${skill.id}: ${skill.errors.join('; ')}`);
|
|
117
|
+
}
|
|
118
|
+
return report.valid === report.total ? 0 : 1;
|
|
119
|
+
}
|
|
120
|
+
async function cmdSync(options, flags, cwd, write) {
|
|
121
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
122
|
+
const canonicalArg = options.get('canonical');
|
|
123
|
+
// Auto-discover all skills-* packs, or use explicit --canonical
|
|
124
|
+
let canonicalDirs;
|
|
125
|
+
if (canonicalArg !== undefined) {
|
|
126
|
+
canonicalDirs = [resolve(cwd, canonicalArg)];
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const baseDir = join(projectRoot, 'packages', '@dzhechkov');
|
|
130
|
+
canonicalDirs = existsSync(baseDir)
|
|
131
|
+
? readdirSync(baseDir, { withFileTypes: true })
|
|
132
|
+
.filter((e) => e.isDirectory() && e.name.startsWith('skills-'))
|
|
133
|
+
.map((e) => join(baseDir, e.name))
|
|
134
|
+
: [resolve(cwd, 'packages/@dzhechkov/skills-meta')];
|
|
135
|
+
}
|
|
136
|
+
const report = await runSync({
|
|
137
|
+
canonicalDirs,
|
|
138
|
+
projectRoot,
|
|
139
|
+
dryRun: flags.has('dry-run'),
|
|
140
|
+
force: flags.has('force'),
|
|
141
|
+
});
|
|
142
|
+
const { total, inSync, missing, drift } = report.summary;
|
|
143
|
+
write(`dz sync${report.dryRun ? ' --dry-run' : ''}: ${inSync}/${total} in sync, ${missing} missing, ${drift} drift`);
|
|
144
|
+
return missing === 0 && drift === 0 ? 0 : 1;
|
|
145
|
+
}
|
|
146
|
+
function cmdList(options, cwd, write) {
|
|
147
|
+
const skillsDir = resolve(cwd, options.get('skills-dir') ?? '.claude/skills');
|
|
148
|
+
const skills = listSkills(skillsDir);
|
|
149
|
+
if (skills.length === 0) {
|
|
150
|
+
write(`dz list: no skills found in ${skillsDir}`);
|
|
151
|
+
return 1;
|
|
152
|
+
}
|
|
153
|
+
write(`${skills.length} skill(s) in ${skillsDir}:\n`);
|
|
154
|
+
for (const skill of skills) {
|
|
155
|
+
const desc = skill.description.length > 80 ? skill.description.slice(0, 77) + '...' : skill.description;
|
|
156
|
+
write(` ${skill.id.padEnd(35)} ${desc}`);
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
function cmdInfo(options, args, cwd, write) {
|
|
161
|
+
const skillsDir = resolve(cwd, options.get('skills-dir') ?? '.claude/skills');
|
|
162
|
+
// The skill id is the second positional arg (first is 'info')
|
|
163
|
+
const skillId = args.command === 'info' ? (args.options.get('_positional') ?? '') : '';
|
|
164
|
+
// Actually, let's parse it from the raw argv approach — check options map
|
|
165
|
+
// The id might be passed as the value after 'info' in positional args
|
|
166
|
+
// Since parseArgs puts non-flag args in positional, and command is first,
|
|
167
|
+
// we need to find the skill id. Let's check if there's a value in options.
|
|
168
|
+
const id = options.get('id') ?? options.get('skill') ?? '';
|
|
169
|
+
if (id === '') {
|
|
170
|
+
write('dz info: specify a skill id — dz info --id <skill-id>');
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
const info = getSkillInfo(skillsDir, id);
|
|
174
|
+
if (info === undefined) {
|
|
175
|
+
write(`dz info: skill ${JSON.stringify(id)} not found in ${skillsDir}`);
|
|
176
|
+
return 1;
|
|
177
|
+
}
|
|
178
|
+
write(`Skill: ${info.id}`);
|
|
179
|
+
write(`Name: ${info.name ?? info.id}`);
|
|
180
|
+
write(`Description: ${info.description}`);
|
|
181
|
+
if (info.trustTier !== undefined)
|
|
182
|
+
write(`Trust Tier: ${info.trustTier}`);
|
|
183
|
+
if (info.version !== undefined)
|
|
184
|
+
write(`Version: ${info.version}`);
|
|
185
|
+
write(`Assets: ${info.assetCount} file(s)`);
|
|
186
|
+
if (info.assetCount > 0) {
|
|
187
|
+
for (const path of info.assetPaths)
|
|
188
|
+
write(` ${path}`);
|
|
189
|
+
}
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
function cmdWorkflow(options, flags, cwd, write) {
|
|
193
|
+
const taskName = options.get('task') ?? options.get('name') ?? '';
|
|
194
|
+
if (taskName === '' || !WORKFLOW_NAMES.includes(taskName)) {
|
|
195
|
+
write(`dz workflow: specify a task — one of: ${WORKFLOW_NAMES.join(', ')}`);
|
|
196
|
+
return 1;
|
|
197
|
+
}
|
|
198
|
+
const wf = getWorkflow(taskName);
|
|
199
|
+
if (wf === undefined) {
|
|
200
|
+
write(`dz workflow: unknown task ${JSON.stringify(taskName)}`);
|
|
201
|
+
return 1;
|
|
202
|
+
}
|
|
203
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
204
|
+
const script = wf.generate({ projectRoot, dryRun: flags.has('dry-run') });
|
|
205
|
+
write(`// Dynamic Workflow: ${wf.name}`);
|
|
206
|
+
write(`// ${wf.description}`);
|
|
207
|
+
write('');
|
|
208
|
+
write(script);
|
|
209
|
+
write('');
|
|
210
|
+
write(`// To execute: paste the above into Claude Code with Opus 4.8+ and "ultracode" enabled.`);
|
|
211
|
+
write(`// Or save to a .js file and run: claude "Execute this workflow" < workflow.js`);
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
function cmdMigrate(options, cwd, write) {
|
|
215
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
216
|
+
const report = runMigrate({ projectRoot });
|
|
217
|
+
if (report.detections.length === 0) {
|
|
218
|
+
write('dz migrate: no legacy installations detected.');
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
write(`dz migrate: found ${report.detections.length} legacy installation(s):\n`);
|
|
222
|
+
for (const det of report.detections) {
|
|
223
|
+
write(` ${det.manifest} — v${det.version}, ${det.components.length} components, ${det.fileCount} files`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
write(`\nSkills in .claude/skills/: ${report.skillsFound}`);
|
|
227
|
+
write(`\nRecommendation: ${report.recommendation}`);
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
230
|
+
async function cmdDoctor(options, cwd, write) {
|
|
231
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
232
|
+
const report = await runDoctor({ projectRoot });
|
|
233
|
+
write(`dz doctor (${report.node}):`);
|
|
234
|
+
for (const check of report.checks) {
|
|
235
|
+
write(` [${check.ok ? 'OK' : 'XX'}] ${check.name} - ${check.detail}`);
|
|
236
|
+
}
|
|
237
|
+
return report.ok ? 0 : 1;
|
|
238
|
+
}
|
|
239
|
+
function cmdRoam(options, flags, cwd, write) {
|
|
240
|
+
const apply = flags.has('apply');
|
|
241
|
+
// Find repo root
|
|
242
|
+
let repoRoot;
|
|
243
|
+
try {
|
|
244
|
+
repoRoot = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
write('dz roam: not a git repository');
|
|
248
|
+
return 1;
|
|
249
|
+
}
|
|
250
|
+
const roamDir = join(repoRoot, 'roam', 'claude-state');
|
|
251
|
+
const claudeProjectsDir = process.env['CLAUDE_PROJECTS_DIR'] ?? join(homedir(), '.claude', 'projects');
|
|
252
|
+
// Slug: replace every : \ / with -
|
|
253
|
+
const slug = options.get('slug') ?? repoRoot.replace(/[:\\/]/g, '-');
|
|
254
|
+
const target = join(claudeProjectsDir, slug);
|
|
255
|
+
write(`repo root : ${repoRoot}`);
|
|
256
|
+
write(`roam dir : ${roamDir}`);
|
|
257
|
+
write(`slug : ${slug}`);
|
|
258
|
+
write(`target : ${target}`);
|
|
259
|
+
write(`mode : ${apply ? 'APPLY' : 'DRY-RUN'}`);
|
|
260
|
+
write('');
|
|
261
|
+
// Ensure roam dir exists
|
|
262
|
+
if (apply) {
|
|
263
|
+
mkdirSync(roamDir, { recursive: true });
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
write(' would: mkdir -p ' + roamDir);
|
|
267
|
+
}
|
|
268
|
+
// Check current state of target
|
|
269
|
+
if (existsSync(target) && lstatSync(target).isSymbolicLink()) {
|
|
270
|
+
const current = readlinkSync(target);
|
|
271
|
+
if (current === roamDir) {
|
|
272
|
+
write(' symlink already points at roam dir — nothing to do.');
|
|
273
|
+
return 0;
|
|
274
|
+
}
|
|
275
|
+
write(`WARN: ${target} symlinks elsewhere (${current}). Resolve manually.`);
|
|
276
|
+
return 3;
|
|
277
|
+
}
|
|
278
|
+
// Move existing content
|
|
279
|
+
if (existsSync(target) && lstatSync(target).isDirectory()) {
|
|
280
|
+
const entries = readdirSync(target);
|
|
281
|
+
if (entries.length > 0) {
|
|
282
|
+
write(` moving ${entries.length} entries from ${target} → ${roamDir}`);
|
|
283
|
+
if (apply) {
|
|
284
|
+
for (const entry of entries) {
|
|
285
|
+
const dest = join(roamDir, entry);
|
|
286
|
+
if (existsSync(dest)) {
|
|
287
|
+
write(` WARN: ${dest} already exists — skipping`);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
renameSync(join(target, entry), dest);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (apply) {
|
|
295
|
+
rmdirSync(target);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
write(' would: rmdir ' + target);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Create symlink
|
|
302
|
+
if (apply) {
|
|
303
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
304
|
+
symlinkSync(roamDir, target);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
write(' would: mkdir -p ' + dirname(target));
|
|
308
|
+
write(` would: ln -s ${roamDir} ${target}`);
|
|
309
|
+
}
|
|
310
|
+
write('');
|
|
311
|
+
write(`done. (${apply ? 'applied' : 'dry-run'})`);
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Run the `dz` CLI. Returns a process exit code; never calls `process.exit`,
|
|
316
|
+
* so it is safe to call from tests. Output and working directory are injectable.
|
|
317
|
+
*/
|
|
318
|
+
export async function runCli(argv, io = {}) {
|
|
319
|
+
const cwd = io.cwd ?? process.cwd();
|
|
320
|
+
const write = io.write ?? ((line) => { console.log(line); });
|
|
321
|
+
const { command, options, flags } = parseArgs(argv);
|
|
322
|
+
if (command === '' || command === 'help' || flags.has('help')) {
|
|
323
|
+
write(USAGE);
|
|
324
|
+
return 0;
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
switch (command) {
|
|
328
|
+
case 'init':
|
|
329
|
+
return await cmdInit(options, flags, cwd, write);
|
|
330
|
+
case 'verify':
|
|
331
|
+
return await cmdVerify(options, cwd, write);
|
|
332
|
+
case 'sync':
|
|
333
|
+
case 'update':
|
|
334
|
+
return await cmdSync(options, flags, cwd, write);
|
|
335
|
+
case 'list':
|
|
336
|
+
return cmdList(options, cwd, write);
|
|
337
|
+
case 'info':
|
|
338
|
+
return cmdInfo(options, { command, options, flags }, cwd, write);
|
|
339
|
+
case 'workflow':
|
|
340
|
+
return cmdWorkflow(options, flags, cwd, write);
|
|
341
|
+
case 'migrate':
|
|
342
|
+
return cmdMigrate(options, cwd, write);
|
|
343
|
+
case 'doctor':
|
|
344
|
+
return await cmdDoctor(options, cwd, write);
|
|
345
|
+
case 'roam':
|
|
346
|
+
return cmdRoam(options, flags, cwd, write);
|
|
347
|
+
default:
|
|
348
|
+
write(`dz: unknown command ${JSON.stringify(command)}`);
|
|
349
|
+
write(USAGE);
|
|
350
|
+
return 1;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
write(`dz: ${error instanceof Error ? error.message : String(error)}`);
|
|
355
|
+
return 1;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dzhechkov/harness-cli` — the `dz` CLI for the DZ cross-platform harness.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
/** Package version. Kept in sync with `package.json`. */
|
|
7
|
+
export declare const HARNESS_CLI_VERSION = "0.1.0";
|
|
8
|
+
export { runCli } from './cli.js';
|
|
9
|
+
export type { CliIo } from './cli.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dzhechkov/harness-cli` — the `dz` CLI for the DZ cross-platform harness.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
/** Package version. Kept in sync with `package.json`. */
|
|
7
|
+
export const HARNESS_CLI_VERSION = '0.1.0';
|
|
8
|
+
export { runCli } from './cli.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yDAAyD;AACzD,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dzhechkov/harness-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "The dz CLI - init / sync / verify / doctor for the DZ cross-platform harness.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "dzhechko",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"agent-skills",
|
|
10
|
+
"harness",
|
|
11
|
+
"cli",
|
|
12
|
+
"claude-code"
|
|
13
|
+
],
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"dz": "./dist/bin.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"src",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@dzhechkov/harness-core": "0.2.0",
|
|
32
|
+
"@dzhechkov/harness-presets": "0.2.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^25.6.0",
|
|
36
|
+
"typescript": "^5.7.0",
|
|
37
|
+
"vitest": "^3.0.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/djd1m/dz-harness-hub.git",
|
|
48
|
+
"directory": "packages/@dzhechkov/harness-cli"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/djd1m/dz-harness-hub/tree/main/packages/@dzhechkov/harness-cli#readme",
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc -p tsconfig.json",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest",
|
|
55
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
56
|
+
"lint": "tsc -p tsconfig.json --noEmit"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/bin.ts
ADDED
package/src/cli.ts
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `dz` CLI — argv parsing + dispatch over `@dzhechkov/harness-core`.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readlinkSync, renameSync, rmdirSync, symlinkSync } from 'node:fs';
|
|
8
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
getSkillInfo,
|
|
14
|
+
getWorkflow,
|
|
15
|
+
isTargetName,
|
|
16
|
+
listSkills,
|
|
17
|
+
runDoctor,
|
|
18
|
+
runInit,
|
|
19
|
+
runMigrate,
|
|
20
|
+
runSync,
|
|
21
|
+
runVerify,
|
|
22
|
+
TARGET_NAMES,
|
|
23
|
+
WORKFLOW_NAMES,
|
|
24
|
+
} from '@dzhechkov/harness-core';
|
|
25
|
+
import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
|
|
26
|
+
|
|
27
|
+
const USAGE = `dz - DZ cross-platform harness CLI
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
dz init --target <name> [--skills-dir <dir>] [--project <dir>] [--preset <name>] [--select id,id,...] [--force]
|
|
31
|
+
dz verify [--skills-dir <dir>] [--target <name>]
|
|
32
|
+
dz sync [--canonical <dir>] [--project <dir>] [--dry-run] [--force]
|
|
33
|
+
dz update (alias of sync)
|
|
34
|
+
dz list [--skills-dir <dir>]
|
|
35
|
+
dz info <skill-id> [--skills-dir <dir>]
|
|
36
|
+
dz migrate [--project <dir>]
|
|
37
|
+
dz doctor [--project <dir>]
|
|
38
|
+
dz workflow <task> [--dry-run]
|
|
39
|
+
dz roam [--apply] [--slug <slug>]
|
|
40
|
+
dz help
|
|
41
|
+
|
|
42
|
+
Workflows: coverage-lift, mutation-kill, canonicalize, security-audit
|
|
43
|
+
|
|
44
|
+
Targets: ${TARGET_NAMES.join(', ')}
|
|
45
|
+
Presets: ${PRESET_NAMES.join(', ')}`;
|
|
46
|
+
|
|
47
|
+
/** Output sink + working directory — injectable so the CLI is testable. */
|
|
48
|
+
export interface CliIo {
|
|
49
|
+
readonly cwd?: string;
|
|
50
|
+
readonly write?: (line: string) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface ParsedArgs {
|
|
54
|
+
readonly command: string;
|
|
55
|
+
readonly options: Map<string, string>;
|
|
56
|
+
readonly flags: Set<string>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Parse `<command> [--key value] [--flag]` argv. */
|
|
60
|
+
function parseArgs(argv: string[]): ParsedArgs {
|
|
61
|
+
const options = new Map<string, string>();
|
|
62
|
+
const flags = new Set<string>();
|
|
63
|
+
const positional: string[] = [];
|
|
64
|
+
|
|
65
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
66
|
+
const arg = argv[index] ?? '';
|
|
67
|
+
if (arg.startsWith('--')) {
|
|
68
|
+
const key = arg.slice(2);
|
|
69
|
+
const next = argv[index + 1];
|
|
70
|
+
if (next !== undefined && !next.startsWith('--')) {
|
|
71
|
+
options.set(key, next);
|
|
72
|
+
index += 1;
|
|
73
|
+
} else {
|
|
74
|
+
flags.add(key);
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
positional.push(arg);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { command: positional[0] ?? '', options, flags };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type Write = (line: string) => void;
|
|
85
|
+
|
|
86
|
+
async function cmdInit(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): Promise<number> {
|
|
87
|
+
const target = options.get('target');
|
|
88
|
+
if (target === undefined || !isTargetName(target)) {
|
|
89
|
+
write(`dz init: --target must be one of: ${TARGET_NAMES.join(', ')}`);
|
|
90
|
+
return 1;
|
|
91
|
+
}
|
|
92
|
+
const skillsDir = resolve(cwd, options.get('skills-dir') ?? '.claude/skills');
|
|
93
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
94
|
+
|
|
95
|
+
const presetName = options.get('preset');
|
|
96
|
+
const selectArg = options.get('select');
|
|
97
|
+
let select: readonly string[] | undefined;
|
|
98
|
+
if (presetName !== undefined) {
|
|
99
|
+
const preset = getPreset(presetName);
|
|
100
|
+
if (preset === undefined) {
|
|
101
|
+
write(`dz init: --preset must be one of: ${PRESET_NAMES.join(', ')}`);
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
select = preset.skills;
|
|
105
|
+
} else if (selectArg !== undefined) {
|
|
106
|
+
// --select skill1,skill2,skill3 — comma-separated skill ids
|
|
107
|
+
select = selectArg.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
|
|
108
|
+
if (select.length === 0) {
|
|
109
|
+
write('dz init: --select requires comma-separated skill ids');
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const report = await runInit({
|
|
115
|
+
target,
|
|
116
|
+
skillsDir,
|
|
117
|
+
projectRoot,
|
|
118
|
+
force: flags.has('force'),
|
|
119
|
+
...(select !== undefined ? { select } : {}),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
let written = 0;
|
|
123
|
+
let skipped = 0;
|
|
124
|
+
for (const skill of report.skills) {
|
|
125
|
+
written += skill.written.length;
|
|
126
|
+
skipped += skill.skipped.length;
|
|
127
|
+
}
|
|
128
|
+
write(`dz init --target ${target}: ${report.skills.length} skill(s), ${written} file(s) written, ${skipped} skipped`);
|
|
129
|
+
for (const skill of report.skills) {
|
|
130
|
+
for (const warning of skill.warnings) write(` warning [${skill.id}]: ${warning}`);
|
|
131
|
+
}
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function cmdVerify(options: Map<string, string>, cwd: string, write: Write): Promise<number> {
|
|
136
|
+
const skillsDir = resolve(cwd, options.get('skills-dir') ?? '.claude/skills');
|
|
137
|
+
const targetOpt = options.get('target');
|
|
138
|
+
if (targetOpt !== undefined && !isTargetName(targetOpt)) {
|
|
139
|
+
write(`dz verify: --target must be one of: ${TARGET_NAMES.join(', ')}`);
|
|
140
|
+
return 1;
|
|
141
|
+
}
|
|
142
|
+
const report = await runVerify({
|
|
143
|
+
skillsDir,
|
|
144
|
+
...(targetOpt !== undefined ? { target: targetOpt } : {}),
|
|
145
|
+
});
|
|
146
|
+
write(`dz verify (${report.target}): ${report.valid}/${report.total} skill(s) valid`);
|
|
147
|
+
for (const skill of report.skills) {
|
|
148
|
+
if (!skill.ok) write(` FAIL ${skill.id}: ${skill.errors.join('; ')}`);
|
|
149
|
+
}
|
|
150
|
+
return report.valid === report.total ? 0 : 1;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function cmdSync(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): Promise<number> {
|
|
154
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
155
|
+
const canonicalArg = options.get('canonical');
|
|
156
|
+
|
|
157
|
+
// Auto-discover all skills-* packs, or use explicit --canonical
|
|
158
|
+
let canonicalDirs: string[];
|
|
159
|
+
if (canonicalArg !== undefined) {
|
|
160
|
+
canonicalDirs = [resolve(cwd, canonicalArg)];
|
|
161
|
+
} else {
|
|
162
|
+
const baseDir = join(projectRoot, 'packages', '@dzhechkov');
|
|
163
|
+
canonicalDirs = existsSync(baseDir)
|
|
164
|
+
? readdirSync(baseDir, { withFileTypes: true })
|
|
165
|
+
.filter((e) => e.isDirectory() && e.name.startsWith('skills-'))
|
|
166
|
+
.map((e) => join(baseDir, e.name))
|
|
167
|
+
: [resolve(cwd, 'packages/@dzhechkov/skills-meta')];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const report = await runSync({
|
|
171
|
+
canonicalDirs,
|
|
172
|
+
projectRoot,
|
|
173
|
+
dryRun: flags.has('dry-run'),
|
|
174
|
+
force: flags.has('force'),
|
|
175
|
+
});
|
|
176
|
+
const { total, inSync, missing, drift } = report.summary;
|
|
177
|
+
write(
|
|
178
|
+
`dz sync${report.dryRun ? ' --dry-run' : ''}: ${inSync}/${total} in sync, ${missing} missing, ${drift} drift`,
|
|
179
|
+
);
|
|
180
|
+
return missing === 0 && drift === 0 ? 0 : 1;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function cmdList(options: Map<string, string>, cwd: string, write: Write): number {
|
|
184
|
+
const skillsDir = resolve(cwd, options.get('skills-dir') ?? '.claude/skills');
|
|
185
|
+
const skills = listSkills(skillsDir);
|
|
186
|
+
if (skills.length === 0) {
|
|
187
|
+
write(`dz list: no skills found in ${skillsDir}`);
|
|
188
|
+
return 1;
|
|
189
|
+
}
|
|
190
|
+
write(`${skills.length} skill(s) in ${skillsDir}:\n`);
|
|
191
|
+
for (const skill of skills) {
|
|
192
|
+
const desc = skill.description.length > 80 ? skill.description.slice(0, 77) + '...' : skill.description;
|
|
193
|
+
write(` ${skill.id.padEnd(35)} ${desc}`);
|
|
194
|
+
}
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function cmdInfo(options: Map<string, string>, args: ParsedArgs, cwd: string, write: Write): number {
|
|
199
|
+
const skillsDir = resolve(cwd, options.get('skills-dir') ?? '.claude/skills');
|
|
200
|
+
// The skill id is the second positional arg (first is 'info')
|
|
201
|
+
const skillId = args.command === 'info' ? (args.options.get('_positional') ?? '') : '';
|
|
202
|
+
// Actually, let's parse it from the raw argv approach — check options map
|
|
203
|
+
// The id might be passed as the value after 'info' in positional args
|
|
204
|
+
// Since parseArgs puts non-flag args in positional, and command is first,
|
|
205
|
+
// we need to find the skill id. Let's check if there's a value in options.
|
|
206
|
+
const id = options.get('id') ?? options.get('skill') ?? '';
|
|
207
|
+
if (id === '') {
|
|
208
|
+
write('dz info: specify a skill id — dz info --id <skill-id>');
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
const info = getSkillInfo(skillsDir, id);
|
|
212
|
+
if (info === undefined) {
|
|
213
|
+
write(`dz info: skill ${JSON.stringify(id)} not found in ${skillsDir}`);
|
|
214
|
+
return 1;
|
|
215
|
+
}
|
|
216
|
+
write(`Skill: ${info.id}`);
|
|
217
|
+
write(`Name: ${info.name ?? info.id}`);
|
|
218
|
+
write(`Description: ${info.description}`);
|
|
219
|
+
if (info.trustTier !== undefined) write(`Trust Tier: ${info.trustTier}`);
|
|
220
|
+
if (info.version !== undefined) write(`Version: ${info.version}`);
|
|
221
|
+
write(`Assets: ${info.assetCount} file(s)`);
|
|
222
|
+
if (info.assetCount > 0) {
|
|
223
|
+
for (const path of info.assetPaths) write(` ${path}`);
|
|
224
|
+
}
|
|
225
|
+
return 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function cmdWorkflow(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
|
|
229
|
+
const taskName = options.get('task') ?? options.get('name') ?? '';
|
|
230
|
+
if (taskName === '' || !WORKFLOW_NAMES.includes(taskName)) {
|
|
231
|
+
write(`dz workflow: specify a task — one of: ${WORKFLOW_NAMES.join(', ')}`);
|
|
232
|
+
return 1;
|
|
233
|
+
}
|
|
234
|
+
const wf = getWorkflow(taskName);
|
|
235
|
+
if (wf === undefined) {
|
|
236
|
+
write(`dz workflow: unknown task ${JSON.stringify(taskName)}`);
|
|
237
|
+
return 1;
|
|
238
|
+
}
|
|
239
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
240
|
+
const script = wf.generate({ projectRoot, dryRun: flags.has('dry-run') });
|
|
241
|
+
|
|
242
|
+
write(`// Dynamic Workflow: ${wf.name}`);
|
|
243
|
+
write(`// ${wf.description}`);
|
|
244
|
+
write('');
|
|
245
|
+
write(script);
|
|
246
|
+
write('');
|
|
247
|
+
write(`// To execute: paste the above into Claude Code with Opus 4.8+ and "ultracode" enabled.`);
|
|
248
|
+
write(`// Or save to a .js file and run: claude "Execute this workflow" < workflow.js`);
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function cmdMigrate(options: Map<string, string>, cwd: string, write: Write): number {
|
|
253
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
254
|
+
const report = runMigrate({ projectRoot });
|
|
255
|
+
|
|
256
|
+
if (report.detections.length === 0) {
|
|
257
|
+
write('dz migrate: no legacy installations detected.');
|
|
258
|
+
} else {
|
|
259
|
+
write(`dz migrate: found ${report.detections.length} legacy installation(s):\n`);
|
|
260
|
+
for (const det of report.detections) {
|
|
261
|
+
write(` ${det.manifest} — v${det.version}, ${det.components.length} components, ${det.fileCount} files`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
write(`\nSkills in .claude/skills/: ${report.skillsFound}`);
|
|
265
|
+
write(`\nRecommendation: ${report.recommendation}`);
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function cmdDoctor(options: Map<string, string>, cwd: string, write: Write): Promise<number> {
|
|
270
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
271
|
+
const report = await runDoctor({ projectRoot });
|
|
272
|
+
write(`dz doctor (${report.node}):`);
|
|
273
|
+
for (const check of report.checks) {
|
|
274
|
+
write(` [${check.ok ? 'OK' : 'XX'}] ${check.name} - ${check.detail}`);
|
|
275
|
+
}
|
|
276
|
+
return report.ok ? 0 : 1;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function cmdRoam(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
|
|
280
|
+
const apply = flags.has('apply');
|
|
281
|
+
|
|
282
|
+
// Find repo root
|
|
283
|
+
let repoRoot: string;
|
|
284
|
+
try {
|
|
285
|
+
repoRoot = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
|
|
286
|
+
} catch {
|
|
287
|
+
write('dz roam: not a git repository');
|
|
288
|
+
return 1;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const roamDir = join(repoRoot, 'roam', 'claude-state');
|
|
292
|
+
const claudeProjectsDir = process.env['CLAUDE_PROJECTS_DIR'] ?? join(homedir(), '.claude', 'projects');
|
|
293
|
+
|
|
294
|
+
// Slug: replace every : \ / with -
|
|
295
|
+
const slug = options.get('slug') ?? repoRoot.replace(/[:\\/]/g, '-');
|
|
296
|
+
const target = join(claudeProjectsDir, slug);
|
|
297
|
+
|
|
298
|
+
write(`repo root : ${repoRoot}`);
|
|
299
|
+
write(`roam dir : ${roamDir}`);
|
|
300
|
+
write(`slug : ${slug}`);
|
|
301
|
+
write(`target : ${target}`);
|
|
302
|
+
write(`mode : ${apply ? 'APPLY' : 'DRY-RUN'}`);
|
|
303
|
+
write('');
|
|
304
|
+
|
|
305
|
+
// Ensure roam dir exists
|
|
306
|
+
if (apply) {
|
|
307
|
+
mkdirSync(roamDir, { recursive: true });
|
|
308
|
+
} else {
|
|
309
|
+
write(' would: mkdir -p ' + roamDir);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Check current state of target
|
|
313
|
+
if (existsSync(target) && lstatSync(target).isSymbolicLink()) {
|
|
314
|
+
const current = readlinkSync(target);
|
|
315
|
+
if (current === roamDir) {
|
|
316
|
+
write(' symlink already points at roam dir — nothing to do.');
|
|
317
|
+
return 0;
|
|
318
|
+
}
|
|
319
|
+
write(`WARN: ${target} symlinks elsewhere (${current}). Resolve manually.`);
|
|
320
|
+
return 3;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Move existing content
|
|
324
|
+
if (existsSync(target) && lstatSync(target).isDirectory()) {
|
|
325
|
+
const entries = readdirSync(target);
|
|
326
|
+
if (entries.length > 0) {
|
|
327
|
+
write(` moving ${entries.length} entries from ${target} → ${roamDir}`);
|
|
328
|
+
if (apply) {
|
|
329
|
+
for (const entry of entries) {
|
|
330
|
+
const dest = join(roamDir, entry);
|
|
331
|
+
if (existsSync(dest)) {
|
|
332
|
+
write(` WARN: ${dest} already exists — skipping`);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
renameSync(join(target, entry), dest);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (apply) {
|
|
340
|
+
rmdirSync(target);
|
|
341
|
+
} else {
|
|
342
|
+
write(' would: rmdir ' + target);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Create symlink
|
|
347
|
+
if (apply) {
|
|
348
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
349
|
+
symlinkSync(roamDir, target);
|
|
350
|
+
} else {
|
|
351
|
+
write(' would: mkdir -p ' + dirname(target));
|
|
352
|
+
write(` would: ln -s ${roamDir} ${target}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
write('');
|
|
356
|
+
write(`done. (${apply ? 'applied' : 'dry-run'})`);
|
|
357
|
+
return 0;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Run the `dz` CLI. Returns a process exit code; never calls `process.exit`,
|
|
362
|
+
* so it is safe to call from tests. Output and working directory are injectable.
|
|
363
|
+
*/
|
|
364
|
+
export async function runCli(argv: string[], io: CliIo = {}): Promise<number> {
|
|
365
|
+
const cwd = io.cwd ?? process.cwd();
|
|
366
|
+
const write: Write = io.write ?? ((line) => { console.log(line); });
|
|
367
|
+
const { command, options, flags } = parseArgs(argv);
|
|
368
|
+
|
|
369
|
+
if (command === '' || command === 'help' || flags.has('help')) {
|
|
370
|
+
write(USAGE);
|
|
371
|
+
return 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
switch (command) {
|
|
376
|
+
case 'init':
|
|
377
|
+
return await cmdInit(options, flags, cwd, write);
|
|
378
|
+
case 'verify':
|
|
379
|
+
return await cmdVerify(options, cwd, write);
|
|
380
|
+
case 'sync':
|
|
381
|
+
case 'update':
|
|
382
|
+
return await cmdSync(options, flags, cwd, write);
|
|
383
|
+
case 'list':
|
|
384
|
+
return cmdList(options, cwd, write);
|
|
385
|
+
case 'info':
|
|
386
|
+
return cmdInfo(options, { command, options, flags }, cwd, write);
|
|
387
|
+
case 'workflow':
|
|
388
|
+
return cmdWorkflow(options, flags, cwd, write);
|
|
389
|
+
case 'migrate':
|
|
390
|
+
return cmdMigrate(options, cwd, write);
|
|
391
|
+
case 'doctor':
|
|
392
|
+
return await cmdDoctor(options, cwd, write);
|
|
393
|
+
case 'roam':
|
|
394
|
+
return cmdRoam(options, flags, cwd, write);
|
|
395
|
+
default:
|
|
396
|
+
write(`dz: unknown command ${JSON.stringify(command)}`);
|
|
397
|
+
write(USAGE);
|
|
398
|
+
return 1;
|
|
399
|
+
}
|
|
400
|
+
} catch (error) {
|
|
401
|
+
write(`dz: ${error instanceof Error ? error.message : String(error)}`);
|
|
402
|
+
return 1;
|
|
403
|
+
}
|
|
404
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dzhechkov/harness-cli` — the `dz` CLI for the DZ cross-platform harness.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Package version. Kept in sync with `package.json`. */
|
|
8
|
+
export const HARNESS_CLI_VERSION = '0.1.0';
|
|
9
|
+
|
|
10
|
+
export { runCli } from './cli.js';
|
|
11
|
+
export type { CliIo } from './cli.js';
|