@shawnstack/quickforge 1.4.0 → 1.4.1

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 (37) hide show
  1. package/README.md +12 -12
  2. package/dist/assets/{AgentProfilesPage-C79teCgh.js → AgentProfilesPage-CNK5PxA3.js} +1 -1
  3. package/dist/assets/ChatPanelHost-FqPQwwMO.js +217 -0
  4. package/dist/assets/PluginsPage-BCu1Ept0.js +1 -0
  5. package/dist/assets/{ScheduledTasksPage-C047y3p3.js → ScheduledTasksPage-Bx04rjui.js} +2 -2
  6. package/dist/assets/SharedConversationPage-55vX9sqe.js +1 -0
  7. package/dist/assets/TerminalDock-DLN_pLkJ.js +2 -0
  8. package/dist/assets/WorkspaceInspector-DoemHHnY.js +3 -0
  9. package/dist/assets/{WorkspaceReaderDialog-bTeERaGd.js → WorkspaceReaderDialog-C6xUHBCw.js} +2 -2
  10. package/dist/assets/{icons-Dsc5yL3l.js → icons-BWtivFsx.js} +1 -1
  11. package/dist/assets/index-CxOHP41X.css +3 -0
  12. package/dist/assets/index-Dcf73EL8.js +895 -0
  13. package/dist/assets/{monaco-DG4TcBMc.js → monaco-evITXh-m.js} +1 -1
  14. package/dist/assets/{react-vendor-CiCXOLb5.js → react-vendor-Mthyt1p4.js} +1 -1
  15. package/dist/index.html +4 -4
  16. package/package.json +1 -1
  17. package/server/agent-manager.mjs +85 -13
  18. package/server/approval-store.mjs +13 -1
  19. package/server/auto-compaction.mjs +29 -73
  20. package/server/context-usage.mjs +108 -0
  21. package/server/custom-commands.mjs +145 -28
  22. package/server/mcp/registry.mjs +40 -0
  23. package/server/routes/agent.mjs +1 -1
  24. package/server/routes/mcp.mjs +7 -1
  25. package/server/routes/project.mjs +32 -2
  26. package/server/routes/shared-conversation.mjs +1 -1
  27. package/server/storage.mjs +1 -0
  28. package/server/subagents.mjs +8 -6
  29. package/server/system-prompt.mjs +2 -2
  30. package/server/tools/definitions.mjs +1 -1
  31. package/dist/assets/ChatPanelHost-BjdIshtX.js +0 -195
  32. package/dist/assets/PluginsPage-Dt7Iiddo.js +0 -1
  33. package/dist/assets/SharedConversationPage-8X8kfztQ.js +0 -1
  34. package/dist/assets/TerminalDock-CEuJNf0m.js +0 -2
  35. package/dist/assets/WorkspaceInspector-BIa5gLVs.js +0 -3
  36. package/dist/assets/index-CPAWYhzz.css +0 -3
  37. package/dist/assets/index-YTL26wyJ.js +0 -814
