@hienlh/ppm 0.1.0 → 0.1.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 (75) hide show
  1. package/dist/web/assets/api-client-Bnf9LAt4.js +1 -0
  2. package/dist/web/assets/arrow-up-from-line-BXL5dtbG.js +1 -0
  3. package/dist/web/assets/button-BxijdhtM.js +1 -0
  4. package/dist/web/assets/chat-tab-BZopEuub.js +61 -0
  5. package/dist/web/assets/code-editor-hbllHzj7.js +2 -0
  6. package/dist/web/assets/createLucideIcon-Dy1wlrF7.js +1 -0
  7. package/dist/web/assets/dialog-RczsXsmw.js +45 -0
  8. package/dist/web/assets/diff-viewer-D6ixPlNB.js +4 -0
  9. package/dist/web/assets/dist-CSp7ir0r.js +46 -0
  10. package/dist/web/assets/external-link-WSiY-639.js +1 -0
  11. package/dist/web/assets/git-graph-DXMB_DoT.js +1 -0
  12. package/dist/web/assets/git-status-panel-D8ZUQrRF.js +1 -0
  13. package/dist/web/assets/index-DGSLw2GE.js +10 -0
  14. package/dist/web/assets/index-DYd_2slk.css +2 -0
  15. package/dist/web/assets/jsx-runtime-BnxRlLMJ.js +1 -0
  16. package/dist/web/assets/project-list-DWVXEimw.js +1 -0
  17. package/dist/web/assets/react-Uzd0zARU.js +1 -0
  18. package/dist/web/assets/refresh-cw-DtopuYJf.js +1 -0
  19. package/dist/web/assets/settings-tab-DJRzIAuP.js +1 -0
  20. package/dist/web/assets/terminal-tab-BrP-ENHg.css +1 -0
  21. package/dist/web/assets/terminal-tab-CbwaI-oq.js +36 -0
  22. package/dist/web/assets/trash-2-CHLebaNh.js +1 -0
  23. package/dist/web/assets/utils-Cgi2TYRi.js +1 -0
  24. package/dist/web/assets/x-BISR7bpK.js +1 -0
  25. package/dist/web/icon-192.svg +5 -0
  26. package/dist/web/icon-512.svg +5 -0
  27. package/dist/web/index.html +25 -0
  28. package/dist/web/manifest.webmanifest +1 -0
  29. package/dist/web/registerSW.js +1 -0
  30. package/dist/web/sw.js +1 -0
  31. package/dist/web/workbox-3e722498.js +1 -0
  32. package/package.json +2 -1
  33. package/.claude/agent-memory/tester/MEMORY.md +0 -3
  34. package/.claude/agent-memory/tester/project-ppm-test-conventions.md +0 -32
  35. package/.github/workflows/release.yml +0 -46
  36. package/plans/260314-2009-ppm-implementation/phase-01-project-skeleton.md +0 -81
  37. package/plans/260314-2009-ppm-implementation/phase-02-backend-core.md +0 -148
  38. package/plans/260314-2009-ppm-implementation/phase-03-frontend-shell.md +0 -256
  39. package/plans/260314-2009-ppm-implementation/phase-04-file-explorer-editor.md +0 -120
  40. package/plans/260314-2009-ppm-implementation/phase-05-web-terminal.md +0 -174
  41. package/plans/260314-2009-ppm-implementation/phase-06-git-integration.md +0 -244
  42. package/plans/260314-2009-ppm-implementation/phase-07-ai-chat.md +0 -242
  43. package/plans/260314-2009-ppm-implementation/phase-08-cli-commands.md +0 -143
  44. package/plans/260314-2009-ppm-implementation/phase-09-pwa-build-deploy.md +0 -209
  45. package/plans/260314-2009-ppm-implementation/phase-10-testing.md +0 -311
  46. package/plans/260314-2009-ppm-implementation/plan.md +0 -202
  47. package/plans/260315-0356-project-scoped-api-refactor/phase-01-backend-project-router.md +0 -145
  48. package/plans/260315-0356-project-scoped-api-refactor/phase-02-frontend-api-migration.md +0 -107
  49. package/plans/260315-0356-project-scoped-api-refactor/phase-03-per-project-tabs.md +0 -100
  50. package/plans/260315-0356-project-scoped-api-refactor/phase-04-websocket-migration.md +0 -66
  51. package/plans/260315-0356-project-scoped-api-refactor/plan.md +0 -87
  52. package/plans/reports/brainstorm-260314-1938-final-techstack.md +0 -342
  53. package/plans/reports/docs-manager-260315-1314-documentation-creation.md +0 -386
  54. package/plans/reports/fullstack-developer-260314-2252-phase-02-backend-core.md +0 -57
  55. package/plans/reports/fullstack-developer-260314-2253-phase-03-frontend-shell.md +0 -70
  56. package/plans/reports/fullstack-developer-260314-2300-phase-04-05-file-api-terminal-ws.md +0 -49
  57. package/plans/reports/fullstack-developer-260314-2300-phase-04-05-file-explorer-editor-terminal.md +0 -52
  58. package/plans/reports/fullstack-developer-260314-2307-ai-chat-phase7.md +0 -58
  59. package/plans/reports/fullstack-developer-260314-2307-phase-06-git-integration.md +0 -33
  60. package/plans/reports/research-260314-1911-ppm-tech-stack.md +0 -318
  61. package/plans/reports/research-260314-1930-claude-code-integration.md +0 -293
  62. package/plans/reports/researcher-260314-2232-node-pty-bun-crash-analysis.md +0 -305
  63. package/plans/reports/researcher-260314-2232-ui-style.md +0 -942
  64. package/plans/reports/researcher-260315-0300-opcode-claude-interaction.md +0 -745
  65. package/plans/reports/researcher-260315-0303-opcode-deep-analysis.md +0 -742
  66. package/plans/reports/researcher-260315-0305-claude-agent-sdk-github-research.md +0 -423
  67. package/plans/reports/tester-260314-2053-initial-test-suite.md +0 -81
  68. package/repomix-output.xml +0 -23745
  69. package/tests/integration/api/chat-routes.test.ts +0 -95
  70. package/tests/integration/claude-agent-sdk-integration.test.ts +0 -228
  71. package/tests/integration/ws/chat-websocket.test.ts +0 -312
  72. package/tests/test-setup.ts +0 -5
  73. package/tests/unit/providers/claude-agent-sdk.test.ts +0 -339
  74. package/tests/unit/providers/mock-provider.test.ts +0 -143
  75. package/tests/unit/services/chat-service.test.ts +0 -100
