@huicai/cli 2026.711.0 → 2026.711.24617

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 (49) hide show
  1. package/LICENSE +43 -21
  2. package/package.json +4 -3
  3. package/src/advisor.js +1 -46
  4. package/src/agent.js +1 -518
  5. package/src/api.js +1 -359
  6. package/src/autofix.js +1 -90
  7. package/src/banner.js +1 -83
  8. package/src/checkpoints.js +1 -99
  9. package/src/clipboard.js +1 -34
  10. package/src/codeindex.js +1 -229
  11. package/src/colors.js +1 -47
  12. package/src/commands.js +1 -1294
  13. package/src/compaction.js +1 -144
  14. package/src/config.js +1 -86
  15. package/src/consult.js +1 -65
  16. package/src/context-report.js +1 -119
  17. package/src/context.js +1 -152
  18. package/src/cron.js +1 -64
  19. package/src/daemon.js +1 -62
  20. package/src/device.js +1 -88
  21. package/src/diffview.js +1 -115
  22. package/src/doctor.js +1 -72
  23. package/src/executors.js +1 -391
  24. package/src/forks.js +1 -59
  25. package/src/gitflow.js +1 -140
  26. package/src/hooks.js +1 -207
  27. package/src/ide.js +1 -53
  28. package/src/input.js +1 -182
  29. package/src/insights.js +1 -102
  30. package/src/kbsync.js +1 -141
  31. package/src/markdown.js +0 -0
  32. package/src/markup-filter.js +1 -85
  33. package/src/mcp.js +1 -207
  34. package/src/memory.js +1 -161
  35. package/src/permissions.js +1 -120
  36. package/src/render.js +1 -138
  37. package/src/repl.js +1 -271
  38. package/src/scan.js +1 -84
  39. package/src/schedule.js +1 -48
  40. package/src/session.js +1 -140
  41. package/src/settings.js +0 -0
  42. package/src/skills.js +1 -248
  43. package/src/subagent.js +1 -183
  44. package/src/todo.js +1 -75
  45. package/src/tools.js +1 -147
  46. package/src/transcript.js +1 -94
  47. package/src/tui/app.js +1 -666
  48. package/src/usage.js +1 -43
  49. package/src/watch.js +1 -74
