aiblueprint-cli 1.1.8 → 1.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.
Files changed (40) hide show
  1. package/claude-code-config/scripts/command-validator/README.md +147 -0
  2. package/claude-code-config/scripts/command-validator/biome.json +29 -0
  3. package/claude-code-config/scripts/command-validator/bun.lockb +0 -0
  4. package/claude-code-config/scripts/command-validator/dist/cli.js +544 -0
  5. package/claude-code-config/scripts/command-validator/package.json +27 -0
  6. package/claude-code-config/scripts/command-validator/src/__tests__/validator.test.ts +148 -0
  7. package/claude-code-config/scripts/command-validator/src/cli.ts +118 -0
  8. package/claude-code-config/scripts/command-validator/src/lib/security-rules.ts +172 -0
  9. package/claude-code-config/scripts/command-validator/src/lib/types.ts +33 -0
  10. package/claude-code-config/scripts/command-validator/src/lib/validator.ts +360 -0
  11. package/claude-code-config/scripts/command-validator/vitest.config.ts +7 -0
  12. package/claude-code-config/scripts/statusline/package.json +1 -3
  13. package/claude-code-config/scripts/statusline/src/index.ts +5 -107
  14. package/claude-code-config/scripts/statusline/src/lib/context.ts +66 -87
  15. package/claude-code-config/scripts/statusline/src/lib/formatters.ts +16 -186
  16. package/claude-code-config/scripts/statusline/statusline.config.ts +4 -101
  17. package/dist/cli.js +938 -12
  18. package/package.json +1 -1
  19. package/claude-code-config/agents/fix-grammar.md +0 -49
  20. package/claude-code-config/agents/snipper.md +0 -36
  21. package/claude-code-config/commands/claude-memory.md +0 -190
  22. package/claude-code-config/commands/cleanup-context.md +0 -82
  23. package/claude-code-config/commands/debug.md +0 -91
  24. package/claude-code-config/commands/deep-code-analysis.md +0 -87
  25. package/claude-code-config/commands/epct/code.md +0 -171
  26. package/claude-code-config/commands/epct/deploy.md +0 -116
  27. package/claude-code-config/commands/epct/explore.md +0 -97
  28. package/claude-code-config/commands/epct/plan.md +0 -132
  29. package/claude-code-config/commands/epct/tasks.md +0 -206
  30. package/claude-code-config/commands/explain-architecture.md +0 -113
  31. package/claude-code-config/commands/melvynx-plugin.md +0 -1
  32. package/claude-code-config/commands/prompt-agent.md +0 -126
  33. package/claude-code-config/commands/prompt-command.md +0 -225
  34. package/claude-code-config/scripts/statusline/data/.gitignore +0 -5
  35. package/claude-code-config/scripts/statusline/src/commands/CLAUDE.md +0 -3
  36. package/claude-code-config/scripts/statusline/src/commands/spend-month.ts +0 -60
  37. package/claude-code-config/scripts/statusline/src/commands/spend-today.ts +0 -42
  38. package/claude-code-config/scripts/statusline/src/lib/git.ts +0 -100
  39. package/claude-code-config/scripts/statusline/src/lib/spend.ts +0 -119
  40. package/claude-code-config/scripts/statusline/src/lib/usage-limits.ts +0 -147
