dreamcontext 0.6.0 → 0.7.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.
- package/README.md +2 -1
- package/agents/sleep-product.md +19 -2
- package/agents/sleep-state.md +43 -22
- package/agents/sleep-tasks.md +38 -0
- package/dist/agents/sleep-product.md +19 -2
- package/dist/agents/sleep-state.md +43 -22
- package/dist/agents/sleep-tasks.md +38 -0
- package/dist/dashboard/assets/{BrainCanvas3D-LLqVeXtb.js → BrainCanvas3D-Bb8WCKrn.js} +21 -21
- package/dist/dashboard/assets/{_baseUniq-DW0uA0ty.js → _baseUniq-BatmaIH2.js} +1 -1
- package/dist/dashboard/assets/ar-SA-G6X2FPQ2-D5ja_wjH.js +10 -0
- package/dist/dashboard/assets/{arc-fVUGtlbL.js → arc-DqjLlF-x.js} +1 -1
- package/dist/dashboard/assets/{architectureDiagram-Q4EWVU46-Dz2PKBsS.js → architectureDiagram-Q4EWVU46-u9hbL6uu.js} +1 -1
- package/dist/dashboard/assets/az-AZ-76LH7QW2-CCrmRwql.js +1 -0
- package/dist/dashboard/assets/bg-BG-XCXSNQG7-ByFwPOzm.js +5 -0
- package/dist/dashboard/assets/{blockDiagram-DXYQGD6D-ByWKxzhL.js → blockDiagram-DXYQGD6D-dX-e3JNh.js} +1 -1
- package/dist/dashboard/assets/bn-BD-2XOGV67Q-CqnEIuTG.js +5 -0
- package/dist/dashboard/assets/{c4Diagram-AHTNJAMY-dpHsVM3D.js → c4Diagram-AHTNJAMY-DAT2Y4Ei.js} +1 -1
- package/dist/dashboard/assets/ca-ES-6MX7JW3Y-DxUow-Oi.js +8 -0
- package/dist/dashboard/assets/channel-JlaJkLX7.js +1 -0
- package/dist/dashboard/assets/{chunk-4BX2VUAB-HEXb6Yg5.js → chunk-4BX2VUAB-ng6_uP1b.js} +1 -1
- package/dist/dashboard/assets/{chunk-4TB4RGXK-DeVy5g6H.js → chunk-4TB4RGXK-JyQRd6F4.js} +1 -1
- package/dist/dashboard/assets/{chunk-55IACEB6-DGy3ZgDZ.js → chunk-55IACEB6-WENWKOcW.js} +1 -1
- package/dist/dashboard/assets/{chunk-EDXVE4YY-CYJehjz4.js → chunk-EDXVE4YY-BupZpcZ0.js} +1 -1
- package/dist/dashboard/assets/{chunk-FMBD7UC4-DKTOmvgZ.js → chunk-FMBD7UC4-6-Dp_BQr.js} +1 -1
- package/dist/dashboard/assets/{chunk-OYMX7WX6-mmtWyiDA.js → chunk-OYMX7WX6-CRMXMXMR.js} +1 -1
- package/dist/dashboard/assets/{chunk-QZHKN3VN-DSdV0iwY.js → chunk-QZHKN3VN-CdV3chea.js} +1 -1
- package/dist/dashboard/assets/{chunk-YZCP3GAM-DbY-KoDn.js → chunk-YZCP3GAM-DGd7UTQr.js} +1 -1
- package/dist/dashboard/assets/classDiagram-6PBFFD2Q-BBVZgLT7.js +1 -0
- package/dist/dashboard/assets/classDiagram-v2-HSJHXN6E-BBVZgLT7.js +1 -0
- package/dist/dashboard/assets/clone-DSjY5D8g.js +1 -0
- package/dist/dashboard/assets/{cose-bilkent-S5V4N54A-p0t1DY88.js → cose-bilkent-S5V4N54A-86wk8_0k.js} +1 -1
- package/dist/dashboard/assets/cs-CZ-2BRQDIVT-Vsx90G3k.js +11 -0
- package/dist/dashboard/assets/da-DK-5WZEPLOC-BqlSmIrC.js +5 -0
- package/dist/dashboard/assets/{dagre-KV5264BT-DYE0WzHM.js → dagre-KV5264BT-BsnIu1de.js} +1 -1
- package/dist/dashboard/assets/de-DE-XR44H4JA-D1X2JmBd.js +8 -0
- package/dist/dashboard/assets/{diagram-5BDNPKRD-D9EiQCOP.js → diagram-5BDNPKRD-BfKZpkO_.js} +1 -1
- package/dist/dashboard/assets/{diagram-G4DWMVQ6-poDXSAfu.js → diagram-G4DWMVQ6-BGEdOZis.js} +1 -1
- package/dist/dashboard/assets/{diagram-MMDJMWI5-CHZ7wgN1.js → diagram-MMDJMWI5-4PK6U-9k.js} +1 -1
- package/dist/dashboard/assets/{diagram-TYMM5635-DVk6fYG7.js → diagram-TYMM5635-Co4XFxnB.js} +1 -1
- package/dist/dashboard/assets/directory-open-01563666-DWU9wJ6I.js +1 -0
- package/dist/dashboard/assets/directory-open-4ed118d0-CunoC1EB.js +1 -0
- package/dist/dashboard/assets/el-GR-BZB4AONW-JfJ7Iw6d.js +10 -0
- package/dist/dashboard/assets/{erDiagram-SMLLAGMA-D2sqkGin.js → erDiagram-SMLLAGMA-tggQbGeM.js} +1 -1
- package/dist/dashboard/assets/es-ES-U4NZUMDT-CrTG5SH1.js +9 -0
- package/dist/dashboard/assets/eu-ES-A7QVB2H4-CLi-f2S4.js +11 -0
- package/dist/dashboard/assets/extends-CF3RwP-h.js +1 -0
- package/dist/dashboard/assets/fa-IR-HGAKTJCU-B78t-ifl.js +8 -0
- package/dist/dashboard/assets/fi-FI-Z5N7JZ37-BiYGo0tM.js +6 -0
- package/dist/dashboard/assets/file-open-002ab408-DIuFHtCF.js +1 -0
- package/dist/dashboard/assets/file-open-7c801643-684qeFg4.js +1 -0
- package/dist/dashboard/assets/file-save-3189631c-C1wFhQhH.js +1 -0
- package/dist/dashboard/assets/file-save-745eba88-Bb9F9Kg7.js +1 -0
- package/dist/dashboard/assets/{flowDiagram-DWJPFMVM-DrMlKBYA.js → flowDiagram-DWJPFMVM-DfOOuTHa.js} +1 -1
- package/dist/dashboard/assets/fr-FR-RHASNOE6-CHuvxlm9.js +9 -0
- package/dist/dashboard/assets/{ganttDiagram-T4ZO3ILL-Cf4FbEFp.js → ganttDiagram-T4ZO3ILL-CqRBfUlO.js} +1 -1
- package/dist/dashboard/assets/{gitGraphDiagram-UUTBAWPF-BsbOq1C9.js → gitGraphDiagram-UUTBAWPF-DCooi0ou.js} +1 -1
- package/dist/dashboard/assets/gl-ES-HMX3MZ6V-DloFVgGH.js +10 -0
- package/dist/dashboard/assets/{graph-CUxxgCtS.js → graph-CbTgvSod.js} +1 -1
- package/dist/dashboard/assets/he-IL-6SHJWFNN-sKyHtCj5.js +10 -0
- package/dist/dashboard/assets/hi-IN-IWLTKZ5I-BDBktoGz.js +4 -0
- package/dist/dashboard/assets/hu-HU-A5ZG7DT2-CI0m6WdK.js +7 -0
- package/dist/dashboard/assets/id-ID-SAP4L64H-o3oCWYJ1.js +10 -0
- package/dist/dashboard/assets/image-blob-reduce.esm-D6s-rqMO.js +7 -0
- package/dist/dashboard/assets/index-BMwAG0PF.css +1 -0
- package/dist/dashboard/assets/index-BzUItYQF.js +19 -0
- package/dist/dashboard/assets/index-CIMJcxbn.js +480 -0
- package/dist/dashboard/assets/{infoDiagram-42DDH7IO-DTkMnZiD.js → infoDiagram-42DDH7IO-DvXAiIKM.js} +1 -1
- package/dist/dashboard/assets/{ishikawaDiagram-UXIWVN3A-CahQ348K.js → ishikawaDiagram-UXIWVN3A-B3k3F-SR.js} +1 -1
- package/dist/dashboard/assets/it-IT-JPQ66NNP-B69FvIBl.js +11 -0
- package/dist/dashboard/assets/ja-JP-DBVTYXUO-09Dz5R3w.js +8 -0
- package/dist/dashboard/assets/{journeyDiagram-VCZTEJTY-BDY2Nslc.js → journeyDiagram-VCZTEJTY-3sYlQlzX.js} +1 -1
- package/dist/dashboard/assets/kaa-6HZHGXH3-SFvv5G_m.js +1 -0
- package/dist/dashboard/assets/kab-KAB-ZGHBKWFO-Cj4UcDfk.js +8 -0
- package/dist/dashboard/assets/{kanban-definition-6JOO6SKY-CgrwoTjI.js → kanban-definition-6JOO6SKY-cGLqpTJv.js} +1 -1
- package/dist/dashboard/assets/kk-KZ-P5N5QNE5-BVT_-IeJ.js +1 -0
- package/dist/dashboard/assets/km-KH-HSX4SM5Z-BpH_zneK.js +11 -0
- package/dist/dashboard/assets/ko-KR-MTYHY66A-BWtVfnIt.js +9 -0
- package/dist/dashboard/assets/ku-TR-6OUDTVRD-DKg1sGxW.js +9 -0
- package/dist/dashboard/assets/{layout-IaUxkFkm.js → layout-gKZamora.js} +1 -1
- package/dist/dashboard/assets/{linear-shc0iNFn.js → linear-DOSQ02II.js} +1 -1
- package/dist/dashboard/assets/lt-LT-XHIRWOB4-DFICvEvq.js +3 -0
- package/dist/dashboard/assets/lv-LV-5QDEKY6T-CeIp3POy.js +7 -0
- package/dist/dashboard/assets/min-BD9nuOyV.js +1 -0
- package/dist/dashboard/assets/{mindmap-definition-QFDTVHPH-GmO0JRtA.js → mindmap-definition-QFDTVHPH-DNg9-qbI.js} +7 -7
- package/dist/dashboard/assets/mr-IN-CRQNXWMA-CMiofriy.js +13 -0
- package/dist/dashboard/assets/my-MM-5M5IBNSE-WhbW1bUQ.js +1 -0
- package/dist/dashboard/assets/nb-NO-T6EIAALU-kvj3GRtN.js +10 -0
- package/dist/dashboard/assets/nl-NL-IS3SIHDZ-BdYOVmBV.js +8 -0
- package/dist/dashboard/assets/nn-NO-6E72VCQL-BAI1MQoO.js +8 -0
- package/dist/dashboard/assets/oc-FR-POXYY2M6-ChavJGTr.js +8 -0
- package/dist/dashboard/assets/pa-IN-N4M65BXN-AWmvLRaa.js +4 -0
- package/dist/dashboard/assets/percentages-BXMCSKIN-QYboYPU4.js +215 -0
- package/dist/dashboard/assets/pica-DzAIGpll.js +7 -0
- package/dist/dashboard/assets/{pieDiagram-DEJITSTG-Bfm_toYA.js → pieDiagram-DEJITSTG-C4ZFpuC0.js} +1 -1
- package/dist/dashboard/assets/pl-PL-T2D74RX3-C06LuJit.js +9 -0
- package/dist/dashboard/assets/pt-BR-5N22H2LF-DP-AXG39.js +9 -0
- package/dist/dashboard/assets/pt-PT-UZXXM6DQ-T-2COPG-.js +9 -0
- package/dist/dashboard/assets/{quadrantDiagram-34T5L4WZ-DIjTM3lm.js → quadrantDiagram-34T5L4WZ-yvEQRUmq.js} +1 -1
- package/dist/dashboard/assets/{requirementDiagram-MS252O5E-B9AEoGYh.js → requirementDiagram-MS252O5E-BkjbZkw1.js} +1 -1
- package/dist/dashboard/assets/ro-RO-JPDTUUEW-V8ShMmhj.js +11 -0
- package/dist/dashboard/assets/roundRect-0PYZxl1G.js +1 -0
- package/dist/dashboard/assets/ru-RU-B4JR7IUQ-Bk5QWjQg.js +9 -0
- package/dist/dashboard/assets/{sankeyDiagram-XADWPNL6-C_O5XLXm.js → sankeyDiagram-XADWPNL6-_6wZr0Q4.js} +1 -1
- package/dist/dashboard/assets/{sequenceDiagram-FGHM5R23-CHSxSvmJ.js → sequenceDiagram-FGHM5R23-B03MUCIO.js} +1 -1
- package/dist/dashboard/assets/si-LK-N5RQ5JYF-DrLyNjX_.js +1 -0
- package/dist/dashboard/assets/sk-SK-C5VTKIMK-MG2XJDav.js +6 -0
- package/dist/dashboard/assets/sl-SI-NN7IZMDC-DyF2jYcb.js +6 -0
- package/dist/dashboard/assets/{stateDiagram-FHFEXIEX-B0URrhHw.js → stateDiagram-FHFEXIEX-DW9W8hRb.js} +1 -1
- package/dist/dashboard/assets/stateDiagram-v2-QKLJ7IA2-DGLHFCvB.js +1 -0
- package/dist/dashboard/assets/subset-shared.chunk-DxnLkUkc.js +84 -0
- package/dist/dashboard/assets/subset-worker.chunk-BCGTROg4.js +1 -0
- package/dist/dashboard/assets/sv-SE-XGPEYMSR-BJOlh7Ta.js +10 -0
- package/dist/dashboard/assets/ta-IN-2NMHFXQM-DbvdzD_M.js +9 -0
- package/dist/dashboard/assets/th-TH-HPSO5L25-BZoStbWu.js +2 -0
- package/dist/dashboard/assets/{timeline-definition-GMOUNBTQ-CycU2gVC.js → timeline-definition-GMOUNBTQ-Cq3P6C9q.js} +1 -1
- package/dist/dashboard/assets/tr-TR-DEFEU3FU-D0q3dp6Y.js +7 -0
- package/dist/dashboard/assets/uk-UA-QMV73CPH-KQC76_Jh.js +6 -0
- package/dist/dashboard/assets/{vennDiagram-DHZGUBPP-DcU2536G.js → vennDiagram-DHZGUBPP-BO3azTdL.js} +1 -1
- package/dist/dashboard/assets/vi-VN-M7AON7JQ-BsyXHyOP.js +5 -0
- package/dist/dashboard/assets/{wardley-RL74JXVD-BWqqaX3S.js → wardley-RL74JXVD-BjaWFGf-.js} +1 -1
- package/dist/dashboard/assets/{wardleyDiagram-NUSXRM2D-CUnIHJgd.js → wardleyDiagram-NUSXRM2D-D0Ghwhzw.js} +1 -1
- package/dist/dashboard/assets/{xychartDiagram-5P7HB3ND-b9fcOYTj.js → xychartDiagram-5P7HB3ND-D1QYeAFg.js} +1 -1
- package/dist/dashboard/assets/zh-CN-LNUGB5OW-Cg54ea9D.js +10 -0
- package/dist/dashboard/assets/zh-HK-E62DVLB3-N7JXSzdg.js +1 -0
- package/dist/dashboard/assets/zh-TW-RAJ6MFWO-CTX6fzNp.js +9 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/index.js +2519 -1270
- package/dist/skill-packs/excalidraw/SKILL.md +82 -3
- package/dist/skill-packs/video-watching/SKILL.md +54 -13
- package/dist/skill-packs/video-watching/scripts/build_frame_index.py +61 -10
- package/dist/skill-packs/video-watching/scripts/transcribe.sh +147 -54
- package/dist/templates/init/data-structures/default.md +26 -24
- package/package.json +1 -1
- package/skill/SKILL.md +48 -12
- package/skill-packs/excalidraw/SKILL.md +82 -3
- package/skill-packs/video-watching/SKILL.md +54 -13
- package/skill-packs/video-watching/scripts/build_frame_index.py +61 -10
- package/skill-packs/video-watching/scripts/transcribe.sh +147 -54
- package/dist/dashboard/assets/channel-zrLwggBX.js +0 -1
- package/dist/dashboard/assets/classDiagram-6PBFFD2Q-BvejNiwH.js +0 -1
- package/dist/dashboard/assets/classDiagram-v2-HSJHXN6E-BvejNiwH.js +0 -1
- package/dist/dashboard/assets/clone-BFVdml6g.js +0 -1
- package/dist/dashboard/assets/index-CqSkXBSu.css +0 -1
- package/dist/dashboard/assets/index-flRpQtDj.js +0 -476
- package/dist/dashboard/assets/min-BIL7YgTN.js +0 -1
- package/dist/dashboard/assets/stateDiagram-v2-QKLJ7IA2-FdAb9MBo.js +0 -1
- package/dist/skill-packs/video-watching/scripts/gap_fill.py +0 -45
- package/skill-packs/video-watching/scripts/gap_fill.py +0 -45
|
@@ -3,33 +3,35 @@ name: {{PRODUCT_NAME}}
|
|
|
3
3
|
description: Data structures for {{PRODUCT_NAME}}
|
|
4
4
|
type: data-structures
|
|
5
5
|
product: {{PRODUCT_NAME}}
|
|
6
|
+
tags:
|
|
7
|
+
- data-structures
|
|
8
|
+
- database
|
|
9
|
+
- schema
|
|
6
10
|
updated: {{DATE}}
|
|
7
11
|
---
|
|
8
|
-
|
|
9
|
-
# Data Structures — {{PRODUCT_NAME}}
|
|
10
|
-
|
|
11
|
-
Document this product's database schema, key models, and API contracts here. This file is the source of truth for data shapes.
|
|
12
|
-
|
|
13
|
-
## Conventions
|
|
14
|
-
|
|
15
|
-
- One `data-structures/<product>.md` file per product in a monorepo. `default.md` is the single-product fallback.
|
|
16
|
-
- Sleep-state applies the **single-observation gate** to this file: a single session that adds, removes, or changes a schema MUST be reflected here in the same sleep cycle. No pattern repetition required.
|
|
17
|
-
- Tasks MAY include `product: <name>` in frontmatter to route data-structure observations to the matching product file.
|
|
18
|
-
|
|
19
|
-
## Schema
|
|
20
|
-
|
|
21
|
-
<!-- Example:
|
|
22
|
-
|
|
23
12
|
```sql
|
|
13
|
+
-- Data Structures — {{PRODUCT_NAME}}
|
|
14
|
+
-- Updated: {{DATE}}
|
|
15
|
+
--
|
|
16
|
+
-- Document all tables, models, and key JSON shapes here.
|
|
17
|
+
-- One observation gate: a schema change in a session MUST be reflected here
|
|
18
|
+
-- in the same sleep cycle. No pattern repetition required.
|
|
19
|
+
--
|
|
20
|
+
-- Convention:
|
|
21
|
+
-- - Single-product projects use default.md.
|
|
22
|
+
-- - Multi-product monorepos use <product>.md per product.
|
|
23
|
+
-- - The body should always be a ```sql fenced block for dashboard highlighting.
|
|
24
|
+
-- - Use SQL comments (-- ...) for documentation and guidance.
|
|
25
|
+
|
|
26
|
+
-- ============================================================
|
|
27
|
+
-- EXAMPLE — replace or extend with your actual schema
|
|
28
|
+
-- ============================================================
|
|
29
|
+
|
|
24
30
|
CREATE TABLE users (
|
|
25
|
-
id
|
|
26
|
-
email
|
|
27
|
-
created_at
|
|
31
|
+
id UUID PRIMARY KEY,
|
|
32
|
+
email VARCHAR(255) NOT NULL UNIQUE,
|
|
33
|
+
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
|
28
34
|
);
|
|
29
|
-
```
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## API Contracts
|
|
34
|
-
|
|
35
|
-
<!-- Document request/response shapes, route handlers, and external API integrations relevant to this product. -->
|
|
36
|
+
-- Add additional tables, indexes, and JSON model shapes below.
|
|
37
|
+
```
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -87,7 +87,7 @@ All three are auto-loaded every session via the SessionStart hook.
|
|
|
87
87
|
Every session start injects these automatically (zero tool calls needed):
|
|
88
88
|
|
|
89
89
|
- **Soul, User, Memory** -- full content
|
|
90
|
-
- **Extended core files index** -- names/types of style guide, tech stack,
|
|
90
|
+
- **Extended core files index** -- names/types of style guide, tech stack, system flow
|
|
91
91
|
- **Active tasks** -- status, priority, last updated (answer "which tasks are active?" directly from this, zero tool calls needed)
|
|
92
92
|
- **Bookmarks** -- tagged important moments from previous sessions, ordered by salience
|
|
93
93
|
- **Contextual reminders** -- matching triggers for active tasks (prospective memory)
|
|
@@ -115,7 +115,7 @@ Decide dynamically. Match the task to what you need. Choose the right operation
|
|
|
115
115
|
| `core/features/<name>.md` | READ | Feature scoping, sprint work, planning, "what's next" questions |
|
|
116
116
|
| `core/3.style_guide_and_branding.md` | READ | UI/UX work, frontend, branding, copy, design tasks |
|
|
117
117
|
| `core/4.tech_stack.md` | READ | Architecture decisions, integrations, dependency questions, infra |
|
|
118
|
-
| `
|
|
118
|
+
| `knowledge/data-structures/<product>.md` (or `default.md`) | READ or SEARCH | Database work, API design, schema changes, data modeling (recall-indexed like all knowledge) |
|
|
119
119
|
| `core/CHANGELOG.json` | SEARCH | Bug investigations, "what changed recently?" |
|
|
120
120
|
| `core/RELEASES.json` | SEARCH | "Which release shipped this?", rollback decisions |
|
|
121
121
|
| `state/<task>.md` | READ | Continuing previous work. Changelog section = where you left off |
|
|
@@ -134,17 +134,17 @@ When debugging (e.g., "notifications are broken"):
|
|
|
134
134
|
|
|
135
135
|
Some projects are monorepos with multiple products. Init asks "Is this a monorepo with multiple products?" and records the product list in `_dream_context/state/.config.json` under `multiProduct: string[] | false`. When products are configured:
|
|
136
136
|
|
|
137
|
-
- **Per-product data structures** live at `_dream_context/
|
|
137
|
+
- **Per-product data structures** live at `_dream_context/knowledge/data-structures/<product>.md` (single-product projects use `default.md`). They're knowledge files — recall-indexed, staleness-tracked, owned by `sleep-product`. **Body format: a single \`\`\`sql fenced block** with SQL comments (`-- ...`) for documentation — this is what the dashboard highlights. `migrateDataStructures` auto-wraps unfenced bodies; `dreamcontext init` scaffolds the fenced template.
|
|
138
138
|
- **Per-product knowledge** lives at `_dream_context/knowledge/products/<product>.md`. Cross-cutting knowledge still lives at the top-level `knowledge/`.
|
|
139
139
|
- **Tasks** MAY include `product: <name>` in frontmatter. The dashboard / CLI surfaces a product filter when `.config.json` lists products.
|
|
140
140
|
- **Auto-injection — handled by the SessionStart hook.** The hook (`npx dreamcontext hook session-start` → `generateSnapshot()`) resolves the active task (override file `_dream_context/state/.active-task`, or fallback: most recently modified task with status `in_progress`). If that task's frontmatter has `product: <name>` and `<name>` is listed under `multiProduct`, the hook injects the body of `_dream_context/knowledge/products/<name>.md` into the snapshot under an `## Active Product Knowledge: <name>` section (capped at 200 lines, with a "read full" pointer if truncated). You don't need to remember to load it — it's already in your context. Cross-cutting knowledge still lives at the top-level `knowledge/`.
|
|
141
141
|
- **Feature PRDs** MAY include `product: <name>` in frontmatter for product scoping; they still live in the flat `core/features/` directory.
|
|
142
142
|
|
|
143
|
-
If `multiProduct` is `false` or missing, treat the project as single-product and use `data-structures/default.md` exclusively. The SessionStart hook no-ops the product-knowledge injection in that case.
|
|
143
|
+
If `multiProduct` is `false` or missing, treat the project as single-product and use `knowledge/data-structures/default.md` exclusively. The SessionStart hook no-ops the product-knowledge injection in that case.
|
|
144
144
|
|
|
145
145
|
### Extended Core Files (3+)
|
|
146
146
|
|
|
147
|
-
Beyond the auto-loaded soul/user/memory, projects define additional core files (style guide, tech stack,
|
|
147
|
+
Beyond the auto-loaded soul/user/memory, projects define additional core files (style guide, tech stack, and potentially more). (Data structures are no longer a core file — they live under `knowledge/data-structures/` as recall-indexed knowledge.)
|
|
148
148
|
|
|
149
149
|
**Discovery protocol**:
|
|
150
150
|
1. The extended core files index is auto-loaded each session (names and types visible).
|
|
@@ -366,6 +366,18 @@ Tasks are your **working documents**. All context, decisions, user stories, acce
|
|
|
366
366
|
dreamcontext tasks list # List non-completed tasks (or --all for everything)
|
|
367
367
|
Read _dream_context/state/<task>.md # Load full context (Why + Changelog = where you left off)
|
|
368
368
|
|
|
369
|
+
# Slice the list — filters compose (AND), and stack with --status / --all
|
|
370
|
+
dreamcontext tasks list --version S5 # All tasks in version/milestone S5 (no more frontmatter scraping)
|
|
371
|
+
dreamcontext tasks list --tag memoryos --tag backend --status todo # --tag is repeatable, AND semantics (must have ALL)
|
|
372
|
+
dreamcontext tasks list --any-tag lina --any-tag studio # --any-tag is repeatable, OR semantics (at least one)
|
|
373
|
+
dreamcontext tasks list --priority critical # critical | high | medium | low
|
|
374
|
+
dreamcontext tasks list --feature recall-engine # match related_feature
|
|
375
|
+
dreamcontext tasks list --long # also show version + tags inline
|
|
376
|
+
dreamcontext tasks list --group-by version --all # sectioned output with per-group counts (tag|version|priority|status)
|
|
377
|
+
dreamcontext tasks list --tag lina --json # scriptable: emit the filtered set as JSON (use this, not awk/grep)
|
|
378
|
+
dreamcontext tasks tags # distinct tags with counts (--all to include completed)
|
|
379
|
+
# Filters are case-insensitive; version/priority/feature match exactly; multiple --tag = AND, --any-tag = OR.
|
|
380
|
+
|
|
369
381
|
# Create (rich task with Why, User Stories, Acceptance Criteria, Constraints, Technical Details, Notes, Changelog)
|
|
370
382
|
dreamcontext tasks create <name> --description "..." --priority medium --why "What this task accomplishes"
|
|
371
383
|
|
|
@@ -451,9 +463,6 @@ dreamcontext memory status
|
|
|
451
463
|
_dream_context/
|
|
452
464
|
+-- core/
|
|
453
465
|
| +-- features/<feature>.md <- Feature PRDs (may include product: <name>)
|
|
454
|
-
| +-- data-structures/ <- Per-product schemas
|
|
455
|
-
| | +-- default.md <- single-product fallback
|
|
456
|
-
| | +-- <product>.md <- one per product if monorepo
|
|
457
466
|
| +-- 0.soul.md <- Identity, principles, rules
|
|
458
467
|
| +-- 1.user.md <- Preferences, project details
|
|
459
468
|
| +-- 2.memory.md <- Decisions + Known Issues (ship narrative moved to CHANGELOG 2026-05-23)
|
|
@@ -462,6 +471,9 @@ _dream_context/
|
|
|
462
471
|
| +-- 6.system_flow.md
|
|
463
472
|
| +-- CHANGELOG.json, RELEASES.json
|
|
464
473
|
+-- knowledge/<topic>.md <- Deep research, resources (global)
|
|
474
|
+
| +-- data-structures/ <- Per-product schemas (recall-indexed knowledge)
|
|
475
|
+
| | +-- default.md <- single-product fallback
|
|
476
|
+
| | +-- <product>.md <- one per product if monorepo
|
|
465
477
|
| +-- products/<product>.md <- Per-product knowledge (multi-product)
|
|
466
478
|
+-- state/
|
|
467
479
|
| +-- <task>.md <- Active tasks (frontmatter may include product:)
|
|
@@ -470,20 +482,42 @@ _dream_context/
|
|
|
470
482
|
|
|
471
483
|
---
|
|
472
484
|
|
|
485
|
+
## Improving dreamcontext (Agent Feedback)
|
|
486
|
+
|
|
487
|
+
You are not just a *user* of dreamcontext — you are its field reporter. When dreamcontext gets in your way, the system can only improve if that friction reaches its maintainers. Take responsibility: file it.
|
|
488
|
+
|
|
489
|
+
**Trigger — the moment you notice any of these, consider filing feedback:**
|
|
490
|
+
- You expected a fact to be in memory but `memory recall` didn't surface it (a recall gap, not a missing fact).
|
|
491
|
+
- You wished a CLI command existed and there was no path (e.g. "I want to reopen a completed task" and no `tasks reopen`).
|
|
492
|
+
- A command, hook, or doc behaved wrong, was confusing, or crashed.
|
|
493
|
+
- The structure forced an awkward workaround to do something the system should support directly.
|
|
494
|
+
|
|
495
|
+
Do **not** silently work around it. A workaround fixes today; feedback fixes the system.
|
|
496
|
+
|
|
497
|
+
**The loop (this is the only sanctioned way to file — never run `gh issue create` by hand):**
|
|
498
|
+
1. **Draft.** Run `dreamcontext feedback --dry-run` with the category and a complete scenario. Fill `-s` (what you were doing), `-e` (what you expected), `-g` (what was missing/broken), `-r` (exact commands), `-p` (your proposed improvement). A maintainer who has never seen your session must understand it from the issue alone — include the whole scenario.
|
|
499
|
+
2. **Confirm with the user.** Show them the rendered draft and ask permission. This writes to a public repo on their behalf — never file without an explicit yes.
|
|
500
|
+
3. **File.** Re-run the same command without `--dry-run` and with `--yes`. It checks for near-duplicate open issues, applies the `agent-feedback` label, and files to the dreamcontext project (`meanllbrl/dreamcontext`) — **not** the user's own repo.
|
|
501
|
+
|
|
502
|
+
**No GitHub access?** If the command reports `gh` is missing or unauthenticated, relay its guidance to the user: install `gh` + run `gh auth login`, and if they have no GitHub account, ask them to create a free one at github.com/signup. They need an account to file. Once they're signed in, re-run the loop.
|
|
503
|
+
|
|
504
|
+
**Quality bar:** one issue per distinct gap, concrete title, full scenario, a concrete proposal. Vague feedback ("recall is bad") is noise; a reproducible scenario with a proposed command is signal.
|
|
505
|
+
|
|
473
506
|
## Command Reference
|
|
474
507
|
|
|
475
508
|
All commands prefixed with `dreamcontext`. For reading/searching, use native tools directly.
|
|
476
509
|
|
|
477
510
|
| Command | Description |
|
|
478
511
|
|---------|-------------|
|
|
479
|
-
| `init [--multi-product=a,b,c]` | Initialize `_dream_context/`. Prompts interactively whether the project is a monorepo with multiple products; `--multi-product=a,b,c` skips the prompt and provides kebab-case product names directly. Creates `
|
|
512
|
+
| `init [--multi-product=a,b,c]` | Initialize `_dream_context/`. Prompts interactively whether the project is a monorepo with multiple products; `--multi-product=a,b,c` skips the prompt and provides kebab-case product names directly. Creates `knowledge/data-structures/<product>.md` per product (or `default.md` for single-product) and seeds `knowledge/products/`. |
|
|
480
513
|
| `core changelog add` | Add changelog entry (interactive) |
|
|
481
514
|
| `core releases add [--ver v --summary s --yes] [--status planning]` | Create release (default: released with auto-discovery; --status planning: empty planning version, auto-becomes active) |
|
|
482
515
|
| `core releases active [<version>] [--clear]` | Get/set/clear the active planning version (default for new tasks' `version` field) |
|
|
483
516
|
| `core releases list [-n count]` | List recent releases |
|
|
484
517
|
| `core releases show <version>` | Show release details |
|
|
485
|
-
| `features create <name
|
|
486
|
-
| `features insert <name> <section> <content>` | Insert into feature section |
|
|
518
|
+
| `features create <name> [-w why] [-t tags] [-s status] [--related-tasks a,b]` | Create feature PRD; frontmatter set without hand-editing (status: planning\|in_progress\|in_review\|active\|shipped\|deprecated) |
|
|
519
|
+
| `features insert <name> <section> <content>` | Insert into feature section (replaces template placeholders on first write; `user_stories`/`acceptance_criteria` auto-formatted as `- [ ]` items) |
|
|
520
|
+
| `features set <name> <tags\|status\|related_tasks> <value...>` | Set a feature frontmatter field (comma-separated for tags/related_tasks) without hand-editing |
|
|
487
521
|
| `knowledge create <name>` | Create knowledge file |
|
|
488
522
|
| `knowledge index [--tag <tag>]` | Show knowledge index |
|
|
489
523
|
| `knowledge tags` | List standard tags |
|
|
@@ -494,7 +528,8 @@ All commands prefixed with `dreamcontext`. For reading/searching, use native too
|
|
|
494
528
|
| `memory delete <slug> [--force]` | Remove a knowledge entry |
|
|
495
529
|
| `memory list [--types ...]` | List indexable memory corpus by type |
|
|
496
530
|
| `memory status` | Show corpus size broken down by type |
|
|
497
|
-
| `tasks list [-s status] [--all]` | List tasks
|
|
531
|
+
| `tasks list [-s status] [--all] [--tag t]… [--any-tag t]… [--version id] [--priority p] [--feature slug] [--group-by tag\|version\|priority\|status] [--long] [--json]` | List/filter/group tasks. Default excludes completed. `--tag` repeatable (AND), `--any-tag` repeatable (OR), filters compose; `--json` emits the filtered set; case-insensitive matching |
|
|
532
|
+
| `tasks tags [--all] [--json]` | List distinct task tags with counts (discover before filtering) |
|
|
498
533
|
| `tasks create <name> [-d desc] [-p priority] [-s status] [-t tags] [-w why] [--reach N --impact N --confidence N --effort N]` | Create task (defaults: priority=medium, status=todo). RICE flags optional and additive. |
|
|
499
534
|
| `tasks rice <name> [--reach N] [--impact N] [--confidence N] [--effort N] [--clear]` | Print or update RICE values; no flags prints current values |
|
|
500
535
|
| `tasks insert <name> <section> <content>` | Insert into task section |
|
|
@@ -529,6 +564,7 @@ All commands prefixed with `dreamcontext`. For reading/searching, use native too
|
|
|
529
564
|
| `config show` | Print project config (platforms, packs, native-memory state) |
|
|
530
565
|
| `config native-memory <enable\|disable>` | Toggle Claude's native auto-memory; disabled by default so dreamcontext owns project memory |
|
|
531
566
|
| `upgrade [--check]` | Update the dreamcontext CLI itself to the latest npm release |
|
|
567
|
+
| `feedback -c <category> -t <title> -s <scenario> [-e expected] [-g gap] [-r repro] [-p proposal] [--dry-run] [--yes]` | File a structured gap/bug as a GitHub issue to the **dreamcontext project** (upstream, not the user's repo). Use `--dry-run` to render a draft for the user; file with `--yes` only after they approve. Categories: `bug \| missing-cli \| unseen-memory \| feature \| docs \| other`. See "Improving dreamcontext" below. |
|
|
532
568
|
|
|
533
569
|
Feature insert sections: `changelog`, `notes`, `technical_details`, `constraints`, `user_stories`, `acceptance_criteria`, `why`
|
|
534
570
|
Task insert sections: `why`, `user_stories`, `acceptance_criteria`, `constraints`, `technical_details`, `notes`, `changelog`
|
|
@@ -32,14 +32,90 @@ Write a spec JSON, then run it. The script prints `elements/images/texts` counts
|
|
|
32
32
|
|
|
33
33
|
### JS API (for pipelines that generate many boards)
|
|
34
34
|
```js
|
|
35
|
-
const
|
|
36
|
-
|
|
35
|
+
const path = require('path');
|
|
36
|
+
// skill lives at <project>/.claude/skills/excalidraw/ — adjust leading ../ count to match your script's depth from project root
|
|
37
|
+
const { buildExcalidraw, lane, grid } = require(path.resolve(__dirname, '../.claude/skills/excalidraw/scripts/build_excalidraw.js'));
|
|
38
|
+
buildExcalidraw({ out: path.resolve(__dirname, '../boards/Board.excalidraw.md'), elements: [ ...lane({ title, images, x, y, thumbW }) ] });
|
|
37
39
|
```
|
|
38
40
|
|
|
41
|
+
## File layout
|
|
42
|
+
|
|
43
|
+
### Single board (default)
|
|
44
|
+
Keep the spec next to the generated board but clearly separated:
|
|
45
|
+
```
|
|
46
|
+
boards/
|
|
47
|
+
├── MyBoard.excalidraw.md ← generated deliverable; do not hand-edit
|
|
48
|
+
└── _spec/
|
|
49
|
+
└── MyBoard.json ← source of truth; edit this, then regenerate
|
|
50
|
+
```
|
|
51
|
+
The `.excalidraw.md` is **disposable** — it is fully derived from the spec. If the two ever
|
|
52
|
+
disagree, the spec wins. Commit both (the board for Obsidian/GitHub preview, the spec for
|
|
53
|
+
reproducibility), but only edit the spec.
|
|
54
|
+
|
|
55
|
+
### Multi-board pipeline
|
|
56
|
+
When a single generator produces several boards, isolate it in a `pipeline/` folder so the
|
|
57
|
+
deliverable boards stay at the top of the project and are easy to open in Obsidian:
|
|
58
|
+
```
|
|
59
|
+
boards/
|
|
60
|
+
├── Overview.excalidraw.md ← generated
|
|
61
|
+
├── Funnel.excalidraw.md ← generated
|
|
62
|
+
├── Pricing.excalidraw.md ← generated
|
|
63
|
+
└── pipeline/
|
|
64
|
+
├── generate.js ← single regen entrypoint: `node pipeline/generate.js`
|
|
65
|
+
├── shared-style.js ← shared palette / helpers
|
|
66
|
+
└── spec/
|
|
67
|
+
├── Overview.json ← source spec for Overview board
|
|
68
|
+
├── Funnel.json ← source spec for Funnel board
|
|
69
|
+
└── Pricing.json ← source spec for Pricing board
|
|
70
|
+
```
|
|
71
|
+
- **Generated files** (`*.excalidraw.md`) live one level above `pipeline/` — open them in Obsidian without navigating into a sub-folder.
|
|
72
|
+
- **Source specs** live in `pipeline/spec/` — one JSON per board.
|
|
73
|
+
- **Single entrypoint**: `node pipeline/generate.js` rebuilds every board. No per-board manual commands.
|
|
74
|
+
|
|
75
|
+
### Many boards from shared data (recipe)
|
|
76
|
+
Use this pattern when multiple boards pull from the same data set (e.g. one board per product, per region, or per funnel step):
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
// pipeline/generate.js (lives at boards/pipeline/generate.js)
|
|
80
|
+
const path = require('path');
|
|
81
|
+
const ROOT = path.resolve(__dirname, '..'); // boards/ directory
|
|
82
|
+
// ../../ = project root (boards/ → project/); adjust if boards/ is nested deeper
|
|
83
|
+
const { buildExcalidraw, lane } = require(path.resolve(__dirname, '../../.claude/skills/excalidraw/scripts/build_excalidraw.js'));
|
|
84
|
+
const style = require(path.resolve(__dirname, 'shared-style.js'));
|
|
85
|
+
const items = require(path.resolve(__dirname, 'spec/items.json')); // shared data
|
|
86
|
+
|
|
87
|
+
for (const item of items) {
|
|
88
|
+
const elements = style.buildItemBoard(item); // per-item spec logic
|
|
89
|
+
buildExcalidraw({
|
|
90
|
+
out: path.resolve(ROOT, `${item.slug}.excalidraw.md`),
|
|
91
|
+
elements,
|
|
92
|
+
});
|
|
93
|
+
console.log('wrote', item.slug);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
// pipeline/shared-style.js (lives at boards/pipeline/shared-style.js)
|
|
99
|
+
const path = require('path');
|
|
100
|
+
// ../../ = project root (boards/ → project/); adjust if boards/ is nested deeper
|
|
101
|
+
const { card, connector, sectionTitle } = require(path.resolve(__dirname, '../../.claude/skills/excalidraw/scripts/lib/style.js'));
|
|
102
|
+
|
|
103
|
+
exports.buildItemBoard = (item) => [
|
|
104
|
+
sectionTitle({ x: 0, y: 0, text: item.name, fontSize: 40 }),
|
|
105
|
+
// … common layout using item fields
|
|
106
|
+
];
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Key conventions:
|
|
110
|
+
- `ROOT = path.resolve(__dirname, '..')` pins paths relative to the generator file, not the working directory. The generator works correctly wherever it is invoked from.
|
|
111
|
+
- Each item produces exactly one board; the mapping is `items.json → <slug>.excalidraw.md`.
|
|
112
|
+
- `shared-style.js` owns the layout logic — boards stay visually consistent; change the style once, regenerate all.
|
|
113
|
+
- Add a `package.json` script or `Makefile` alias so the command is always `npm run boards` (or similar) and never has to be rediscovered.
|
|
114
|
+
|
|
39
115
|
## Spec schema
|
|
40
116
|
```jsonc
|
|
41
117
|
{
|
|
42
|
-
"out": "/
|
|
118
|
+
"out": "./boards/Board.excalidraw.md", // prefer __dirname-relative in JS generators; relative to cwd for CLI
|
|
43
119
|
"vaultRoot": "/abs/vault", // optional; auto-detected by walking up to `.obsidian`
|
|
44
120
|
"attachDir": "Attachments", // external images get copied here (relative to board dir)
|
|
45
121
|
"wikilinkMode": "basename", // "basename" (default) or "path" (vault-relative)
|
|
@@ -48,6 +124,9 @@ buildExcalidraw({ out, elements: [ ...lane({ title, images, x, y, thumbW }) ] })
|
|
|
48
124
|
}
|
|
49
125
|
```
|
|
50
126
|
|
|
127
|
+
**Paths in generators**: always use `path.resolve(__dirname, ...)` for `out` and image `path` fields — never
|
|
128
|
+
hardcode absolute paths. This keeps the generator portable: move the folder and it still runs.
|
|
129
|
+
|
|
51
130
|
### Element types
|
|
52
131
|
- `text` — `{ x, y, text, fontSize?, color?, width?, align?, fontFamily? }` (fontFamily 1=hand, 2=normal, 3=code). **Set `width` for any caption/label that must stay inside a column or card** → the text WRAPS to that width (autoResize off) and its height is computed from the wrapped line count. Omit `width` only for short single-line text you want sized to content (it renders on one line and will overlap neighbours if long).
|
|
53
132
|
- `image` — `{ x, y, path, width? , height? }` — give ONE of width/height; the other is derived from aspect. `path` is an absolute file path.
|
|
@@ -39,15 +39,36 @@ if it's installed.
|
|
|
39
39
|
# language auto-detects — no need to pass it. Override only if auto mislabels a
|
|
40
40
|
# short/ambiguous clip: ./transcribe.sh "/abs/path/clip.mp4" tr
|
|
41
41
|
```
|
|
42
|
+
|
|
43
|
+
**Pick the mode for the kind of video — this matters most for app/UI recordings:**
|
|
44
|
+
- **Talking-head / lecture / ad creative** → the default is right. Scene-detect + a
|
|
45
|
+
10s gap-fill catches the visuals.
|
|
46
|
+
- **App screen-recording / onboarding funnel / UI walkthrough** → add `--mode ui`.
|
|
47
|
+
App screens linger 3–5s and change by **text only** (a questionnaire step, a
|
|
48
|
+
paywall) — they don't move enough to trip scene-detect, so the 10s default
|
|
49
|
+
silently drops most of them. `--mode ui` samples every ~2.5s so each screen lands.
|
|
50
|
+
If you only need the screens (no narration), add `--frames-only` to skip whisper:
|
|
51
|
+
```bash
|
|
52
|
+
./scripts/transcribe.sh "/abs/path/onboarding.mp4" --mode ui --frames-only --contact-sheet
|
|
53
|
+
```
|
|
54
|
+
|
|
42
55
|
This writes everything into `<video_dir>/<slug>.media/`:
|
|
43
|
-
- `transcript.srt` / `.json` / `.txt` — timestamped transcript (large-v3-turbo)
|
|
56
|
+
- `transcript.srt` / `.json` / `.txt` — timestamped transcript (large-v3-turbo) — *skipped with `--frames-only`*
|
|
44
57
|
- `frames/anchor_first.jpg` / `anchor_last.jpg` — first + last frame, always captured
|
|
45
58
|
(short cut-heavy creatives carry the hook and CTA here; scene-detect misses both)
|
|
46
|
-
- `frames/
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
- `frames/frame_*.jpg` — frames selected in **one time-based pass**: a scene change
|
|
60
|
+
fired (slide/UI transition) **or** `MAX_GAP` seconds elapsed since the last frame,
|
|
61
|
+
whichever comes first. The gap rule is wall-clock based (`prev_selected_t`), so it
|
|
62
|
+
works on variable-frame-rate screen recordings where frame-number sampling breaks,
|
|
63
|
+
and it guarantees every static stretch (app demos, slides, CTAs) gets a frame.
|
|
64
|
+
- `frames/contact_sheet.jpg` — tiled montage of all frames, *only with `--contact-sheet`*
|
|
49
65
|
- `frames.json` — **the index you read**: `[{file, t, at, type}]`, sorted by time,
|
|
50
|
-
near-duplicate timestamps collapsed (anchors always kept)
|
|
66
|
+
near-duplicate timestamps collapsed (anchors always kept). `type` is `scene` (a
|
|
67
|
+
picture change fired), `gap` (a periodic fill at the `MAX_GAP` cadence), or `anchor`.
|
|
68
|
+
|
|
69
|
+
The engine prints a **coverage check** at the end: `longest unsampled gap = Xs`. If it
|
|
70
|
+
warns the gap is >2× `MAX_GAP`, frames are likely missing — re-run denser (`--max-gap`
|
|
71
|
+
lower, or `--mode ui`) **before** any expensive deep-analysis pass.
|
|
51
72
|
|
|
52
73
|
`audio.wav` is auto-deleted after transcription (it's a ~1.9MB/min whisper-only
|
|
53
74
|
intermediate). The whole `*.media/` dir is gitignored — it stays next to the video
|
|
@@ -69,7 +90,9 @@ nothing on screen need no frame.
|
|
|
69
90
|
> Heuristic: the two `anchor` frames (first/last) almost always matter — the hook
|
|
70
91
|
> and the CTA. Every `scene` frame is a candidate (the picture changed for a
|
|
71
92
|
> reason). `gap` frames cover static stretches scene-detect skipped — often the
|
|
72
|
-
> most informative part (an app demo or
|
|
93
|
+
> most informative part (an app demo or onboarding screen that doesn't "move"), so
|
|
94
|
+
> check them. In `--mode ui` runs most frames are `gap` — that's expected and you
|
|
95
|
+
> generally want to look at all of them, one per screen.
|
|
73
96
|
|
|
74
97
|
**Need a frame the index doesn't have? Grab it on demand.** Scene-detect fires on
|
|
75
98
|
motion, not on meaning — on fast-cut video the most informative moment often sits
|
|
@@ -116,6 +139,14 @@ relevant dreamcontext skill (per the skill-triage rule) and load it:
|
|
|
116
139
|
- **Knowledge / training** → if it should persist for the project, hand the transcript to `dreamcontext knowledge` so it becomes durable context
|
|
117
140
|
Don't guess the use-case — let the user direct it.
|
|
118
141
|
|
|
142
|
+
> **UI teardown (no transcript needed).** When the goal is purely the app flow —
|
|
143
|
+
> e.g. tearing down a competitor's onboarding funnel — run `--frames-only --mode ui`
|
|
144
|
+
> and skip the `.transcript.md` entirely. The deliverable becomes a **curated screen
|
|
145
|
+
> list** (one entry per onboarding step, in order, from `frames.json`), which feeds a
|
|
146
|
+
> board (`excalidraw`) or an `onboarding-design` analysis directly. Use `--contact-sheet`
|
|
147
|
+
> to eyeball coverage first, and trust the coverage warning — under-sampling here is
|
|
148
|
+
> exactly what makes a teardown wrongly report "screen X wasn't shown".
|
|
149
|
+
|
|
119
150
|
## What this skill is NOT
|
|
120
151
|
- Not a knowledge-base writer or ingestion pipeline. This skill produces the
|
|
121
152
|
transcript artifact; persisting it (chunking, embedding, ingest into a project's
|
|
@@ -140,12 +171,22 @@ brew install whisper-cpp ffmpeg # yt-dlp too, only for remote links
|
|
|
140
171
|
├── SKILL.md ← you are here
|
|
141
172
|
└── scripts/
|
|
142
173
|
├── transcribe.sh ← video → transcript + frames + frames.json (the engine)
|
|
143
|
-
|
|
144
|
-
└── build_frame_index.py ← frames/*.jpg → frames.json (pts_time index)
|
|
174
|
+
└── build_frame_index.py ← frames/*.jpg → frames.json (pts_time index + coverage check)
|
|
145
175
|
```
|
|
146
176
|
|
|
147
|
-
##
|
|
148
|
-
|
|
149
|
-
-
|
|
150
|
-
|
|
151
|
-
-
|
|
177
|
+
## Flags & tuning
|
|
178
|
+
Flags (each also settable as an env var, e.g. `MODE=ui`):
|
|
179
|
+
- `--mode ui|lecture` — sampling preset. `ui` = `MAX_GAP` 2.5s (app screens); `lecture`
|
|
180
|
+
(default) = 10s (talking-head). Explicit `--max-gap`/`--scene-threshold` always win.
|
|
181
|
+
- `--frames-only` — skip whisper; extract frames only (UI/UX teardowns).
|
|
182
|
+
- `--max-gap N` — guarantee a frame at least every N seconds.
|
|
183
|
+
- `--scene-threshold N` — scene-change sensitivity, lower = more frames.
|
|
184
|
+
- `--contact-sheet` — also emit `frames/contact_sheet.jpg` (coverage at a glance).
|
|
185
|
+
- `--lang CODE` — force the transcript language (default: auto-detect).
|
|
186
|
+
|
|
187
|
+
Common adjustments:
|
|
188
|
+
- Onboarding/UI recording under-sampled? `--mode ui` (or push further: `--max-gap 1.5`).
|
|
189
|
+
- Too few frames on a slide-heavy talk? `--scene-threshold 0.1`.
|
|
190
|
+
- Long static lecture over-sampled? `--max-gap 20`.
|
|
191
|
+
- Capturing too many near-identical frames? `DEDUPE_SEC=1.0 ./transcribe.sh …`.
|
|
192
|
+
- Force a model: `WHISPER_MODEL=/abs/ggml-large-v3.bin ./transcribe.sh …`.
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""Build frames.json: map each extracted frame to its pts_time on the video timeline.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
zero-padded frame file in selection order, adds the first/last anchor frames, drops
|
|
7
|
-
near-duplicate timestamps,
|
|
4
|
+
transcribe.sh selects frames in ONE time-based pass (scene change OR every MAX_GAP),
|
|
5
|
+
dumping each kept frame's pts_time to frame_times.txt. This pairs every timestamp with
|
|
6
|
+
its zero-padded frame file in selection order, adds the first/last anchor frames, drops
|
|
7
|
+
near-duplicate timestamps, labels each frame (scene vs gap vs anchor), and emits
|
|
8
|
+
frames.json sorted by time. It also prints a COVERAGE check — the largest unsampled
|
|
9
|
+
stretch — so a human can catch under-sampling before an expensive deep-analysis pass.
|
|
10
|
+
|
|
11
|
+
Type labels are reconstructed from inter-frame spacing (no second decode): a frame that
|
|
12
|
+
landed sooner than MAX_GAP after the previous one was triggered by a scene change
|
|
13
|
+
('scene'); one that landed at the MAX_GAP cadence is a periodic fill ('gap'). They are
|
|
14
|
+
hints for which frames to prioritize, not a hard contract.
|
|
8
15
|
|
|
9
16
|
Stdlib only — no venv needed.
|
|
10
17
|
Usage: build_frame_index.py <OUT_DIR> [DURATION_SEC]
|
|
11
18
|
Env: DEDUPE_SEC=0.4 collapse non-anchor frames closer than this to the kept one.
|
|
19
|
+
MAX_GAP=10 the gap target the engine sampled at (drives type + coverage warn).
|
|
12
20
|
"""
|
|
13
21
|
import json
|
|
14
22
|
import os
|
|
@@ -19,6 +27,7 @@ from pathlib import Path
|
|
|
19
27
|
# metadata=print header line, e.g.: "frame:0 pts:321024 pts_time:12.852500"
|
|
20
28
|
FRAME_RE = re.compile(r"frame:(\d+)\b.*?pts_time:([\d.]+)", re.DOTALL)
|
|
21
29
|
DEDUPE_SEC = float(os.environ.get("DEDUPE_SEC", "0.4"))
|
|
30
|
+
MAX_GAP = float(os.environ.get("MAX_GAP", "10"))
|
|
22
31
|
|
|
23
32
|
|
|
24
33
|
def parse_times(meta_file: Path) -> dict[int, float]:
|
|
@@ -29,11 +38,11 @@ def parse_times(meta_file: Path) -> dict[int, float]:
|
|
|
29
38
|
return {int(n): round(float(t), 2) for n, t in FRAME_RE.findall(text)}
|
|
30
39
|
|
|
31
40
|
|
|
32
|
-
def collect(out_dir: Path, prefix: str, meta_name: str
|
|
41
|
+
def collect(out_dir: Path, prefix: str, meta_name: str) -> list[dict]:
|
|
33
42
|
times = parse_times(out_dir / "frames" / meta_name)
|
|
34
43
|
frames = sorted((out_dir / "frames").glob(f"{prefix}_*.jpg"))
|
|
35
44
|
# transcribe.sh names files 1-based (%04d starts at 0001); metadata frame: is 0-based.
|
|
36
|
-
return [{"file": f"frames/{f.name}", "t": times.get(i), "type":
|
|
45
|
+
return [{"file": f"frames/{f.name}", "t": times.get(i), "type": "key"}
|
|
37
46
|
for i, f in enumerate(frames)]
|
|
38
47
|
|
|
39
48
|
|
|
@@ -69,6 +78,40 @@ def dedupe(rows: list[dict]) -> list[dict]:
|
|
|
69
78
|
return kept
|
|
70
79
|
|
|
71
80
|
|
|
81
|
+
def label_types(rows: list[dict]) -> None:
|
|
82
|
+
"""Reconstruct scene/gap from spacing. Frames spaced >= ~MAX_GAP are periodic
|
|
83
|
+
fills ('gap'); closer ones were pulled in early by a scene change ('scene')."""
|
|
84
|
+
near = MAX_GAP * 0.9
|
|
85
|
+
prev_t = 0.0
|
|
86
|
+
for r in rows:
|
|
87
|
+
if r["type"] == "anchor":
|
|
88
|
+
if r["t"] is not None:
|
|
89
|
+
prev_t = r["t"]
|
|
90
|
+
continue
|
|
91
|
+
if r["t"] is None:
|
|
92
|
+
r["type"] = "scene"
|
|
93
|
+
continue
|
|
94
|
+
r["type"] = "gap" if (r["t"] - prev_t) >= near else "scene"
|
|
95
|
+
prev_t = r["t"]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def coverage(rows: list[dict], duration: float) -> tuple[float, float]:
|
|
99
|
+
"""Largest unsampled stretch (s) and where it starts, over [0, duration]."""
|
|
100
|
+
ts = sorted(r["t"] for r in rows if r["t"] is not None)
|
|
101
|
+
if not ts:
|
|
102
|
+
return (duration, 0.0)
|
|
103
|
+
# Bound the right edge with the real duration; if ffprobe couldn't read it (0), fall
|
|
104
|
+
# back to the last sampled time so the tail gap (last frame -> end) still surfaces
|
|
105
|
+
# rather than being silently dropped.
|
|
106
|
+
right = round(duration, 2) if duration > 0 else ts[-1]
|
|
107
|
+
bounds = [0.0] + ts + [right]
|
|
108
|
+
worst, at = 0.0, 0.0
|
|
109
|
+
for a, b in zip(bounds, bounds[1:]):
|
|
110
|
+
if b - a > worst:
|
|
111
|
+
worst, at = b - a, a
|
|
112
|
+
return (worst, at)
|
|
113
|
+
|
|
114
|
+
|
|
72
115
|
def main() -> int:
|
|
73
116
|
if len(sys.argv) < 2:
|
|
74
117
|
print("usage: build_frame_index.py <OUT_DIR> [DURATION_SEC]", file=sys.stderr)
|
|
@@ -76,18 +119,26 @@ def main() -> int:
|
|
|
76
119
|
out_dir = Path(sys.argv[1])
|
|
77
120
|
duration = float(sys.argv[2]) if len(sys.argv) > 2 else 0.0
|
|
78
121
|
|
|
79
|
-
rows = collect(out_dir, "
|
|
80
|
-
rows += collect(out_dir, "gap", "gap_times.txt", "gap")
|
|
122
|
+
rows = collect(out_dir, "frame", "frame_times.txt")
|
|
81
123
|
rows += anchors(out_dir, duration)
|
|
82
|
-
# Sort by time
|
|
83
|
-
|
|
124
|
+
# Sort by time (unknown timestamps sink to the end); on a tie, anchors sort FIRST so
|
|
125
|
+
# the eq(n,0) seed frame at t=0 (and any selected frame coincident with anchor_last)
|
|
126
|
+
# dedupes away against the identical anchor instead of producing a duplicate entry.
|
|
127
|
+
rows.sort(key=lambda r: (r["t"] is None, r["t"] or 0.0, r["type"] != "anchor"))
|
|
84
128
|
rows = dedupe(rows)
|
|
129
|
+
label_types(rows)
|
|
85
130
|
for r in rows:
|
|
86
131
|
r["at"] = fmt(r["t"])
|
|
87
132
|
|
|
88
133
|
(out_dir / "frames.json").write_text(json.dumps(rows, ensure_ascii=False, indent=2))
|
|
134
|
+
|
|
89
135
|
n_anchor = sum(r["type"] == "anchor" for r in rows)
|
|
136
|
+
worst, at = coverage(rows, duration)
|
|
90
137
|
print(f" frames.json: {len(rows)} frames indexed ({n_anchor} anchors, dedupe<{DEDUPE_SEC}s)")
|
|
138
|
+
print(f" coverage: longest unsampled gap = {worst:.1f}s at {fmt(at)} (target MAX_GAP={MAX_GAP:g}s)")
|
|
139
|
+
if duration > 0 and worst > MAX_GAP * 2:
|
|
140
|
+
print(f" ⚠ coverage gap is >2x MAX_GAP — frames may be missing around {fmt(at)}. "
|
|
141
|
+
f"Re-run denser: --max-gap {MAX_GAP / 2:g} (or --mode ui for app recordings).")
|
|
91
142
|
return 0
|
|
92
143
|
|
|
93
144
|
|