superlab 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +65 -17
  2. package/README.zh-CN.md +64 -15
  3. package/bin/superlab.cjs +232 -1
  4. package/lib/context.cjs +337 -0
  5. package/lib/i18n.cjs +673 -41
  6. package/lib/install.cjs +249 -51
  7. package/package-assets/claude/commands/lab/framing.md +11 -0
  8. package/package-assets/claude/commands/lab/idea.md +2 -1
  9. package/package-assets/claude/commands/lab/iterate.md +2 -1
  10. package/package-assets/claude/commands/lab/report.md +2 -1
  11. package/package-assets/claude/commands/lab/review.md +2 -1
  12. package/package-assets/claude/commands/lab/run.md +2 -1
  13. package/package-assets/claude/commands/lab/spec.md +2 -1
  14. package/package-assets/claude/commands/lab/write.md +2 -1
  15. package/package-assets/codex/prompts/lab-framing.md +9 -0
  16. package/package-assets/codex/prompts/lab-idea.md +2 -1
  17. package/package-assets/codex/prompts/lab-iterate.md +2 -1
  18. package/package-assets/codex/prompts/lab-report.md +2 -1
  19. package/package-assets/codex/prompts/lab-review.md +2 -1
  20. package/package-assets/codex/prompts/lab-run.md +2 -1
  21. package/package-assets/codex/prompts/lab-spec.md +2 -1
  22. package/package-assets/codex/prompts/lab-write.md +2 -1
  23. package/package-assets/shared/{templates → lab/.managed/templates}/design.md +3 -3
  24. package/package-assets/shared/lab/.managed/templates/framing.md +66 -0
  25. package/package-assets/shared/{templates → lab/.managed/templates}/paper-plan.md +1 -0
  26. package/package-assets/shared/{templates → lab/.managed/templates}/proposal.md +1 -1
  27. package/package-assets/shared/{templates → lab/.managed/templates}/spec.md +2 -2
  28. package/package-assets/shared/{templates → lab/.managed/templates}/tasks.md +2 -2
  29. package/package-assets/shared/{templates → lab/.managed/templates}/write-iteration.md +1 -0
  30. package/package-assets/shared/lab/changes/README.md +10 -0
  31. package/package-assets/shared/lab/config/workflow.json +6 -0
  32. package/package-assets/shared/lab/context/next-action.md +19 -0
  33. package/package-assets/shared/lab/context/session-brief.md +30 -0
  34. package/package-assets/shared/lab/context/summary.md +21 -0
  35. package/package-assets/shared/lab/context/terminology-lock.md +27 -0
  36. package/package-assets/shared/lab/system/core.md +41 -0
  37. package/package-assets/shared/skills/lab/SKILL.md +54 -37
  38. package/package-assets/shared/skills/lab/references/paper-writing/abstract.md +2 -17
  39. package/package-assets/shared/skills/lab/references/paper-writing/introduction.md +3 -63
  40. package/package-assets/shared/skills/lab/references/paper-writing/method.md +4 -34
  41. package/package-assets/shared/skills/lab/references/workflow.md +4 -4
  42. package/package-assets/shared/skills/lab/stages/framing.md +70 -0
  43. package/package-assets/shared/skills/lab/stages/idea.md +5 -5
  44. package/package-assets/shared/skills/lab/stages/iterate.md +8 -8
  45. package/package-assets/shared/skills/lab/stages/report.md +7 -6
  46. package/package-assets/shared/skills/lab/stages/review.md +6 -5
  47. package/package-assets/shared/skills/lab/stages/run.md +4 -4
  48. package/package-assets/shared/skills/lab/stages/spec.md +11 -11
  49. package/package-assets/shared/skills/lab/stages/write.md +14 -10
  50. package/package.json +1 -1
  51. package/package-assets/shared/changes/README.md +0 -10
  52. package/package-assets/shared/config/workflow.json +0 -5
  53. package/package-assets/shared/examples/minimal-uplift-workflow.md +0 -73
  54. /package/package-assets/shared/{scripts → lab/.managed/scripts}/eval_report.py +0 -0
  55. /package/package-assets/shared/{scripts → lab/.managed/scripts}/register_run.py +0 -0
  56. /package/package-assets/shared/{scripts → lab/.managed/scripts}/summarize_iterations.py +0 -0
  57. /package/package-assets/shared/{scripts → lab/.managed/scripts}/validate_results.py +0 -0
  58. /package/package-assets/shared/{templates → lab/.managed/templates}/final-report.md +0 -0
  59. /package/package-assets/shared/{templates → lab/.managed/templates}/idea.md +0 -0
  60. /package/package-assets/shared/{templates → lab/.managed/templates}/iteration-report.md +0 -0
  61. /package/package-assets/shared/{templates → lab/.managed/templates}/paper-section.md +0 -0
  62. /package/package-assets/shared/{templates → lab/.managed/templates}/paper-section.tex +0 -0
  63. /package/package-assets/shared/{templates → lab/.managed/templates}/paper.tex +0 -0
  64. /package/package-assets/shared/{templates → lab/.managed/templates}/review-checklist.md +0 -0
  65. /package/package-assets/shared/{context → lab/context}/decisions.md +0 -0
  66. /package/package-assets/shared/{context → lab/context}/evidence-index.md +0 -0
  67. /package/package-assets/shared/{context → lab/context}/mission.md +0 -0
  68. /package/package-assets/shared/{context → lab/context}/open-questions.md +0 -0
  69. /package/package-assets/shared/{context → lab/context}/state.md +0 -0