@@ -0,0 +1 @@
1
+ import{t as e}from"./createLucideIcon-Dy1wlrF7.js";var t=e(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]);export{t};
@@ -0,0 +1 @@
1
+ function e(t){var n,r,i=``;if(typeof t==`string`||typeof t==`number`)i+=t;else if(typeof t==`object`)if(Array.isArray(t)){var a=t.length;for(n=0;n<a;n++)t[n]&&(r=e(t[n]))&&(i&&(i+=` `),i+=r)}else for(r in t)t[r]&&(i&&(i+=` `),i+=r);return i}function t(){for(var t,n,r=0,i=``,a=arguments.length;r<a;r++)(t=arguments[r])&&(n=e(t))&&(i&&(i+=` `),i+=n);return i}var n=(e,t)=>{let n=Array(e.length+t.length);for(let t=0;t<e.length;t++)n[t]=e[t];for(let r=0;r<t.length;r++)n[e.length+r]=t[r];return n},r=(e,t)=>({classGroupId:e,validator:t}),i=(e=new Map,t=null,n)=>({nextPart:e,validators:t,classGroupId:n}),a=`-`,o=[],s=`arbitrary..`,c=e=>{let t=ee(e),{conflictingClassGroups:r,conflictingClassGroupModifiers:i}=e;return{getClassGroupId:e=>{if(e.startsWith(`[`)&&e.endsWith(`]`))return u(e);let n=e.split(a);return l(n,n[0]===``&&n.length>1?1:0,t)},getConflictingClassGroupIds:(e,t)=>{if(t){let t=i[e],a=r[e];return t?a?n(a,t):t:a||o}return r[e]||o}}},l=(e,t,n)=>{if(e.length-t===0)return n.classGroupId;let r=e[t],i=n.nextPart.get(r);if(i){let n=l(e,t+1,i);if(n)return n}let o=n.validators;if(o===null)return;let s=t===0?e.join(a):e.slice(t).join(a),c=o.length;for(let e=0;e<c;e++){let t=o[e];if(t.validator(s))return t.classGroupId}},u=e=>e.slice(1,-1).indexOf(`:`)===-1?void 0:(()=>{let t=e.slice(1,-1),n=t.indexOf(`:`),r=t.slice(0,n);return r?s+r:void 0})(),ee=e=>{let{theme:t,classGroups:n}=e;return d(n,t)},d=(e,t)=>{let n=i();for(let r in e){let i=e[r];f(i,n,r,t)}return n},f=(e,t,n,r)=>{let i=e.length;for(let a=0;a<i;a++){let i=e[a];p(i,t,n,r)}},p=(e,t,n,r)=>{if(typeof e==`string`){m(e,t,n);return}if(typeof e==`function`){h(e,t,n,r);return}g(e,t,n,r)},m=(e,t,n)=>{let r=e===``?t:_(t,e);r.classGroupId=n},h=(e,t,n,i)=>{if(v(e)){f(e(i),t,n,i);return}t.validators===null&&(t.validators=[]),t.validators.push(r(n,e))},g=(e,t,n,r)=>{let i=Object.entries(e),a=i.length;for(let e=0;e<a;e++){let[a,o]=i[e];f(o,_(t,a),n,r)}},_=(e,t)=>{let n=e,r=t.split(a),o=r.length;for(let e=0;e<o;e++){let t=r[e],a=n.nextPart.get(t);a||(a=i(),n.nextPart.set(t,a)),n=a}return n},v=e=>`isThemeGetter`in e&&e.isThemeGetter===!0,y=e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let t=0,n=Object.create(null),r=Object.create(null),i=(i,a)=>{n[i]=a,t++,t>e&&(t=0,r=n,n=Object.create(null))};return{get(e){let t=n[e];if(t!==void 0)return t;if((t=r[e])!==void 0)return i(e,t),t},set(e,t){e in n?n[e]=t:i(e,t)}}},b=`!`,x=`:`,S=[],C=(e,t,n,r,i)=>({modifiers:e,hasImportantModifier:t,baseClassName:n,maybePostfixModifierPosition:r,isExternal:i}),w=e=>{let{prefix:t,experimentalParseClassName:n}=e,r=e=>{let t=[],n=0,r=0,i=0,a,o=e.length;for(let s=0;s<o;s++){let o=e[s];if(n===0&&r===0){if(o===x){t.push(e.slice(i,s)),i=s+1;continue}if(o===`/`){a=s;continue}}o===`[`?n++:o===`]`?n--:o===`(`?r++:o===`)`&&r--}let s=t.length===0?e:e.slice(i),c=s,l=!1;s.endsWith(b)?(c=s.slice(0,-1),l=!0):s.startsWith(b)&&(c=s.slice(1),l=!0);let u=a&&a>i?a-i:void 0;return C(t,l,c,u)};if(t){let e=t+x,n=r;r=t=>t.startsWith(e)?n(t.slice(e.length)):C(S,!1,t,void 0,!0)}if(n){let e=r;r=t=>n({className:t,parseClassName:e})}return r},te=e=>{let t=new Map;return e.orderSensitiveModifiers.forEach((e,n)=>{t.set(e,1e6+n)}),e=>{let n=[],r=[];for(let i=0;i<e.length;i++){let a=e[i],o=a[0]===`[`,s=t.has(a);o||s?(r.length>0&&(r.sort(),n.push(...r),r=[]),n.push(a)):r.push(a)}return r.length>0&&(r.sort(),n.push(...r)),n}},ne=e=>({cache:y(e.cacheSize),parseClassName:w(e),sortModifiers:te(e),...c(e)}),T=/\s+/,re=(e,t)=>{let{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:i,sortModifiers:a}=t,o=[],s=e.trim().split(T),c=``;for(let e=s.length-1;e>=0;--e){let t=s[e],{isExternal:l,modifiers:u,hasImportantModifier:ee,baseClassName:d,maybePostfixModifierPosition:f}=n(t);if(l){c=t+(c.length>0?` `+c:c);continue}let p=!!f,m=r(p?d.substring(0,f):d);if(!m){if(!p){c=t+(c.length>0?` `+c:c);continue}if(m=r(d),!m){c=t+(c.length>0?` `+c:c);continue}p=!1}let h=u.length===0?``:u.length===1?u[0]:a(u).join(`:`),g=ee?h+b:h,_=g+m;if(o.indexOf(_)>-1)continue;o.push(_);let v=i(m,p);for(let e=0;e<v.length;++e){let t=v[e];o.push(g+t)}c=t+(c.length>0?` `+c:c)}return c},E=(...e)=>{let t=0,n,r,i=``;for(;t<e.length;)(n=e[t++])&&(r=D(n))&&(i&&(i+=` `),i+=r);return i},D=e=>{if(typeof e==`string`)return e;let t,n=``;for(let r=0;r<e.length;r++)e[r]&&(t=D(e[r]))&&(n&&(n+=` `),n+=t);return n},O=(e,...t)=>{let n,r,i,a,o=o=>(n=ne(t.reduce((e,t)=>t(e),e())),r=n.cache.get,i=n.cache.set,a=s,s(o)),s=e=>{let t=r(e);if(t)return t;let a=re(e,n);return i(e,a),a};return a=o,(...e)=>a(E(...e))},k=[],A=e=>{let t=t=>t[e]||k;return t.isThemeGetter=!0,t},j=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,M=/^\((?:(\w[\w-]*):)?(.+)\)$/i,N=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,ie=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,ae=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,oe=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,P=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,F=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,I=e=>N.test(e),L=e=>!!e&&!Number.isNaN(Number(e)),R=e=>!!e&&Number.isInteger(Number(e)),se=e=>e.endsWith(`%`)&&L(e.slice(0,-1)),z=e=>ie.test(e),ce=()=>!0,B=e=>ae.test(e)&&!oe.test(e),V=()=>!1,le=e=>P.test(e),H=e=>F.test(e),ue=e=>!U(e)&&!K(e),de=e=>Y(e,$,V),U=e=>j.test(e),W=e=>Y(e,Se,B),fe=e=>Y(e,Ce,L),pe=e=>Y(e,Te,ce),me=e=>Y(e,we,V),he=e=>Y(e,Z,V),ge=e=>Y(e,Q,H),G=e=>Y(e,Ee,le),K=e=>M.test(e),q=e=>X(e,Se),_e=e=>X(e,we),ve=e=>X(e,Z),ye=e=>X(e,$),be=e=>X(e,Q),J=e=>X(e,Ee,!0),xe=e=>X(e,Te,!0),Y=(e,t,n)=>{let r=j.exec(e);return r?r[1]?t(r[1]):n(r[2]):!1},X=(e,t,n=!1)=>{let r=M.exec(e);return r?r[1]?t(r[1]):n:!1},Z=e=>e===`position`||e===`percentage`,Q=e=>e===`image`||e===`url`,$=e=>e===`length`||e===`size`||e===`bg-size`,Se=e=>e===`length`,Ce=e=>e===`number`,we=e=>e===`family-name`,Te=e=>e===`number`||e===`weight`,Ee=e=>e===`shadow`,De=O(()=>{let e=A(`color`),t=A(`font`),n=A(`text`),r=A(`font-weight`),i=A(`tracking`),a=A(`leading`),o=A(`breakpoint`),s=A(`container`),c=A(`spacing`),l=A(`radius`),u=A(`shadow`),ee=A(`inset-shadow`),d=A(`text-shadow`),f=A(`drop-shadow`),p=A(`blur`),m=A(`perspective`),h=A(`aspect`),g=A(`ease`),_=A(`animate`),v=()=>[`auto`,`avoid`,`all`,`avoid-page`,`page`,`left`,`right`,`column`],y=()=>[`center`,`top`,`bottom`,`left`,`right`,`top-left`,`left-top`,`top-right`,`right-top`,`bottom-right`,`right-bottom`,`bottom-left`,`left-bottom`],b=()=>[...y(),K,U],x=()=>[`auto`,`hidden`,`clip`,`visible`,`scroll`],S=()=>[`auto`,`contain`,`none`],C=()=>[K,U,c],w=()=>[I,`full`,`auto`,...C()],te=()=>[R,`none`,`subgrid`,K,U],ne=()=>[`auto`,{span:[`full`,R,K,U]},R,K,U],T=()=>[R,`auto`,K,U],re=()=>[`auto`,`min`,`max`,`fr`,K,U],E=()=>[`start`,`end`,`center`,`between`,`around`,`evenly`,`stretch`,`baseline`,`center-safe`,`end-safe`],D=()=>[`start`,`end`,`center`,`stretch`,`center-safe`,`end-safe`],O=()=>[`auto`,...C()],k=()=>[I,`auto`,`full`,`dvw`,`dvh`,`lvw`,`lvh`,`svw`,`svh`,`min`,`max`,`fit`,...C()],j=()=>[I,`screen`,`full`,`dvw`,`lvw`,`svw`,`min`,`max`,`fit`,...C()],M=()=>[I,`screen`,`full`,`lh`,`dvh`,`lvh`,`svh`,`min`,`max`,`fit`,...C()],N=()=>[e,K,U],ie=()=>[...y(),ve,he,{position:[K,U]}],ae=()=>[`no-repeat`,{repeat:[``,`x`,`y`,`space`,`round`]}],oe=()=>[`auto`,`cover`,`contain`,ye,de,{size:[K,U]}],P=()=>[se,q,W],F=()=>[``,`none`,`full`,l,K,U],B=()=>[``,L,q,W],V=()=>[`solid`,`dashed`,`dotted`,`double`],le=()=>[`normal`,`multiply`,`screen`,`overlay`,`darken`,`lighten`,`color-dodge`,`color-burn`,`hard-light`,`soft-light`,`difference`,`exclusion`,`hue`,`saturation`,`color`,`luminosity`],H=()=>[L,se,ve,he],Y=()=>[``,`none`,p,K,U],X=()=>[`none`,L,K,U],Z=()=>[`none`,L,K,U],Q=()=>[L,K,U],$=()=>[I,`full`,...C()];return{cacheSize:500,theme:{animate:[`spin`,`ping`,`pulse`,`bounce`],aspect:[`video`],blur:[z],breakpoint:[z],color:[ce],container:[z],"drop-shadow":[z],ease:[`in`,`out`,`in-out`],font:[ue],"font-weight":[`thin`,`extralight`,`light`,`normal`,`medium`,`semibold`,`bold`,`extrabold`,`black`],"inset-shadow":[z],leading:[`none`,`tight`,`snug`,`normal`,`relaxed`,`loose`],perspective:[`dramatic`,`near`,`normal`,`midrange`,`distant`,`none`],radius:[z],shadow:[z],spacing:[`px`,L],text:[z],"text-shadow":[z],tracking:[`tighter`,`tight`,`normal`,`wide`,`wider`,`widest`]},classGroups:{aspect:[{aspect:[`auto`,`square`,I,U,K,h]}],container:[`container`],columns:[{columns:[L,U,K,s]}],"break-after":[{"break-after":v()}],"break-before":[{"break-before":v()}],"break-inside":[{"break-inside":[`auto`,`avoid`,`avoid-page`,`avoid-column`]}],"box-decoration":[{"box-decoration":[`slice`,`clone`]}],box:[{box:[`border`,`content`]}],display:[`block`,`inline-block`,`inline`,`flex`,`inline-flex`,`table`,`inline-table`,`table-caption`,`table-cell`,`table-column`,`table-column-group`,`table-footer-group`,`table-header-group`,`table-row-group`,`table-row`,`flow-root`,`grid`,`inline-grid`,`contents`,`list-item`,`hidden`],sr:[`sr-only`,`not-sr-only`],float:[{float:[`right`,`left`,`none`,`start`,`end`]}],clear:[{clear:[`left`,`right`,`both`,`none`,`start`,`end`]}],isolation:[`isolate`,`isolation-auto`],"object-fit":[{object:[`contain`,`cover`,`fill`,`none`,`scale-down`]}],"object-position":[{object:b()}],overflow:[{overflow:x()}],"overflow-x":[{"overflow-x":x()}],"overflow-y":[{"overflow-y":x()}],overscroll:[{overscroll:S()}],"overscroll-x":[{"overscroll-x":S()}],"overscroll-y":[{"overscroll-y":S()}],position:[`static`,`fixed`,`absolute`,`relative`,`sticky`],inset:[{inset:w()}],"inset-x":[{"inset-x":w()}],"inset-y":[{"inset-y":w()}],start:[{"inset-s":w(),start:w()}],end:[{"inset-e":w(),end:w()}],"inset-bs":[{"inset-bs":w()}],"inset-be":[{"inset-be":w()}],top:[{top:w()}],right:[{right:w()}],bottom:[{bottom:w()}],left:[{left:w()}],visibility:[`visible`,`invisible`,`collapse`],z:[{z:[R,`auto`,K,U]}],basis:[{basis:[I,`full`,`auto`,s,...C()]}],"flex-direction":[{flex:[`row`,`row-reverse`,`col`,`col-reverse`]}],"flex-wrap":[{flex:[`nowrap`,`wrap`,`wrap-reverse`]}],flex:[{flex:[L,I,`auto`,`initial`,`none`,U]}],grow:[{grow:[``,L,K,U]}],shrink:[{shrink:[``,L,K,U]}],order:[{order:[R,`first`,`last`,`none`,K,U]}],"grid-cols":[{"grid-cols":te()}],"col-start-end":[{col:ne()}],"col-start":[{"col-start":T()}],"col-end":[{"col-end":T()}],"grid-rows":[{"grid-rows":te()}],"row-start-end":[{row:ne()}],"row-start":[{"row-start":T()}],"row-end":[{"row-end":T()}],"grid-flow":[{"grid-flow":[`row`,`col`,`dense`,`row-dense`,`col-dense`]}],"auto-cols":[{"auto-cols":re()}],"auto-rows":[{"auto-rows":re()}],gap:[{gap:C()}],"gap-x":[{"gap-x":C()}],"gap-y":[{"gap-y":C()}],"justify-content":[{justify:[...E(),`normal`]}],"justify-items":[{"justify-items":[...D(),`normal`]}],"justify-self":[{"justify-self":[`auto`,...D()]}],"align-content":[{content:[`normal`,...E()]}],"align-items":[{items:[...D(),{baseline:[``,`last`]}]}],"align-self":[{self:[`auto`,...D(),{baseline:[``,`last`]}]}],"place-content":[{"place-content":E()}],"place-items":[{"place-items":[...D(),`baseline`]}],"place-self":[{"place-self":[`auto`,...D()]}],p:[{p:C()}],px:[{px:C()}],py:[{py:C()}],ps:[{ps:C()}],pe:[{pe:C()}],pbs:[{pbs:C()}],pbe:[{pbe:C()}],pt:[{pt:C()}],pr:[{pr:C()}],pb:[{pb:C()}],pl:[{pl:C()}],m:[{m:O()}],mx:[{mx:O()}],my:[{my:O()}],ms:[{ms:O()}],me:[{me:O()}],mbs:[{mbs:O()}],mbe:[{mbe:O()}],mt:[{mt:O()}],mr:[{mr:O()}],mb:[{mb:O()}],ml:[{ml:O()}],"space-x":[{"space-x":C()}],"space-x-reverse":[`space-x-reverse`],"space-y":[{"space-y":C()}],"space-y-reverse":[`space-y-reverse`],size:[{size:k()}],"inline-size":[{inline:[`auto`,...j()]}],"min-inline-size":[{"min-inline":[`auto`,...j()]}],"max-inline-size":[{"max-inline":[`none`,...j()]}],"block-size":[{block:[`auto`,...M()]}],"min-block-size":[{"min-block":[`auto`,...M()]}],"max-block-size":[{"max-block":[`none`,...M()]}],w:[{w:[s,`screen`,...k()]}],"min-w":[{"min-w":[s,`screen`,`none`,...k()]}],"max-w":[{"max-w":[s,`screen`,`none`,`prose`,{screen:[o]},...k()]}],h:[{h:[`screen`,`lh`,...k()]}],"min-h":[{"min-h":[`screen`,`lh`,`none`,...k()]}],"max-h":[{"max-h":[`screen`,`lh`,...k()]}],"font-size":[{text:[`base`,n,q,W]}],"font-smoothing":[`antialiased`,`subpixel-antialiased`],"font-style":[`italic`,`not-italic`],"font-weight":[{font:[r,xe,pe]}],"font-stretch":[{"font-stretch":[`ultra-condensed`,`extra-condensed`,`condensed`,`semi-condensed`,`normal`,`semi-expanded`,`expanded`,`extra-expanded`,`ultra-expanded`,se,U]}],"font-family":[{font:[_e,me,t]}],"font-features":[{"font-features":[U]}],"fvn-normal":[`normal-nums`],"fvn-ordinal":[`ordinal`],"fvn-slashed-zero":[`slashed-zero`],"fvn-figure":[`lining-nums`,`oldstyle-nums`],"fvn-spacing":[`proportional-nums`,`tabular-nums`],"fvn-fraction":[`diagonal-fractions`,`stacked-fractions`],tracking:[{tracking:[i,K,U]}],"line-clamp":[{"line-clamp":[L,`none`,K,fe]}],leading:[{leading:[a,...C()]}],"list-image":[{"list-image":[`none`,K,U]}],"list-style-position":[{list:[`inside`,`outside`]}],"list-style-type":[{list:[`disc`,`decimal`,`none`,K,U]}],"text-alignment":[{text:[`left`,`center`,`right`,`justify`,`start`,`end`]}],"placeholder-color":[{placeholder:N()}],"text-color":[{text:N()}],"text-decoration":[`underline`,`overline`,`line-through`,`no-underline`],"text-decoration-style":[{decoration:[...V(),`wavy`]}],"text-decoration-thickness":[{decoration:[L,`from-font`,`auto`,K,W]}],"text-decoration-color":[{decoration:N()}],"underline-offset":[{"underline-offset":[L,`auto`,K,U]}],"text-transform":[`uppercase`,`lowercase`,`capitalize`,`normal-case`],"text-overflow":[`truncate`,`text-ellipsis`,`text-clip`],"text-wrap":[{text:[`wrap`,`nowrap`,`balance`,`pretty`]}],indent:[{indent:C()}],"vertical-align":[{align:[`baseline`,`top`,`middle`,`bottom`,`text-top`,`text-bottom`,`sub`,`super`,K,U]}],whitespace:[{whitespace:[`normal`,`nowrap`,`pre`,`pre-line`,`pre-wrap`,`break-spaces`]}],break:[{break:[`normal`,`words`,`all`,`keep`]}],wrap:[{wrap:[`break-word`,`anywhere`,`normal`]}],hyphens:[{hyphens:[`none`,`manual`,`auto`]}],content:[{content:[`none`,K,U]}],"bg-attachment":[{bg:[`fixed`,`local`,`scroll`]}],"bg-clip":[{"bg-clip":[`border`,`padding`,`content`,`text`]}],"bg-origin":[{"bg-origin":[`border`,`padding`,`content`]}],"bg-position":[{bg:ie()}],"bg-repeat":[{bg:ae()}],"bg-size":[{bg:oe()}],"bg-image":[{bg:[`none`,{linear:[{to:[`t`,`tr`,`r`,`br`,`b`,`bl`,`l`,`tl`]},R,K,U],radial:[``,K,U],conic:[R,K,U]},be,ge]}],"bg-color":[{bg:N()}],"gradient-from-pos":[{from:P()}],"gradient-via-pos":[{via:P()}],"gradient-to-pos":[{to:P()}],"gradient-from":[{from:N()}],"gradient-via":[{via:N()}],"gradient-to":[{to:N()}],rounded:[{rounded:F()}],"rounded-s":[{"rounded-s":F()}],"rounded-e":[{"rounded-e":F()}],"rounded-t":[{"rounded-t":F()}],"rounded-r":[{"rounded-r":F()}],"rounded-b":[{"rounded-b":F()}],"rounded-l":[{"rounded-l":F()}],"rounded-ss":[{"rounded-ss":F()}],"rounded-se":[{"rounded-se":F()}],"rounded-ee":[{"rounded-ee":F()}],"rounded-es":[{"rounded-es":F()}],"rounded-tl":[{"rounded-tl":F()}],"rounded-tr":[{"rounded-tr":F()}],"rounded-br":[{"rounded-br":F()}],"rounded-bl":[{"rounded-bl":F()}],"border-w":[{border:B()}],"border-w-x":[{"border-x":B()}],"border-w-y":[{"border-y":B()}],"border-w-s":[{"border-s":B()}],"border-w-e":[{"border-e":B()}],"border-w-bs":[{"border-bs":B()}],"border-w-be":[{"border-be":B()}],"border-w-t":[{"border-t":B()}],"border-w-r":[{"border-r":B()}],"border-w-b":[{"border-b":B()}],"border-w-l":[{"border-l":B()}],"divide-x":[{"divide-x":B()}],"divide-x-reverse":[`divide-x-reverse`],"divide-y":[{"divide-y":B()}],"divide-y-reverse":[`divide-y-reverse`],"border-style":[{border:[...V(),`hidden`,`none`]}],"divide-style":[{divide:[...V(),`hidden`,`none`]}],"border-color":[{border:N()}],"border-color-x":[{"border-x":N()}],"border-color-y":[{"border-y":N()}],"border-color-s":[{"border-s":N()}],"border-color-e":[{"border-e":N()}],"border-color-bs":[{"border-bs":N()}],"border-color-be":[{"border-be":N()}],"border-color-t":[{"border-t":N()}],"border-color-r":[{"border-r":N()}],"border-color-b":[{"border-b":N()}],"border-color-l":[{"border-l":N()}],"divide-color":[{divide:N()}],"outline-style":[{outline:[...V(),`none`,`hidden`]}],"outline-offset":[{"outline-offset":[L,K,U]}],"outline-w":[{outline:[``,L,q,W]}],"outline-color":[{outline:N()}],shadow:[{shadow:[``,`none`,u,J,G]}],"shadow-color":[{shadow:N()}],"inset-shadow":[{"inset-shadow":[`none`,ee,J,G]}],"inset-shadow-color":[{"inset-shadow":N()}],"ring-w":[{ring:B()}],"ring-w-inset":[`ring-inset`],"ring-color":[{ring:N()}],"ring-offset-w":[{"ring-offset":[L,W]}],"ring-offset-color":[{"ring-offset":N()}],"inset-ring-w":[{"inset-ring":B()}],"inset-ring-color":[{"inset-ring":N()}],"text-shadow":[{"text-shadow":[`none`,d,J,G]}],"text-shadow-color":[{"text-shadow":N()}],opacity:[{opacity:[L,K,U]}],"mix-blend":[{"mix-blend":[...le(),`plus-darker`,`plus-lighter`]}],"bg-blend":[{"bg-blend":le()}],"mask-clip":[{"mask-clip":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]},`mask-no-clip`],"mask-composite":[{mask:[`add`,`subtract`,`intersect`,`exclude`]}],"mask-image-linear-pos":[{"mask-linear":[L]}],"mask-image-linear-from-pos":[{"mask-linear-from":H()}],"mask-image-linear-to-pos":[{"mask-linear-to":H()}],"mask-image-linear-from-color":[{"mask-linear-from":N()}],"mask-image-linear-to-color":[{"mask-linear-to":N()}],"mask-image-t-from-pos":[{"mask-t-from":H()}],"mask-image-t-to-pos":[{"mask-t-to":H()}],"mask-image-t-from-color":[{"mask-t-from":N()}],"mask-image-t-to-color":[{"mask-t-to":N()}],"mask-image-r-from-pos":[{"mask-r-from":H()}],"mask-image-r-to-pos":[{"mask-r-to":H()}],"mask-image-r-from-color":[{"mask-r-from":N()}],"mask-image-r-to-color":[{"mask-r-to":N()}],"mask-image-b-from-pos":[{"mask-b-from":H()}],"mask-image-b-to-pos":[{"mask-b-to":H()}],"mask-image-b-from-color":[{"mask-b-from":N()}],"mask-image-b-to-color":[{"mask-b-to":N()}],"mask-image-l-from-pos":[{"mask-l-from":H()}],"mask-image-l-to-pos":[{"mask-l-to":H()}],"mask-image-l-from-color":[{"mask-l-from":N()}],"mask-image-l-to-color":[{"mask-l-to":N()}],"mask-image-x-from-pos":[{"mask-x-from":H()}],"mask-image-x-to-pos":[{"mask-x-to":H()}],"mask-image-x-from-color":[{"mask-x-from":N()}],"mask-image-x-to-color":[{"mask-x-to":N()}],"mask-image-y-from-pos":[{"mask-y-from":H()}],"mask-image-y-to-pos":[{"mask-y-to":H()}],"mask-image-y-from-color":[{"mask-y-from":N()}],"mask-image-y-to-color":[{"mask-y-to":N()}],"mask-image-radial":[{"mask-radial":[K,U]}],"mask-image-radial-from-pos":[{"mask-radial-from":H()}],"mask-image-radial-to-pos":[{"mask-radial-to":H()}],"mask-image-radial-from-color":[{"mask-radial-from":N()}],"mask-image-radial-to-color":[{"mask-radial-to":N()}],"mask-image-radial-shape":[{"mask-radial":[`circle`,`ellipse`]}],"mask-image-radial-size":[{"mask-radial":[{closest:[`side`,`corner`],farthest:[`side`,`corner`]}]}],"mask-image-radial-pos":[{"mask-radial-at":y()}],"mask-image-conic-pos":[{"mask-conic":[L]}],"mask-image-conic-from-pos":[{"mask-conic-from":H()}],"mask-image-conic-to-pos":[{"mask-conic-to":H()}],"mask-image-conic-from-color":[{"mask-conic-from":N()}],"mask-image-conic-to-color":[{"mask-conic-to":N()}],"mask-mode":[{mask:[`alpha`,`luminance`,`match`]}],"mask-origin":[{"mask-origin":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]}],"mask-position":[{mask:ie()}],"mask-repeat":[{mask:ae()}],"mask-size":[{mask:oe()}],"mask-type":[{"mask-type":[`alpha`,`luminance`]}],"mask-image":[{mask:[`none`,K,U]}],filter:[{filter:[``,`none`,K,U]}],blur:[{blur:Y()}],brightness:[{brightness:[L,K,U]}],contrast:[{contrast:[L,K,U]}],"drop-shadow":[{"drop-shadow":[``,`none`,f,J,G]}],"drop-shadow-color":[{"drop-shadow":N()}],grayscale:[{grayscale:[``,L,K,U]}],"hue-rotate":[{"hue-rotate":[L,K,U]}],invert:[{invert:[``,L,K,U]}],saturate:[{saturate:[L,K,U]}],sepia:[{sepia:[``,L,K,U]}],"backdrop-filter":[{"backdrop-filter":[``,`none`,K,U]}],"backdrop-blur":[{"backdrop-blur":Y()}],"backdrop-brightness":[{"backdrop-brightness":[L,K,U]}],"backdrop-contrast":[{"backdrop-contrast":[L,K,U]}],"backdrop-grayscale":[{"backdrop-grayscale":[``,L,K,U]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[L,K,U]}],"backdrop-invert":[{"backdrop-invert":[``,L,K,U]}],"backdrop-opacity":[{"backdrop-opacity":[L,K,U]}],"backdrop-saturate":[{"backdrop-saturate":[L,K,U]}],"backdrop-sepia":[{"backdrop-sepia":[``,L,K,U]}],"border-collapse":[{border:[`collapse`,`separate`]}],"border-spacing":[{"border-spacing":C()}],"border-spacing-x":[{"border-spacing-x":C()}],"border-spacing-y":[{"border-spacing-y":C()}],"table-layout":[{table:[`auto`,`fixed`]}],caption:[{caption:[`top`,`bottom`]}],transition:[{transition:[``,`all`,`colors`,`opacity`,`shadow`,`transform`,`none`,K,U]}],"transition-behavior":[{transition:[`normal`,`discrete`]}],duration:[{duration:[L,`initial`,K,U]}],ease:[{ease:[`linear`,`initial`,g,K,U]}],delay:[{delay:[L,K,U]}],animate:[{animate:[`none`,_,K,U]}],backface:[{backface:[`hidden`,`visible`]}],perspective:[{perspective:[m,K,U]}],"perspective-origin":[{"perspective-origin":b()}],rotate:[{rotate:X()}],"rotate-x":[{"rotate-x":X()}],"rotate-y":[{"rotate-y":X()}],"rotate-z":[{"rotate-z":X()}],scale:[{scale:Z()}],"scale-x":[{"scale-x":Z()}],"scale-y":[{"scale-y":Z()}],"scale-z":[{"scale-z":Z()}],"scale-3d":[`scale-3d`],skew:[{skew:Q()}],"skew-x":[{"skew-x":Q()}],"skew-y":[{"skew-y":Q()}],transform:[{transform:[K,U,``,`none`,`gpu`,`cpu`]}],"transform-origin":[{origin:b()}],"transform-style":[{transform:[`3d`,`flat`]}],translate:[{translate:$()}],"translate-x":[{"translate-x":$()}],"translate-y":[{"translate-y":$()}],"translate-z":[{"translate-z":$()}],"translate-none":[`translate-none`],accent:[{accent:N()}],appearance:[{appearance:[`none`,`auto`]}],"caret-color":[{caret:N()}],"color-scheme":[{scheme:[`normal`,`dark`,`light`,`light-dark`,`only-dark`,`only-light`]}],cursor:[{cursor:[`auto`,`default`,`pointer`,`wait`,`text`,`move`,`help`,`not-allowed`,`none`,`context-menu`,`progress`,`cell`,`crosshair`,`vertical-text`,`alias`,`copy`,`no-drop`,`grab`,`grabbing`,`all-scroll`,`col-resize`,`row-resize`,`n-resize`,`e-resize`,`s-resize`,`w-resize`,`ne-resize`,`nw-resize`,`se-resize`,`sw-resize`,`ew-resize`,`ns-resize`,`nesw-resize`,`nwse-resize`,`zoom-in`,`zoom-out`,K,U]}],"field-sizing":[{"field-sizing":[`fixed`,`content`]}],"pointer-events":[{"pointer-events":[`auto`,`none`]}],resize:[{resize:[`none`,``,`y`,`x`]}],"scroll-behavior":[{scroll:[`auto`,`smooth`]}],"scroll-m":[{"scroll-m":C()}],"scroll-mx":[{"scroll-mx":C()}],"scroll-my":[{"scroll-my":C()}],"scroll-ms":[{"scroll-ms":C()}],"scroll-me":[{"scroll-me":C()}],"scroll-mbs":[{"scroll-mbs":C()}],"scroll-mbe":[{"scroll-mbe":C()}],"scroll-mt":[{"scroll-mt":C()}],"scroll-mr":[{"scroll-mr":C()}],"scroll-mb":[{"scroll-mb":C()}],"scroll-ml":[{"scroll-ml":C()}],"scroll-p":[{"scroll-p":C()}],"scroll-px":[{"scroll-px":C()}],"scroll-py":[{"scroll-py":C()}],"scroll-ps":[{"scroll-ps":C()}],"scroll-pe":[{"scroll-pe":C()}],"scroll-pbs":[{"scroll-pbs":C()}],"scroll-pbe":[{"scroll-pbe":C()}],"scroll-pt":[{"scroll-pt":C()}],"scroll-pr":[{"scroll-pr":C()}],"scroll-pb":[{"scroll-pb":C()}],"scroll-pl":[{"scroll-pl":C()}],"snap-align":[{snap:[`start`,`end`,`center`,`align-none`]}],"snap-stop":[{snap:[`normal`,`always`]}],"snap-type":[{snap:[`none`,`x`,`y`,`both`]}],"snap-strictness":[{snap:[`mandatory`,`proximity`]}],touch:[{touch:[`auto`,`none`,`manipulation`]}],"touch-x":[{"touch-pan":[`x`,`left`,`right`]}],"touch-y":[{"touch-pan":[`y`,`up`,`down`]}],"touch-pz":[`touch-pinch-zoom`],select:[{select:[`none`,`text`,`all`,`auto`]}],"will-change":[{"will-change":[`auto`,`scroll`,`contents`,`transform`,K,U]}],fill:[{fill:[`none`,...N()]}],"stroke-w":[{stroke:[L,q,W,fe]}],stroke:[{stroke:[`none`,...N()]}],"forced-color-adjust":[{"forced-color-adjust":[`auto`,`none`]}]},conflictingClassGroups:{overflow:[`overflow-x`,`overflow-y`],overscroll:[`overscroll-x`,`overscroll-y`],inset:[`inset-x`,`inset-y`,`inset-bs`,`inset-be`,`start`,`end`,`top`,`right`,`bottom`,`left`],"inset-x":[`right`,`left`],"inset-y":[`top`,`bottom`],flex:[`basis`,`grow`,`shrink`],gap:[`gap-x`,`gap-y`],p:[`px`,`py`,`ps`,`pe`,`pbs`,`pbe`,`pt`,`pr`,`pb`,`pl`],px:[`pr`,`pl`],py:[`pt`,`pb`],m:[`mx`,`my`,`ms`,`me`,`mbs`,`mbe`,`mt`,`mr`,`mb`,`ml`],mx:[`mr`,`ml`],my:[`mt`,`mb`],size:[`w`,`h`],"font-size":[`leading`],"fvn-normal":[`fvn-ordinal`,`fvn-slashed-zero`,`fvn-figure`,`fvn-spacing`,`fvn-fraction`],"fvn-ordinal":[`fvn-normal`],"fvn-slashed-zero":[`fvn-normal`],"fvn-figure":[`fvn-normal`],"fvn-spacing":[`fvn-normal`],"fvn-fraction":[`fvn-normal`],"line-clamp":[`display`,`overflow`],rounded:[`rounded-s`,`rounded-e`,`rounded-t`,`rounded-r`,`rounded-b`,`rounded-l`,`rounded-ss`,`rounded-se`,`rounded-ee`,`rounded-es`,`rounded-tl`,`rounded-tr`,`rounded-br`,`rounded-bl`],"rounded-s":[`rounded-ss`,`rounded-es`],"rounded-e":[`rounded-se`,`rounded-ee`],"rounded-t":[`rounded-tl`,`rounded-tr`],"rounded-r":[`rounded-tr`,`rounded-br`],"rounded-b":[`rounded-br`,`rounded-bl`],"rounded-l":[`rounded-tl`,`rounded-bl`],"border-spacing":[`border-spacing-x`,`border-spacing-y`],"border-w":[`border-w-x`,`border-w-y`,`border-w-s`,`border-w-e`,`border-w-bs`,`border-w-be`,`border-w-t`,`border-w-r`,`border-w-b`,`border-w-l`],"border-w-x":[`border-w-r`,`border-w-l`],"border-w-y":[`border-w-t`,`border-w-b`],"border-color":[`border-color-x`,`border-color-y`,`border-color-s`,`border-color-e`,`border-color-bs`,`border-color-be`,`border-color-t`,`border-color-r`,`border-color-b`,`border-color-l`],"border-color-x":[`border-color-r`,`border-color-l`],"border-color-y":[`border-color-t`,`border-color-b`],translate:[`translate-x`,`translate-y`,`translate-none`],"translate-none":[`translate`,`translate-x`,`translate-y`,`translate-z`],"scroll-m":[`scroll-mx`,`scroll-my`,`scroll-ms`,`scroll-me`,`scroll-mbs`,`scroll-mbe`,`scroll-mt`,`scroll-mr`,`scroll-mb`,`scroll-ml`],"scroll-mx":[`scroll-mr`,`scroll-ml`],"scroll-my":[`scroll-mt`,`scroll-mb`],"scroll-p":[`scroll-px`,`scroll-py`,`scroll-ps`,`scroll-pe`,`scroll-pbs`,`scroll-pbe`,`scroll-pt`,`scroll-pr`,`scroll-pb`,`scroll-pl`],"scroll-px":[`scroll-pr`,`scroll-pl`],"scroll-py":[`scroll-pt`,`scroll-pb`],touch:[`touch-x`,`touch-y`,`touch-pz`],"touch-x":[`touch`],"touch-y":[`touch`],"touch-pz":[`touch`]},conflictingClassGroupModifiers:{"font-size":[`leading`]},orderSensitiveModifiers:[`*`,`**`,`after`,`backdrop`,`before`,`details-content`,`file`,`first-letter`,`first-line`,`marker`,`placeholder`,`selection`]}});function Oe(...e){return De(t(e))}export{t as n,Oe as t};
@@ -0,0 +1 @@
1
+ import{t as e}from"./createLucideIcon-Dy1wlrF7.js";var t=e(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]);export{t};
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 192 192">
2
+ <rect width="192" height="192" rx="24" fill="#0f1419"/>
3
+ <rect x="8" y="8" width="176" height="176" rx="20" fill="#1a1f2e"/>
4
+ <text x="96" y="110" text-anchor="middle" font-family="sans-serif" font-weight="700" font-size="56" fill="#3b82f6">PPM</text>
5
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 192 192">
2
+ <rect width="192" height="192" rx="24" fill="#0f1419"/>
3
+ <rect x="8" y="8" width="176" height="176" rx="20" fill="#1a1f2e"/>
4
+ <text x="96" y="110" text-anchor="middle" font-family="sans-serif" font-weight="700" font-size="56" fill="#3b82f6">PPM</text>
5
+ </svg>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
6
+ <meta name="theme-color" content="#0f1419" />
7
+ <title>PPM — Personal Project Manager</title>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
+ <link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&family=Geist:wght@400;500;600;700&display=swap" rel="stylesheet" />
11
+ <script type="module" crossorigin src="/assets/index-DGSLw2GE.js"></script>
12
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-BnxRlLMJ.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/utils-Cgi2TYRi.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/button-BxijdhtM.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-Dy1wlrF7.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/x-BISR7bpK.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/dialog-RczsXsmw.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/api-client-Bnf9LAt4.js">
19
+ <link rel="modulepreload" crossorigin href="/assets/react-Uzd0zARU.js">
20
+ <link rel="stylesheet" crossorigin href="/assets/index-DYd_2slk.css">
21
+ <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
22
+ <body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
23
+ <div id="root"></div>
24
+ </body>
25
+ </html>
@@ -0,0 +1 @@
1
+ {"name":"PPM — Personal Project Manager","short_name":"PPM","description":"Mobile-first web IDE for managing code projects","start_url":"/","display":"standalone","background_color":"#0f1419","theme_color":"#0f1419","lang":"en","scope":"/","orientation":"any","icons":[{"src":"/icon-192.png","sizes":"192x192","type":"image/png"},{"src":"/icon-512.png","sizes":"512x512","type":"image/png"}]}
@@ -0,0 +1 @@
1
+ if('serviceWorker' in navigator) {window.addEventListener('load', () => {navigator.serviceWorker.register('/sw.js', { scope: '/' })})}
package/dist/web/sw.js ADDED
@@ -0,0 +1 @@
1
+ if(!self.define){let s,e={};const i=(i,l)=>(i=new URL(i+".js",l).href,e[i]||new Promise(e=>{if("document"in self){const s=document.createElement("script");s.src=i,s.onload=e,document.head.appendChild(s)}else s=i,importScripts(i),e()}).then(()=>{let s=e[i];if(!s)throw new Error(`Module ${i} didn’t register its module`);return s}));self.define=(l,r)=>{const n=s||("document"in self?document.currentScript.src:"")||location.href;if(e[n])return;let t={};const u=s=>i(s,n),o={module:{uri:n},exports:t,require:u};e[n]=Promise.all(l.map(s=>o[s]||u(s))).then(s=>(r(...s),t))}}define(["./workbox-3e722498"],function(s){"use strict";self.skipWaiting(),s.clientsClaim(),s.precacheAndRoute([{url:"registerSW.js",revision:"1872c500de691dce40960bb85481de07"},{url:"index.html",revision:"5bbe3db98c1f5e52e3f88df7bf70cfa0"},{url:"icon-512.svg",revision:"a0fb34fc84eb148d51812cd62669f20d"},{url:"icon-192.svg",revision:"a0fb34fc84eb148d51812cd62669f20d"},{url:"assets/x-BISR7bpK.js",revision:null},{url:"assets/utils-Cgi2TYRi.js",revision:null},{url:"assets/trash-2-CHLebaNh.js",revision:null},{url:"assets/terminal-tab-CbwaI-oq.js",revision:null},{url:"assets/terminal-tab-BrP-ENHg.css",revision:null},{url:"assets/settings-tab-DJRzIAuP.js",revision:null},{url:"assets/refresh-cw-DtopuYJf.js",revision:null},{url:"assets/react-Uzd0zARU.js",revision:null},{url:"assets/project-list-DWVXEimw.js",revision:null},{url:"assets/jsx-runtime-BnxRlLMJ.js",revision:null},{url:"assets/index-DYd_2slk.css",revision:null},{url:"assets/index-DGSLw2GE.js",revision:null},{url:"assets/git-status-panel-D8ZUQrRF.js",revision:null},{url:"assets/git-graph-DXMB_DoT.js",revision:null},{url:"assets/external-link-WSiY-639.js",revision:null},{url:"assets/dist-CSp7ir0r.js",revision:null},{url:"assets/diff-viewer-D6ixPlNB.js",revision:null},{url:"assets/dialog-RczsXsmw.js",revision:null},{url:"assets/createLucideIcon-Dy1wlrF7.js",revision:null},{url:"assets/code-editor-hbllHzj7.js",revision:null},{url:"assets/chat-tab-BZopEuub.js",revision:null},{url:"assets/button-BxijdhtM.js",revision:null},{url:"assets/arrow-up-from-line-BXL5dtbG.js",revision:null},{url:"assets/api-client-Bnf9LAt4.js",revision:null},{url:"manifest.webmanifest",revision:"79c8870653c8f419f2e3323085e1f4be"}],{}),s.cleanupOutdatedCaches(),s.registerRoute(new s.NavigationRoute(s.createHandlerBoundToURL("index.html"))),s.registerRoute(/^https?:\/\/.*\/api\//,new s.NetworkOnly,"GET"),s.registerRoute(/^https?:\/\/.*\/ws\//,new s.NetworkOnly,"GET")});
@@ -0,0 +1 @@
1
+ define(["exports"],function(t){"use strict";try{self["workbox:core:7.3.0"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:7.3.0"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class i extends r{constructor(t,e,s){super(({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)},e,s)}}class o{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)})}addCacheListener(){self.addEventListener("message",t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map(e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})}));t.waitUntil(s),t.ports&&t.ports[0]&&s.then(()=>t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:r,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let o=i&&i.handler;const c=t.method;if(!o&&this.i.has(c)&&(o=this.i.get(c)),!o)return;let a;try{a=o.handle({url:s,request:t,event:e,params:r})}catch(t){a=Promise.reject(t)}const h=i&&i.catchHandler;return a instanceof Promise&&(this.o||h)&&(a=a.catch(async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:r})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n})),a}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const r=this.t.get(s.method)||[];for(const i of r){let r;const o=i.match({url:t,sameOrigin:e,request:s,event:n});if(o)return r=o,(Array.isArray(r)&&0===r.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(r=void 0),{route:i,params:r}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let c;const a=()=>(c||(c=new o,c.addFetchListener(),c.addCacheListener()),c);function h(t,e,n){let o;if("string"==typeof t){const s=new URL(t,location.href);o=new r(({url:t})=>t.href===s.href,e,n)}else if(t instanceof RegExp)o=new i(t,e,n);else if("function"==typeof t)o=new r(t,e,n);else{if(!(t instanceof r))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}return a().registerRoute(o),o}function u(t){return new Promise(e=>setTimeout(e,t))}const l={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},f=t=>[l.prefix,t,l.suffix].filter(t=>t&&t.length>0).join("-"),w=t=>t||f(l.precache),d=t=>t||f(l.runtime);function p(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class y{constructor(){this.promise=new Promise((t,e)=>{this.resolve=t,this.reject=e})}}const g=new Set;try{self["workbox:strategies:7.3.0"]&&_()}catch(t){}function R(t){return"string"==typeof t?new Request(t):t}class m{constructor(t,e){this.h={},Object.assign(this,e),this.event=e.event,this.u=t,this.l=new y,this.p=[],this.R=[...t.plugins],this.m=new Map;for(const t of this.R)this.m.set(t,{});this.event.waitUntil(this.l.promise)}async fetch(t){const{event:e}=this;let n=R(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const r=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const i=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.u.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:i,response:t});return t}catch(t){throw r&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:r.clone(),request:i.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=R(t);let s;const{cacheName:n,matchOptions:r}=this.u,i=await this.getCacheKey(e,"read"),o=Object.assign(Object.assign({},r),{cacheName:n});s=await caches.match(i,o);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:n,matchOptions:r,cachedResponse:s,request:i,event:this.event})||void 0;return s}async cachePut(t,e){const n=R(t);await u(0);const r=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(i=r.url,new URL(String(i),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var i;const o=await this.v(e);if(!o)return!1;const{cacheName:c,matchOptions:a}=this.u,h=await self.caches.open(c),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const r=p(e.url,s);if(e.url===r)return t.match(e,n);const i=Object.assign(Object.assign({},n),{ignoreSearch:!0}),o=await t.keys(e,i);for(const e of o)if(r===p(e.url,s))return t.match(e,n)}(h,r.clone(),["__WB_REVISION__"],a):null;try{await h.put(r,l?o.clone():o)}catch(t){if(t instanceof Error)throw"QuotaExceededError"===t.name&&await async function(){for(const t of g)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:c,oldResponse:f,newResponse:o.clone(),request:r,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.h[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=R(await t({mode:e,request:n,event:this.event,params:this.params}));this.h[s]=n}return this.h[s]}hasCallback(t){for(const e of this.u.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.u.plugins)if("function"==typeof e[t]){const s=this.m.get(e),n=n=>{const r=Object.assign(Object.assign({},n),{state:s});return e[t](r)};yield n}}waitUntil(t){return this.p.push(t),t}async doneWaiting(){for(;this.p.length;){const t=this.p.splice(0),e=(await Promise.allSettled(t)).find(t=>"rejected"===t.status);if(e)throw e.reason}}destroy(){this.l.resolve(null)}async v(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class v{constructor(t={}){this.cacheName=d(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,r=new m(this,{event:e,request:s,params:n}),i=this.q(r,s,e);return[i,this.U(i,r,s,e)]}async q(t,e,n){let r;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(r=await this.L(e,t),!r||"error"===r.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const i of t.iterateCallbacks("handlerDidError"))if(r=await i({error:s,event:n,request:e}),r)break;if(!r)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))r=await s({event:n,request:e,response:r});return r}async U(t,e,s,n){let r,i;try{r=await t}catch(i){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:r}),await e.doneWaiting()}catch(t){t instanceof Error&&(i=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:r,error:i}),e.destroy(),i)throw i}}function q(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:7.3.0"]&&_()}catch(t){}function U(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const r=new URL(n,location.href),i=new URL(n,location.href);return r.searchParams.set("__WB_REVISION__",e),{cacheKey:r.href,url:i.href}}class L{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class b{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this._.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this._=t}}let E,C;async function x(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const r=t.clone(),i={headers:new Headers(r.headers),status:r.status,statusText:r.statusText},o=e?e(i):i,c=function(){if(void 0===E){const t=new Response("");if("body"in t)try{new Response(t.body),E=!0}catch(t){E=!1}E=!1}return E}()?r.body:await r.blob();return new Response(c,o)}class O extends v{constructor(t={}){t.cacheName=w(t.cacheName),super(t),this.C=!1!==t.fallbackToNetwork,this.plugins.push(O.copyRedirectedCacheableResponsesPlugin)}async L(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.O(t,e):await this.N(t,e))}async N(t,e){let n;const r=e.params||{};if(!this.C)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=r.integrity,i=t.integrity,o=!i||i===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?i||s:void 0})),s&&o&&"no-cors"!==t.mode&&(this.P(),await e.cachePut(t,n.clone()))}return n}async O(t,e){this.P();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}P(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==O.copyRedirectedCacheableResponsesPlugin&&(n===O.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(O.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}O.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},O.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await x(t):t};class N{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.k=new Map,this.T=new Map,this.j=new Map,this.u=new O({cacheName:w(t),plugins:[...e,new b({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.u}precache(t){this.addToCacheList(t),this.K||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.K=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:r}=U(n),i="string"!=typeof n&&n.revision?"reload":"default";if(this.k.has(r)&&this.k.get(r)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.k.get(r),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.j.has(t)&&this.j.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:r});this.j.set(t,n.integrity)}if(this.k.set(r,t),this.T.set(r,i),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return q(t,async()=>{const e=new L;this.strategy.plugins.push(e);for(const[e,s]of this.k){const n=this.j.get(s),r=this.T.get(e),i=new Request(e,{integrity:n,cache:r,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:i,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}})}activate(t){return q(t,async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.k.values()),n=[];for(const r of e)s.has(r.url)||(await t.delete(r),n.push(r.url));return{deletedURLs:n}})}getURLsToCacheKeys(){return this.k}getCachedURLs(){return[...this.k.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.k.get(e.href)}getIntegrityForCacheKey(t){return this.j.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}const P=()=>(C||(C=new N),C);class k extends r{constructor(t,e){super(({request:s})=>{const n=t.getURLsToCacheKeys();for(const r of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:r}={}){const i=new URL(t,location.href);i.hash="",yield i.href;const o=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some(t=>t.test(s))&&t.searchParams.delete(s);return t}(i,e);if(yield o.href,s&&o.pathname.endsWith("/")){const t=new URL(o.href);t.pathname+=s,yield t.href}if(n){const t=new URL(o.href);t.pathname+=".html",yield t.href}if(r){const t=r({url:i});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(r);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}},t.strategy)}}t.NavigationRoute=class extends r{constructor(t,{allowlist:e=[/./],denylist:s=[]}={}){super(t=>this.W(t),t),this.M=e,this.S=s}W({url:t,request:e}){if(e&&"navigate"!==e.mode)return!1;const s=t.pathname+t.search;for(const t of this.S)if(t.test(s))return!1;return!!this.M.some(t=>t.test(s))}},t.NetworkOnly=class extends v{constructor(t={}){super(t),this.D=t.networkTimeoutSeconds||0}async L(t,e){let n,r;try{const s=[e.fetch(t)];if(this.D){const t=u(1e3*this.D);s.push(t)}if(r=await Promise.race(s),!r)throw new Error(`Timed out the network response after ${this.D} seconds.`)}catch(t){t instanceof Error&&(n=t)}if(!r)throw new s("no-response",{url:t.url,error:n});return r}},t.cleanupOutdatedCaches=function(){self.addEventListener("activate",t=>{const e=w();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter(s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t);return await Promise.all(s.map(t=>self.caches.delete(t))),s})(e).then(t=>{}))})},t.clientsClaim=function(){self.addEventListener("activate",()=>self.clients.claim())},t.createHandlerBoundToURL=function(t){return P().createHandlerBoundToURL(t)},t.precacheAndRoute=function(t,e){!function(t){P().precache(t)}(t),function(t){const e=P();h(new k(e,t))}(e)},t.registerRoute=h});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -14,6 +14,7 @@
14
14
  "build": "bun run build:web && bun build src/index.ts --compile --outfile dist/ppm",