@@ -1,60 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { formatCost, formatDuration } from "../lib/formatters";
4
- import {
5
- calculateTotalCost,
6
- calculateTotalDuration,
7
- filterSessionsByDate,
8
- getMonthStart,
9
- loadSpendData,
10
- } from "../lib/spend";
11
-
12
- async function main() {
13
- const data = await loadSpendData();
14
- const monthStart = getMonthStart();
15
- const monthSessions = filterSessionsByDate(data.sessions, monthStart);
16
-
17
- if (monthSessions.length === 0) {
18
- console.log("šŸ“Š No sessions this month");
19
- return;
20
- }
21
-
22
- const totalCost = calculateTotalCost(monthSessions);
23
- const totalDuration = calculateTotalDuration(monthSessions);
24
-
25
- // Group by date
26
- const sessionsByDate = monthSessions.reduce(
27
- (acc, session) => {
28
- if (!acc[session.date]) {
29
- acc[session.date] = [];
30
- }
31
- acc[session.date].push(session);
32
- return acc;
33
- },
34
- {} as Record<string, typeof monthSessions>,
35
- );
36
-
37
- const monthName = monthStart.toLocaleString("default", { month: "long" });
38
-
39
- console.log(`\nšŸ“Š ${monthName}'s Spend\n`);
40
- console.log(`Sessions: ${monthSessions.length}`);
41
- console.log(`Total Cost: $${formatCost(totalCost)}`);
42
- console.log(`Total Duration: ${formatDuration(totalDuration)}`);
43
- console.log(`\nšŸ“… By Date:\n`);
44
-
45
- const sortedDates = Object.keys(sessionsByDate).sort();
46
-
47
- for (const date of sortedDates) {
48
- const sessions = sessionsByDate[date];
49
- const dayCost = calculateTotalCost(sessions);
50
- const dayDuration = calculateTotalDuration(sessions);
51
-
52
- console.log(
53
- ` ${date}: $${formatCost(dayCost)} (${formatDuration(dayDuration)}) - ${sessions.length} session(s)`,
54
- );
55
- }
56
-
57
- console.log("");
58
- }
59
-
60
- main();
@@ -1,42 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { formatCost, formatDuration } from "../lib/formatters";
4
- import {
5
- calculateTotalCost,
6
- calculateTotalDuration,
7
- filterSessionsByDate,
8
- getTodayStart,
9
- loadSpendData,
10
- } from "../lib/spend";
11
-
12
- async function main() {
13
- const data = await loadSpendData();
14
- const today = getTodayStart();
15
- const todaySessions = filterSessionsByDate(data.sessions, today);
16
-
17
- if (todaySessions.length === 0) {
18
- console.log("šŸ“Š No sessions today");
19
- return;
20
- }
21
-
22
- const totalCost = calculateTotalCost(todaySessions);
23
- const totalDuration = calculateTotalDuration(todaySessions);
24
-
25
- console.log(`\nšŸ“Š Today's Spend\n`);
26
- console.log(`Sessions: ${todaySessions.length}`);
27
- console.log(`Total Cost: $${formatCost(totalCost)}`);
28
- console.log(`Total Duration: ${formatDuration(totalDuration)}`);
29
- console.log(`\nšŸ“ Sessions:\n`);
30
-
31
- for (const session of todaySessions) {
32
- console.log(
33
- ` • $${formatCost(session.cost)} (${formatDuration(session.duration_ms)})`,
34
- );
35
- console.log(` ${session.cwd}`);
36
- console.log(
37
- ` +${session.lines_added} -${session.lines_removed} lines\n`,
38
- );
39
- }
40
- }
41
-
42
- main();
@@ -1,100 +0,0 @@
1
- import { $ } from "bun";
2
-
3
- export interface GitStatus {
4
- branch: string;
5
- hasChanges: boolean;
6
- staged: {
7
- added: number;
8
- deleted: number;
9
- files: number;
10
- };
11
- unstaged: {
12
- added: number;
13
- deleted: number;
14
- files: number;
15
- };
16
- }
17
-
18
- export async function getGitStatus(): Promise<GitStatus> {
19
- try {
20
- const isGitRepo = await $`git rev-parse --git-dir`.quiet().nothrow();
21
- if (isGitRepo.exitCode !== 0) {
22
- return {
23
- branch: "no-git",
24
- hasChanges: false,
25
- staged: { added: 0, deleted: 0, files: 0 },
26
- unstaged: { added: 0, deleted: 0, files: 0 },
27
- };
28
- }
29
-
30
- const branchResult = await $`git branch --show-current`.quiet().text();
31
- const branch = branchResult.trim() || "detached";
32
-
33
- const diffCheck = await $`git diff-index --quiet HEAD --`.quiet().nothrow();
34
- const cachedCheck = await $`git diff-index --quiet --cached HEAD --`
35
- .quiet()
36
- .nothrow();
37
-
38
- if (diffCheck.exitCode !== 0 || cachedCheck.exitCode !== 0) {
39
- const unstagedDiff = await $`git diff --numstat`.quiet().text();
40
- const stagedDiff = await $`git diff --cached --numstat`.quiet().text();
41
- const stagedFilesResult = await $`git diff --cached --name-only`
42
- .quiet()
43
- .text();
44
- const unstagedFilesResult = await $`git diff --name-only`.quiet().text();
45
-
46
- const parseStats = (diff: string) => {
47
- let added = 0;
48
- let deleted = 0;
49
- for (const line of diff.split("\n")) {
50
- if (!line.trim()) continue;
51
- const [a, d] = line
52
- .split("\t")
53
- .map((n) => Number.parseInt(n, 10) || 0);
54
- added += a;
55
- deleted += d;
56
- }
57
- return { added, deleted };
58
- };
59
-
60
- const unstagedStats = parseStats(unstagedDiff);
61
- const stagedStats = parseStats(stagedDiff);
62
-
63
- const stagedFilesCount = stagedFilesResult
64
- .split("\n")
65
- .filter((f) => f.trim()).length;
66
- const unstagedFilesCount = unstagedFilesResult
67
- .split("\n")
68
- .filter((f) => f.trim()).length;
69
-
70
- return {
71
- branch,
72
- hasChanges: true,
73
- staged: {
74
- added: stagedStats.added,
75
- deleted: stagedStats.deleted,
76
- files: stagedFilesCount,
77
- },
78
- unstaged: {
79
- added: unstagedStats.added,
80
- deleted: unstagedStats.deleted,
81
- files: unstagedFilesCount,
82
- },
83
- };
84
- }
85
-
86
- return {
87
- branch,
88
- hasChanges: false,
89
- staged: { added: 0, deleted: 0, files: 0 },
90
- unstaged: { added: 0, deleted: 0, files: 0 },
91
- };
92
- } catch {
93
- return {
94
- branch: "no-git",
95
- hasChanges: false,
96
- staged: { added: 0, deleted: 0, files: 0 },
97
- unstaged: { added: 0, deleted: 0, files: 0 },
98
- };
99
- }
100
- }
@@ -1,119 +0,0 @@
1
- import { existsSync, mkdirSync } from "node:fs";
2
- import { readFile, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import type { HookInput } from "./types";
5
-
6
- export interface SpendSession {
7
- id: string;
8
- cost: number;
9
- date: string;
10
- duration_ms: number;
11
- lines_added: number;
12
- lines_removed: number;
13
- cwd: string;
14
- }
15
-
16
- export interface SpendData {
17
- sessions: SpendSession[];
18
- }
19
-
20
- export function getSpendFilePath(): string {
21
- // Use the project's data folder instead of ~/.claude
22
- const projectRoot = join(import.meta.dir, "..", "..");
23
- return join(projectRoot, "data", "spend.json");
24
- }
25
-
26
- export async function loadSpendData(): Promise<SpendData> {
27
- const spendFile = getSpendFilePath();
28
-
29
- if (!existsSync(spendFile)) {
30
- return { sessions: [] };
31
- }
32
-
33
- try {
34
- const content = await readFile(spendFile, "utf-8");
35
- return JSON.parse(content);
36
- } catch {
37
- return { sessions: [] };
38
- }
39
- }
40
-
41
- export async function saveSpendData(data: SpendData): Promise<void> {
42
- const spendFile = getSpendFilePath();
43
- const projectRoot = join(import.meta.dir, "..", "..");
44
- const dataDir = join(projectRoot, "data");
45
-
46
- if (!existsSync(dataDir)) {
47
- mkdirSync(dataDir, { recursive: true });
48
- }
49
-
50
- await writeFile(spendFile, JSON.stringify(data, null, 2));
51
- }
52
-
53
- export async function saveSession(input: HookInput): Promise<void> {
54
- if (
55
- !input.session_id ||
56
- input.cost.total_cost_usd === 0 ||
57
- input.cost.total_cost_usd === null
58
- ) {
59
- return;
60
- }
61
-
62
- const data = await loadSpendData();
63
- const isoDate = new Date().toISOString().split("T")[0];
64
-
65
- const session: SpendSession = {
66
- id: input.session_id,
67
- cost: input.cost.total_cost_usd,
68
- date: isoDate,
69
- duration_ms: input.cost.total_duration_ms,
70
- lines_added: input.cost.total_lines_added,
71
- lines_removed: input.cost.total_lines_removed,
72
- cwd: input.cwd,
73
- };
74
-
75
- const existingIndex = data.sessions.findIndex(
76
- (s) => s.id === input.session_id,
77
- );
78
-
79
- if (existingIndex !== -1) {
80
- data.sessions[existingIndex] = session;
81
- } else {
82
- data.sessions.push(session);
83
- }
84
-
85
- await saveSpendData(data);
86
- }
87
-
88
- export function filterSessionsByDate(
89
- sessions: SpendSession[],
90
- startDate: Date,
91
- endDate?: Date,
92
- ): SpendSession[] {
93
- return sessions.filter((session) => {
94
- const sessionDate = new Date(session.date);
95
- if (endDate) {
96
- return sessionDate >= startDate && sessionDate <= endDate;
97
- }
98
- return sessionDate >= startDate;
99
- });
100
- }
101
-
102
- export function calculateTotalCost(sessions: SpendSession[]): number {
103
- return sessions.reduce((sum, session) => sum + session.cost, 0);
104
- }
105
-
106
- export function calculateTotalDuration(sessions: SpendSession[]): number {
107
- return sessions.reduce((sum, session) => sum + session.duration_ms, 0);
108
- }
109
-
110
- export function getTodayStart(): Date {
111
- const today = new Date();
112
- today.setHours(0, 0, 0, 0);
113
- return today;
114
- }
115
-
116
- export function getMonthStart(): Date {
117
- const today = new Date();
118
- return new Date(today.getFullYear(), today.getMonth(), 1);
119
- }
@@ -1,147 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { readFile, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { $ } from "bun";
5
-
6
- export interface UsageLimits {
7
- five_hour: {
8
- utilization: number;
9
- resets_at: string | null;
10
- } | null;
11
- seven_day: {
12
- utilization: number;
13
- resets_at: string | null;
14
- } | null;
15
- }
16
-
17
- interface CachedUsageLimits {
18
- data: UsageLimits;
19
- timestamp: number;
20
- }
21
-
22
- const CACHE_DURATION_MS = 60 * 1000; // 1 minute
23
-
24
- function getCacheFilePath(): string {
25
- const projectRoot = join(import.meta.dir, "..", "..");
26
- return join(projectRoot, "data", "usage-limits-cache.json");
27
- }
28
-
29
- interface Credentials {
30
- claudeAiOauth: {
31
- accessToken: string;
32
- refreshToken: string;
33
- expiresAt: number;
34
- scopes: string[];
35
- subscriptionType: string;
36
- };
37
- }
38
-
39
- export async function getCredentials(): Promise<string | null> {
40
- try {
41
- const result =
42
- await $`security find-generic-password -s "Claude Code-credentials" -w`
43
- .quiet()
44
- .text();
45
- const creds: Credentials = JSON.parse(result.trim());
46
- return creds.claudeAiOauth.accessToken;
47
- } catch {
48
- return null;
49
- }
50
- }
51
-
52
- export async function fetchUsageLimits(
53
- token: string,
54
- ): Promise<UsageLimits | null> {
55
- try {
56
- const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
57
- method: "GET",
58
- headers: {
59
- Accept: "application/json, text/plain, */*",
60
- "Content-Type": "application/json",
61
- "User-Agent": "claude-code/2.0.31",
62
- Authorization: `Bearer ${token}`,
63
- "anthropic-beta": "oauth-2025-04-20",
64
- "Accept-Encoding": "gzip, compress, deflate, br",
65
- },
66
- });
67
-
68
- if (!response.ok) {
69
- return null;
70
- }
71
-
72
- const data = await response.json();
73
-
74
- return {
75
- five_hour: data.five_hour || null,
76
- seven_day: data.seven_day || null,
77
- };
78
- } catch {
79
- return null;
80
- }
81
- }
82
-
83
- async function loadCache(): Promise<CachedUsageLimits | null> {
84
- try {
85
- const cacheFile = getCacheFilePath();
86
- if (!existsSync(cacheFile)) {
87
- return null;
88
- }
89
-
90
- const content = await readFile(cacheFile, "utf-8");
91
- const cached: CachedUsageLimits = JSON.parse(content);
92
-
93
- // Check if cache is still valid (< 1 minute old)
94
- const now = Date.now();
95
- if (now - cached.timestamp < CACHE_DURATION_MS) {
96
- return cached;
97
- }
98
-
99
- return null;
100
- } catch {
101
- return null;
102
- }
103
- }
104
-
105
- async function saveCache(data: UsageLimits): Promise<void> {
106
- try {
107
- const cacheFile = getCacheFilePath();
108
- const cached: CachedUsageLimits = {
109
- data,
110
- timestamp: Date.now(),
111
- };
112
-
113
- await writeFile(cacheFile, JSON.stringify(cached, null, 2));
114
- } catch {
115
- // Fail silently
116
- }
117
- }
118
-
119
- export async function getUsageLimits(): Promise<UsageLimits> {
120
- try {
121
- // Try to load from cache first
122
- const cached = await loadCache();
123
- if (cached) {
124
- return cached.data;
125
- }
126
-
127
- // Cache miss or expired - fetch from API
128
- const token = await getCredentials();
129
-
130
- if (!token) {
131
- return { five_hour: null, seven_day: null };
132
- }
133
-
134
- const limits = await fetchUsageLimits(token);
135
-
136
- if (!limits) {
137
- return { five_hour: null, seven_day: null };
138
- }
139
-
140
- // Save to cache
141
- await saveCache(limits);
142
-
143
- return limits;
144
- } catch {
145
- return { five_hour: null, seven_day: null };
146
- }
147
- }