@luuthuong/devboard 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.claude/skills/devboard/SKILL.md +111 -0
  2. package/bin/devboard.mjs +59 -0
  3. package/dist/assets/CardDetailView-CVWOsYyA.js +24 -0
  4. package/dist/assets/DiffPane-CpoDDQrA.css +1 -0
  5. package/dist/assets/DiffPane-bQsGoT7y.js +22 -0
  6. package/dist/assets/KnowledgeView-C5KHiBRG.js +3 -0
  7. package/dist/assets/ListBoardView-CzJcCLXS.js +3 -0
  8. package/dist/assets/NewTaskDialog-BuxvfgNU.js +1 -0
  9. package/dist/assets/OnboardingWizard-8WLFjTd8.js +1 -0
  10. package/dist/assets/ProcessManagerView-BIYjQpoP.js +1 -0
  11. package/dist/assets/ProposalCard-BbPobl1f.js +1 -0
  12. package/dist/assets/SearchOverlay-B4aywpIh.js +1 -0
  13. package/dist/assets/SettingsView-spBB3EwB.js +1 -0
  14. package/dist/assets/SetupProjectView-BuUDqDYU.js +1 -0
  15. package/dist/assets/Surface-BQKeigoq.js +1 -0
  16. package/dist/assets/TaskDescriptionEditor-Cv0nlU3w.js +58 -0
  17. package/dist/assets/Terminal-BrP-ENHg.css +1 -0
  18. package/dist/assets/Terminal-DJJcg-t6.js +80 -0
  19. package/dist/assets/chevron-right-BsVThPz4.js +1 -0
  20. package/dist/assets/index-DNKB6XwG.css +1 -0
  21. package/dist/assets/index-gWSenOqD.js +18 -0
  22. package/dist/assets/jsx-runtime-QmcfnBgR.js +1 -0
  23. package/dist/assets/plus-XSPxArj5.js +1 -0
  24. package/dist/assets/react-dom-DjtJUrx3.js +1 -0
  25. package/dist/assets/x-CdEDbLMR.js +1 -0
  26. package/dist/index.html +22 -0
  27. package/package.json +81 -4
  28. package/server-dist/server/assets/remote-shell.sh +239 -0
  29. package/server-dist/server/board.js +486 -0
  30. package/server-dist/server/commands.js +424 -0
  31. package/server-dist/server/context.js +34 -0
  32. package/server-dist/server/coordination.js +442 -0
  33. package/server-dist/server/editor.js +101 -0
  34. package/server-dist/server/events.js +17 -0
  35. package/server-dist/server/git.js +258 -0
  36. package/server-dist/server/harness/config.js +90 -0
  37. package/server-dist/server/harness/entry.js +98 -0
  38. package/server-dist/server/harness/intents.js +268 -0
  39. package/server-dist/server/harness/kbIndex.js +291 -0
  40. package/server-dist/server/harness/kbStore.js +258 -0
  41. package/server-dist/server/harness/librarian.js +147 -0
  42. package/server-dist/server/harness/pack.js +182 -0
  43. package/server-dist/server/harness/proposals.js +113 -0
  44. package/server-dist/server/harness/repo.js +150 -0
  45. package/server-dist/server/harness/scrub.js +30 -0
  46. package/server-dist/server/harness/service.js +683 -0
  47. package/server-dist/server/harness/template/README.md +38 -0
  48. package/server-dist/server/harness/template/agents/code-reviewer.md +23 -0
  49. package/server-dist/server/harness/template/agents/security-reviewer.md +20 -0
  50. package/server-dist/server/harness/template/agents/test-writer.md +16 -0
  51. package/server-dist/server/harness/template/agents.yaml +41 -0
  52. package/server-dist/server/harness/template/harness.json +11 -0
  53. package/server-dist/server/harness/template/kb/team/README.md +4 -0
  54. package/server-dist/server/harness/template/skills/aidlc-design/SKILL.md +41 -0
  55. package/server-dist/server/harness/template/skills/aidlc-implement/SKILL.md +34 -0
  56. package/server-dist/server/harness/template/skills/aidlc-postmortem/SKILL.md +35 -0
  57. package/server-dist/server/harness/template/skills/aidlc-pr/SKILL.md +22 -0
  58. package/server-dist/server/harness/template/skills/aidlc-requirements/SKILL.md +40 -0
  59. package/server-dist/server/harness/template/skills/aidlc-units/SKILL.md +36 -0
  60. package/server-dist/server/harness/template/skills/project-kb/SKILL.md +41 -0
  61. package/server-dist/server/harness/usage.js +82 -0
  62. package/server-dist/server/hooks.js +222 -0
  63. package/server-dist/server/http.js +266 -0
  64. package/server-dist/server/index.js +77 -0
  65. package/server-dist/server/integrations.js +292 -0
  66. package/server-dist/server/pollers.js +332 -0
  67. package/server-dist/server/processes.js +123 -0
  68. package/server-dist/server/psmux.js +120 -0
  69. package/server-dist/server/pty.js +93 -0
  70. package/server-dist/server/remote.js +216 -0
  71. package/server-dist/server/sessionReaper.js +101 -0
  72. package/server-dist/server/sessions.js +315 -0
  73. package/server-dist/server/settings.js +125 -0
  74. package/server-dist/server/skill.js +29 -0
  75. package/server-dist/server/tmux.js +217 -0
  76. package/server-dist/server/transcripts.js +335 -0
  77. package/server-dist/server/util.js +286 -0
  78. package/server-dist/src/types/index.js +53 -0
  79. package/README.md +0 -3