package/README.md CHANGED
@@ -8,7 +8,7 @@ It combines:
8
8
 
9
9
  - command adapters for Codex and Claude
10
10
  - a shared `/lab` skill with stage-specific rules
11
- - lab-native change artifacts under `.superlab/changes/`
11
+ - lab-native change artifacts under `.lab/changes/`
12
12
  - bounded Ralph Wiggum style iteration for experiments
13
13
  - helper scripts for run registration and evaluation summaries
14
14
 
@@ -47,11 +47,14 @@ This writes:
47
47
  - `.codex/skills/lab/`
48
48
  - `.claude/commands/lab/*.md`
49
49
  - `.claude/skills/lab/`
50
- - `.superlab/config/workflow.json`
51
- - `.superlab/changes/`
52
- - `.superlab/templates/`
53
- - `.superlab/scripts/`
54
- - `.superlab/examples/`
50
+ - `AGENTS.md`
51
+ - `CLAUDE.md`
52
+ - `.lab/system/core.md`
53
+ - `.lab/config/workflow.json`
54
+ - `.lab/context/`
55
+ - `.lab/changes/`
56
+ - `.lab/.managed/templates/`
57
+ - `.lab/.managed/scripts/`
55
58
 
56
59
  If you prefer non-interactive use:
57
60
 
@@ -90,9 +93,48 @@ Refresh every registered project initialized from this user account:
90
93
  superlab update --all-projects
