@respan/cli 0.7.4 → 0.8.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.
@@ -0,0 +1,17 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class SetupDoctor extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ global: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ };
15
+ run(): Promise<void>;
16
+ private isBinaryInstalled;
17
+ }
@@ -0,0 +1,224 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as os from 'node:os';
4
+ import { execSync } from 'node:child_process';
5
+ import { Flags } from '@oclif/core';
6
+ import { BaseCommand } from '../../lib/base-command.js';
7
+ import { findProjectRoot, expandHome, readTextFile } from '../../lib/integrate.js';
8
+ const GREEN = '\x1b[32m';
9
+ const RED = '\x1b[31m';
10
+ const YELLOW = '\x1b[33m';
11
+ const DIM = '\x1b[2m';
12
+ const BOLD = '\x1b[1m';
13
+ const RESET = '\x1b[0m';
14
+ class SetupDoctor extends BaseCommand {
15
+ async run() {
16
+ const { flags } = await this.parse(SetupDoctor);
17
+ this.globalFlags = flags;
18
+ const projectRoot = findProjectRoot();
19
+ const home = os.homedir();
20
+ const checkGlobal = flags.global || !flags.local;
21
+ const checkLocal = flags.local || !flags.global;
22
+ this.log('');
23
+ this.log(` ${BOLD}Respan Setup Doctor${RESET}`);
24
+ this.log('');
25
+ // ── 1. API Key ─────────────────────────────────────────────────
26
+ this.log(` ${BOLD}API Key${RESET}`);
27
+ const envPath = path.join(projectRoot, '.env');
28
+ const envContent = readTextFile(envPath);
29
+ const hasApiKey = /^RESPAN_API_KEY=.+$/m.test(envContent);
30
+ const hasEnvVar = !!process.env.RESPAN_API_KEY;
31
+ if (hasApiKey) {
32
+ this.log(` ${GREEN}\u2713${RESET} Found in ${DIM}${envPath}${RESET}`);
33
+ }
34
+ else {
35
+ this.log(` ${RED}\u2717${RESET} Not found in ${DIM}${envPath}${RESET}`);
36
+ }
37
+ if (hasEnvVar) {
38
+ this.log(` ${GREEN}\u2713${RESET} RESPAN_API_KEY environment variable is set`);
39
+ }
40
+ if (!hasApiKey && !hasEnvVar) {
41
+ this.log(` ${YELLOW}!${RESET} Run ${DIM}respan setup${RESET} to configure your API key`);
42
+ }
43
+ this.log('');
44
+ // ── 2. Respan CLI ──────────────────────────────────────────────
45
+ this.log(` ${BOLD}Respan CLI${RESET}`);
46
+ const respanInstalled = this.isBinaryInstalled('respan');
47
+ if (respanInstalled) {
48
+ try {
49
+ const version = execSync('respan --version', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
50
+ this.log(` ${GREEN}\u2713${RESET} Installed ${DIM}(${version})${RESET}`);
51
+ }
52
+ catch {
53
+ this.log(` ${GREEN}\u2713${RESET} Installed`);
54
+ }
55
+ }
56
+ else {
57
+ this.log(` ${YELLOW}!${RESET} Not installed globally. Install with: ${DIM}npm install -g @respan/cli${RESET}`);
58
+ }
59
+ this.log('');
60
+ // ── 3. Agent checks ────────────────────────────────────────────
61
+ this.log(` ${BOLD}Coding Agents${RESET}`);
62
+ this.log('');
63
+ const tools = {
64
+ 'claude-code': {
65
+ binary: 'claude',
66
+ name: 'Claude Code',
67
+ globalConfigs: ['~/.claude/settings.json'],
68
+ localConfigs: ['.claude/settings.local.json'],
69
+ globalSkills: ['~/.claude/skills/respan/SKILL.md', '~/.agents/skills/respan/SKILL.md'],
70
+ localSkills: ['.claude/skills/respan/SKILL.md', '.agents/skills/respan/SKILL.md'],
71
+ },
72
+ 'cursor': {
73
+ binary: 'cursor',
74
+ name: 'Cursor',
75
+ globalConfigs: ['~/.cursor/rules'],
76
+ localConfigs: ['.cursor/rules'],
77
+ globalSkills: ['~/.cursor/skills/respan/SKILL.md', '~/.agents/skills/respan/SKILL.md'],
78
+ localSkills: ['.cursor/skills/respan/SKILL.md', '.agents/skills/respan/SKILL.md'],
79
+ },
80
+ 'codex-cli': {
81
+ binary: 'codex',
82
+ name: 'Codex CLI',
83
+ globalConfigs: ['~/.codex/config.toml'],
84
+ localConfigs: ['.codex/respan.json'],
85
+ globalSkills: ['~/.codex/skills/respan/SKILL.md', '~/.agents/skills/respan/SKILL.md'],
86
+ localSkills: ['.codex/skills/respan/SKILL.md', '.agents/skills/respan/SKILL.md'],
87
+ },
88
+ 'gemini-cli': {
89
+ binary: 'gemini',
90
+ name: 'Gemini CLI',
91
+ globalConfigs: ['~/.gemini/settings.json'],
92
+ localConfigs: ['.gemini/settings.json'],
93
+ globalSkills: ['~/.gemini/skills/respan/SKILL.md', '~/.agents/skills/respan/SKILL.md'],
94
+ localSkills: ['.gemini/skills/respan/SKILL.md', '.agents/skills/respan/SKILL.md'],
95
+ },
96
+ 'opencode': {
97
+ binary: 'opencode',
98
+ name: 'OpenCode',
99
+ globalConfigs: ['~/.config/opencode'],
100
+ localConfigs: ['.opencode'],
101
+ globalSkills: ['~/.config/opencode/skills/respan/SKILL.md', '~/.agents/skills/respan/SKILL.md'],
102
+ localSkills: ['.opencode/skills/respan/SKILL.md', '.agents/skills/respan/SKILL.md'],
103
+ },
104
+ };
105
+ for (const [id, tool] of Object.entries(tools)) {
106
+ const binaryFound = this.isBinaryInstalled(tool.binary);
107
+ this.log(` ${BOLD}${tool.name}${RESET} ${DIM}(${tool.binary})${RESET}`);
108
+ // Binary
109
+ if (binaryFound) {
110
+ this.log(` ${GREEN}\u2713${RESET} Binary found on PATH`);
111
+ }
112
+ else {
113
+ this.log(` ${DIM}\u2013${RESET} Binary not found`);
114
+ }
115
+ // Config
116
+ if (checkGlobal) {
117
+ for (const cfg of tool.globalConfigs) {
118
+ const resolved = expandHome(cfg);
119
+ if (fs.existsSync(resolved)) {
120
+ this.log(` ${GREEN}\u2713${RESET} Global config: ${DIM}${cfg}${RESET}`);
121
+ }
122
+ }
123
+ }
124
+ if (checkLocal) {
125
+ for (const cfg of tool.localConfigs) {
126
+ const resolved = path.join(projectRoot, cfg);
127
+ if (fs.existsSync(resolved)) {
128
+ this.log(` ${GREEN}\u2713${RESET} Local config: ${DIM}${cfg}${RESET}`);
129
+ }
130
+ }
131
+ }
132
+ // Skills
133
+ let hasSkill = false;
134
+ if (checkGlobal) {
135
+ for (const skill of tool.globalSkills) {
136
+ const resolved = expandHome(skill);
137
+ if (fs.existsSync(resolved)) {
138
+ this.log(` ${GREEN}\u2713${RESET} Global skill: ${DIM}${skill}${RESET}`);
139
+ hasSkill = true;
140
+ }
141
+ }
142
+ }
143
+ if (checkLocal) {
144
+ for (const skill of tool.localSkills) {
145
+ const resolved = path.join(projectRoot, skill);
146
+ if (fs.existsSync(resolved)) {
147
+ this.log(` ${GREEN}\u2713${RESET} Local skill: ${DIM}${skill}${RESET}`);
148
+ hasSkill = true;
149
+ }
150
+ }
151
+ }
152
+ if (binaryFound && !hasSkill) {
153
+ this.log(` ${YELLOW}!${RESET} No Respan setup skill installed. Run ${DIM}respan setup --agent ${id}${RESET}`);
154
+ }
155
+ this.log('');
156
+ }
157
+ // ── 4. SDK install docs ────────────────────────────────────────
158
+ this.log(` ${BOLD}SDK Install Docs${RESET}`);
159
+ const localDocsPath = path.join(projectRoot, '.respan', 'skills', 'sdk-install');
160
+ const globalDocsPath = expandHome('~/.respan/skills/sdk-install');
161
+ if (checkLocal && fs.existsSync(localDocsPath)) {
162
+ const files = fs.readdirSync(localDocsPath);
163
+ this.log(` ${GREEN}\u2713${RESET} Local: ${DIM}${localDocsPath}${RESET} (${files.length} files)`);
164
+ }
165
+ else if (checkLocal) {
166
+ this.log(` ${DIM}\u2013${RESET} No local SDK docs`);
167
+ }
168
+ if (checkGlobal && fs.existsSync(globalDocsPath)) {
169
+ const files = fs.readdirSync(globalDocsPath);
170
+ this.log(` ${GREEN}\u2713${RESET} Global: ${DIM}${globalDocsPath}${RESET} (${files.length} files)`);
171
+ }
172
+ else if (checkGlobal) {
173
+ this.log(` ${DIM}\u2013${RESET} No global SDK docs`);
174
+ }
175
+ this.log('');
176
+ // ── 5. Fetched docs ────────────────────────────────────────────
177
+ this.log(` ${BOLD}Documentation${RESET}`);
178
+ const localFetchedDocs = path.join(projectRoot, '.respan', 'skills', 'docs');
179
+ const globalFetchedDocs = expandHome('~/.respan/skills/docs');
180
+ if (checkLocal && fs.existsSync(localFetchedDocs)) {
181
+ const files = fs.readdirSync(localFetchedDocs);
182
+ this.log(` ${GREEN}\u2713${RESET} Local: ${DIM}${localFetchedDocs}${RESET} (${files.length} pages)`);
183
+ }
184
+ if (checkGlobal && fs.existsSync(globalFetchedDocs)) {
185
+ const files = fs.readdirSync(globalFetchedDocs);
186
+ this.log(` ${GREEN}\u2713${RESET} Global: ${DIM}${globalFetchedDocs}${RESET} (${files.length} pages)`);
187
+ }
188
+ if (checkLocal && !fs.existsSync(localFetchedDocs)) {
189
+ this.log(` ${DIM}\u2013${RESET} No local docs`);
190
+ }
191
+ if (checkGlobal && !fs.existsSync(globalFetchedDocs)) {
192
+ this.log(` ${DIM}\u2013${RESET} No global docs`);
193
+ }
194
+ this.log('');
195
+ }
196
+ isBinaryInstalled(binary) {
197
+ try {
198
+ execSync(`command -v ${binary}`, { stdio: 'pipe' });
199
+ return true;
200
+ }
201
+ catch {
202
+ return false;
203
+ }
204
+ }
205
+ }
206
+ SetupDoctor.description = 'Diagnose coding-agent setup for Respan';
207
+ SetupDoctor.examples = [
208
+ 'respan setup doctor',
209
+ 'respan setup doctor --global',
210
+ ];
211
+ SetupDoctor.flags = {
212
+ ...BaseCommand.baseFlags,
213
+ local: Flags.boolean({
214
+ description: 'Check local project setup',
215
+ default: false,
216
+ exclusive: ['global'],
217
+ }),
218
+ global: Flags.boolean({
219
+ description: 'Check global setup',
220
+ default: false,
221
+ exclusive: ['local'],
222
+ }),
223
+ };
224
+ export default SetupDoctor;
@@ -0,0 +1,45 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class Setup extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ agent: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'no-instrument': import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ };
15
+ run(): Promise<void>;
16
+ private askApiKey;
17
+ private verifyApiKey;
18
+ /**
19
+ * Build a rich demo OTLP trace that mimics a customer support agent workflow:
20
+ * workflow: customer_support_pipeline
21
+ * └── agent: support_agent
22
+ * ├── tool: lookup_order
23
+ * ├── chat: openai.chat (classify intent)
24
+ * ├── tool: process_refund
25
+ * ├── chat: openai.chat (generate response)
26
+ * └── task: log_resolution
27
+ */
28
+ private buildDemoTrace;
29
+ private detectAgents;
30
+ private selectAgent;
31
+ private installSkill;
32
+ private runInstrumentAgent;
33
+ private getSkillMd;
34
+ private logStep;
35
+ private isBinaryInstalled;
36
+ private extractEnvVar;
37
+ private saveToEnv;
38
+ private getGitEmail;
39
+ /**
40
+ * Send a lightweight setup notification (email only).
41
+ * This helps the team know when new users set up Respan so we can
42
+ * reach out and help with onboarding.
43
+ */
44
+ private notifySetup;
45
+ }