chub-dev 0.2.0-beta.2 → 0.2.0-beta.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "generated": "2026-02-28T08:21:28.567Z",
3
+ "generated": "2026-03-01T01:33:15.554Z",
4
4
  "docs": [
5
5
  {
6
6
  "id": "anthropic/sdk",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chub-dev",
3
- "version": "0.2.0-beta.2",
3
+ "version": "0.2.0-beta.3",
4
4
  "description": "CLI for Context Hub - search and retrieve LLM-optimized docs and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -7,17 +7,14 @@ import { output, error, info } from '../lib/output.js';
7
7
  import { trackEvent } from '../lib/analytics.js';
8
8
 
9
9
  /**
10
- * Core fetch logic shared by `get docs` and `get skills`.
11
- * @param {string} type - "doc" or "skill"
12
- * @param {string[]} ids - one or more entry ids
13
- * @param {object} opts - command options (lang, version, output, full)
14
- * @param {object} globalOpts - global options (json)
10
+ * Fetch one or more entries by ID. Auto-detects doc vs skill per entry.
15
11
  */
16
- async function fetchEntries(type, ids, opts, globalOpts) {
12
+ async function fetchEntries(ids, opts, globalOpts) {
17
13
  const results = [];
18
14
 
19
15
  for (const id of ids) {
20
- const result = getEntry(id, type);
16
+ // Search both docs and skills — auto-detect type
17
+ const result = getEntry(id);
21
18
 
22
19
  if (result.ambiguous) {
23
20
  error(
@@ -27,10 +24,11 @@ async function fetchEntries(type, ids, opts, globalOpts) {
27
24
  }
28
25
 
29
26
  if (!result.entry) {
30
- error(`Entry "${id}" not found in ${type}s.`, globalOpts);
27
+ error(`Entry "${id}" not found.`, globalOpts);
31
28
  }
32
29
 
33
30
  const entry = result.entry;
31
+ const type = entry.languages ? 'doc' : 'skill';
34
32
  const resolved = resolveDocPath(entry, opts.lang, opts.version);
35
33
 
36
34
  if (!resolved) {
@@ -52,10 +50,10 @@ async function fetchEntries(type, ids, opts, globalOpts) {
52
50
  try {
53
51
  if (opts.full && resolved.files.length > 0) {
54
52
  const allFiles = await fetchDocFull(resolved.source, resolved.path, resolved.files);
55
- results.push({ id: entry.id, files: allFiles, path: resolved.path });
53
+ results.push({ id: entry.id, type, files: allFiles, path: resolved.path });
56
54
  } else {
57
55
  const content = await fetchDoc(resolved.source, entryFile.filePath);
58
- results.push({ id: entry.id, content, path: entryFile.filePath });
56
+ results.push({ id: entry.id, type, content, path: entryFile.filePath });
59
57
  }
60
58
  } catch (err) {
61
59
  error(err.message, globalOpts);
@@ -64,7 +62,7 @@ async function fetchEntries(type, ids, opts, globalOpts) {
64
62
 
65
63
  // Track fetches
66
64
  for (const r of results) {
67
- trackEvent(type === 'doc' ? 'doc_fetched' : 'skill_fetched', {
65
+ trackEvent(r.type === 'doc' ? 'doc_fetched' : 'skill_fetched', {
68
66
  entry_id: r.id,
69
67
  full: !!opts.full,
70
68
  lang: opts.lang || undefined,
@@ -74,7 +72,6 @@ async function fetchEntries(type, ids, opts, globalOpts) {
74
72
  // Output
75
73
  if (opts.output) {
76
74
  if (opts.full) {
77
- // --full -o: write individual files preserving directory structure
78
75
  for (const r of results) {
79
76
  if (r.files) {
80
77
  const baseDir = ids.length > 1 ? join(opts.output, r.id) : opts.output;
@@ -111,18 +108,16 @@ async function fetchEntries(type, ids, opts, globalOpts) {
111
108
  }
112
109
  }
113
110
  if (globalOpts.json) {
114
- console.log(JSON.stringify(results.map((r) => ({ id: r.id, path: opts.output }))));
111
+ console.log(JSON.stringify(results.map((r) => ({ id: r.id, type: r.type, path: opts.output }))));
115
112
  }
116
113
  } else {
117
- // stdout
118
114
  if (results.length === 1 && !results[0].files) {
119
115
  output(
120
- { id: results[0].id, content: results[0].content, path: results[0].path },
116
+ { id: results[0].id, type: results[0].type, content: results[0].content, path: results[0].path },
121
117
  (data) => process.stdout.write(data.content),
122
118
  globalOpts
123
119
  );
124
120
  } else {
125
- // Concatenate all content (--full to stdout, or multiple entries)
126
121
  const parts = results.flatMap((r) => {
127
122
  if (r.files) {
128
123
  return r.files.map((f) => `# FILE: ${f.name}\n\n${f.content}`);
@@ -131,7 +126,7 @@ async function fetchEntries(type, ids, opts, globalOpts) {
131
126
  });
132
127
  const combined = parts.join('\n\n---\n\n');
133
128
  output(
134
- results.map((r) => ({ id: r.id, path: r.path })),
129
+ results.map((r) => ({ id: r.id, type: r.type, path: r.path })),
135
130
  () => process.stdout.write(combined),
136
131
  globalOpts
137
132
  );
@@ -140,29 +135,15 @@ async function fetchEntries(type, ids, opts, globalOpts) {
140
135
  }
141
136
 
142
137
  export function registerGetCommand(program) {
143
- const get = program
144
- .command('get')
145
- .description('Retrieve docs or skills');
146
-
147
- get
148
- .command('docs <ids...>')
149
- .description('Fetch documentation content')
150
- .option('--lang <language>', 'Language variant')
151
- .option('--version <version>', 'Specific version')
152
- .option('-o, --output <path>', 'Write to file or directory')
153
- .option('--full', 'Fetch all files (not just entry point)')
154
- .action(async (ids, opts) => {
155
- const globalOpts = program.optsWithGlobals();
156
- await fetchEntries('doc', ids, opts, globalOpts);
157
- });
158
-
159
- get
160
- .command('skills <ids...>')
161
- .description('Fetch skill content')
138
+ program
139
+ .command('get <ids...>')
140
+ .description('Fetch docs or skills by ID (auto-detects type)')
141
+ .option('--lang <language>', 'Language variant (for docs)')
142
+ .option('--version <version>', 'Specific version (for docs)')
162
143
  .option('-o, --output <path>', 'Write to file or directory')
163
144
  .option('--full', 'Fetch all files (not just entry point)')
164
145
  .action(async (ids, opts) => {
165
146
  const globalOpts = program.optsWithGlobals();
166
- await fetchEntries('skill', ids, opts, globalOpts);
147
+ await fetchEntries(ids, opts, globalOpts);
167
148
  });
168
149
  }
package/src/index.js CHANGED
@@ -26,17 +26,16 @@ ${chalk.bold.underline('Getting Started')}
26
26
  ${chalk.dim('$')} chub search ${chalk.dim('# list everything available')}
27
27
  ${chalk.dim('$')} chub search "stripe" ${chalk.dim('# fuzzy search')}
28
28
  ${chalk.dim('$')} chub search stripe/payments ${chalk.dim('# exact id → full detail')}
29
- ${chalk.dim('$')} chub get docs stripe/payments ${chalk.dim('# print doc to terminal')}
30
- ${chalk.dim('$')} chub get docs stripe/payments -o doc.md ${chalk.dim('# save to file')}
31
- ${chalk.dim('$')} chub get docs stripe/payments --lang py ${chalk.dim('# specific language')}
32
- ${chalk.dim('$')} chub get skills pw/login-flows ${chalk.dim('# fetch a skill')}
33
- ${chalk.dim('$')} chub get docs openai/chat stripe/payments ${chalk.dim('# fetch multiple')}
29
+ ${chalk.dim('$')} chub get stripe/api ${chalk.dim('# print doc to terminal')}
30
+ ${chalk.dim('$')} chub get stripe/api -o doc.md ${chalk.dim('# save to file')}
31
+ ${chalk.dim('$')} chub get openai/chat --lang py ${chalk.dim('# specific language')}
32
+ ${chalk.dim('$')} chub get pw-community/login-flows ${chalk.dim('# fetch a skill')}
33
+ ${chalk.dim('$')} chub get openai/chat stripe/api ${chalk.dim('# fetch multiple')}
34
34
 
35
35
  ${chalk.bold.underline('Commands')}
36
36
 
37
37
  ${chalk.bold('search')} [query] Search docs and skills (no query = list all)
38
- ${chalk.bold('get docs')} <ids...> Fetch documentation content
39
- ${chalk.bold('get skills')} <ids...> Fetch skill content
38
+ ${chalk.bold('get')} <ids...> Fetch docs or skills by ID
40
39
  ${chalk.bold('update')} Refresh the cached registry
41
40
  ${chalk.bold('cache')} status|clear Manage the local cache
42
41
  ${chalk.bold('build')} <content-dir> Build registry from content directory
@@ -56,10 +55,10 @@ ${chalk.bold.underline('Agent Piping Patterns')}
56
55
 
57
56
  ${chalk.dim('# Search → pick → fetch → save')}
58
57
  ${chalk.dim('$')} ID=$(chub search "stripe" --json | jq -r '.results[0].id')
59
- ${chalk.dim('$')} chub get docs "$ID" --lang js -o .context/stripe.md
58
+ ${chalk.dim('$')} chub get "$ID" --lang js -o .context/stripe.md
60
59
 
61
- ${chalk.dim('# Fetch multiple docs at once')}
62
- ${chalk.dim('$')} chub get docs openai/chat stripe/payments -o .context/
60
+ ${chalk.dim('# Fetch multiple at once')}
61
+ ${chalk.dim('$')} chub get openai/chat stripe/api -o .context/
63
62
 
64
63
  ${chalk.bold.underline('Multi-Source Config')} ${chalk.dim('(~/.chub/config.yaml)')}
65
64
 
@@ -69,7 +68,7 @@ ${chalk.bold.underline('Multi-Source Config')} ${chalk.dim('(~/.chub/config.yaml
69
68
  ${chalk.dim(' - name: internal')}
70
69
  ${chalk.dim(' path: /path/to/local/docs')}
71
70
 
72
- ${chalk.dim('# On id collision, use source: prefix: chub get docs internal:openai/chat')}
71
+ ${chalk.dim('# On id collision, use source: prefix: chub get internal:openai/chat')}
73
72
  `);
74
73
  }
75
74