package/src/usage.js CHANGED
@@ -1,43 +1 @@
1
- // Token 用量窗口告警判定(纯函数,零依赖)。从 /v1/usage 的快照推导「是否该提示 / 是否用尽」。
2
- // limit 约定:>0 上限;-1 无限;0 该档未开放。两窗(5小时/7天)取占比最高者。
3
-
4
- /**
5
- * @param {{allowed?:boolean, used5h?:number, limit5h?:number, used7d?:number, limit7d?:number, resetAtEpochMs?:number|null}|null} win
6
- * @returns {{level:'warn'|'exhausted', label:string, used:number, limit:number, pct:number, resetAtEpochMs?:number|null}|null}
7
- * null = 正常(<80% 或无可判定窗口)。warn ≥80%;exhausted ≥100% 或平台判定 allowed=false。
8
- */
9
- export function usageStatus(win) {
10
- if (!win) return null;
11
- let worst = null;
12
- for (const w of [
13
- { label: '5小时', used: win.used5h, limit: win.limit5h },
14
- { label: '7天', used: win.used7d, limit: win.limit7d },
15
- ]) {
16
- if (typeof w.limit !== 'number' || w.limit <= 0 || typeof w.used !== 'number') continue; // -1 无限 / 0 未开放
17
- const pct = w.used / w.limit;
18
- if (!worst || pct > worst.pct) worst = { ...w, pct };
19
- }
20
- if (win.allowed === false) {
21
- return {
22
- level: 'exhausted',
23
- label: worst?.label || '窗口',
24
- used: worst?.used,
25
- limit: worst?.limit,
26
- pct: worst ? worst.pct : 1,
27
- resetAtEpochMs: win.resetAtEpochMs,
28
- };
29
- }
30
- if (!worst) return null;
31
- if (worst.pct >= 1) return { level: 'exhausted', ...worst, resetAtEpochMs: win.resetAtEpochMs };
32
- if (worst.pct >= 0.8) return { level: 'warn', ...worst, resetAtEpochMs: win.resetAtEpochMs };
33
- return null;
34
- }
35
-
36
- /** 剩余时长(到 resetAtEpochMs)转「N小时M分后重置」;已过或缺省返回空串。 */
37
- export function fmtReset(ms) {
38
- const d = (ms || 0) - Date.now();
39
- if (d <= 0) return '';
40
- const h = Math.floor(d / 3600000);
41
- const m = Math.round((d % 3600000) / 60000);
42
- return h > 0 ? `${h}小时${m}分后重置` : `${m}分后重置`;
43
- }
1
+ export function usageStatus(e){if(!e)return null;let t=null;for(const l of[{label:"5小时",used:e.used5h,limit:e.limit5h},{label:"7天",used:e.used7d,limit:e.limit7d}]){if("number"!=typeof l.limit||l.limit<=0||"number"!=typeof l.used)continue;const e=l.used/l.limit;(!t||e>t.pct)&&(t={...l,pct:e})}return!1===e.allowed?{level:"exhausted",label:t?.label||"窗口",used:t?.used,limit:t?.limit,pct:t?t.pct:1,resetAtEpochMs:e.resetAtEpochMs}:t?t.pct>=1?{level:"exhausted",...t,resetAtEpochMs:e.resetAtEpochMs}:t.pct>=.8?{level:"warn",...t,resetAtEpochMs:e.resetAtEpochMs}:null:null}export function fmtReset(e){const t=(e||0)-Date.now();if(t<=0)return"";const l=Math.floor(t/36e5),s=Math.round(t%36e5/6e4);return l>0?`${l}小时${s}分后重置`:`${s}分后重置`}
package/src/watch.js CHANGED
@@ -1,74 +1 @@
1
- // huicai watch:监听工作目录文件变更,防抖后派生一个智能体任务(响应式自动化)。
2
- // 例:保存代码即自动跑测试/审查。默认任务可自定义(-p),文件名可用 glob 过滤(--glob "*.js")。
3
-
4
- import { spawn } from 'node:child_process';
5
- import fs from 'node:fs';
6
- import path from 'node:path';
7
- import { huicaiBin } from './forks.js';
8
-
9
- const IGNORE = new Set([
10
- '.git', 'node_modules', 'dist', 'build', 'target', 'out', '.next', '.nuxt',
11
- '.venv', 'venv', '__pycache__', '.idea', '.gradle', 'coverage', '.cache',
12
- ]);
13
-
14
- function patToRegex(pattern) {
15
- return new RegExp('^' + String(pattern).replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') + '$');
16
- }
17
-
18
- /**
19
- * 启动文件监听。
20
- * @param {{cwd:string, prompt:string, pattern?:string, debounceMs?:number, log?:(s:string)=>void, spawnImpl?:Function}} p
21
- */
22
- export async function runWatch({ cwd, prompt, pattern = null, debounceMs = 1500, log = (s) => process.stdout.write(s + '\n'), spawnImpl }) {
23
- const bin = huicaiBin();
24
- if (!bin && !spawnImpl) throw new Error('无法定位 huicai 可执行文件(watch 需在 CLI 环境运行)');
25
- const re = pattern ? patToRegex(pattern) : null;
26
- let timer = null;
27
- let running = false;
28
- const changed = new Set();
29
-
30
- const doSpawn = spawnImpl || ((fullPrompt) => {
31
- const child = spawn(process.execPath, [bin, '-p', fullPrompt, '--yolo', '--plain'], { cwd, stdio: 'inherit', env: process.env });
32
- return child;
33
- });
34
-
35
- const trigger = () => {
36
- if (running) return;
37
- running = true;
38
- const files = [...changed];
39
- changed.clear();
40
- log(`\n▶ 变更 ${files.length} 文件 → 触发智能体:${prompt}`);
41
- const fullPrompt = prompt + (files.length ? `\n(刚变更的文件:${files.slice(0, 20).join(', ')})` : '');
42
- const child = doSpawn(fullPrompt);
43
- const done = () => {
44
- running = false;
45
- if (changed.size) schedule();
46
- };
47
- if (child && child.on) child.on('close', done);
48
- else done();
49
- };
50
- const schedule = () => {
51
- if (timer) clearTimeout(timer);
52
- timer = setTimeout(trigger, debounceMs);
53
- };
54
-
55
- const watcher = fs.watch(cwd, { recursive: true }, (event, filename) => {
56
- if (!filename) return;
57
- const rel = String(filename);
58
- const top = rel.split(path.sep)[0];
59
- if (IGNORE.has(top)) return;
60
- if (re && !re.test(path.basename(rel))) return;
61
- changed.add(rel);
62
- schedule();
63
- });
64
-
65
- log(`huicai 监听已启动:${cwd}${pattern ? ` (${pattern})` : ''} → 变更即跑「${prompt}」· Ctrl-C 退出`);
66
- await new Promise((resolve) => {
67
- process.on('SIGINT', () => {
68
- watcher.close();
69
- if (timer) clearTimeout(timer);
70
- log('\n监听已停止。');
71
- resolve();
72
- });
73
- });
74
- }
1
+ import{spawn as e}from"node:child_process";import t from"node:fs";import n from"node:path";import{huicaiBin as o}from"./forks.js";const r=new Set([".git","node_modules","dist","build","target","out",".next",".nuxt",".venv","venv","__pycache__",".idea",".gradle","coverage",".cache"]);export async function runWatch({cwd:c,prompt:s,pattern:i=null,debounceMs:a=1500,log:l=e=>process.stdout.write(e+"\n"),spawnImpl:p}){const u=o();if(!u&&!p)throw new Error("无法定位 huicai 可执行文件(watch 需在 CLI 环境运行)");const d=i?function(e){return new RegExp("^"+String(e).replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")+"$")}(i):null;let m=null,h=!1;const w=new Set,g=p||(t=>e(process.execPath,[u,"-p",t,"--yolo","--plain"],{cwd:c,stdio:"inherit",env:process.env})),f=()=>{if(h)return;h=!0;const e=[...w];w.clear(),l(`\n▶ 变更 ${e.length} 文件 → 触发智能体:${s}`);const t=s+(e.length?`\n(刚变更的文件:${e.slice(0,20).join(", ")})`:""),n=g(t),o=()=>{h=!1,w.size&&$()};n&&n.on?n.on("close",o):o()},$=()=>{m&&clearTimeout(m),m=setTimeout(f,a)},v=t.watch(c,{recursive:!0},(e,t)=>{if(!t)return;const o=String(t),c=o.split(n.sep)[0];r.has(c)||d&&!d.test(n.basename(o))||(w.add(o),$())});l(`huicai 监听已启动:${c}${i?` (${i})`:""} → 变更即跑「${s}」· Ctrl-C 退出`),await new Promise(e=>{process.on("SIGINT",()=>{v.close(),m&&clearTimeout(m),l("\n监听已停止。"),e()})})}