@hncode/hncode 0.4.2 → 0.5.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.
Files changed (60) hide show
  1. package/README.md +1 -15
  2. package/README_CN.md +1 -15
  3. package/bin/hncode +22 -9
  4. package/package.json +3 -2
  5. package/src/agent.js +145 -33
  6. package/src/bootstrap.js +56 -0
  7. package/src/btw-panel.js +224 -0
  8. package/src/cache.js +3 -2
  9. package/src/cell-buffer.js +173 -0
  10. package/src/ci.js +4 -1
  11. package/src/clipboard.js +202 -43
  12. package/src/colors.js +175 -30
  13. package/src/config.js +376 -34
  14. package/src/context-bar.js +237 -0
  15. package/src/doctor.js +320 -0
  16. package/src/experiments.js +122 -0
  17. package/src/file-history.js +112 -0
  18. package/src/file-tree.js +255 -0
  19. package/src/flush-tick.js +56 -0
  20. package/src/fork.js +44 -0
  21. package/src/fx.js +148 -0
  22. package/src/index.js +123 -11
  23. package/src/keybindings.js +229 -0
  24. package/src/llm.js +355 -34
  25. package/src/lsp.js +401 -0
  26. package/src/notebook.js +232 -0
  27. package/src/output-styles.js +183 -0
  28. package/src/patch.js +248 -0
  29. package/src/permissions.js +130 -1
  30. package/src/plan.js +3 -2
  31. package/src/plugin.js +611 -58
  32. package/src/prompt-presets.js +1 -1
  33. package/src/registry-browser.js +31 -13
  34. package/src/remote.js +20 -2
  35. package/src/rollout.js +151 -0
  36. package/src/row-cache.js +143 -0
  37. package/src/schedule.js +294 -0
  38. package/src/session-approvals.js +86 -0
  39. package/src/session.js +178 -8
  40. package/src/shell-safety.js +473 -0
  41. package/src/side-thread.js +240 -0
  42. package/src/skills.js +27 -0
  43. package/src/stream-buffer.js +350 -0
  44. package/src/subagent-control.js +160 -0
  45. package/src/teleport.js +260 -0
  46. package/src/term-caps.js +411 -0
  47. package/src/term.js +283 -8
  48. package/src/tools/agent-control.js +98 -0
  49. package/src/tools/agent-swarm.js +11 -2
  50. package/src/tools/agent.js +35 -1
  51. package/src/tools/diagnostics.js +175 -0
  52. package/src/tools/edit.js +25 -10
  53. package/src/tools/grep.js +62 -20
  54. package/src/tools/ignore.js +38 -14
  55. package/src/tools/index.js +10 -0
  56. package/src/tools/notebook.js +113 -0
  57. package/src/tools/read-media-file.js +10 -3
  58. package/src/tools/shell.js +34 -6
  59. package/src/tui.js +5219 -792
  60. package/src/web-public/app.js +7 -0
@@ -15,10 +15,26 @@
15
15
  // on which one was found.
16
16
 
17
17
  import cp from 'node:child_process';
18
+ import fs from 'node:fs';
18
19
 
19
20
  // The two PowerShell families, by the name they are invoked as.
20
21
  export const SHELL_CANDIDATES = ['pwsh', 'powershell'];
21
22
 
23
+ // Last-resort Windows fallback. A Windows box always has cmd.exe (%COMSPEC%),
24
+ // so when neither PowerShell exists the Bash tool must still run SOMETHING
25
+ // rather than failing outright. `cmd` is not a PowerShell, so the syntax
26
+ // guidance and the spawn args differ (see cmdSpawnArgs).
27
+ export function findCmdExe() {
28
+ const fromEnv = process.env.COMSPEC || process.env.ComSpec;
29
+ if (fromEnv) {
30
+ try { if (fs.existsSync(fromEnv)) return fromEnv; } catch { /* fall through to the PATH probe */ }
31
+ }
32
+ if (process.platform === 'win32') {
33
+ return whichSync('cmd') || whichSync('cmd.exe') || 'cmd.exe';
34
+ }
35
+ return null;
36
+ }
37
+
22
38
  /**
23
39
  * Resolve an executable name through PATH without spawning it.
24
40
  *
@@ -42,13 +58,15 @@ function whichSync(name) {
42
58
  }
43
59
 
44
60
  /**
45
- * Find a usable PowerShell: pwsh first, then the built-in powershell.
46
- * Returns { path, name, edition } or null when neither exists.
61
+ * Find a usable shell: pwsh, then the built-in powershell, then (on Windows)
62
+ * cmd.exe as a last resort. Returns { path, name, edition } or null.
47
63
  *
48
64
  * `edition` drives syntax guidance — see the header. It is inferred from the NAME
49
65
  * rather than by running the binary (a spawn per probe is not worth it, and
50
66
  * PowerShell 6, also named `pwsh`, is EOL and not a realistic target, so `pwsh`
51
- * safely implies 7+).
67
+ * safely implies 7+). `edition: 'cmd'` means neither PowerShell exists and the
68
+ * Bash tool is running through cmd.exe: it still works, but the command syntax is
69
+ * cmd's, not PowerShell's.
52
70
  */
53
71
  export function detectShell() {
54
72
  for (const name of SHELL_CANDIDATES) {
@@ -57,6 +75,9 @@ export function detectShell() {
57
75
  return { path, name, edition: name === 'pwsh' ? 'core' : 'desktop' };
58
76
  }
59
77
  }
78
+ // No PowerShell at all: on Windows, guarantee cmd.exe so the tool is not dead.
79
+ const cmd = findCmdExe();
80
+ if (cmd) return { path: cmd, name: 'cmd', edition: 'cmd' };
60
81
  return null;
61
82
  }
62
83
 
@@ -91,16 +112,23 @@ export function resetShellCache() {
91
112
  * on every single tool call, and `-NonInteractive` stops a command that prompts
92
113
  * from hanging the tool forever with no way to answer.
93
114
  *
94
- * Throws a message naming the fix when no PowerShell is installed, instead of
95
- * letting the caller hit a bare ENOENT.
115
+ * When neither PowerShell exists, `edition === 'cmd'` selects cmd.exe args instead
116
+ * (see detectShell). Throws a message naming the fix only when no shell at all
117
+ * was found, instead of letting the caller hit a bare ENOENT.
96
118
  */
97
119
  export function shellSpawnArgs(command) {
98
120
  const shell = getShell();
99
121
  if (!shell) {
100
122
  throw new Error(
101
- 'No PowerShell found on PATH (tried pwsh, powershell). '
123
+ 'No shell found (tried pwsh, powershell, and cmd.exe). '
102
124
  + 'Install PowerShell 7+ or add it to PATH, then retry.',
103
125
  );
104
126
  }
127
+ if (shell.edition === 'cmd') {
128
+ // cmd.exe: `/d` skips AutoRun commands, `/s` makes `/c` quoting behave
129
+ // predictably. The command is passed as ONE argv element, so Node does the
130
+ // quoting and we do not wrap it ourselves.
131
+ return { bin: shell.path, args: ['/d', '/s', '/c', command], shell };
132
+ }
105
133
  return { bin: shell.path, args: ['-NoProfile', '-NonInteractive', '-Command', command], shell };
106
134
  }