am-i-vibing 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -27,16 +27,18 @@ console.log(`Detected: ${result.name} (${result.type})`);
27
27
  - **Aider**
28
28
  - **Bolt**
29
29
  - **Claude Code**
30
+ - **Codex CLI**
31
+ - **Crush**
30
32
  - **Cursor**
31
33
  - **Gemini CLI**
32
34
  - **GitHub Copilot Agent**
33
35
  - **Jules**
34
- - **Codex CLI**
36
+ - **Octofriend** (process detection only — requires `checkProcesses`)
37
+ - **opencode**
35
38
  - **Replit**
36
39
  - **Warp**
37
40
  - **Windsurf**
38
41
  - **Zed**
39
- - **opencode**
40
42
 
41
43
  ## Example use case
42
44
 
@@ -104,6 +106,36 @@ if (isHybrid()) {
104
106
  // Note: Hybrid environments return true for both isAgent() and isInteractive()
105
107
  ```
106
108
 
109
+ ### Process-tree detection (opt-in)
110
+
111
+ Most providers expose an environment variable that uniquely identifies them, and
112
+ that's the only signal `am-i-vibing` consults by default. A small number (e.g.
113
+ Octofriend) can only be detected by inspecting the parent process chain. Reading
114
+ the process tree spawns a subprocess and is meaningfully slow on Windows, so it
115
+ is **off by default**.
116
+
117
+ To opt in:
118
+
119
+ ```typescript
120
+ import { detectAgenticEnvironment } from "am-i-vibing";
121
+
122
+ const result = detectAgenticEnvironment({ checkProcesses: true });
123
+ ```
124
+
125
+ You can also pre-supply an ancestry (e.g. if you are already collecting one):
126
+
127
+ ```typescript
128
+ import { detectAgenticEnvironment } from "am-i-vibing";
129
+ import { getProcessAncestry } from "process-ancestry";
130
+
131
+ const result = detectAgenticEnvironment({
132
+ processAncestry: getProcessAncestry(),
133
+ });
134
+ ```
135
+
136
+ Supplying `processAncestry` implies `checkProcesses: true` unless you set it to
137
+ `false` explicitly.
138
+
107
139
  ## Detection Result
108
140
 
109
141
  The library returns a `DetectionResult` object with the following structure:
@@ -151,7 +183,8 @@ npx am-i-vibing --debug
151
183
  - `-f, --format <json|text>` - Output format (default: text)
152
184
  - `-c, --check <agent|interactive|hybrid>` - Check for specific environment type
153
185
  - `-q, --quiet` - Only output result, no labels
154
- - `-d, --debug` - Debug output with environment and process info
186
+ - `-p, --check-processes` - Also walk the process tree for detection (slower, off by default)
187
+ - `-d, --debug` - Debug output with environment and process info (implies `--check-processes`)
155
188
  - `-h, --help` - Show help message
156
189
 
157
190
  ### Exit Codes
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { detectAgenticEnvironment, isAgent, isHybrid, isInteractive } from "./detector-LEsE74_L.js";
3
- import { parseArgs } from "node:util";
2
+ import { i as isInteractive, n as isAgent, r as isHybrid, t as detectAgenticEnvironment } from "./detector-BAdXY3uq.mjs";
4
3
  import { getProcessAncestry } from "process-ancestry";
5
-
4
+ import { parseArgs } from "node:util";
6
5
  //#region src/cli.ts
7
6
  function parseCliArgs() {
8
7
  const { values } = parseArgs({
@@ -31,6 +30,11 @@ function parseCliArgs() {
31
30
  type: "boolean",
32
31
  short: "d",
33
32
  default: false
33
+ },
34
+ "check-processes": {
35
+ type: "boolean",
36
+ short: "p",
37
+ default: false
34
38
  }
35
39
  },
36
40
  allowPositionals: false
@@ -52,7 +56,8 @@ function parseCliArgs() {
52
56
  check: values.check,
53
57
  quiet: values.quiet,
54
58
  help: values.help,
55
- debug: values.debug
59
+ debug: values.debug,
60
+ checkProcesses: values["check-processes"]
56
61
  };
57
62
  }
58
63
  function showHelp() {
@@ -66,6 +71,8 @@ OPTIONS:
66
71
  -f, --format <json|text> Output format (default: text)
67
72
  -c, --check <agent|interactive|hybrid> Check for specific environment type
68
73
  -q, --quiet Only output result, no labels
74
+ -p, --check-processes Also walk the process tree for detection
75
+ (slower, off by default)
69
76
  -d, --debug Debug output with environment and process info
70
77
  -h, --help Show this help message
71
78
 
@@ -75,6 +82,7 @@ EXAMPLES:
75
82
  npx am-i-vibing --check agent # Check if running under agent
76
83
  npx am-i-vibing --check hybrid # Check if running under hybrid
77
84
  npx am-i-vibing --quiet # Minimal output
85
+ npx am-i-vibing --check-processes # Include process-tree detection
78
86
  npx am-i-vibing --debug # Debug with full environment info
79
87
 
80
88
  EXIT CODES:
@@ -82,11 +90,11 @@ EXIT CODES:
82
90
  1 No agentic environment detected (or specific check failed)
83
91
  `);
84
92
  }
85
- function checkEnvironmentType(checkType) {
93
+ function checkEnvironmentType(checkType, options) {
86
94
  switch (checkType) {
87
- case "agent": return isAgent();
88
- case "interactive": return isInteractive();
89
- case "hybrid": return isHybrid();
95
+ case "agent": return isAgent({ checkProcesses: options.checkProcesses });
96
+ case "interactive": return isInteractive({ checkProcesses: options.checkProcesses });
97
+ case "hybrid": return isHybrid({ checkProcesses: options.checkProcesses });
90
98
  default: return false;
91
99
  }
92
100
  }
@@ -107,13 +115,10 @@ function formatOutput(result, options) {
107
115
  }
108
116
  if (options.format === "json") return JSON.stringify(result, null, 2);
109
117
  if (options.quiet) {
110
- if (options.check) return checkEnvironmentType(options.check) ? "true" : "false";
118
+ if (options.check) return checkEnvironmentType(options.check, options) ? "true" : "false";
111
119
  return result.isAgentic ? `${result.name}` : "none";
112
120
  }
113
- if (options.check) {
114
- const matches = checkEnvironmentType(options.check);
115
- return matches ? `✓ Running in ${options.check} environment: ${result.name}` : `✗ Not running in ${options.check} environment`;
116
- }
121
+ if (options.check) return checkEnvironmentType(options.check, options) ? `✓ Running in ${options.check} environment: ${result.name}` : `✗ Not running in ${options.check} environment`;
117
122
  if (!result.isAgentic) return "✗ No agentic environment detected";
118
123
  return `✓ Detected: [${result.id}] ${result.name} (${result.type})`;
119
124
  }
@@ -123,15 +128,17 @@ function main() {
123
128
  showHelp();
124
129
  process.exit(0);
125
130
  }
126
- const result = detectAgenticEnvironment();
131
+ const checkProcesses = options.checkProcesses || options.debug;
132
+ const result = detectAgenticEnvironment({ checkProcesses });
127
133
  let exitCode = 1;
128
- if (options.check) exitCode = checkEnvironmentType(options.check) ? 0 : 1;
134
+ if (options.check) exitCode = checkEnvironmentType(options.check, { checkProcesses }) ? 0 : 1;
129
135
  else exitCode = result.isAgentic ? 0 : 1;
130
136
  const output = formatOutput(result, options);
131
137
  console.log(output);
132
138
  process.exit(exitCode);
133
139
  }
134
140
  main();
135
-
136
141
  //#endregion
