claude-setup 1.1.4 → 1.1.5

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.
@@ -6,6 +6,22 @@
6
6
  */
7
7
  export const MARKETPLACE_REPO = "jeremylongshore/claude-code-plugins-plus-skills";
8
8
  export const MARKETPLACE_CATALOG_URL = `https://raw.githubusercontent.com/${MARKETPLACE_REPO}/main/.claude-plugin/marketplace.extended.json`;
9
+ /** Additional marketplace sources for broader coverage */
10
+ export const ADDITIONAL_MARKETPLACE_SOURCES = [
11
+ {
12
+ name: "claude-plugins-official",
13
+ description: "Official Anthropic plugins (GitHub, Slack, Linear, Notion, etc.)",
14
+ installPrefix: "claude-plugins-official",
15
+ note: "No marketplace add needed — available by default"
16
+ },
17
+ {
18
+ name: "awesome-claude-code",
19
+ description: "Community collection of Claude Code skills and workflows",
20
+ catalogUrl: "https://raw.githubusercontent.com/hesreallyhim/awesome-claude-code/main/catalog.json",
21
+ installPrefix: null,
22
+ note: "Browse and manually install skills"
23
+ }
24
+ ];
9
25
  /** The 20 skill categories in the marketplace */
10
26
  export const SKILL_CATEGORIES = [
11
27
  "01-code-quality", "02-testing", "03-security",
@@ -83,77 +99,117 @@ export function classifyRequest(input) {
83
99
  }
84
100
  return { categories: [...categories], saasMatches };
85
101
  }
