@kolisachint/hoocode-agent 0.4.110 → 0.4.112
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/CHANGELOG.md +107 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +8 -3
- package/dist/cli/args.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +10 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +17 -2
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/plugins/marketplace.d.ts +36 -5
- package/dist/core/extensions/plugins/marketplace.d.ts.map +1 -1
- package/dist/core/extensions/plugins/marketplace.js +63 -17
- package/dist/core/extensions/plugins/marketplace.js.map +1 -1
- package/dist/core/format-duration.d.ts +7 -0
- package/dist/core/format-duration.d.ts.map +1 -0
- package/dist/core/format-duration.js +16 -0
- package/dist/core/format-duration.js.map +1 -0
- package/dist/core/keybindings.d.ts +10 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +10 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/lifeguard.d.ts +15 -0
- package/dist/core/lifeguard.d.ts.map +1 -1
- package/dist/core/lifeguard.js +48 -18
- package/dist/core/lifeguard.js.map +1 -1
- package/dist/core/scheduler.d.ts +8 -2
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +9 -3
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +1 -1
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +2 -2
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/subagent-inbox.d.ts +1 -1
- package/dist/core/subagent-inbox.d.ts.map +1 -1
- package/dist/core/subagent-inbox.js +2 -0
- package/dist/core/subagent-inbox.js.map +1 -1
- package/dist/core/subagent-pool-instance.d.ts.map +1 -1
- package/dist/core/subagent-pool-instance.js +18 -11
- package/dist/core/subagent-pool-instance.js.map +1 -1
- package/dist/core/subagent-pool.d.ts +25 -3
- package/dist/core/subagent-pool.d.ts.map +1 -1
- package/dist/core/subagent-pool.js +120 -25
- package/dist/core/subagent-pool.js.map +1 -1
- package/dist/core/subagent-result.d.ts.map +1 -1
- package/dist/core/subagent-result.js +6 -4
- package/dist/core/subagent-result.js.map +1 -1
- package/dist/core/task-store.d.ts +45 -3
- package/dist/core/task-store.d.ts.map +1 -1
- package/dist/core/task-store.js +51 -5
- package/dist/core/task-store.js.map +1 -1
- package/dist/core/team-view.d.ts.map +1 -1
- package/dist/core/team-view.js +2 -0
- package/dist/core/team-view.js.map +1 -1
- package/dist/core/token-budget.d.ts +13 -1
- package/dist/core/token-budget.d.ts.map +1 -1
- package/dist/core/token-budget.js +18 -1
- package/dist/core/token-budget.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +16 -4
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +211 -67
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/core/tools/todo.d.ts.map +1 -1
- package/dist/core/tools/todo.js +39 -7
- package/dist/core/tools/todo.js.map +1 -1
- package/dist/extensions/core/loop.d.ts.map +1 -1
- package/dist/extensions/core/loop.js +4 -1
- package/dist/extensions/core/loop.js.map +1 -1
- package/dist/extensions/core/marketplace.d.ts +5 -3
- package/dist/extensions/core/marketplace.d.ts.map +1 -1
- package/dist/extensions/core/marketplace.js +37 -15
- package/dist/extensions/core/marketplace.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +10 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +27 -1
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/task-panel.d.ts +22 -1
- package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-panel.js +302 -80
- package/dist/modes/interactive/components/task-panel.js.map +1 -1
- package/dist/modes/interactive/components/team-attach-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/team-attach-panel.js +15 -5
- package/dist/modes/interactive/components/team-attach-panel.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +8 -1
- package/dist/modes/interactive/theme/light.json +8 -1
- package/dist/modes/interactive/theme/theme-schema.json +28 -1
- package/dist/modes/interactive/theme/theme.d.ts +12 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +52 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/interactive/voice/voice-panel.d.ts.map +1 -1
- package/dist/modes/interactive/voice/voice-panel.js +5 -2
- package/dist/modes/interactive/voice/voice-panel.js.map +1 -1
- package/dist/modes/rpc/jsonl.d.ts +16 -1
- package/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/dist/modes/rpc/jsonl.js +53 -8
- package/dist/modes/rpc/jsonl.js.map +1 -1
- package/dist/utils/atomic-file.d.ts +13 -0
- package/dist/utils/atomic-file.d.ts.map +1 -0
- package/dist/utils/atomic-file.js +26 -0
- package/dist/utils/atomic-file.js.map +1 -0
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../../src/extensions/core/loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAO/B,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,MAAM,oBAAoB,GAAG,WAAW,CAAC;AACzC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAElC,qFAAqF;AACrF,SAAS,cAAc,CAAC,KAAa,EAAiB;IACrD,MAAM,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC,UAAU,CAAC;IAC1C,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,OAAO,CAAC,QAAQ,CAAC;IAC1C,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO;AAAR,CACxB;AAED,yEAAyE;AACzE,SAAS,iBAAiB,CAAC,IAAY,EAAyC;IAC/E,MAAM,CAAC,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAAA,CAC3D;AAED,SAAS,eAAe,CAAC,IAAY,EAAW;IAC/C,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAAA,CAC7C;AAED,kDAAkD;AAClD,SAAS,aAAa,CAAC,OAA6B,EAAU;IAC7D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,OAAO,OAAO;SACZ,MAAM,CAAC,CAAC,CAAC,EAAuC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAK,CAAuB,CAAC,IAAI,KAAK,MAAM,CAAC;SACnG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;SAClB,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACb;AAED,MAAM,UAAU,SAAS,CAAC,EAAgB,EAAQ;IACjD,IAAI,SAAoC,CAAC;IACzC,IAAI,IAAI,GAAiC,IAAI,CAAC;IAE9C,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,MAAyB,EAAE,GAAqB,EAAE,EAAE,CAAC;QAC5E,IAAI,SAAS;YAAE,OAAO;QACtB,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC;YACpB,IAAI,CAAC;gBACJ,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC;gBACR,OAAO,IAAI,CAAC;YACb,CAAC;QAAA,CACD,CAAC;QACF,SAAS,GAAG,IAAI,aAAa,CAAC;YAC7B,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,sBAAsB,CAAC;YAC5D,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;YACvE,MAAM;SACN,CAAC,CAAC;QACH,SAAS,CAAC,KAAK,EAAE,CAAC;IAAA,CAClB,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC;QAC/B,SAAS,EAAE,IAAI,EAAE,CAAC;QAClB,IAAI,GAAG,IAAI,CAAC;IAAA,CACZ,CAAC,CAAC;IAEH,kFAAkF;IAClF,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,IAAI,GAAG,CAAC,kBAAkB,EAAE;YAAE,OAAO,CAAC,6BAA2B;QACjE,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;QAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,IAAI,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACzC,IAAI,GAAG,IAAI,CAAC;YACZ,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,2BAA2B,EAAE,MAAM,CAAC,CAAC;YACnD,OAAO;QACR,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;YACzB,IAAI,GAAG,IAAI,CAAC;YACZ,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,6CAA6C,EAAE,SAAS,CAAC,CAAC;YACxE,OAAO;QACR,CAAC;QACD,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QACpB,EAAE,CAAC,eAAe,CAAC,gDAAgD,oBAAoB,uBAAuB,EAAE;YAC/G,SAAS,EAAE,UAAU;SACrB,CAAC,CAAC;IAAA,CACH,CAAC,CAAC;IAEH,qKAA6E;IAC7E,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAExG,EAAE,CAAC,YAAY,CACd,UAAU,CAAC;QACV,IAAI,EAAE,YAAY;QAClB,KAAK,EAAE,eAAe;QACtB,WAAW,EACV,qKAAqK;QACtK,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;YACvB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC;YAC3E,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;YAC3E,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC,CAAC;SACjG,CAAC;QACF,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE;YAC1B,IAAI,CAAC,SAAS;gBAAE,OAAO,QAAQ,CAAC,sBAAsB,CAAC,CAAC;YACxD,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,OAAO,QAAQ,CAAC,iBAAiB,MAAM,CAAC,IAAI,oBAAoB,CAAC,CAAC;YACrG,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC;gBAC7B,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,IAAI;aACnC,CAAC,CAAC;YACH,OAAO,QAAQ,CAAC,aAAa,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAAA,CACnG;KACD,CAAC,CACF,CAAC;IAEF,EAAE,CAAC,YAAY,CACd,UAAU,CAAC;QACV,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EAAE,yDAAyD;QACtE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,KAAK,CAAC,OAAO,GAAG;YACf,MAAM,KAAK,GAAG,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,QAAQ,CAAC,qBAAqB,CAAC,CAAC;YAC/D,OAAO,QAAQ,CACd,KAAK;iBACH,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;iBACpG,IAAI,CAAC,IAAI,CAAC,CACZ,CAAC;QAAA,CACF;KACD,CAAC,CACF,CAAC;IAEF,EAAE,CAAC,YAAY,CACd,UAAU,CAAC;QACV,IAAI,EAAE,YAAY;QAClB,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EAAE,gCAAgC;QAC7C,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC,EAAE,CAAC;QACjG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE;YAC1B,MAAM,OAAO,GAAG,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC;YACtD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;QAAA,CAC7E;KACD,CAAC,CACF,CAAC;IAEF,mMAA6E;IAC7E,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE;QAC1B,WAAW,EACV,wNAAwN;QACzN,sBAAsB,EAAE,CAAC,MAAc,EAAE,EAAE,CAC1C,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;aACxC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;aACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACvC,OAAO,EAAE,KAAK,EAAE,IAAY,EAAE,GAA4B,EAAiB,EAAE,CAAC;YAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,SAAS,EAAE,CAAC;gBAChB,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,0BAA0B,EAAE,SAAS,CAAC,CAAC;gBACrD,OAAO;YACR,CAAC;YAED,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;gBACpC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;gBAC/B,GAAG,CAAC,EAAE,CAAC,MAAM,CACZ,KAAK,CAAC,MAAM,KAAK,CAAC;oBACjB,CAAC,CAAC,IAAI;wBACL,CAAC,CAAC,2BAA2B,IAAI,CAAC,SAAS,eAAe;wBAC1D,CAAC,CAAC,qBAAqB;oBACxB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,OAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EACjG,MAAM,CACN,CAAC;gBACF,OAAO;YACR,CAAC;YAED,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;gBACxB,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,KAAK,IAAI,CAAC;gBACzD,SAAS,CAAC,KAAK,EAAE,CAAC;gBAClB,IAAI,GAAG,IAAI,CAAC;gBACZ,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;gBAC3F,OAAO;YACR,CAAC;YAED,IAAI,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClC,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;gBACjD,IAAI,CAAC,EAAE,EAAE,CAAC;oBACT,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,0BAA0B,EAAE,SAAS,CAAC,CAAC;oBACrD,OAAO;gBACR,CAAC;gBACD,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;gBAClF,OAAO;YACR,CAAC;YAED,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChC,IAAI,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC/C,IAAI,QAAQ,GAAG,sBAAsB,CAAC;gBACtC,MAAM,IAAI,GAAG,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACvD,IAAI,IAAI,EAAE,CAAC;oBACV,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC3B,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBACvB,CAAC;gBACD,IAAI,CAAC,IAAI,EAAE,CAAC;oBACX,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,0CAA0C,EAAE,SAAS,CAAC,CAAC;oBACrE,OAAO;gBACR,CAAC;gBACD,IAAI,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;gBAC/B,EAAE,CAAC,eAAe,CACjB,GAAG,IAAI,wFAAwF,oBAAoB,IAAI,EACvH,EAAE,SAAS,EAAE,UAAU,EAAE,CACzB,CAAC;gBACF,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,gCAAgC,QAAQ,gCAAgC,EAAE,MAAM,CAAC,CAAC;gBAChG,OAAO;YACR,CAAC;YAED,wEAAwE;YACxE,IAAI,SAAS,GAAG,IAAI,CAAC;YACrB,IAAI,IAAI,GAAG,OAAO,CAAC;YACnB,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7B,SAAS,GAAG,KAAK,CAAC;gBAClB,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACzC,CAAC;YAED,IAAI,IAAI,GAAkB,IAAI,CAAC;YAC/B,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,MAAM,EAAE,CAAC;gBACZ,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;gBACnB,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACP,MAAM,CAAC,KAAK,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAChD,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;gBAC7B,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;YACrC,CAAC;YAED,IAAI,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,+DAA+D,EAAE,SAAS,CAAC,CAAC;gBAC1F,OAAO;YACR,CAAC;YACD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACb,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,8DAA4D,EAAE,SAAS,CAAC,CAAC;gBACvF,OAAO;YACR,CAAC;YAED,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,EAAE,CAAC,MAAM,CACZ,aAAa,IAAI,CAAC,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,SAAO,MAAM,8CAA4C,IAAI,CAAC,EAAE,GAAG,EACtI,MAAM,CACN,CAAC;QAAA,CACF;KACD,CAAC,CAAC;AAAA,CACH","sourcesContent":["/**\n * /loop — cron scheduler, Cron* tools, and autonomous continuation.\n *\n * `/loop` schedules prompts via cron and drives autonomous continuation. The same\n * scheduler backs the agent-callable CronCreate/CronList/CronDelete tools.\n *\n * /loop \"<cron>\" <prompt> schedule recurring (5-field cron, local time)\n * /loop <5m|2h|1d> <prompt> schedule recurring at a simple interval\n * /loop once \"<cron>\" <prompt> schedule a one-shot\n * /loop list | /loop delete <id> | /loop stop\n * /loop auto [--max-turns N] <task> keep iterating until the task says LOOP_DONE\n */\n\nimport { join } from \"node:path\";\nimport { Type } from \"typebox\";\nimport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tSessionStartEvent,\n} from \"../../core/extensions/types.js\";\nimport { defineTool } from \"../../core/extensions/types.js\";\nimport { TaskScheduler } from \"../../core/scheduler.js\";\n\nconst AUTO_LOOP_DONE_TOKEN = \"LOOP_DONE\";\nconst DEFAULT_AUTO_MAX_TURNS = 10;\n\n/** Convert a simple interval token (\"5m\", \"2h\", \"1d\") to a 5-field cron, or null. */\nfunction intervalToCron(token: string): string | null {\n\tconst m = /^(\\d+)(m|h|d)$/.exec(token.trim());\n\tif (!m) return null;\n\tconst n = Number(m[1]);\n\tif (n < 1) return null;\n\tif (m[2] === \"m\") return `*/${n} * * * *`;\n\tif (m[2] === \"h\") return `0 */${n} * * *`;\n\treturn `0 0 */${n} * *`; // days\n}\n\n/** Pull a quoted cron expression off the front of an argument string. */\nfunction extractQuotedCron(args: string): { cron: string; rest: string } | null {\n\tconst m = /^\"([^\"]+)\"\\s*(.*)$/.exec(args.trim());\n\treturn m ? { cron: m[1].trim(), rest: m[2].trim() } : null;\n}\n\nfunction isFiveFieldCron(expr: string): boolean {\n\treturn expr.trim().split(/\\s+/).length === 5;\n}\n\n/** Flatten an assistant message's text blocks. */\nfunction assistantText(message: { content: unknown }): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.filter((b): b is { type: \"text\"; text: string } => !!b && (b as { type?: string }).type === \"text\")\n\t\t.map((b) => b.text)\n\t\t.join(\"\\n\");\n}\n\nexport function setupLoop(pi: ExtensionAPI): void {\n\tlet scheduler: TaskScheduler | undefined;\n\tlet auto: { remaining: number } | null = null;\n\n\tpi.on(\"session_start\", (_event: SessionStartEvent, ctx: ExtensionContext) => {\n\t\tif (scheduler) return;\n\t\tconst isIdle = () => {\n\t\t\ttry {\n\t\t\t\treturn ctx.isIdle();\n\t\t\t} catch {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\tscheduler = new TaskScheduler({\n\t\t\tstorePath: join(ctx.cwd, \".hoocode\", \"scheduled_tasks.json\"),\n\t\t\tfire: (prompt) => pi.sendUserMessage(prompt, { deliverAs: \"followUp\" }),\n\t\t\tisIdle,\n\t\t});\n\t\tscheduler.start();\n\t});\n\n\tpi.on(\"session_shutdown\", () => {\n\t\tscheduler?.stop();\n\t\tauto = null;\n\t});\n\n\t// Autonomous continuation: re-prompt on each agent_end until LOOP_DONE or budget.\n\tpi.on(\"agent_end\", (event, ctx) => {\n\t\tif (!auto) return;\n\t\tif (ctx.hasPendingMessages()) return; // user is steering — yield\n\t\tconst last = [...event.messages].reverse().find((m) => m.role === \"assistant\");\n\t\tconst text = last ? assistantText(last) : \"\";\n\t\tif (text.includes(AUTO_LOOP_DONE_TOKEN)) {\n\t\t\tauto = null;\n\t\t\tctx.ui.notify(\"Autonomous loop complete.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t\tif (auto.remaining <= 0) {\n\t\t\tauto = null;\n\t\t\tctx.ui.notify(\"Autonomous loop stopped: max turns reached.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tauto.remaining -= 1;\n\t\tpi.sendUserMessage(`Continue working toward the goal. Reply with ${AUTO_LOOP_DONE_TOKEN} when fully complete.`, {\n\t\t\tdeliverAs: \"followUp\",\n\t\t});\n\t});\n\n\t// ── Cron* tools (agent-callable) ──────────────────────────────────────────\n\tconst toolText = (s: string) => ({ content: [{ type: \"text\" as const, text: s }], details: undefined });\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronCreate\",\n\t\t\tlabel: \"Schedule Task\",\n\t\t\tdescription:\n\t\t\t\t\"Schedule a prompt to be re-submitted on a cron schedule (5-field, local time: minute hour day-of-month month day-of-week). recurring=false fires once then deletes.\",\n\t\t\tparameters: Type.Object({\n\t\t\t\tcron: Type.String({ description: \"5-field cron expression in local time\" }),\n\t\t\t\tprompt: Type.String({ description: \"Prompt to enqueue at each fire time\" }),\n\t\t\t\trecurring: Type.Optional(Type.Boolean({ description: \"Fire repeatedly (default true) or once\" })),\n\t\t\t}),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tif (!scheduler) return toolText(\"Scheduler not ready.\");\n\t\t\t\tif (!isFiveFieldCron(params.cron)) return toolText(`Invalid cron \"${params.cron}\" (need 5 fields).`);\n\t\t\t\tconst task = scheduler.create({\n\t\t\t\t\tcron: params.cron,\n\t\t\t\t\tprompt: params.prompt,\n\t\t\t\t\trecurring: params.recurring ?? true,\n\t\t\t\t});\n\t\t\t\treturn toolText(`Scheduled ${task.id}: \"${task.cron}\" (${task.recurring ? \"recurring\" : \"once\"})`);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronList\",\n\t\t\tlabel: \"List Scheduled Tasks\",\n\t\t\tdescription: \"List all scheduled tasks (id, cron, recurring, prompt).\",\n\t\t\tparameters: Type.Object({}),\n\t\t\tasync execute() {\n\t\t\t\tconst tasks = scheduler?.list() ?? [];\n\t\t\t\tif (tasks.length === 0) return toolText(\"No scheduled tasks.\");\n\t\t\t\treturn toolText(\n\t\t\t\t\ttasks\n\t\t\t\t\t\t.map((t) => `${t.id} ${t.cron} ${t.recurring ? \"recurring\" : \"once\"} ${JSON.stringify(t.prompt)}`)\n\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronDelete\",\n\t\t\tlabel: \"Delete Scheduled Task\",\n\t\t\tdescription: \"Delete a scheduled task by id.\",\n\t\t\tparameters: Type.Object({ id: Type.String({ description: \"Task id from CronCreate/CronList\" }) }),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tconst removed = scheduler?.delete(params.id) ?? false;\n\t\t\t\treturn toolText(removed ? `Deleted ${params.id}.` : `No task ${params.id}.`);\n\t\t\t},\n\t\t}),\n\t);\n\n\t// ── /loop command ─────────────────────────────────────────────────────────\n\tpi.registerCommand(\"loop\", {\n\t\tdescription:\n\t\t\t'Schedule prompts via cron or run an autonomous loop. /loop \"<cron>\" <prompt> | /loop <5m|2h> <prompt> | /loop once \"<cron>\" <prompt> | /loop list | /loop delete <id> | /loop stop | /loop auto [--max-turns N] <task>',\n\t\tgetArgumentCompletions: (prefix: string) =>\n\t\t\t[\"list\", \"delete\", \"stop\", \"once\", \"auto\"]\n\t\t\t\t.filter((s) => s.startsWith(prefix))\n\t\t\t\t.map((s) => ({ value: s, label: s })),\n\t\thandler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tif (!scheduler) {\n\t\t\t\tctx.ui.notify(\"Scheduler not ready yet.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!trimmed || trimmed === \"list\") {\n\t\t\t\tconst tasks = scheduler.list();\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\ttasks.length === 0\n\t\t\t\t\t\t? auto\n\t\t\t\t\t\t\t? `Autonomous loop active (${auto.remaining} turns left).`\n\t\t\t\t\t\t\t: \"No scheduled tasks.\"\n\t\t\t\t\t\t: tasks.map((t) => `${t.id}: ${t.cron} ${t.recurring ? \"\" : \"(once) \"}— ${t.prompt}`).join(\"\\n\"),\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed === \"stop\") {\n\t\t\t\tconst had = scheduler.list().length > 0 || auto !== null;\n\t\t\t\tscheduler.clear();\n\t\t\t\tauto = null;\n\t\t\t\tctx.ui.notify(had ? \"Stopped all loops and scheduled tasks.\" : \"Nothing to stop.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"delete\")) {\n\t\t\t\tconst id = trimmed.slice(\"delete\".length).trim();\n\t\t\t\tif (!id) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop delete <id>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tctx.ui.notify(scheduler.delete(id) ? `Deleted ${id}.` : `No task ${id}.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"auto\")) {\n\t\t\t\tlet rest = trimmed.slice(\"auto\".length).trim();\n\t\t\t\tlet maxTurns = DEFAULT_AUTO_MAX_TURNS;\n\t\t\t\tconst flag = /^--max-turns\\s+(\\d+)\\s*(.*)$/.exec(rest);\n\t\t\t\tif (flag) {\n\t\t\t\t\tmaxTurns = Number(flag[1]);\n\t\t\t\t\trest = flag[2].trim();\n\t\t\t\t}\n\t\t\t\tif (!rest) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop auto [--max-turns N] <task>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tauto = { remaining: maxTurns };\n\t\t\t\tpi.sendUserMessage(\n\t\t\t\t\t`${rest}\\n\\n(Autonomous loop: keep working until the task is fully complete, then reply with ${AUTO_LOOP_DONE_TOKEN}.)`,\n\t\t\t\t\t{ deliverAs: \"followUp\" },\n\t\t\t\t);\n\t\t\t\tctx.ui.notify(`Autonomous loop started (max ${maxTurns} turns). Stop with /loop stop.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Scheduling: one-shot or recurring, via quoted cron or interval token.\n\t\t\tlet recurring = true;\n\t\t\tlet body = trimmed;\n\t\t\tif (body.startsWith(\"once\")) {\n\t\t\t\trecurring = false;\n\t\t\t\tbody = body.slice(\"once\".length).trim();\n\t\t\t}\n\n\t\t\tlet cron: string | null = null;\n\t\t\tlet prompt = \"\";\n\t\t\tconst quoted = extractQuotedCron(body);\n\t\t\tif (quoted) {\n\t\t\t\tcron = quoted.cron;\n\t\t\t\tprompt = quoted.rest;\n\t\t\t} else {\n\t\t\t\tconst [first, ...restWords] = body.split(/\\s+/);\n\t\t\t\tcron = intervalToCron(first);\n\t\t\t\tprompt = restWords.join(\" \").trim();\n\t\t\t}\n\n\t\t\tif (!cron || !isFiveFieldCron(cron)) {\n\t\t\t\tctx.ui.notify('Usage: /loop \"<cron>\" <prompt> or /loop <5m|2h|1d> <prompt>', \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!prompt) {\n\t\t\t\tctx.ui.notify(\"Nothing to schedule — provide a prompt after the schedule.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst task = scheduler.create({ cron, prompt, recurring });\n\t\t\tctx.ui.notify(\n\t\t\t\t`Scheduled ${task.id}: \"${cron}\" ${recurring ? \"recurring\" : \"once\"} — \"${prompt}\". Manage with /loop list • /loop delete ${task.id}.`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../../src/extensions/core/loop.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /loop — cron scheduler, Cron* tools, and autonomous continuation.\n *\n * `/loop` schedules prompts via cron and drives autonomous continuation. The same\n * scheduler backs the agent-callable CronCreate/CronList/CronDelete tools.\n *\n * /loop \"<cron>\" <prompt> schedule recurring (5-field cron, local time)\n * /loop <5m|2h|1d> <prompt> schedule recurring at a simple interval\n * /loop once \"<cron>\" <prompt> schedule a one-shot\n * /loop list | /loop delete <id> | /loop stop\n * /loop auto [--max-turns N] <task> keep iterating until the task says LOOP_DONE\n */\n\nimport { join } from \"node:path\";\nimport { Type } from \"typebox\";\nimport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tSessionStartEvent,\n} from \"../../core/extensions/types.js\";\nimport { defineTool } from \"../../core/extensions/types.js\";\nimport { TaskScheduler } from \"../../core/scheduler.js\";\n\nconst AUTO_LOOP_DONE_TOKEN = \"LOOP_DONE\";\nconst DEFAULT_AUTO_MAX_TURNS = 10;\n\n/** Convert a simple interval token (\"5m\", \"2h\", \"1d\") to a 5-field cron, or null. */\nfunction intervalToCron(token: string): string | null {\n\tconst m = /^(\\d+)(m|h|d)$/.exec(token.trim());\n\tif (!m) return null;\n\tconst n = Number(m[1]);\n\tif (n < 1) return null;\n\tif (m[2] === \"m\") return `*/${n} * * * *`;\n\tif (m[2] === \"h\") return `0 */${n} * * *`;\n\treturn `0 0 */${n} * *`; // days\n}\n\n/** Pull a quoted cron expression off the front of an argument string. */\nfunction extractQuotedCron(args: string): { cron: string; rest: string } | null {\n\tconst m = /^\"([^\"]+)\"\\s*(.*)$/.exec(args.trim());\n\treturn m ? { cron: m[1].trim(), rest: m[2].trim() } : null;\n}\n\nfunction isFiveFieldCron(expr: string): boolean {\n\treturn expr.trim().split(/\\s+/).length === 5;\n}\n\n/** Flatten an assistant message's text blocks. */\nfunction assistantText(message: { content: unknown }): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.filter((b): b is { type: \"text\"; text: string } => !!b && (b as { type?: string }).type === \"text\")\n\t\t.map((b) => b.text)\n\t\t.join(\"\\n\");\n}\n\nexport function setupLoop(pi: ExtensionAPI): void {\n\tlet scheduler: TaskScheduler | undefined;\n\tlet auto: { remaining: number } | null = null;\n\n\tpi.on(\"session_start\", (_event: SessionStartEvent, ctx: ExtensionContext) => {\n\t\tif (scheduler) return;\n\t\tconst isIdle = () => {\n\t\t\ttry {\n\t\t\t\treturn ctx.isIdle();\n\t\t\t} catch {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\tscheduler = new TaskScheduler({\n\t\t\t// `.agents/` is the primary, cross-vendor home; the legacy `.hoocode/`\n\t\t\t// store is read once and migrates forward on the next persist.\n\t\t\tstorePath: join(ctx.cwd, \".agents\", \"scheduled_tasks.json\"),\n\t\t\tlegacyStorePath: join(ctx.cwd, \".hoocode\", \"scheduled_tasks.json\"),\n\t\t\tfire: (prompt) => pi.sendUserMessage(prompt, { deliverAs: \"followUp\" }),\n\t\t\tisIdle,\n\t\t});\n\t\tscheduler.start();\n\t});\n\n\tpi.on(\"session_shutdown\", () => {\n\t\tscheduler?.stop();\n\t\tauto = null;\n\t});\n\n\t// Autonomous continuation: re-prompt on each agent_end until LOOP_DONE or budget.\n\tpi.on(\"agent_end\", (event, ctx) => {\n\t\tif (!auto) return;\n\t\tif (ctx.hasPendingMessages()) return; // user is steering — yield\n\t\tconst last = [...event.messages].reverse().find((m) => m.role === \"assistant\");\n\t\tconst text = last ? assistantText(last) : \"\";\n\t\tif (text.includes(AUTO_LOOP_DONE_TOKEN)) {\n\t\t\tauto = null;\n\t\t\tctx.ui.notify(\"Autonomous loop complete.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t\tif (auto.remaining <= 0) {\n\t\t\tauto = null;\n\t\t\tctx.ui.notify(\"Autonomous loop stopped: max turns reached.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tauto.remaining -= 1;\n\t\tpi.sendUserMessage(`Continue working toward the goal. Reply with ${AUTO_LOOP_DONE_TOKEN} when fully complete.`, {\n\t\t\tdeliverAs: \"followUp\",\n\t\t});\n\t});\n\n\t// ── Cron* tools (agent-callable) ──────────────────────────────────────────\n\tconst toolText = (s: string) => ({ content: [{ type: \"text\" as const, text: s }], details: undefined });\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronCreate\",\n\t\t\tlabel: \"Schedule Task\",\n\t\t\tdescription:\n\t\t\t\t\"Schedule a prompt to be re-submitted on a cron schedule (5-field, local time: minute hour day-of-month month day-of-week). recurring=false fires once then deletes.\",\n\t\t\tparameters: Type.Object({\n\t\t\t\tcron: Type.String({ description: \"5-field cron expression in local time\" }),\n\t\t\t\tprompt: Type.String({ description: \"Prompt to enqueue at each fire time\" }),\n\t\t\t\trecurring: Type.Optional(Type.Boolean({ description: \"Fire repeatedly (default true) or once\" })),\n\t\t\t}),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tif (!scheduler) return toolText(\"Scheduler not ready.\");\n\t\t\t\tif (!isFiveFieldCron(params.cron)) return toolText(`Invalid cron \"${params.cron}\" (need 5 fields).`);\n\t\t\t\tconst task = scheduler.create({\n\t\t\t\t\tcron: params.cron,\n\t\t\t\t\tprompt: params.prompt,\n\t\t\t\t\trecurring: params.recurring ?? true,\n\t\t\t\t});\n\t\t\t\treturn toolText(`Scheduled ${task.id}: \"${task.cron}\" (${task.recurring ? \"recurring\" : \"once\"})`);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronList\",\n\t\t\tlabel: \"List Scheduled Tasks\",\n\t\t\tdescription: \"List all scheduled tasks (id, cron, recurring, prompt).\",\n\t\t\tparameters: Type.Object({}),\n\t\t\tasync execute() {\n\t\t\t\tconst tasks = scheduler?.list() ?? [];\n\t\t\t\tif (tasks.length === 0) return toolText(\"No scheduled tasks.\");\n\t\t\t\treturn toolText(\n\t\t\t\t\ttasks\n\t\t\t\t\t\t.map((t) => `${t.id} ${t.cron} ${t.recurring ? \"recurring\" : \"once\"} ${JSON.stringify(t.prompt)}`)\n\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronDelete\",\n\t\t\tlabel: \"Delete Scheduled Task\",\n\t\t\tdescription: \"Delete a scheduled task by id.\",\n\t\t\tparameters: Type.Object({ id: Type.String({ description: \"Task id from CronCreate/CronList\" }) }),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tconst removed = scheduler?.delete(params.id) ?? false;\n\t\t\t\treturn toolText(removed ? `Deleted ${params.id}.` : `No task ${params.id}.`);\n\t\t\t},\n\t\t}),\n\t);\n\n\t// ── /loop command ─────────────────────────────────────────────────────────\n\tpi.registerCommand(\"loop\", {\n\t\tdescription:\n\t\t\t'Schedule prompts via cron or run an autonomous loop. /loop \"<cron>\" <prompt> | /loop <5m|2h> <prompt> | /loop once \"<cron>\" <prompt> | /loop list | /loop delete <id> | /loop stop | /loop auto [--max-turns N] <task>',\n\t\tgetArgumentCompletions: (prefix: string) =>\n\t\t\t[\"list\", \"delete\", \"stop\", \"once\", \"auto\"]\n\t\t\t\t.filter((s) => s.startsWith(prefix))\n\t\t\t\t.map((s) => ({ value: s, label: s })),\n\t\thandler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tif (!scheduler) {\n\t\t\t\tctx.ui.notify(\"Scheduler not ready yet.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!trimmed || trimmed === \"list\") {\n\t\t\t\tconst tasks = scheduler.list();\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\ttasks.length === 0\n\t\t\t\t\t\t? auto\n\t\t\t\t\t\t\t? `Autonomous loop active (${auto.remaining} turns left).`\n\t\t\t\t\t\t\t: \"No scheduled tasks.\"\n\t\t\t\t\t\t: tasks.map((t) => `${t.id}: ${t.cron} ${t.recurring ? \"\" : \"(once) \"}— ${t.prompt}`).join(\"\\n\"),\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed === \"stop\") {\n\t\t\t\tconst had = scheduler.list().length > 0 || auto !== null;\n\t\t\t\tscheduler.clear();\n\t\t\t\tauto = null;\n\t\t\t\tctx.ui.notify(had ? \"Stopped all loops and scheduled tasks.\" : \"Nothing to stop.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"delete\")) {\n\t\t\t\tconst id = trimmed.slice(\"delete\".length).trim();\n\t\t\t\tif (!id) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop delete <id>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tctx.ui.notify(scheduler.delete(id) ? `Deleted ${id}.` : `No task ${id}.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"auto\")) {\n\t\t\t\tlet rest = trimmed.slice(\"auto\".length).trim();\n\t\t\t\tlet maxTurns = DEFAULT_AUTO_MAX_TURNS;\n\t\t\t\tconst flag = /^--max-turns\\s+(\\d+)\\s*(.*)$/.exec(rest);\n\t\t\t\tif (flag) {\n\t\t\t\t\tmaxTurns = Number(flag[1]);\n\t\t\t\t\trest = flag[2].trim();\n\t\t\t\t}\n\t\t\t\tif (!rest) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop auto [--max-turns N] <task>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tauto = { remaining: maxTurns };\n\t\t\t\tpi.sendUserMessage(\n\t\t\t\t\t`${rest}\\n\\n(Autonomous loop: keep working until the task is fully complete, then reply with ${AUTO_LOOP_DONE_TOKEN}.)`,\n\t\t\t\t\t{ deliverAs: \"followUp\" },\n\t\t\t\t);\n\t\t\t\tctx.ui.notify(`Autonomous loop started (max ${maxTurns} turns). Stop with /loop stop.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Scheduling: one-shot or recurring, via quoted cron or interval token.\n\t\t\tlet recurring = true;\n\t\t\tlet body = trimmed;\n\t\t\tif (body.startsWith(\"once\")) {\n\t\t\t\trecurring = false;\n\t\t\t\tbody = body.slice(\"once\".length).trim();\n\t\t\t}\n\n\t\t\tlet cron: string | null = null;\n\t\t\tlet prompt = \"\";\n\t\t\tconst quoted = extractQuotedCron(body);\n\t\t\tif (quoted) {\n\t\t\t\tcron = quoted.cron;\n\t\t\t\tprompt = quoted.rest;\n\t\t\t} else {\n\t\t\t\tconst [first, ...restWords] = body.split(/\\s+/);\n\t\t\t\tcron = intervalToCron(first);\n\t\t\t\tprompt = restWords.join(\" \").trim();\n\t\t\t}\n\n\t\t\tif (!cron || !isFiveFieldCron(cron)) {\n\t\t\t\tctx.ui.notify('Usage: /loop \"<cron>\" <prompt> or /loop <5m|2h|1d> <prompt>', \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!prompt) {\n\t\t\t\tctx.ui.notify(\"Nothing to schedule — provide a prompt after the schedule.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst task = scheduler.create({ cron, prompt, recurring });\n\t\t\tctx.ui.notify(\n\t\t\t\t`Scheduled ${task.id}: \"${cron}\" ${recurring ? \"recurring\" : \"once\"} — \"${prompt}\". Manage with /loop list • /loop delete ${task.id}.`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* /plugin — marketplace add/list + plugin install/list/remove.
|
|
3
3
|
*
|
|
4
4
|
* `/plugin` installs plugins from marketplaces (a git repo or local dir with a
|
|
5
|
-
*
|
|
6
|
-
* index). Installed plugins are placed in
|
|
7
|
-
*
|
|
5
|
+
* native `.agents-plugin/marketplace.json`, Claude `.claude-plugin/marketplace.json`,
|
|
6
|
+
* or Copilot-style `.github/marketplace.json` index). Installed plugins are placed in
|
|
7
|
+
* `.agents/plugins/<name>` (the primary, cross-vendor home) and loaded by the plugin
|
|
8
|
+
* loader after a reload. Plugins hand-placed under `.hoocode/plugins/` are still
|
|
9
|
+
* discovered and can be removed, but new installs go to `.agents/`.
|
|
8
10
|
*
|
|
9
11
|
* /plugin marketplace add <git-url|path>
|
|
10
12
|
* /plugin marketplace list
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marketplace.d.ts","sourceRoot":"","sources":["../../../src/extensions/core/marketplace.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"marketplace.d.ts","sourceRoot":"","sources":["../../../src/extensions/core/marketplace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAUH,OAAO,KAAK,EAAE,YAAY,EAA2B,MAAM,gCAAgC,CAAC;AAU5F,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAqLvD","sourcesContent":["/**\n * /plugin — marketplace add/list + plugin install/list/remove.\n *\n * `/plugin` installs plugins from marketplaces (a git repo or local dir with a\n * native `.agents-plugin/marketplace.json`, Claude `.claude-plugin/marketplace.json`,\n * or Copilot-style `.github/marketplace.json` index). Installed plugins are placed in\n * `.agents/plugins/<name>` (the primary, cross-vendor home) and loaded by the plugin\n * loader after a reload. Plugins hand-placed under `.hoocode/plugins/` are still\n * discovered and can be removed, but new installs go to `.agents/`.\n *\n * /plugin marketplace add <git-url|path>\n * /plugin marketplace list\n * /plugin list list available plugins across marketplaces\n * /plugin install <name>\n * /plugin remove <name>\n */\n\nimport { cpSync, existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tparseMarketplaceDir,\n\treadMarketplaceStore,\n\tresolvePluginSource,\n\twriteMarketplaceStore,\n} from \"../../core/extensions/plugins/marketplace.js\";\nimport type { ExtensionAPI, ExtensionCommandContext } from \"../../core/extensions/types.js\";\n\nfunction sanitizeForDir(s: string): string {\n\treturn s.replace(/[^a-zA-Z0-9._-]+/g, \"_\").slice(0, 80);\n}\n\nfunction isGitSource(loc: string): boolean {\n\treturn /^https?:\\/\\//.test(loc) || loc.startsWith(\"git@\") || loc.endsWith(\".git\");\n}\n\nexport function setupMarketplace(pi: ExtensionAPI): void {\n\t// `.agents/` is the primary, cross-vendor home for installed plugins and the\n\t// added-marketplace registry, matching the \"`.agents/` first\" policy. Legacy\n\t// `.hoocode/plugins/` is still discovered and honored by `remove`.\n\tconst storePath = (cwd: string) => join(cwd, \".agents\", \"marketplaces.json\");\n\tconst legacyStorePath = (cwd: string) => join(cwd, \".hoocode\", \"marketplaces.json\");\n\tconst pluginsDir = (cwd: string) => join(cwd, \".agents\", \"plugins\");\n\tconst legacyPluginsDir = (cwd: string) => join(cwd, \".hoocode\", \"plugins\");\n\tconst cacheDir = (cwd: string) => join(cwd, \".agents\", \"marketplace-cache\");\n\n\t// Read the registry from `.agents/` first, falling back to the legacy\n\t// `.hoocode/` location so marketplaces added before the move stay visible.\n\tconst readStore = (cwd: string) => {\n\t\tconst primary = readMarketplaceStore(storePath(cwd));\n\t\tif (primary.length > 0 || existsSync(storePath(cwd))) return primary;\n\t\treturn readMarketplaceStore(legacyStorePath(cwd));\n\t};\n\n\tconst findPlugin = (cwd: string, name: string) => {\n\t\tfor (const record of readStore(cwd)) {\n\t\t\tconst market = parseMarketplaceDir(record.dir);\n\t\t\tconst entry = market?.plugins.find((p) => p.name === name);\n\t\t\tif (market && entry) return { market, entry };\n\t\t}\n\t\treturn undefined;\n\t};\n\n\tpi.registerCommand(\"plugin\", {\n\t\tdescription:\n\t\t\t\"Manage plugin marketplaces. /plugin marketplace add <git-url|path> | /plugin marketplace list | /plugin list | /plugin install <name> | /plugin remove <name>\",\n\t\tgetArgumentCompletions: (prefix: string) =>\n\t\t\t[\"marketplace\", \"list\", \"install\", \"remove\"]\n\t\t\t\t.filter((s) => s.startsWith(prefix))\n\t\t\t\t.map((s) => ({ value: s, label: s })),\n\t\thandler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tconst cwd = ctx.cwd;\n\n\t\t\t// ── marketplace add / list ──────────────────────────────────────────\n\t\t\tif (trimmed.startsWith(\"marketplace\")) {\n\t\t\t\tconst sub = trimmed.slice(\"marketplace\".length).trim();\n\n\t\t\t\tif (sub === \"list\" || sub === \"\") {\n\t\t\t\t\tconst records = readStore(cwd);\n\t\t\t\t\tif (records.length === 0) {\n\t\t\t\t\t\tctx.ui.notify(\"No marketplaces. Add one with /plugin marketplace add <git-url|path>.\", \"info\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst lines = records.map((r) => {\n\t\t\t\t\t\tconst market = parseMarketplaceDir(r.dir);\n\t\t\t\t\t\tconst platforms =\n\t\t\t\t\t\t\tmarket && market.supportPlatform.length > 1 ? ` · ${market.supportPlatform.join(\", \")}` : \"\";\n\t\t\t\t\t\treturn `${market?.name ?? r.location} — ${market?.plugins.length ?? 0} plugin(s)${platforms} [${r.location}]`;\n\t\t\t\t\t});\n\t\t\t\t\tctx.ui.notify(lines.join(\"\\n\"), \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (sub.startsWith(\"add\")) {\n\t\t\t\t\tconst loc = sub.slice(\"add\".length).trim();\n\t\t\t\t\tif (!loc) {\n\t\t\t\t\t\tctx.ui.notify(\"Usage: /plugin marketplace add <git-url|path>\", \"warning\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet dir: string;\n\t\t\t\t\tif (isGitSource(loc)) {\n\t\t\t\t\t\tdir = join(cacheDir(cwd), sanitizeForDir(loc));\n\t\t\t\t\t\trmSync(dir, { recursive: true, force: true });\n\t\t\t\t\t\tmkdirSync(cacheDir(cwd), { recursive: true });\n\t\t\t\t\t\tconst res = await pi.exec(\"git\", [\"clone\", \"--depth\", \"1\", loc, dir]);\n\t\t\t\t\t\tif (res.code !== 0) {\n\t\t\t\t\t\t\tctx.ui.notify(`Clone failed: ${res.stderr || res.stdout}`, \"error\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdir = resolvePluginSource(loc, cwd).kind === \"local\" ? join(cwd, loc) : loc;\n\t\t\t\t\t\tif (!existsSync(dir)) {\n\t\t\t\t\t\t\tctx.ui.notify(`Path not found: ${dir}`, \"error\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst market = parseMarketplaceDir(dir);\n\t\t\t\t\tif (!market) {\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\t\"No marketplace manifest found (.agents-plugin/, .claude-plugin/, or .github/marketplace.json).\",\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst records = readStore(cwd).filter((r) => r.location !== loc);\n\t\t\t\t\trecords.push({ location: loc, dir });\n\t\t\t\t\twriteMarketplaceStore(storePath(cwd), records);\n\t\t\t\t\tctx.ui.notify(`Added marketplace \"${market.name}\" (${market.plugins.length} plugin(s)).`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tctx.ui.notify(\"Usage: /plugin marketplace add <git-url|path> | /plugin marketplace list\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ── list available plugins ──────────────────────────────────────────\n\t\t\tif (trimmed === \"list\" || trimmed === \"\") {\n\t\t\t\tconst records = readStore(cwd);\n\t\t\t\tconst lines: string[] = [];\n\t\t\t\tfor (const record of records) {\n\t\t\t\t\tconst market = parseMarketplaceDir(record.dir);\n\t\t\t\t\tfor (const p of market?.plugins ?? []) {\n\t\t\t\t\t\tlines.push(`${p.name} — ${p.description ?? p.source}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tctx.ui.notify(lines.length ? lines.join(\"\\n\") : \"No plugins available. Add a marketplace first.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ── install <name> ──────────────────────────────────────────────────\n\t\t\tif (trimmed.startsWith(\"install\")) {\n\t\t\t\tconst name = trimmed.slice(\"install\".length).trim();\n\t\t\t\tif (!name) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /plugin install <name>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst found = findPlugin(cwd, name);\n\t\t\t\tif (!found) {\n\t\t\t\t\tctx.ui.notify(`Plugin \"${name}\" not found in any marketplace.`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst resolved = resolvePluginSource(found.entry.source, found.market.root);\n\t\t\t\tconst dest = join(pluginsDir(cwd), sanitizeForDir(name));\n\t\t\t\trmSync(dest, { recursive: true, force: true });\n\t\t\t\tmkdirSync(pluginsDir(cwd), { recursive: true });\n\n\t\t\t\tif (resolved.kind === \"local\") {\n\t\t\t\t\tcpSync(resolved.path, dest, { recursive: true });\n\t\t\t\t} else if (resolved.kind === \"git\") {\n\t\t\t\t\tconst res = await pi.exec(\"git\", [\"clone\", \"--depth\", \"1\", resolved.url, dest]);\n\t\t\t\t\tif (res.code !== 0) {\n\t\t\t\t\t\tctx.ui.notify(`Clone failed: ${res.stderr || res.stdout}`, \"error\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tctx.ui.notify(`npm plugin sources are not supported yet (${resolved.spec}).`, \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tctx.ui.notify(`Installed \"${name}\" — reloading…`, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ── remove <name> ───────────────────────────────────────────────────\n\t\t\tif (trimmed.startsWith(\"remove\")) {\n\t\t\t\tconst name = trimmed.slice(\"remove\".length).trim();\n\t\t\t\tif (!name) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /plugin remove <name>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Remove from `.agents/` first, then the legacy `.hoocode/` location.\n\t\t\t\tconst candidates = [\n\t\t\t\t\tjoin(pluginsDir(cwd), sanitizeForDir(name)),\n\t\t\t\t\tjoin(legacyPluginsDir(cwd), sanitizeForDir(name)),\n\t\t\t\t];\n\t\t\t\tconst installed = candidates.filter((p) => existsSync(p));\n\t\t\t\tif (installed.length === 0) {\n\t\t\t\t\tctx.ui.notify(`Plugin \"${name}\" is not installed.`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tfor (const p of installed) rmSync(p, { recursive: true, force: true });\n\t\t\t\tctx.ui.notify(`Removed \"${name}\" — reloading…`, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tctx.ui.notify(\n\t\t\t\t\"Usage: /plugin marketplace add|list | /plugin list | /plugin install <name> | /plugin remove <name>\",\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* /plugin — marketplace add/list + plugin install/list/remove.
|
|
3
3
|
*
|
|
4
4
|
* `/plugin` installs plugins from marketplaces (a git repo or local dir with a
|
|
5
|
-
*
|
|
6
|
-
* index). Installed plugins are placed in
|
|
7
|
-
*
|
|
5
|
+
* native `.agents-plugin/marketplace.json`, Claude `.claude-plugin/marketplace.json`,
|
|
6
|
+
* or Copilot-style `.github/marketplace.json` index). Installed plugins are placed in
|
|
7
|
+
* `.agents/plugins/<name>` (the primary, cross-vendor home) and loaded by the plugin
|
|
8
|
+
* loader after a reload. Plugins hand-placed under `.hoocode/plugins/` are still
|
|
9
|
+
* discovered and can be removed, but new installs go to `.agents/`.
|
|
8
10
|
*
|
|
9
11
|
* /plugin marketplace add <git-url|path>
|
|
10
12
|
* /plugin marketplace list
|
|
@@ -22,11 +24,24 @@ function isGitSource(loc) {
|
|
|
22
24
|
return /^https?:\/\//.test(loc) || loc.startsWith("git@") || loc.endsWith(".git");
|
|
23
25
|
}
|
|
24
26
|
export function setupMarketplace(pi) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
// `.agents/` is the primary, cross-vendor home for installed plugins and the
|
|
28
|
+
// added-marketplace registry, matching the "`.agents/` first" policy. Legacy
|
|
29
|
+
// `.hoocode/plugins/` is still discovered and honored by `remove`.
|
|
30
|
+
const storePath = (cwd) => join(cwd, ".agents", "marketplaces.json");
|
|
31
|
+
const legacyStorePath = (cwd) => join(cwd, ".hoocode", "marketplaces.json");
|
|
32
|
+
const pluginsDir = (cwd) => join(cwd, ".agents", "plugins");
|
|
33
|
+
const legacyPluginsDir = (cwd) => join(cwd, ".hoocode", "plugins");
|
|
34
|
+
const cacheDir = (cwd) => join(cwd, ".agents", "marketplace-cache");
|
|
35
|
+
// Read the registry from `.agents/` first, falling back to the legacy
|
|
36
|
+
// `.hoocode/` location so marketplaces added before the move stay visible.
|
|
37
|
+
const readStore = (cwd) => {
|
|
38
|
+
const primary = readMarketplaceStore(storePath(cwd));
|
|
39
|
+
if (primary.length > 0 || existsSync(storePath(cwd)))
|
|
40
|
+
return primary;
|
|
41
|
+
return readMarketplaceStore(legacyStorePath(cwd));
|
|
42
|
+
};
|
|
28
43
|
const findPlugin = (cwd, name) => {
|
|
29
|
-
for (const record of
|
|
44
|
+
for (const record of readStore(cwd)) {
|
|
30
45
|
const market = parseMarketplaceDir(record.dir);
|
|
31
46
|
const entry = market?.plugins.find((p) => p.name === name);
|
|
32
47
|
if (market && entry)
|
|
@@ -46,14 +61,15 @@ export function setupMarketplace(pi) {
|
|
|
46
61
|
if (trimmed.startsWith("marketplace")) {
|
|
47
62
|
const sub = trimmed.slice("marketplace".length).trim();
|
|
48
63
|
if (sub === "list" || sub === "") {
|
|
49
|
-
const records =
|
|
64
|
+
const records = readStore(cwd);
|
|
50
65
|
if (records.length === 0) {
|
|
51
66
|
ctx.ui.notify("No marketplaces. Add one with /plugin marketplace add <git-url|path>.", "info");
|
|
52
67
|
return;
|
|
53
68
|
}
|
|
54
69
|
const lines = records.map((r) => {
|
|
55
70
|
const market = parseMarketplaceDir(r.dir);
|
|
56
|
-
|
|
71
|
+
const platforms = market && market.supportPlatform.length > 1 ? ` · ${market.supportPlatform.join(", ")}` : "";
|
|
72
|
+
return `${market?.name ?? r.location} — ${market?.plugins.length ?? 0} plugin(s)${platforms} [${r.location}]`;
|
|
57
73
|
});
|
|
58
74
|
ctx.ui.notify(lines.join("\n"), "info");
|
|
59
75
|
return;
|
|
@@ -84,10 +100,10 @@ export function setupMarketplace(pi) {
|
|
|
84
100
|
}
|
|
85
101
|
const market = parseMarketplaceDir(dir);
|
|
86
102
|
if (!market) {
|
|
87
|
-
ctx.ui.notify("No marketplace manifest found (.claude-plugin
|
|
103
|
+
ctx.ui.notify("No marketplace manifest found (.agents-plugin/, .claude-plugin/, or .github/marketplace.json).", "error");
|
|
88
104
|
return;
|
|
89
105
|
}
|
|
90
|
-
const records =
|
|
106
|
+
const records = readStore(cwd).filter((r) => r.location !== loc);
|
|
91
107
|
records.push({ location: loc, dir });
|
|
92
108
|
writeMarketplaceStore(storePath(cwd), records);
|
|
93
109
|
ctx.ui.notify(`Added marketplace "${market.name}" (${market.plugins.length} plugin(s)).`, "info");
|
|
@@ -98,7 +114,7 @@ export function setupMarketplace(pi) {
|
|
|
98
114
|
}
|
|
99
115
|
// ── list available plugins ──────────────────────────────────────────
|
|
100
116
|
if (trimmed === "list" || trimmed === "") {
|
|
101
|
-
const records =
|
|
117
|
+
const records = readStore(cwd);
|
|
102
118
|
const lines = [];
|
|
103
119
|
for (const record of records) {
|
|
104
120
|
const market = parseMarketplaceDir(record.dir);
|
|
@@ -150,12 +166,18 @@ export function setupMarketplace(pi) {
|
|
|
150
166
|
ctx.ui.notify("Usage: /plugin remove <name>", "warning");
|
|
151
167
|
return;
|
|
152
168
|
}
|
|
153
|
-
|
|
154
|
-
|
|
169
|
+
// Remove from `.agents/` first, then the legacy `.hoocode/` location.
|
|
170
|
+
const candidates = [
|
|
171
|
+
join(pluginsDir(cwd), sanitizeForDir(name)),
|
|
172
|
+
join(legacyPluginsDir(cwd), sanitizeForDir(name)),
|
|
173
|
+
];
|
|
174
|
+
const installed = candidates.filter((p) => existsSync(p));
|
|
175
|
+
if (installed.length === 0) {
|
|
155
176
|
ctx.ui.notify(`Plugin "${name}" is not installed.`, "info");
|
|
156
177
|
return;
|
|
157
178
|
}
|
|
158
|
-
|
|
179
|
+
for (const p of installed)
|
|
180
|
+
rmSync(p, { recursive: true, force: true });
|
|
159
181
|
ctx.ui.notify(`Removed "${name}" — reloading…`, "info");
|
|
160
182
|
await ctx.reload();
|
|
161
183
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marketplace.js","sourceRoot":"","sources":["../../../src/extensions/core/marketplace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /plugin — marketplace add/list + plugin install/list/remove.\n *\n * `/plugin` installs plugins from marketplaces (a git repo or local dir with a\n * Claude `.claude-plugin/marketplace.json` or Copilot-style `.github/marketplace.json`\n * index). Installed plugins are placed in `.hoocode/plugins/<name>` and loaded by the\n * plugin loader after a reload.\n *\n * /plugin marketplace add <git-url|path>\n * /plugin marketplace list\n * /plugin list list available plugins across marketplaces\n * /plugin install <name>\n * /plugin remove <name>\n */\n\nimport { cpSync, existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tparseMarketplaceDir,\n\treadMarketplaceStore,\n\tresolvePluginSource,\n\twriteMarketplaceStore,\n} from \"../../core/extensions/plugins/marketplace.js\";\nimport type { ExtensionAPI, ExtensionCommandContext } from \"../../core/extensions/types.js\";\n\nfunction sanitizeForDir(s: string): string {\n\treturn s.replace(/[^a-zA-Z0-9._-]+/g, \"_\").slice(0, 80);\n}\n\nfunction isGitSource(loc: string): boolean {\n\treturn /^https?:\\/\\//.test(loc) || loc.startsWith(\"git@\") || loc.endsWith(\".git\");\n}\n\nexport function setupMarketplace(pi: ExtensionAPI): void {\n\tconst storePath = (cwd: string) => join(cwd, \".hoocode\", \"marketplaces.json\");\n\tconst pluginsDir = (cwd: string) => join(cwd, \".hoocode\", \"plugins\");\n\tconst cacheDir = (cwd: string) => join(cwd, \".hoocode\", \"marketplace-cache\");\n\n\tconst findPlugin = (cwd: string, name: string) => {\n\t\tfor (const record of readMarketplaceStore(storePath(cwd))) {\n\t\t\tconst market = parseMarketplaceDir(record.dir);\n\t\t\tconst entry = market?.plugins.find((p) => p.name === name);\n\t\t\tif (market && entry) return { market, entry };\n\t\t}\n\t\treturn undefined;\n\t};\n\n\tpi.registerCommand(\"plugin\", {\n\t\tdescription:\n\t\t\t\"Manage plugin marketplaces. /plugin marketplace add <git-url|path> | /plugin marketplace list | /plugin list | /plugin install <name> | /plugin remove <name>\",\n\t\tgetArgumentCompletions: (prefix: string) =>\n\t\t\t[\"marketplace\", \"list\", \"install\", \"remove\"]\n\t\t\t\t.filter((s) => s.startsWith(prefix))\n\t\t\t\t.map((s) => ({ value: s, label: s })),\n\t\thandler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tconst cwd = ctx.cwd;\n\n\t\t\t// ── marketplace add / list ──────────────────────────────────────────\n\t\t\tif (trimmed.startsWith(\"marketplace\")) {\n\t\t\t\tconst sub = trimmed.slice(\"marketplace\".length).trim();\n\n\t\t\t\tif (sub === \"list\" || sub === \"\") {\n\t\t\t\t\tconst records = readMarketplaceStore(storePath(cwd));\n\t\t\t\t\tif (records.length === 0) {\n\t\t\t\t\t\tctx.ui.notify(\"No marketplaces. Add one with /plugin marketplace add <git-url|path>.\", \"info\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst lines = records.map((r) => {\n\t\t\t\t\t\tconst market = parseMarketplaceDir(r.dir);\n\t\t\t\t\t\treturn `${market?.name ?? r.location} — ${market?.plugins.length ?? 0} plugin(s) [${r.location}]`;\n\t\t\t\t\t});\n\t\t\t\t\tctx.ui.notify(lines.join(\"\\n\"), \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (sub.startsWith(\"add\")) {\n\t\t\t\t\tconst loc = sub.slice(\"add\".length).trim();\n\t\t\t\t\tif (!loc) {\n\t\t\t\t\t\tctx.ui.notify(\"Usage: /plugin marketplace add <git-url|path>\", \"warning\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet dir: string;\n\t\t\t\t\tif (isGitSource(loc)) {\n\t\t\t\t\t\tdir = join(cacheDir(cwd), sanitizeForDir(loc));\n\t\t\t\t\t\trmSync(dir, { recursive: true, force: true });\n\t\t\t\t\t\tmkdirSync(cacheDir(cwd), { recursive: true });\n\t\t\t\t\t\tconst res = await pi.exec(\"git\", [\"clone\", \"--depth\", \"1\", loc, dir]);\n\t\t\t\t\t\tif (res.code !== 0) {\n\t\t\t\t\t\t\tctx.ui.notify(`Clone failed: ${res.stderr || res.stdout}`, \"error\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdir = resolvePluginSource(loc, cwd).kind === \"local\" ? join(cwd, loc) : loc;\n\t\t\t\t\t\tif (!existsSync(dir)) {\n\t\t\t\t\t\t\tctx.ui.notify(`Path not found: ${dir}`, \"error\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst market = parseMarketplaceDir(dir);\n\t\t\t\t\tif (!market) {\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\t\"No marketplace manifest found (.claude-plugin/ or .github/marketplace.json).\",\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst records = readMarketplaceStore(storePath(cwd)).filter((r) => r.location !== loc);\n\t\t\t\t\trecords.push({ location: loc, dir });\n\t\t\t\t\twriteMarketplaceStore(storePath(cwd), records);\n\t\t\t\t\tctx.ui.notify(`Added marketplace \"${market.name}\" (${market.plugins.length} plugin(s)).`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tctx.ui.notify(\"Usage: /plugin marketplace add <git-url|path> | /plugin marketplace list\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ── list available plugins ──────────────────────────────────────────\n\t\t\tif (trimmed === \"list\" || trimmed === \"\") {\n\t\t\t\tconst records = readMarketplaceStore(storePath(cwd));\n\t\t\t\tconst lines: string[] = [];\n\t\t\t\tfor (const record of records) {\n\t\t\t\t\tconst market = parseMarketplaceDir(record.dir);\n\t\t\t\t\tfor (const p of market?.plugins ?? []) {\n\t\t\t\t\t\tlines.push(`${p.name} — ${p.description ?? p.source}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tctx.ui.notify(lines.length ? lines.join(\"\\n\") : \"No plugins available. Add a marketplace first.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ── install <name> ──────────────────────────────────────────────────\n\t\t\tif (trimmed.startsWith(\"install\")) {\n\t\t\t\tconst name = trimmed.slice(\"install\".length).trim();\n\t\t\t\tif (!name) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /plugin install <name>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst found = findPlugin(cwd, name);\n\t\t\t\tif (!found) {\n\t\t\t\t\tctx.ui.notify(`Plugin \"${name}\" not found in any marketplace.`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst resolved = resolvePluginSource(found.entry.source, found.market.root);\n\t\t\t\tconst dest = join(pluginsDir(cwd), sanitizeForDir(name));\n\t\t\t\trmSync(dest, { recursive: true, force: true });\n\t\t\t\tmkdirSync(pluginsDir(cwd), { recursive: true });\n\n\t\t\t\tif (resolved.kind === \"local\") {\n\t\t\t\t\tcpSync(resolved.path, dest, { recursive: true });\n\t\t\t\t} else if (resolved.kind === \"git\") {\n\t\t\t\t\tconst res = await pi.exec(\"git\", [\"clone\", \"--depth\", \"1\", resolved.url, dest]);\n\t\t\t\t\tif (res.code !== 0) {\n\t\t\t\t\t\tctx.ui.notify(`Clone failed: ${res.stderr || res.stdout}`, \"error\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tctx.ui.notify(`npm plugin sources are not supported yet (${resolved.spec}).`, \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tctx.ui.notify(`Installed \"${name}\" — reloading…`, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ── remove <name> ───────────────────────────────────────────────────\n\t\t\tif (trimmed.startsWith(\"remove\")) {\n\t\t\t\tconst name = trimmed.slice(\"remove\".length).trim();\n\t\t\t\tif (!name) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /plugin remove <name>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst dest = join(pluginsDir(cwd), sanitizeForDir(name));\n\t\t\t\tif (!existsSync(dest)) {\n\t\t\t\t\tctx.ui.notify(`Plugin \"${name}\" is not installed.`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\trmSync(dest, { recursive: true, force: true });\n\t\t\t\tctx.ui.notify(`Removed \"${name}\" — reloading…`, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tctx.ui.notify(\n\t\t\t\t\"Usage: /plugin marketplace add|list | /plugin list | /plugin install <name> | /plugin remove <name>\",\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"marketplace.js","sourceRoot":"","sources":["../../../src/extensions/core/marketplace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /plugin — marketplace add/list + plugin install/list/remove.\n *\n * `/plugin` installs plugins from marketplaces (a git repo or local dir with a\n * native `.agents-plugin/marketplace.json`, Claude `.claude-plugin/marketplace.json`,\n * or Copilot-style `.github/marketplace.json` index). Installed plugins are placed in\n * `.agents/plugins/<name>` (the primary, cross-vendor home) and loaded by the plugin\n * loader after a reload. Plugins hand-placed under `.hoocode/plugins/` are still\n * discovered and can be removed, but new installs go to `.agents/`.\n *\n * /plugin marketplace add <git-url|path>\n * /plugin marketplace list\n * /plugin list list available plugins across marketplaces\n * /plugin install <name>\n * /plugin remove <name>\n */\n\nimport { cpSync, existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tparseMarketplaceDir,\n\treadMarketplaceStore,\n\tresolvePluginSource,\n\twriteMarketplaceStore,\n} from \"../../core/extensions/plugins/marketplace.js\";\nimport type { ExtensionAPI, ExtensionCommandContext } from \"../../core/extensions/types.js\";\n\nfunction sanitizeForDir(s: string): string {\n\treturn s.replace(/[^a-zA-Z0-9._-]+/g, \"_\").slice(0, 80);\n}\n\nfunction isGitSource(loc: string): boolean {\n\treturn /^https?:\\/\\//.test(loc) || loc.startsWith(\"git@\") || loc.endsWith(\".git\");\n}\n\nexport function setupMarketplace(pi: ExtensionAPI): void {\n\t// `.agents/` is the primary, cross-vendor home for installed plugins and the\n\t// added-marketplace registry, matching the \"`.agents/` first\" policy. Legacy\n\t// `.hoocode/plugins/` is still discovered and honored by `remove`.\n\tconst storePath = (cwd: string) => join(cwd, \".agents\", \"marketplaces.json\");\n\tconst legacyStorePath = (cwd: string) => join(cwd, \".hoocode\", \"marketplaces.json\");\n\tconst pluginsDir = (cwd: string) => join(cwd, \".agents\", \"plugins\");\n\tconst legacyPluginsDir = (cwd: string) => join(cwd, \".hoocode\", \"plugins\");\n\tconst cacheDir = (cwd: string) => join(cwd, \".agents\", \"marketplace-cache\");\n\n\t// Read the registry from `.agents/` first, falling back to the legacy\n\t// `.hoocode/` location so marketplaces added before the move stay visible.\n\tconst readStore = (cwd: string) => {\n\t\tconst primary = readMarketplaceStore(storePath(cwd));\n\t\tif (primary.length > 0 || existsSync(storePath(cwd))) return primary;\n\t\treturn readMarketplaceStore(legacyStorePath(cwd));\n\t};\n\n\tconst findPlugin = (cwd: string, name: string) => {\n\t\tfor (const record of readStore(cwd)) {\n\t\t\tconst market = parseMarketplaceDir(record.dir);\n\t\t\tconst entry = market?.plugins.find((p) => p.name === name);\n\t\t\tif (market && entry) return { market, entry };\n\t\t}\n\t\treturn undefined;\n\t};\n\n\tpi.registerCommand(\"plugin\", {\n\t\tdescription:\n\t\t\t\"Manage plugin marketplaces. /plugin marketplace add <git-url|path> | /plugin marketplace list | /plugin list | /plugin install <name> | /plugin remove <name>\",\n\t\tgetArgumentCompletions: (prefix: string) =>\n\t\t\t[\"marketplace\", \"list\", \"install\", \"remove\"]\n\t\t\t\t.filter((s) => s.startsWith(prefix))\n\t\t\t\t.map((s) => ({ value: s, label: s })),\n\t\thandler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tconst cwd = ctx.cwd;\n\n\t\t\t// ── marketplace add / list ──────────────────────────────────────────\n\t\t\tif (trimmed.startsWith(\"marketplace\")) {\n\t\t\t\tconst sub = trimmed.slice(\"marketplace\".length).trim();\n\n\t\t\t\tif (sub === \"list\" || sub === \"\") {\n\t\t\t\t\tconst records = readStore(cwd);\n\t\t\t\t\tif (records.length === 0) {\n\t\t\t\t\t\tctx.ui.notify(\"No marketplaces. Add one with /plugin marketplace add <git-url|path>.\", \"info\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst lines = records.map((r) => {\n\t\t\t\t\t\tconst market = parseMarketplaceDir(r.dir);\n\t\t\t\t\t\tconst platforms =\n\t\t\t\t\t\t\tmarket && market.supportPlatform.length > 1 ? ` · ${market.supportPlatform.join(\", \")}` : \"\";\n\t\t\t\t\t\treturn `${market?.name ?? r.location} — ${market?.plugins.length ?? 0} plugin(s)${platforms} [${r.location}]`;\n\t\t\t\t\t});\n\t\t\t\t\tctx.ui.notify(lines.join(\"\\n\"), \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (sub.startsWith(\"add\")) {\n\t\t\t\t\tconst loc = sub.slice(\"add\".length).trim();\n\t\t\t\t\tif (!loc) {\n\t\t\t\t\t\tctx.ui.notify(\"Usage: /plugin marketplace add <git-url|path>\", \"warning\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet dir: string;\n\t\t\t\t\tif (isGitSource(loc)) {\n\t\t\t\t\t\tdir = join(cacheDir(cwd), sanitizeForDir(loc));\n\t\t\t\t\t\trmSync(dir, { recursive: true, force: true });\n\t\t\t\t\t\tmkdirSync(cacheDir(cwd), { recursive: true });\n\t\t\t\t\t\tconst res = await pi.exec(\"git\", [\"clone\", \"--depth\", \"1\", loc, dir]);\n\t\t\t\t\t\tif (res.code !== 0) {\n\t\t\t\t\t\t\tctx.ui.notify(`Clone failed: ${res.stderr || res.stdout}`, \"error\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdir = resolvePluginSource(loc, cwd).kind === \"local\" ? join(cwd, loc) : loc;\n\t\t\t\t\t\tif (!existsSync(dir)) {\n\t\t\t\t\t\t\tctx.ui.notify(`Path not found: ${dir}`, \"error\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst market = parseMarketplaceDir(dir);\n\t\t\t\t\tif (!market) {\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\t\"No marketplace manifest found (.agents-plugin/, .claude-plugin/, or .github/marketplace.json).\",\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst records = readStore(cwd).filter((r) => r.location !== loc);\n\t\t\t\t\trecords.push({ location: loc, dir });\n\t\t\t\t\twriteMarketplaceStore(storePath(cwd), records);\n\t\t\t\t\tctx.ui.notify(`Added marketplace \"${market.name}\" (${market.plugins.length} plugin(s)).`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tctx.ui.notify(\"Usage: /plugin marketplace add <git-url|path> | /plugin marketplace list\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ── list available plugins ──────────────────────────────────────────\n\t\t\tif (trimmed === \"list\" || trimmed === \"\") {\n\t\t\t\tconst records = readStore(cwd);\n\t\t\t\tconst lines: string[] = [];\n\t\t\t\tfor (const record of records) {\n\t\t\t\t\tconst market = parseMarketplaceDir(record.dir);\n\t\t\t\t\tfor (const p of market?.plugins ?? []) {\n\t\t\t\t\t\tlines.push(`${p.name} — ${p.description ?? p.source}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tctx.ui.notify(lines.length ? lines.join(\"\\n\") : \"No plugins available. Add a marketplace first.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ── install <name> ──────────────────────────────────────────────────\n\t\t\tif (trimmed.startsWith(\"install\")) {\n\t\t\t\tconst name = trimmed.slice(\"install\".length).trim();\n\t\t\t\tif (!name) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /plugin install <name>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst found = findPlugin(cwd, name);\n\t\t\t\tif (!found) {\n\t\t\t\t\tctx.ui.notify(`Plugin \"${name}\" not found in any marketplace.`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst resolved = resolvePluginSource(found.entry.source, found.market.root);\n\t\t\t\tconst dest = join(pluginsDir(cwd), sanitizeForDir(name));\n\t\t\t\trmSync(dest, { recursive: true, force: true });\n\t\t\t\tmkdirSync(pluginsDir(cwd), { recursive: true });\n\n\t\t\t\tif (resolved.kind === \"local\") {\n\t\t\t\t\tcpSync(resolved.path, dest, { recursive: true });\n\t\t\t\t} else if (resolved.kind === \"git\") {\n\t\t\t\t\tconst res = await pi.exec(\"git\", [\"clone\", \"--depth\", \"1\", resolved.url, dest]);\n\t\t\t\t\tif (res.code !== 0) {\n\t\t\t\t\t\tctx.ui.notify(`Clone failed: ${res.stderr || res.stdout}`, \"error\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tctx.ui.notify(`npm plugin sources are not supported yet (${resolved.spec}).`, \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tctx.ui.notify(`Installed \"${name}\" — reloading…`, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ── remove <name> ───────────────────────────────────────────────────\n\t\t\tif (trimmed.startsWith(\"remove\")) {\n\t\t\t\tconst name = trimmed.slice(\"remove\".length).trim();\n\t\t\t\tif (!name) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /plugin remove <name>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Remove from `.agents/` first, then the legacy `.hoocode/` location.\n\t\t\t\tconst candidates = [\n\t\t\t\t\tjoin(pluginsDir(cwd), sanitizeForDir(name)),\n\t\t\t\t\tjoin(legacyPluginsDir(cwd), sanitizeForDir(name)),\n\t\t\t\t];\n\t\t\t\tconst installed = candidates.filter((p) => existsSync(p));\n\t\t\t\tif (installed.length === 0) {\n\t\t\t\t\tctx.ui.notify(`Plugin \"${name}\" is not installed.`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tfor (const p of installed) rmSync(p, { recursive: true, force: true });\n\t\t\t\tctx.ui.notify(`Removed \"${name}\" — reloading…`, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tctx.ui.notify(\n\t\t\t\t\"Usage: /plugin marketplace add|list | /plugin list | /plugin install <name> | /plugin remove <name>\",\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Utilities for formatting keybinding hints in the UI.
|
|
3
3
|
*/
|
|
4
4
|
import { type Keybinding } from "@kolisachint/hoocode-tui";
|
|
5
|
+
import { type AppKeybinding } from "../../../core/keybindings.js";
|
|
5
6
|
export interface KeyTextFormatOptions {
|
|
6
7
|
capitalize?: boolean;
|
|
7
8
|
}
|
|
@@ -10,4 +11,13 @@ export declare function keyText(keybinding: Keybinding): string;
|
|
|
10
11
|
export declare function keyDisplayText(keybinding: Keybinding): string;
|
|
11
12
|
export declare function keyHint(keybinding: Keybinding, description: string): string;
|
|
12
13
|
export declare function rawKeyHint(key: string, description: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Match input against an app-level keybinding. The global manager is the
|
|
16
|
+
* app-aware one in normal runs (user overrides apply); when it is the bare TUI
|
|
17
|
+
* default (tests, early boot) the app definition's default keys are used so
|
|
18
|
+
* the component never goes dead.
|
|
19
|
+
*/
|
|
20
|
+
export declare function matchesAppKey(data: string, id: AppKeybinding): boolean;
|
|
21
|
+
/** First configured key for an app-level binding, for hint lines. */
|
|
22
|
+
export declare function appKeyLabel(id: AppKeybinding): string;
|
|
13
23
|
//# sourceMappingURL=keybinding-hints.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keybinding-hints.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAkB,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"keybinding-hints.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAkB,KAAK,UAAU,EAA0B,MAAM,0BAA0B,CAAC;AACnG,OAAO,EAAE,KAAK,aAAa,EAAe,MAAM,8BAA8B,CAAC;AAG/E,MAAM,WAAW,oBAAoB;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAOD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAUrF;AAOD,wBAAgB,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEtD;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAE7D;AAED,wBAAgB,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAEnE;AAQD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,GAAG,OAAO,CAItE;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,EAAE,EAAE,aAAa,GAAG,MAAM,CAKrD","sourcesContent":["/**\n * Utilities for formatting keybinding hints in the UI.\n */\n\nimport { getKeybindings, type Keybinding, type KeyId, matchesKey } from \"@kolisachint/hoocode-tui\";\nimport { type AppKeybinding, KEYBINDINGS } from \"../../../core/keybindings.js\";\nimport { theme } from \"../theme/theme.js\";\n\nexport interface KeyTextFormatOptions {\n\tcapitalize?: boolean;\n}\n\nfunction formatKeyPart(part: string, options: KeyTextFormatOptions): string {\n\tconst displayPart = process.platform === \"darwin\" && part.toLowerCase() === \"alt\" ? \"option\" : part;\n\treturn options.capitalize ? displayPart.charAt(0).toUpperCase() + displayPart.slice(1) : displayPart;\n}\n\nexport function formatKeyText(key: string, options: KeyTextFormatOptions = {}): string {\n\treturn key\n\t\t.split(\"/\")\n\t\t.map((k) =>\n\t\t\tk\n\t\t\t\t.split(\"+\")\n\t\t\t\t.map((part) => formatKeyPart(part, options))\n\t\t\t\t.join(\"+\"),\n\t\t)\n\t\t.join(\"/\");\n}\n\nfunction formatKeys(keys: KeyId[], options: KeyTextFormatOptions = {}): string {\n\tif (keys.length === 0) return \"\";\n\treturn formatKeyText(keys.join(\"/\"), options);\n}\n\nexport function keyText(keybinding: Keybinding): string {\n\treturn formatKeys(getKeybindings().getKeys(keybinding));\n}\n\nexport function keyDisplayText(keybinding: Keybinding): string {\n\treturn formatKeys(getKeybindings().getKeys(keybinding), { capitalize: true });\n}\n\nexport function keyHint(keybinding: Keybinding, description: string): string {\n\treturn theme.fg(\"dim\", keyText(keybinding)) + theme.fg(\"muted\", ` ${description}`);\n}\n\nexport function rawKeyHint(key: string, description: string): string {\n\treturn theme.fg(\"dim\", formatKeyText(key)) + theme.fg(\"muted\", ` ${description}`);\n}\n\n/** Default keys of an app-level binding, from its definition. */\nfunction appDefaultKeys(id: AppKeybinding): KeyId[] {\n\tconst defaults = KEYBINDINGS[id].defaultKeys;\n\treturn Array.isArray(defaults) ? defaults : [defaults];\n}\n\n/**\n * Match input against an app-level keybinding. The global manager is the\n * app-aware one in normal runs (user overrides apply); when it is the bare TUI\n * default (tests, early boot) the app definition's default keys are used so\n * the component never goes dead.\n */\nexport function matchesAppKey(data: string, id: AppKeybinding): boolean {\n\tconst keybindings = getKeybindings();\n\tif (keybindings.getDefinition(id)) return keybindings.matches(data, id);\n\treturn appDefaultKeys(id).some((key) => matchesKey(data, key));\n}\n\n/** First configured key for an app-level binding, for hint lines. */\nexport function appKeyLabel(id: AppKeybinding): string {\n\tconst keybindings = getKeybindings();\n\tconst keys = keybindings.getDefinition(id) ? keybindings.getKeys(id) : undefined;\n\tif (keys && keys.length > 0) return keys[0] as string;\n\treturn (appDefaultKeys(id)[0] ?? \"\") as string;\n}\n"]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utilities for formatting keybinding hints in the UI.
|
|
3
3
|
*/
|
|
4
|
-
import { getKeybindings } from "@kolisachint/hoocode-tui";
|
|
4
|
+
import { getKeybindings, matchesKey } from "@kolisachint/hoocode-tui";
|
|
5
|
+
import { KEYBINDINGS } from "../../../core/keybindings.js";
|
|
5
6
|
import { theme } from "../theme/theme.js";
|
|
6
7
|
function formatKeyPart(part, options) {
|
|
7
8
|
const displayPart = process.platform === "darwin" && part.toLowerCase() === "alt" ? "option" : part;
|
|
@@ -33,4 +34,29 @@ export function keyHint(keybinding, description) {
|
|
|
33
34
|
export function rawKeyHint(key, description) {
|
|
34
35
|
return theme.fg("dim", formatKeyText(key)) + theme.fg("muted", ` ${description}`);
|
|
35
36
|
}
|
|
37
|
+
/** Default keys of an app-level binding, from its definition. */
|
|
38
|
+
function appDefaultKeys(id) {
|
|
39
|
+
const defaults = KEYBINDINGS[id].defaultKeys;
|
|
40
|
+
return Array.isArray(defaults) ? defaults : [defaults];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Match input against an app-level keybinding. The global manager is the
|
|
44
|
+
* app-aware one in normal runs (user overrides apply); when it is the bare TUI
|
|
45
|
+
* default (tests, early boot) the app definition's default keys are used so
|
|
46
|
+
* the component never goes dead.
|
|
47
|
+
*/
|
|
48
|
+
export function matchesAppKey(data, id) {
|
|
49
|
+
const keybindings = getKeybindings();
|
|
50
|
+
if (keybindings.getDefinition(id))
|
|
51
|
+
return keybindings.matches(data, id);
|
|
52
|
+
return appDefaultKeys(id).some((key) => matchesKey(data, key));
|
|
53
|
+
}
|
|
54
|
+
/** First configured key for an app-level binding, for hint lines. */
|
|
55
|
+
export function appKeyLabel(id) {
|
|
56
|
+
const keybindings = getKeybindings();
|
|
57
|
+
const keys = keybindings.getDefinition(id) ? keybindings.getKeys(id) : undefined;
|
|
58
|
+
if (keys && keys.length > 0)
|
|
59
|
+
return keys[0];
|
|
60
|
+
return (appDefaultKeys(id)[0] ?? "");
|
|
61
|
+
}
|
|
36
62
|
//# sourceMappingURL=keybinding-hints.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keybinding-hints.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAA+B,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"keybinding-hints.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Utilities for formatting keybinding hints in the UI.\n */\n\nimport { getKeybindings, type Keybinding, type KeyId, matchesKey } from \"@kolisachint/hoocode-tui\";\nimport { type AppKeybinding, KEYBINDINGS } from \"../../../core/keybindings.js\";\nimport { theme } from \"../theme/theme.js\";\n\nexport interface KeyTextFormatOptions {\n\tcapitalize?: boolean;\n}\n\nfunction formatKeyPart(part: string, options: KeyTextFormatOptions): string {\n\tconst displayPart = process.platform === \"darwin\" && part.toLowerCase() === \"alt\" ? \"option\" : part;\n\treturn options.capitalize ? displayPart.charAt(0).toUpperCase() + displayPart.slice(1) : displayPart;\n}\n\nexport function formatKeyText(key: string, options: KeyTextFormatOptions = {}): string {\n\treturn key\n\t\t.split(\"/\")\n\t\t.map((k) =>\n\t\t\tk\n\t\t\t\t.split(\"+\")\n\t\t\t\t.map((part) => formatKeyPart(part, options))\n\t\t\t\t.join(\"+\"),\n\t\t)\n\t\t.join(\"/\");\n}\n\nfunction formatKeys(keys: KeyId[], options: KeyTextFormatOptions = {}): string {\n\tif (keys.length === 0) return \"\";\n\treturn formatKeyText(keys.join(\"/\"), options);\n}\n\nexport function keyText(keybinding: Keybinding): string {\n\treturn formatKeys(getKeybindings().getKeys(keybinding));\n}\n\nexport function keyDisplayText(keybinding: Keybinding): string {\n\treturn formatKeys(getKeybindings().getKeys(keybinding), { capitalize: true });\n}\n\nexport function keyHint(keybinding: Keybinding, description: string): string {\n\treturn theme.fg(\"dim\", keyText(keybinding)) + theme.fg(\"muted\", ` ${description}`);\n}\n\nexport function rawKeyHint(key: string, description: string): string {\n\treturn theme.fg(\"dim\", formatKeyText(key)) + theme.fg(\"muted\", ` ${description}`);\n}\n\n/** Default keys of an app-level binding, from its definition. */\nfunction appDefaultKeys(id: AppKeybinding): KeyId[] {\n\tconst defaults = KEYBINDINGS[id].defaultKeys;\n\treturn Array.isArray(defaults) ? defaults : [defaults];\n}\n\n/**\n * Match input against an app-level keybinding. The global manager is the\n * app-aware one in normal runs (user overrides apply); when it is the bare TUI\n * default (tests, early boot) the app definition's default keys are used so\n * the component never goes dead.\n */\nexport function matchesAppKey(data: string, id: AppKeybinding): boolean {\n\tconst keybindings = getKeybindings();\n\tif (keybindings.getDefinition(id)) return keybindings.matches(data, id);\n\treturn appDefaultKeys(id).some((key) => matchesKey(data, key));\n}\n\n/** First configured key for an app-level binding, for hint lines. */\nexport function appKeyLabel(id: AppKeybinding): string {\n\tconst keybindings = getKeybindings();\n\tconst keys = keybindings.getDefinition(id) ? keybindings.getKeys(id) : undefined;\n\tif (keys && keys.length > 0) return keys[0] as string;\n\treturn (appDefaultKeys(id)[0] ?? \"\") as string;\n}\n"]}
|
|
@@ -22,7 +22,10 @@ export type TaskPanelView = "flat" | "subagents" | "teams";
|
|
|
22
22
|
* ("[planner]", fed by `--team <url>`), or the MCP server ("[github]").
|
|
23
23
|
* - Three views split by ownership (cycled via app.tasks.cycleView, shown as a
|
|
24
24
|
* `tasks · subagents · teams` switcher in the header):
|
|
25
|
-
* - flat ("tasks") —
|
|
25
|
+
* - flat ("tasks") — the main agent's own TodoWrite plan, with each
|
|
26
|
+
* dispatched subagent run nested under the plan item it was dispatched
|
|
27
|
+
* for (linkedTaskId), so the plan and the agents executing it read as
|
|
28
|
+
* one picture;
|
|
26
29
|
* - subagents — a recursive task tree over delegated work, where a subagent
|
|
27
30
|
* that spawned a subagent shows its nested tasks (roots are the dispatched
|
|
28
31
|
* subagents and direct MCP calls; children link via parentTaskId, merged
|
|
@@ -47,6 +50,24 @@ export declare class TaskPanelComponent implements Component, Focusable {
|
|
|
47
50
|
private animationTimer;
|
|
48
51
|
private view;
|
|
49
52
|
private disposed;
|
|
53
|
+
private memoVersion;
|
|
54
|
+
private memoWidth;
|
|
55
|
+
private lineMemo;
|
|
56
|
+
private agentByIdMemo;
|
|
57
|
+
private subagentRowsMemo;
|
|
58
|
+
private flatRowsMemo;
|
|
59
|
+
/** Drop all memoized render state when the store or width changed. */
|
|
60
|
+
private syncMemo;
|
|
61
|
+
private agentById;
|
|
62
|
+
private subagentRows;
|
|
63
|
+
private flatRows;
|
|
64
|
+
/**
|
|
65
|
+
* formatTaskLine with caching for rows that don't animate. In-progress rows
|
|
66
|
+
* depend on the spinner frame and the owner's live activity, so they render
|
|
67
|
+
* fresh every frame; everything else is stable until the next store mutation
|
|
68
|
+
* or width change (handled by syncMemo).
|
|
69
|
+
*/
|
|
70
|
+
private taskLine;
|
|
50
71
|
focused: boolean;
|
|
51
72
|
private selectedRole;
|
|
52
73
|
/** Open the inline nudge editor for the selected role. */
|