137
- //# sourceMappingURL=cli.js.map
142
+ export {};
143
+
144
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { parseArgs } from \"node:util\";\nimport { getProcessAncestry } from \"process-ancestry\";\nimport {\n detectAgenticEnvironment,\n isAgent,\n isInteractive,\n isHybrid,\n} from \"./detector.js\";\n\ninterface CliOptions {\n format?: \"json\" | \"text\";\n check?: \"agent\" | \"interactive\" | \"hybrid\";\n quiet?: boolean;\n help?: boolean;\n debug?: boolean;\n checkProcesses?: boolean;\n}\n\nfunction parseCliArgs(): CliOptions {\n const { values } = parseArgs({\n args: process.argv.slice(2),\n options: {\n format: {\n type: \"string\",\n short: \"f\",\n default: \"text\",\n },\n check: {\n type: \"string\",\n short: \"c\",\n },\n quiet: {\n type: \"boolean\",\n short: \"q\",\n default: false,\n },\n help: {\n type: \"boolean\",\n short: \"h\",\n default: false,\n },\n debug: {\n type: \"boolean\",\n short: \"d\",\n default: false,\n },\n \"check-processes\": {\n type: \"boolean\",\n short: \"p\",\n default: false,\n },\n },\n allowPositionals: false,\n });\n\n // Validate format option\n if (values.format && ![\"json\", \"text\"].includes(values.format)) {\n console.error(\n `Error: Invalid format '${values.format}'. Must be 'json' or 'text'.`,\n );\n process.exit(1);\n }\n\n // Validate check option\n if (\n values.check &&\n ![\"agent\", \"interactive\", \"hybrid\"].includes(values.check)\n ) {\n console.error(\n `Error: Invalid check type '${values.check}'. Must be 'agent', 'interactive', or 'hybrid'.`,\n );\n process.exit(1);\n }\n\n return {\n format: values.format as \"json\" | \"text\",\n check: values.check as \"agent\" | \"interactive\" | \"hybrid\",\n quiet: values.quiet,\n help: values.help,\n debug: values.debug,\n checkProcesses: values[\"check-processes\"],\n };\n}\n\nfunction showHelp(): void {\n console.log(`\nam-i-vibing - Detect agentic coding environments\n\nUSAGE:\n npx am-i-vibing [OPTIONS]\n\nOPTIONS:\n -f, --format <json|text> Output format (default: text)\n -c, --check <agent|interactive|hybrid> Check for specific environment type\n -q, --quiet Only output result, no labels\n -p, --check-processes Also walk the process tree for detection\n (slower, off by default)\n -d, --debug Debug output with environment and process info\n -h, --help Show this help message\n\nEXAMPLES:\n npx am-i-vibing # Detect current environment\n npx am-i-vibing --format json # JSON output\n npx am-i-vibing --check agent # Check if running under agent\n npx am-i-vibing --check hybrid # Check if running under hybrid\n npx am-i-vibing --quiet # Minimal output\n npx am-i-vibing --check-processes # Include process-tree detection\n npx am-i-vibing --debug # Debug with full environment info\n\nEXIT CODES:\n 0 Agentic environment detected (or specific check passed)\n 1 No agentic environment detected (or specific check failed)\n`);\n}\n\nfunction checkEnvironmentType(\n checkType: string,\n options: { checkProcesses?: boolean },\n): boolean {\n switch (checkType) {\n case \"agent\":\n return isAgent({ checkProcesses: options.checkProcesses });\n case \"interactive\":\n return isInteractive({ checkProcesses: options.checkProcesses });\n case \"hybrid\":\n return isHybrid({ checkProcesses: options.checkProcesses });\n default:\n return false;\n }\n}\n\nfunction formatOutput(\n result: ReturnType<typeof detectAgenticEnvironment>,\n options: CliOptions,\n): string {\n if (options.debug) {\n let processAncestry: any[] = [];\n try {\n processAncestry = getProcessAncestry();\n } catch (error) {\n processAncestry = [{ error: \"Failed to get process ancestry\" }];\n }\n\n const debugOutput = {\n detection: result,\n environment: process.env,\n processAncestry,\n };\n return JSON.stringify(debugOutput, null, 2);\n }\n\n if (options.format === \"json\") {\n return JSON.stringify(result, null, 2);\n }\n\n if (options.quiet) {\n if (options.check) {\n return checkEnvironmentType(options.check, options) ? \"true\" : \"false\";\n }\n return result.isAgentic ? `${result.name}` : \"none\";\n }\n\n if (options.check) {\n const matches = checkEnvironmentType(options.check, options);\n return matches\n ? `✓ Running in ${options.check} environment: ${result.name}`\n : `✗ Not running in ${options.check} environment`;\n }\n\n if (!result.isAgentic) {\n return \"✗ No agentic environment detected\";\n }\n\n return `✓ Detected: [${result.id}] ${result.name} (${result.type})`;\n}\n\nfunction main(): void {\n const options = parseCliArgs();\n\n if (options.help) {\n showHelp();\n process.exit(0);\n }\n\n // --debug always reveals the full picture, including process detection\n const checkProcesses = options.checkProcesses || options.debug;\n const result = detectAgenticEnvironment({ checkProcesses });\n let exitCode = 1;\n\n if (options.check) {\n exitCode = checkEnvironmentType(options.check, { checkProcesses }) ? 0 : 1;\n } else {\n exitCode = result.isAgentic ? 0 : 1;\n }\n\n const output = formatOutput(result, options);\n console.log(output);\n\n process.exit(exitCode);\n}\n\nmain();\n"],"mappings":"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"}
@@ -1,15 +1,15 @@
1
1
  import { getProcessAncestry } from "process-ancestry";
2
-
3
2
  //#region src/providers.ts
4
3
  /**
5
4
  * Provider configurations for major AI coding tools
6
5
  */