@@ -0,0 +1 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t,t.exports),e=null),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,o)=>(o=n==null?{}:e(i(n)),c(r||!n||!n.__esModule||!a.call(n,`default`)?t(o,`default`,{value:n,enumerable:!0}):o,n)),u=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.for(`react.view_transition`),m=Symbol.iterator;function h(e){return typeof e!=`object`||!e?null:(e=m&&e[m]||e[`@@iterator`],typeof e==`function`?e:null)}var g={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},_=Object.assign,v={};function y(e,t,n){this.props=e,this.context=t,this.refs=v,this.updater=n||g}y.prototype.isReactComponent={},y.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},y.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function b(){}b.prototype=y.prototype;function x(e,t,n){this.props=e,this.context=t,this.refs=v,this.updater=n||g}var S=x.prototype=new b;S.constructor=x,_(S,y.prototype),S.isPureReactComponent=!0;var C=Array.isArray;function w(){}var T={H:null,A:null,T:null,S:null},E=Object.prototype.hasOwnProperty;function D(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function O(e,t){return D(e.type,t,e.props)}function k(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function A(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var j=/\/+/g;function M(e,t){return typeof e==`object`&&e&&e.key!=null?A(``+e.key):t.toString(36)}function N(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(w,w):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function P(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,P(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+M(e,0):a,C(o)?(i=``,c!=null&&(i=c.replace(j,`$&/`)+`/`),P(o,r,i,``,function(e){return e})):o!=null&&(k(o)&&(o=O(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(j,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(C(e))for(var u=0;u<e.length;u++)a=e[u],s=l+M(a,u),c+=P(a,r,i,s,o);else if(u=h(e),typeof u==`function`)for(e=u.call(e),u=0;!(a=e.next()).done;)a=a.value,s=l+M(a,u++),c+=P(a,r,i,s,o);else if(s===`object`){if(typeof e.then==`function`)return P(N(e),r,i,a,o);throw r=String(e),Error(`Objects are not valid as a React child (found: `+(r===`[object Object]`?`object with keys {`+Object.keys(e).join(`, `)+`}`:r)+`). If you meant to render a collection of children, use an array instead.`)}return c}function F(e,t,n){if(e==null)return e;var r=[],i=0;return P(e,r,``,``,function(e){return t.call(n,e,i++)}),r}function I(e){if(e._status===-1){var t=e._result,n=t();n.then(function(t){(e._status===0||e._status===-1)&&(e._status=1,e._result=t,n.status===void 0&&(n.status=`fulfilled`,n.value=t))},function(t){(e._status===0||e._status===-1)&&(e._status=2,e._result=t,n.status===void 0&&(n.status=`rejected`,n.reason=t))}),e._status===-1&&(e._status=0,e._result=n)}if(e._status===1)return e._result.default;throw e._result}var L=typeof reportError==`function`?reportError:function(e){if(typeof window==`object`&&typeof window.ErrorEvent==`function`){var t=new window.ErrorEvent(`error`,{bubbles:!0,cancelable:!0,message:typeof e==`object`&&e&&typeof e.message==`string`?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if(typeof process==`object`&&typeof process.emit==`function`){process.emit(`uncaughtException`,e);return}console.error(e)};function R(e){var t=T.T,n={};n.types=t===null?null:t.types,T.T=n;try{var r=e(),i=T.S;i!==null&&i(n,r),typeof r==`object`&&r&&typeof r.then==`function`&&r.then(w,L)}catch(e){L(e)}finally{t!==null&&n.types!==null&&(t.types=n.types),T.T=t}}function z(e){var t=T.T;if(t!==null){var n=t.types;n===null?t.types=[e]:n.indexOf(e)===-1&&n.push(e)}else R(z.bind(null,e))}var B={map:F,forEach:function(e,t,n){F(e,function(){t.apply(this,arguments)},n)},count:function(e){var t=0;return F(e,function(){t++}),t},toArray:function(e){return F(e,function(e){return e})||[]},only:function(e){if(!k(e))throw Error(`React.Children.only expected to receive a single React element child.`);return e}};e.Activity=f,e.Children=B,e.Component=y,e.Fragment=r,e.Profiler=a,e.PureComponent=x,e.StrictMode=i,e.Suspense=l,e.ViewTransition=p,e.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=T,e.__COMPILER_RUNTIME={__proto__:null,c:function(e){return T.H.useMemoCache(e)}},e.addTransitionType=z,e.cache=function(e){return function(){return e.apply(null,arguments)}},e.cacheSignal=function(){return null},e.cloneElement=function(e,t,n){if(e==null)throw Error(`The argument must be a React element, but you passed `+e+`.`);var r=_({},e.props),i=e.key;if(t!=null)for(a in t.key!==void 0&&(i=``+t.key),t)!E.call(t,a)||a===`key`||a===`__self`||a===`__source`||a===`ref`&&t.ref===void 0||(r[a]=t[a]);var a=arguments.length-2;if(a===1)r.children=n;else if(1<a){for(var o=Array(a),s=0;s<a;s++)o[s]=arguments[s+2];r.children=o}return D(e.type,i,r)},e.createContext=function(e){return e={$$typeof:s,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null},e.Provider=e,e.Consumer={$$typeof:o,_context:e},e},e.createElement=function(e,t,n){var r,i={},a=null;if(t!=null)for(r in t.key!==void 0&&(a=``+t.key),t)E.call(t,r)&&r!==`key`&&r!==`__self`&&r!==`__source`&&(i[r]=t[r]);var o=arguments.length-2;if(o===1)i.children=n;else if(1<o){for(var s=Array(o),c=0;c<o;c++)s[c]=arguments[c+2];i.children=s}if(e&&e.defaultProps)for(r in o=e.defaultProps,o)i[r]===void 0&&(i[r]=o[r]);return D(e,a,i)},e.createRef=function(){return{current:null}},e.forwardRef=function(e){return{$$typeof:c,render:e}},e.isValidElement=k,e.lazy=function(e){return{$$typeof:d,_payload:{_status:-1,_result:e},_init:I}},e.memo=function(e,t){return{$$typeof:u,type:e,compare:t===void 0?null:t}},e.startTransition=R,e.unstable_useCacheRefresh=function(){return T.H.useCacheRefresh()},e.use=function(e){return T.H.use(e)},e.useActionState=function(e,t,n){return T.H.useActionState(e,t,n)},e.useCallback=function(e,t){return T.H.useCallback(e,t)},e.useContext=function(e){return T.H.useContext(e)},e.useDebugValue=function(){},e.useDeferredValue=function(e,t){return T.H.useDeferredValue(e,t)},e.useEffect=function(e,t){return T.H.useEffect(e,t)},e.useEffectEvent=function(e){return T.H.useEffectEvent(e)},e.useId=function(){return T.H.useId()},e.useImperativeHandle=function(e,t,n){return T.H.useImperativeHandle(e,t,n)},e.useInsertionEffect=function(e,t){return T.H.useInsertionEffect(e,t)},e.useLayoutEffect=function(e,t){return T.H.useLayoutEffect(e,t)},e.useMemo=function(e,t){return T.H.useMemo(e,t)},e.useOptimistic=function(e,t){return T.H.useOptimistic(e,t)},e.useReducer=function(e,t,n){return T.H.useReducer(e,t,n)},e.useRef=function(e){return T.H.useRef(e)},e.useState=function(e){return T.H.useState(e)},e.useSyncExternalStore=function(e,t,n){return T.H.useSyncExternalStore(e,t,n)},e.useTransition=function(){return T.H.useTransition()},e.version=`19.3.1`})),d=o(((e,t)=>{t.exports=u()})),f=o((e=>{var t=d().__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;e.c=function(e){return t.H.useMemoCache(e)}})),p=o(((e,t)=>{t.exports=f()}));async function m(e,t={}){let n;try{n=await fetch(`/api/invoke/${encodeURIComponent(e)}`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(t)})}catch(e){throw`cannot reach the DevBoard server (${String(e)})`}let r=null;try{r=await n.json()}catch{}if(!n.ok)throw r?.error??`${e} failed (HTTP ${n.status})`;return r?.result??null}var h=new Map,g=new Set,_=null,v=500,y=!1;function b(e){return`${location.protocol===`https:`?`wss:`:`ws:`}//${location.host}${e}`}function x(){if(_)return;let e=new WebSocket(b(`/ws`));_=e,e.onopen=()=>{v=500,y&&g.forEach(e=>e()),y=!0},e.onmessage=e=>{try{let{event:t,payload:n}=JSON.parse(e.data);h.get(t)?.forEach(e=>e({payload:n}))}catch{}},e.onclose=()=>{_=null,setTimeout(x,v),v=Math.min(v*2,1e4)},e.onerror=()=>e.close()}async function S(e,t){x();let n=h.get(e);return n||h.set(e,n=new Set),n.add(t),()=>{n.delete(t)}}function C(e){return x(),g.add(e),()=>{g.delete(e)}}function w(e,t,n){let r=new WebSocket(b(`/pty`)),i=[],a=[],o=[],s=!1,c=e=>{let t=JSON.stringify(e);r.readyState===WebSocket.OPEN?r.send(t):r.readyState===WebSocket.CONNECTING&&o.push(t)};r.onopen=()=>{r.send(JSON.stringify({type:`spawn`,file:e,args:t,cols:n.cols,rows:n.rows,cwd:n.cwd})),o.splice(0).forEach(e=>r.send(e))};let l=e=>{s||(s=!0,a.forEach(t=>t(e)))};return r.onmessage=e=>{let t;try{t=JSON.parse(e.data)}catch{return}t.type===`data`?i.forEach(e=>e(t.data)):t.type===`exit`?l({exitCode:t.exitCode,signal:t.signal}):t.type===`error`&&(i.forEach(e=>e(`\r\n\x1b[31m${t.message}\x1b[0m\r\n`)),l({exitCode:-1}))},r.onclose=()=>l({exitCode:-1}),{onData:e=>void i.push(e),onExit:e=>void a.push(e),write:e=>c({type:`data`,data:e}),resize:(e,t)=>c({type:`resize`,cols:e,rows:t}),kill:()=>{r.readyState===WebSocket.OPEN?(r.send(JSON.stringify({type:`kill`})),r.close()):r.readyState===WebSocket.CONNECTING&&(r.onopen=()=>r.close())}}}var T=e=>`/api/file?path=${encodeURIComponent(e)}`;async function E(e,t){let n=await fetch(e,{method:`POST`,body:t}),r=await n.json().catch(()=>null);if(!n.ok)throw r?.error??`upload failed (HTTP ${n.status})`;return r.path}var D=e=>E(`/api/upload/image`,e),O=e=>E(`/api/upload/file?name=${encodeURIComponent(e.name)}`,e);function k(){if(S(`notification`,({payload:e})=>{typeof Notification<`u`&&Notification.permission===`granted`&&new Notification(e.title,{body:e.body,tag:e.tag})}),typeof Notification<`u`&&Notification.permission==="default"){let e=()=>{Notification.requestPermission(),window.removeEventListener(`pointerdown`,e)};window.addEventListener(`pointerdown`,e,{once:!0})}}var A=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),j=o(((e,t)=>{t.exports=A()}));export{S as a,O as c,d,o as f,m as i,D as l,l as m,T as n,C as o,s as p,k as r,w as s,j as t,p as u};
@@ -0,0 +1 @@
1
+ import{yt as e}from"./index-gWSenOqD.js";var t={name:`plus`,size:24,node:[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]]};t.node;var n=e(t);export{n as t};
@@ -0,0 +1 @@
1
+ import{d as e,f as t}from"./jsx-runtime-QmcfnBgR.js";var n=t((t=>{var n=e();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`),s=Symbol.for(`react.recoverable`),c=Symbol.for(`react.optimistic_key`);function l(e,t,n){var r=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:o,key:r==null?null:r===c?c:``+r,children:e,containerInfo:t,implementation:n}}var u=n.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function d(e,t){if(e===`font`)return``;if(typeof t==`string`)return t===`use-credentials`?t:``}t.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=a,t.browser=function(e){return{$$typeof:s,_reason:e}},t.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 l(e,t,null,n)},t.flushSync=function(e){var t=u.T,n=a.p;try{if(u.T=null,a.p=2,e)return e()}finally{u.T=t,a.p=n,a.d.f()}},t.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))},t.prefetchDNS=function(e){typeof e==`string`&&a.d.D(e)},t.preinit=function(e,t){if(typeof e==`string`&&t&&typeof t.as==`string`){var n=t.as,r=d(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})}},t.preinitModule=function(e,t){if(typeof e==`string`){if(typeof t==`object`&&t){if(t.as==null||t.as===`script`){var n=d(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,fetchPriority:typeof t.fetchPriority==`string`?t.fetchPriority:void 0})}}else t??a.d.M(e)}},t.preload=function(e,t){if(typeof e==`string`&&typeof t==`object`&&t&&typeof t.as==`string`){var n=t.as,r=d(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})}},t.preloadModule=function(e,t){if(typeof e==`string`){if(t){var n=d(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,nonce:typeof t.nonce==`string`?t.nonce:void 0,fetchPriority:typeof t.fetchPriority==`string`?t.fetchPriority:void 0})}else a.d.m(e)}},t.requestFormReset=function(e){a.d.r(e)},t.unstable_batchedUpdates=function(e,t){return e(t)},t.useFormState=function(e,t,n){return u.H.useFormState(e,t,n)},t.useFormStatus=function(){return u.H.useHostTransitionStatus()},t.version=`19.3.1`})),r=t(((e,t)=>{function r(){if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<`u`&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE==`function`)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(r)}catch(e){console.error(e)}}r(),t.exports=n()}));export{r as t};
@@ -0,0 +1 @@
1
+ import{yt as e}from"./index-gWSenOqD.js";var t={name:`x`,size:24,node:[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]};t.node;var n=e(t);export{n as t};
@@ -0,0 +1,22 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
7
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
8
+ <link
9
+ href="https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:wght@400;500;600&display=swap"
10
+ rel="stylesheet"
11
+ />
12
+ <title>DevBoard</title>
13
+ <script type="module" crossorigin src="/assets/index-gWSenOqD.js"></script>
14
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-QmcfnBgR.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/react-dom-DjtJUrx3.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/Surface-BQKeigoq.js">
17
+ <link rel="stylesheet" crossorigin href="/assets/index-DNKB6XwG.css">
18
+ </head>
19
+ <body>
20
+ <div id="root"></div>
21
+ </body>
22
+ </html>
package/package.json CHANGED
@@ -1,6 +1,83 @@
1
1
  {
2
2
  "name": "@luuthuong/devboard",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "DevBoard — a local devboard board for orchestrating Claude Code sessions",
5
+ "keywords": [
6
+ "agents",
7
+ "claude-code",
8
+ "devboard",
9
+ "kanban",
10
+ "tmux"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/luuthuong/devboard.git"
15
+ },
16
+ "bin": {
17
+ "devboard": "./bin/devboard.mjs"
18
+ },
19
+ "files": [
20
+ "bin",
21
+ "dist",
22
+ "server-dist",
23
+ ".claude"
24
+ ],
25
+ "type": "module",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "dev": "concurrently -k -n server,ui -c blue,green \"bun:dev:server\" \"bun:dev:ui\"",
31
+ "dev:server": "tsx watch server/index.ts",
32
+ "dev:ui": "vite",
33
+ "build": "tsc && vite build",
34
+ "start": "tsx server/index.ts",
35
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.server.json",
36
+ "test": "tsx --test --test-reporter=spec 'server/**/*.test.ts'",
37
+ "preview": "vite preview",
38
+ "prepack": "node scripts/prepack.mjs",
39
+ "build:server": "node scripts/build-server.mjs"
40
+ },
41
+ "dependencies": {
42
+ "@lydell/node-pty": "^1.2.0-beta.15",
43
+ "ws": "^8.22.0",
44
+ "yaml": "^2.9.1"
45
+ },
46
+ "devDependencies": {
47
+ "@devboard/ui": "0.1.0",
48
+ "@dnd-kit/core": "^6.3.1",
49
+ "@dnd-kit/sortable": "^10.0.0",
50
+ "@dnd-kit/utilities": "^3.2.2",
51
+ "@git-diff-view/react": "^0.1.8",
52
+ "@milkdown/kit": "^7.22.3",
53
+ "@milkdown/react": "^7.22.3",
54
+ "@tailwindcss/vite": "^4.3.3",
55
+ "@tanstack/react-virtual": "^3.14.22",
56
+ "@types/node": "^26.6.5",
57
+ "@types/react": "^19.3.0",
58
+ "@types/react-dom": "^19.3.0",
59
+ "@types/ws": "^8.18.2",
60
+ "@vitejs/plugin-react": "^6.1.2",
61
+ "@xterm/addon-fit": "^0.11.0",
62
+ "@xterm/addon-web-links": "^0.12.0",
63
+ "@xterm/addon-webgl": "^0.19.0",
64
+ "@xterm/xterm": "^6.0.0",
65
+ "concurrently": "^10.0.6",
66
+ "fuse.js": "^7.5.0",
67
+ "lowlight": "^3.3.0",
68
+ "lucide-react": "^1.61.0",
69
+ "oxc-transform-react": "^0.155.0",
70
+ "react": "^19.3.1",
71
+ "react-dom": "^19.3.1",
72
+ "react-markdown": "^10.1.0",
73
+ "remark-gfm": "^4.0.1",
74
+ "tailwindcss": "^4.3.3",
75
+ "tsx": "^4.23.15",
76
+ "typescript": "^7.0.2",
77
+ "vite": "^8.3.5",
78
+ "zustand": "^5.0.16"
79
+ },
80
+ "engines": {
81
+ "node": ">=24"
82
+ }
83
+ }
@@ -0,0 +1,239 @@
1
+ #!/bin/bash
2
+ #
3
+ # Remote shell wrapper for Claude Code
4
+ # Intercepts shell commands and executes them on the remote machine
5
+ # Falls back to local execution if remote is unavailable
6
+ #
7
+ # Windows port: requires Git for Windows (provides /bin/bash, /usr/bin/perl,
8
+ # /usr/bin/ssh, /usr/bin/sed). Configuration read from settings.json at the
9
+ # templated absolute POSIX path below. Online/offline notifications are
10
+ # delivered by writing JSON status files — the devboard server polls these and
11
+ # raises a notification.
12
+ #
13
+
14
+ # --- Recursion guard ---
15
+ if [[ -n "${__DEVBOARD_REMOTE_WRAPPER:-}" ]]; then
16
+ exec /bin/bash "$@"
17
+ fi
18
+ export __DEVBOARD_REMOTE_WRAPPER=1
19
+
20
+ # --- Hook/script fast-path ---
21
+ for __arg in "$@"; do
22
+ if [[ "$__arg" == "-c" ]] || [[ "$__arg" == "-l" ]] || [[ "$__arg" == "-i" ]]; then
23
+ continue
24
+ fi
25
+ __first_word="${__arg%% *}"
26
+ if [[ "$__first_word" == /* ]] && [[ -x "$__first_word" ]]; then
27
+ exec /bin/bash "$@"
28
+ fi
29
+ break
30
+ done
31
+ unset __arg __first_word
32
+
33
+ CONFIG_FILE="@@CONFIG_FILE@@"
34
+ STATUS_DIR="@@STATUS_DIR@@"
35
+ REMOTE_HOST=""
36
+ REMOTE_DIR=""
37
+ LOCAL_MOUNT=""
38
+
39
+ if [[ -f "$CONFIG_FILE" ]]; then
40
+ REMOTE_HOST=$(/usr/bin/perl -MJSON::PP -e 'open my $f,"<","'"$CONFIG_FILE"'" or exit;local $/;my $d=decode_json(<$f>);print $d->{remote}{host}//"" if $d->{remote}' 2>/dev/null || echo "")
41
+ REMOTE_DIR=$(/usr/bin/perl -MJSON::PP -e 'open my $f,"<","'"$CONFIG_FILE"'" or exit;local $/;my $d=decode_json(<$f>);print $d->{remote}{remotePath}//"" if $d->{remote}' 2>/dev/null || echo "")
42
+ LOCAL_MOUNT=$(/usr/bin/perl -MJSON::PP -e 'open my $f,"<","'"$CONFIG_FILE"'" or exit;local $/;my $d=decode_json(<$f>);print $d->{remote}{localPath}//"" if $d->{remote}' 2>/dev/null || echo "")
43
+ fi
44
+
45
+ # ControlMaster sockets in TMPDIR — Git-for-Windows bash maps that to a path
46
+ # native ssh.exe can also read. Falls back to /tmp on POSIX.
47
+ SSH_TMP="${TMPDIR:-/tmp}"
48
+ SSH_TMP="${SSH_TMP%/}"
49
+ SSH_OPTS="-o ControlMaster=auto -o ControlPath=${SSH_TMP}/ssh-devboard-%r@%h:%p -o ControlPersist=600 -o ConnectTimeout=5"
50
+ STATE_FILE="${SSH_TMP}/devboard-remote-state"
51
+ NOTIFY_COOLDOWN=300
52
+ MUTAGEN=$(command -v mutagen 2>/dev/null || echo "")
53
+
54
+ ensure_sync() {
55
+ [[ -n "$MUTAGEN" ]] || return 0
56
+ if ! "$MUTAGEN" sync list --label-selector devboard=true 2>/dev/null | grep -q "Name:"; then
57
+ "$MUTAGEN" sync create "$LOCAL_MOUNT" "${REMOTE_HOST}:${REMOTE_DIR}" \
58
+ --name devboard-sync \
59
+ --label devboard=true \
60
+ --sync-mode two-way-resolved \
61
+ --default-file-mode-beta 0644 \
62
+ --default-directory-mode-beta 0755 \
63
+ --ignore node_modules --ignore .venv --ignore .cache \
64
+ --ignore dist --ignore '.next*' --ignore __pycache__ \
65
+ --ignore .pytest_cache --ignore .mypy_cache --ignore .turbo \
66
+ --ignore '*.pyc' --ignore .DS_Store --ignore coverage \
67
+ --ignore .nyc_output --ignore target --ignore build \
68
+ --ignore .build --ignore .swiftpm \
69
+ >/dev/null 2>&1 || true
70
+ fi
71
+ "$MUTAGEN" sync flush --label-selector devboard=true >/dev/null 2>&1 || true
72
+ }
73
+
74
+ read -r -d '' WORKTREE_FIX_FN << 'WFIX' || true
75
+ __relpath(){
76
+ local t="$1" b="$2"; t="${t%/}"; b="${b%/}"
77
+ local c="$b" r=""
78
+ while [ "${t#"$c"}" = "$t" ]; do c=$(dirname "$c"); r="../$r"; done
79
+ local f="${t#"$c"}"; f="${f#/}"; printf '%s\n' "${r}${f}"
80
+ }
81
+ __fix_gitlink(){
82
+ local f="$1" wp="$2" rp="$3"
83
+ [ -f "$f" ] || return 0
84
+ local c; c=$(cat "$f"); local ig=false p="$c"
85
+ case "$c" in gitdir:*) ig=true; p="${c#gitdir: }";; esac
86
+ p="${p//$wp/$rp}"
87
+ case "$p" in /*) ;; *) return 0;; esac
88
+ [ -e "$p" ] || return 0
89
+ local d; d=$(dirname "$f")
90
+ local rl; rl=$(__relpath "$p" "$d")
91
+ [ -n "$rl" ] || return 0
92
+ if $ig; then printf 'gitdir: %s\n' "$rl" > "$f"; else printf '%s\n' "$rl" > "$f"; fi
93
+ }
94
+ __fix_wt(){
95
+ local wp="$1" rp="$2" d; d=$(pwd); local gr=""
96
+ while [ "$d" != "/" ]; do
97
+ if [ -d "$d/.git" ]; then gr="$d"; break
98
+ elif [ -f "$d/.git" ]; then
99
+ __fix_gitlink "$d/.git" "$wp" "$rp"
100
+ local g; g=$(cat "$d/.git"); g="${g#gitdir: }"
101
+ case "$g" in /*) gr="${g%/.git/worktrees/*}";; *) gr=$(cd "$d/$g/../../.." 2>/dev/null && pwd);; esac
102
+ break
103
+ fi
104
+ d=$(dirname "$d")
105
+ done
106
+ [ -n "$gr" ] && [ -d "$gr/.git/worktrees" ] || return 0
107
+ for m in "$gr/.git/worktrees"/*/; do
108
+ [ -d "$m" ] || continue
109
+ __fix_gitlink "${m}gitdir" "$wp" "$rp"
110
+ local gc; [ -f "${m}gitdir" ] && gc=$(cat "${m}gitdir") || continue
111
+ [ -n "$gc" ] || continue
112
+ local wf
113
+ case "$gc" in /*) wf="$gc";;
114
+ *) wf=$(cd "$m" && cd "$(dirname "$gc")" 2>/dev/null && printf '%s/%s\n' "$(pwd)" "$(basename "$gc")") || continue;;
115
+ esac
116
+ [ -n "$wf" ] && __fix_gitlink "$wf" "$wp" "$rp"
117
+ done
118
+ }
119
+ WFIX
120
+
121
+ local_to_remote() { echo "${1/#$LOCAL_MOUNT/$REMOTE_DIR}"; }
122
+ remote_to_local() { echo "${1/#$REMOTE_DIR/$LOCAL_MOUNT}"; }
123
+
124
+ # Notification = write a status JSON file. The devboard server polls
125
+ # these and fires the notification. Rate-limited identically to the macOS path.
126
+ notify() {
127
+ local message="$1" state="$2"
128
+ local now=$(date +%s) last_state="" last_notify=0
129
+ if [[ -f "$STATE_FILE" ]]; then
130
+ last_state=$(head -1 "$STATE_FILE")
131
+ last_notify=$(tail -1 "$STATE_FILE")
132
+ fi
133
+ if [[ "$state" != "$last_state" ]] || { [[ "$state" == "offline" ]] && [[ $((now - last_notify)) -ge $NOTIFY_COOLDOWN ]]; }; then
134
+ mkdir -p "$STATUS_DIR" 2>/dev/null
135
+ local iso=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
136
+ local safe_host="${REMOTE_HOST//\//_}"
137
+ safe_host="${safe_host//:/_}"
138
+ printf '{"status":"%s","since":"%s","message":"%s"}\n' "$state" "$iso" "$message" \
139
+ > "$STATUS_DIR/status-${safe_host}.json"
140
+ echo -e "$state\n$now" > "$STATE_FILE"
141
+ fi
142
+ }
143
+
144
+ run_with_timeout() {
145
+ local secs="$1"; shift
146
+ /usr/bin/perl -e 'alarm shift @ARGV; exec @ARGV' "$secs" "$@"
147
+ }
148
+
149
+ is_remote_available() {
150
+ local socket="${SSH_TMP}/ssh-devboard-${REMOTE_HOST}:22"
151
+ if [[ -S "$socket" ]]; then
152
+ if ! run_with_timeout 1 /usr/bin/ssh -o ControlPath="$socket" -O check "$REMOTE_HOST" 2>/dev/null; then
153
+ /bin/rm -f "$socket" 2>/dev/null
154
+ fi
155
+ fi
156
+ run_with_timeout 5 /usr/bin/ssh -o ConnectTimeout=5 -o BatchMode=yes "$REMOTE_HOST" "exit 0" 2>/dev/null
157
+ }
158
+
159
+ while [[ $# -gt 0 ]]; do
160
+ case "$1" in
161
+ -c) shift ;;
162
+ -l|-i) shift ;;
163
+ *) cmd="$1"; break ;;
164
+ esac
165
+ done
166
+
167
+ if [[ -n "${cmd:-}" ]]; then
168
+ pwd_file=""
169
+ if [[ "$cmd" =~ (.*)(\&\&\ pwd\ -P\ \>\|\ ([^[:space:]]+))$ ]]; then
170
+ cmd="${BASH_REMATCH[1]}"
171
+ pwd_file="${BASH_REMATCH[3]}"
172
+ fi
173
+
174
+ LOCAL_CWD="$(pwd -P)"
175
+
176
+ if [[ -z "$REMOTE_HOST" ]] || [[ -z "$REMOTE_DIR" ]] || [[ -z "$LOCAL_MOUNT" ]]; then
177
+ /bin/bash -c "$cmd"
178
+ exit_code=$?
179
+ [[ -n "$pwd_file" ]] && pwd -P > "$pwd_file"
180
+ exit $exit_code
181
+ fi
182
+
183
+ if is_remote_available; then
184
+ notify "Remote instance available" "online"
185
+ REMOTE_CWD="$(local_to_remote "$LOCAL_CWD")"
186
+ cmd="${cmd//$LOCAL_MOUNT/$REMOTE_DIR}"
187
+ cmd=$(echo "$cmd" | /usr/bin/sed -E 's|/var/folders/[^[:space:];]+\.tmp|true|g')
188
+ ensure_sync
189
+ MARKER="__DEVBOARD_REMOTE_PWD__"
190
+ remote_cmd="${WORKTREE_FIX_FN}
191
+ source ~/.profile 2>/dev/null; source <(sed 's/return;;/;;/' ~/.bashrc) 2>/dev/null; cd '$REMOTE_CWD' 2>/dev/null || cd '$REMOTE_DIR'; __fix_wt '$LOCAL_MOUNT' '$REMOTE_DIR'; /bin/bash -c $(printf '%q' "$cmd"); __fix_wt '$LOCAL_MOUNT' '$REMOTE_DIR'; echo $MARKER; pwd -P"
192
+ remote_output=$(/usr/bin/ssh $SSH_OPTS "$REMOTE_HOST" "$remote_cmd")
193
+ exit_code=$?
194
+ "$MUTAGEN" sync flush --label-selector devboard=true >/dev/null 2>&1 || true
195
+ if [[ "$remote_output" == *"$MARKER"* ]]; then
196
+ cmd_output="${remote_output%$MARKER*}"
197
+ remote_pwd="${remote_output##*$MARKER}"
198
+ remote_pwd=$(echo "$remote_pwd" | tr -d '\n')
199
+ printf "%s" "$cmd_output"
200
+ if [[ -n "$pwd_file" ]]; then
201
+ echo "$(remote_to_local "$remote_pwd")" > "$pwd_file"
202
+ fi
203
+ else
204
+ echo "$remote_output"
205
+ [[ -n "$pwd_file" ]] && echo "$LOCAL_CWD" > "$pwd_file"
206
+ fi
207
+ else
208
+ notify "Remote unavailable - using local execution" "offline"
209
+ cmd="${cmd//$REMOTE_DIR/$LOCAL_MOUNT}"
210
+ MARKER="__DEVBOARD_LOCAL_PWD__"
211
+ local_output=$(/bin/bash -c "$cmd; echo $MARKER; pwd -P" 2>&1)
212
+ exit_code=$?
213
+ if [[ "$local_output" == *"$MARKER"* ]]; then
214
+ cmd_output="${local_output%$MARKER*}"
215
+ local_pwd="${local_output##*$MARKER}"
216
+ local_pwd=$(echo "$local_pwd" | tr -d '\n')
217
+ printf "%s" "$cmd_output"
218
+ [[ -n "$pwd_file" ]] && echo "$local_pwd" > "$pwd_file"
219
+ else
220
+ echo "$local_output"
221
+ [[ -n "$pwd_file" ]] && echo "$LOCAL_CWD" > "$pwd_file"
222
+ fi
223
+ fi
224
+
225
+ exit $exit_code
226
+ else
227
+ if [[ -z "$REMOTE_HOST" ]] || [[ -z "$REMOTE_DIR" ]] || [[ -z "$LOCAL_MOUNT" ]]; then
228
+ exec /bin/bash -l
229
+ fi
230
+
231
+ if is_remote_available; then
232
+ notify "Remote instance available" "online"
233
+ REMOTE_CWD="$(local_to_remote "$(pwd -P)")"
234
+ /usr/bin/ssh $SSH_OPTS -t "$REMOTE_HOST" "cd '$REMOTE_CWD' 2>/dev/null || cd '$REMOTE_DIR'; /bin/bash -l"
235
+ else
236
+ notify "Remote unavailable - using local shell" "offline"
237
+ /bin/bash -l
238
+ fi
239
+ fi