claudenv 1.2.4 → 1.3.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 +243 -178
- package/bin/cli.js +265 -4
- package/package.json +1 -1
- package/scaffold/.claude/memories/README.md +44 -0
- package/scaffold/global/.claude/commands/autonomy.md +90 -0
- package/scaffold/global/.claude/commands/canon.md +33 -0
- package/scaffold/global/.claude/commands/decisions.md +29 -0
- package/scaffold/global/.claude/commands/deeper.md +55 -0
- package/scaffold/global/.claude/commands/just-code.md +26 -0
- package/scaffold/global/.claude/commands/why.md +33 -0
- package/scaffold/global/.claude/skills/vibe-decisions/SKILL.md +127 -0
- package/scaffold/global/.claude/skills/vibe-decisions/deep-dive-template.md +41 -0
- package/scaffold/global-claudenv/config.yaml +16 -0
- package/scaffold/global-claudenv/memories/INDEX.md +25 -0
- package/scaffold/global-claudenv/memories/canon/index.yaml +21 -0
- package/scaffold/global-claudenv/memories/user/preferences.md.example +16 -0
- package/src/autonomy.js +7 -1
- package/src/canon.js +214 -0
- package/src/decisions.js +161 -0
- package/src/doctor.js +175 -0
- package/src/hooks/decisions-logger.js +183 -0
- package/src/hooks/dispatcher.js +91 -0
- package/src/hooks/regen-index.js +198 -0
- package/src/hooks-gen.js +60 -5
- package/src/installer.js +56 -14
- package/src/loop.js +148 -42
- package/src/memory-context.js +63 -0
- package/src/memory-paths.js +86 -0
- package/src/memory.js +111 -0
- package/src/profiles.js +4 -0
- package/src/report.js +160 -0
package/bin/cli.js
CHANGED
|
@@ -10,9 +10,10 @@ import { generateDocs, writeDocs, installScaffold } from '../src/generator.js';
|
|
|
10
10
|
import { validateClaudeMd, validateStructure, crossReferenceCheck } from '../src/validator.js';
|
|
11
11
|
import { runExistingProjectFlow, runColdStartFlow, buildDefaultConfig } from '../src/prompts.js';
|
|
12
12
|
import { installGlobal, uninstallGlobal } from '../src/installer.js';
|
|
13
|
-
import { runLoop, rollback, checkClaudeCli } from '../src/loop.js';
|
|
13
|
+
import { runLoop, rollback, checkClaudeCli, readLoopLog } from '../src/loop.js';
|
|
14
14
|
import { generateAutonomyConfig, printSecuritySummary, getFullModeWarning } from '../src/autonomy.js';
|
|
15
15
|
import { getProfile, listProfiles } from '../src/profiles.js';
|
|
16
|
+
import { readReport, formatReport, formatEventLine, watchReport } from '../src/report.js';
|
|
16
17
|
|
|
17
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
19
|
const pkgJson = JSON.parse(await readFile(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
@@ -131,6 +132,7 @@ program
|
|
|
131
132
|
.option('-d, --dir <path>', 'Target project directory')
|
|
132
133
|
.option('--allow-dirty', 'Allow running with uncommitted git changes')
|
|
133
134
|
.option('--rollback', 'Undo all changes from the most recent loop run')
|
|
135
|
+
.option('--resume', 'Resume from last rate-limited iteration')
|
|
134
136
|
.option('--unsafe', 'Remove default tool restrictions (allows rm -rf)')
|
|
135
137
|
.option('--worktree', 'Run each iteration in an isolated git worktree')
|
|
136
138
|
.option('--profile <name>', 'Autonomy profile: safe, moderate, full, ci')
|
|
@@ -141,6 +143,36 @@ program
|
|
|
141
143
|
return;
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
// --- Resume mode ---
|
|
147
|
+
if (opts.resume) {
|
|
148
|
+
const resumeCwd = opts.dir ? resolve(opts.dir) : process.cwd();
|
|
149
|
+
const prevLog = await readLoopLog(resumeCwd);
|
|
150
|
+
if (!prevLog || !prevLog.pausedAt) {
|
|
151
|
+
console.error('\n No resumable loop found. Run a loop first or check .claude/loop-log.json.\n');
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
const saved = prevLog.options || {};
|
|
155
|
+
console.log(`\n Resuming loop from iteration ${prevLog.pausedAt.iteration}`);
|
|
156
|
+
console.log(` Goal: ${saved.goal || 'General improvement'}`);
|
|
157
|
+
console.log(` Session: ${prevLog.pausedAt.sessionId || 'new'}\n`);
|
|
158
|
+
|
|
159
|
+
await runLoop({
|
|
160
|
+
iterations: opts.iterations || Infinity,
|
|
161
|
+
trust: saved.trust || false,
|
|
162
|
+
goal: saved.goal,
|
|
163
|
+
pause: false,
|
|
164
|
+
maxTurns: saved.maxTurns || 30,
|
|
165
|
+
model: opts.model || saved.model,
|
|
166
|
+
budget: saved.budget,
|
|
167
|
+
cwd: resumeCwd,
|
|
168
|
+
allowDirty: true,
|
|
169
|
+
worktree: saved.worktree || false,
|
|
170
|
+
startIteration: prevLog.pausedAt.iteration,
|
|
171
|
+
initialSessionId: prevLog.pausedAt.sessionId,
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
144
176
|
// --- Pre-flight: check Claude CLI ---
|
|
145
177
|
const cli = checkClaudeCli();
|
|
146
178
|
if (!cli.installed) {
|
|
@@ -176,6 +208,7 @@ program
|
|
|
176
208
|
disallowedTools: profile.disallowedTools,
|
|
177
209
|
maxTurns: profile.maxTurns,
|
|
178
210
|
budget: profile.maxBudget,
|
|
211
|
+
model: profile.model,
|
|
179
212
|
};
|
|
180
213
|
console.log(` Profile: ${profile.name} — ${profile.description}`);
|
|
181
214
|
}
|
|
@@ -189,7 +222,8 @@ program
|
|
|
189
222
|
if (opts.worktree) console.log(` Worktree: enabled (each iteration in isolated worktree)`);
|
|
190
223
|
if (opts.iterations) console.log(` Max iterations: ${opts.iterations}`);
|
|
191
224
|
if (opts.goal) console.log(` Goal: ${opts.goal}`);
|
|
192
|
-
|
|
225
|
+
const model = opts.model || profileDefaults.model || undefined;
|
|
226
|
+
if (model) console.log(` Model: ${model}`);
|
|
193
227
|
if (opts.budget || profileDefaults.budget) console.log(` Budget: $${opts.budget || profileDefaults.budget}/iteration`);
|
|
194
228
|
if (opts.maxTurns || profileDefaults.maxTurns) console.log(` Max turns: ${opts.maxTurns || profileDefaults.maxTurns}`);
|
|
195
229
|
|
|
@@ -199,7 +233,7 @@ program
|
|
|
199
233
|
goal: opts.goal,
|
|
200
234
|
pause,
|
|
201
235
|
maxTurns: opts.maxTurns || profileDefaults.maxTurns || 30,
|
|
202
|
-
model
|
|
236
|
+
model,
|
|
203
237
|
budget: opts.budget || profileDefaults.budget,
|
|
204
238
|
cwd,
|
|
205
239
|
allowDirty: opts.allowDirty || false,
|
|
@@ -209,6 +243,40 @@ program
|
|
|
209
243
|
});
|
|
210
244
|
});
|
|
211
245
|
|
|
246
|
+
// --- report ---
|
|
247
|
+
program
|
|
248
|
+
.command('report')
|
|
249
|
+
.description('View work report from autonomous loop runs')
|
|
250
|
+
.option('-f, --follow', 'Live stream events (tail -f style)')
|
|
251
|
+
.option('--last <n>', 'Show last N events', parseInt)
|
|
252
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
253
|
+
.action(async (opts) => {
|
|
254
|
+
const cwd = opts.dir ? resolve(opts.dir) : process.cwd();
|
|
255
|
+
const events = await readReport(cwd);
|
|
256
|
+
|
|
257
|
+
if (opts.follow) {
|
|
258
|
+
// Print existing events first
|
|
259
|
+
if (events.length > 0) {
|
|
260
|
+
const show = opts.last ? events.slice(-opts.last) : events;
|
|
261
|
+
process.stdout.write(formatReport(show));
|
|
262
|
+
}
|
|
263
|
+
console.log(' Watching for new events... (Ctrl+C to stop)\n');
|
|
264
|
+
await watchReport(cwd, (event) => {
|
|
265
|
+
process.stdout.write(formatEventLine(event));
|
|
266
|
+
});
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (events.length === 0) {
|
|
271
|
+
console.log('\n No work report found. Run `claudenv loop` first.\n');
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const show = opts.last ? events.slice(-opts.last) : events;
|
|
276
|
+
console.log();
|
|
277
|
+
process.stdout.write(formatReport(show));
|
|
278
|
+
});
|
|
279
|
+
|
|
212
280
|
// --- autonomy ---
|
|
213
281
|
program
|
|
214
282
|
.command('autonomy')
|
|
@@ -220,6 +288,198 @@ program
|
|
|
220
288
|
.option('--dry-run', 'Preview without writing')
|
|
221
289
|
.action(runAutonomy);
|
|
222
290
|
|
|
291
|
+
// --- hook (internal entry point for Claude Code hooks) ---
|
|
292
|
+
program
|
|
293
|
+
.command('hook')
|
|
294
|
+
.description('Internal: dispatch a Claude Code hook by name (decisions-logger, regen-index)')
|
|
295
|
+
.argument('<name>', 'Hook name')
|
|
296
|
+
.action(async (name) => {
|
|
297
|
+
const { dispatch } = await import('../src/hooks/dispatcher.js');
|
|
298
|
+
await dispatch(name);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// =============================================
|
|
302
|
+
// 1.3.0: memory / decisions / canon / doctor
|
|
303
|
+
// =============================================
|
|
304
|
+
|
|
305
|
+
// --- memory ---
|
|
306
|
+
const memoryCmd = program.command('memory').description('Manage the global ~/.claudenv/memories/ layout');
|
|
307
|
+
|
|
308
|
+
memoryCmd
|
|
309
|
+
.command('init')
|
|
310
|
+
.description('Create the ~/.claudenv/memories/ structure (idempotent)')
|
|
311
|
+
.action(async () => {
|
|
312
|
+
const { memoryInit } = await import('../src/memory.js');
|
|
313
|
+
const { created, skipped } = await memoryInit();
|
|
314
|
+
for (const p of created) console.log(` + ${p}`);
|
|
315
|
+
for (const p of skipped) console.log(` ~ ${p}`);
|
|
316
|
+
console.log(`\n ${created.length} created, ${skipped.length} already present.`);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
memoryCmd
|
|
320
|
+
.command('index')
|
|
321
|
+
.description('Regenerate ~/.claudenv/memories/INDEX.md from current decisions and prefs')
|
|
322
|
+
.action(async () => {
|
|
323
|
+
const { memoryIndex } = await import('../src/memory.js');
|
|
324
|
+
const result = await memoryIndex();
|
|
325
|
+
console.log(` INDEX.md regenerated → ${result.indexPath}`);
|
|
326
|
+
console.log(` ${result.recentCount} recent of ${result.decisionCount} total decisions`);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
memoryCmd
|
|
330
|
+
.command('show')
|
|
331
|
+
.argument('<path>', 'Path relative to ~/.claudenv/memories/')
|
|
332
|
+
.description('Print a memory file to stdout')
|
|
333
|
+
.action(async (path) => {
|
|
334
|
+
const { memoryShow } = await import('../src/memory.js');
|
|
335
|
+
try {
|
|
336
|
+
process.stdout.write(await memoryShow(path));
|
|
337
|
+
} catch (err) {
|
|
338
|
+
console.error(err.message);
|
|
339
|
+
process.exit(2);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
memoryCmd
|
|
344
|
+
.command('edit')
|
|
345
|
+
.argument('<path>', 'Path relative to ~/.claudenv/memories/')
|
|
346
|
+
.description('Open a memory file in $EDITOR (vi by default)')
|
|
347
|
+
.action(async (path) => {
|
|
348
|
+
const { memoryEdit } = await import('../src/memory.js');
|
|
349
|
+
const result = await memoryEdit(path);
|
|
350
|
+
process.exit(result.exitCode);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// --- decisions ---
|
|
354
|
+
const decisionsCmd = program.command('decisions').description('List, show, or search logged vibe-decisions');
|
|
355
|
+
|
|
356
|
+
decisionsCmd
|
|
357
|
+
.command('list')
|
|
358
|
+
.description('List recent decisions (newest first)')
|
|
359
|
+
.option('--scope <s>', 'global | project | all', 'all')
|
|
360
|
+
.option('--limit <n>', 'Max entries', '10')
|
|
361
|
+
.action(async (opts) => {
|
|
362
|
+
const { listDecisions, formatDecisionList } = await import('../src/decisions.js');
|
|
363
|
+
const limit = parseInt(opts.limit, 10) || 10;
|
|
364
|
+
const all = await listDecisions({ scope: opts.scope });
|
|
365
|
+
console.log(formatDecisionList(all.slice(0, limit)));
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
decisionsCmd
|
|
369
|
+
.command('show')
|
|
370
|
+
.argument('<id>', 'Slug or substring of the decision')
|
|
371
|
+
.description('Show full details of one decision')
|
|
372
|
+
.action(async (id) => {
|
|
373
|
+
const { showDecision, formatDecisionDetail } = await import('../src/decisions.js');
|
|
374
|
+
try {
|
|
375
|
+
const d = await showDecision(id);
|
|
376
|
+
console.log(formatDecisionDetail(d));
|
|
377
|
+
console.log('\n---\n');
|
|
378
|
+
console.log(d.text);
|
|
379
|
+
} catch (err) {
|
|
380
|
+
console.error(err.message);
|
|
381
|
+
process.exit(err.notFound ? 1 : 2);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
decisionsCmd
|
|
386
|
+
.command('search')
|
|
387
|
+
.argument('<query>', 'Substring to search topic/reason/chose')
|
|
388
|
+
.description('Search decisions')
|
|
389
|
+
.action(async (query) => {
|
|
390
|
+
const { searchDecisions, formatDecisionList } = await import('../src/decisions.js');
|
|
391
|
+
const hits = await searchDecisions(query);
|
|
392
|
+
console.log(formatDecisionList(hits));
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
decisionsCmd
|
|
396
|
+
.command('archive')
|
|
397
|
+
.argument('<id>', 'Slug to archive')
|
|
398
|
+
.description('Move a decision into <scope-dir>/archive/')
|
|
399
|
+
.action(async (id) => {
|
|
400
|
+
const { archiveDecision } = await import('../src/decisions.js');
|
|
401
|
+
try {
|
|
402
|
+
const { from, to } = await archiveDecision(id);
|
|
403
|
+
console.log(` archived: ${from} → ${to}`);
|
|
404
|
+
} catch (err) {
|
|
405
|
+
console.error(err.message);
|
|
406
|
+
process.exit(2);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// --- canon ---
|
|
411
|
+
const canonCmd = program.command('canon').description('Personal canon of references (~/.claudenv/memories/canon/index.yaml)');
|
|
412
|
+
|
|
413
|
+
canonCmd
|
|
414
|
+
.command('add')
|
|
415
|
+
.argument('<topic>', 'Topic slug')
|
|
416
|
+
.argument('<url>', 'URL to add')
|
|
417
|
+
.option('--why <reason>', 'Why this reference is in the canon')
|
|
418
|
+
.option('--title <title>', 'Title')
|
|
419
|
+
.option('--author <author>', 'Author or venue')
|
|
420
|
+
.action(async (topic, url, opts) => {
|
|
421
|
+
const { canonAdd } = await import('../src/canon.js');
|
|
422
|
+
try {
|
|
423
|
+
const res = await canonAdd({
|
|
424
|
+
topic,
|
|
425
|
+
url,
|
|
426
|
+
why: opts.why,
|
|
427
|
+
title: opts.title,
|
|
428
|
+
author: opts.author,
|
|
429
|
+
});
|
|
430
|
+
if (res.added) console.log(` + ${topic}: ${res.entry.title || res.entry.url}`);
|
|
431
|
+
else console.log(` ~ duplicate url in ${topic} — skipped`);
|
|
432
|
+
} catch (err) {
|
|
433
|
+
console.error(err.message);
|
|
434
|
+
process.exit(2);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
canonCmd
|
|
439
|
+
.command('list')
|
|
440
|
+
.argument('[topic]', 'Optional topic filter')
|
|
441
|
+
.action(async (topic) => {
|
|
442
|
+
const { canonList, formatCanon } = await import('../src/canon.js');
|
|
443
|
+
const data = await canonList(topic);
|
|
444
|
+
console.log(formatCanon(data));
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
canonCmd
|
|
448
|
+
.command('search')
|
|
449
|
+
.argument('<query>', 'Substring search')
|
|
450
|
+
.action(async (query) => {
|
|
451
|
+
const { canonSearch, formatCanon } = await import('../src/canon.js');
|
|
452
|
+
const data = await canonSearch(query);
|
|
453
|
+
console.log(formatCanon(data));
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
canonCmd
|
|
457
|
+
.command('prune')
|
|
458
|
+
.option('--months <n>', 'Age threshold in months', '6')
|
|
459
|
+
.action(async (opts) => {
|
|
460
|
+
const { canonPrune } = await import('../src/canon.js');
|
|
461
|
+
const stale = await canonPrune(parseInt(opts.months, 10) || 6);
|
|
462
|
+
if (stale.length === 0) {
|
|
463
|
+
console.log(' No stale entries.');
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
for (const { topic, entry, reason } of stale) {
|
|
467
|
+
const why = reason || `added ${entry.added}`;
|
|
468
|
+
console.log(` ${topic}: ${entry.url} (${why})`);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// --- doctor ---
|
|
473
|
+
program
|
|
474
|
+
.command('doctor')
|
|
475
|
+
.description('Health-check the claudenv setup')
|
|
476
|
+
.action(async () => {
|
|
477
|
+
const { runDoctor } = await import('../src/doctor.js');
|
|
478
|
+
const { lines, hasFail } = await runDoctor();
|
|
479
|
+
for (const l of lines) console.log(l);
|
|
480
|
+
process.exit(hasFail ? 1 : 0);
|
|
481
|
+
});
|
|
482
|
+
|
|
223
483
|
// =============================================
|
|
224
484
|
// Install / Uninstall
|
|
225
485
|
// =============================================
|
|
@@ -247,7 +507,8 @@ async function runInstall(opts) {
|
|
|
247
507
|
console.log(`
|
|
248
508
|
Done! Now open Claude Code in any project and type:
|
|
249
509
|
|
|
250
|
-
/claudenv
|
|
510
|
+
/claudenv — Set up project documentation
|
|
511
|
+
/autonomy — Manage autonomy profiles
|
|
251
512
|
|
|
252
513
|
Claude will analyze your project and generate documentation.
|
|
253
514
|
`);
|
package/package.json
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Project memories
|
|
2
|
+
|
|
3
|
+
This directory holds **project-scoped** memory for `vibe-decisions` and related skills. Cross-project memory lives in `~/.claudenv/memories/`.
|
|
4
|
+
|
|
5
|
+
## Layout
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
.claude/memories/
|
|
9
|
+
├── project.md # стек, конвенции, важные urls — заполняешь вручную
|
|
10
|
+
├── decisions/ # project-specific tech decisions (committed to repo)
|
|
11
|
+
│ └── 2026-05-27-auth-jwt.md
|
|
12
|
+
└── README.md # this file
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## When goes here vs `~/.claudenv/memories/`
|
|
16
|
+
|
|
17
|
+
- **Здесь (project)**: выбор специфичен этому проекту — внутренний сервис, формат API, deployment target, project-only convention
|
|
18
|
+
- **В `~/.claudenv/memories/decisions/` (global)**: универсальный tech-выбор — какая БД, какой algo, который применим в любом проекте
|
|
19
|
+
|
|
20
|
+
Skill `vibe-decisions` решает scope автоматически по полю `scope: global|project` в frontmatter. По умолчанию `global` если непонятно.
|
|
21
|
+
|
|
22
|
+
## project.md шаблон
|
|
23
|
+
|
|
24
|
+
Создай этот файл вручную (claudenv не генерит автоматически):
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
# <project-name>
|
|
28
|
+
|
|
29
|
+
- **Stack:** Python 3.12, FastAPI, Postgres, Redis
|
|
30
|
+
- **Tests:** `pytest -m "not slow"` локально; CI запускает всё
|
|
31
|
+
- **Package manager:** uv (NOT pip, NOT poetry)
|
|
32
|
+
- **Линтеры:** ruff + mypy strict
|
|
33
|
+
- **Deploy:** GitHub Actions → ArgoCD → k8s
|
|
34
|
+
- **Внутренние ADR:** `docs/adr/`
|
|
35
|
+
- **Слаг для decisions/:** `<project-slug>`
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`vibe-decisions` читает project.md перед каждым нетривиальным выбором.
|
|
39
|
+
|
|
40
|
+
## Commit policy
|
|
41
|
+
|
|
42
|
+
- `project.md` — committed
|
|
43
|
+
- `decisions/*.md` — committed (это shared с командой)
|
|
44
|
+
- Никаких secrets (production endpoints, API keys, internal hostnames) — для них используй `~/.claudenv/memories/` с глобальным scope (выйдет за пределы repo)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Manage autonomy profiles — switch between safe/moderate/full/ci or view current status
|
|
3
|
+
allowed-tools: Bash, Read, Glob, Grep
|
|
4
|
+
argument-hint: <status|safe|moderate|full|ci> [--dry-run]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Autonomy Profile Manager
|
|
8
|
+
|
|
9
|
+
Manage Claude Code autonomy profiles directly from within a session.
|
|
10
|
+
|
|
11
|
+
## Instructions
|
|
12
|
+
|
|
13
|
+
Parse `$ARGUMENTS` and execute the matching action below.
|
|
14
|
+
|
|
15
|
+
### 1. Determine the action
|
|
16
|
+
|
|
17
|
+
- If `$ARGUMENTS` is empty or `status` → go to **Status**
|
|
18
|
+
- If `$ARGUMENTS` starts with `safe`, `moderate`, `full`, or `ci` → go to **Switch Profile**
|
|
19
|
+
- Otherwise → go to **Usage Help**
|
|
20
|
+
|
|
21
|
+
### 2. Status
|
|
22
|
+
|
|
23
|
+
Show the current autonomy configuration:
|
|
24
|
+
|
|
25
|
+
1. Check if `.claude/hooks/pre-tool-use.sh` exists in the current project directory.
|
|
26
|
+
- If it exists, read the first 5 lines to find the `# Profile:` comment header and report the active profile name.
|
|
27
|
+
- If it doesn't exist, report "No autonomy profile configured for this project."
|
|
28
|
+
2. Check if `.claude/settings.json` exists. If so, read it and summarize the permissions (allowedTools, disallowedTools counts).
|
|
29
|
+
3. List available profiles: `safe`, `moderate`, `full`, `ci`.
|
|
30
|
+
|
|
31
|
+
### 3. Switch Profile
|
|
32
|
+
|
|
33
|
+
The target profile is the first word of `$ARGUMENTS`. Check for `--dry-run` flag in the remaining arguments.
|
|
34
|
+
|
|
35
|
+
**If the profile is `full`:**
|
|
36
|
+
Before proceeding, display this warning:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
⚠️ FULL AUTONOMY MODE — UNRESTRICTED ACCESS
|
|
40
|
+
|
|
41
|
+
This profile grants Claude Code complete access including:
|
|
42
|
+
• All file system operations (read, write, delete)
|
|
43
|
+
• Credential files (~/.ssh, ~/.aws, ~/.gnupg, ~/.kube, etc.)
|
|
44
|
+
• All git operations including force push
|
|
45
|
+
• No permission prompts (--dangerously-skip-permissions)
|
|
46
|
+
|
|
47
|
+
Safety net: audit logging + hard blocks on rm -rf / and force push to main
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Ask the user to confirm before proceeding. If they decline, abort.
|
|
51
|
+
|
|
52
|
+
**Apply the profile:**
|
|
53
|
+
|
|
54
|
+
Build the command:
|
|
55
|
+
```
|
|
56
|
+
claudenv autonomy --profile <name> --yes --overwrite
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If `--dry-run` is present in `$ARGUMENTS`, append `--dry-run` to the command.
|
|
60
|
+
|
|
61
|
+
Run the command via Bash. If the command fails with "command not found", tell the user:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
claudenv is not on PATH. Install it with:
|
|
65
|
+
npm install -g claudenv
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
After successful execution, show the output and confirm the profile was applied.
|
|
69
|
+
|
|
70
|
+
### 4. Usage Help
|
|
71
|
+
|
|
72
|
+
Display:
|
|
73
|
+
```
|
|
74
|
+
Usage: /autonomy <action> [options]
|
|
75
|
+
|
|
76
|
+
Actions:
|
|
77
|
+
status Show current autonomy profile and permissions
|
|
78
|
+
safe Switch to safe profile (read-only + limited bash)
|
|
79
|
+
moderate Switch to moderate profile (read/write + controlled bash)
|
|
80
|
+
full Switch to full profile (unrestricted — requires confirmation)
|
|
81
|
+
ci Switch to CI profile (optimized for CI/CD pipelines)
|
|
82
|
+
|
|
83
|
+
Options:
|
|
84
|
+
--dry-run Preview generated files without writing them
|
|
85
|
+
|
|
86
|
+
Examples:
|
|
87
|
+
/autonomy status
|
|
88
|
+
/autonomy moderate
|
|
89
|
+
/autonomy full --dry-run
|
|
90
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Показать или добавить ссылки в личный канон (~/.claudenv/memories/canon/index.yaml)
|
|
3
|
+
allowed-tools: Bash(claudenv:*), Read
|
|
4
|
+
argument-hint: [list [<topic>]|search <query>|add <topic> <url> --why "<reason>"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /canon — Личный канон ссылок
|
|
8
|
+
|
|
9
|
+
Wrapper над `claudenv canon` CLI.
|
|
10
|
+
|
|
11
|
+
## Routing
|
|
12
|
+
|
|
13
|
+
Разбери `$ARGUMENTS`:
|
|
14
|
+
|
|
15
|
+
- пусто или `list` → `claudenv canon list`
|
|
16
|
+
- `list <topic>` → `claudenv canon list <topic>`
|
|
17
|
+
- `search <query>` → `claudenv canon search "<query>"`
|
|
18
|
+
- `add <topic> <url> --why "<reason>"` → `claudenv canon add <topic> <url> --why "<reason>"`
|
|
19
|
+
- иначе если выглядит как topic slug → `claudenv canon list <slug>`
|
|
20
|
+
- иначе → `claudenv canon search "$ARGUMENTS"`
|
|
21
|
+
|
|
22
|
+
## Output
|
|
23
|
+
|
|
24
|
+
Печатай stdout как есть. На non-zero exit code покажи stderr и подскажи usage.
|
|
25
|
+
|
|
26
|
+
## Когда канон пуст
|
|
27
|
+
|
|
28
|
+
Если `claudenv canon list` ничего не вернул:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
Канон пока пуст. Добавь первые записи когда будешь делать /deeper —
|
|
32
|
+
после deep dive предложу `claudenv canon add <topic> <url> --why "..."`.
|
|
33
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Показать или найти принятые vibe-decisions (последние N, по slug, или по поисковому запросу)
|
|
3
|
+
allowed-tools: Bash(claudenv:*), Read
|
|
4
|
+
argument-hint: [list|show <id>|search <query>] [--limit N] [--scope global|project|all]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /decisions — Список и поиск принятых решений
|
|
8
|
+
|
|
9
|
+
Wrapper над `claudenv decisions` CLI. Запускается из Bash, результат показывается пользователю как есть.
|
|
10
|
+
|
|
11
|
+
## Routing
|
|
12
|
+
|
|
13
|
+
Разбери `$ARGUMENTS`:
|
|
14
|
+
|
|
15
|
+
- пусто или `list` → `claudenv decisions list --limit 10`
|
|
16
|
+
- `list --limit N` → `claudenv decisions list --limit N`
|
|
17
|
+
- `list --scope <s>` → `claudenv decisions list --scope <s>`
|
|
18
|
+
- `show <id>` → `claudenv decisions show <id>`
|
|
19
|
+
- `search <query>` → `claudenv decisions search "<query>"`
|
|
20
|
+
- иначе если выглядит как id или slug → `claudenv decisions show "$ARGUMENTS"`
|
|
21
|
+
- иначе → `claudenv decisions search "$ARGUMENTS"`
|
|
22
|
+
|
|
23
|
+
## Output
|
|
24
|
+
|
|
25
|
+
Печатай stdout команды без редактирования. Если команда вернула non-zero exit code — покажи stderr пользователю и предложи `claudenv decisions list` для проверки доступных id.
|
|
26
|
+
|
|
27
|
+
## Если `claudenv` не на PATH
|
|
28
|
+
|
|
29
|
+
Подскажи: `npm i -g claudenv` или `npx claudenv decisions list`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Развернуть последнее принятое решение в подробное объяснение (deep dive — концепт, варианты, канон)
|
|
3
|
+
allowed-tools: Read, Glob, Grep, WebSearch, WebFetch, Bash(claudenv:*), Bash(ls:*), Write, Edit
|
|
4
|
+
argument-hint: [topic-or-decision-id]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /deeper — Подробное объяснение последнего решения
|
|
8
|
+
|
|
9
|
+
Развернуть последнее (или указанное в `$ARGUMENTS`) принятое vibe-decision в полный формат.
|
|
10
|
+
|
|
11
|
+
## Step 1 — Find the target decision
|
|
12
|
+
|
|
13
|
+
If `$ARGUMENTS` is empty:
|
|
14
|
+
|
|
15
|
+
1. Run `claudenv decisions list --limit 1` (Bash) — get the most recent decision id
|
|
16
|
+
2. Run `claudenv decisions show <id>` to load its frontmatter
|
|
17
|
+
|
|
18
|
+
If `$ARGUMENTS` is a topic slug or id:
|
|
19
|
+
|
|
20
|
+
1. Run `claudenv decisions show $ARGUMENTS` to load it
|
|
21
|
+
2. If not found — fall back to `claudenv decisions search "$ARGUMENTS"` and pick the top match
|
|
22
|
+
|
|
23
|
+
If nothing matches:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Не нашёл недавнего решения. Сначала сделай нетривиальный выбор — vibe-decisions залогирует его. Потом вызови /deeper.
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Stop here.
|
|
30
|
+
|
|
31
|
+
## Step 2 — Expand into the deep dive format
|
|
32
|
+
|
|
33
|
+
Follow `~/.claude/skills/vibe-decisions/deep-dive-template.md` exactly:
|
|
34
|
+
|
|
35
|
+
1. **Концептуально** (3-5 lines) — what it is, what problem it solves, name 1-2 alternatives
|
|
36
|
+
2. **Как работает** (5-10 lines, optional code sketch) — simplified model + key invariants
|
|
37
|
+
3. **2-3 варианта реализации** — for each: code sketch + when-suits / when-doesn't
|
|
38
|
+
4. **Канон** — 2-4 references, **first** from `~/.claudenv/memories/canon/index.yaml` by topic; if no match, WebSearch and propose `claudenv canon add` afterward
|
|
39
|
+
|
|
40
|
+
## Step 3 — Update the decision file
|
|
41
|
+
|
|
42
|
+
Edit the original decision file to set:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
deep_dive_done: yes
|
|
46
|
+
sources_consulted: [<urls and canon ids you cited>]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Preserve the `__VIBE_DECISION__` marker on the last line.
|
|
50
|
+
|
|
51
|
+
## Style
|
|
52
|
+
|
|
53
|
+
- User's language
|
|
54
|
+
- Concrete to the actual task, not generic textbook
|
|
55
|
+
- If a sketch is shorter than prose — use the sketch
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Подавить vibe-decisions overview на следующий ответ — просто пиши код без brief overview
|
|
3
|
+
allowed-tools:
|
|
4
|
+
argument-hint:
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /just-code — Подавить vibe overview одноразово
|
|
8
|
+
|
|
9
|
+
Эффект на ОДИН следующий ответ:
|
|
10
|
+
|
|
11
|
+
- vibe-decisions skill НЕ выдаёт brief overview / auto-log block
|
|
12
|
+
- Просто пишет код для текущей задачи
|
|
13
|
+
- Decision файл НЕ создаётся для этого ответа
|
|
14
|
+
- На ответ после следующего поведение возвращается к дефолту
|
|
15
|
+
|
|
16
|
+
## В AUTO-LOG mode (loop)
|
|
17
|
+
|
|
18
|
+
В loop эта команда фактически no-op для большинства случаев — auto-log не паузит, а просто логирует. `/just-code` пропускает логирование на один шаг. Это полезно когда выбор тривиален но vibe-decisions всё равно зажёгся.
|
|
19
|
+
|
|
20
|
+
## В INTERACTIVE mode
|
|
21
|
+
|
|
22
|
+
Пропускает паузу на следующем шаге. Используй когда явно знаешь что хочешь, без нужды в overview.
|
|
23
|
+
|
|
24
|
+
## После использования
|
|
25
|
+
|
|
26
|
+
Не нужно ничего откатывать — действие истекает само через один turn. Если хочешь надолго отключить vibe-decisions — отредактируй его SKILL.md trigger criteria.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Объяснить технологию или подход в контексте текущей задачи — без принятия решения и без записи в decisions/
|
|
3
|
+
allowed-tools: Read, Glob, Grep, WebSearch, WebFetch, Bash(claudenv:*)
|
|
4
|
+
argument-hint: <technology-or-question>
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /why — Краткое объяснение
|
|
8
|
+
|
|
9
|
+
Объяснить `$ARGUMENTS` в контексте текущей задачи. Не вызывает vibe-decisions, не пишет в `~/.claudenv/memories/decisions/`.
|
|
10
|
+
|
|
11
|
+
## Step 1 — Check canon first
|
|
12
|
+
|
|
13
|
+
Run `claudenv canon search "$ARGUMENTS"` (Bash). If matches found — упомяни их как primary references.
|
|
14
|
+
|
|
15
|
+
## Step 2 — Brief explanation (3-5 lines)
|
|
16
|
+
|
|
17
|
+
Структура:
|
|
18
|
+
|
|
19
|
+
- **Что это:** 1 строка
|
|
20
|
+
- **Когда используется:** 1-2 строки
|
|
21
|
+
- **Когда не подходит:** 1 строка
|
|
22
|
+
- **Ссылки (опционально):** канон или WebSearch если канон пуст
|
|
23
|
+
|
|
24
|
+
## Не делать
|
|
25
|
+
|
|
26
|
+
- Не выводи vibe-decisions overview format (это explainer, не decision)
|
|
27
|
+
- Не пиши файл в decisions/
|
|
28
|
+
- Если пользователь после `/why` начинает реальный выбор технологии — тогда уже триггерится vibe-decisions skill
|
|
29
|
+
|
|
30
|
+
## Style
|
|
31
|
+
|
|
32
|
+
- User's language
|
|
33
|
+
- Краткость > полнота. Для полноты есть `/deeper` после реального decision.
|