15
15
  "start": "bun run src/index.ts start",
16
16
  "typecheck": "bunx tsc --noEmit",
17
+ "prepublishOnly": "bun run build:web",
17
18
  "postinstall": "node node_modules/ccburn/scripts/postinstall.js"
18
19
  },
19
20
  "devDependencies": {
@@ -1,3 +0,0 @@
1
- # Tester Agent Memory Index
2
-
3
- - [project-ppm-test-conventions.md](project-ppm-test-conventions.md) - PPM test setup, gotchas, and conventions
@@ -1,32 +0,0 @@
1
- ---
2
- name: PPM test conventions and gotchas
3
- description: Key patterns, pitfalls, and setup details for writing tests in the PPM project
4
- type: project
5
- ---
6
-
7
- ## Test runner: `bun test` (Jest-compatible API from `bun:test`)
8
-
9
- ## Test structure
10
- - `tests/setup.ts` — shared helpers: `createTempDir`, `cleanupDir`, `createTempGitRepo`, `buildTestApp`
11
- - `tests/unit/services/` — unit tests for ConfigService, ProjectService, FileService, GitService
12
- - `tests/integration/api/` — integration tests using `app.request()` (no real server needed)
13
-
14
- ## Critical gotchas
15
-
16
- ### ppm.yaml in CWD
17
- The project root has a real `ppm.yaml` with `port: 5555`. `ConfigService.load(missingPath)` falls through to `LOCAL_CONFIG = "ppm.yaml"` in CWD when the given path doesn't exist. Always write an actual file before calling `load()` to avoid picking up this real config.
18
-
19
- ### Global configService in git routes
20
- `src/server/routes/git.ts` imports and uses the global `configService` singleton (not injected). Integration tests for git API must mutate `configService.config.projects` directly to register the test repo. Restore to `[]` in `afterEach`.
21
-
22
- ### ConfigService.load() fallback behavior
23
- Candidates checked in order: explicit path → PPM_CONFIG env → LOCAL_CONFIG (ppm.yaml) → HOME_CONFIG (~/.ppm/config.yaml). A missing explicit path does NOT stop the fallback chain.
24
-
25
- ### buildTestApp in setup.ts
26
- Overrides `configService.save = () => {}` (no-op) to prevent tests writing to disk. Injects config directly by mutating private fields via `as unknown as`.
27
-
28
- ### Real git repos for git tests
29
- `createTempGitRepo()` uses `Bun.spawn` with git env vars (author name/email) to create a real repo with an initial commit. No mocks for git operations.
30
-
31
- **Why:** Tests must use real implementations — no fakes/mocks that diverge from production behavior.
32
- **How to apply:** Always use `createTempGitRepo` for anything touching GitService or git API routes.
@@ -1,46 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- tags: ['v*']
6
-
7
- jobs:
8
- build:
9
- strategy:
10
- matrix:
11
- include:
12
- - os: ubuntu-latest
13
- target: bun-linux-x64
14
- artifact: ppm-linux-x64
15
- - os: macos-latest
16
- target: bun-darwin-arm64
17
- artifact: ppm-darwin-arm64
18
- - os: macos-13
19
- target: bun-darwin-x64
20
- artifact: ppm-darwin-x64
21
-
22
- runs-on: ${{ matrix.os }}
23
- steps:
24
- - uses: actions/checkout@v4
25
- - uses: oven-sh/setup-bun@v2
26
- - run: bun install
27
- - run: bun run build:web
28
- - run: bun build src/index.ts --compile --target=${{ matrix.target }} --outfile=dist/${{ matrix.artifact }}
29
- - uses: actions/upload-artifact@v4
30
- with:
31
- name: ${{ matrix.artifact }}
32
- path: dist/${{ matrix.artifact }}
33
-
34
- release:
35
- needs: build
36
- runs-on: ubuntu-latest
37
- permissions:
38
- contents: write
39
- steps:
40
- - uses: actions/download-artifact@v4
41
- - uses: softprops/action-gh-release@v2
42
- with:
43
- files: |
44
- ppm-linux-x64/ppm-linux-x64
45
- ppm-darwin-arm64/ppm-darwin-arm64
46
- ppm-darwin-x64/ppm-darwin-x64
@@ -1,81 +0,0 @@
1
- # Phase 1: Project Skeleton + Shared Types
2
-
3
- **Owner:** Lead
4
- **Priority:** Critical
5
- **Depends on:** None
6
- **Effort:** Small
7
-
8
- ## Overview
9
-
10
- Initialize monorepo, install dependencies, create shared types, config files. This unblocks all other phases.
11
-
12
- ## Steps
13
-
14
- 1. **Init Bun project**
15
- ```bash
16
- bun init
17
- ```
18
-
19
- 2. **Install backend dependencies**
20
- ```bash
21
- bun add hono commander js-yaml simple-git @anthropic-ai/claude-agent-sdk
22
- bun add -d @types/node typescript
23
- ```
24
-
25
- 3. **Install frontend dependencies**
26
- ```bash
27
- bun add react react-dom zustand @tanstack/react-query
28
- bun add -d vite @vitejs/plugin-react tailwindcss @tailwindcss/vite vite-plugin-pwa
29
- bun add codemirror @codemirror/lang-javascript @codemirror/lang-typescript @codemirror/lang-python @codemirror/lang-html @codemirror/lang-css @codemirror/lang-json @codemirror/lang-markdown @codemirror/autocomplete @codemirror/merge @codemirror/theme-one-dark @uiw/react-codemirror
30
- bun add @xterm/xterm @xterm/addon-fit @xterm/addon-web-links
31
- bun add diff2html react-resizable-panels lucide-react
32
- ```
33
-
34
- 4. **Init shadcn/ui**
35
- ```bash
36
- bunx --bun shadcn@latest init
37
- bunx --bun shadcn@latest add button dialog dropdown-menu context-menu input tabs scroll-area tooltip separator sonner
38
- ```
39
-
40
- 5. **Create config files**
41
- - `tsconfig.json` — strict, paths alias `@/` → `src/web/`
42
- - `vite.config.ts` — React plugin, Tailwind, PWA plugin, build output to `dist/web`
43
- - `tailwind.config.ts` — shadcn/ui preset, mobile-first
44
- - `bunfig.toml` — Bun config
45
- - `ppm.example.yaml` — example config
46
- - `.env.example` — `ANTHROPIC_API_KEY=`
47
- - `.gitignore`
48
-
49
- 6. **Create shared types** (`src/types/`)
50
- - `config.ts` — PpmConfig, AuthConfig, ProjectConfig, AIProviderConfig
51
- - `project.ts` — Project, ProjectInfo
52
- - `git.ts` — GitCommit, GitBranch, GitStatus, GitGraphData, GitDiffResult, GitFileChange
53
- - `chat.ts` — AIProvider interface, ChatEvent, SessionConfig, SessionInfo, ToolApprovalRequest
54
- - `terminal.ts` — TerminalSession, TerminalResize
55
- - `api.ts` — API request/response wrappers
56
-
57
- 7. **Create entry point** `src/index.ts` — minimal Commander.js setup with `start` command placeholder
58
-
59
- 8. **Create README.md** with project description, setup instructions
60
-
61
- 9. **Create CLAUDE.md** with project conventions
62
-
63
- 10. **Verify build**
64
- ```bash
65
- bun run src/index.ts --help
66
- ```
67
-
68
- ## Files to Create
69
-
70
- - `package.json`, `tsconfig.json`, `vite.config.ts`, `tailwind.config.ts`, `bunfig.toml`
71
- - `ppm.example.yaml`, `.env.example`, `.gitignore`
72
- - `src/index.ts`
73
- - `src/types/*.ts` (6 files)
74
- - `README.md`, `CLAUDE.md`, `LICENSE`
75
-
76
- ## Success Criteria
77
-
78
- - [x] `bun run src/index.ts --help` shows CLI help
79
- - [x] `bun run --hot src/web/main.tsx` (via vite) starts dev server
80
- - [x] All shared types compile without errors
81
- - [x] shadcn/ui components installed and importable
@@ -1,148 +0,0 @@
1
- # Phase 2: Backend Core (Server + CLI + Config)
2
-
3
- **Owner:** backend-dev
4
- **Priority:** Critical
5
- **Depends on:** Phase 1
6
- **Effort:** Medium
7
-
8
- ## Overview
9
-
10
- Hono HTTP server, WebSocket upgrade, config loading, auth middleware, static file serving. CLI `init`, `start`, `stop`, `open` commands.
11
-
12
- ## Key Insights
13
-
14
- - Hono on Bun has built-in WebSocket support via `Bun.serve`
15
- - Config loaded from `ppm.yaml` via js-yaml, validated at startup
16
- - Auth = simple Bearer token header check
17
- - Static files = serve Vite build output from `dist/web/`
18
-
19
- ## Files to Create/Modify
20
-
21
- ```
22
- src/
23
- ├── server/
24
- │ ├── index.ts # Hono app, mount routes, WS upgrade
25
- │ ├── middleware/auth.ts # Token auth
26
- │ ├── routes/static.ts # Serve SPA with fallback to index.html
27
- │ └── routes/projects.ts # GET /api/projects, POST /api/projects
28
- ├── services/
29
- │ ├── config.service.ts # Load/save ppm.yaml
30
- │ └── project.service.ts # Project CRUD (add/remove/list)
31
- ├── cli/
32
- │ ├── index.ts # Commander.js program setup
33
- │ ├── commands/init.ts # Scan .git folders, interactive setup
34
- │ ├── commands/start.ts # Start Hono server (+ daemon mode)
35
- │ ├── commands/stop.ts # Kill daemon by PID file
36
- │ ├── commands/open.ts # Open browser to http://localhost:PORT
37
- │ └── utils/project-resolver.ts # CWD detect + -p flag
38
- └── index.ts # Wire CLI → commands
39
- ```
40
-
41
- ## Implementation Steps
42
-
43
- ### 1. Config Service
44
- ```typescript
45
- // config.service.ts
46
- class ConfigService {
47
- private config: PpmConfig;
48
- load(path?: string): PpmConfig // Default: ~/.ppm/config.yaml or ./ppm.yaml
49
- save(): void
50
- get<K extends keyof PpmConfig>(key: K): PpmConfig[K]
51
- set<K extends keyof PpmConfig>(key: K, value: PpmConfig[K]): void
52
- }
53
- ```
54
- - Search order: `--config` flag → `./ppm.yaml` → `~/.ppm/config.yaml`
55
- - Create default config on first run
56
-
57
- ### 2. Project Service
58
- ```typescript
59
- class ProjectService {
60
- constructor(private config: ConfigService)
61
- list(): ProjectInfo[]
62
- add(path: string, name?: string): void
63
- remove(nameOrPath: string): void
64
- resolve(nameOrPath?: string): Project // CWD or -p flag
65
- scanForGitRepos(dir: string): string[] // Find .git folders recursively
66
- }
67
- ```
68
-
69
- ### 3. Hono Server
70
- ```typescript
71
- // server/index.ts
72
- const app = new Hono()
73
- app.use('/api/*', authMiddleware)
74
- app.route('/api/projects', projectRoutes)
75
- // WS upgrade handled at Bun.serve level
76
- // Static files: serve dist/web/ with SPA fallback
77
- ```
78
-
79
- ### 4. Auth Middleware
80
- - Check `Authorization: Bearer <token>` header
81
- - Skip auth if `config.auth.enabled === false`
82
- - Return 401 on failure
83
-
84
- **Auth flow (minimal):**
85
- - Token stored in `ppm.yaml` → `auth.token` field
86
- - Generated on `ppm init` (random 32-char hex)
87
- - Browser: on first visit, if `auth.enabled === true`, show password input screen
88
- - User enters token → stored in localStorage → sent as Bearer header on all API calls
89
- - No user/password DB — single shared token from config
90
- - Config example:
91
- ```yaml
92
- auth:
93
- enabled: true
94
- token: "a1b2c3..." # auto-generated, user can change
95
- ```
96
-
97
- **Frontend auth screen** (`src/web/components/auth/login-screen.tsx`):
98
- - Full-screen centered card: "Enter Access Token" + input + submit
99
- - On submit: store token in localStorage, redirect to app
100
- - On 401 from any API call: clear localStorage, show login screen
101
-
102
- ### 5. CLI Commands
103
- - `ppm init` — interactive: scan parent dir for .git, prompt to add, create config
104
- - `ppm start` — start Hono server, optionally daemonize (`-d` flag → detach process, write PID to `~/.ppm/ppm.pid`)
105
- - `ppm stop` — read PID file, kill process
106
- - `ppm open` — `open http://localhost:${port}` (macOS) / `xdg-open` (Linux)
107
-
108
- ### 6. Project Resolver
109
- ```typescript
110
- function resolveProject(options: { project?: string }): Project {
111
- if (options.project) return projectService.resolve(options.project);
112
- const cwd = process.cwd();
113
- const match = projectService.list().find(p => cwd.startsWith(p.path));
114
- if (match) return match;
115
- throw new Error('Not in a registered project. Use -p <name>');
116
- }
117
- ```
118
-
119
- ### 7. Shared Project Resolver (`src/server/helpers/resolve-project.ts`)
120
-
121
- **[V2 FIX]** All API routes must resolve project by NAME first, fallback to path:
122
- ```typescript
123
- export function resolveProjectPath(nameOrPath: string): string {
124
- const projects = configService.get("projects");
125
- const byName = projects.find(p => p.name === nameOrPath);
126
- if (byName) return resolve(byName.path);
127
- const abs = resolve(nameOrPath);
128
- const allowed = projects.some(p => abs === p.path || abs.startsWith(p.path + "/"));
129
- if (!allowed) throw new Error(`Project not found: ${nameOrPath}`);
130
- return abs;
131
- }
132
- ```
133
- Import this in every route file (files.ts, git.ts, projects.ts).
134
-
135
- ## Success Criteria
136
-
137
- - [ ] `ppm init` scans current dir, creates config file with auto-generated auth token
138
- - [ ] `ppm start` starts HTTP server on configured port
139
- - [ ] `ppm start -d` runs as daemon, writes PID file; `ppm stop` reads PID and kills process
140
- - [ ] `GET /api/projects` with valid Bearer token returns project list as `{ ok: true, data: [...] }`
141
- - [ ] `GET /api/projects` without token returns 401 `{ ok: false, error: "Unauthorized" }`
142
- - [ ] `GET /api/projects` with `auth.enabled: false` in config returns data without token
143
- - [ ] `ppm open` opens `http://localhost:<port>` in default browser
144
- - [ ] `resolveProjectPath("ppm")` returns correct filesystem path from config
145
- - [ ] `resolveProjectPath("nonexistent")` throws descriptive error
146
- - [ ] Config service loads from `./ppm.yaml` → `~/.ppm/config.yaml` fallback chain
147
- - [ ] Config service creates default config if none exists
148
- - [ ] Static route serves `dist/web/index.html` for all non-API paths (SPA fallback)