91
94
  ```
92
95
 
93
- `superlab init` writes `.superlab/install.json` inside each project and registers the project in the user-level registry so `update --all-projects` knows what to refresh.
96
+ `superlab init` writes `.lab/.managed/install.json` inside each project and registers the project in the user-level registry so `update --all-projects` knows what to refresh.
94
97
 
95
- Legacy projects from older `superlab` versions may already contain `.codex/`, `.claude/`, or `.superlab/` assets without `.superlab/install.json`. `superlab update` now reconstructs that metadata and upgrades the project in place.
98
+ Legacy projects from older `superlab` versions may already contain `.codex/`, `.claude/`, or `.superlab/` assets without `.lab/.managed/install.json`. `superlab update` now reconstructs that metadata and upgrades the project in place.
99
+
100
+ ## Context Tools
101
+
102
+ Refresh the compressed handoff layer from live project context:
103
+
104
+ ```bash
105
+ superlab context refresh
106
+ ```
107
+
108
+ Prune resolved or stale active-context entries:
109
+
110
+ ```bash
111
+ superlab context prune
112
+ ```
113
+
114
+ Archive cold iteration and writing history:
115
+
116
+ ```bash
117
+ superlab context archive
118
+ ```
119
+
120
+ Print the minimal handoff bundle for a new AI session:
121
+
122
+ ```bash
123
+ superlab handoff
124
+ ```
125
+
126
+ Check that the managed `.lab` layout is healthy:
127
+
128
+ ```bash
129
+ superlab doctor
130
+ ```
131
+
132
+ `doctor` validates:
133
+
134
+ - required `.lab` files
135
+ - `workflow.json` schema
136
+ - `deliverables_root` placement
137
+ - LaTeX-first paper output layout under `<deliverables_root>/paper/`
96
138
 
97
139
  ## Version
98
140
 
@@ -127,11 +169,12 @@ superlab init --lang zh
127
169
  superlab init --lang en
128
170
  ```
129
171
 
130
- This installer also writes `.superlab/config/workflow.json`, which is the global contract for:
172
+ This installer also writes `.lab/config/workflow.json`, which is the global contract for:
131
173
 
132
174
  - `workflow_language`
133
175
  - `paper_language`
134
176
  - `paper_format`
177
+ - `deliverables_root`
135
178
 
136
179
  Stages should follow that file rather than guess language locally.
137
180
 
@@ -146,12 +189,14 @@ Stages should follow that file rather than guess language locally.
146
189
  ## Command Set
147
190
 
148
191
  - `/lab:idea` researches an idea, critiques it, and writes the initial research framing.
149
- - `/lab:spec` converts the approved idea into one `.superlab/changes/<change-id>/` directory.
192
+ - `/lab:framing` locks paper-facing method names, module names, titles, and contribution wording before drafting.
193
+ - `/lab:spec` converts the approved idea into one `.lab/changes/<change-id>/` directory.
150
194
  - `/lab:run` executes a small-scale validation run and establishes the evaluation pipeline.
151
195
  - `/lab:iterate` runs bounded experiment loops with fixed mission, explicit thresholds, and per-round reports.
152
196
  - `/lab:review` audits documents and results in reviewer mode.
153
197
  - `/lab:report` produces the final report from accumulated artifacts.
154
198
  - `/lab:write` converts stable report artifacts into paper sections through small writing rounds.
199
+ It requires an approved `/lab:framing` artifact before drafting.
155
200
  It reads vendored section references derived from `Research-Paper-Writing-Skills`.
156
201
 
157
202
  See the source command docs in [commands/codex/lab.md](/Users/zhouhao119/coding/write/commands/codex/lab.md) and [commands/claude/lab.md](/Users/zhouhao119/coding/write/commands/claude/lab.md). The npm package installs real command assets from `package-assets/`.