86
- /** Generate marketplace search instructions for the add template */
102
+ /** Generate fully-automated marketplace search and install instructions */
87
103
  export function buildMarketplaceInstructions(input) {
88
104
  const { categories, saasMatches } = classifyRequest(input);
105
+ const categoryFilter = categories[0] ?? "";
89
106
  const lines = [];
90
- lines.push(`## Marketplace intelligence`);
107
+ lines.push(`## Marketplace — fully automated install`);
91
108
  lines.push(``);
92
- lines.push(`A plugin marketplace is available with 340+ plugins and 1,367+ agent skills.`);
109
+ lines.push(`A plugin marketplace has 416 community skills + 13 official Anthropic plugins.`);
110
+ lines.push(`Follow the steps below IN ORDER. Do not stop until a skill is installed.`);
93
111
  lines.push(``);
94
112
  if (categories.length > 0 || saasMatches.length > 0) {
95
- lines.push(`### Matched categories for "${input}":`);
96
- for (const cat of categories) {
97
- lines.push(`- ${cat}`);
113
+ lines.push(`### Matched for "${input}":`);
114
+ for (const cat of categories)
115
+ lines.push(`- category: ${cat}`);
116
+ for (const saas of saasMatches)
117
+ lines.push(`- SaaS: ${saas}`);
118
+ lines.push(``);
119
+ }
120
+ lines.push(`---`);
121
+ lines.push(``);
122
+ lines.push(`### STEP 1 — Try official Anthropic plugins first (no schema errors, always works)`);
123
+ lines.push(``);
124
+ lines.push(`\`\`\``);
125
+ lines.push(`/plugin marketplace add anthropics/claude-code`);
126
+ lines.push(`\`\`\``);
127
+ lines.push(`Then search installed plugins for a match. If found, install:`);
128
+ lines.push(`\`\`\``);
129
+ lines.push(`/plugin install <name>@claude-code-plugins`);
130
+ lines.push(`\`\`\``);
131
+ lines.push(`Official plugins: agent-sdk-dev, claude-opus-4-5-migration, code-review, commit-commands,`);
132
+ lines.push(`explanatory-output-style, feature-dev, frontend-design, hookify, learning-output-style,`);
133
+ lines.push(`plugin-dev, pr-review-toolkit, ralph-wiggum, security-guidance`);
134
+ lines.push(``);
135
+ lines.push(`---`);
136
+ lines.push(``);
137
+ lines.push(`### STEP 2 — Search community catalog (jeremylongshore — 416 plugins)`);
138
+ lines.push(``);
139
+ lines.push(`NOTE: /plugin marketplace add FAILS for this repo (one entry has broken schema).`);
140
+ lines.push(`Use the DIRECT FETCH approach below instead — no marketplace add needed.`);
141
+ lines.push(``);
142
+ lines.push(`**2a. Fetch catalog and find matching plugin:**`);
143
+ lines.push(`\`\`\`bash`);
144
+ lines.push(`curl -s "${MARKETPLACE_CATALOG_URL}" \\`);
145
+ lines.push(` | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));const q='${categoryFilter}';const r=d.plugins.filter(p=>(!q||p.category.includes(q))&&p.name&&p.source).slice(0,5).map(p=>({name:p.name,source:p.source,desc:p.description}));console.log(JSON.stringify(r,null,2));"`);
146
+ lines.push(`\`\`\``);
147
+ lines.push(``);
148
+ lines.push(`**2b. Pick the best match — get its source path (e.g. \`./plugins/productivity/my-skill\`)**`);
149
+ lines.push(``);
150
+ lines.push(`**2c. List the skills inside that plugin:**`);
151
+ lines.push(`\`\`\`bash`);
152
+ lines.push(`# Replace PLUGIN_SOURCE_PATH with value from step 2b (e.g. plugins/productivity/my-skill)`);
153
+ lines.push(`PLUGIN_SOURCE_PATH="plugins/productivity/my-skill"`);
154
+ lines.push(`curl -s "https://api.github.com/repos/${MARKETPLACE_REPO}/contents/\${PLUGIN_SOURCE_PATH}/skills" \\`);
155
+ lines.push(` | node -e "const a=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));console.log(a.map(x=>x.name).join('\\n'));"`);
156
+ lines.push(`\`\`\``);
157
+ lines.push(``);
158
+ lines.push(`**2d. For each skill listed, download and install it:**`);
159
+ lines.push(`\`\`\`bash`);
160
+ lines.push(`# Replace PLUGIN_SOURCE_PATH and SKILL_NAME with actual values`);
161
+ lines.push(`PLUGIN_SOURCE_PATH="plugins/productivity/my-skill"`);
162
+ lines.push(`SKILL_NAME="skill-directory-name"`);
163
+ lines.push(`mkdir -p ".claude/skills/\${SKILL_NAME}"`);
164
+ lines.push(`curl -s "https://raw.githubusercontent.com/${MARKETPLACE_REPO}/main/\${PLUGIN_SOURCE_PATH}/skills/\${SKILL_NAME}/SKILL.md" \\`);
165
+ lines.push(` -o ".claude/skills/\${SKILL_NAME}/SKILL.md"`);
166
+ lines.push(`echo "Installed: .claude/skills/\${SKILL_NAME}/SKILL.md"`);
167
+ lines.push(`\`\`\``);
168
+ lines.push(``);
169
+ lines.push(`**On Windows, replace curl with:**`);
170
+ lines.push(`\`\`\`powershell`);
171
+ lines.push(`$url = "https://raw.githubusercontent.com/${MARKETPLACE_REPO}/main/$PLUGIN_SOURCE_PATH/skills/$SKILL_NAME/SKILL.md"`);
172
+ lines.push(`New-Item -ItemType Directory -Force ".claude/skills/$SKILL_NAME" | Out-Null`);
173
+ lines.push(`Invoke-WebRequest $url -OutFile ".claude/skills/$SKILL_NAME/SKILL.md"`);
174
+ lines.push(`\`\`\``);
175
+ lines.push(``);
176
+ lines.push(`---`);
177
+ lines.push(``);
178
+ lines.push(`### STEP 3 — Search additional sources`);
179
+ lines.push(``);
180
+ for (const source of ADDITIONAL_MARKETPLACE_SOURCES) {
181
+ lines.push(`**${source.name}** — ${source.description}`);
182
+ if ("catalogUrl" in source && source.catalogUrl) {
183
+ lines.push(`Catalog: ${source.catalogUrl}`);
98
184
  }
99
- for (const saas of saasMatches) {
100
- lines.push(`- SaaS pack: ${saas} (~30 skills)`);
185
+ if (source.note) {
186
+ lines.push(`Note: ${source.note}`);
101
187
  }
102
188
  lines.push(``);
103
189
  }
104
- lines.push(`### How to search and install`);
105
- lines.push(``);
106
- lines.push(`**Step 1Add the marketplace** (if not already added):`);
107
- lines.push("```");
108
- lines.push(`/plugin marketplace add ${MARKETPLACE_REPO}`);
109
- lines.push("```");
110
- lines.push(``);
111
- lines.push(`**Step 2 Search for matching plugins:**`);
112
- lines.push("```");
113
- lines.push(`# Fetch the catalog:`);
114
- lines.push(`curl -s ${MARKETPLACE_CATALOG_URL} | jq '[.[] | select(.category | test("${categories[0] ?? ""}"; "i"))]'`);
115
- lines.push("```");
116
- lines.push(``);
117
- lines.push(`**Step 3 — Install matching plugins:**`);
118
- lines.push("```");
119
- lines.push(`/plugin install <name>@claude-code-plugins-plus`);
120
- lines.push("```");
121
- lines.push(``);
122
- lines.push(`### Before suggesting any plugin, validate:`);
123
- lines.push(`- \`mcp_required\` field — if true, flag the MCP dependency`);
124
- lines.push(`- \`free\` field — if false, flag that it needs a paid API`);
125
- lines.push(`- Never suggest a plugin without checking the catalog first`);
126
- lines.push(`- Never hardcode a plugin name from memory — validate against the fetched catalog`);
127
- lines.push(``);
128
- lines.push(`### Suggestion format:`);
129
- lines.push("```");
130
- lines.push(`📦 Suggested from [claude-code-plugins-plus-skills]`);
131
- lines.push(``);
132
- lines.push(` [plugin/skill name]`);
133
- lines.push(` Category : [category]`);
134
- lines.push(` What it does: [one sentence from catalog description]`);
135
- lines.push(` Requires : [nothing / MCP: name / Paid API: service name]`);
136
- lines.push(``);
137
- lines.push(` Install:`);
138
- lines.push(` /plugin marketplace add ${MARKETPLACE_REPO}`);
139
- lines.push(` /plugin install [name]@claude-code-plugins-plus`);
140
- lines.push("```");
141
- lines.push(``);
142
- // Official Anthropic marketplace plugins (always available)
143
- lines.push(`### Official Anthropic plugins (always available, no marketplace add needed):`);
144
- lines.push(`These are installed via \`/plugin install <name>@claude-plugins-official\`:`);
145
- lines.push(`- **github** — GitHub integration (PRs, issues, repos)`);
146
- lines.push(`- **gitlab** — GitLab integration`);
147
- lines.push(`- **slack** — Slack messaging`);
148
- lines.push(`- **linear** — Linear project management`);
149
- lines.push(`- **notion** — Notion workspace`);
150
- lines.push(`- **sentry** — Error monitoring`);
151
- lines.push(`- **figma** — Design files`);
152
- lines.push(`- **vercel** — Deployment`);
153
- lines.push(`- **firebase** — Firebase services`);
154
- lines.push(`- **supabase** — Supabase backend`);
155
- lines.push(`- **atlassian** — Jira/Confluence`);
156
- lines.push(`- **asana** — Project management`);
190
+ lines.push(`---`);
191
+ lines.push(``);
192
+ lines.push(`### STEP 4 If no match found in any source, create a custom skill`);
193
+ lines.push(``);
194
+ lines.push(`\`\`\`bash`);
195
+ lines.push(`mkdir -p ".claude/skills/${input.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}"`);
196
+ lines.push(`\`\`\``);
197
+ lines.push(`Then create SKILL.md with:`);
198
+ lines.push(`\`\`\`yaml`);
199
+ lines.push(`---`);
200
+ lines.push(`name: ${input.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}`);
201
+ lines.push(`description: ${input}`);
202
+ lines.push(`---`);
203
+ lines.push(``);
204
+ lines.push(`[Skill instructions here]`);
205
+ lines.push(`\`\`\``);
206
+ lines.push(``);
207
+ lines.push(`---`);
208
+ lines.push(``);
209
+ lines.push(`### Install result format`);
210
+ lines.push(`After installing, confirm:`);
211
+ lines.push(`✅ Installed: .claude/skills/<name>/SKILL.md [one line: what it does]`);
212
+ lines.push(`⏭ No match: searched [categories], created custom skill instead`);
157
213
  lines.push(``);
158
214
  return lines.join("\n");
159
215
  }
@@ -22,6 +22,7 @@ export interface SnapshotNode {
22
22
  }
23
23
  export interface SnapshotTimeline {
24
24
  nodes: SnapshotNode[];
25
+ restoredTo?: string;
25
26
  }
26
27
  export interface SnapshotData {
27
28
  files: Record<string, string>;
@@ -39,15 +40,28 @@ export declare function createSnapshot(cwd: string, command: string, changedFile
39
40
  input?: string;
40
41
  summary?: string;
41
42
  }): SnapshotNode;
43
+ /**
44
+ * Build the cumulative file state at a given node by accumulating
45
+ * all files from node 0 through the target node. Later nodes override
46
+ * earlier ones (last-write-wins), giving the full state at that point.
47
+ */
48
+ export declare function buildCumulativeState(cwd: string, nodeId: string, timeline: SnapshotTimeline): Record<string, string> | null;
42
49
  /**
43
50
  * Restore files from a snapshot node.
44
- * Writes stored file contents back to disk.
51
+ * Accumulates all file states from node 0 through the target node,
52
+ * then writes them to disk. This reconstructs the full project state
53
+ * at that point in time, not just the delta.
45
54
  * Does NOT delete other nodes — all nodes are preserved (like git).
46
55
  */
47
- export declare function restoreSnapshot(cwd: string, nodeId: string): {
56
+ export declare function restoreSnapshot(cwd: string, nodeId: string, timeline?: SnapshotTimeline): {
48
57
  restored: string[];
49
58
  failed: string[];
59
+ stale: string[];
50
60
  };
61
+ /**
62
+ * Record the last restored node in the timeline (for display purposes).
63
+ */
64
+ export declare function updateRestoredNode(cwd: string, nodeId: string): void;
51
65
  /**
52
66
  * Compare two snapshot nodes. Returns files that differ between them.
53
67
  */
package/dist/snapshot.js CHANGED
@@ -91,18 +91,43 @@ export function createSnapshot(cwd, command, changedFiles, opts = {}) {
91
91
  writeNodeData(cwd, nodeId, data);
92
92
  return node;
93
93
  }
94
+ /**
95
+ * Build the cumulative file state at a given node by accumulating
96
+ * all files from node 0 through the target node. Later nodes override
97
+ * earlier ones (last-write-wins), giving the full state at that point.
98
+ */
99
+ export function buildCumulativeState(cwd, nodeId, timeline) {
100
+ const targetIndex = timeline.nodes.findIndex(n => n.id === nodeId);
101
+ if (targetIndex < 0)
102
+ return null;
103
+ const cumulative = {};
104
+ for (let i = 0; i <= targetIndex; i++) {
105
+ const data = readNodeData(cwd, timeline.nodes[i].id);
106
+ if (data) {
107
+ for (const [filePath, content] of Object.entries(data.files)) {
108
+ cumulative[filePath] = content;
109
+ }
110
+ }
111
+ }
112
+ return cumulative;
113
+ }
94
114
  /**
95
115
  * Restore files from a snapshot node.
96
- * Writes stored file contents back to disk.
116
+ * Accumulates all file states from node 0 through the target node,
117
+ * then writes them to disk. This reconstructs the full project state
118
+ * at that point in time, not just the delta.
97
119
  * Does NOT delete other nodes — all nodes are preserved (like git).
98
120
  */
99
- export function restoreSnapshot(cwd, nodeId) {
100
- const data = readNodeData(cwd, nodeId);
101
- if (!data)
102
- return { restored: [], failed: [nodeId] };
121
+ export function restoreSnapshot(cwd, nodeId, timeline) {
122
+ // If no timeline provided, read it
123
+ const tl = timeline ?? readTimeline(cwd);
124
+ // Build cumulative state up to this node
125
+ const cumulativeFiles = buildCumulativeState(cwd, nodeId, tl);
126
+ if (!cumulativeFiles)
127
+ return { restored: [], failed: [nodeId], stale: [] };
103
128
  const restored = [];
104
129
  const failed = [];
105
- for (const [filePath, content] of Object.entries(data.files)) {
130
+ for (const [filePath, content] of Object.entries(cumulativeFiles)) {
106
131
  const fullPath = join(cwd, filePath);
107
132
  try {
108
133
  const dir = dirname(fullPath);
@@ -115,7 +140,37 @@ export function restoreSnapshot(cwd, nodeId) {
115
140
  failed.push(filePath);
116
141
  }
117
142
  }
118
- return { restored, failed };
143
+ // Detect files that exist now but weren't in the cumulative state
144
+ // These are files added in later snapshots that may be stale
145
+ const stale = [];
146
+ const targetIndex = tl.nodes.findIndex(n => n.id === nodeId);
147
+ if (targetIndex >= 0) {
148
+ const laterNodes = tl.nodes.slice(targetIndex + 1);
149
+ const allLaterFiles = new Set();
150
+ for (const node of laterNodes) {
151
+ const nodeData = readNodeData(cwd, node.id);
152
+ if (nodeData) {
153
+ for (const fp of Object.keys(nodeData.files)) {
154
+ allLaterFiles.add(fp);
155
+ }
156
+ }
157
+ }
158
+ // Files in later snapshots but NOT in cumulative state at target
159
+ for (const filePath of allLaterFiles) {
160
+ if (!cumulativeFiles[filePath] && existsSync(join(cwd, filePath))) {
161
+ stale.push(filePath);
162
+ }
163
+ }
164
+ }
165
+ return { restored, failed, stale };
166
+ }
167
+ /**
168
+ * Record the last restored node in the timeline (for display purposes).
169
+ */
170
+ export function updateRestoredNode(cwd, nodeId) {
171
+ const timeline = readTimeline(cwd);
172
+ timeline.restoredTo = nodeId;
173
+ writeTimeline(cwd, timeline);
119
174
  }
120
175
  /**
121
176
  * Compare two snapshot nodes. Returns files that differ between them.
package/dist/tokens.d.ts CHANGED
@@ -1,13 +1,20 @@
1
1
  /**
2
2
  * Token cost tracking — visibility into what every command costs.
3
3
  *
4
- * Zero extra API calls. Token count is computed from content length.
5
- * Estimates are based on ~4 chars per token approximation.
4
+ * Two data sources:
5
+ * 1. Estimates: computed from content length (~4 chars/token)
6
+ * 2. Real usage: parsed from Claude Code JSONL session transcripts
7
+ * stored at ~/.config/claude/projects/ and ~/.claude/projects/
6
8
  *
7
- * Supports all pricing models:
8
- * - Opus: $15/M input
9
- * - Sonnet: $3/M input
10
- * - Haiku: $0.25/M input
9
+ * Pricing engine inspired by ccusage (github.com/syunmoca/ccusage):
10
+ * - Per-model pricing with tiered rates (200k token threshold)
11
+ * - Cache creation/read tokens tracked separately
12
+ * - Per-session, per-project, per-model breakdowns
13
+ *
14
+ * Current pricing (per million tokens):
15
+ * Opus 4.6: $15 input / $75 output / $18.75 cache-write / $1.50 cache-read
16
+ * Sonnet 4.6: $3 input / $15 output / $3.75 cache-write / $0.30 cache-read
17
+ * Haiku 4.5: $0.80 input / $4 output / $1.00 cache-write / $0.08 cache-read
11
18
  */
12
19
  export interface CostBreakdown {
13
20
  opus: number;
@@ -22,6 +29,8 @@ export interface TokenEstimate {
22
29
  tokens: number;
23
30
  }>;
24
31
  }
32
+ /** Calculate real cost for a set of token counts using a specific model */
33
+ export declare function calculateRealCost(inputTokens: number, outputTokens: number, cacheCreate: number, cacheRead: number, modelName: string): number;
25
34
  export declare function estimateTokens(content: string): number;
26
35
  export declare function estimateCost(tokens: number): CostBreakdown;
27
36
  export declare function formatCost(cost: CostBreakdown): string;
@@ -43,6 +52,66 @@ export declare function generateHints(runs: Array<{
43
52
  command: string;
44
53
  estimatedTokens?: number;
45
54
  }>, currentTokens: number, budget: number): string[];
55
+ export interface RealTokenRecord {
56
+ sessionId: string;
57
+ timestamp: string;
58
+ model: string;
59
+ inputTokens: number;
60
+ outputTokens: number;
61
+ cacheCreate: number;
62
+ cacheRead: number;
63
+ cost: number;
64
+ }
65
+ export declare function readRealTokenUsage(cwd: string): RealTokenRecord[];
66
+ export interface ModelBreakdown {
67
+ model: string;
68
+ inputTokens: number;
69
+ outputTokens: number;
70
+ cacheCreateTokens: number;
71
+ cacheReadTokens: number;
72
+ cost: number;
73
+ totalTokens: number;
74
+ }
75
+ export interface SessionSummary {
76
+ sessionId: string;
77
+ project: string;
78
+ timestamp: string;
79
+ models: ModelBreakdown[];
80
+ inputTokens: number;
81
+ outputTokens: number;
82
+ cacheCreateTokens: number;
83
+ cacheReadTokens: number;
84
+ totalTokens: number;
85
+ totalCost: number;
86
+ }
87
+ export interface ProjectSummary {
88
+ project: string;
89
+ sessions: number;
90
+ models: ModelBreakdown[];
91
+ inputTokens: number;
92
+ outputTokens: number;
93
+ cacheCreateTokens: number;
94
+ cacheReadTokens: number;
95
+ totalTokens: number;
96
+ totalCost: number;
97
+ }
98
+ /**
99
+ * Read all JSONL session files for a given project directory.
100
+ * Scans Claude's data directories for matching project paths.
101
+ * Returns per-session summaries with per-model breakdowns.
102
+ */
103
+ export declare function readProjectSessions(projectCwd: string): SessionSummary[];
104
+ /**
105
+ * Aggregate all sessions for a project into a single summary.
106
+ */
107
+ export declare function getProjectUsageSummary(projectCwd: string): ProjectSummary | null;
108
+ export declare function getTokenHookScript(): string;
109
+ /**
110
+ * Format a real-cost summary. Prefers JSONL transcript data (ccusage-style),
111
+ * falls back to Stop hook data if no JSONL sessions found.
112
+ * Returns null if no real data is available from either source.
113
+ */
114
+ export declare function formatRealCostSummary(cwd: string): string | null;
46
115
  /**
47
116
  * Compute cumulative stats for status dashboard.
48
117
  */