@boostecom/provider 0.0.1

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 (70) hide show
  1. package/README.md +90 -0
  2. package/dist/index.cjs +2522 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +848 -0
  5. package/dist/index.d.ts +848 -0
  6. package/dist/index.js +2484 -0
  7. package/dist/index.js.map +1 -0
  8. package/docs/content/README.md +337 -0
  9. package/docs/content/agent-teams.mdx +324 -0
  10. package/docs/content/api.mdx +757 -0
  11. package/docs/content/best-practices.mdx +624 -0
  12. package/docs/content/examples.mdx +675 -0
  13. package/docs/content/guide.mdx +516 -0
  14. package/docs/content/index.mdx +99 -0
  15. package/docs/content/installation.mdx +246 -0
  16. package/docs/content/skills.mdx +548 -0
  17. package/docs/content/troubleshooting.mdx +588 -0
  18. package/docs/examples/README.md +499 -0
  19. package/docs/examples/abort-signal.ts +125 -0
  20. package/docs/examples/agent-teams.ts +122 -0
  21. package/docs/examples/basic-usage.ts +73 -0
  22. package/docs/examples/check-cli.ts +51 -0
  23. package/docs/examples/conversation-history.ts +69 -0
  24. package/docs/examples/custom-config.ts +90 -0
  25. package/docs/examples/generate-object-constraints.ts +209 -0
  26. package/docs/examples/generate-object.ts +211 -0
  27. package/docs/examples/hooks-callbacks.ts +63 -0
  28. package/docs/examples/images.ts +76 -0
  29. package/docs/examples/integration-test.ts +241 -0
  30. package/docs/examples/limitations.ts +150 -0
  31. package/docs/examples/logging-custom-logger.ts +99 -0
  32. package/docs/examples/logging-default.ts +55 -0
  33. package/docs/examples/logging-disabled.ts +74 -0
  34. package/docs/examples/logging-verbose.ts +64 -0
  35. package/docs/examples/long-running-tasks.ts +179 -0
  36. package/docs/examples/message-injection.ts +210 -0
  37. package/docs/examples/mid-stream-injection.ts +126 -0
  38. package/docs/examples/run-all-examples.sh +48 -0
  39. package/docs/examples/sdk-tools-callbacks.ts +49 -0
  40. package/docs/examples/skills-discovery.ts +144 -0
  41. package/docs/examples/skills-management.ts +140 -0
  42. package/docs/examples/stream-object.ts +80 -0
  43. package/docs/examples/streaming.ts +52 -0
  44. package/docs/examples/structured-output-repro.ts +227 -0
  45. package/docs/examples/tool-management.ts +215 -0
  46. package/docs/examples/tool-streaming.ts +132 -0
  47. package/docs/examples/zod4-compatibility-test.ts +290 -0
  48. package/docs/src/claude-code-language-model.test.ts +3883 -0
  49. package/docs/src/claude-code-language-model.ts +2586 -0
  50. package/docs/src/claude-code-provider.test.ts +97 -0
  51. package/docs/src/claude-code-provider.ts +179 -0
  52. package/docs/src/convert-to-claude-code-messages.images.test.ts +104 -0
  53. package/docs/src/convert-to-claude-code-messages.test.ts +193 -0
  54. package/docs/src/convert-to-claude-code-messages.ts +419 -0
  55. package/docs/src/errors.test.ts +213 -0
  56. package/docs/src/errors.ts +216 -0
  57. package/docs/src/index.test.ts +49 -0
  58. package/docs/src/index.ts +98 -0
  59. package/docs/src/logger.integration.test.ts +164 -0
  60. package/docs/src/logger.test.ts +184 -0
  61. package/docs/src/logger.ts +65 -0
  62. package/docs/src/map-claude-code-finish-reason.test.ts +120 -0
  63. package/docs/src/map-claude-code-finish-reason.ts +60 -0
  64. package/docs/src/mcp-helpers.test.ts +71 -0
  65. package/docs/src/mcp-helpers.ts +123 -0
  66. package/docs/src/message-injection.test.ts +460 -0
  67. package/docs/src/types.ts +447 -0
  68. package/docs/src/validation.test.ts +558 -0
  69. package/docs/src/validation.ts +360 -0
  70. package/package.json +124 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Example: Mid-Session Message Injection