7
6
  const providers = [
8
7
  {
9
- id: "sst-opencode",
10
- name: "SST OpenCode",
8
+ id: "opencode",
9
+ name: "OpenCode",
11
10
  type: "agent",
12
11
  envVars: [{ any: [
12
+ "OPENCODE",
13
13
  "OPENCODE_BIN_PATH",
14
14
  "OPENCODE_SERVER",
15
15
  "OPENCODE_APP_INFO",
@@ -42,14 +42,16 @@ const providers = [
42
42
  },
43
43
  {
44
44
  id: "gemini-agent",
45
- name: "Gemini Agent",
45
+ name: "Gemini CLI",
46
46
  type: "agent",
47
+ envVars: [["GEMINI_CLI", "1"]],
47
48
  processChecks: ["gemini"]
48
49
  },
49
50
  {
50
51
  id: "codex",
51
52
  name: "OpenAI Codex",
52
53
  type: "agent",
54
+ envVars: ["CODEX_THREAD_ID"],
53
55
  processChecks: ["codex"]
54
56
  },
55
57
  {
@@ -120,6 +122,11 @@ const providers = [
120
122
  id: "crush",
121
123
  name: "Crush",
122
124
  type: "agent",
125
+ envVars: [{ any: [
126
+ ["CRUSH", "1"],
127
+ ["AGENT", "crush"],
128
+ ["AI_AGENT", "crush"]
129
+ ] }],
123
130
  processChecks: ["crush"]
124
131
  },
125
132
  {
@@ -153,7 +160,6 @@ function getProvider(name) {
153
160
  function getProvidersByType(type) {
154
161
  return providers.filter((p) => p.type === type);
155
162
  }
156
-
157
163
  //#endregion
158
164
  //#region src/detector.ts
159
165
  /**
@@ -168,10 +174,7 @@ function checkEnvVar(envVarDef, env = process.env) {
168
174
  * Check if a process is running in the process tree
169
175
  */
170
176
  function checkProcess(processName, processAncestry) {
171
- try {
172
- const ancestry = processAncestry ?? getProcessAncestry();
173
- for (const ancestorProcess of ancestry) if (ancestorProcess.command?.includes(processName)) return true;
174
- } catch (error) {}
177
+ for (const ancestorProcess of processAncestry) if (ancestorProcess.command?.includes(processName)) return true;
175
178
  return false;
176
179
  }
177
180
  /**
@@ -209,13 +212,44 @@ function createDetectedResult(provider) {
209
212
  };
210
213
  }
211
214
  /**
212
- * Detect agentic coding environment
215
+ * Normalize the various supported argument shapes into a DetectOptions object.
216
+ *
217
+ * Supported shapes:
218
+ * - detectAgenticEnvironment()
219
+ * - detectAgenticEnvironment(options)
220
+ * - detectAgenticEnvironment(env) // legacy
221
+ * - detectAgenticEnvironment(env, processAncestry) // legacy
213
222
  */
214
- function detectAgenticEnvironment(env = process.env, processAncestry) {
215
- for (const provider of providers) {
216
- if (provider.envVars?.some((group) => checkEnvVars(group, env))) return createDetectedResult(provider);
217
- if (provider.processChecks?.some((processName) => checkProcess(processName, processAncestry))) return createDetectedResult(provider);
218
- if (runCustomDetectors(provider)) return createDetectedResult(provider);
223
+ function normalizeOptions(envOrOptions, legacyAncestry) {
224
+ if (envOrOptions != null && typeof envOrOptions === "object" && ("env" in envOrOptions || "processAncestry" in envOrOptions || "checkProcesses" in envOrOptions)) {
225
+ const opts = envOrOptions;
226
+ return {
227
+ env: opts.env ?? process.env,
228
+ processAncestry: opts.processAncestry,
229
+ checkProcesses: opts.checkProcesses ?? opts.processAncestry !== void 0
230
+ };
231
+ }
232
+ return {
233
+ env: envOrOptions ?? process.env,
234
+ processAncestry: legacyAncestry,
235
+ checkProcesses: legacyAncestry !== void 0
236
+ };
237
+ }
238
+ function detectAgenticEnvironment(envOrOptions, legacyAncestry) {
239
+ const { env, processAncestry, checkProcesses } = normalizeOptions(envOrOptions, legacyAncestry);
240
+ for (const provider of providers) if (provider.envVars?.some((group) => checkEnvVars(group, env))) return createDetectedResult(provider);
241
+ for (const provider of providers) if (runCustomDetectors(provider)) return createDetectedResult(provider);
242
+ if (checkProcesses) {
243
+ let cachedAncestry = processAncestry;
244
+ const getAncestry = () => {
245
+ if (cachedAncestry === void 0) try {
246
+ cachedAncestry = getProcessAncestry();
247
+ } catch {
248
+ cachedAncestry = [];
249
+ }
250
+ return cachedAncestry;
251
+ };
252
+ for (const provider of providers) if (provider.processChecks?.some((processName) => checkProcess(processName, getAncestry()))) return createDetectedResult(provider);
219
253
  }
220
254
  return {
221
255
  isAgentic: false,
@@ -224,28 +258,21 @@ function detectAgenticEnvironment(env = process.env, processAncestry) {
224
258
  type: null
225
259
  };
226
260
  }
227
- /**
228
- * Check if currently running in any agent environment
229
- */
230
- function isAgent(env = process.env, processAncestry) {
231
- const result = detectAgenticEnvironment(env, processAncestry);
261
+ function isProvider(providerName, envOrOptions, legacyAncestry) {
262
+ return detectAgenticEnvironment(envOrOptions, legacyAncestry).name === providerName;
263
+ }
264
+ function isAgent(envOrOptions, legacyAncestry) {
265
+ const result = detectAgenticEnvironment(envOrOptions, legacyAncestry);
232
266
  return result.type === "agent" || result.type === "hybrid";
233
267
  }
234
- /**
235
- * Check if currently running in any interactive AI environment
236
- */
237
- function isInteractive(env = process.env, processAncestry) {
238
- const result = detectAgenticEnvironment(env, processAncestry);
268
+ function isInteractive(envOrOptions, legacyAncestry) {
269
+ const result = detectAgenticEnvironment(envOrOptions, legacyAncestry);
239
270
  return result.type === "interactive" || result.type === "hybrid";
240
271
  }
241
- /**
242
- * Check if currently running in any hybrid AI environment
243
- */
244
- function isHybrid(env = process.env, processAncestry) {
245
- const result = detectAgenticEnvironment(env, processAncestry);
246
- return result.type === "hybrid";
272
+ function isHybrid(envOrOptions, legacyAncestry) {
273
+ return detectAgenticEnvironment(envOrOptions, legacyAncestry).type === "hybrid";
247
274
  }
248
-
249
275
  //#endregion
250
- export { detectAgenticEnvironment, getProvider, getProvidersByType, isAgent, isHybrid, isInteractive, providers };
251
- //# sourceMappingURL=detector-LEsE74_L.js.map
276
+ export { isProvider as a, providers as c, isInteractive as i, isAgent as n, getProvider as o, isHybrid as r, getProvidersByType as s, detectAgenticEnvironment as t };
277
+
278
+ //# sourceMappingURL=detector-BAdXY3uq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector-BAdXY3uq.mjs","names":[],"sources":["../src/providers.ts","../src/detector.ts"],"sourcesContent":["import type { ProviderConfig } from \"./types.js\";\n\n/**\n * Provider configurations for major AI coding tools\n */\nexport const providers: ProviderConfig[] = [\n {\n id: \"opencode\",\n name: \"OpenCode\",\n type: \"agent\",\n envVars: [\n {\n any: [\n \"OPENCODE\",\n \"OPENCODE_BIN_PATH\",\n \"OPENCODE_SERVER\",\n \"OPENCODE_APP_INFO\",\n \"OPENCODE_MODES\",\n ],\n },\n ],\n },\n {\n id: \"jules\",\n name: \"Jules\",\n type: \"agent\",\n envVars: [\n {\n all: [\n [\"HOME\", \"/home/jules\"],\n [\"USER\", \"swebot\"],\n ],\n },\n ],\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n type: \"agent\",\n envVars: [\"CLAUDECODE\"],\n },\n {\n id: \"cursor-agent\",\n name: \"Cursor Agent\",\n type: \"agent\",\n envVars: [\n {\n all: [\"CURSOR_TRACE_ID\", [\"PAGER\", \"head -n 10000 | cat\"]],\n },\n ],\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n type: \"interactive\",\n envVars: [\"CURSOR_TRACE_ID\"],\n },\n {\n id: \"gemini-agent\",\n name: \"Gemini CLI\",\n type: \"agent\",\n // Gemini CLI sets GEMINI_CLI=1 on every shell command and MCP server it spawns.\n // See: https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/services/shellExecutionService.ts\n envVars: [[\"GEMINI_CLI\", \"1\"]],\n processChecks: [\"gemini\"],\n },\n {\n id: \"codex\",\n name: \"OpenAI Codex\",\n type: \"agent\",\n // Codex injects CODEX_THREAD_ID (the session conversation id) into every shell command it runs.\n // See: https://github.com/openai/codex/blob/main/codex-rs/protocol/src/shell_environment.rs\n envVars: [\"CODEX_THREAD_ID\"],\n processChecks: [\"codex\"],\n },\n {\n id: \"replit\",\n name: \"Replit\",\n type: \"agent\",\n envVars: [\"REPL_ID\"],\n },\n {\n id: \"aider\",\n name: \"Aider\",\n type: \"agent\",\n envVars: [\"AIDER_API_KEY\"],\n processChecks: [\"aider\"],\n },\n {\n id: \"bolt-agent\",\n name: \"Bolt.new Agent\",\n type: \"agent\",\n envVars: [\n {\n all: [[\"SHELL\", \"/bin/jsh\"], \"npm_config_yes\"],\n },\n ],\n },\n {\n id: \"bolt\",\n name: \"Bolt.new\",\n type: \"interactive\",\n envVars: [\n {\n all: [[\"SHELL\", \"/bin/jsh\"]],\n none: [\"npm_config_yes\"],\n },\n ],\n },\n {\n id: \"zed-agent\",\n name: \"Zed Agent\",\n type: \"agent\",\n envVars: [\n {\n all: [\n [\"TERM_PROGRAM\", \"zed\"],\n [\"PAGER\", \"cat\"],\n ],\n },\n ],\n },\n {\n id: \"zed\",\n name: \"Zed\",\n type: \"interactive\",\n envVars: [\n {\n all: [[\"TERM_PROGRAM\", \"zed\"]],\n none: [[\"PAGER\", \"cat\"]],\n },\n ],\n },\n {\n id: \"replit-assistant\",\n name: \"Replit Assistant\",\n type: \"agent\",\n envVars: [\n {\n all: [\"REPL_ID\", [\"REPLIT_MODE\", \"assistant\"]],\n },\n ],\n },\n {\n id: \"replit\",\n name: \"Replit\",\n type: \"interactive\",\n envVars: [\n {\n all: [\"REPL_ID\"],\n none: [[\"REPLIT_MODE\", \"assistant\"]],\n },\n ],\n },\n {\n id: \"windsurf\",\n name: \"Windsurf\",\n type: \"agent\",\n envVars: [\"CODEIUM_EDITOR_APP_ROOT\"],\n },\n {\n id: \"crush\",\n name: \"Crush\",\n type: \"agent\",\n // Crush sets CRUSH=1 (and AGENT=crush, AI_AGENT=crush) on every shell exec.\n // See: https://github.com/charmbracelet/crush/blob/main/internal/shell/shell.go\n envVars: [\n {\n any: [\n [\"CRUSH\", \"1\"],\n [\"AGENT\", \"crush\"],\n [\"AI_AGENT\", \"crush\"],\n ],\n },\n ],\n processChecks: [\"crush\"],\n },\n {\n id: \"vscode-copilot-agent\",\n name: \"GitHub Copilot in VS Code\",\n type: \"agent\",\n envVars: [\n {\n all: [\n [\"TERM_PROGRAM\", \"vscode\"],\n [\"GIT_PAGER\", \"cat\"],\n ],\n },\n ],\n },\n {\n id: \"warp\",\n name: \"Warp Terminal\",\n type: \"hybrid\",\n envVars: [\n {\n all: [[\"TERM_PROGRAM\", \"WarpTerminal\"]],\n },\n ],\n },\n {\n id: \"octofriend\",\n name: \"Octofriend\",\n type: \"agent\",\n // Octofriend does not currently expose an environment variable signal, so it\n // can only be detected via process ancestry (opt-in).\n processChecks: [\"octofriend\"],\n },\n];\n\n/**\n * Get provider configuration by name\n */\nexport function getProvider(name: string): ProviderConfig | undefined {\n return providers.find((p) => p.name === name);\n}\n\n/**\n * Get all providers of a specific type\n */\nexport function getProvidersByType(\n type: \"agent\" | \"interactive\" | \"hybrid\",\n): ProviderConfig[] {\n return providers.filter((p) => p.type === type);\n}\n","import type {\n DetectionResult,\n DetectOptions,\n ProviderConfig,\n EnvVarDefinition,\n EnvVarGroup,\n} from \"./types.js\";\nimport { providers } from \"./providers.js\";\nimport { getProcessAncestry } from \"process-ancestry\";\n\n/**\n * Check if a specific environment variable exists (handles both strings and tuples)\n */\nfunction checkEnvVar(\n envVarDef: EnvVarDefinition,\n env: Record<string, string | undefined> = process.env,\n): boolean {\n const [envVar, expectedValue] =\n typeof envVarDef === \"string\" ? [envVarDef, undefined] : envVarDef;\n\n const actualValue = env[envVar];\n return Boolean(\n actualValue && (!expectedValue || actualValue === expectedValue),\n );\n}\n\n/**\n * Check if a process is running in the process tree\n */\nfunction checkProcess(\n processName: string,\n processAncestry: Array<{ command?: string }>,\n): boolean {\n for (const ancestorProcess of processAncestry) {\n if (ancestorProcess.command?.includes(processName)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if an environment variable group matches based on its properties\n */\nfunction checkEnvVars(\n definition: EnvVarGroup | EnvVarDefinition,\n env: Record<string, string | undefined> = process.env,\n): boolean {\n if (typeof definition === \"string\" || Array.isArray(definition)) {\n return checkEnvVar(definition, env);\n }\n\n const { any, all, none } = definition;\n\n // Check ANY conditions (OR logic) - at least one must pass\n const anyResult =\n !any?.length || any.some((envVar) => checkEnvVar(envVar, env));\n\n // Check ALL conditions (AND logic) - all must pass\n const allResult =\n !all?.length || all.every((envVar) => checkEnvVar(envVar, env));\n\n // Check NONE conditions (NOT logic) - none should pass\n const noneResult =\n !none?.length || !none.some((envVar) => checkEnvVar(envVar, env));\n\n return anyResult && allResult && noneResult;\n}\n\n/**\n * Run custom detectors for a provider\n */\nfunction runCustomDetectors(provider: ProviderConfig): boolean {\n return (\n provider.customDetectors?.some((detector) => {\n try {\n return detector();\n } catch {\n return false;\n }\n }) ?? false\n );\n}\n\n/**\n * Create a positive detection result\n */\nfunction createDetectedResult(provider: ProviderConfig): DetectionResult {\n return {\n isAgentic: true,\n id: provider.id,\n name: provider.name,\n type: provider.type,\n };\n}\n\n/**\n * Normalize the various supported argument shapes into a DetectOptions object.\n *\n * Supported shapes:\n * - detectAgenticEnvironment()\n * - detectAgenticEnvironment(options)\n * - detectAgenticEnvironment(env) // legacy\n * - detectAgenticEnvironment(env, processAncestry) // legacy\n */\nfunction normalizeOptions(\n envOrOptions?: Record<string, string | undefined> | DetectOptions,\n legacyAncestry?: Array<{ command?: string }>,\n): Required<Pick<DetectOptions, \"env\" | \"checkProcesses\">> & {\n processAncestry?: Array<{ command?: string }>;\n} {\n // Distinguish a DetectOptions object from a raw env record. DetectOptions\n // has at least one of the known keys; an env record is a flat string map.\n const looksLikeOptions =\n envOrOptions != null &&\n typeof envOrOptions === \"object\" &&\n (\"env\" in envOrOptions ||\n \"processAncestry\" in envOrOptions ||\n \"checkProcesses\" in envOrOptions);\n\n if (looksLikeOptions) {\n const opts = envOrOptions as DetectOptions;\n return {\n env: opts.env ?? process.env,\n processAncestry: opts.processAncestry,\n // If the caller pre-supplied an ancestry, default checkProcesses to true\n // unless they explicitly opted out.\n checkProcesses:\n opts.checkProcesses ?? opts.processAncestry !== undefined,\n };\n }\n\n return {\n env:\n (envOrOptions as Record<string, string | undefined> | undefined) ??\n process.env,\n processAncestry: legacyAncestry,\n // Legacy callers that explicitly passed an ancestry are presumed to want\n // process checks; otherwise default off.\n checkProcesses: legacyAncestry !== undefined,\n };\n}\n\n/**\n * Detect agentic coding environment\n */\nexport function detectAgenticEnvironment(\n options?: DetectOptions,\n): DetectionResult;\n/**\n * @deprecated Pass an options object instead. This signature is retained for\n * backwards compatibility and will be removed in a future major release.\n */\nexport function detectAgenticEnvironment(\n env: Record<string, string | undefined>,\n processAncestry?: Array<{ command?: string }>,\n): DetectionResult;\nexport function detectAgenticEnvironment(\n envOrOptions?: Record<string, string | undefined> | DetectOptions,\n legacyAncestry?: Array<{ command?: string }>,\n): DetectionResult {\n const { env, processAncestry, checkProcesses } = normalizeOptions(\n envOrOptions,\n legacyAncestry,\n );\n\n // Fast path: check all environment variables first\n for (const provider of providers) {\n if (provider.envVars?.some((group) => checkEnvVars(group, env))) {\n return createDetectedResult(provider);\n }\n }\n\n // Custom detectors next (cheap, in-process)\n for (const provider of providers) {\n if (runCustomDetectors(provider)) {\n return createDetectedResult(provider);\n }\n }\n\n // Slow path: process ancestry checks. Opt-in only because reading the process\n // tree spawns a subprocess (notably slow on Windows).\n if (checkProcesses) {\n let cachedAncestry = processAncestry;\n const getAncestry = () => {\n if (cachedAncestry === undefined) {\n try {\n cachedAncestry = getProcessAncestry();\n } catch {\n cachedAncestry = [];\n }\n }\n return cachedAncestry;\n };\n\n for (const provider of providers) {\n if (\n provider.processChecks?.some((processName) =>\n checkProcess(processName, getAncestry()),\n )\n ) {\n return createDetectedResult(provider);\n }\n }\n }\n\n // No provider detected\n return {\n isAgentic: false,\n id: null,\n name: null,\n type: null,\n };\n}\n\n/**\n * Check if currently running in a specific provider\n */\nexport function isProvider(\n providerName: string,\n options?: DetectOptions,\n): boolean;\n/**\n * @deprecated Pass an options object instead.\n */\nexport function isProvider(\n providerName: string,\n env: Record<string, string | undefined>,\n processAncestry?: Array<{ command?: string }>,\n): boolean;\nexport function isProvider(\n providerName: string,\n envOrOptions?: Record<string, string | undefined> | DetectOptions,\n legacyAncestry?: Array<{ command?: string }>,\n): boolean {\n const result = detectAgenticEnvironment(\n envOrOptions as DetectOptions,\n legacyAncestry as Array<{ command?: string }> | undefined,\n );\n return result.name === providerName;\n}\n\n/**\n * Check if currently running in any agent environment\n */\nexport function isAgent(options?: DetectOptions): boolean;\n/**\n * @deprecated Pass an options object instead.\n */\nexport function isAgent(\n env: Record<string, string | undefined>,\n processAncestry?: Array<{ command?: string }>,\n): boolean;\nexport function isAgent(\n envOrOptions?: Record<string, string | undefined> | DetectOptions,\n legacyAncestry?: Array<{ command?: string }>,\n): boolean {\n const result = detectAgenticEnvironment(\n envOrOptions as DetectOptions,\n legacyAncestry as Array<{ command?: string }> | undefined,\n );\n return result.type === \"agent\" || result.type === \"hybrid\";\n}\n\n/**\n * Check if currently running in any interactive AI environment\n */\nexport function isInteractive(options?: DetectOptions): boolean;\n/**\n * @deprecated Pass an options object instead.\n */\nexport function isInteractive(\n env: Record<string, string | undefined>,\n processAncestry?: Array<{ command?: string }>,\n): boolean;\nexport function isInteractive(\n envOrOptions?: Record<string, string | undefined> | DetectOptions,\n legacyAncestry?: Array<{ command?: string }>,\n): boolean {\n const result = detectAgenticEnvironment(\n envOrOptions as DetectOptions,\n legacyAncestry as Array<{ command?: string }> | undefined,\n );\n return result.type === \"interactive\" || result.type === \"hybrid\";\n}\n\n/**\n * Check if currently running in any hybrid AI environment\n */\nexport function isHybrid(options?: DetectOptions): boolean;\n/**\n * @deprecated Pass an options object instead.\n */\nexport function isHybrid(\n env: Record<string, string | undefined>,\n processAncestry?: Array<{ command?: string }>,\n): boolean;\nexport function isHybrid(\n envOrOptions?: Record<string, string | undefined> | DetectOptions,\n legacyAncestry?: Array<{ command?: string }>,\n): boolean {\n const result = detectAgenticEnvironment(\n envOrOptions as DetectOptions,\n legacyAncestry as Array<{ command?: string }> | undefined,\n );\n return result.type === \"hybrid\";\n}\n"],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ //#region src/types.d.ts
2
+ /**
3
+ * The type of AI coding environment detected
4
+ */
5
+ type AgenticType = "agent" | "interactive" | "hybrid";
6
+ /**
7
+ * Environment variable definition - either just a name or a name/value tuple
8
+ */
9
+ type EnvVarDefinition = string | [string, string];
10
+ /**
11
+ * Environment variable group with logical operators
12
+ */
13
+ interface EnvVarGroup {
14
+ /** ANY of these environment variables can match (OR logic) */
15
+ any?: EnvVarDefinition[];
16
+ /** ALL of these environment variables must match (AND logic) */
17
+ all?: EnvVarDefinition[];
18
+ /** NONE of these environment variables should be present (NOT logic) */
19
+ none?: EnvVarDefinition[];
20
+ }
21
+ /**
22
+ * Configuration for detecting a specific AI coding provider
23
+ */
24
+ interface ProviderConfig {
25
+ /** Unique identifier for the provider */
26
+ id: string;
27
+ /** Human-readable name of the provider */
28
+ name: string;
29
+ /** Type of AI coding environment */
30
+ type: AgenticType;
31
+ /** Environment variables */
32
+ envVars?: Array<EnvVarGroup | EnvVarDefinition>;
33
+ /** Process names to check for in the process tree (only used when checkProcesses is enabled) */
34
+ processChecks?: string[];
35
+ /** Custom detection functions for complex logic */
36
+ customDetectors?: (() => boolean)[];
37
+ }
38
+ /**
39
+ * Result of agentic environment detection
40
+ */
41
+ interface DetectionResult {
42
+ /** Whether an agentic environment was detected */
43
+ isAgentic: boolean;
44
+ /** ID of the detected provider, if any */
45
+ id: string | null;
46
+ /** Name of the detected provider, if any */
47
+ name: string | null;
48
+ /** Type of agentic environment, if detected */
49
+ type: AgenticType | null;
50
+ }
51
+ /**
52
+ * Options for `detectAgenticEnvironment` and related helpers.
53
+ */
54
+ interface DetectOptions {
55
+ /**
56
+ * Environment variables to inspect. Defaults to `process.env`.
57
+ */
58
+ env?: Record<string, string | undefined>;
59
+ /**
60
+ * Pre-computed process ancestry (from `process-ancestry` or compatible). When
61
+ * provided, this is used in place of fetching ancestry at detection time.
62
+ * Implies `checkProcesses: true` unless explicitly set to `false`.
63
+ */
64
+ processAncestry?: Array<{
65
+ command?: string;
66
+ }>;
67
+ /**
68
+ * Whether to fall back to process-ancestry checks when no environment-variable
69
+ * match is found. Defaults to `false` because fetching the process tree is
70
+ * expensive on some platforms (notably Windows).
71
+ *
72
+ * Set to `true` to enable detection of providers that only expose a
73
+ * processChecks signal (e.g. Octofriend), at the cost of spawning a
74
+ * subprocess to read the process tree.
75
+ */
76
+ checkProcesses?: boolean;
77
+ }
78
+ //#endregion
79
+ //#region src/providers.d.ts
80
+ /**
81
+ * Provider configurations for major AI coding tools
82
+ */
83
+ declare const providers: ProviderConfig[];
84
+ /**
85
+ * Get provider configuration by name
86
+ */
87
+ declare function getProvider(name: string): ProviderConfig | undefined;
88
+ /**
89
+ * Get all providers of a specific type
90
+ */
91
+ declare function getProvidersByType(type: "agent" | "interactive" | "hybrid"): ProviderConfig[];
92
+ //#endregion
93
+ //#region src/detector.d.ts
94
+ /**
95
+ * Detect agentic coding environment
96
+ */
97
+ declare function detectAgenticEnvironment(options?: DetectOptions): DetectionResult;
98
+ /**
99
+ * @deprecated Pass an options object instead. This signature is retained for
100
+ * backwards compatibility and will be removed in a future major release.
101
+ */
102
+ declare function detectAgenticEnvironment(env: Record<string, string | undefined>, processAncestry?: Array<{
103
+ command?: string;
104
+ }>): DetectionResult;
105
+ /**
106
+ * Check if currently running in a specific provider
107
+ */
108
+ declare function isProvider(providerName: string, options?: DetectOptions): boolean;
109
+ /**
110
+ * @deprecated Pass an options object instead.
111
+ */
112
+ declare function isProvider(providerName: string, env: Record<string, string | undefined>, processAncestry?: Array<{
113
+ command?: string;
114
+ }>): boolean;
115
+ /**
116
+ * Check if currently running in any agent environment
117
+ */
118
+ declare function isAgent(options?: DetectOptions): boolean;
119
+ /**
120
+ * @deprecated Pass an options object instead.
121
+ */
122
+ declare function isAgent(env: Record<string, string | undefined>, processAncestry?: Array<{
123
+ command?: string;
124
+ }>): boolean;
125
+ /**
126
+ * Check if currently running in any interactive AI environment
127
+ */
128
+ declare function isInteractive(options?: DetectOptions): boolean;
129
+ /**
130
+ * @deprecated Pass an options object instead.
131
+ */
132
+ declare function isInteractive(env: Record<string, string | undefined>, processAncestry?: Array<{
133
+ command?: string;
134
+ }>): boolean;
135
+ /**
136
+ * Check if currently running in any hybrid AI environment
137
+ */
138
+ declare function isHybrid(options?: DetectOptions): boolean;
139
+ /**
140
+ * @deprecated Pass an options object instead.
141
+ */
142
+ declare function isHybrid(env: Record<string, string | undefined>, processAncestry?: Array<{
143
+ command?: string;
144
+ }>): boolean;
145
+ //#endregion
146
+ export { type AgenticType, type DetectOptions, type DetectionResult, type EnvVarDefinition, type EnvVarGroup, type ProviderConfig, detectAgenticEnvironment as default, detectAgenticEnvironment, getProvider, getProvidersByType, isAgent, isHybrid, isInteractive, isProvider, providers };
147
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/providers.ts","../src/detector.ts"],"mappings":";;AAGA;;KAAY,WAAA;;;AAKZ;KAAY,gBAAA;;;;UAKK,WAAA;EAAW;EAE1B,GAAA,GAAM,gBAAA;EAAA;EAGN,GAAA,GAAM,gBAAA;EAGC;EAAP,IAAA,GAAO,gBAAA;AAAA;;;;UAMQ,cAAA;EANf;EAQA,EAAA;EARuB;EAWvB,IAAA;EALe;EAQf,IAAA,EAAM,WAAA;;EAGN,OAAA,GAAU,KAAA,CAAM,WAAA,GAAc,gBAAA;EAAd;EAGhB,aAAA;EAHU;EAMV,eAAA;AAAA;;;;UAMe,eAAA;EAZf;EAcA,SAAA;EAdgB;EAiBhB,EAAA;EAdA;EAiBA,IAAA;EAde;EAiBf,IAAA,EAAM,WAAA;AAAA;;;;UAMS,aAAA;EAZf;;;EAgBA,GAAA,GAAM,MAAA;EAVW;;AAMnB;;;EAWE,eAAA,GAAkB,KAAA;IAAQ,OAAA;EAAA;EAA1B;;;;;;;;;EAWA,cAAA;AAAA;;;AAtFF;;;AAAA,cCEa,SAAA,EAAW,cAAA;;ADGxB;;iBC6MgB,WAAA,CAAY,IAAA,WAAe,cAAA;;;ADxM3C;iBC+MgB,kBAAA,CACd,IAAA,uCACC,cAAA;;;AD3NH;;;AAAA,iBE+IgB,wBAAA,CACd,OAAA,GAAU,aAAA,GACT,eAAA;;AF5IH;;;iBEiJgB,wBAAA,CACd,GAAA,EAAK,MAAA,8BACL,eAAA,GAAkB,KAAA;EAAQ,OAAA;AAAA,KACzB,eAAA;;;;iBA8Da,UAAA,CACd,YAAA,UACA,OAAA,GAAU,aAAA;;;;iBAKI,UAAA,CACd,YAAA,UACA,GAAA,EAAK,MAAA,8BACL,eAAA,GAAkB,KAAA;EAAQ,OAAA;AAAA;;;;iBAiBZ,OAAA,CAAQ,OAAA,GAAU,aAAA;;AF1NlC;;iBE8NgB,OAAA,CACd,GAAA,EAAK,MAAA,8BACL,eAAA,GAAkB,KAAA;EAAQ,OAAA;AAAA;;;;iBAgBZ,aAAA,CAAc,OAAA,GAAU,aAAA;;;;iBAIxB,aAAA,CACd,GAAA,EAAK,MAAA,8BACL,eAAA,GAAkB,KAAA;EAAQ,OAAA;AAAA;;;;iBAgBZ,QAAA,CAAS,OAAA,GAAU,aAAA;;;AF/OnC;iBEmPgB,QAAA,CACd,GAAA,EAAK,MAAA,8BACL,eAAA,GAAkB,KAAA;EAAQ,OAAA;AAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import { a as isProvider, c as providers, i as isInteractive, n as isAgent, o as getProvider, r as isHybrid, s as getProvidersByType, t as detectAgenticEnvironment } from "./detector-BAdXY3uq.mjs";
2
+ //#region src/index.ts
3
+ var src_default = detectAgenticEnvironment;
4
+ //#endregion
5
+ export { src_default as default, detectAgenticEnvironment, getProvider, getProvidersByType, isAgent, isHybrid, isInteractive, isProvider, providers };
6
+
7
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * am-i-vibing - Detect agentic coding environments and AI assistant tools\n */\n\n// Export types\nexport type {\n AgenticType,\n ProviderConfig,\n DetectionResult,\n DetectOptions,\n EnvVarDefinition,\n EnvVarGroup,\n} from \"./types.js\";\n\n// Export providers\nexport { providers, getProvider, getProvidersByType } from \"./providers.js\";\n\n// Export detection functions\nexport {\n detectAgenticEnvironment,\n isProvider,\n isAgent,\n isInteractive,\n isHybrid,\n} from \"./detector.js\";\n\n// Import for default export\nimport { detectAgenticEnvironment } from \"./detector.js\";\n\n// Convenience export for the main function\nexport default detectAgenticEnvironment;\n"],"mappings":";;AA8BA,IAAA,cAAe"}
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "name": "am-i-vibing",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Detect agentic coding environments and AI assistant tools",
5
5
  "type": "module",
6
- "main": "dist/index.js",
6
+ "main": "dist/index.mjs",
7
7
  "bin": {
8
- "am-i-vibing": "dist/cli.js"
8
+ "am-i-vibing": "dist/cli.mjs"
9
9
  },
10
10
  "files": [
11
11
  "dist"
12
12
  ],
13
13
  "exports": {
14
- ".": "./dist/index.js"
14
+ ".": "./dist/index.mjs"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@arethetypeswrong/cli": "^0.18.2",
18
- "@types/node": "^22.17.0",
19
- "publint": "^0.3.12",
20
- "tsdown": "^0.13.3",
21
- "typescript": "^5.9.2",
22
- "vitest": "^3.2.4"
18
+ "@types/node": "^24.12.2",
19
+ "publint": "^0.3.18",
20
+ "tsdown": "^0.21.10",
21
+ "typescript": "^6.0.3",
22
+ "vitest": "^4.1.5"
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",
@@ -38,7 +38,7 @@
38
38
  "author": "Matt Kane",
39
39
  "license": "MIT",
40
40
  "dependencies": {
41
- "process-ancestry": "^0.0.2"
41
+ "process-ancestry": "^0.1.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsdown src/index.ts src/cli.ts --format esm --dts --clean",
package/dist/cli.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.js","names":["checkType: string","result: ReturnType<typeof detectAgenticEnvironment>","options: CliOptions","processAncestry: any[]"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { parseArgs } from \"node:util\";\nimport { getProcessAncestry } from \"process-ancestry\";\nimport {\n detectAgenticEnvironment,\n isAgent,\n isInteractive,\n isHybrid,\n} from \"./detector.js\";\n\ninterface CliOptions {\n format?: \"json\" | \"text\";\n check?: \"agent\" | \"interactive\" | \"hybrid\";\n quiet?: boolean;\n help?: boolean;\n debug?: boolean;\n}\n\nfunction parseCliArgs(): CliOptions {\n const { values } = parseArgs({\n args: process.argv.slice(2),\n options: {\n format: {\n type: \"string\",\n short: \"f\",\n default: \"text\",\n },\n check: {\n type: \"string\",\n short: \"c\",\n },\n quiet: {\n type: \"boolean\",\n short: \"q\",\n default: false,\n },\n help: {\n type: \"boolean\",\n short: \"h\",\n default: false,\n },\n debug: {\n type: \"boolean\",\n short: \"d\",\n default: false,\n },\n },\n allowPositionals: false,\n });\n\n // Validate format option\n if (values.format && ![\"json\", \"text\"].includes(values.format)) {\n console.error(\n `Error: Invalid format '${values.format}'. Must be 'json' or 'text'.`,\n );\n process.exit(1);\n }\n\n // Validate check option\n if (values.check && ![\"agent\", \"interactive\", \"hybrid\"].includes(values.check)) {\n console.error(\n `Error: Invalid check type '${values.check}'. Must be 'agent', 'interactive', or 'hybrid'.`,\n );\n process.exit(1);\n }\n\n return {\n format: values.format as \"json\" | \"text\",\n check: values.check as \"agent\" | \"interactive\" | \"hybrid\",\n quiet: values.quiet,\n help: values.help,\n debug: values.debug,\n };\n}\n\nfunction showHelp(): void {\n console.log(`\nam-i-vibing - Detect agentic coding environments\n\nUSAGE:\n npx am-i-vibing [OPTIONS]\n\nOPTIONS:\n -f, --format <json|text> Output format (default: text)\n -c, --check <agent|interactive|hybrid> Check for specific environment type\n -q, --quiet Only output result, no labels\n -d, --debug Debug output with environment and process info\n -h, --help Show this help message\n\nEXAMPLES:\n npx am-i-vibing # Detect current environment\n npx am-i-vibing --format json # JSON output\n npx am-i-vibing --check agent # Check if running under agent\n npx am-i-vibing --check hybrid # Check if running under hybrid\n npx am-i-vibing --quiet # Minimal output\n npx am-i-vibing --debug # Debug with full environment info\n\nEXIT CODES:\n 0 Agentic environment detected (or specific check passed)\n 1 No agentic environment detected (or specific check failed)\n`);\n}\n\nfunction checkEnvironmentType(checkType: string): boolean {\n switch (checkType) {\n case \"agent\":\n return isAgent();\n case \"interactive\":\n return isInteractive();\n case \"hybrid\":\n return isHybrid();\n default:\n return false;\n }\n}\n\nfunction formatOutput(\n result: ReturnType<typeof detectAgenticEnvironment>,\n options: CliOptions,\n): string {\n if (options.debug) {\n let processAncestry: any[] = [];\n try {\n processAncestry = getProcessAncestry();\n } catch (error) {\n processAncestry = [{ error: \"Failed to get process ancestry\" }];\n }\n\n const debugOutput = {\n detection: result,\n environment: process.env,\n processAncestry,\n };\n return JSON.stringify(debugOutput, null, 2);\n }\n\n if (options.format === \"json\") {\n return JSON.stringify(result, null, 2);\n }\n\n if (options.quiet) {\n if (options.check) {\n return checkEnvironmentType(options.check) ? \"true\" : \"false\";\n }\n return result.isAgentic ? `${result.name}` : \"none\";\n }\n\n if (options.check) {\n const matches = checkEnvironmentType(options.check);\n return matches\n ? `✓ Running in ${options.check} environment: ${result.name}`\n : `✗ Not running in ${options.check} environment`;\n }\n\n if (!result.isAgentic) {\n return \"✗ No agentic environment detected\";\n }\n\n return `✓ Detected: [${result.id}] ${result.name} (${result.type})`;\n}\n\nfunction main(): void {\n const options = parseCliArgs();\n\n if (options.help) {\n showHelp();\n process.exit(0);\n }\n\n const result = detectAgenticEnvironment();\n let exitCode = 1;\n\n if (options.check) {\n exitCode = checkEnvironmentType(options.check) ? 0 : 1;\n } else {\n exitCode = result.isAgentic ? 0 : 1;\n }\n\n const output = formatOutput(result, options);\n console.log(output);\n\n process.exit(exitCode);\n}\n\nmain();\n"],"mappings":";;;;;;AAmBA,SAAS,eAA2B;CAClC,MAAM,EAAE,QAAQ,GAAG,UAAU;EAC3B,MAAM,QAAQ,KAAK,MAAM,EAAE;EAC3B,SAAS;GACP,QAAQ;IACN,MAAM;IACN,OAAO;IACP,SAAS;GACV;GACD,OAAO;IACL,MAAM;IACN,OAAO;GACR;GACD,OAAO;IACL,MAAM;IACN,OAAO;IACP,SAAS;GACV;GACD,MAAM;IACJ,MAAM;IACN,OAAO;IACP,SAAS;GACV;GACD,OAAO;IACL,MAAM;IACN,OAAO;IACP,SAAS;GACV;EACF;EACD,kBAAkB;CACnB,EAAC;AAGF,KAAI,OAAO,UAAU,CAAC,CAAC,QAAQ,MAAO,EAAC,SAAS,OAAO,OAAO,EAAE;EAC9D,QAAQ,MACN,CAAC,uBAAuB,EAAE,OAAO,OAAO,4BAA4B,CAAC,CACtE;EACD,QAAQ,KAAK,EAAE;CAChB;AAGD,KAAI,OAAO,SAAS,CAAC;EAAC;EAAS;EAAe;CAAS,EAAC,SAAS,OAAO,MAAM,EAAE;EAC9E,QAAQ,MACN,CAAC,2BAA2B,EAAE,OAAO,MAAM,+CAA+C,CAAC,CAC5F;EACD,QAAQ,KAAK,EAAE;CAChB;AAED,QAAO;EACL,QAAQ,OAAO;EACf,OAAO,OAAO;EACd,OAAO,OAAO;EACd,MAAM,OAAO;EACb,OAAO,OAAO;CACf;AACF;AAED,SAAS,WAAiB;CACxB,QAAQ,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;AAwBf,CAAC,CAAC;AACD;AAED,SAAS,qBAAqBA,WAA4B;AACxD,SAAQ,WAAR;EACE,KAAK,QACH,QAAO,SAAS;EAClB,KAAK,cACH,QAAO,eAAe;EACxB,KAAK,SACH,QAAO,UAAU;EACnB,QACE,QAAO;CACV;AACF;AAED,SAAS,aACPC,QACAC,SACQ;AACR,KAAI,QAAQ,OAAO;EACjB,IAAIC,kBAAyB,CAAE;AAC/B,MAAI;GACF,kBAAkB,oBAAoB;EACvC,SAAQ,OAAO;GACd,kBAAkB,CAAC,EAAE,OAAO,iCAAkC,CAAC;EAChE;EAED,MAAM,cAAc;GAClB,WAAW;GACX,aAAa,QAAQ;GACrB;EACD;AACD,SAAO,KAAK,UAAU,aAAa,MAAM,EAAE;CAC5C;AAED,KAAI,QAAQ,WAAW,OACrB,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE;AAGxC,KAAI,QAAQ,OAAO;AACjB,MAAI,QAAQ,MACV,QAAO,qBAAqB,QAAQ,MAAM,GAAG,SAAS;AAExD,SAAO,OAAO,YAAY,GAAG,OAAO,MAAM,GAAG;CAC9C;AAED,KAAI,QAAQ,OAAO;EACjB,MAAM,UAAU,qBAAqB,QAAQ,MAAM;AACnD,SAAO,UACH,CAAC,aAAa,EAAE,QAAQ,MAAM,cAAc,EAAE,OAAO,MAAM,GAC3D,CAAC,iBAAiB,EAAE,QAAQ,MAAM,YAAY,CAAC;CACpD;AAED,KAAI,CAAC,OAAO,UACV,QAAO;AAGT,QAAO,CAAC,aAAa,EAAE,OAAO,GAAG,EAAE,EAAE,OAAO,KAAK,EAAE,EAAE,OAAO,KAAK,CAAC,CAAC;AACpE;AAED,SAAS,OAAa;CACpB,MAAM,UAAU,cAAc;AAE9B,KAAI,QAAQ,MAAM;EAChB,UAAU;EACV,QAAQ,KAAK,EAAE;CAChB;CAED,MAAM,SAAS,0BAA0B;CACzC,IAAI,WAAW;AAEf,KAAI,QAAQ,OACV,WAAW,qBAAqB,QAAQ,MAAM,GAAG,IAAI;MAErD,WAAW,OAAO,YAAY,IAAI;CAGpC,MAAM,SAAS,aAAa,QAAQ,QAAQ;CAC5C,QAAQ,IAAI,OAAO;CAEnB,QAAQ,KAAK,SAAS;AACvB;AAED,MAAM"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"detector-LEsE74_L.js","names":["providers: ProviderConfig[]","name: string","type: \"agent\" | \"interactive\" | \"hybrid\"","envVarDef: EnvVarDefinition","env: Record<string, string | undefined>","processName: string","processAncestry?: Array<{ command?: string }>","definition: EnvVarGroup | EnvVarDefinition","provider: ProviderConfig"],"sources":["../src/providers.ts","../src/detector.ts"],"sourcesContent":["import type { ProviderConfig } from \"./types.js\";\n\n/**\n * Provider configurations for major AI coding tools\n */\nexport const providers: ProviderConfig[] = [\n {\n id: \"sst-opencode\",\n name: \"SST OpenCode\",\n type: \"agent\",\n envVars: [\n {\n any: [\n \"OPENCODE_BIN_PATH\",\n \"OPENCODE_SERVER\",\n \"OPENCODE_APP_INFO\",\n \"OPENCODE_MODES\",\n ],\n },\n ],\n },\n {\n id: \"jules\",\n name: \"Jules\",\n type: \"agent\",\n envVars: [{ all: [[\"HOME\", \"/home/jules\"], [\"USER\", \"swebot\"]] }],\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n type: \"agent\",\n envVars: [\"CLAUDECODE\"],\n },\n {\n id: \"cursor-agent\",\n name: \"Cursor Agent\",\n type: \"agent\",\n envVars: [\n {\n all: [\"CURSOR_TRACE_ID\", [\"PAGER\", \"head -n 10000 | cat\"]],\n },\n ],\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n type: \"interactive\",\n envVars: [\"CURSOR_TRACE_ID\"],\n },\n {\n id: \"gemini-agent\",\n name: \"Gemini Agent\",\n type: \"agent\",\n processChecks: [\"gemini\"],\n },\n {\n id: \"codex\",\n name: \"OpenAI Codex\",\n type: \"agent\",\n processChecks: [\"codex\"],\n },\n {\n id: \"replit\",\n name: \"Replit\",\n type: \"agent\",\n envVars: [\"REPL_ID\"],\n },\n {\n id: \"aider\",\n name: \"Aider\",\n type: \"agent\",\n envVars: [\"AIDER_API_KEY\"],\n processChecks: [\"aider\"],\n },\n {\n id: \"bolt-agent\",\n name: \"Bolt.new Agent\",\n type: \"agent\",\n envVars: [\n {\n all: [[\"SHELL\", \"/bin/jsh\"], \"npm_config_yes\"],\n },\n ],\n },\n {\n id: \"bolt\",\n name: \"Bolt.new\",\n type: \"interactive\",\n envVars: [\n {\n all: [[\"SHELL\", \"/bin/jsh\"]],\n none: [\"npm_config_yes\"],\n },\n ],\n },\n {\n id: \"zed-agent\",\n name: \"Zed Agent\",\n type: \"agent\",\n envVars: [\n {\n all: [\n [\"TERM_PROGRAM\", \"zed\"],\n [\"PAGER\", \"cat\"],\n ],\n },\n ],\n },\n {\n id: \"zed\",\n name: \"Zed\",\n type: \"interactive\",\n envVars: [\n {\n all: [[\"TERM_PROGRAM\", \"zed\"]],\n none: [[\"PAGER\", \"cat\"]],\n },\n ],\n },\n {\n id: \"replit-assistant\",\n name: \"Replit Assistant\",\n type: \"agent\",\n envVars: [\n {\n all: [\"REPL_ID\", [\"REPLIT_MODE\", \"assistant\"]],\n },\n ],\n },\n {\n id: \"replit\",\n name: \"Replit\",\n type: \"interactive\",\n envVars: [\n {\n all: [\"REPL_ID\"],\n none: [[\"REPLIT_MODE\", \"assistant\"]],\n },\n ],\n },\n {\n id: \"windsurf\",\n name: \"Windsurf\",\n type: \"agent\",\n envVars: [\"CODEIUM_EDITOR_APP_ROOT\"],\n },\n {\n id: \"crush\",\n name: \"Crush\",\n type: \"agent\",\n processChecks: [\"crush\"],\n },\n {\n id: \"vscode-copilot-agent\",\n name: \"GitHub Copilot in VS Code\",\n type: \"agent\",\n envVars: [\n {\n all: [\n [\"TERM_PROGRAM\", \"vscode\"],\n [\"GIT_PAGER\", \"cat\"],\n ],\n },\n ],\n },\n {\n id: \"warp\",\n name: \"Warp Terminal\",\n type: \"hybrid\",\n envVars: [\n {\n all: [\n [\"TERM_PROGRAM\", \"WarpTerminal\"],\n ],\n },\n ],\n },\n {\n id: \"octofriend\",\n name: \"Octofriend\",\n type: \"agent\",\n processChecks: [\"octofriend\"],\n },\n];\n\n/**\n * Get provider configuration by name\n */\nexport function getProvider(name: string): ProviderConfig | undefined {\n return providers.find((p) => p.name === name);\n}\n\n/**\n * Get all providers of a specific type\n */\nexport function getProvidersByType(\n type: \"agent\" | \"interactive\" | \"hybrid\",\n): ProviderConfig[] {\n return providers.filter((p) => p.type === type);\n}\n","import type {\n DetectionResult,\n ProviderConfig,\n EnvVarDefinition,\n EnvVarGroup,\n} from \"./types.js\";\nimport { providers } from \"./providers.js\";\nimport { getProcessAncestry } from \"process-ancestry\";\n\n/**\n * Check if a specific environment variable exists (handles both strings and tuples)\n */\nfunction checkEnvVar(\n envVarDef: EnvVarDefinition,\n env: Record<string, string | undefined> = process.env,\n): boolean {\n const [envVar, expectedValue] =\n typeof envVarDef === \"string\" ? [envVarDef, undefined] : envVarDef;\n\n const actualValue = env[envVar];\n return Boolean(\n actualValue && (!expectedValue || actualValue === expectedValue),\n );\n}\n\n/**\n * Check if a process is running in the process tree\n */\nfunction checkProcess(\n processName: string,\n processAncestry?: Array<{ command?: string }>,\n): boolean {\n try {\n const ancestry = processAncestry ?? getProcessAncestry();\n for (const ancestorProcess of ancestry) {\n if (ancestorProcess.command?.includes(processName)) {\n return true;\n }\n }\n } catch (error) {\n // Ignore process check errors\n }\n return false;\n}\n\n/**\n * Check if an environment variable group matches based on its properties\n */\nfunction checkEnvVars(\n definition: EnvVarGroup | EnvVarDefinition,\n env: Record<string, string | undefined> = process.env,\n): boolean {\n if (typeof definition === \"string\" || Array.isArray(definition)) {\n return checkEnvVar(definition, env);\n }\n\n const { any, all, none } = definition;\n\n // Check ANY conditions (OR logic) - at least one must pass\n const anyResult =\n !any?.length || any.some((envVar) => checkEnvVar(envVar, env));\n\n // Check ALL conditions (AND logic) - all must pass\n const allResult =\n !all?.length || all.every((envVar) => checkEnvVar(envVar, env));\n\n // Check NONE conditions (NOT logic) - none should pass\n const noneResult =\n !none?.length || !none.some((envVar) => checkEnvVar(envVar, env));\n\n return anyResult && allResult && noneResult;\n}\n\n/**\n * Run custom detectors for a provider\n */\nfunction runCustomDetectors(provider: ProviderConfig): boolean {\n return (\n provider.customDetectors?.some((detector) => {\n try {\n return detector();\n } catch {\n return false;\n }\n }) ?? false\n );\n}\n\n/**\n * Create a positive detection result\n */\nfunction createDetectedResult(provider: ProviderConfig): DetectionResult {\n return {\n isAgentic: true,\n id: provider.id,\n name: provider.name,\n type: provider.type,\n };\n}\n\n/**\n * Detect agentic coding environment\n */\nexport function detectAgenticEnvironment(\n env: Record<string, string | undefined> = process.env,\n processAncestry?: Array<{ command?: string }>,\n): DetectionResult {\n for (const provider of providers) {\n // Check environment variables\n if (provider.envVars?.some((group) => checkEnvVars(group, env))) {\n return createDetectedResult(provider);\n }\n\n // Check processes\n if (provider.processChecks?.some((processName) => checkProcess(processName, processAncestry))) {\n return createDetectedResult(provider);\n }\n\n // Run custom detectors\n if (runCustomDetectors(provider)) {\n return createDetectedResult(provider);\n }\n }\n\n // No provider detected\n return {\n isAgentic: false,\n id: null,\n name: null,\n type: null,\n };\n}\n\n/**\n * Check if currently running in a specific provider\n */\nexport function isProvider(\n providerName: string,\n env: Record<string, string | undefined> = process.env,\n processAncestry?: Array<{ command?: string }>,\n): boolean {\n const result = detectAgenticEnvironment(env, processAncestry);\n return result.name === providerName;\n}\n\n/**\n * Check if currently running in any agent environment\n */\nexport function isAgent(\n env: Record<string, string | undefined> = process.env,\n processAncestry?: Array<{ command?: string }>,\n): boolean {\n const result = detectAgenticEnvironment(env, processAncestry);\n return result.type === \"agent\" || result.type === \"hybrid\";\n}\n\n/**\n * Check if currently running in any interactive AI environment\n */\nexport function isInteractive(\n env: Record<string, string | undefined> = process.env,\n processAncestry?: Array<{ command?: string }>,\n): boolean {\n const result = detectAgenticEnvironment(env, processAncestry);\n return result.type === \"interactive\" || result.type === \"hybrid\";\n}\n\n/**\n * Check if currently running in any hybrid AI environment\n */\nexport function isHybrid(\n env: Record<string, string | undefined> = process.env,\n processAncestry?: Array<{ command?: string }>,\n): boolean {\n const result = detectAgenticEnvironment(env, processAncestry);\n return result.type === \"hybrid\";\n}\n"],"mappings":"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"}
package/dist/index.d.ts DELETED
@@ -1,97 +0,0 @@
1
- //#region src/types.d.ts
2
- /**
3
- * The type of AI coding environment detected
4
- */
5
- type AgenticType = "agent" | "interactive" | "hybrid";
6
- /**
7
- * Environment variable definition - either just a name or a name/value tuple
8
- */
9
- type EnvVarDefinition = string | [string, string];
10
- /**
11
- * Environment variable group with logical operators
12
- */
13
- interface EnvVarGroup {
14
- /** ANY of these environment variables can match (OR logic) */
15
- any?: EnvVarDefinition[];
16
- /** ALL of these environment variables must match (AND logic) */
17
- all?: EnvVarDefinition[];
18
- /** NONE of these environment variables should be present (NOT logic) */
19
- none?: EnvVarDefinition[];
20
- }
21
- /**
22
- * Configuration for detecting a specific AI coding provider
23
- */
24
- interface ProviderConfig {
25
- /** Unique identifier for the provider */
26
- id: string;
27
- /** Human-readable name of the provider */
28
- name: string;
29
- /** Type of AI coding environment */
30
- type: AgenticType;
31
- /** Environment variables */
32
- envVars?: Array<EnvVarGroup | EnvVarDefinition>;
33
- /** Process names to check for in the process tree */
34
- processChecks?: string[];
35
- /** Custom detection functions for complex logic */
36
- customDetectors?: (() => boolean)[];
37
- }
38
- /**
39
- * Result of agentic environment detection
40
- */
41
- interface DetectionResult {
42
- /** Whether an agentic environment was detected */
43
- isAgentic: boolean;
44
- /** ID of the detected provider, if any */
45
- id: string | null;
46
- /** Name of the detected provider, if any */
47
- name: string | null;
48
- /** Type of agentic environment, if detected */
49
- type: AgenticType | null;
50
- }
51
- //#endregion
52
- //#region src/providers.d.ts
53
- /**
54
- * Provider configurations for major AI coding tools
55
- */
56
- declare const providers: ProviderConfig[];
57
- /**
58
- * Get provider configuration by name
59
- */
60
- declare function getProvider(name: string): ProviderConfig | undefined;
61
- /**
62
- * Get all providers of a specific type
63
- */
64
- declare function getProvidersByType(type: "agent" | "interactive" | "hybrid"): ProviderConfig[];
65
- //#endregion
66
- //#region src/detector.d.ts
67
- /**
68
- * Detect agentic coding environment
69
- */
70
- declare function detectAgenticEnvironment(env?: Record<string, string | undefined>, processAncestry?: Array<{
71
- command?: string;
72
- }>): DetectionResult;
73
- /**
74
- * Check if currently running in a specific provider
75
- */
76
-
77
- /**
78
- * Check if currently running in any agent environment
79
- */
80
- declare function isAgent(env?: Record<string, string | undefined>, processAncestry?: Array<{
81
- command?: string;
82
- }>): boolean;
83
- /**
84
- * Check if currently running in any interactive AI environment
85
- */
86
- declare function isInteractive(env?: Record<string, string | undefined>, processAncestry?: Array<{
87
- command?: string;
88
- }>): boolean;
89
- /**
90
- * Check if currently running in any hybrid AI environment
91
- */
92
- declare function isHybrid(env?: Record<string, string | undefined>, processAncestry?: Array<{
93
- command?: string;
94
- }>): boolean;
95
- //#endregion
96
- export { type AgenticType, type DetectionResult, type ProviderConfig, detectAgenticEnvironment as default, detectAgenticEnvironment, getProvider, getProvidersByType, isAgent, isHybrid, isInteractive, providers };
97
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/providers.ts","../src/detector.ts"],"sourcesContent":[],"mappings":";;AAGA;AAKA;AAKiB,KAVL,WAAA,GAUgB,OAAA,GAAA,aAAA,GAAA,QAAA;;;;AAQnB,KAbG,gBAAA,GAaH,MAAA,GAAA,CAAA,MAAA,EAAA,MAAA,CAAA;AAAgB;AAMzB;;AAQQ,UAtBS,WAAA,CAsBT;EAAW;EAGU,GAAG,CAAA,EAvBxB,gBAuBwB,EAAA;EAAgB;EAA/B,GAAA,CAAA,EApBT,gBAoBS,EAAA;EAYA;SA7BR;;;AChBT;AAuLA;AAOgB,UDxKC,cAAA,CCwKiB;;;;EC5FlB,IAAA,EAAA,MAAA;EAAwB;EAAA,IACjC,EFrEC,WEqED;EAAM;EACY,OACtB,CAAA,EFpES,KEoET,CFpEe,WEoEf,GFpE6B,gBEoE7B,CAAA;EAAe;EA0CF,aAAO,CAAA,EAAA,MAAA,EAAA;EAAA;EAAA,eAChB,CAAA,EAAA,CAAA,GAAA,GAAA,OAAA,CAAA,EAAA;;AACkB;AASzB;;AACO,UF9GU,eAAA,CE8GV;EAAM;EACY,SAAA,EAAA,OAAA;EAST;EAAQ,EAAA,EAAA,MAAA,GAAA,IAAA;EAAA;EACX,IACO,EAAA,MAAA,GAAA,IAAA;EAAK;QF/GjB;;;;AA1DR;AAKA;AAKA;AAA4B,cCRf,SDQe,ECRJ,cDQI,EAAA;;;;AAQH,iBCuKT,WAAA,CDvKS,IAAA,EAAA,MAAA,CAAA,ECuKkB,cDvKlB,GAAA,SAAA;AAMzB;;;AAWkB,iBC6JF,kBAAA,CD7JE,IAAA,EAAA,OAAA,GAAA,aAAA,GAAA,QAAA,CAAA,EC+Jf,cD/Je,EAAA;;;AAnClB;AAKA;AAKA;AAA4B,iBE0FZ,wBAAA,CF1FY,GAAA,CAAA,EE2FrB,MF3FqB,CAAA,MAAA,EAAA,MAAA,GAAA,SAAA,CAAA,EAAA,eAEJ,CAFI,EE4FR,KF5FQ,CAAA;EAAA,OAEpB,CAAA,EAAA,MAAA;CAAgB,CAAA,CAAA,EE2FrB,eFxFK;;AAGiB;AAMzB;;;;AAWiB;AAYA,iBEkGD,OAAA,CFlGgB,GAWb,CAAX,EEwFD,MFxFY,CAAA,MAAA,EAAA,MAAA,GAAA,SAAA,CAAA,EAAA,gBAAA,EEyFC,KFzFD,CAAA;;;;ACxDnB;AAuLA;AAOgB,iBCpCA,aAAA,CDsCb,GAAc,CAAd,ECrCI,MDqCU,CAAA,MAAA,EAAA,MAAA,GAAA,SAAA,CAAA,EAAA,gBAAA,ECpCG,KDoCH,CAAA;;;;AC9FjB;;AACO,iBAkES,QAAA,CAlET,GAAA,CAAA,EAmEA,MAnEA,CAAA,MAAA,EAAA,MAAA,GAAA,SAAA,CAAA,EAAA,eACkB,CADlB,EAoEa,KApEb,CAAA;EAAM,OACO,CAAA,EAAA,MAAA;CAAK,CAAA,CAAA,EAAA,OACtB"}
package/dist/index.js DELETED
@@ -1,8 +0,0 @@
1
- import { detectAgenticEnvironment, getProvider, getProvidersByType, isAgent, isHybrid, isInteractive, providers } from "./detector-LEsE74_L.js";
2
-
3
- //#region src/index.ts
4
- var src_default = detectAgenticEnvironment;
5
-
6
- //#endregion
7
- export { src_default as default, detectAgenticEnvironment, getProvider, getProvidersByType, isAgent, isHybrid, isInteractive, providers };
8
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * am-i-vibing - Detect agentic coding environments and AI assistant tools\n */\n\n// Export types\nexport type { AgenticType, ProviderConfig, DetectionResult } from \"./types.js\";\n\n// Export providers\nexport { providers, getProvider, getProvidersByType } from \"./providers.js\";\n\n// Export detection functions\nexport {\n detectAgenticEnvironment,\n isAgent,\n isInteractive,\n isHybrid,\n} from \"./detector.js\";\n\n// Import for default export\nimport { detectAgenticEnvironment } from \"./detector.js\";\n\n// Convenience export for the main function\nexport default detectAgenticEnvironment;\n"],"mappings":";;;AAsBA,kBAAe"}
File without changes