sillyspec 3.20.2 → 3.20.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/sillyspec-archive/SKILL.md +1 -5
- package/.claude/skills/sillyspec-auto/SKILL.md +2 -8
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +1 -28
- package/.claude/skills/sillyspec-commit/SKILL.md +3 -4
- package/.claude/skills/sillyspec-continue/SKILL.md +4 -5
- package/.claude/skills/sillyspec-doctor/SKILL.md +3 -12
- package/.claude/skills/sillyspec-execute/SKILL.md +2 -15
- package/.claude/skills/sillyspec-explore/SKILL.md +1 -14
- package/.claude/skills/sillyspec-plan/SKILL.md +36 -5
- package/.claude/skills/sillyspec-propose/SKILL.md +0 -4
- package/.claude/skills/sillyspec-quick/SKILL.md +1 -5
- package/.claude/skills/sillyspec-resume/SKILL.md +66 -23
- package/.claude/skills/sillyspec-scan/SKILL.md +1 -5
- package/.claude/skills/sillyspec-state/SKILL.md +8 -8
- package/.claude/skills/sillyspec-status/SKILL.md +1 -5
- package/.claude/skills/sillyspec-verify/SKILL.md +1 -5
- package/.claude/skills/sillyspec-workspace/SKILL.md +3 -11
- package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +97 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +51 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +29 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +34 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +13 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +43 -0
- package/.sillyspec/changes/auto-mode/design.md +50 -0
- package/.sillyspec/changes/auto-mode/proposal.md +19 -0
- package/.sillyspec/changes/auto-mode/requirements.md +21 -0
- package/.sillyspec/changes/auto-mode/tasks.md +7 -0
- package/.sillyspec/changes/brainstorm-archive/2026-04-05-dashboard-design.md +206 -0
- package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +199 -0
- package/.sillyspec/changes/dashboard/design.md +219 -0
- package/.sillyspec/changes/dashboard/design.md.braindraft +206 -0
- package/.sillyspec/changes/run-command-design/design.md +1230 -0
- package/.sillyspec/changes/unified-docs-design/design.md +199 -0
- package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
- package/.sillyspec/knowledge/INDEX.md +8 -0
- package/.sillyspec/knowledge/uncategorized.md +3 -0
- package/.sillyspec/plans/2026-04-05-dashboard.md +737 -0
- package/.sillyspec/projects/sillyspec.yaml +3 -0
- package/README.md +11 -13
- package/SKILL.md +40 -44
- package/dist/steps/brainstorm/01-load-context.md +30 -0
- package/dist/steps/brainstorm/02-reuse-check.md +6 -0
- package/dist/steps/brainstorm/03-prototype-analysis.md +11 -0
- package/dist/steps/brainstorm/04-module-split.md +23 -0
- package/dist/steps/brainstorm/05-dialog-explore.md +8 -0
- package/dist/steps/brainstorm/06-propose-approaches.md +3 -0
- package/dist/steps/brainstorm/07-present-design.md +3 -0
- package/dist/steps/brainstorm/08-write-design.md +21 -0
- package/dist/steps/brainstorm/09-self-review.md +15 -0
- package/dist/steps/brainstorm/10-user-confirm.md +3 -0
- package/dist/steps/brainstorm/11-output-spec.md +7 -0
- package/dist/steps/brainstorm/manifest.yaml +26 -0
- package/dist/steps/execute/01-load-context.md +41 -0
- package/dist/steps/execute/02-scan-conventions.md +47 -0
- package/dist/steps/execute/03-skill-mcp.md +19 -0
- package/dist/steps/execute/04-assign-task.md +22 -0
- package/dist/steps/execute/04b-prompt-template.md +54 -0
- package/dist/steps/execute/05-write-test.md +7 -0
- package/dist/steps/execute/06-write-code.md +8 -0
- package/dist/steps/execute/07-run-test.md +26 -0
- package/dist/steps/execute/08-fix-issues.md +28 -0
- package/dist/steps/execute/09-next-task.md +33 -0
- package/dist/steps/execute/manifest.yaml +28 -0
- package/dist/steps/plan/01-load-context.md +22 -0
- package/dist/steps/plan/02-anchor-confirm.md +1 -0
- package/dist/steps/plan/03-expand-tasks.md +33 -0
- package/dist/steps/plan/04-mark-order.md +15 -0
- package/dist/steps/plan/05-e2e-planning.md +17 -0
- package/dist/steps/plan/06-self-check.md +16 -0
- package/dist/steps/plan/07-save.md +1 -0
- package/dist/steps/plan/manifest.yaml +18 -0
- package/dist/steps/scan/01-env-detect.md +51 -0
- package/dist/steps/scan/02-tech-stack.md +16 -0
- package/dist/steps/scan/03-conventions.md +16 -0
- package/dist/steps/scan/04-structure.md +19 -0
- package/dist/steps/scan/05-quality.md +18 -0
- package/dist/steps/scan/06-complete.md +49 -0
- package/dist/steps/scan/manifest.yaml +16 -0
- package/dist/steps/verify/01-load-specs.md +28 -0
- package/dist/steps/verify/02-check-tasks.md +1 -0
- package/dist/steps/verify/03-check-design.md +6 -0
- package/dist/steps/verify/04-run-tests.md +7 -0
- package/dist/steps/verify/05-e2e-tests.md +27 -0
- package/dist/steps/verify/05b-e2e-fix.md +33 -0
- package/dist/steps/verify/06-code-quality.md +25 -0
- package/dist/steps/verify/07-lint-check.md +27 -0
- package/dist/steps/verify/08-output-report.md +14 -0
- package/dist/steps/verify/manifest.yaml +22 -0
- package/package.json +1 -7
- package/packages/dashboard/dist/assets/{index-Bq_Z2hne.js → index-D1EVTLmc.js} +1264 -1264
- package/packages/dashboard/dist/assets/index-DGe8CqeP.css +1 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package-lock.json +6 -6
- package/packages/dashboard/server/executor.js +1 -1
- package/packages/dashboard/server/index.js +18 -98
- package/packages/dashboard/server/parser.js +71 -139
- package/packages/dashboard/server/watcher.js +6 -14
- package/packages/dashboard/src/App.vue +185 -418
- package/packages/dashboard/src/components/ActionBar.vue +1 -10
- package/packages/dashboard/src/components/CommandPalette.vue +1 -5
- package/packages/dashboard/src/components/DocPreview.vue +8 -105
- package/packages/dashboard/src/components/DocTree.vue +19 -75
- package/packages/dashboard/src/components/PipelineStage.vue +2 -22
- package/packages/dashboard/src/components/PipelineView.vue +5 -32
- package/packages/dashboard/src/components/ProjectOverview.vue +139 -113
- package/packages/dashboard/src/components/StageBadge.vue +3 -17
- package/packages/dashboard/src/components/StepCard.vue +2 -7
- package/packages/dashboard/src/composables/useDashboard.js +0 -28
- package/src/derive.js +147 -0
- package/src/index.js +40 -688
- package/src/init.js +63 -119
- package/src/migrate.js +7 -7
- package/src/progress.js +248 -1474
- package/src/run.js +302 -3008
- package/src/setup.js +64 -2
- package/src/stages/archive.js +17 -123
- package/src/stages/brainstorm.js +48 -454
- package/src/stages/doctor.js +46 -99
- package/src/stages/execute.js +59 -420
- package/src/stages/index.js +18 -12
- package/src/stages/plan.js +189 -492
- package/src/stages/propose.js +11 -70
- package/src/stages/quick.js +13 -52
- package/src/stages/scan.js +68 -485
- package/src/stages/status.js +1 -1
- package/src/stages/verify.js +16 -203
- package/src/step.js +543 -0
- package/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
- package/.husky/pre-push +0 -13
- package/CLAUDE.md +0 -18
- package/docs/brainstorm-plan-contract.md +0 -64
- package/docs/plan-execute-contract.md +0 -123
- package/docs/platform-scan-protocol.md +0 -298
- package/docs/revision-mode.md +0 -115
- package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +0 -99
- package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +0 -218
- package/docs/sillyspec/file-lifecycle/stage-artifacts.md +0 -167
- package/docs/sillyspec/file-lifecycle/storage-and-state.md +0 -148
- package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +0 -193
- package/docs/sillyspec/file-lifecycle.md +0 -125
- package/docs/workflow-contract-regression.md +0 -106
- package/docs/worktree-isolation.md +0 -252
- package/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
- package/packages/dashboard/dist/prototype-dashboard.html +0 -836
- package/packages/dashboard/dist/prototype-overview.html +0 -256
- package/packages/dashboard/public/prototype-dashboard.html +0 -836
- package/packages/dashboard/public/prototype-overview.html +0 -256
- package/packages/dashboard/src/components/HResizeHandle.vue +0 -48
- package/packages/dashboard/src/components/ProjectCard.vue +0 -187
- package/packages/dashboard/src/components/VResizeHandle.vue +0 -61
- package/packages/dashboard/src/composables/useLayout.js +0 -131
- package/src/brainstorm-postcheck.js +0 -158
- package/src/change-list.js +0 -52
- package/src/change-risk-profile.js +0 -352
- package/src/classify-change.js +0 -73
- package/src/constants.js +0 -70
- package/src/contract-matrix.js +0 -278
- package/src/db.js +0 -201
- package/src/endpoint-extractor.js +0 -315
- package/src/hooks/claude-pre-tool-use.cjs +0 -125
- package/src/hooks/worktree-guard.js +0 -761
- package/src/knowledge-match.js +0 -130
- package/src/modules.js +0 -482
- package/src/scan-postcheck.js +0 -383
- package/src/stage-contract.js +0 -700
- package/src/stages/brainstorm-auto.js +0 -229
- package/src/stages/explore.js +0 -34
- package/src/stages/knowledge.js +0 -498
- package/src/stages/plan-postcheck.js +0 -513
- package/src/sync.js +0 -497
- package/src/task-review.js +0 -346
- package/src/workflow.js +0 -785
- package/src/worktree-apply.js +0 -549
- package/src/worktree.js +0 -932
- package/templates/workflows/archive-impact.yaml +0 -79
- package/templates/workflows/scan-docs.yaml +0 -132
- package/test/brainstorm-plan-contract.test.mjs +0 -273
- package/test/check-syntax.mjs +0 -26
- package/test/cli-top-level-aliases.test.mjs +0 -174
- package/test/contract-artifacts.test.mjs +0 -323
- package/test/decision-supersede.test.mjs +0 -277
- package/test/knowledge-match.test.mjs +0 -231
- package/test/plan-execute-contract.test.mjs +0 -330
- package/test/plan-optimization.test.mjs +0 -572
- package/test/platform-artifacts.test.mjs +0 -190
- package/test/platform-failure-samples.test.mjs +0 -199
- package/test/platform-recovery-chain.test.mjs +0 -179
- package/test/platform-recovery.test.mjs +0 -167
- package/test/platform-scan-p0.test.mjs +0 -175
- package/test/revision-v1.test.mjs +0 -1145
- package/test/run-sanitize-project-name.test.mjs +0 -51
- package/test/run-scan-postcheck-fail.test.mjs +0 -64
- package/test/run-scan-project-parse.test.mjs +0 -200
- package/test/run-tests.mjs +0 -48
- package/test/scan-docs-yaml-placeholders.test.mjs +0 -84
- package/test/scan-knowledge.test.mjs +0 -175
- package/test/scan-paths.test.mjs +0 -68
- package/test/scan-postcheck-project-priority.test.mjs +0 -85
- package/test/scan-postcheck.test.mjs +0 -197
- package/test/scan-workflow-anyfailed-block.test.mjs +0 -52
- package/test/spec-dir.test.mjs +0 -206
- package/test/stage-contract-failed-post-check.test.mjs +0 -102
- package/test/stage-contract.test.mjs +0 -299
- package/test/stage-definitions.test.mjs +0 -39
- package/test/wait-gates.test.mjs +0 -501
- package/test/workflow-spec-base.test.mjs +0 -142
- package/test/worktree-guard.test.mjs +0 -136
- package/test/worktree-native-overlay.test.mjs +0 -188
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.slide-enter-active[data-v-b5f771a9],.slide-leave-active[data-v-b5f771a9]{transition:all .2s ease}.slide-enter-from[data-v-b5f771a9],.slide-leave-to[data-v-b5f771a9]{opacity:0;max-height:0;overflow:hidden}.slide-enter-to[data-v-b5f771a9],.slide-leave-from[data-v-b5f771a9]{max-height:300px}.line-clamp-2[data-v-fac2f74c]{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.doc-preview[data-v-fc57afbe]{font-size:13px;line-height:1.7;color:#374151}.doc-preview[data-v-fc57afbe] h1{color:#d97706;font-size:18px;font-weight:700;margin:0 0 16px;border-bottom:1px solid #E5E5EA;padding-bottom:8px}.doc-preview[data-v-fc57afbe] h2{color:#1c1c1e;font-size:15px;font-weight:600;margin:20px 0 10px}.doc-preview[data-v-fc57afbe] h3{color:#d97706;font-size:14px;font-weight:600;margin:16px 0 8px}.doc-preview[data-v-fc57afbe] p{margin:8px 0}.doc-preview[data-v-fc57afbe] strong{color:#1c1c1e;font-weight:600}.doc-preview[data-v-fc57afbe] a{color:#d97706;text-decoration:none}.doc-preview[data-v-fc57afbe] a:hover{text-decoration:underline}.doc-preview[data-v-fc57afbe] code{background:#e5e5ea;color:#1c1c1e;padding:1px 5px;border-radius:3px;font-size:12px;font-family:JetBrains Mono,monospace}.doc-preview[data-v-fc57afbe] pre{background:#1c1c1e;color:#e5e5e7;border:1px solid #E5E5EA;border-radius:6px;padding:12px 16px;overflow-x:auto;font-size:12px;margin:8px 0;line-height:1.5}.doc-preview[data-v-fc57afbe] pre code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.doc-preview[data-v-fc57afbe] ul,.doc-preview[data-v-fc57afbe] ol{padding-left:20px;margin:8px 0;color:#374151}.doc-preview[data-v-fc57afbe] li{margin:4px 0}.doc-preview[data-v-fc57afbe] blockquote{border-left:3px solid #D97706;padding-left:12px;margin:12px 0;color:#636366;font-style:italic}.doc-preview[data-v-fc57afbe] table{width:100%;border-collapse:collapse;margin:12px 0;font-size:12px}.doc-preview[data-v-fc57afbe] thead th{background:#f0f0f3;color:#1c1c1e;font-weight:600;text-align:left;padding:8px 12px;border:1px solid #E5E5EA}.doc-preview[data-v-fc57afbe] tbody td{padding:6px 12px;border:1px solid #E5E5EA;color:#374151}.doc-preview[data-v-fc57afbe] tbody tr:hover{background:#f9fafb}.doc-preview[data-v-fc57afbe] hr{border:none;border-top:1px solid #E5E5EA;margin:16px 0}.project-overview[data-v-543019d4]{display:flex;align-items:center;background:#fff;border-bottom:1px solid #E5E5EA;padding:8px 16px;min-height:48px;gap:8px;flex-shrink:0;overflow-x:auto}.ov-section[data-v-543019d4]{display:flex;align-items:center;gap:6px;white-space:nowrap;flex-shrink:0}.ov-clickable[data-v-543019d4]{cursor:pointer;border-radius:4px;padding:2px 6px;margin:-2px -6px;transition:background .15s}.ov-clickable[data-v-543019d4]:hover{background:#d977060f}.ov-name[data-v-543019d4]{font-weight:600;font-size:13px;color:#1c1c1e}.ov-path[data-v-543019d4],.ov-label[data-v-543019d4]{font-size:11px;color:#636366}.ov-value[data-v-543019d4]{font-size:12px;color:#1c1c1e;font-weight:500}.ov-tag[data-v-543019d4]{font-size:10px;padding:1px 8px;border-radius:10px;background:#d9770614;color:#d97706;font-weight:500}.ov-divider[data-v-543019d4]{width:1px;height:20px;background:#e5e5ea;flex-shrink:0}.ov-git[data-v-543019d4]{margin-left:auto;gap:10px}.ov-branch[data-v-543019d4]{font-size:11px;color:#636366;background:#f5f5f7;padding:1px 8px;border-radius:4px}.ov-commit[data-v-543019d4]{font-size:11px;color:#636366;max-width:200px;overflow:hidden;text-overflow:ellipsis}.ov-dirty[data-v-543019d4]{font-size:11px;color:#d97706;font-weight:500}*{margin:0;padding:0;box-sizing:border-box}body{font-family:DM Sans,-apple-system,BlinkMacSystemFont,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#app{width:100vw;height:100vh;overflow:hidden}/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--animate-pulse-dot:pulse-dot 1.5s ease-in-out infinite}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.inset-x-0{inset-inline:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.z-10{z-index:10}.z-20{z-index:20}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-4{margin-inline:calc(var(--spacing) * 4)}.-mt-0\.5{margin-top:calc(var(--spacing) * -.5)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.list-item{display:list-item}.table{display:table}.h-1{height:calc(var(--spacing) * 1)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-12{height:calc(var(--spacing) * 12)}.h-\[2px\]{height:2px}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-32{max-height:calc(var(--spacing) * 32)}.max-h-72{max-height:calc(var(--spacing) * 72)}.w-0{width:calc(var(--spacing) * 0)}.w-1{width:calc(var(--spacing) * 1)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-\[2px\]{width:2px}.w-\[200px\]{width:200px}.w-\[340px\]{width:340px}.w-px{width:1px}.w-screen{width:100vw}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.border-collapse{border-collapse:collapse}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse-dot{animation:var(--animate-pulse-dot)}.cursor-col-resize{cursor:col-resize}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-px>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(1px * var(--tw-space-y-reverse));margin-block-end:calc(1px * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.p-3{padding:calc(var(--spacing) * 3)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-px{padding-block:1px}.pt-1\.5{padding-top:calc(var(--spacing) * 1.5)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-0{padding-bottom:calc(var(--spacing) * 0)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pl-3\.5{padding-left:calc(var(--spacing) * 3.5)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-\[3px\]{padding-left:3px}.text-center{text-align:center}.font-\[DM_Sans\,sans-serif\]{font-family:DM Sans,sans-serif}.font-\[JetBrains_Mono\,monospace\]{font-family:JetBrains Mono,monospace}.font-mono{font-family:var(--font-mono)}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[18px\]{font-size:18px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.2em\]{--tw-tracking:.2em;letter-spacing:.2em}.tracking-\[0\.15em\]{--tw-tracking:.15em;letter-spacing:.15em}.tracking-\[0\.25em\]{--tw-tracking:.25em;letter-spacing:.25em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-black{color:var(--color-black)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.hover\:bg-\[\#D97706\]:hover{background-color:#d97706}.hover\:bg-\[\#FEF3C7\]:hover{background-color:#fef3c7}}.active\:bg-\[\#D97706\]:active{background-color:#d97706}}@keyframes pulse-glow{0%,to{box-shadow:0 0 #fbbf2466}50%{box-shadow:0 0 12px 2px #fbbf2426}}@keyframes pulse-dot{0%,to{opacity:1;transform:scale(1)}50%{opacity:.4;transform:scale(1.5)}}@keyframes breathe{0%,to{opacity:.5}50%{opacity:1}}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}@keyframes slide-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes glow-border{0%,to{border-color:#fbbf2426}50%{border-color:#fbbf2480}}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-3px)}}.font-mono-log{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.noise-bg{position:relative}.noise-bg:before{content:"";pointer-events:none;z-index:0;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");position:absolute;top:0;right:0;bottom:0;left:0}.accent-stripe{background:repeating-linear-gradient(-45deg,#0000,#0000 8px,#fbbf2408 8px,#fbbf2408 9px)}.progress-gradient{background:linear-gradient(90deg,#fbbf24,#f59e0b,#fb923c)}.skeleton-shimmer{background:linear-gradient(90deg,#1a1e28 25%,#2a3040,#1a1e28 75%) 0 0/200% 100%;animation:1.5s ease-in-out infinite shimmer}::-webkit-scrollbar{width:5px;height:5px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:#2a3040;border-radius:10px}::-webkit-scrollbar-thumb:hover{background:#3a4555}*{scrollbar-width:thin;scrollbar-color:#2a2a2d transparent}::selection{color:#fbbf24;background:#fbbf2433}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-D1EVTLmc.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DGe8CqeP.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body style="background-color: #0A0A0B; margin: 0;">
|
|
14
14
|
<div id="app"></div>
|
|
@@ -2050,9 +2050,9 @@
|
|
|
2050
2050
|
}
|
|
2051
2051
|
},
|
|
2052
2052
|
"node_modules/postcss": {
|
|
2053
|
-
"version": "8.5.
|
|
2054
|
-
"resolved": "https://registry.
|
|
2055
|
-
"integrity": "sha512-
|
|
2053
|
+
"version": "8.5.8",
|
|
2054
|
+
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz",
|
|
2055
|
+
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
|
|
2056
2056
|
"funding": [
|
|
2057
2057
|
{
|
|
2058
2058
|
"type": "opencollective",
|
|
@@ -2219,9 +2219,9 @@
|
|
|
2219
2219
|
}
|
|
2220
2220
|
},
|
|
2221
2221
|
"node_modules/vite": {
|
|
2222
|
-
"version": "6.4.
|
|
2223
|
-
"resolved": "https://registry.
|
|
2224
|
-
"integrity": "sha512
|
|
2222
|
+
"version": "6.4.1",
|
|
2223
|
+
"resolved": "https://registry.npmmirror.com/vite/-/vite-6.4.1.tgz",
|
|
2224
|
+
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
|
2225
2225
|
"dev": true,
|
|
2226
2226
|
"license": "MIT",
|
|
2227
2227
|
"dependencies": {
|
|
@@ -9,7 +9,7 @@ import { spawn } from 'child_process'
|
|
|
9
9
|
* @returns {function} Kill function to terminate the process
|
|
10
10
|
*/
|
|
11
11
|
export function executeCommand(projectPath, command, onOutput, onComplete) {
|
|
12
|
-
const args =
|
|
12
|
+
const args = command.split(' ')
|
|
13
13
|
const proc = spawn('npx', ['sillyspec', ...args], {
|
|
14
14
|
cwd: projectPath,
|
|
15
15
|
env: { ...process.env }
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createServer } from 'http'
|
|
2
2
|
import { WebSocketServer } from 'ws'
|
|
3
3
|
import { existsSync, readFileSync, readdirSync, realpathSync, watch } from 'fs'
|
|
4
|
-
import { join, dirname, basename, sep, resolve
|
|
4
|
+
import { join, dirname, basename, sep, resolve } from 'path'
|
|
5
5
|
import { fileURLToPath } from 'url'
|
|
6
6
|
import { homedir } from 'os'
|
|
7
7
|
import open from 'open'
|
|
8
|
-
import { parseProjectState,
|
|
8
|
+
import { parseProjectState, parseDocsTree, parseProjectOverview, parseGitDetail, parseTechStackDetail, parseDocsList } from './parser.js'
|
|
9
9
|
import { startWatcher, stopWatcher, addCustomScanPath, removeCustomScanPath, getCustomScanPaths, customScanPaths } from './watcher.js'
|
|
10
10
|
import { executeCommand } from './executor.js'
|
|
11
11
|
|
|
@@ -14,31 +14,6 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
|
14
14
|
// WebSocket clients and active processes
|
|
15
15
|
let wss = null
|
|
16
16
|
const activeProcesses = new Map()
|
|
17
|
-
const allowedStages = new Set(['brainstorm', 'plan', 'execute', 'verify', 'scan', 'quick', 'explore', 'archive', 'status', 'doctor', 'auto'])
|
|
18
|
-
|
|
19
|
-
function isAllowedCliCommand(command) {
|
|
20
|
-
const args = String(command || '').trim().split(/\s+/).filter(Boolean)
|
|
21
|
-
if (args.length === 0) return false
|
|
22
|
-
if (args[0] === 'run') {
|
|
23
|
-
if (!allowedStages.has(args[1])) return false
|
|
24
|
-
const allowedFlags = new Set(['--status', '--reset', '--done', '--skip', '--output', '--input', '--change'])
|
|
25
|
-
for (let i = 2; i < args.length; i++) {
|
|
26
|
-
if (args[i].startsWith('-') && !allowedFlags.has(args[i])) return false
|
|
27
|
-
if (['--output', '--input', '--change'].includes(args[i])) i++
|
|
28
|
-
}
|
|
29
|
-
return true
|
|
30
|
-
}
|
|
31
|
-
if (args[0] === 'progress') {
|
|
32
|
-
const sub = args[1]
|
|
33
|
-
if (!['show', 'status', 'validate', 'reset'].includes(sub)) return false
|
|
34
|
-
for (let i = 2; i < args.length; i++) {
|
|
35
|
-
if (!['--stage', '--deep'].includes(args[i])) return false
|
|
36
|
-
if (args[i] === '--stage') i++
|
|
37
|
-
}
|
|
38
|
-
return true
|
|
39
|
-
}
|
|
40
|
-
return false
|
|
41
|
-
}
|
|
42
17
|
|
|
43
18
|
// Progress file watchers: projectPath -> { watcher, timer, refCount }
|
|
44
19
|
const progressWatchers = new Map()
|
|
@@ -48,13 +23,12 @@ function startProgressWatch(projectPath) {
|
|
|
48
23
|
progressWatchers.get(projectPath).refCount++
|
|
49
24
|
return
|
|
50
25
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (!existsSync(dbFile)) return
|
|
26
|
+
const progressFile = join(projectPath, '.sillyspec', '.runtime', 'progress.json')
|
|
27
|
+
if (!existsSync(progressFile)) return
|
|
54
28
|
|
|
55
29
|
let timer = null
|
|
56
30
|
try {
|
|
57
|
-
const watcher = watch(
|
|
31
|
+
const watcher = watch(progressFile, (eventType) => {
|
|
58
32
|
if (timer) clearTimeout(timer)
|
|
59
33
|
timer = setTimeout(() => {
|
|
60
34
|
timer = null
|
|
@@ -93,30 +67,6 @@ function broadcast(data) {
|
|
|
93
67
|
})
|
|
94
68
|
}
|
|
95
69
|
|
|
96
|
-
function isInside(parent, child) {
|
|
97
|
-
const rel = relative(resolve(parent), resolve(child))
|
|
98
|
-
return rel === '' || (!!rel && !rel.startsWith('..') && !rel.includes(`..${sep}`))
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function isSillyspecPath(filePath) {
|
|
102
|
-
const parts = resolve(filePath).split(/[\\/]+/)
|
|
103
|
-
return parts.includes('.sillyspec')
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function isViewableDocPath(filePath) {
|
|
107
|
-
return /\.(md|markdown|mdx|html?|txt|log|json|ya?ml|toml|xml|csv)$/i.test(filePath)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function isLocalOrigin(origin) {
|
|
111
|
-
if (!origin) return true
|
|
112
|
-
try {
|
|
113
|
-
const { hostname } = new URL(origin)
|
|
114
|
-
return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1'
|
|
115
|
-
} catch {
|
|
116
|
-
return false
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
70
|
// --- Shared scan logic (aligned with watcher.js) ---
|
|
121
71
|
|
|
122
72
|
const excludeDirs = new Set([
|
|
@@ -228,14 +178,6 @@ function handleCliExecute(ws, data) {
|
|
|
228
178
|
return
|
|
229
179
|
}
|
|
230
180
|
|
|
231
|
-
if (!isAllowedCliCommand(command)) {
|
|
232
|
-
ws.send(JSON.stringify({
|
|
233
|
-
type: 'cli:error',
|
|
234
|
-
data: { message: `Command not allowed: ${command}` }
|
|
235
|
-
}))
|
|
236
|
-
return
|
|
237
|
-
}
|
|
238
|
-
|
|
239
181
|
discoverProjects().then(projects => {
|
|
240
182
|
const project = projects.find(p => p.name === projectName)
|
|
241
183
|
if (!project) {
|
|
@@ -279,14 +221,7 @@ function handleCliExecute(ws, data) {
|
|
|
279
221
|
*/
|
|
280
222
|
function startServer({ port = 3456, open: openBrowser = true } = {}) {
|
|
281
223
|
const server = createServer((req, res) => {
|
|
282
|
-
|
|
283
|
-
if (!isLocalOrigin(origin)) {
|
|
284
|
-
res.writeHead(403)
|
|
285
|
-
res.end('Forbidden')
|
|
286
|
-
return
|
|
287
|
-
}
|
|
288
|
-
res.setHeader('Access-Control-Allow-Origin', origin || `http://127.0.0.1:${port}`)
|
|
289
|
-
res.setHeader('Vary', 'Origin')
|
|
224
|
+
res.setHeader('Access-Control-Allow-Origin', '*')
|
|
290
225
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
|
291
226
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
|
|
292
227
|
|
|
@@ -318,7 +253,7 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
|
|
|
318
253
|
try {
|
|
319
254
|
if (type === 'git') data = parseGitDetail(projectPath)
|
|
320
255
|
else if (type === 'tech') data = parseTechStackDetail(projectPath)
|
|
321
|
-
else if (type === 'docs') data =
|
|
256
|
+
else if (type === 'docs') data = parseDocsList(projectPath)
|
|
322
257
|
else { res.writeHead(400); res.end(JSON.stringify({ error: 'Invalid type' })); return }
|
|
323
258
|
res.setHeader('Content-Type', 'application/json')
|
|
324
259
|
res.writeHead(200)
|
|
@@ -375,9 +310,9 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
|
|
|
375
310
|
res.end(JSON.stringify({ error: 'Missing path parameter' }))
|
|
376
311
|
return
|
|
377
312
|
}
|
|
378
|
-
// Security: only allow reading
|
|
313
|
+
// Security: only allow reading files under .sillyspec/docs/
|
|
379
314
|
const normalizedPath = resolve(filePath)
|
|
380
|
-
if (!
|
|
315
|
+
if (!normalizedPath.includes('.sillyspec' + sep + 'docs')) {
|
|
381
316
|
res.writeHead(403)
|
|
382
317
|
res.end(JSON.stringify({ error: 'Access denied' }))
|
|
383
318
|
return
|
|
@@ -407,7 +342,7 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
|
|
|
407
342
|
res.end(JSON.stringify({ error: 'Missing project parameter' }))
|
|
408
343
|
return
|
|
409
344
|
}
|
|
410
|
-
const docs =
|
|
345
|
+
const docs = parseDocsTree(projectPath)
|
|
411
346
|
res.setHeader('Content-Type', 'application/json')
|
|
412
347
|
res.writeHead(200)
|
|
413
348
|
res.end(JSON.stringify(docs))
|
|
@@ -418,15 +353,8 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
|
|
|
418
353
|
const distDir = join(__dirname, '../dist')
|
|
419
354
|
const indexPath = join(distDir, 'index.html')
|
|
420
355
|
if (existsSync(distDir)) {
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
const filePath = resolve(distDir, requestPath === '/' ? 'index.html' : `.${requestPath}`)
|
|
424
|
-
if (!isInside(distDir, filePath)) {
|
|
425
|
-
res.writeHead(403)
|
|
426
|
-
res.end('Access denied')
|
|
427
|
-
return
|
|
428
|
-
}
|
|
429
|
-
if (existsSync(filePath)) {
|
|
356
|
+
const filePath = join(distDir, req.url === '/' ? 'index.html' : req.url.replace(/^\//, ''))
|
|
357
|
+
if (existsSync(filePath) && !filePath.includes('..')) {
|
|
430
358
|
const ext = filePath.split('.').pop()
|
|
431
359
|
const mimeTypes = { html: 'text/html', js: 'application/javascript', css: 'text/css', svg: 'image/svg+xml', png: 'image/png', jpg: 'image/jpeg' }
|
|
432
360
|
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream')
|
|
@@ -452,11 +380,7 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
|
|
|
452
380
|
console.error('WebSocket server error:', err)
|
|
453
381
|
})
|
|
454
382
|
|
|
455
|
-
wss.on('connection', (ws
|
|
456
|
-
if (!isLocalOrigin(req.headers.origin)) {
|
|
457
|
-
ws.close(1008, 'Forbidden origin')
|
|
458
|
-
return
|
|
459
|
-
}
|
|
383
|
+
wss.on('connection', (ws) => {
|
|
460
384
|
console.log('WebSocket client connected')
|
|
461
385
|
|
|
462
386
|
// Send initial projects list
|
|
@@ -510,11 +434,7 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
|
|
|
510
434
|
discoverProjects().then(projects => {
|
|
511
435
|
broadcast({
|
|
512
436
|
type: 'projects:updated',
|
|
513
|
-
data: projects.map(p => ({
|
|
514
|
-
...p,
|
|
515
|
-
state: parseProjectState(p.path),
|
|
516
|
-
overview: parseProjectOverview(p.path)
|
|
517
|
-
}))
|
|
437
|
+
data: projects.map(p => ({ ...p, state: parseProjectState(p.path) }))
|
|
518
438
|
})
|
|
519
439
|
})
|
|
520
440
|
}
|
|
@@ -530,7 +450,7 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
|
|
|
530
450
|
break
|
|
531
451
|
case 'docs:get':
|
|
532
452
|
if (data.data?.projectPath) {
|
|
533
|
-
const docs =
|
|
453
|
+
const docs = parseDocsTree(data.data.projectPath)
|
|
534
454
|
ws.send(JSON.stringify({ type: 'docs:tree', data: docs }))
|
|
535
455
|
}
|
|
536
456
|
break
|
|
@@ -565,10 +485,10 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
|
|
|
565
485
|
console.error('Failed to start file watcher:', err)
|
|
566
486
|
}
|
|
567
487
|
|
|
568
|
-
server.listen(port,
|
|
569
|
-
console.log(`Dashboard server running on http://
|
|
488
|
+
server.listen(port, () => {
|
|
489
|
+
console.log(`Dashboard server running on http://localhost:${port}`)
|
|
570
490
|
if (openBrowser) {
|
|
571
|
-
open(`http://
|
|
491
|
+
open(`http://localhost:${port}`)
|
|
572
492
|
}
|
|
573
493
|
})
|
|
574
494
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync, existsSync, readdirSync, statSync } from 'fs'
|
|
2
2
|
import { execSync } from 'child_process'
|
|
3
|
-
import { join
|
|
3
|
+
import { join } from 'path'
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
5
|
import { dirname } from 'path'
|
|
6
6
|
|
|
@@ -77,13 +77,18 @@ export function parseProjectOverview(projectPath) {
|
|
|
77
77
|
|
|
78
78
|
// --- Last active ---
|
|
79
79
|
const sillyspecDir = join(projectPath, '.sillyspec')
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const dbPath = join(sillyspecDir, '.runtime', 'sillyspec.db')
|
|
83
|
-
if (existsSync(dbPath)) {
|
|
80
|
+
const progressPath = join(sillyspecDir, '.runtime', 'progress.json')
|
|
81
|
+
if (existsSync(progressPath)) {
|
|
84
82
|
try {
|
|
85
|
-
const
|
|
86
|
-
|
|
83
|
+
const progress = JSON.parse(readFileSync(progressPath, 'utf-8'))
|
|
84
|
+
if (progress.stages) {
|
|
85
|
+
for (const stageData of Object.values(progress.stages)) {
|
|
86
|
+
if (stageData.lastActive && (!result.lastActive || new Date(stageData.lastActive) > new Date(result.lastActive))) {
|
|
87
|
+
result.lastActive = stageData.lastActive
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (progress.lastActive) result.lastActive = progress.lastActive
|
|
87
92
|
} catch {}
|
|
88
93
|
}
|
|
89
94
|
if (!result.lastActive) {
|
|
@@ -318,155 +323,82 @@ export function parseDocsTree(projectPath) {
|
|
|
318
323
|
return { groups }
|
|
319
324
|
}
|
|
320
325
|
|
|
321
|
-
const VIEWABLE_SILLYSPEC_DOC_EXTENSIONS = new Set([
|
|
322
|
-
'.md', '.markdown', '.mdx',
|
|
323
|
-
'.html', '.htm',
|
|
324
|
-
'.txt', '.log',
|
|
325
|
-
'.json', '.yaml', '.yml', '.toml',
|
|
326
|
-
'.xml', '.csv'
|
|
327
|
-
])
|
|
328
|
-
|
|
329
|
-
const SILLYSPEC_DOC_GROUPS = [
|
|
330
|
-
{ key: 'docs', label: '📚 docs', dir: 'docs' },
|
|
331
|
-
{ key: 'changes', label: '⚙️ changes', dir: 'changes' },
|
|
332
|
-
{ key: 'plans', label: '🧾 plans', dir: 'plans' },
|
|
333
|
-
{ key: 'quicklog', label: '⚡ quicklog', dir: 'quicklog' },
|
|
334
|
-
{ key: 'knowledge', label: '🧠 knowledge', dir: 'knowledge' },
|
|
335
|
-
{ key: 'projects', label: '📁 projects', dir: 'projects' },
|
|
336
|
-
{ key: 'workspace', label: '🗂️ workspace', dir: 'workspace' },
|
|
337
|
-
{ key: 'shared', label: '🔗 shared', dir: 'shared' },
|
|
338
|
-
{ key: 'runtime', label: '🧰 .runtime', dir: '.runtime' }
|
|
339
|
-
]
|
|
340
|
-
|
|
341
|
-
function sillyspecDocExt(fileName) {
|
|
342
|
-
const index = fileName.lastIndexOf('.')
|
|
343
|
-
return index === -1 ? '' : fileName.slice(index).toLowerCase()
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function isViewableSillyspecDoc(fileName) {
|
|
347
|
-
return VIEWABLE_SILLYSPEC_DOC_EXTENSIONS.has(sillyspecDocExt(fileName))
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function titleFromSillyspecDoc(filePath, fileName) {
|
|
351
|
-
const ext = sillyspecDocExt(fileName)
|
|
352
|
-
if (ext === '.md' || ext === '.markdown' || ext === '.mdx') {
|
|
353
|
-
try {
|
|
354
|
-
const content = readFileSync(filePath, 'utf-8')
|
|
355
|
-
const titleMatch = content.match(/^#\s+(.+)$/m)
|
|
356
|
-
if (titleMatch) return titleMatch[1]
|
|
357
|
-
} catch {}
|
|
358
|
-
}
|
|
359
|
-
return fileName.replace(/\.(md|markdown|mdx|html?|txt|log|json|ya?ml|toml|xml|csv)$/i, '')
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function listSillyspecDocsRecursive(dir, rootDir) {
|
|
363
|
-
const files = []
|
|
364
|
-
try {
|
|
365
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
366
|
-
const filePath = join(dir, entry.name)
|
|
367
|
-
if (entry.isDirectory()) {
|
|
368
|
-
files.push(...listSillyspecDocsRecursive(filePath, rootDir))
|
|
369
|
-
} else if (entry.isFile() && isViewableSillyspecDoc(entry.name)) {
|
|
370
|
-
const s = statSync(filePath)
|
|
371
|
-
files.push({
|
|
372
|
-
name: relative(rootDir, filePath).split(sep).join('/'),
|
|
373
|
-
path: filePath,
|
|
374
|
-
title: titleFromSillyspecDoc(filePath, entry.name),
|
|
375
|
-
extension: sillyspecDocExt(entry.name).slice(1),
|
|
376
|
-
size: s.size,
|
|
377
|
-
mtime: s.mtime.toISOString()
|
|
378
|
-
})
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
} catch {}
|
|
382
|
-
return files
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
export function parseSillyspecDocsTree(projectPath) {
|
|
386
|
-
const sillyspecDir = join(projectPath, '.sillyspec')
|
|
387
|
-
if (!existsSync(sillyspecDir)) return { groups: [] }
|
|
388
|
-
|
|
389
|
-
const groups = []
|
|
390
|
-
const seen = new Set()
|
|
391
|
-
|
|
392
|
-
for (const group of SILLYSPEC_DOC_GROUPS) {
|
|
393
|
-
const groupDir = join(sillyspecDir, group.dir)
|
|
394
|
-
if (!existsSync(groupDir)) continue
|
|
395
|
-
|
|
396
|
-
const files = listSillyspecDocsRecursive(groupDir, groupDir)
|
|
397
|
-
.sort((a, b) => a.name.localeCompare(b.name))
|
|
398
|
-
|
|
399
|
-
if (files.length === 0) continue
|
|
400
|
-
for (const file of files) seen.add(file.path)
|
|
401
|
-
groups.push({ key: group.key, label: group.label, project: '.sillyspec', files })
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const rootFiles = []
|
|
405
|
-
try {
|
|
406
|
-
for (const entry of readdirSync(sillyspecDir, { withFileTypes: true })) {
|
|
407
|
-
if (!entry.isFile() || !isViewableSillyspecDoc(entry.name)) continue
|
|
408
|
-
const filePath = join(sillyspecDir, entry.name)
|
|
409
|
-
if (seen.has(filePath)) continue
|
|
410
|
-
const s = statSync(filePath)
|
|
411
|
-
rootFiles.push({
|
|
412
|
-
name: entry.name,
|
|
413
|
-
path: filePath,
|
|
414
|
-
title: titleFromSillyspecDoc(filePath, entry.name),
|
|
415
|
-
extension: sillyspecDocExt(entry.name).slice(1),
|
|
416
|
-
size: s.size,
|
|
417
|
-
mtime: s.mtime.toISOString()
|
|
418
|
-
})
|
|
419
|
-
}
|
|
420
|
-
} catch {}
|
|
421
|
-
|
|
422
|
-
if (rootFiles.length > 0) {
|
|
423
|
-
groups.unshift({ key: 'root', label: '📄 .sillyspec', project: '.sillyspec', files: rootFiles })
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
return { groups }
|
|
427
|
-
}
|
|
428
|
-
|
|
429
326
|
/**
|
|
430
|
-
* Parse project state from .sillyspec
|
|
431
|
-
* Progress data is stored in SQLite (.sillyspec/.runtime/sillyspec.db),
|
|
432
|
-
* accessed through `sillyspec progress show`.
|
|
327
|
+
* Parse project state from .sillyspec directory
|
|
433
328
|
* @param {string} projectPath - Path to the project directory
|
|
434
|
-
* @returns {object
|
|
329
|
+
* @returns {object} Project state with currentStage, nextStep, progress, stages, specs, lastActive
|
|
435
330
|
*/
|
|
436
331
|
export function parseProjectState(projectPath) {
|
|
437
332
|
const sillyspecDir = join(projectPath, '.sillyspec')
|
|
438
|
-
const dbPath = join(sillyspecDir, '.runtime', 'sillyspec.db')
|
|
439
333
|
|
|
440
|
-
if (!existsSync(sillyspecDir)
|
|
334
|
+
if (!existsSync(sillyspecDir)) {
|
|
441
335
|
return null
|
|
442
336
|
}
|
|
443
337
|
|
|
444
338
|
let currentStage = ''
|
|
339
|
+
let nextStep = null
|
|
340
|
+
let progress = { stages: {} }
|
|
341
|
+
let stages = []
|
|
342
|
+
let specs = []
|
|
445
343
|
let lastActive = null
|
|
446
344
|
|
|
447
|
-
//
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
345
|
+
// Read progress.json for current stage
|
|
346
|
+
const progressPath = join(sillyspecDir, '.runtime', 'progress.json')
|
|
347
|
+
if (existsSync(progressPath)) {
|
|
348
|
+
try {
|
|
349
|
+
const progressData = JSON.parse(readFileSync(progressPath, 'utf-8'))
|
|
350
|
+
progress = progressData
|
|
351
|
+
currentStage = progressData.currentStage || ''
|
|
352
|
+
stages = Object.keys(progressData.stages || {})
|
|
353
|
+
|
|
354
|
+
// Find last active
|
|
355
|
+
if (progressData.lastActive) lastActive = progressData.lastActive
|
|
356
|
+
if (progressData.stages) {
|
|
357
|
+
for (const [stageName, stageData] of Object.entries(progressData.stages)) {
|
|
358
|
+
if (stageData.lastActive || stageData.startedAt) {
|
|
359
|
+
const t = stageData.lastActive || stageData.startedAt
|
|
360
|
+
if (!lastActive || new Date(t) > new Date(lastActive)) lastActive = t
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} catch (err) {
|
|
365
|
+
// Progress file exists but couldn't be parsed
|
|
366
|
+
}
|
|
367
|
+
}
|
|
452
368
|
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
369
|
+
// List all spec files
|
|
370
|
+
const specsDir = join(sillyspecDir, 'specs')
|
|
371
|
+
if (existsSync(specsDir)) {
|
|
372
|
+
try {
|
|
373
|
+
const specFiles = readdirSync(specsDir)
|
|
374
|
+
.filter(f => f.endsWith('.md'))
|
|
375
|
+
.sort()
|
|
376
|
+
|
|
377
|
+
specs = specFiles.map(f => {
|
|
378
|
+
const specPath = join(specsDir, f)
|
|
379
|
+
try {
|
|
380
|
+
const content = readFileSync(specPath, 'utf-8')
|
|
381
|
+
const titleMatch = content.match(/^#\s+(.+)$/m)
|
|
382
|
+
return {
|
|
383
|
+
name: f,
|
|
384
|
+
title: titleMatch ? titleMatch[1] : f,
|
|
385
|
+
path: specPath
|
|
386
|
+
}
|
|
387
|
+
} catch {
|
|
388
|
+
return { name: f, title: f, path: specPath }
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
} catch (err) {
|
|
392
|
+
// Specs directory couldn't be read
|
|
393
|
+
}
|
|
462
394
|
}
|
|
463
395
|
|
|
464
396
|
return {
|
|
465
397
|
currentStage,
|
|
466
|
-
nextStep
|
|
467
|
-
progress
|
|
468
|
-
stages
|
|
469
|
-
specs
|
|
398
|
+
nextStep,
|
|
399
|
+
progress,
|
|
400
|
+
stages,
|
|
401
|
+
specs,
|
|
470
402
|
lastActive
|
|
471
403
|
}
|
|
472
404
|
}
|
|
@@ -2,7 +2,7 @@ import chokidar from 'chokidar'
|
|
|
2
2
|
import { join, basename, dirname, sep } from 'path'
|
|
3
3
|
import { homedir } from 'os'
|
|
4
4
|
import { existsSync, readdirSync, realpathSync } from 'fs'
|
|
5
|
-
import { parseProjectState
|
|
5
|
+
import { parseProjectState } from './parser.js'
|
|
6
6
|
|
|
7
7
|
let watcher = null
|
|
8
8
|
let updateCallback = null
|
|
@@ -146,12 +146,8 @@ function scanSelf(seen) {
|
|
|
146
146
|
const cwd = process.cwd()
|
|
147
147
|
const projects = []
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const normalizedPath = realPath.toLowerCase()
|
|
152
|
-
|
|
153
|
-
if (!seen.has(normalizedPath)) {
|
|
154
|
-
seen.add(normalizedPath)
|
|
149
|
+
if (!seen.has(cwd)) {
|
|
150
|
+
seen.add(cwd)
|
|
155
151
|
const sillyspecPath = join(cwd, '.sillyspec')
|
|
156
152
|
if (existsSync(sillyspecPath)) {
|
|
157
153
|
projects.push({
|
|
@@ -204,9 +200,8 @@ export function startWatcher(callback) {
|
|
|
204
200
|
// Parse initial states
|
|
205
201
|
for (const project of projects) {
|
|
206
202
|
const state = parseProjectState(project.path)
|
|
207
|
-
const overview = parseProjectOverview(project.path)
|
|
208
203
|
if (state) {
|
|
209
|
-
projectStates.set(project.name, { ...project, state
|
|
204
|
+
projectStates.set(project.name, { ...project, state })
|
|
210
205
|
}
|
|
211
206
|
}
|
|
212
207
|
|
|
@@ -266,9 +261,8 @@ async function handleFileChange(filePath) {
|
|
|
266
261
|
const project = projectStates.get(projectName)
|
|
267
262
|
if (project) {
|
|
268
263
|
const newState = parseProjectState(project.path)
|
|
269
|
-
const overview = parseProjectOverview(project.path)
|
|
270
264
|
if (newState) {
|
|
271
|
-
projectStates.set(projectName, { ...project, state: newState
|
|
265
|
+
projectStates.set(projectName, { ...project, state: newState })
|
|
272
266
|
}
|
|
273
267
|
}
|
|
274
268
|
|
|
@@ -286,13 +280,11 @@ async function rescanProjects() {
|
|
|
286
280
|
for (const project of projects) {
|
|
287
281
|
if (!projectStates.has(project.name)) {
|
|
288
282
|
const state = parseProjectState(project.path)
|
|
289
|
-
const overview = parseProjectOverview(project.path)
|
|
290
283
|
if (state) {
|
|
291
284
|
projectStates.set(project.name, {
|
|
292
285
|
name: project.name,
|
|
293
286
|
path: project.path,
|
|
294
|
-
state
|
|
295
|
-
overview
|
|
287
|
+
state
|
|
296
288
|
})
|
|
297
289
|
}
|
|
298
290
|
}
|