@event4u/agent-config 1.22.0 → 1.24.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/.agent-src/commands/agents/cleanup.md +31 -17
- package/.agent-src/commands/analyze-reference-repo.md +3 -0
- package/.agent-src/commands/commit/in-chunks.md +30 -10
- package/.agent-src/commands/commit.md +46 -6
- package/.agent-src/commands/compress.md +19 -13
- package/.agent-src/commands/cost-report.md +120 -0
- package/.agent-src/commands/create-pr/description-only.md +8 -0
- package/.agent-src/commands/create-pr.md +95 -80
- package/.agent-src/commands/feature/plan.md +13 -7
- package/.agent-src/commands/memory/add.md +16 -8
- package/.agent-src/commands/memory/promote.md +17 -9
- package/.agent-src/commands/optimize/rtk.md +16 -11
- package/.agent-src/commands/prepare-for-review.md +12 -6
- package/.agent-src/commands/project-analyze.md +31 -20
- package/.agent-src/commands/review-changes.md +24 -15
- package/.agent-src/commands/roadmap/create.md +14 -9
- package/.agent-src/commands/roadmap/process-full.md +41 -1
- package/.agent-src/contexts/contracts/frugality-charter.md +57 -0
- package/.agent-src/contexts/execution/roadmap-process-loop.md +29 -6
- package/.agent-src/rules/architecture.md +9 -0
- package/.agent-src/rules/ask-when-uncertain.md +3 -13
- package/.agent-src/rules/caveman-speak.md +78 -0
- package/.agent-src/rules/direct-answers.md +5 -14
- package/.agent-src/rules/markdown-safe-codeblocks.md +6 -7
- package/.agent-src/rules/no-cheap-questions.md +4 -14
- package/.agent-src/rules/roadmap-progress-sync.md +37 -3
- package/.agent-src/rules/token-efficiency.md +5 -7
- package/.agent-src/skills/adr-create/SKILL.md +197 -0
- package/.agent-src/skills/agent-docs-writing/SKILL.md +23 -1
- package/.agent-src/skills/command-writing/SKILL.md +23 -0
- package/.agent-src/skills/context-authoring/SKILL.md +23 -0
- package/.agent-src/skills/conventional-commits-writing/SKILL.md +23 -0
- package/.agent-src/skills/guideline-writing/SKILL.md +22 -0
- package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +9 -0
- package/.agent-src/skills/markitdown/SKILL.md +239 -0
- package/.agent-src/skills/persona-writing/SKILL.md +153 -0
- package/.agent-src/skills/readme-writing/SKILL.md +20 -0
- package/.agent-src/skills/readme-writing-package/SKILL.md +19 -0
- package/.agent-src/skills/roadmap-writing/SKILL.md +157 -0
- package/.agent-src/skills/rule-writing/SKILL.md +22 -0
- package/.agent-src/skills/script-writing/SKILL.md +226 -0
- package/.agent-src/skills/skill-writing/SKILL.md +23 -0
- package/.agent-src/skills/test-driven-development/SKILL.md +24 -0
- package/.agent-src/skills/universal-project-analysis/SKILL.md +8 -0
- package/.agent-src/templates/agent-settings.md +73 -0
- package/.agent-src/templates/command.md +15 -10
- package/.agent-src/templates/rule.md +6 -0
- package/.agent-src/templates/skill.md +32 -0
- package/.claude-plugin/marketplace.json +10 -4
- package/AGENTS.md +14 -3
- package/CHANGELOG.md +61 -0
- package/README.md +5 -5
- package/docs/architecture.md +4 -4
- package/docs/catalog.md +25 -8
- package/docs/customization.md +72 -0
- package/docs/decisions/INDEX.md +15 -0
- package/docs/getting-started.md +2 -2
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +27 -19
- package/docs/guidelines/agent-infra/carve-out-predicates.md +17 -0
- package/docs/guidelines/agent-infra/mcp-request-signing.md +199 -0
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +11 -4
- package/package.json +1 -1
- package/scripts/_lib/__init__.py +5 -0
- package/scripts/_lib/script_output.py +140 -0
- package/scripts/adr/regenerate_index.py +79 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_add_quiet.py +149 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_inject_quiet_flag.py +33 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_v2.sh +36 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_verbosity.sh +26 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_per_task.sh +41 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_silent_taskfiles.py +98 -0
- package/scripts/check_augmentignore.py +4 -1
- package/scripts/check_command_count_messaging.py +4 -1
- package/scripts/check_compressed_paths.py +4 -1
- package/scripts/check_council_layout.py +4 -1
- package/scripts/check_council_references.py +4 -1
- package/scripts/check_iron_law_prominence.py +3 -1
- package/scripts/check_md_language.py +3 -1
- package/scripts/check_memory_proposal.py +3 -1
- package/scripts/check_public_catalog_links.py +4 -1
- package/scripts/check_reply_consistency.py +8 -2
- package/scripts/check_roadmap_trackable.py +4 -1
- package/scripts/compile_router.py +27 -0
- package/scripts/compress.py +33 -19
- package/scripts/cost/budget.mjs +152 -0
- package/scripts/cost/track.mjs +144 -0
- package/scripts/first-run.sh +3 -9
- package/scripts/install-hooks.sh +19 -1
- package/scripts/install.py +17 -12
- package/scripts/install.sh +19 -8
- package/scripts/lint_examples.py +6 -2
- package/scripts/lint_handoffs.py +4 -1
- package/scripts/lint_load_context.py +4 -1
- package/scripts/lint_roadmap_complexity.py +6 -2
- package/scripts/lint_rule_interactions.py +4 -1
- package/scripts/lint_rule_tiers.py +4 -1
- package/scripts/measure_frugality_savings.py +164 -0
- package/scripts/measure_markitdown_lift.py +127 -0
- package/scripts/runtime_dispatcher.py +11 -0
- package/scripts/skill_linter.py +207 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cost-budget — set / get / check the project's cost budget against
|
|
3
|
+
// accumulated session spend in agents/cost-tracking/sessions.jsonl.
|
|
4
|
+
//
|
|
5
|
+
// Forked from ruvnet/ruflo plugins/ruflo-cost-tracker/scripts/budget.mjs.
|
|
6
|
+
// Local-JSONL swap replaces the upstream `mcp__claude-flow__memory_store`
|
|
7
|
+
// dependency. Budget config lives next to the sessions store as budget.json.
|
|
8
|
+
//
|
|
9
|
+
// Usage: node scripts/cost/budget.mjs {set <usd>|get|check}
|
|
10
|
+
// Env: BUDGET_STORE, BUDGET_CONFIG, BUDGET_PERIOD={today|week|month|all}, BUDGET_QUIET=1
|
|
11
|
+
|
|
12
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
13
|
+
import { dirname } from 'node:path';
|
|
14
|
+
|
|
15
|
+
const STORE = process.env.BUDGET_STORE || 'agents/cost-tracking/sessions.jsonl';
|
|
16
|
+
const CONFIG = process.env.BUDGET_CONFIG || 'agents/cost-tracking/budget.json';
|
|
17
|
+
|
|
18
|
+
function loadConfig() {
|
|
19
|
+
if (!existsSync(CONFIG)) return null;
|
|
20
|
+
try { return JSON.parse(readFileSync(CONFIG, 'utf-8')); } catch { return null; }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function saveConfig(cfg) {
|
|
24
|
+
mkdirSync(dirname(CONFIG), { recursive: true });
|
|
25
|
+
writeFileSync(CONFIG, JSON.stringify(cfg, null, 2));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function loadSessions() {
|
|
29
|
+
if (!existsSync(STORE)) return [];
|
|
30
|
+
const out = [];
|
|
31
|
+
for (const line of readFileSync(STORE, 'utf-8').split('\n')) {
|
|
32
|
+
if (!line.trim()) continue;
|
|
33
|
+
try { out.push(JSON.parse(line)); } catch { /* skip malformed line */ }
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function periodFilter(period) {
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
const day = 24 * 3600 * 1000;
|
|
41
|
+
if (period === 'today') return (ts) => ts && new Date(ts).toDateString() === new Date().toDateString();
|
|
42
|
+
if (period === 'week') return (ts) => ts && (now - new Date(ts).getTime()) < 7 * day;
|
|
43
|
+
if (period === 'month') return (ts) => ts && (now - new Date(ts).getTime()) < 30 * day;
|
|
44
|
+
return () => true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function alertLevel(u) {
|
|
48
|
+
if (u >= 1.00) return { level: 'HARD_STOP', emoji: '🛑', threshold: 100 };
|
|
49
|
+
if (u >= 0.90) return { level: 'CRITICAL', emoji: '🔴', threshold: 90 };
|
|
50
|
+
if (u >= 0.75) return { level: 'WARNING', emoji: '🟠', threshold: 75 };
|
|
51
|
+
if (u >= 0.50) return { level: 'INFO', emoji: '🟡', threshold: 50 };
|
|
52
|
+
return { level: 'OK', emoji: '🟢', threshold: 0 };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function recommendedAction(level) {
|
|
56
|
+
return ({
|
|
57
|
+
OK: 'within budget — no action.',
|
|
58
|
+
INFO: '50% consumed — log notification, no UX disruption.',
|
|
59
|
+
WARNING: '75% consumed — suggest /set-cost-profile balanced→minimal.',
|
|
60
|
+
CRITICAL: '90% consumed — recommend model downgrades, consider /set-cost-profile minimal.',
|
|
61
|
+
HARD_STOP: '100% consumed — halt non-essential work; review /cost:report before continuing.',
|
|
62
|
+
}[level]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function cmdSet(args) {
|
|
66
|
+
const amount = parseFloat(args[0]);
|
|
67
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
68
|
+
console.error('usage: budget.mjs set <usd-amount> (positive number)');
|
|
69
|
+
process.exit(2);
|
|
70
|
+
}
|
|
71
|
+
const config = {
|
|
72
|
+
budget_usd: amount,
|
|
73
|
+
setAt: new Date().toISOString(),
|
|
74
|
+
thresholds: { info: 0.50, warning: 0.75, critical: 0.90, hard_stop: 1.00 },
|
|
75
|
+
};
|
|
76
|
+
saveConfig(config);
|
|
77
|
+
if (process.env.BUDGET_QUIET === '1') {
|
|
78
|
+
console.log(JSON.stringify(config));
|
|
79
|
+
} else {
|
|
80
|
+
console.log(`✓ Budget set: $${amount.toFixed(2)} (config: ${CONFIG})`);
|
|
81
|
+
console.log(' Alerts: 50% INFO · 75% WARNING · 90% CRITICAL · 100% HARD_STOP');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function cmdGet() {
|
|
86
|
+
const cfg = loadConfig();
|
|
87
|
+
if (process.env.BUDGET_QUIET === '1') {
|
|
88
|
+
console.log(JSON.stringify(cfg || { error: 'no budget configured' }));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!cfg) {
|
|
92
|
+
console.log(`No budget configured (config: ${CONFIG}).`);
|
|
93
|
+
console.log('Set one with: node scripts/cost/budget.mjs set <usd>');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
console.log(`Budget: $${cfg.budget_usd?.toFixed(2)} (set ${cfg.setAt})`);
|
|
97
|
+
console.log('Thresholds: 50/75/90/100%');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function cmdCheck() {
|
|
101
|
+
const cfg = loadConfig();
|
|
102
|
+
const period = process.env.BUDGET_PERIOD || 'all';
|
|
103
|
+
const filt = periodFilter(period);
|
|
104
|
+
const filtered = loadSessions().filter((r) => filt(r.capturedAt || r.endedAt));
|
|
105
|
+
const totalSpend = filtered.reduce((s, r) => s + (r.total_cost_usd || 0), 0);
|
|
106
|
+
if (!cfg || !Number.isFinite(cfg.budget_usd)) {
|
|
107
|
+
const out = { period, totalSpend, recordCount: filtered.length, error: 'no budget configured' };
|
|
108
|
+
if (process.env.BUDGET_QUIET === '1') return console.log(JSON.stringify(out));
|
|
109
|
+
console.log(`Period: ${period}`);
|
|
110
|
+
console.log(`Spent so far: $${totalSpend.toFixed(2)} across ${filtered.length} sessions`);
|
|
111
|
+
console.log('No budget set — run `node scripts/cost/budget.mjs set <usd>` to enable alerts.');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const utilization = totalSpend / cfg.budget_usd;
|
|
115
|
+
const alert = alertLevel(utilization);
|
|
116
|
+
const out = {
|
|
117
|
+
period,
|
|
118
|
+
budget_usd: cfg.budget_usd,
|
|
119
|
+
spent_usd: totalSpend,
|
|
120
|
+
remaining_usd: Math.max(0, cfg.budget_usd - totalSpend),
|
|
121
|
+
utilization_pct: utilization * 100,
|
|
122
|
+
level: alert.level,
|
|
123
|
+
threshold: alert.threshold,
|
|
124
|
+
recommended_action: recommendedAction(alert.level),
|
|
125
|
+
sessionCount: filtered.length,
|
|
126
|
+
};
|
|
127
|
+
if (process.env.BUDGET_QUIET === '1') return console.log(JSON.stringify(out));
|
|
128
|
+
console.log(`# Budget check (period: ${period})\n`);
|
|
129
|
+
console.log('| Metric | Value |\n|---|---:|');
|
|
130
|
+
console.log(`| Budget | $${cfg.budget_usd.toFixed(2)} |`);
|
|
131
|
+
console.log(`| Spent | $${totalSpend.toFixed(2)} |`);
|
|
132
|
+
console.log(`| Remaining | $${out.remaining_usd.toFixed(2)} |`);
|
|
133
|
+
console.log(`| Utilization | ${out.utilization_pct.toFixed(1)}% |`);
|
|
134
|
+
console.log(`| Sessions counted | ${filtered.length} |`);
|
|
135
|
+
console.log(`| **Alert** | **${alert.emoji} ${alert.level}** |`);
|
|
136
|
+
console.log(`\nAction: ${out.recommended_action}`);
|
|
137
|
+
if (alert.level === 'HARD_STOP') process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function main() {
|
|
141
|
+
const [cmd, ...rest] = process.argv.slice(2);
|
|
142
|
+
switch (cmd) {
|
|
143
|
+
case 'set': return cmdSet(rest);
|
|
144
|
+
case 'get': return cmdGet();
|
|
145
|
+
case 'check': return cmdCheck();
|
|
146
|
+
default:
|
|
147
|
+
console.error('usage: budget.mjs {set <usd>|get|check}');
|
|
148
|
+
process.exit(2);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
main();
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cost-track — auto-capture token usage from a Claude Code session jsonl
|
|
3
|
+
// and append a structured record to agents/cost-tracking/sessions.jsonl.
|
|
4
|
+
//
|
|
5
|
+
// Forked from ruvnet/ruflo plugins/ruflo-cost-tracker/scripts/track.mjs.
|
|
6
|
+
// Local-JSONL swap replaces the upstream `mcp__claude-flow__memory_store`
|
|
7
|
+
// dependency. Pricing constants are kept in sync with REFERENCE.md.
|
|
8
|
+
//
|
|
9
|
+
// Env:
|
|
10
|
+
// TRACK_CWD=<path> override which project's sessions to scan
|
|
11
|
+
// TRACK_SESSION=<file> pin to a specific session jsonl
|
|
12
|
+
// TRACK_OUT=<path> also write the JSON summary to this path
|
|
13
|
+
// TRACK_DRY_RUN=1 skip the JSONL append
|
|
14
|
+
// TRACK_QUIET=1 suppress markdown summary
|
|
15
|
+
// TRACK_STORE=<path> override (default: agents/cost-tracking/sessions.jsonl)
|
|
16
|
+
|
|
17
|
+
import { readFileSync, writeFileSync, readdirSync, statSync, existsSync, mkdirSync, appendFileSync } from 'node:fs';
|
|
18
|
+
import { join, dirname } from 'node:path';
|
|
19
|
+
import { homedir } from 'node:os';
|
|
20
|
+
|
|
21
|
+
const PROJECTS_DIR = join(homedir(), '.claude', 'projects');
|
|
22
|
+
const DEFAULT_STORE = 'agents/cost-tracking/sessions.jsonl';
|
|
23
|
+
|
|
24
|
+
// USD per 1M tokens.
|
|
25
|
+
const PRICING = {
|
|
26
|
+
haiku: { input: 0.25, output: 1.25, cache_write: 0.30, cache_read: 0.03 },
|
|
27
|
+
sonnet: { input: 3.00, output: 15.00, cache_write: 3.75, cache_read: 0.30 },
|
|
28
|
+
opus: { input: 15.00, output: 75.00, cache_write: 18.75, cache_read: 1.50 },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function modelTier(model) {
|
|
32
|
+
if (!model) return 'unknown';
|
|
33
|
+
const m = String(model).toLowerCase();
|
|
34
|
+
if (m.includes('haiku')) return 'haiku';
|
|
35
|
+
if (m.includes('sonnet')) return 'sonnet';
|
|
36
|
+
if (m.includes('opus')) return 'opus';
|
|
37
|
+
return 'unknown';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function costForUsage(tier, u) {
|
|
41
|
+
const p = PRICING[tier];
|
|
42
|
+
if (!p || !u) return 0;
|
|
43
|
+
return (u.input_tokens || 0) / 1e6 * p.input
|
|
44
|
+
+ (u.output_tokens || 0) / 1e6 * p.output
|
|
45
|
+
+ (u.cache_creation_input_tokens || 0) / 1e6 * p.cache_write
|
|
46
|
+
+ (u.cache_read_input_tokens || 0) / 1e6 * p.cache_read;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function encodeProjectPath(cwd) { return cwd.replace(/\//g, '-'); }
|
|
50
|
+
|
|
51
|
+
function findProjectDir(cwd) {
|
|
52
|
+
const c = join(PROJECTS_DIR, encodeProjectPath(cwd));
|
|
53
|
+
return existsSync(c) ? c : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function findActiveSession(dir) {
|
|
57
|
+
const e = readdirSync(dir).filter((f) => f.endsWith('.jsonl'))
|
|
58
|
+
.map((f) => ({ f, mtime: statSync(join(dir, f)).mtimeMs }))
|
|
59
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
60
|
+
return e[0] ? join(dir, e[0].f) : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function summarizeSession(jsonlPath) {
|
|
64
|
+
const lines = readFileSync(jsonlPath, 'utf-8').split('\n').filter(Boolean);
|
|
65
|
+
const byModel = {};
|
|
66
|
+
const byTier = { haiku: 0, sonnet: 0, opus: 0, unknown: 0 };
|
|
67
|
+
let messageCount = 0, totalCost = 0, firstTs = null, lastTs = null;
|
|
68
|
+
let sessionId = null, cwd = null;
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
let m; try { m = JSON.parse(line); } catch { continue; }
|
|
71
|
+
if (!sessionId && m.sessionId) sessionId = m.sessionId;
|
|
72
|
+
if (!cwd && m.cwd) cwd = m.cwd;
|
|
73
|
+
if (m.timestamp) {
|
|
74
|
+
if (!firstTs || m.timestamp < firstTs) firstTs = m.timestamp;
|
|
75
|
+
if (!lastTs || m.timestamp > lastTs) lastTs = m.timestamp;
|
|
76
|
+
}
|
|
77
|
+
if (m.type !== 'assistant' || !m.message?.usage) continue;
|
|
78
|
+
messageCount++;
|
|
79
|
+
const model = m.message.model || 'unknown';
|
|
80
|
+
const tier = modelTier(model);
|
|
81
|
+
const u = m.message.usage;
|
|
82
|
+
const cost = costForUsage(tier, u);
|
|
83
|
+
const slot = byModel[model] || { tier, input_tokens: 0, output_tokens: 0,
|
|
84
|
+
cache_creation_input_tokens: 0, cache_read_input_tokens: 0, messages: 0, cost_usd: 0 };
|
|
85
|
+
slot.input_tokens += u.input_tokens || 0;
|
|
86
|
+
slot.output_tokens += u.output_tokens || 0;
|
|
87
|
+
slot.cache_creation_input_tokens += u.cache_creation_input_tokens || 0;
|
|
88
|
+
slot.cache_read_input_tokens += u.cache_read_input_tokens || 0;
|
|
89
|
+
slot.messages++; slot.cost_usd += cost;
|
|
90
|
+
byModel[model] = slot; byTier[tier] += cost; totalCost += cost;
|
|
91
|
+
}
|
|
92
|
+
return { sessionId, cwd, startedAt: firstTs, endedAt: lastTs, messageCount,
|
|
93
|
+
byModel, byTier, total_cost_usd: totalCost, capturedAt: new Date().toISOString() };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function persistJsonl(summary, store) {
|
|
97
|
+
mkdirSync(dirname(store), { recursive: true });
|
|
98
|
+
appendFileSync(store, JSON.stringify(summary) + '\n');
|
|
99
|
+
return { ok: true, path: store };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function main() {
|
|
103
|
+
const targetCwd = process.env.TRACK_CWD || process.cwd();
|
|
104
|
+
const projectDir = findProjectDir(targetCwd);
|
|
105
|
+
if (!projectDir) {
|
|
106
|
+
console.error(`cost-track: no Claude Code project dir for cwd=${targetCwd}`);
|
|
107
|
+
console.error(`looked under ${PROJECTS_DIR}/${encodeProjectPath(targetCwd)}`);
|
|
108
|
+
process.exit(2);
|
|
109
|
+
}
|
|
110
|
+
const sessionPath = process.env.TRACK_SESSION || findActiveSession(projectDir);
|
|
111
|
+
if (!sessionPath || !existsSync(sessionPath)) {
|
|
112
|
+
console.error(`cost-track: no session jsonl in ${projectDir}`); process.exit(2);
|
|
113
|
+
}
|
|
114
|
+
const summary = summarizeSession(sessionPath);
|
|
115
|
+
if (process.env.TRACK_OUT) writeFileSync(process.env.TRACK_OUT, JSON.stringify(summary, null, 2));
|
|
116
|
+
const store = process.env.TRACK_STORE || DEFAULT_STORE;
|
|
117
|
+
let res = { ok: false, reason: 'dry-run' };
|
|
118
|
+
if (process.env.TRACK_DRY_RUN !== '1') res = persistJsonl(summary, store);
|
|
119
|
+
if (process.env.TRACK_QUIET === '1') return;
|
|
120
|
+
|
|
121
|
+
console.log(`# cost-track — session ${(summary.sessionId || '').slice(0, 8) || 'unknown'}`);
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log('| Metric | Value |\n|---|---:|');
|
|
124
|
+
console.log(`| Session ID | \`${summary.sessionId}\` |`);
|
|
125
|
+
console.log(`| Project | \`${summary.cwd}\` |`);
|
|
126
|
+
console.log(`| First message | ${summary.startedAt} |`);
|
|
127
|
+
console.log(`| Last message | ${summary.endedAt} |`);
|
|
128
|
+
console.log(`| Assistant messages | ${summary.messageCount} |`);
|
|
129
|
+
console.log(`| **Total cost** | **$${summary.total_cost_usd.toFixed(6)}** |`);
|
|
130
|
+
console.log(`| Persisted | ${res.ok ? `\`${res.path}\`` : `**FAILED** (${res.reason})`} |`);
|
|
131
|
+
console.log('\n## Per-model breakdown\n');
|
|
132
|
+
console.log('| Model | Tier | Messages | Input | Output | Cache write | Cache read | Cost |');
|
|
133
|
+
console.log('|---|---|---:|---:|---:|---:|---:|---:|');
|
|
134
|
+
for (const [m, s] of Object.entries(summary.byModel).sort((a, b) => b[1].cost_usd - a[1].cost_usd)) {
|
|
135
|
+
console.log(`| \`${m}\` | ${s.tier} | ${s.messages} | ${s.input_tokens} | ${s.output_tokens} | ${s.cache_creation_input_tokens} | ${s.cache_read_input_tokens} | $${s.cost_usd.toFixed(6)} |`);
|
|
136
|
+
}
|
|
137
|
+
console.log('\n## Per-tier breakdown\n');
|
|
138
|
+
console.log('| Tier | Cost |\n|---|---:|');
|
|
139
|
+
for (const [t, c] of Object.entries(summary.byTier).sort((a, b) => b[1] - a[1])) {
|
|
140
|
+
if (c > 0) console.log(`| ${t} | $${c.toFixed(6)} |`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
main();
|
package/scripts/first-run.sh
CHANGED
|
@@ -5,9 +5,7 @@ SETTINGS_FILE=".agent-settings.yml"
|
|
|
5
5
|
LEGACY_SETTINGS_FILE=".agent-settings"
|
|
6
6
|
|
|
7
7
|
echo ""
|
|
8
|
-
echo "
|
|
9
|
-
echo " Agent Config — First Run"
|
|
10
|
-
echo "========================================"
|
|
8
|
+
echo "Agent Config — First Run"
|
|
11
9
|
echo ""
|
|
12
10
|
|
|
13
11
|
# --- Profile detection ---
|
|
@@ -63,9 +61,7 @@ echo " ✅ Zero token overhead in minimal mode"
|
|
|
63
61
|
echo ""
|
|
64
62
|
|
|
65
63
|
# --- 3 test prompts ---
|
|
66
|
-
echo "
|
|
67
|
-
echo " Try these 3 prompts now"
|
|
68
|
-
echo "========================================"
|
|
64
|
+
echo "Try these 3 prompts now:"
|
|
69
65
|
echo ""
|
|
70
66
|
|
|
71
67
|
echo "1️⃣ Refactoring check"
|
|
@@ -94,9 +90,7 @@ echo " → Agent challenges weak requirements"
|
|
|
94
90
|
echo ""
|
|
95
91
|
|
|
96
92
|
# --- Next steps ---
|
|
97
|
-
echo "
|
|
98
|
-
echo " Next steps"
|
|
99
|
-
echo "========================================"
|
|
93
|
+
echo "Next steps:"
|
|
100
94
|
echo ""
|
|
101
95
|
echo "Cost profiles:"
|
|
102
96
|
echo " minimal rules, skills, commands only"
|
package/scripts/install-hooks.sh
CHANGED
|
@@ -39,7 +39,9 @@ echo "✅ Pre-push hook installed."
|
|
|
39
39
|
cat > "$HOOKS_DIR/pre-commit" << 'EOF'
|
|
40
40
|
#!/usr/bin/env bash
|
|
41
41
|
# Pre-commit hook: verify .claude-plugin/marketplace.json lists every skill
|
|
42
|
-
# that exists on disk under .claude/skills
|
|
42
|
+
# that exists on disk under .claude/skills/, AND verify
|
|
43
|
+
# agents/roadmaps-progress.md is in sync with the current state of
|
|
44
|
+
# agents/roadmaps/ (roadmap-progress-sync Iron Law).
|
|
43
45
|
|
|
44
46
|
python3 scripts/lint_marketplace.py
|
|
45
47
|
status=$?
|
|
@@ -52,6 +54,22 @@ if [ $status -ne 0 ]; then
|
|
|
52
54
|
echo " git commit --no-verify"
|
|
53
55
|
exit 1
|
|
54
56
|
fi
|
|
57
|
+
|
|
58
|
+
# Roadmap dashboard sync — only fires when staged changes touch a roadmap
|
|
59
|
+
# file or the dashboard itself, so unrelated commits stay fast.
|
|
60
|
+
if git diff --cached --name-only | grep -qE '^agents/roadmaps(-progress\.md|/)'; then
|
|
61
|
+
python3 .augment/scripts/update_roadmap_progress.py --check
|
|
62
|
+
rstatus=$?
|
|
63
|
+
if [ $rstatus -ne 0 ]; then
|
|
64
|
+
echo ""
|
|
65
|
+
echo "❌ Commit blocked — agents/roadmaps-progress.md is stale."
|
|
66
|
+
echo " Run './agent-config roadmap:progress' (or"
|
|
67
|
+
echo " 'python3 .augment/scripts/update_roadmap_progress.py'),"
|
|
68
|
+
echo " stage agents/roadmaps-progress.md, then re-commit."
|
|
69
|
+
echo " To bypass for an unrelated WIP commit: git commit --no-verify"
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
55
73
|
EOF
|
|
56
74
|
|
|
57
75
|
chmod +x "$HOOKS_DIR/pre-commit"
|
package/scripts/install.py
CHANGED
|
@@ -1280,6 +1280,7 @@ def main(argv: list[str]) -> int:
|
|
|
1280
1280
|
fail(f"Unsupported profile: {opts.profile}. Supported: {', '.join(SUPPORTED_PROFILES)}")
|
|
1281
1281
|
|
|
1282
1282
|
project_root = Path(opts.project or os.environ.get("PROJECT_ROOT") or os.getcwd()).resolve()
|
|
1283
|
+
is_first_run = not (project_root / SETTINGS_FILE).exists()
|
|
1283
1284
|
|
|
1284
1285
|
if opts.package:
|
|
1285
1286
|
package_root = Path(opts.package).resolve()
|
|
@@ -1335,18 +1336,22 @@ def main(argv: list[str]) -> int:
|
|
|
1335
1336
|
if not QUIET:
|
|
1336
1337
|
print()
|
|
1337
1338
|
success("Done.")
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1339
|
+
if is_first_run:
|
|
1340
|
+
print()
|
|
1341
|
+
print(" Try these 3 prompts with your agent:")
|
|
1342
|
+
print(' 1. "Refactor this function" → agent analyzes first')
|
|
1343
|
+
print(' 2. "Add caching to this" → agent asks instead of guessing')
|
|
1344
|
+
print(' 3. "Implement this feature" → agent respects your codebase')
|
|
1345
|
+
print()
|
|
1346
|
+
print(" Next steps:")
|
|
1347
|
+
print(" • Commit .agent-settings.yml and bridge files to your repo")
|
|
1348
|
+
print(" • New team members just run composer install / npm install — done")
|
|
1349
|
+
print(" • Inspect hook coverage: ./agent-config hooks:status")
|
|
1350
|
+
print(" • Full walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md")
|
|
1351
|
+
print()
|
|
1352
|
+
else:
|
|
1353
|
+
print(" Re-run complete. Walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md")
|
|
1354
|
+
print()
|
|
1350
1355
|
return 0
|
|
1351
1356
|
|
|
1352
1357
|
|
package/scripts/install.sh
CHANGED
|
@@ -693,11 +693,18 @@ install_cli_wrapper() {
|
|
|
693
693
|
main() {
|
|
694
694
|
parse_args "$@"
|
|
695
695
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
696
|
+
# First-run detection: gate the verbose source/target banner behind the
|
|
697
|
+
# absence of .agent-settings.yml. Re-runs print a single status line.
|
|
698
|
+
local is_first_run=false
|
|
699
|
+
[[ ! -f "$TARGET_DIR/.agent-settings.yml" ]] && is_first_run=true
|
|
700
|
+
|
|
701
|
+
if $is_first_run && ! $QUIET; then
|
|
702
|
+
echo "🔧 Syncing agent-config payload..."
|
|
703
|
+
echo " Source: $SOURCE_DIR"
|
|
704
|
+
echo " Target: $TARGET_DIR"
|
|
705
|
+
$DRY_RUN && echo " Mode: DRY RUN"
|
|
706
|
+
echo ""
|
|
707
|
+
fi
|
|
701
708
|
|
|
702
709
|
# 0. Migrate legacy infra files (root → agents/) before any content sync.
|
|
703
710
|
migrate_legacy_root_infra "$TARGET_DIR"
|
|
@@ -737,9 +744,13 @@ main() {
|
|
|
737
744
|
# 6. Manage .gitignore
|
|
738
745
|
ensure_gitignore "$TARGET_DIR"
|
|
739
746
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
747
|
+
if $is_first_run && ! $QUIET; then
|
|
748
|
+
echo ""
|
|
749
|
+
echo "✅ agent-config payload synced."
|
|
750
|
+
echo " Run scripts/install (or python3 scripts/install.py) to render .agent-settings.yml and bridges."
|
|
751
|
+
elif ! $QUIET; then
|
|
752
|
+
echo "✅ agent-config payload synced."
|
|
753
|
+
fi
|
|
743
754
|
}
|
|
744
755
|
|
|
745
756
|
main "$@"
|
package/scripts/lint_examples.py
CHANGED
|
@@ -15,6 +15,8 @@ import re
|
|
|
15
15
|
import sys
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
|
+
QUIET = "--quiet" in sys.argv
|
|
19
|
+
|
|
18
20
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
19
21
|
DEMO_GLOB = "docs/guidelines/agent-infra/*-demos.md"
|
|
20
22
|
REQUIRED_FM_KEYS = ("demo_for:", "layer: pattern-memory", "prose_delta:")
|
|
@@ -86,11 +88,13 @@ def main() -> int:
|
|
|
86
88
|
for p in problems:
|
|
87
89
|
print(f" - {p}", file=sys.stderr)
|
|
88
90
|
else:
|
|
89
|
-
|
|
91
|
+
if not QUIET:
|
|
92
|
+
print(f"✅ {rel}")
|
|
90
93
|
if failed:
|
|
91
94
|
print(f"\n❌ {failed} demo file(s) failed shape lint", file=sys.stderr)
|
|
92
95
|
return 1
|
|
93
|
-
|
|
96
|
+
if not QUIET:
|
|
97
|
+
print(f"\n✅ {len(demos)} demo file(s) shape-clean")
|
|
94
98
|
return 0
|
|
95
99
|
|
|
96
100
|
|
package/scripts/lint_handoffs.py
CHANGED
|
@@ -24,6 +24,8 @@ from dataclasses import dataclass
|
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
from typing import Iterable
|
|
26
26
|
|
|
27
|
+
QUIET = "--quiet" in sys.argv
|
|
28
|
+
|
|
27
29
|
REPO = Path(__file__).resolve().parents[1]
|
|
28
30
|
SKILLS_DIR = REPO / ".agent-src.uncompressed" / "skills"
|
|
29
31
|
|
|
@@ -202,7 +204,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
202
204
|
skills_dir = Path(argv[0]).resolve()
|
|
203
205
|
violations = lint(skills_dir)
|
|
204
206
|
if not violations:
|
|
205
|
-
|
|
207
|
+
if not QUIET:
|
|
208
|
+
print(f"✅ lint_handoffs: no violations under {skills_dir.relative_to(REPO)}")
|
|
206
209
|
return 0
|
|
207
210
|
for v in violations:
|
|
208
211
|
print(v.render(REPO))
|
|
@@ -19,6 +19,8 @@ from typing import Iterable
|
|
|
19
19
|
|
|
20
20
|
import yaml
|
|
21
21
|
|
|
22
|
+
QUIET = "--quiet" in sys.argv
|
|
23
|
+
|
|
22
24
|
ROOT = Path(__file__).resolve().parent.parent
|
|
23
25
|
|
|
24
26
|
SCAN_DIRS = [
|
|
@@ -185,7 +187,8 @@ def main() -> int:
|
|
|
185
187
|
print(f"❌ {e}")
|
|
186
188
|
if errors:
|
|
187
189
|
return 1
|
|
188
|
-
|
|
190
|
+
if not QUIET:
|
|
191
|
+
print(f"✅ load_context schema clean ({len(graph)} declarer(s))")
|
|
189
192
|
return 0
|
|
190
193
|
|
|
191
194
|
|
|
@@ -21,6 +21,8 @@ import re
|
|
|
21
21
|
import sys
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
|
|
24
|
+
QUIET = "--quiet" in sys.argv
|
|
25
|
+
|
|
24
26
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
25
27
|
ROADMAP_GLOB = "agents/roadmaps/*.md"
|
|
26
28
|
LIGHTWEIGHT_LINE_CAP = 600
|
|
@@ -107,7 +109,8 @@ def main() -> int:
|
|
|
107
109
|
for p in problems:
|
|
108
110
|
print(f" - {p}", file=sys.stderr)
|
|
109
111
|
else:
|
|
110
|
-
|
|
112
|
+
if not QUIET:
|
|
113
|
+
print(f"✅ {rel} [{complexity}]")
|
|
111
114
|
print()
|
|
112
115
|
light = sum(1 for _, c in summary if c == "lightweight")
|
|
113
116
|
structural = sum(1 for _, c in summary if c == "structural")
|
|
@@ -119,7 +122,8 @@ def main() -> int:
|
|
|
119
122
|
if failed:
|
|
120
123
|
print(f"\n❌ {failed} roadmap(s) failed complexity lint", file=sys.stderr)
|
|
121
124
|
return 1
|
|
122
|
-
|
|
125
|
+
if not QUIET:
|
|
126
|
+
print(f"\n✅ {len(roadmaps)} roadmap(s) complexity-clean")
|
|
123
127
|
return 0
|
|
124
128
|
|
|
125
129
|
|
|
@@ -22,6 +22,8 @@ from pathlib import Path
|
|
|
22
22
|
|
|
23
23
|
import yaml
|
|
24
24
|
|
|
25
|
+
QUIET = "--quiet" in sys.argv
|
|
26
|
+
|
|
25
27
|
ROOT = Path(__file__).resolve().parent.parent
|
|
26
28
|
MATRIX = ROOT / "docs" / "contracts" / "rule-interactions.yml"
|
|
27
29
|
RULES_DIR = ROOT / ".agent-src.uncompressed" / "rules"
|
|
@@ -141,7 +143,8 @@ def main() -> int:
|
|
|
141
143
|
if errors:
|
|
142
144
|
fail(errors)
|
|
143
145
|
|
|
144
|
-
|
|
146
|
+
if not QUIET:
|
|
147
|
+
print(f"✅ rule-interactions.yml clean — {len(declared_rules)} rules, {len(pairs)} pairs.")
|
|
145
148
|
return 0
|
|
146
149
|
|
|
147
150
|
|
|
@@ -17,6 +17,8 @@ from __future__ import annotations
|
|
|
17
17
|
import sys
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
|
+
QUIET = "--quiet" in sys.argv
|
|
21
|
+
|
|
20
22
|
REPO = Path(__file__).resolve().parents[1]
|
|
21
23
|
RULES_DIR = REPO / ".agent-src.uncompressed" / "rules"
|
|
22
24
|
|
|
@@ -70,7 +72,8 @@ def main() -> int:
|
|
|
70
72
|
)
|
|
71
73
|
return 1
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
if not QUIET:
|
|
76
|
+
print(f"✅ lint_rule_tiers: {len(rules)} rules, all tier values valid")
|
|
74
77
|
return 0
|
|
75
78
|
|
|
76
79
|
|