3
+ *
4
+ * Demonstrates how to inject messages into an active Claude Code session.
5
+ * Shows stream events to understand when injection can/cannot occur.
6
+ *
7
+ * Usage: npx tsx examples/message-injection.ts
8
+ */
9
+
10
+ import { streamText } from 'ai';
11
+ import { createClaudeCode } from '../dist/index.js';
12
+ import type { ClaudeCodeSettings, MessageInjector } from '../src/types.js';
13
+
14
+ const CYAN = '\x1b[36m';
15
+ const GREEN = '\x1b[32m';
16
+ const YELLOW = '\x1b[33m';
17
+ const RED = '\x1b[31m';
18
+ const DIM = '\x1b[2m';
19
+ const RESET = '\x1b[0m';
20
+
21
+ function timestamp() {
22
+ return `${DIM}[${(performance.now() / 1000).toFixed(2)}s]${RESET}`;
23
+ }
24
+
25
+ /**
26
+ * Example 1: Multiple tool calls - injection succeeds
27
+ */
28
+ async function multipleToolCalls() {
29
+ console.log(`\n${CYAN}═══════════════════════════════════════════════════════════${RESET}`);
30
+ console.log(`${CYAN} EXAMPLE 1: Multiple Tool Calls${RESET}`);
31
+ console.log(`${CYAN}═══════════════════════════════════════════════════════════${RESET}`);
32
+ console.log(`${DIM}Task: Write 5 files. Inject after 3 seconds.${RESET}\n`);
33
+
34
+ let injector: MessageInjector | null = null;
35
+
36
+ const defaultSettings: ClaudeCodeSettings = {
37
+ streamingInput: 'always',
38
+ permissionMode: 'bypassPermissions',
39
+ allowDangerouslySkipPermissions: true,
40
+ allowedTools: ['Write'],
41
+ onStreamStart: (inj: MessageInjector) => {
42
+ injector = inj;
43
+ console.log(`${timestamp()} ${GREEN}SESSION STARTED${RESET}`);
44
+
45
+ setTimeout(() => {
46
+ console.log(`${timestamp()} ${YELLOW}>>> INJECT QUEUED: "STOP!"${RESET}`);
47
+ injector!.inject(
48
+ 'STOP! Do not write more files. Say how many you created.',
49
+ (delivered) => {
50
+ console.log(
51
+ `${timestamp()} ${delivered ? GREEN + '✓ DELIVERED' : RED + '✗ NOT DELIVERED'}${RESET}`
52
+ );
53
+ }
54
+ );
55
+ }, 3000);
56
+ },
57
+ };
58
+
59
+ const provider = createClaudeCode({ defaultSettings });
60
+
61
+ const result = streamText({
62
+ model: provider('haiku'),
63
+ prompt: 'Write 5 files: /tmp/inj-demo/a.txt through e.txt. Each with "hello". One at a time.',
64
+ });
65
+
66
+ let inText = false;
67
+ for await (const part of result.fullStream) {
68
+ if (part.type === 'text-delta') {
69
+ if (!inText) {
70
+ process.stdout.write(`${timestamp()} ${CYAN}TEXT:${RESET} `);
71
+ inText = true;
72
+ }
73
+ process.stdout.write(part.text);
74
+ } else if (part.type === 'tool-call') {
75
+ if (inText) {
76
+ console.log('');
77
+ inText = false;
78
+ }
79
+ console.log(`${timestamp()} ${CYAN}TOOL-CALL: ${part.toolName}${RESET}`);
80
+ } else if (part.type === 'tool-result') {
81
+ console.log(`${timestamp()} ${CYAN}TOOL-RESULT${RESET}`);
82
+ } else if (part.type === 'finish') {
83
+ if (inText) {
84
+ console.log('');
85
+ inText = false;
86
+ }
87
+ console.log(`${timestamp()} ${GREEN}FINISH${RESET}`);
88
+ }
89
+ }
90
+
91
+ console.log('');
92
+ const { execSync } = await import('child_process');
93
+ try {
94
+ execSync('rm -rf /tmp/inj-demo');
95
+ } catch {
96
+ // Cleanup is best-effort, ignore errors
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Example 2: Too late - inject after session ends, then recover
102
+ */
103
+ async function tooLateWithRecovery() {
104
+ console.log(`\n${CYAN}═══════════════════════════════════════════════════════════${RESET}`);
105
+ console.log(`${CYAN} EXAMPLE 2: Too Late + Recovery${RESET}`);
106
+ console.log(`${CYAN}═══════════════════════════════════════════════════════════${RESET}`);
107
+ console.log(`${DIM}Task: Quick task. Inject after finish fails, then recover.${RESET}\n`);
108
+
109
+ let injector: MessageInjector | null = null;
110
+ let missedMessage: string | null = null;
111
+
112
+ const defaultSettings: ClaudeCodeSettings = {
113
+ streamingInput: 'always',
114
+ permissionMode: 'bypassPermissions',
115
+ allowDangerouslySkipPermissions: true,
116
+ allowedTools: ['Read'],
117
+ onStreamStart: (inj: MessageInjector) => {
118
+ injector = inj;
119
+ console.log(`${timestamp()} ${GREEN}SESSION STARTED${RESET}`);
120
+ },
121
+ };
122
+
123
+ const provider = createClaudeCode({ defaultSettings });
124
+
125
+ const result = streamText({
126
+ model: provider('haiku'),
127
+ prompt: 'Read /etc/hosts and say how many lines. Be brief.',
128
+ });
129
+
130
+ let inText = false;
131
+ for await (const part of result.fullStream) {
132
+ if (part.type === 'text-delta') {
133
+ if (!inText) {
134
+ process.stdout.write(`${timestamp()} ${CYAN}TEXT:${RESET} `);
135
+ inText = true;
136
+ }
137
+ process.stdout.write(part.text);
138
+ } else if (part.type === 'tool-call') {
139
+ if (inText) {
140
+ console.log('');
141
+ inText = false;
142
+ }
143
+ console.log(`${timestamp()} ${CYAN}TOOL-CALL: ${part.toolName}${RESET}`);
144
+ } else if (part.type === 'tool-result') {
145
+ console.log(`${timestamp()} ${CYAN}TOOL-RESULT${RESET}`);
146
+ } else if (part.type === 'finish') {
147
+ if (inText) {
148
+ console.log('');
149
+ inText = false;
150
+ }
151
+ console.log(`${timestamp()} ${GREEN}FINISH${RESET}`);
152
+ // Inject AFTER session ends - too late!
153
+ const msg = 'What is the first line of /etc/hosts?';
154
+ console.log(`${timestamp()} ${YELLOW}>>> INJECT QUEUED after finish (too late!)${RESET}`);
155
+ injector!.inject(msg, (delivered) => {
156
+ if (!delivered) {
157
+ console.log(
158
+ `${timestamp()} ${GREEN}✓ NOT DELIVERED detected - saving for recovery${RESET}`
159
+ );
160
+ missedMessage = msg;
161
+ }
162
+ });
163
+ }
164
+ }
165
+
166
+ // Recovery: send missed message as a new turn
167
+ if (missedMessage) {
168
+ console.log(
169
+ `${timestamp()} ${YELLOW}>>> RECOVERING: sending missed message as new prompt${RESET}`
170
+ );
171
+ const recovery = streamText({
172
+ model: provider('haiku'),
173
+ prompt: missedMessage,
174
+ });
175
+ for await (const part of recovery.fullStream) {
176
+ if (part.type === 'text-delta') {
177
+ if (!inText) {
178
+ process.stdout.write(`${timestamp()} ${CYAN}TEXT:${RESET} `);
179
+ inText = true;
180
+ }
181
+ process.stdout.write(part.text);
182
+ } else if (part.type === 'finish') {
183
+ if (inText) {
184
+ console.log('');
185
+ inText = false;
186
+ }
187
+ console.log(`${timestamp()} ${GREEN}FINISH (recovery complete)${RESET}`);
188
+ }
189
+ }
190
+ }
191
+
192
+ console.log('');
193
+ }
194
+
195
+ async function main() {
196
+ console.log(`${CYAN}Message Injection Examples${RESET}`);
197
+ console.log(`${DIM}Timestamps show when each event occurs.${RESET}`);
198
+
199
+ await multipleToolCalls();
200
+ await tooLateWithRecovery();
201
+
202
+ console.log(`\n${CYAN}═══════════════════════════════════════════════════════════${RESET}`);
203
+ console.log(`${DIM}The delivery callback lets you know if injection succeeded`);
204
+ console.log(`or failed, enabling recovery patterns like Example 2.${RESET}\n`);
205
+ }
206
+
207
+ main().catch((err) => {
208
+ console.error('Example failed:', err);
209
+ process.exit(1);
210
+ });
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Mid-stream injection example for Claude Code AI SDK Provider.
3
+ *
4
+ * This example shows how to re-steer a running query by sending an
5
+ * additional user message via the underlying Query object.
6
+ */
7
+
8
+ import { streamText } from 'ai';
9
+ import type { CanUseTool } from '@anthropic-ai/claude-agent-sdk';
10
+ import { claudeCode, type Query } from '../dist/index.js';
11
+
12
+ type SdkUserMessage = {
13
+ type: 'user';
14
+ message: {
15
+ role: 'user';
16
+ content: Array<{ type: 'text'; text: string }>;
17
+ };
18
+ parent_tool_use_id: null;
19
+ session_id: string;
20
+ };
21
+
22
+ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
23
+
24
+ const toSdkUserMessage = (text: string, sessionId = ''): SdkUserMessage => ({
25
+ type: 'user',
26
+ message: {
27
+ role: 'user',
28
+ content: [{ type: 'text', text }],
29
+ },
30
+ parent_tool_use_id: null,
31
+ session_id: sessionId,
32
+ });
33
+
34
+ async function* singleMessage(text: string, sessionId = '') {
35
+ yield toSdkUserMessage(text, sessionId);
36
+ }
37
+
38
+ const allowAllTools: CanUseTool = async (_toolName, input) => ({
39
+ behavior: 'allow',
40
+ updatedInput: input,
41
+ });
42
+
43
+ async function main() {
44
+ let activeQuery: Query | undefined;
45
+ let injected = false;
46
+ let streamedChars = 0;
47
+ let sessionId: string | undefined;
48
+
49
+ const tryInject = async () => {
50
+ if (injected || !activeQuery) return;
51
+ injected = true;
52
+ await sleep(400);
53
+
54
+ try {
55
+ await activeQuery.streamInput(
56
+ singleMessage(
57
+ 'Mid-stream update: switch to nautical metaphors and add a short 3-bullet list of benefits.',
58
+ sessionId ?? ''
59
+ )
60
+ );
61
+ console.log('\n\n[Injected a mid-stream update]\n');
62
+ } catch (error) {
63
+ console.error('\n[Failed to inject mid-stream update]', error);
64
+ }
65
+ };
66
+
67
+ const model = claudeCode('sonnet', {
68
+ streamingInput: 'always',
69
+ canUseTool: allowAllTools,
70
+ permissionMode: 'bypassPermissions',
71
+ allowedTools: ['Bash'],
72
+ cwd: process.cwd(),
73
+ onQueryCreated: (query) => {
74
+ activeQuery = query;
75
+ void tryInject();
76
+ },
77
+ });
78
+
79
+ const result = streamText({
80
+ model,
81
+ prompt:
82
+ 'Write the first paragraph of a product pitch for a coffee subscription. Then run the Bash command `sleep 2` before continuing with the remaining paragraph. Be upbeat and detailed.',
83
+ });
84
+
85
+ console.log('--- Streaming response (watch for the tone shift) ---\n');
86
+
87
+ const stream = result.fullStream as AsyncIterable<unknown>;
88
+
89
+ for await (const part of stream) {
90
+ if (!part || typeof part !== 'object') continue;
91
+ const typed = part as {
92
+ type?: string;
93
+ id?: string;
94
+ delta?: unknown;
95
+ text?: unknown;
96
+ };
97
+
98
+ if (typed.type === 'response-metadata') {
99
+ sessionId = typed.id;
100
+ void tryInject();
101
+ }
102
+
103
+ if (typed.type === 'tool-call') {
104
+ void tryInject();
105
+ }
106
+
107
+ if (typed.type === 'text-delta') {
108
+ const chunk = typed.delta ?? typed.text;
109
+ if (typeof chunk === 'string') {
110
+ process.stdout.write(chunk);
111
+ streamedChars += chunk.length;
112
+
113
+ if (streamedChars > 200) {
114
+ void tryInject();
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ console.log('\n');
121
+ }
122
+
123
+ main().catch((error) => {
124
+ console.error('Example failed:', error);
125
+ process.exitCode = 1;
126
+ });
@@ -0,0 +1,48 @@
1
+ #!/bin/bash
2
+
3
+ # Build once
4
+ echo "Building project..."
5
+ npm run build
6
+
7
+ # List of all examples
8
+ examples=(
9
+ "basic-usage"
10
+ "streaming"
11
+ "conversation-history"
12
+ "custom-config"
13
+ "generate-object"
14
+ "generate-object-constraints"
15
+ "tool-management"
16
+ "long-running-tasks"
17
+ "abort-signal"
18
+ "check-cli"
19
+ "integration-test"
20
+ "limitations"
21
+ "hooks-callbacks"
22
+ "sdk-tools-callbacks"
23
+ "tool-streaming"
24
+ "images"
25
+ )
26
+
27
+ # Run each example
28
+ for example in "${examples[@]}"; do
29
+ echo ""
30
+ echo "========================================="
31
+ echo "Running: $example.ts"
32
+ echo "========================================="
33
+
34
+ # images.ts optionally accepts a path argument
35
+ if [ "$example" = "images" ] && [ -n "$EXAMPLE_IMAGE_PATH" ]; then
36
+ npx tsx "examples/$example.ts" "$EXAMPLE_IMAGE_PATH"
37
+ else
38
+ npx tsx "examples/$example.ts"
39
+ fi
40
+
41
+ # Check if the command succeeded
42
+ if [ $? -ne 0 ]; then
43
+ echo "❌ Failed: $example.ts"
44
+ fi
45
+ done
46
+
47
+ echo ""
48
+ echo "✅ All examples completed!"
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Example: SDK MCP Tools (callbacks)
3
+ *
4
+ * Demonstrates defining in-process tools via createSdkMcpServer and tool,
5
+ * wiring them to the provider via mcpServers, and constraining access
6
+ * via allowedTools.
7
+ */
8
+
9
+ import { z } from 'zod';
10
+ import { streamText } from 'ai';
11
+ import { createClaudeCode, createSdkMcpServer, tool } from '../dist/index.js';
12
+ // NOTE: Migrating to Claude Agent SDK:
13
+ // - System prompt is not applied by default
14
+ // - Filesystem settings (CLAUDE.md, settings.json) are not loaded by default
15
+ // To restore old behavior, set when creating model instances, e.g.:
16
+ // systemPrompt: { type: 'preset', preset: 'claude_code' }
17
+ // settingSources: ['user', 'project', 'local']
18
+
19
+ // Define an in-process tool
20
+ const add = tool('add', 'Add two numbers', { a: z.number(), b: z.number() }, async ({ a, b }) => ({
21
+ content: [{ type: 'text', text: String(a + b) }],
22
+ }));
23
+
24
+ // Create SDK MCP server
25
+ const sdkServer = createSdkMcpServer({ name: 'local', tools: [add] });
26
+
27
+ async function main() {
28
+ // Wire the MCP server and restrict to this tool
29
+ const provider = createClaudeCode({
30
+ defaultSettings: {
31
+ mcpServers: { local: sdkServer },
32
+ allowedTools: ['mcp__local__add'],
33
+ },
34
+ });
35
+
36
+ const result = streamText({
37
+ model: provider('opus'),
38
+ prompt: 'Use the add tool to sum 3 and 4. Provide only the number.',
39
+ });
40
+
41
+ let text = '';
42
+ for await (const chunk of result.textStream) text += chunk;
43
+ console.log('Response:', text.trim());
44
+ }
45
+
46
+ main().catch((err) => {
47
+ console.error('Example failed:', err);
48
+ process.exit(1);
49
+ });
@@ -0,0 +1,144 @@
1
+ import { claudeCode } from '../dist/index.js';
2
+ import { generateText } from 'ai';
3
+
4
+ /**
5
+ * Skills Discovery Example
6
+ *
7
+ * Demonstrates how to programmatically discover and use available Skills
8
+ * in your Claude Code environment. This is useful for:
9
+ * - Building dynamic UIs that show available capabilities
10
+ * - Validating Skills are properly loaded
11
+ * - Testing Skills configuration
12
+ * - Creating Skills marketplaces or directories
13
+ */
14
+
15
+ async function main() {
16
+ console.log('=== Skills Discovery Example ===\n');
17
+
18
+ // Create a model with Skills enabled
19
+ const model = claudeCode('sonnet', {
20
+ // Load Skills from filesystem
21
+ settingSources: ['user', 'project'],
22
+
23
+ // Enable Skill tool
24
+ allowedTools: ['Skill'],
25
+
26
+ verbose: true,
27
+ });
28
+
29
+ try {
30
+ // Ask Claude to list available Skills
31
+ console.log('Discovering available Skills...\n');
32
+
33
+ const { text } = await generateText({
34
+ model,
35
+ prompt: `Please list all available Skills you have access to. For each Skill, provide:
36
+ 1. The Skill name
37
+ 2. A brief description of what it does
38
+ 3. When it would be useful to invoke it
39
+
40
+ Format your response as a structured list.`,
41
+ });
42
+
43
+ console.log('=== Available Skills ===');
44
+ console.log(text);
45
+ console.log('\n');
46
+
47
+ // Test specific Skill invocation
48
+ console.log('=== Testing Skill Invocation ===\n');
49
+
50
+ const skillTest = await generateText({
51
+ model: claudeCode('haiku', {
52
+ settingSources: ['user', 'project'],
53
+ allowedTools: ['Skill', 'Read'],
54
+ }),
55
+ prompt: 'Can you show me an example of using a Skill? Pick any available Skill and demonstrate it.',
56
+ });
57
+
58
+ console.log(skillTest.text);
59
+
60
+ } catch (error) {
61
+ console.error('Error:', error);
62
+
63
+ if (error instanceof Error && error.message.includes('settingSources')) {
64
+ console.error('\nTip: Make sure you have Skills configured in:');
65
+ console.error(' - User Skills: ~/.claude/skills/');
66
+ console.error(' - Project Skills: ./.claude/skills/');
67
+ console.error('\nYou need to set settingSources to load Skills from filesystem.');
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Advanced: Validate Skills Configuration
74
+ *
75
+ * This function demonstrates how to validate that Skills are
76
+ * properly configured and accessible.
77
+ */
78
+ async function validateSkillsSetup() {
79
+ console.log('\n=== Validating Skills Setup ===\n');
80
+
81
+ const configs = [
82
+ {
83
+ name: 'Without settingSources',
84
+ settings: { allowedTools: ['Skill'] },
85
+ expectedBehavior: 'Skills NOT loaded',
86
+ },
87
+ {
88
+ name: 'With user settingSources',
89
+ settings: { settingSources: ['user'], allowedTools: ['Skill'] },
90
+ expectedBehavior: 'User Skills loaded',
91
+ },
92
+ {
93
+ name: 'With project settingSources',
94
+ settings: { settingSources: ['project'], allowedTools: ['Skill'] },
95
+ expectedBehavior: 'Project Skills loaded',
96
+ },
97
+ {
98
+ name: 'With both settingSources',
99
+ settings: { settingSources: ['user', 'project'], allowedTools: ['Skill'] },
100
+ expectedBehavior: 'All Skills loaded',
101
+ },
102
+ ] as const;
103
+
104
+ for (const config of configs) {
105
+ console.log(`Testing: ${config.name}`);
106
+ console.log(`Expected: ${config.expectedBehavior}`);
107
+
108
+ try {
109
+ const model = claudeCode('haiku', config.settings as any);
110
+
111
+ const { text } = await generateText({
112
+ model,
113
+ prompt: 'List available Skills. If none, say "No Skills available".',
114
+ maxTokens: 200,
115
+ });
116
+
117
+ const hasSkills = !text.toLowerCase().includes('no skills');
118
+ console.log(`Result: ${hasSkills ? 'Skills found' : 'No Skills'}`);
119
+ console.log(`Sample: ${text.slice(0, 100)}...\n`);
120
+
121
+ } catch (error) {
122
+ console.log(`Error: ${error instanceof Error ? error.message : 'Unknown error'}\n`);
123
+ }
124
+ }
125
+ }
126
+
127
+ // Run main example
128
+ main()
129
+ .then(() => {
130
+ // Optionally run validation
131
+ if (process.argv.includes('--validate')) {
132
+ return validateSkillsSetup();
133
+ }
134
+ })
135
+ .catch(console.error);
136
+
137
+ /**
138
+ * Usage:
139
+ *
140
+ * npm run build && npx tsx examples/skills-discovery.ts
141
+ *
142
+ * Or with validation:
143
+ * npm run build && npx tsx examples/skills-discovery.ts --validate
144
+ */