codehere 1.0.2 → 2.0.0-beta.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.
@@ -1,133 +0,0 @@
1
- /**
2
- * Typo guard for the bare `codehere <word>` action.
3
- *
4
- * Why this exists: codehere's bare action treats any string as a one-shot task
5
- * prompt for the Conductor. Without a guard, a typo like `codehere helpp`
6
- * silently spawns a Claude task to interpret "helpp" as a question, burning
7
- * API tokens for what was clearly meant to be `codehere help`.
8
- *
9
- * Earlier rc.15 attempt (greedy edit-check + prefix match) over-matched and
10
- * blocked legitimate single-word prompts like "task-runner" or "h". Codex
11
- * caught the regression in review. The rc.16 version uses real Levenshtein,
12
- * a length floor, and explicit kebab-case exemption.
13
- *
14
- * v0.6 extension: handle multi-token input. Original guard bailed on any
15
- * whitespace, which let `setupp foo bar` and `daemon start` silently route to
16
- * the task spawner. Now: checkTypo tokenizes and runs typo-detection on the
17
- * first token. looksLikeUnknownCommand catches the narrow `cmd subcmd` shape
18
- * (two short alphabetic tokens, first not a known command, not a typo) so
19
- * cases like `daemon start` get an "unknown command" message instead of
20
- * silently spawning an LLM task.
21
- */
22
- /** Standard Levenshtein distance. Bounded inputs only — no allocation guard. */
23
- export function levenshtein(a, b) {
24
- const m = a.length;
25
- const n = b.length;
26
- if (m === 0)
27
- return n;
28
- if (n === 0)
29
- return m;
30
- const dp = new Array(n + 1);
31
- for (let j = 0; j <= n; j++)
32
- dp[j] = j;
33
- for (let i = 1; i <= m; i++) {
34
- let prev = dp[0];
35
- dp[0] = i;
36
- for (let j = 1; j <= n; j++) {
37
- const tmp = dp[j];
38
- dp[j] = a[i - 1] === b[j - 1] ? prev : Math.min(prev, dp[j], dp[j - 1]) + 1;
39
- prev = tmp;
40
- }
41
- }
42
- return dp[n];
43
- }
44
- /**
45
- * Internal: typo-check a single token against known commands.
46
- * Returns up to 3 closest matches (Lev ≤ 2, distance < input length) or null.
47
- */
48
- function checkTypoSingle(token, knownCommands) {
49
- if (token.length < 4 || token.length > 14)
50
- return null;
51
- if (!/^[a-z]+$/i.test(token))
52
- return null;
53
- const lower = token.toLowerCase();
54
- // Defensively normalize knownCommands to lowercase too — Codex rc.16 review
55
- // flagged this for symmetry. Currently every Commander subcommand is lower
56
- // already, but a future addition that registers an uppercase name shouldn't
57
- // silently slip through the equality check.
58
- const knownLower = knownCommands.map((n) => n.toLowerCase());
59
- if (knownLower.includes(lower))
60
- return null;
61
- // Length-prefilter cheap reject before computing DP — same outcome, less work.
62
- const scored = knownLower
63
- .filter((n) => Math.abs(n.length - lower.length) <= 2)
64
- .map((n) => ({ n, d: levenshtein(lower, n) }))
65
- .filter((s) => s.d > 0 && s.d <= 2 && s.d < lower.length)
66
- // Deterministic tie-breaker (alphabetical) so ranking is stable across runs.
67
- .sort((a, b) => a.d - b.d || a.n.localeCompare(b.n))
68
- .slice(0, 3);
69
- return scored.length > 0 ? scored.map((s) => s.n) : null;
70
- }
71
- /**
72
- * Decide whether a bare-action input contains a typo of a known subcommand.
73
- * Returns suggestions if the FIRST token looks like a typo; null otherwise.
74
- *
75
- * Multi-token inputs (e.g. `setupp foo bar`) tokenize on whitespace and only
76
- * the first token is typo-checked — the rest are treated as positional args
77
- * the user meant to pass to the suggested command.
78
- *
79
- * Heuristics (intentionally narrow — false positives are worse than false negatives):
80
- * - First token is 4–14 chars, pure alphabetic, no hyphens (kebab-case is a likely real prompt)
81
- * - True Levenshtein distance ≤ 2 to a known command
82
- * - Distance must be < input length (avoid 2-char words matching anything)
83
- * - Returns up to 3 closest commands ranked by ascending distance
84
- */
85
- export function checkTypo(input, knownCommands) {
86
- const trimmed = input.trim();
87
- if (!trimmed)
88
- return null;
89
- const first = trimmed.split(/\s+/)[0];
90
- return checkTypoSingle(first, knownCommands);
91
- }
92
- /**
93
- * Detect inputs shaped like a typed-but-unknown subcommand invocation:
94
- * exactly two short alphabetic tokens, first token NOT a known command and NOT
95
- * a near-typo of one. Returns the unknown first token (for error formatting)
96
- * or null if input doesn't match the shape.
97
- *
98
- * Catches cases the typo guard misses — e.g. `daemon start`, `webui open`,
99
- * `nuke session` — where the first word isn't close enough to a real command
100
- * for Levenshtein suggestions but is clearly a command attempt rather than a
101
- * natural-language prompt.
102
- *
103
- * Conservative on purpose:
104
- * - Exactly 2 tokens (3+ tokens are usually prompts: "fix the login bug")
105
- * - Both tokens pure alphabetic (a slash/period in token 2 → file path → prompt)
106
- * - First token 4-14 chars (skip "go run", "h x"); second 2-14 chars
107
- * - First not a known command and not a typo (those branches handled elsewhere)
108
- */
109
- export function looksLikeUnknownCommand(input, knownCommands) {
110
- const trimmed = input.trim();
111
- if (!trimmed)
112
- return null;
113
- const tokens = trimmed.split(/\s+/);
114
- if (tokens.length !== 2)
115
- return null;
116
- const [first, second] = tokens;
117
- if (first.length < 4 || first.length > 14)
118
- return null;
119
- if (second.length < 2 || second.length > 14)
120
- return null;
121
- if (!/^[a-z]+$/i.test(first))
122
- return null;
123
- if (!/^[a-z]+$/i.test(second))
124
- return null;
125
- const lower = first.toLowerCase();
126
- const knownLower = knownCommands.map((n) => n.toLowerCase());
127
- if (knownLower.includes(lower))
128
- return null;
129
- if (checkTypoSingle(first, knownCommands))
130
- return null;
131
- return first;
132
- }
133
- //# sourceMappingURL=typo-guard.js.map
@@ -1,40 +0,0 @@
1
- /**
2
- * Centralized Version Utility
3
- * Single source of truth for version information
4
- * Reads from package.json to ensure consistency
5
- *
6
- * Clean Architecture: Infrastructure/Utils Layer
7
- */
8
- import { readFileSync, existsSync } from 'fs';
9
- import { join, dirname } from 'path';
10
- import { fileURLToPath } from 'url';
11
- let cachedVersion = null;
12
- /**
13
- * Get the current version from package.json
14
- * Caches the result for performance
15
- */
16
- export function getVersion() {
17
- if (cachedVersion) {
18
- return cachedVersion;
19
- }
20
- try {
21
- const __filename = fileURLToPath(import.meta.url);
22
- const __dirname = dirname(__filename);
23
- const agentDir = join(__dirname, '../..');
24
- const packageJsonPath = join(agentDir, 'package.json');
25
- if (existsSync(packageJsonPath)) {
26
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
27
- const version = packageJson.version || '0.3.0';
28
- cachedVersion = version;
29
- return version;
30
- }
31
- }
32
- catch {
33
- // Fallback if can't read package.json
34
- console.warn('Warning: Could not read version from package.json, using fallback');
35
- }
36
- // Fallback version (should match package.json)
37
- cachedVersion = '0.3.0';
38
- return cachedVersion;
39
- }
40
- //# sourceMappingURL=version.js.map