@@ -165,7 +210,7 @@ See the source command docs in [commands/codex/lab.md](/Users/zhouhao119/coding/
165
210
  - `scripts/` contains reproducibility helpers.
166
211
  - `tests/` contains minimal regression coverage for the helper scripts.
167
212
  - `examples/` contains a minimal end-to-end usage example.
168
- - `docs/` contains the design spec and generated reports.
213
+ - `docs/` contains the design spec and release notes.
169
214
 
170
215
  ## Typical Flow
171
216
 
@@ -175,17 +220,20 @@ See the source command docs in [commands/codex/lab.md](/Users/zhouhao119/coding/
175
220
  4. Run `/lab:iterate` to improve against fixed targets using bounded iterations.
176
221
  5. Run `/lab:review` whenever you need reviewer-grade critique.
177
222
  6. Run `/lab:report` to synthesize the final report.
178
- 7. Run `/lab:write` to draft paper sections from stable report evidence.
223
+ 7. Run `/lab:framing` to lock naming, title, and contribution wording.
224
+ 8. Run `/lab:write` to draft paper sections from stable report evidence.
179
225
 
180
- `/lab:write` writes final manuscript output to:
226
+ `/lab:write` writes final manuscript output under the configured `deliverables_root` (default: `docs/research`):
181
227
 
182
- - `docs/paper/paper.tex`
183
- - `docs/paper/sections/*.tex`
228
+ - `docs/research/report.md`
229
+ - `docs/research/paper/main.tex`
230
+ - `docs/research/paper/sections/*.tex`
184
231
 
185
232
  Internal writing-control artifacts stay under:
186
233
 
187
- - `.superlab/write/plan.md`
188
- - `.superlab/write/iterations/*.md`
234
+ - `.lab/writing/framing.md`
235
+ - `.lab/writing/plan.md`
236
+ - `.lab/writing/iterations/*.md`
189
237
 
190
238
  ## Helper Scripts
191
239
 
package/README.zh-CN.md CHANGED
@@ -45,11 +45,14 @@ npx github:zhouhaoUCAS/superlab init
45
45
  - `.codex/skills/lab/`
46
46
  - `.claude/commands/lab/*.md`
47
47
  - `.claude/skills/lab/`
48
- - `.superlab/config/workflow.json`
49
- - `.superlab/changes/`
50
- - `.superlab/templates/`
51
- - `.superlab/scripts/`
52
- - `.superlab/examples/`
48
+ - `AGENTS.md`
49
+ - `CLAUDE.md`
50
+ - `.lab/system/core.md`
51
+ - `.lab/config/workflow.json`
52
+ - `.lab/context/`
53
+ - `.lab/changes/`
54
+ - `.lab/.managed/templates/`
55
+ - `.lab/.managed/scripts/`
53
56
 
54
57
  如果你更喜欢非交互方式,也可以直接传参:
55
58
 
@@ -88,9 +91,48 @@ superlab update --target /path/to/project
88
91
  superlab update --all-projects
89
92
  ```
90
93
 
91
- `superlab init` 会在项目内写入 `.superlab/install.json`,并在用户级 registry 里登记项目路径,所以 `update --all-projects` 才知道要刷新哪些项目。
94
+ `superlab init` 会在项目内写入 `.lab/.managed/install.json`,并在用户级 registry 里登记项目路径,所以 `update --all-projects` 才知道要刷新哪些项目。
92
95
 
93
- 较老版本的 `superlab` 项目可能已经有 `.codex/`、`.claude/` 或 `.superlab/` 资产,但还没有 `.superlab/install.json`。现在 `superlab update` 会自动重建这份 metadata,然后原地升级该项目。
96
+ 较老版本的 `superlab` 项目可能已经有 `.codex/`、`.claude/` 或 `.superlab/` 资产,但还没有 `.lab/.managed/install.json`。现在 `superlab update` 会自动重建这份 metadata,然后原地升级该项目。
97
+
98
+ ## 上下文工具
99
+
100
+ 根据当前项目上下文重建压缩后的 handoff 层:
101
+
102
+ ```bash
103
+ superlab context refresh
104
+ ```
105
+
106
+ 清理已解决或已过期的 active-context 内容:
107
+
108
+ ```bash
109
+ superlab context prune
110
+ ```
111
+
112
+ 把冷历史迭代和写作轮次归档出去:
113
+
114
+ ```bash
115
+ superlab context archive
116
+ ```
117
+
118
+ 输出新 AI 会话最小接手包:
119
+
120
+ ```bash
121
+ superlab handoff
122
+ ```
123
+
124
+ 检查当前 `.lab` 受管布局是否健康:
125
+
126
+ ```bash
127
+ superlab doctor
128
+ ```
129
+
130
+ `doctor` 目前会校验:
131
+
132
+ - 必需的 `.lab` 文件是否存在
133
+ - `workflow.json` 结构是否合法
134
+ - `deliverables_root` 是否放在合理位置
135
+ - `<deliverables_root>/paper/` 下是否仍然满足 LaTeX-first 输出约束
94
136
 
95
137
  ## 版本查询
96
138
 
@@ -125,11 +167,12 @@ superlab init --lang zh
125
167
  superlab init --lang en
126
168
  ```
127
169
 
128
- 安装器还会写入 `.superlab/config/workflow.json`。这个文件是全局约束,至少控制:
170
+ 安装器还会写入 `.lab/config/workflow.json`。这个文件是全局约束,至少控制:
129
171
 
130
172
  - `workflow_language`
131
173
  - `paper_language`
132
174
  - `paper_format`
175
+ - `deliverables_root`
133
176
 
134
177
  后续 stage 应该按这个配置决定语言和论文格式,而不是各模板自己猜。
135
178
 
@@ -144,12 +187,14 @@ superlab init --lang en
144
187
  ## 命令集合
145
188
 
146
189
  - `/lab:idea` 调研 idea、文献、数据集、指标和 baseline,并输出初始方案。
147
- - `/lab:spec` 把批准后的方案转换成一个统一的 `.superlab/changes/<change-id>/` 目录。
190
+ - `/lab:framing` 在正式写作前收紧方法名、模块名、论文题目和 contribution wording。
191
+ - `/lab:spec` 把批准后的方案转换成一个统一的 `.lab/changes/<change-id>/` 目录。
148
192
  - `/lab:run` 执行最小可运行实验,并建立首版评估链路。
149
193
  - `/lab:iterate` 以固定 mission、固定阈值和最大轮次做有边界的实验迭代。
150
194
  - `/lab:review` 以审稿人模式检查方法、对照、公平性、泄漏和统计问题。
151
195
  - `/lab:report` 根据累积工件生成最终实验报告。
152
196
  - `/lab:write` 把稳定的 report 工件转成论文 section,并按小步方式逐轮修改。
197
+ 它要求先有一个经批准的 `/lab:framing` 工件。
153
198
  它会读取随 `lab` 一起安装的 vendored 章节参考,这些参考来源于 `Research-Paper-Writing-Skills`。
154
199
 
155
200
  ## 使用流程
@@ -161,17 +206,20 @@ superlab init --lang en
161
206
  5. 用 `/lab:iterate` 进行多轮迭代。
162
207
  6. 在关键节点运行 `/lab:review`。
163
208
  7. 最后用 `/lab:report` 产出总报告。
164
- 8. 用 `/lab:write` 把稳定结果写成论文各 section
209
+ 8. 用 `/lab:framing` 收紧题目、命名和 contribution wording
210
+ 9. 用 `/lab:write` 把稳定结果写成论文各 section。
165
211
 
166
- `/lab:write` 最终只把论文写入:
212
+ `/lab:write` 会把最终可交付物写到 `deliverables_root` 指定的目录,默认是 `docs/research`:
167
213
 
168
- - `docs/paper/paper.tex`
169
- - `docs/paper/sections/*.tex`
214
+ - `docs/research/report.md`
215
+ - `docs/research/paper/main.tex`
216
+ - `docs/research/paper/sections/*.tex`
170
217
 
171
218
  内部写作控制工件放在:
172
219
 
173
- - `.superlab/write/plan.md`
174
- - `.superlab/write/iterations/*.md`
220
+ - `.lab/writing/framing.md`
221
+ - `.lab/writing/plan.md`
222
+ - `.lab/writing/iterations/*.md`
175
223
 
176
224
  ## 仓库内容
177
225
 
@@ -182,6 +230,7 @@ superlab init --lang en
182
230
  - `scripts/` 是运行登记、评估汇总和结果校验脚本。
183
231
  - `tests/` 是脚本与安装器测试。
184
232
  - `examples/` 是最小端到端示例。
233
+ - `docs/` 存放设计文档和发布说明。
185
234
  - `docs/release.md` 是发布到 npm 的操作说明。
186
235
  - `.github/workflows/ci.yml` 会在 `main` 和 PR 上自动执行 `npm run release:check`。
187
236
  - `.github/workflows/publish.yml` 支持在配置好 `NPM_TOKEN` 后手动从 GitHub Actions 发布到 npm。
package/bin/superlab.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ const fs = require("node:fs");
3
4
  const path = require("node:path");
4
5
  const {
5
6
  PACKAGE_VERSION,
@@ -9,6 +10,11 @@ const {
9
10
  updateAllProjects,
10
11
  updateSuperlabProject,
11
12
  } = require("../lib/install.cjs");
13
+ const {
14
+ archiveContext,
15
+ pruneContext,
16
+ refreshContext,
17
+ } = require("../lib/context.cjs");
12
18
  const { promptSelect } = require("../lib/init_tui.cjs");
13
19
 
14
20
  function restartReminder(lang) {
@@ -27,12 +33,20 @@ Usage:
27
33
  superlab update [--target <dir>]
28
34
  superlab update --all-projects
29
35
  superlab version [--target <dir>] [--global|--project]
36
+ superlab handoff [--target <dir>]
37
+ superlab doctor [--target <dir>]
38
+ superlab context refresh [--target <dir>]
39
+ superlab context prune [--target <dir>]
40
+ superlab context archive [--target <dir>]
30
41
 
31
42
  Commands:
32
43
  init Initialize /lab commands, skills, templates, and scripts in a target
33
44
  install Backward-compatible alias for init
34
45
  update Refresh an initialized project or all registered projects
35
46
  version Show installed CLI version and project asset version
47
+ handoff Print the minimal context handoff bundle for a project
48
+ doctor Check project health for the managed .lab layout
49
+ context Maintain project context summaries and archives
36
50
  help Show this help message
37
51
  `);
38
52
  }
@@ -128,6 +142,35 @@ function parseVersionArgs(argv) {
128
142
  return options;
129
143
  }
130
144
 
145
+ function parseTargetOnlyArgs(argv) {
146
+ const options = {
147
+ targetDir: process.cwd(),
148
+ };
149
+
150
+ for (let index = 0; index < argv.length; index += 1) {
151
+ const value = argv[index];
152
+ if (value === "--target") {
153
+ options.targetDir = path.resolve(argv[index + 1]);
154
+ index += 1;
155
+ } else {
156
+ throw new Error(`Unknown option: ${value}`);
157
+ }
158
+ }
159
+
160
+ return options;
161
+ }
162
+
163
+ function parseContextArgs(argv) {
164
+ const [action, ...rest] = argv;
165
+ if (!["refresh", "prune", "archive"].includes(action || "")) {
166
+ throw new Error(`Unknown context action: ${action || "(missing)"}`);
167
+ }
168
+ return {
169
+ action,
170
+ ...parseTargetOnlyArgs(rest),
171
+ };
172
+ }
173
+
131
174
  function printVersion(options) {
132
175
  const lines = [];
133
176
  if (!options.projectOnly) {
@@ -150,6 +193,167 @@ function printVersion(options) {
150
193
  console.log(lines.join("\n"));
151
194
  }
152
195
 
196
+ function printHandoff(options) {
197
+ const handoffFiles = [
198
+ ".lab/context/session-brief.md",
199
+ ".lab/context/mission.md",
200
+ ".lab/context/state.md",
201
+ ".lab/context/evidence-index.md",
202
+ ];
203
+ const sessionBriefPath = path.join(options.targetDir, handoffFiles[0]);
204
+ if (!fs.existsSync(sessionBriefPath)) {
205
+ throw new Error(`No handoff brief found in ${options.targetDir}. Run 'superlab init' first.`);
206
+ }
207
+
208
+ const sessionBrief = fs.readFileSync(sessionBriefPath, "utf8").trimEnd();
209
+ console.log(`target: ${options.targetDir}`);
210
+ console.log("read-order:");
211
+ for (const filePath of handoffFiles) {
212
+ console.log(`- ${filePath}`);
213
+ }
214
+ console.log("");
215
+ console.log(sessionBrief);
216
+ }
217
+
218
+ function listFilesRecursive(dir) {
219
+ if (!fs.existsSync(dir)) {
220
+ return [];
221
+ }
222
+ const files = [];
223
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
224
+ const fullPath = path.join(dir, entry.name);
225
+ if (entry.isDirectory()) {
226
+ files.push(...listFilesRecursive(fullPath));
227
+ } else {
228
+ files.push(fullPath);
229
+ }
230
+ }
231
+ return files;
232
+ }
233
+
234
+ function readWorkflowConfig(targetDir) {
235
+ const configPath = path.join(targetDir, ".lab", "config", "workflow.json");
236
+ try {
237
+ return {
238
+ config: JSON.parse(fs.readFileSync(configPath, "utf8")),
239
+ issues: [],
240
+ };
241
+ } catch (error) {
242
+ return {
243
+ config: null,
244
+ issues: [`invalid workflow.json: ${error.message}`],
245
+ };
246
+ }
247
+ }
248
+
249
+ function validateWorkflowConfig(config) {
250
+ const issues = [];
251
+ if (!config || typeof config !== "object") {
252
+ return issues;
253
+ }
254
+ if (!["en", "zh"].includes(config.workflow_language)) {
255
+ issues.push("invalid workflow_language");
256
+ }
257
+ if (!["en", "zh"].includes(config.paper_language)) {
258
+ issues.push("invalid paper_language");
259
+ }
260
+ if (config.paper_format !== "latex") {
261
+ issues.push("unsupported paper_format");
262
+ }
263
+ if (typeof config.deliverables_root !== "string" || config.deliverables_root.trim() === "") {
264
+ issues.push("invalid deliverables_root");
265
+ } else {
266
+ const normalized = config.deliverables_root.replace(/\\/g, "/");
267
+ if (normalized === ".lab" || normalized.startsWith(".lab/")) {
268
+ issues.push("deliverables_root must not point inside .lab");
269
+ }
270
+ }
271
+ return issues;
272
+ }
273
+
274
+ function validateDeliverables(targetDir, config) {
275
+ const issues = [];
276
+ if (!config || typeof config.deliverables_root !== "string" || config.deliverables_root.trim() === "") {
277
+ return issues;
278
+ }
279
+
280
+ const deliverablesRoot = path.join(targetDir, config.deliverables_root);
281
+ const paperDir = path.join(deliverablesRoot, "paper");
282
+ const mainTexPath = path.join(paperDir, "main.tex");
283
+ const mainMdPath = path.join(paperDir, "main.md");
284
+ const sectionsDir = path.join(paperDir, "sections");
285
+
286
+ if (fs.existsSync(mainMdPath)) {
287
+ issues.push("paper main manuscript must be main.tex");
288
+ }
289
+
290
+ if (fs.existsSync(paperDir) && !fs.existsSync(mainTexPath)) {
291
+ issues.push("paper output is missing paper/main.tex");
292
+ }
293
+
294
+ if (fs.existsSync(sectionsDir)) {
295
+ const nonTexFiles = listFilesRecursive(sectionsDir)
296
+ .filter((filePath) => path.extname(filePath) !== ".tex")
297
+ .map((filePath) => path.relative(targetDir, filePath));
298
+ if (nonTexFiles.length > 0) {
299
+ issues.push(`paper sections must be .tex files: ${nonTexFiles.join(", ")}`);
300
+ }
301
+ }
302
+
303
+ return issues;
304
+ }
305
+
306
+ function printDoctor(options) {
307
+ const projectInfo = getProjectVersionInfo({ targetDir: options.targetDir });
308
+ const requiredPaths = [
309
+ ".lab/system/core.md",
310
+ ".lab/config/workflow.json",
311
+ ".lab/context/mission.md",
312
+ ".lab/context/state.md",
313
+ ".lab/context/decisions.md",
314
+ ".lab/context/evidence-index.md",
315
+ ".lab/context/open-questions.md",
316
+ ".lab/context/terminology-lock.md",
317
+ ".lab/context/summary.md",
318
+ ".lab/context/next-action.md",
319
+ ".lab/context/session-brief.md",
320
+ ".lab/.managed/install.json",
321
+ ];
322
+ const missing = requiredPaths.filter((relativePath) => !fs.existsSync(path.join(options.targetDir, relativePath)));
323
+ const { config, issues: configReadIssues } = readWorkflowConfig(options.targetDir);
324
+ const configIssues = configReadIssues.concat(validateWorkflowConfig(config));
325
+ const deliverableIssues = validateDeliverables(options.targetDir, config);
326
+
327
+ if (projectInfo.status === "missing") {
328
+ console.log("status: missing");
329
+ console.log(`target: ${options.targetDir}`);
330
+ console.log("project: not initialized");
331
+ return;
332
+ }
333
+
334
+ if (missing.length > 0 || configIssues.length > 0 || deliverableIssues.length > 0) {
335
+ console.log("status: degraded");
336
+ console.log(`target: ${options.targetDir}`);
337
+ console.log(`project: ${projectInfo.package_version}`);
338
+ console.log(`platform: ${projectInfo.platform}`);
339
+ console.log(`language: ${projectInfo.lang}`);
340
+ console.log(`missing: ${missing.length > 0 ? missing.join(", ") : "none"}`);
341
+ console.log(`config: ${configIssues.length > 0 ? configIssues.join(" | ") : "none"}`);
342
+ console.log(`outputs: ${deliverableIssues.length > 0 ? deliverableIssues.join(" | ") : "none"}`);
343
+ return;
344
+ }
345
+
346
+ console.log("status: ok");
347
+ console.log(`target: ${options.targetDir}`);
348
+ console.log(`cli: ${PACKAGE_VERSION}`);
349
+ console.log(`project: ${projectInfo.package_version}`);
350
+ console.log(`platform: ${projectInfo.platform}`);
351
+ console.log(`language: ${projectInfo.lang}`);
352
+ console.log("missing: none");
353
+ console.log("config: none");
354
+ console.log("outputs: none");
355
+ }
356
+
153
357
  function shouldUseInteractiveInit(options) {
154
358
  if (options.lang && options.platform) {
155
359
  return false;
@@ -201,7 +405,7 @@ async function main() {
201
405
  return;
202
406
  }
203
407
 
204
- if (!["init", "install", "update", "version"].includes(command)) {
408
+ if (!["init", "install", "update", "version", "handoff", "doctor", "context"].includes(command)) {
205
409
  throw new Error(`Unknown command: ${command}`);
206
410
  }
207
411
 
@@ -210,6 +414,33 @@ async function main() {
210
414
  return;
211
415
  }
212
416
 
417
+ if (command === "handoff") {
418
+ printHandoff(parseTargetOnlyArgs(rest));
419
+ return;
420
+ }
421
+
422
+ if (command === "doctor") {
423
+ printDoctor(parseTargetOnlyArgs(rest));
424
+ return;
425
+ }
426
+
427
+ if (command === "context") {
428
+ const options = parseContextArgs(rest);
429
+ if (options.action === "refresh") {
430
+ refreshContext({ targetDir: options.targetDir });
431
+ console.log(`context refreshed in ${options.targetDir}`);
432
+ return;
433
+ }
434
+ if (options.action === "prune") {
435
+ pruneContext({ targetDir: options.targetDir });
436
+ console.log(`context pruned in ${options.targetDir}`);
437
+ return;
438
+ }
439
+ const result = archiveContext({ targetDir: options.targetDir });
440
+ console.log(`context archived in ${result.archivePath}`);
441
+ return;
442
+ }
443
+
213
444
  if (command === "update") {
214
445
  const options = parseUpdateArgs(rest);
215
446
  if (options.allProjects) {