@@ -1,4 +1,4 @@
1
- import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{it as t}from"./icons-Dsc5yL3l.js";function n(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function r(e){if(Array.isArray(e))return e}function i(e,t,n){return(t=p(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=e==null?null:typeof Symbol<`u`&&e[Symbol.iterator]||e[`@@iterator`];if(n!=null){var r,i,a,o,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,t!==0)for(;!(c=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);c=!0);}catch(e){l=!0,i=e}finally{try{if(!c&&n.return!=null&&(o=n.return(),Object(o)!==o))return}finally{if(l)throw i}}return s}}function o(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
1
+ import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{ut as t}from"./icons-BWtivFsx.js";function n(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function r(e){if(Array.isArray(e))return e}function i(e,t,n){return(t=p(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=e==null?null:typeof Symbol<`u`&&e[Symbol.iterator]||e[`@@iterator`];if(n!=null){var r,i,a,o,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,t!==0)for(;!(c=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);c=!0);}catch(e){l=!0,i=e}finally{try{if(!c&&n.return!=null&&(o=n.return(),Object(o)!==o))return}finally{if(l)throw i}}return s}}function o(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
2
2
  In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function s(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function c(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?s(Object(n),!0).forEach(function(t){i(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):s(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function l(e,t){if(e==null)return{};var n,r,i=u(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r<a.length;r++)n=a[r],t.indexOf(n)===-1&&{}.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function u(e,t){if(e==null)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(t.indexOf(r)!==-1)continue;n[r]=e[r]}return n}function d(e,t){return r(e)||a(e,t)||m(e,t)||o()}function f(e,t){if(typeof e!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(typeof r!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function p(e){var t=f(e,`string`);return typeof t==`symbol`?t:t+``}function m(e,t){if(e){if(typeof e==`string`)return n(e,t);var r={}.toString.call(e).slice(8,-1);return r===`Object`&&e.constructor&&(r=e.constructor.name),r===`Map`||r===`Set`?Array.from(e):r===`Arguments`||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(e,t):void 0}}function h(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function _(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?g(Object(n),!0).forEach(function(t){h(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):g(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function v(){var e=[...arguments];return function(t){return e.reduceRight(function(e,t){return t(e)},t)}}function y(e){return function t(){var n=this,r=[...arguments];return r.length>=e.length?e.apply(this,r):function(){var e=[...arguments];return t.apply(n,[].concat(r,e))}}}function b(e){return{}.toString.call(e).includes(`Object`)}function x(e){return!Object.keys(e).length}function S(e){return typeof e==`function`}function C(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function w(e,t){return b(t)||k(`changeType`),Object.keys(t).some(function(t){return!C(e,t)})&&k(`changeField`),t}function T(e){S(e)||k(`selectorType`)}function E(e){S(e)||b(e)||k(`handlerType`),b(e)&&Object.values(e).some(function(e){return!S(e)})&&k(`handlersType`)}function D(e){e||k(`initialIsRequired`),b(e)||k(`initialType`),x(e)&&k(`initialContent`)}function O(e,t){throw Error(e[t]||e.default)}var k=y(O)({initialIsRequired:`initial state is required`,initialType:`initial state should be an object`,initialContent:`initial state shouldn't be an empty object`,handlerType:`handler should be an object or a function`,handlersType:`all handlers should be a functions`,selectorType:`selector should be a function`,changeType:`provided value of changes should be an object`,changeField:`it seams you want to change a field in the state which is not specified in the "initial" state`,default:"an unknown error accured in `state-local` package"}),A={changes:w,selector:T,handler:E,initial:D};function j(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};A.initial(e),A.handler(t);var n={current:e},r=y(P)(n,t),i=y(N)(n),a=y(A.changes)(e),o=y(M)(n);function s(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:function(e){return e};return A.selector(e),e(n.current)}function c(e){v(r,i,a,o)(e)}return[s,c]}function M(e,t){return S(t)?t(e.current):t}function N(e,t){return e.current=_(_({},e.current),t),t}function P(e,t,n){return S(t)?t(e.current):Object.keys(n).forEach(function(n){return t[n]?.call(t,e.current[n])}),n}var F={create:j},I={paths:{vs:`https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs`}};function ee(e){return function t(){var n=this,r=[...arguments];return r.length>=e.length?e.apply(this,r):function(){var e=[...arguments];return t.apply(n,[].concat(r,e))}}}function te(e){return{}.toString.call(e).includes(`Object`)}function ne(e){return e||R(`configIsRequired`),te(e)||R(`configType`),e.urls?(re(),{paths:{vs:e.urls.monacoBase}}):e}function re(){console.warn(L.deprecation)}function ie(e,t){throw Error(e[t]||e.default)}var L={configIsRequired:`the configuration object is required`,configType:`the configuration object should be an object`,default:"an unknown error accured in `@monaco-editor/loader` package",deprecation:`Deprecation warning!
3
3
  You are using deprecated way of configuration.
4
4
 
@@ -1,4 +1,4 @@
1
- import{t as e}from"./rolldown-runtime-DWdDZTNf.js";import{it as t}from"./icons-Dsc5yL3l.js";var n=e((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0<n;){var r=n-1>>>1,a=e[r];if(0<i(a,t))e[r]=t,e[n]=a,n=r;else break a}}function n(e){return e.length===0?null:e[0]}function r(e){if(e.length===0)return null;var t=e[0],n=e.pop();if(n!==t){e[0]=n;a:for(var r=0,a=e.length,o=a>>>1;r<o;){var s=2*(r+1)-1,c=e[s],l=s+1,u=e[l];if(0>i(c,n))l<a&&0>i(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(l<a&&0>i(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,D());else{var t=n(l);t!==null&&A(x,t.startTime-e)}}var S=!1,C=-1,ee=5,w=-1;function T(){return g?!0:!(e.unstable_now()-w<ee)}function E(){if(g=!1,S){var t=e.unstable_now();w=t;var i=!0;try{a:{m=!1,h&&(h=!1,v(C),C=-1),p=!0;var a=f;try{b:{for(b(t),d=n(c);d!==null&&!(d.expirationTime>t&&T());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&A(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?D():S=!1}}}var D;if(typeof y==`function`)D=function(){y(E)};else if(typeof MessageChannel<`u`){var O=new MessageChannel,k=O.port2;O.port1.onmessage=E,D=function(){k.postMessage(null)}}else D=function(){_(E,0)};function A(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125<e?console.error(`forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported`):ee=0<e?Math.floor(1e3/e):5},e.unstable_getCurrentPriorityLevel=function(){return f},e.unstable_next=function(e){switch(f){case 1:case 2:case 3:var t=3;break;default:t=f}var n=f;f=t;try{return e()}finally{f=n}},e.unstable_requestPaint=function(){g=!0},e.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=f;f=e;try{return t()}finally{f=n}},e.unstable_scheduleCallback=function(r,i,a){var o=e.unstable_now();switch(typeof a==`object`&&a?(a=a.delay,a=typeof a==`number`&&0<a?o+a:o):a=o,r){case 1:var s=-1;break;case 2:s=250;break;case 5:s=1073741823;break;case 4:s=1e4;break;default:s=5e3}return s=a+s,r={id:u++,callback:i,priorityLevel:r,startTime:a,expirationTime:s,sortIndex:-1},a>o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,A(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,D()))),r},e.unstable_shouldYield=T,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),r=e(((e,t)=>{t.exports=n()})),i=e((e=>{var n=t();function r(e){var t=`https://react.dev/errors/`+e;if(1<arguments.length){t+=`?args[]=`+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)t+=`&args[]=`+encodeURIComponent(arguments[n])}return`Minified React error #`+e+`; visit `+t+` for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`}function i(){}var a={d:{f:i,r:function(){throw Error(r(522))},D:i,C:i,L:i,m:i,X:i,S:i,M:i},p:0,findDOMNode:null},o=Symbol.for(`react.portal`);function s(e,t,n){var r=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:o,key:r==null?null:``+r,children:e,containerInfo:t,implementation:n}}var c=n.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function l(e,t){if(e===`font`)return``;if(typeof t==`string`)return t===`use-credentials`?t:``}e.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=a,e.createPortal=function(e,t){var n=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)throw Error(r(299));return s(e,t,null,n)},e.flushSync=function(e){var t=c.T,n=a.p;try{if(c.T=null,a.p=2,e)return e()}finally{c.T=t,a.p=n,a.d.f()}},e.preconnect=function(e,t){typeof e==`string`&&(t?(t=t.crossOrigin,t=typeof t==`string`?t===`use-credentials`?t:``:void 0):t=null,a.d.C(e,t))},e.prefetchDNS=function(e){typeof e==`string`&&a.d.D(e)},e.preinit=function(e,t){if(typeof e==`string`&&t&&typeof t.as==`string`){var n=t.as,r=l(n,t.crossOrigin),i=typeof t.integrity==`string`?t.integrity:void 0,o=typeof t.fetchPriority==`string`?t.fetchPriority:void 0;n===`style`?a.d.S(e,typeof t.precedence==`string`?t.precedence:void 0,{crossOrigin:r,integrity:i,fetchPriority:o}):n===`script`&&a.d.X(e,{crossOrigin:r,integrity:i,fetchPriority:o,nonce:typeof t.nonce==`string`?t.nonce:void 0})}},e.preinitModule=function(e,t){if(typeof e==`string`)if(typeof t==`object`&&t){if(t.as==null||t.as===`script`){var n=l(t.as,t.crossOrigin);a.d.M(e,{crossOrigin:n,integrity:typeof t.integrity==`string`?t.integrity:void 0,nonce:typeof t.nonce==`string`?t.nonce:void 0})}}else t??a.d.M(e)},e.preload=function(e,t){if(typeof e==`string`&&typeof t==`object`&&t&&typeof t.as==`string`){var n=t.as,r=l(n,t.crossOrigin);a.d.L(e,n,{crossOrigin:r,integrity:typeof t.integrity==`string`?t.integrity:void 0,nonce:typeof t.nonce==`string`?t.nonce:void 0,type:typeof t.type==`string`?t.type:void 0,fetchPriority:typeof t.fetchPriority==`string`?t.fetchPriority:void 0,referrerPolicy:typeof t.referrerPolicy==`string`?t.referrerPolicy:void 0,imageSrcSet:typeof t.imageSrcSet==`string`?t.imageSrcSet:void 0,imageSizes:typeof t.imageSizes==`string`?t.imageSizes:void 0,media:typeof t.media==`string`?t.media:void 0})}},e.preloadModule=function(e,t){if(typeof e==`string`)if(t){var n=l(t.as,t.crossOrigin);a.d.m(e,{as:typeof t.as==`string`&&t.as!==`script`?t.as:void 0,crossOrigin:n,integrity:typeof t.integrity==`string`?t.integrity:void 0})}else a.d.m(e)},e.requestFormReset=function(e){a.d.r(e)},e.unstable_batchedUpdates=function(e,t){return e(t)},e.useFormState=function(e,t,n){return c.H.useFormState(e,t,n)},e.useFormStatus=function(){return c.H.useHostTransitionStatus()},e.version=`19.2.5`})),a=e(((e,t)=>{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=i()})),o=e((e=>{var n=r(),i=t(),o=a();function s(e){var t=`https://react.dev/errors/`+e;if(1<arguments.length){t+=`?args[]=`+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)t+=`&args[]=`+encodeURIComponent(arguments[n])}return`Minified React error #`+e+`; visit `+t+` for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`}function c(e){return!(!e||e.nodeType!==1&&e.nodeType!==9&&e.nodeType!==11)}function l(e){var t=e,n=e;if(e.alternate)for(;t.return;)t=t.return;else{e=t;do t=e,t.flags&4098&&(n=t.return),e=t.return;while(e)}return t.tag===3?n:null}function u(e){if(e.tag===13){var t=e.memoizedState;if(t===null&&(e=e.alternate,e!==null&&(t=e.memoizedState)),t!==null)return t.dehydrated}return null}function d(e){if(e.tag===31){var t=e.memoizedState;if(t===null&&(e=e.alternate,e!==null&&(t=e.memoizedState)),t!==null)return t.dehydrated}return null}function f(e){if(l(e)!==e)throw Error(s(188))}function p(e){var t=e.alternate;if(!t){if(t=l(e),t===null)throw Error(s(188));return t===e?e:null}for(var n=e,r=t;;){var i=n.return;if(i===null)break;var a=i.alternate;if(a===null){if(r=i.return,r!==null){n=r;continue}break}if(i.child===a.child){for(a=i.child;a;){if(a===n)return f(i),e;if(a===r)return f(i),t;a=a.sibling}throw Error(s(188))}if(n.return!==r.return)n=i,r=a;else{for(var o=!1,c=i.child;c;){if(c===n){o=!0,n=i,r=a;break}if(c===r){o=!0,r=i,n=a;break}c=c.sibling}if(!o){for(c=a.child;c;){if(c===n){o=!0,n=a,r=i;break}if(c===r){o=!0,r=a,n=i;break}c=c.sibling}if(!o)throw Error(s(189))}}if(n.alternate!==r)throw Error(s(190))}if(n.tag!==3)throw Error(s(188));return n.stateNode.current===n?e:t}function m(e){var t=e.tag;if(t===5||t===26||t===27||t===6)return e;for(e=e.child;e!==null;){if(t=m(e),t!==null)return t;e=e.sibling}return null}var h=Object.assign,g=Symbol.for(`react.element`),_=Symbol.for(`react.transitional.element`),v=Symbol.for(`react.portal`),y=Symbol.for(`react.fragment`),b=Symbol.for(`react.strict_mode`),x=Symbol.for(`react.profiler`),S=Symbol.for(`react.consumer`),C=Symbol.for(`react.context`),ee=Symbol.for(`react.forward_ref`),w=Symbol.for(`react.suspense`),T=Symbol.for(`react.suspense_list`),E=Symbol.for(`react.memo`),D=Symbol.for(`react.lazy`),O=Symbol.for(`react.activity`),k=Symbol.for(`react.memo_cache_sentinel`),A=Symbol.iterator;function te(e){return typeof e!=`object`||!e?null:(e=A&&e[A]||e[`@@iterator`],typeof e==`function`?e:null)}var j=Symbol.for(`react.client.reference`);function M(e){if(e==null)return null;if(typeof e==`function`)return e.$$typeof===j?null:e.displayName||e.name||null;if(typeof e==`string`)return e;switch(e){case y:return`Fragment`;case x:return`Profiler`;case b:return`StrictMode`;case w:return`Suspense`;case T:return`SuspenseList`;case O:return`Activity`}if(typeof e==`object`)switch(e.$$typeof){case v:return`Portal`;case C:return e.displayName||`Context`;case S:return(e._context.displayName||`Context`)+`.Consumer`;case ee:var t=e.render;return e=e.displayName,e||=(e=t.displayName||t.name||``,e===``?`ForwardRef`:`ForwardRef(`+e+`)`),e;case E:return t=e.displayName||null,t===null?M(e.type)||`Memo`:t;case D:t=e._payload,e=e._init;try{return M(e(t))}catch{}}return null}var N=Array.isArray,P=i.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,F=o.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ne={pending:!1,data:null,method:null,action:null},I=[],L=-1;function re(e){return{current:e}}function R(e){0>L||(e.current=I[L],I[L]=null,L--)}function z(e,t){L++,I[L]=e.current,e.current=t}var B=re(null),ie=re(null),ae=re(null),oe=re(null);function se(e,t){switch(z(ae,t),z(ie,e),z(B,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Vd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Vd(t),e=Hd(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}R(B),z(B,e)}function ce(){R(B),R(ie),R(ae)}function V(e){e.memoizedState!==null&&z(oe,e);var t=B.current,n=Hd(t,e.type);t!==n&&(z(ie,e),z(B,n))}function le(e){ie.current===e&&(R(B),R(ie)),oe.current===e&&(R(oe),Qf._currentValue=ne)}var H,ue;function de(e){if(H===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);H=t&&t[1]||``,ue=-1<e.stack.indexOf(`
1
+ import{t as e}from"./rolldown-runtime-DWdDZTNf.js";import{ut as t}from"./icons-BWtivFsx.js";var n=e((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0<n;){var r=n-1>>>1,a=e[r];if(0<i(a,t))e[r]=t,e[n]=a,n=r;else break a}}function n(e){return e.length===0?null:e[0]}function r(e){if(e.length===0)return null;var t=e[0],n=e.pop();if(n!==t){e[0]=n;a:for(var r=0,a=e.length,o=a>>>1;r<o;){var s=2*(r+1)-1,c=e[s],l=s+1,u=e[l];if(0>i(c,n))l<a&&0>i(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(l<a&&0>i(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,D());else{var t=n(l);t!==null&&A(x,t.startTime-e)}}var S=!1,C=-1,ee=5,w=-1;function T(){return g?!0:!(e.unstable_now()-w<ee)}function E(){if(g=!1,S){var t=e.unstable_now();w=t;var i=!0;try{a:{m=!1,h&&(h=!1,v(C),C=-1),p=!0;var a=f;try{b:{for(b(t),d=n(c);d!==null&&!(d.expirationTime>t&&T());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&A(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?D():S=!1}}}var D;if(typeof y==`function`)D=function(){y(E)};else if(typeof MessageChannel<`u`){var O=new MessageChannel,k=O.port2;O.port1.onmessage=E,D=function(){k.postMessage(null)}}else D=function(){_(E,0)};function A(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125<e?console.error(`forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported`):ee=0<e?Math.floor(1e3/e):5},e.unstable_getCurrentPriorityLevel=function(){return f},e.unstable_next=function(e){switch(f){case 1:case 2:case 3:var t=3;break;default:t=f}var n=f;f=t;try{return e()}finally{f=n}},e.unstable_requestPaint=function(){g=!0},e.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=f;f=e;try{return t()}finally{f=n}},e.unstable_scheduleCallback=function(r,i,a){var o=e.unstable_now();switch(typeof a==`object`&&a?(a=a.delay,a=typeof a==`number`&&0<a?o+a:o):a=o,r){case 1:var s=-1;break;case 2:s=250;break;case 5:s=1073741823;break;case 4:s=1e4;break;default:s=5e3}return s=a+s,r={id:u++,callback:i,priorityLevel:r,startTime:a,expirationTime:s,sortIndex:-1},a>o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,A(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,D()))),r},e.unstable_shouldYield=T,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),r=e(((e,t)=>{t.exports=n()})),i=e((e=>{var n=t();function r(e){var t=`https://react.dev/errors/`+e;if(1<arguments.length){t+=`?args[]=`+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)t+=`&args[]=`+encodeURIComponent(arguments[n])}return`Minified React error #`+e+`; visit `+t+` for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`}function i(){}var a={d:{f:i,r:function(){throw Error(r(522))},D:i,C:i,L:i,m:i,X:i,S:i,M:i},p:0,findDOMNode:null},o=Symbol.for(`react.portal`);function s(e,t,n){var r=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:o,key:r==null?null:``+r,children:e,containerInfo:t,implementation:n}}var c=n.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function l(e,t){if(e===`font`)return``;if(typeof t==`string`)return t===`use-credentials`?t:``}e.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=a,e.createPortal=function(e,t){var n=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)throw Error(r(299));return s(e,t,null,n)},e.flushSync=function(e){var t=c.T,n=a.p;try{if(c.T=null,a.p=2,e)return e()}finally{c.T=t,a.p=n,a.d.f()}},e.preconnect=function(e,t){typeof e==`string`&&(t?(t=t.crossOrigin,t=typeof t==`string`?t===`use-credentials`?t:``:void 0):t=null,a.d.C(e,t))},e.prefetchDNS=function(e){typeof e==`string`&&a.d.D(e)},e.preinit=function(e,t){if(typeof e==`string`&&t&&typeof t.as==`string`){var n=t.as,r=l(n,t.crossOrigin),i=typeof t.integrity==`string`?t.integrity:void 0,o=typeof t.fetchPriority==`string`?t.fetchPriority:void 0;n===`style`?a.d.S(e,typeof t.precedence==`string`?t.precedence:void 0,{crossOrigin:r,integrity:i,fetchPriority:o}):n===`script`&&a.d.X(e,{crossOrigin:r,integrity:i,fetchPriority:o,nonce:typeof t.nonce==`string`?t.nonce:void 0})}},e.preinitModule=function(e,t){if(typeof e==`string`)if(typeof t==`object`&&t){if(t.as==null||t.as===`script`){var n=l(t.as,t.crossOrigin);a.d.M(e,{crossOrigin:n,integrity:typeof t.integrity==`string`?t.integrity:void 0,nonce:typeof t.nonce==`string`?t.nonce:void 0})}}else t??a.d.M(e)},e.preload=function(e,t){if(typeof e==`string`&&typeof t==`object`&&t&&typeof t.as==`string`){var n=t.as,r=l(n,t.crossOrigin);a.d.L(e,n,{crossOrigin:r,integrity:typeof t.integrity==`string`?t.integrity:void 0,nonce:typeof t.nonce==`string`?t.nonce:void 0,type:typeof t.type==`string`?t.type:void 0,fetchPriority:typeof t.fetchPriority==`string`?t.fetchPriority:void 0,referrerPolicy:typeof t.referrerPolicy==`string`?t.referrerPolicy:void 0,imageSrcSet:typeof t.imageSrcSet==`string`?t.imageSrcSet:void 0,imageSizes:typeof t.imageSizes==`string`?t.imageSizes:void 0,media:typeof t.media==`string`?t.media:void 0})}},e.preloadModule=function(e,t){if(typeof e==`string`)if(t){var n=l(t.as,t.crossOrigin);a.d.m(e,{as:typeof t.as==`string`&&t.as!==`script`?t.as:void 0,crossOrigin:n,integrity:typeof t.integrity==`string`?t.integrity:void 0})}else a.d.m(e)},e.requestFormReset=function(e){a.d.r(e)},e.unstable_batchedUpdates=function(e,t){return e(t)},e.useFormState=function(e,t,n){return c.H.useFormState(e,t,n)},e.useFormStatus=function(){return c.H.useHostTransitionStatus()},e.version=`19.2.5`})),a=e(((e,t)=>{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=i()})),o=e((e=>{var n=r(),i=t(),o=a();function s(e){var t=`https://react.dev/errors/`+e;if(1<arguments.length){t+=`?args[]=`+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)t+=`&args[]=`+encodeURIComponent(arguments[n])}return`Minified React error #`+e+`; visit `+t+` for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`}function c(e){return!(!e||e.nodeType!==1&&e.nodeType!==9&&e.nodeType!==11)}function l(e){var t=e,n=e;if(e.alternate)for(;t.return;)t=t.return;else{e=t;do t=e,t.flags&4098&&(n=t.return),e=t.return;while(e)}return t.tag===3?n:null}function u(e){if(e.tag===13){var t=e.memoizedState;if(t===null&&(e=e.alternate,e!==null&&(t=e.memoizedState)),t!==null)return t.dehydrated}return null}function d(e){if(e.tag===31){var t=e.memoizedState;if(t===null&&(e=e.alternate,e!==null&&(t=e.memoizedState)),t!==null)return t.dehydrated}return null}function f(e){if(l(e)!==e)throw Error(s(188))}function p(e){var t=e.alternate;if(!t){if(t=l(e),t===null)throw Error(s(188));return t===e?e:null}for(var n=e,r=t;;){var i=n.return;if(i===null)break;var a=i.alternate;if(a===null){if(r=i.return,r!==null){n=r;continue}break}if(i.child===a.child){for(a=i.child;a;){if(a===n)return f(i),e;if(a===r)return f(i),t;a=a.sibling}throw Error(s(188))}if(n.return!==r.return)n=i,r=a;else{for(var o=!1,c=i.child;c;){if(c===n){o=!0,n=i,r=a;break}if(c===r){o=!0,r=i,n=a;break}c=c.sibling}if(!o){for(c=a.child;c;){if(c===n){o=!0,n=a,r=i;break}if(c===r){o=!0,r=a,n=i;break}c=c.sibling}if(!o)throw Error(s(189))}}if(n.alternate!==r)throw Error(s(190))}if(n.tag!==3)throw Error(s(188));return n.stateNode.current===n?e:t}function m(e){var t=e.tag;if(t===5||t===26||t===27||t===6)return e;for(e=e.child;e!==null;){if(t=m(e),t!==null)return t;e=e.sibling}return null}var h=Object.assign,g=Symbol.for(`react.element`),_=Symbol.for(`react.transitional.element`),v=Symbol.for(`react.portal`),y=Symbol.for(`react.fragment`),b=Symbol.for(`react.strict_mode`),x=Symbol.for(`react.profiler`),S=Symbol.for(`react.consumer`),C=Symbol.for(`react.context`),ee=Symbol.for(`react.forward_ref`),w=Symbol.for(`react.suspense`),T=Symbol.for(`react.suspense_list`),E=Symbol.for(`react.memo`),D=Symbol.for(`react.lazy`),O=Symbol.for(`react.activity`),k=Symbol.for(`react.memo_cache_sentinel`),A=Symbol.iterator;function te(e){return typeof e!=`object`||!e?null:(e=A&&e[A]||e[`@@iterator`],typeof e==`function`?e:null)}var j=Symbol.for(`react.client.reference`);function M(e){if(e==null)return null;if(typeof e==`function`)return e.$$typeof===j?null:e.displayName||e.name||null;if(typeof e==`string`)return e;switch(e){case y:return`Fragment`;case x:return`Profiler`;case b:return`StrictMode`;case w:return`Suspense`;case T:return`SuspenseList`;case O:return`Activity`}if(typeof e==`object`)switch(e.$$typeof){case v:return`Portal`;case C:return e.displayName||`Context`;case S:return(e._context.displayName||`Context`)+`.Consumer`;case ee:var t=e.render;return e=e.displayName,e||=(e=t.displayName||t.name||``,e===``?`ForwardRef`:`ForwardRef(`+e+`)`),e;case E:return t=e.displayName||null,t===null?M(e.type)||`Memo`:t;case D:t=e._payload,e=e._init;try{return M(e(t))}catch{}}return null}var N=Array.isArray,P=i.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,F=o.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ne={pending:!1,data:null,method:null,action:null},I=[],L=-1;function re(e){return{current:e}}function R(e){0>L||(e.current=I[L],I[L]=null,L--)}function z(e,t){L++,I[L]=e.current,e.current=t}var B=re(null),ie=re(null),ae=re(null),oe=re(null);function se(e,t){switch(z(ae,t),z(ie,e),z(B,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Vd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Vd(t),e=Hd(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}R(B),z(B,e)}function ce(){R(B),R(ie),R(ae)}function V(e){e.memoizedState!==null&&z(oe,e);var t=B.current,n=Hd(t,e.type);t!==n&&(z(ie,e),z(B,n))}function le(e){ie.current===e&&(R(B),R(ie)),oe.current===e&&(R(oe),Qf._currentValue=ne)}var H,ue;function de(e){if(H===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);H=t&&t[1]||``,ue=-1<e.stack.indexOf(`
2
2
  at`)?` (<anonymous>)`:-1<e.stack.indexOf(`@`)?`@unknown:0:0`:``}return`
3
3
  `+H+e+ue}var fe=!1;function pe(e,t){if(!e||fe)return``;fe=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var r={DetermineComponentFrameRoot:function(){try{if(t){var n=function(){throw Error()};if(Object.defineProperty(n.prototype,`props`,{set:function(){throw Error()}}),typeof Reflect==`object`&&Reflect.construct){try{Reflect.construct(n,[])}catch(e){var r=e}Reflect.construct(e,[],n)}else{try{n.call()}catch(e){r=e}e.call(n.prototype)}}else{try{throw Error()}catch(e){r=e}(n=e())&&typeof n.catch==`function`&&n.catch(function(){})}}catch(e){if(e&&r&&typeof e.stack==`string`)return[e.stack,r.stack]}return[null,null]}};r.DetermineComponentFrameRoot.displayName=`DetermineComponentFrameRoot`;var i=Object.getOwnPropertyDescriptor(r.DetermineComponentFrameRoot,`name`);i&&i.configurable&&Object.defineProperty(r.DetermineComponentFrameRoot,`name`,{value:`DetermineComponentFrameRoot`});var a=r.DetermineComponentFrameRoot(),o=a[0],s=a[1];if(o&&s){var c=o.split(`
4
4
  `),l=s.split(`
package/dist/index.html CHANGED
@@ -11,16 +11,16 @@
11
11
  <meta name="apple-mobile-web-app-title" content="QuickForge" />
12
12
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
13
13
  <title>速构 QuickForge</title>
14
- <script type="module" crossorigin src="/assets/index-YTL26wyJ.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-Dcf73EL8.js"></script>
15
15
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-DWdDZTNf.js">
16
16
  <link rel="modulepreload" crossorigin href="/assets/pi-ai-Cx633yhb.js">
17
17
  <link rel="modulepreload" crossorigin href="/assets/lit-vendor-Dr3cpBGF.js">
18
18
  <link rel="modulepreload" crossorigin href="/assets/pi-web-ui-CBet4bMl.js">
19
19
  <link rel="modulepreload" crossorigin href="/assets/css-utils-rkE68RDy.js">
20
- <link rel="modulepreload" crossorigin href="/assets/icons-Dsc5yL3l.js">
21
- <link rel="modulepreload" crossorigin href="/assets/react-vendor-CiCXOLb5.js">
20
+ <link rel="modulepreload" crossorigin href="/assets/icons-BWtivFsx.js">
21
+ <link rel="modulepreload" crossorigin href="/assets/react-vendor-Mthyt1p4.js">
22
22
  <link rel="modulepreload" crossorigin href="/assets/logger-B65Akg8A.js">
23
- <link rel="stylesheet" crossorigin href="/assets/index-CPAWYhzz.css">
23
+ <link rel="stylesheet" crossorigin href="/assets/index-CxOHP41X.css">
24
24
  </head>
25
25
  <body>
26
26
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shawnstack/quickforge",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "AI chat application with YOLO-mode local workspace tools. React + Vite + Tailwind CSS frontend, local Node.js storage server.",
5
5
  "keywords": [
6
6
  "ai",
@@ -35,7 +35,6 @@ import { omitDetailsForLlm, serverConvertToLlm, messageText, lastAssistantText }
35
35
  import { isPlainObject, mergeQuickForgeTiming, wrapToolDefinition, wrapMcpToolDefinition, wrapPluginToolDefinition, sessionSkillsContext } from './tool-wiring.mjs'
36
36
  import {
37
37
  APPROVAL_TIMEOUT_MS,
38
- commandRestrictedTools,
39
38
  safeReadTools,
40
39
  pendingApprovals,
41
40
  pendingAutoCompactApprovals,
@@ -545,9 +544,59 @@ async function clearSession(session) {
545
544
  return { sessionId: session.sessionId, status: session.status, cleared: true }
546
545
  }
547
546
 
548
- async function resolveCommandState(session, userMessage) {
547
+ const QUICKFORGE_COMMAND_DETAILS_KEY = 'quickforgeCommand'
548
+
549
+ function normalizedPromptCommand(command) {
550
+ return command?.type === 'plan' ? { type: 'plan' } : null
551
+ }
552
+
553
+ function objectDetails(message) {
554
+ const details = message?.details
555
+ return details && typeof details === 'object' && !Array.isArray(details) ? details : {}
556
+ }
557
+
558
+ function promptCommandFromMessage(message) {
559
+ return normalizedPromptCommand(objectDetails(message)[QUICKFORGE_COMMAND_DETAILS_KEY])
560
+ }
561
+
562
+ function messageWithPromptCommand(message, command) {
563
+ const normalized = normalizedPromptCommand(command)
564
+ if (!normalized || !message || typeof message !== 'object') return message
565
+ return {
566
+ ...message,
567
+ details: {
568
+ ...objectDetails(message),
569
+ [QUICKFORGE_COMMAND_DETAILS_KEY]: normalized,
570
+ },
571
+ }
572
+ }
573
+
574
+ function internalInvocationForPromptCommand(userMessage, command) {
575
+ const normalized = normalizedPromptCommand(command)
576
+ if (normalized?.type === 'plan') {
577
+ // Derive the task from the message text. Strip a leading "/plan" so that
578
+ // toggling plan mode while typing "/plan <task>" yields the clean task —
579
+ // matching the slash-command parse path and avoiding a redundant prefix.
580
+ const raw = messageText(userMessage).trim()
581
+ const planPrefix = raw.match(/^\/plan(?:\s+([\s\S]*))?$/i)
582
+ return { type: 'plan', args: planPrefix ? (planPrefix[1] || '').trim() : raw }
583
+ }
584
+ return parseInternalCommandInvocation(userMessage)
585
+ }
586
+
587
+ function planCommandState(userMessage, args) {
588
+ return {
589
+ userMessage: messageWithPromptCommand(userMessage, { type: 'plan' }),
590
+ commandPrompt: formatPlanCommandPrompt(args),
591
+ permissions: { allowEdit: false, allowCommands: false, allowSubagents: true },
592
+ commandName: 'plan',
593
+ }
594
+ }
595
+
596
+ async function resolveCommandState(session, userMessage, promptCommand = null) {
597
+ const command = normalizedPromptCommand(promptCommand) || promptCommandFromMessage(userMessage)
549
598
  const internalResponse = await handleInternalCommand(
550
- parseInternalCommandInvocation(userMessage),
599
+ internalInvocationForPromptCommand(userMessage, command),
551
600
  session.projectContext?.workspaceRoot,
552
601
  session.projectContext?.project?.commandDir,
553
602
  )
@@ -555,12 +604,7 @@ async function resolveCommandState(session, userMessage) {
555
604
  if (internalResponse?.clear) return { clear: internalResponse }
556
605
  if (internalResponse?.compact) return { compact: internalResponse }
557
606
  if (internalResponse?.plan) {
558
- return {
559
- userMessage,
560
- commandPrompt: formatPlanCommandPrompt(internalResponse.args),
561
- permissions: { allowEdit: false, allowCommands: false, allowSubagents: true },
562
- commandName: 'plan',
563
- }
607
+ return planCommandState(userMessage, internalResponse.args)
564
608
  }
565
609
  if (internalResponse?.review) {
566
610
  return {
@@ -571,7 +615,22 @@ async function resolveCommandState(session, userMessage) {
571
615
  }
572
616
  }
573
617
 
574
- if (!session.projectContext?.workspaceRoot) return { userMessage }
618
+ if (!session.projectContext?.workspaceRoot) {
619
+ // Even without a project, user-level custom commands (~/.quickforge/commands/) are available
620
+ const invocation = await resolveCustomCommandInvocation(
621
+ userMessage,
622
+ null,
623
+ session.projectContext?.project?.commandDir,
624
+ )
625
+ if (!invocation) return { userMessage }
626
+
627
+ return {
628
+ userMessage,
629
+ commandPrompt: invocation.systemPrompt,
630
+ permissions: invocation.permissions,
631
+ commandName: invocation.command.name,
632
+ }
633
+ }
575
634
 
576
635
  const invocation = await resolveCustomCommandInvocation(
577
636
  userMessage,
@@ -1452,7 +1511,7 @@ export async function rollbackSessionMessages(sessionId, rollbackMessageIndex) {
1452
1511
  * Send a user message to the agent and start the agent loop.
1453
1512
  * Returns immediately; events are streamed via the event bus.
1454
1513
  */
1455
- export async function runPrompt(sessionId, message, selectedCapabilities = []) {
1514
+ export async function runPrompt(sessionId, message, selectedCapabilities = [], promptCommand = null) {
1456
1515
  let session = agentSessions.get(sessionId)
1457
1516
  if (!session) {
1458
1517
  session = await restoreAgent(sessionId)
@@ -1471,7 +1530,7 @@ export async function runPrompt(sessionId, message, selectedCapabilities = []) {
1471
1530
  const initialUserMessage = typeof message === 'string'
1472
1531
  ? { role: 'user', content: message, timestamp: new Date().toISOString() }
1473
1532
  : message
1474
- const commandState = await resolveCommandState(session, initialUserMessage)
1533
+ const commandState = await resolveCommandState(session, initialUserMessage, promptCommand)
1475
1534
  const userMessage = commandState.userMessage ?? initialUserMessage
1476
1535
 
1477
1536
  if (commandState.textResponse) {
@@ -1569,14 +1628,27 @@ export async function continueSession(sessionId) {
1569
1628
  throw Object.assign(new Error('Cannot continue: no user message found.'), { statusCode: 400 })
1570
1629
  }
1571
1630
 
1572
- const trimmedMessages = messages.slice(0, lastUserIndex + 1)
1631
+ const lastUserMessage = messages[lastUserIndex]
1632
+ const commandState = await resolveCommandState(session, lastUserMessage)
1633
+ const continuedUserMessage = commandState.userMessage ?? lastUserMessage
1634
+ const trimmedMessages = messages.slice(0, lastUserIndex).concat(continuedUserMessage)
1573
1635
  updateSessionMessages(session, trimmedMessages)
1574
1636
  resetSessionCompaction(session)
1575
1637
 
1576
1638
  resetIdleTimer(session)
1639
+ session.activeCommandName = commandState.commandName ?? null
1640
+ session.activeCommandPermissions = commandState.permissions ?? null
1641
+ session.activeCommandPrompt = commandState.commandPrompt ?? null
1642
+ session.activeCapabilityPrompt = null
1643
+
1577
1644
  session.agent.continue().catch((err) => {
1578
1645
  logger.error(`Agent continue error for session ${sessionId}:`, err, { sessionId })
1579
1646
  emitSessionEvent(session, { type: 'error', error: err.message || 'Unknown error' })
1647
+ }).finally(() => {
1648
+ session.activeCommandName = null
1649
+ session.activeCommandPermissions = null
1650
+ session.activeCommandPrompt = null
1651
+ session.activeCapabilityPrompt = null
1580
1652
  })
1581
1653
 
1582
1654
  return { sessionId, status: 'running' }
@@ -24,6 +24,14 @@ export const commandRestrictedTools = new Set([
24
24
  'run_subagent',
25
25
  ])
26
26
 
27
+ export const planAllowedTools = new Set([
28
+ 'read_file',
29
+ 'grep_files',
30
+ 'activate_skill',
31
+ 'read_skill_resource',
32
+ 'run_subagent',
33
+ ])
34
+
27
35
  export const safeReadTools = new Set([
28
36
  'read_file',
29
37
  'grep_files',
@@ -45,7 +53,11 @@ export const pendingAutoCompactApprovals = new Map()
45
53
 
46
54
  export function commandToolPermissionError(session, toolName) {
47
55
  const permissions = session?.activeCommandPermissions
48
- if (!permissions || !commandRestrictedTools.has(toolName)) return null
56
+ if (!permissions) return null
57
+ if (session?.activeCommandName === 'plan' && !planAllowedTools.has(toolName)) {
58
+ return `Command /plan is read-only and cannot use ${toolName}.`
59
+ }
60
+ if (!commandRestrictedTools.has(toolName)) return null
49
61
  if (toolName === 'run_command' && permissions.allowCommands === false) {
50
62
  return `Command /${session.activeCommandName} does not allow running shell commands.`
51
63
  }
@@ -1,5 +1,6 @@
1
1
  import { readStore } from './storage.mjs'
2
2
  import { compactConversation, saveCompactBackup } from './conversation-compaction.mjs'
3
+ import { estimateContextUsage, shouldCompactContextByPercent } from './context-usage.mjs'
3
4
 
4
5
  export const AUTO_COMPACT_SETTINGS_KEY = 'auto-compact-settings'
5
6
 
@@ -44,14 +45,6 @@ function safeJson(value) {
44
45
  }
45
46
  }
46
47
 
47
- function estimateTextTokens(value) {
48
- const text = String(value || '')
49
- if (!text) return 0
50
- const cjkChars = text.match(/[\u3400-\u9fff\uf900-\ufaff]/g)?.length ?? 0
51
- const otherChars = Math.max(0, text.length - cjkChars)
52
- return Math.ceil(cjkChars + otherChars / 3.5)
53
- }
54
-
55
48
  function contentToText(content) {
56
49
  if (typeof content === 'string') return content
57
50
  if (!Array.isArray(content)) return ''
@@ -65,19 +58,6 @@ function contentToText(content) {
65
58
  }).filter(Boolean).join('\n')
66
59
  }
67
60
 
68
- function estimateMessageTokens(message) {
69
- if (!message || typeof message !== 'object') return 0
70
- const parts = [message.role || '', contentToText(message.content)]
71
- if (message.toolName) parts.push(message.toolName)
72
- if (message.toolCallId) parts.push(message.toolCallId)
73
- if (message.attachments !== undefined) parts.push(safeJson(message.attachments))
74
- return estimateTextTokens(parts.join('\n'))
75
- }
76
-
77
- function estimateMessagesTokens(messages) {
78
- return (Array.isArray(messages) ? messages : []).reduce((total, message) => total + estimateMessageTokens(message), 0)
79
- }
80
-
81
61
  function estimateMessagesChars(messages) {
82
62
  return (Array.isArray(messages) ? messages : []).reduce((total, message) => {
83
63
  if (!message || typeof message !== 'object') return total
@@ -85,50 +65,6 @@ function estimateMessagesChars(messages) {
85
65
  }, 0)
86
66
  }
87
67
 
88
- function messageTimestampMs(message) {
89
- const timestamp = message?.timestamp
90
- if (typeof timestamp === 'number') return timestamp
91
- if (typeof timestamp === 'string') {
92
- const parsed = Date.parse(timestamp)
93
- return Number.isNaN(parsed) ? 0 : parsed
94
- }
95
- return 0
96
- }
97
-
98
- function latestCompactTimestampMs(session) {
99
- return messageTimestampMs(session?.contextCompaction?.summaryMessage)
100
- }
101
-
102
- function latestKnownInputTokens(messages, sinceTimestamp = 0) {
103
- let latestTimestamp = -1
104
- let latestInput = 0
105
- for (const message of Array.isArray(messages) ? messages : []) {
106
- if (message?.role !== 'assistant' || !message.usage) continue
107
- const timestamp = messageTimestampMs(message)
108
- if (sinceTimestamp > 0 && timestamp <= sinceTimestamp) continue
109
- if (timestamp < latestTimestamp) continue
110
- const input = Math.max(0, Number(message.usage.input ?? message.usage.totalTokens) || 0)
111
- if (input <= 0) continue
112
- latestTimestamp = timestamp
113
- latestInput = input
114
- }
115
- return latestInput
116
- }
117
-
118
- export function estimateContextUsage({ systemPrompt, messages, tools, model, knownInputTokens = 0 }) {
119
- const contextWindow = Number(model?.contextWindow) || 0
120
- const reservedOutputTokens = Math.max(0, Number(model?.maxTokens) || 4096)
121
- const estimatedInputTokens =
122
- estimateTextTokens(systemPrompt) +
123
- estimateMessagesTokens(messages) +
124
- estimateTextTokens(safeJson(tools))
125
- const knownInput = Math.max(0, Number(knownInputTokens) || 0)
126
- const inputTokens = Math.max(estimatedInputTokens, knownInput)
127
- const totalTokens = inputTokens + reservedOutputTokens
128
- const percent = contextWindow > 0 ? Math.round((totalTokens / contextWindow) * 1000) / 10 : 0
129
- return { inputTokens, estimatedInputTokens, knownInputTokens: knownInput, reservedOutputTokens, totalTokens, contextWindow, percent }
130
- }
131
-
132
68
  function isUserMessage(message) {
133
69
  return message?.role === 'user' || message?.role === 'user-with-attachments'
134
70
  }
@@ -220,11 +156,29 @@ export function estimateSessionContextUsage(session, messages = session?.agent?.
220
156
  const sourceMessages = Array.isArray(messages) ? messages : []
221
157
  const contextWindow = Number(session.model?.contextWindow) || 0
222
158
  if (sourceMessages.length === 0) {
223
- return { inputTokens: 0, estimatedInputTokens: 0, knownInputTokens: 0, reservedOutputTokens: 0, totalTokens: 0, contextWindow, percent: 0 }
159
+ return {
160
+ inputTokens: 0,
161
+ estimatedInputTokens: 0,
162
+ knownInputTokens: 0,
163
+ inputTokenSource: 'estimated',
164
+ reservedOutputTokens: 0,
165
+ totalTokens: 0,
166
+ contextWindow,
167
+ percent: 0,
168
+ isCompacted: false,
169
+ originalMessageCount: 0,
170
+ effectiveMessageCount: 0,
171
+ breakdown: {
172
+ systemPromptTokens: 0,
173
+ messagesTokens: 0,
174
+ toolsTokens: 0,
175
+ reservedOutputTokens: 0,
176
+ },
177
+ }
224
178
  }
225
179
 
226
- // Cache by input identity. estimateContextUsage() scans every message with a
227
- // tokenizer regex (O(n)) and JSON-stringifies the full tools array, but its
180
+ // Cache by input identity. Context usage delegates message token estimation
181
+ // to pi-agent-core and JSON-stringifies the full tools array, but its
228
182
  // inputs (messages, model, systemPrompt, tools, contextCompaction) are stable
229
183
  // within a run and only change on discrete events (message_end, tool result,
230
184
  // compaction). Reference equality makes the cache check essentially free, so
@@ -255,14 +209,18 @@ export function estimateSessionContextUsage(session, messages = session?.agent?.
255
209
  }
256
210
 
257
211
  const loopMessages = buildAutoCompactLoopMessages(session, sourceMessages)
258
- const knownInputTokens = latestKnownInputTokens(sourceMessages, latestCompactTimestampMs(session))
259
212
  const value = estimateContextUsage({
260
213
  systemPrompt: session.agent.state.systemPrompt,
261
214
  messages: loopMessages,
262
215
  tools: session.agent.state.tools,
263
216
  model: session.model,
264
- knownInputTokens,
265
217
  })
218
+ value.isCompacted = loopMessages !== sourceMessages
219
+ value.originalMessageCount = sourceMessages.length
220
+ value.effectiveMessageCount = loopMessages.length
221
+ if (session.contextCompaction?.summaryMessage) {
222
+ value.compactedUpToIndex = Math.min(sourceMessages.length, Math.max(0, Number(session.contextCompaction.compactedUpToIndex) || 0))
223
+ }
266
224
 
267
225
  session._contextUsageCache = { key: cacheKey, value }
268
226
  return value
@@ -275,16 +233,14 @@ export async function maybeAutoCompactSession({ session, messages, signal, emitS
275
233
  if (signal?.aborted) return { compacted: false, reason: 'aborted' }
276
234
 
277
235
  const loopMessages = buildAutoCompactLoopMessages(session, messages)
278
- const knownInputTokens = latestKnownInputTokens(messages, latestCompactTimestampMs(session))
279
236
  const usage = estimateContextUsage({
280
237
  systemPrompt: session.agent.state.systemPrompt,
281
238
  messages: loopMessages,
282
239
  tools: session.agent.state.tools,
283
240
  model: session.model,
284
- knownInputTokens,
285
241
  })
286
242
  if (!usage.contextWindow) return { compacted: false, usage, reason: 'missing_context_window' }
287
- if (usage.percent < settings.thresholdPercent) return { compacted: false, usage, reason: 'below_threshold' }
243
+ if (!shouldCompactContextByPercent(usage, settings.thresholdPercent)) return { compacted: false, usage, reason: 'below_threshold' }
288
244
  if (shouldSuppressAfterRejection(session, messages, usage)) return { compacted: false, usage, reason: 'user_rejected_recently' }
289
245
 
290
246
  const now = Date.now()
@@ -0,0 +1,108 @@
1
+ import {
2
+ estimateContextTokens,
3
+ estimateTokens,
4
+ shouldCompact,
5
+ } from '@earendil-works/pi-agent-core'
6
+
7
+ function safeJson(value) {
8
+ try {
9
+ return JSON.stringify(value)
10
+ } catch {
11
+ return ''
12
+ }
13
+ }
14
+
15
+ function normalizeMessageForTokenEstimate(message) {
16
+ if (!message || typeof message !== 'object') return message
17
+ if (message.role !== 'user-with-attachments') return message
18
+
19
+ const content = typeof message.content === 'string'
20
+ ? [{ type: 'text', text: message.content }]
21
+ : Array.isArray(message.content)
22
+ ? [...message.content]
23
+ : []
24
+
25
+ if (Array.isArray(message.attachments)) {
26
+ for (const attachment of message.attachments) {
27
+ if (attachment?.type === 'image' && attachment.content) {
28
+ content.push({ type: 'image', data: attachment.content, mimeType: attachment.mimeType })
29
+ } else if (attachment?.type === 'document' && attachment.extractedText) {
30
+ content.push({ type: 'text', text: `\n\n[Document: ${attachment.fileName || 'Untitled'}]\n${attachment.extractedText}` })
31
+ }
32
+ }
33
+ }
34
+
35
+ return { ...message, role: 'user', content }
36
+ }
37
+
38
+ function normalizeMessagesForTokenEstimate(messages) {
39
+ return (Array.isArray(messages) ? messages : []).map(normalizeMessageForTokenEstimate)
40
+ }
41
+
42
+ function textTokens(text) {
43
+ if (!text) return 0
44
+ return estimateTokens({ role: 'user', content: String(text), timestamp: 0 })
45
+ }
46
+
47
+ function localMessagesTokens(messages) {
48
+ return normalizeMessagesForTokenEstimate(messages).reduce((total, message) => total + estimateTokens(message), 0)
49
+ }
50
+
51
+ export function estimateContextUsage({ systemPrompt, messages, tools, model }) {
52
+ const contextWindow = Number(model?.contextWindow) || 0
53
+ const reservedOutputTokens = Math.max(0, Number(model?.maxTokens) || 4096)
54
+ const normalizedMessages = normalizeMessagesForTokenEstimate(messages)
55
+ const coreEstimate = estimateContextTokens(normalizedMessages)
56
+ const systemPromptTokens = textTokens(systemPrompt)
57
+ const toolsTokens = textTokens(safeJson(tools))
58
+ const messagesTokens = localMessagesTokens(normalizedMessages)
59
+ const estimatedInputTokens = systemPromptTokens + messagesTokens + toolsTokens
60
+ const providerBasedContextTokens = Math.max(0, Number(coreEstimate.usageTokens) || 0) > 0
61
+ ? Math.max(0, Number(coreEstimate.tokens) || 0)
62
+ : 0
63
+ const inputTokens = providerBasedContextTokens > 0
64
+ ? Math.max(estimatedInputTokens, providerBasedContextTokens)
65
+ : estimatedInputTokens
66
+ const totalTokens = inputTokens + reservedOutputTokens
67
+ const percent = contextWindow > 0 ? Math.round((totalTokens / contextWindow) * 1000) / 10 : 0
68
+ const inputTokenSource = providerBasedContextTokens > 0
69
+ ? providerBasedContextTokens >= estimatedInputTokens ? 'provider' : 'mixed'
70
+ : 'estimated'
71
+
72
+ return {
73
+ inputTokens,
74
+ estimatedInputTokens,
75
+ knownInputTokens: providerBasedContextTokens,
76
+ providerContextTokens: providerBasedContextTokens,
77
+ inputTokenSource,
78
+ reservedOutputTokens,
79
+ totalTokens,
80
+ contextWindow,
81
+ percent,
82
+ breakdown: {
83
+ systemPromptTokens,
84
+ messagesTokens,
85
+ toolsTokens,
86
+ reservedOutputTokens,
87
+ providerUsageTokens: Math.max(0, Number(coreEstimate.usageTokens) || 0),
88
+ trailingTokens: Math.max(0, Number(coreEstimate.trailingTokens) || 0),
89
+ lastUsageIndex: coreEstimate.lastUsageIndex,
90
+ localEstimatedContextTokens: estimatedInputTokens,
91
+ },
92
+ }
93
+ }
94
+
95
+ export function shouldCompactContextByPercent(usage, thresholdPercent) {
96
+ const contextWindow = Number(usage?.contextWindow) || 0
97
+ const totalTokens = Math.max(0, Number(usage?.totalTokens) || 0)
98
+ const threshold = Math.min(100, Math.max(0, Number(thresholdPercent) || 0))
99
+ if (!contextWindow) return false
100
+
101
+ const thresholdTokens = Math.ceil(contextWindow * threshold / 100)
102
+ const reserveTokens = Math.min(contextWindow, Math.max(0, contextWindow - thresholdTokens + 1))
103
+ return shouldCompact(totalTokens, contextWindow, {
104
+ enabled: true,
105
+ reserveTokens,
106
+ keepRecentTokens: 0,
107
+ })
108
+ }