-
Notifications
You must be signed in to change notification settings - Fork 3
feat(example): add interactive CLI demo with @clack/prompts #203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,160 @@ | ||||||||||||||||
| /** | ||||||||||||||||
| * Interactive CLI Demo | ||||||||||||||||
| * | ||||||||||||||||
| * This example demonstrates how to build an interactive CLI tool using | ||||||||||||||||
| * @clack/prompts to dynamically discover and execute StackOne tools. | ||||||||||||||||
| * | ||||||||||||||||
| * Features: | ||||||||||||||||
| * - Interactive credential input with environment variable fallback | ||||||||||||||||
| * - Dynamic tool discovery and selection | ||||||||||||||||
| * - Spinner feedback during async operations | ||||||||||||||||
| * | ||||||||||||||||
| * Run with: | ||||||||||||||||
| * ```bash | ||||||||||||||||
| * npx tsx examples/interactive-cli.ts | ||||||||||||||||
| * ``` | ||||||||||||||||
| */ | ||||||||||||||||
|
|
||||||||||||||||
| import process from 'node:process'; | ||||||||||||||||
| import * as clack from '@clack/prompts'; | ||||||||||||||||
| import { StackOneToolSet } from '@stackone/ai'; | ||||||||||||||||
|
|
||||||||||||||||
| // Enable verbose fetch logging when running with Bun | ||||||||||||||||
| process.env.BUN_CONFIG_VERBOSE_FETCH = 'curl'; | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Rule violated: Flag Security Vulnerabilities Verbose fetch logging is enabled unconditionally, which may expose API credentials in console output. HTTP request logging typically includes Authorization headers containing the API key. Consider making this opt-in via a command-line flag or environment variable check rather than enabling it by default. Prompt for AI agents
ryoppippi marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||
|
|
||||||||||||||||
| clack.intro('Welcome to StackOne AI Tool Tester'); | ||||||||||||||||
|
|
||||||||||||||||
| // Check if environment variables are available | ||||||||||||||||
| const hasEnvVars = process.env.STACKONE_API_KEY && process.env.STACKONE_ACCOUNT_ID; | ||||||||||||||||
|
|
||||||||||||||||
| let apiKey: string; | ||||||||||||||||
| let baseUrl: string; | ||||||||||||||||
| let accountId: string; | ||||||||||||||||
|
|
||||||||||||||||
| if (hasEnvVars) { | ||||||||||||||||
| const useEnv = await clack.confirm({ | ||||||||||||||||
| message: 'Use environment variables from .env file?', | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| if (clack.isCancel(useEnv)) { | ||||||||||||||||
| clack.cancel('Operation cancelled'); | ||||||||||||||||
| process.exit(0); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| if (useEnv) { | ||||||||||||||||
| apiKey = process.env.STACKONE_API_KEY!; | ||||||||||||||||
| baseUrl = process.env.STACKONE_BASE_URL || 'https://api.stackone.com'; | ||||||||||||||||
| accountId = process.env.STACKONE_ACCOUNT_ID!; | ||||||||||||||||
| } else { | ||||||||||||||||
| const credentials = await promptCredentials(); | ||||||||||||||||
| apiKey = credentials.apiKey; | ||||||||||||||||
| baseUrl = credentials.baseUrl; | ||||||||||||||||
| accountId = credentials.accountId; | ||||||||||||||||
| } | ||||||||||||||||
| } else { | ||||||||||||||||
| const credentials = await promptCredentials(); | ||||||||||||||||
| apiKey = credentials.apiKey; | ||||||||||||||||
| baseUrl = credentials.baseUrl; | ||||||||||||||||
| accountId = credentials.accountId; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async function promptCredentials(): Promise<{ | ||||||||||||||||
| apiKey: string; | ||||||||||||||||
| baseUrl: string; | ||||||||||||||||
| accountId: string; | ||||||||||||||||
| }> { | ||||||||||||||||
| const apiKeyInput = await clack.text({ | ||||||||||||||||
| message: 'Enter your StackOne API key:', | ||||||||||||||||
| placeholder: 'v1.us1.xxx...', | ||||||||||||||||
| validate: (value) => { | ||||||||||||||||
| if (!value) return 'API key is required'; | ||||||||||||||||
| }, | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| if (clack.isCancel(apiKeyInput)) { | ||||||||||||||||
| clack.cancel('Operation cancelled'); | ||||||||||||||||
| process.exit(0); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| const baseUrlInput = await clack.text({ | ||||||||||||||||
| message: 'Enter StackOne Base URL (optional):', | ||||||||||||||||
| placeholder: 'https://api.stackone.com', | ||||||||||||||||
| defaultValue: 'https://api.stackone.com', | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| if (clack.isCancel(baseUrlInput)) { | ||||||||||||||||
| clack.cancel('Operation cancelled'); | ||||||||||||||||
| process.exit(0); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| const accountIdInput = await clack.text({ | ||||||||||||||||
| message: 'Enter your StackOne Account ID:', | ||||||||||||||||
| placeholder: 'acc_xxx...', | ||||||||||||||||
| validate: (value) => { | ||||||||||||||||
| if (!value) return 'Account ID is required'; | ||||||||||||||||
| }, | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| if (clack.isCancel(accountIdInput)) { | ||||||||||||||||
| clack.cancel('Operation cancelled'); | ||||||||||||||||
| process.exit(0); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| return { | ||||||||||||||||
| apiKey: apiKeyInput as string, | ||||||||||||||||
| baseUrl: baseUrlInput as string, | ||||||||||||||||
| accountId: accountIdInput as string, | ||||||||||||||||
| }; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| const spinner = clack.spinner(); | ||||||||||||||||
| spinner.start('Initialising StackOne client...'); | ||||||||||||||||
|
|
||||||||||||||||
| const toolset = new StackOneToolSet({ | ||||||||||||||||
| apiKey, | ||||||||||||||||
| baseUrl, | ||||||||||||||||
| accountId, | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| spinner.message('Fetching available tools...'); | ||||||||||||||||
| const tools = await toolset.fetchTools(); | ||||||||||||||||
| const allTools = tools.toArray(); | ||||||||||||||||
|
||||||||||||||||
| const allTools = tools.toArray(); | |
| const allTools = tools.toArray(); | |
| if (allTools.length === 0) { | |
| spinner.stop('No tools found'); | |
| clack.log.error('No tools available. Please check your API key and account ID.'); | |
| process.exit(1); | |
| } |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoding { query: { limit: 5 } } as execution parameters may not work for all tools. Different tools have different parameter requirements. Some tools may not accept a query parameter or limit field, which could cause execution failures. Consider either:
- Prompting the user for parameters based on the selected tool's schema
- Using empty parameters
{}for a more generic approach - Adding error handling that explains parameter mismatches
| const result = await selectedTool.execute({ | |
| query: { limit: 5 }, | |
| }); | |
| const result = await selectedTool.execute({}); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The file header documentation lists features but omits "Verbose fetch logging when running with Bun" which is mentioned in the PR description and implemented on line 23. For completeness, consider adding this feature to the documentation comment, or clarify that it's an internal implementation detail not intended as a user-facing feature.