ystack 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -160,6 +160,51 @@ function installHooks(projectRoot, ystackRoot) {
160
160
  });
161
161
  }
162
162
 
163
+ // PreToolUse — progress-before-ship (on Bash for git push / gh pr create)
164
+ const hasProgressBeforeShip = settings.hooks.PreToolUse.some(
165
+ (h) => h.hooks?.some((hh) => hh.command?.includes("progress-before-ship")),
166
+ );
167
+ if (!hasProgressBeforeShip) {
168
+ settings.hooks.PreToolUse.push({
169
+ matcher: "Bash",
170
+ hooks: [{
171
+ type: "command",
172
+ command: `node "${join(projectRoot, ".claude", "hooks", "progress-before-ship.js")}"`,
173
+ timeout: 5,
174
+ }],
175
+ });
176
+ }
177
+
178
+ // PreToolUse — no-undocumented-check (on Edit of progress files)
179
+ const hasNoUndocumentedCheck = settings.hooks.PreToolUse.some(
180
+ (h) => h.hooks?.some((hh) => hh.command?.includes("no-undocumented-check")),
181
+ );
182
+ if (!hasNoUndocumentedCheck) {
183
+ settings.hooks.PreToolUse.push({
184
+ matcher: "Edit",
185
+ hooks: [{
186
+ type: "command",
187
+ command: `node "${join(projectRoot, ".claude", "hooks", "no-undocumented-check.js")}"`,
188
+ timeout: 5,
189
+ }],
190
+ });
191
+ }
192
+
193
+ // PostToolUse — docs-match-progress (after editing doc files)
194
+ const hasDocsMatchProgress = settings.hooks.PostToolUse.some(
195
+ (h) => h.hooks?.some((hh) => hh.command?.includes("docs-match-progress")),
196
+ );
197
+ if (!hasDocsMatchProgress) {
198
+ settings.hooks.PostToolUse.push({
199
+ matcher: "Edit|Write",
200
+ hooks: [{
201
+ type: "command",
202
+ command: `node "${join(projectRoot, ".claude", "hooks", "docs-match-progress.js")}"`,
203
+ timeout: 5,
204
+ }],
205
+ });
206
+ }
207
+
163
208
  mkdirSync(dirname(settingsPath), { recursive: true });
164
209
  writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
165
210
 
@@ -197,19 +242,26 @@ function removeHooks(projectRoot) {
197
242
  const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
198
243
  if (settings.hooks?.PostToolUse) {
199
244
  settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(
200
- (h) => !h.hooks?.some((hh) => hh.command?.includes("context-monitor")),
245
+ (h) => !h.hooks?.some((hh) =>
246
+ hh.command?.includes("context-monitor") ||
247
+ hh.command?.includes("docs-match-progress")
248
+ ),
201
249
  );
202
250
  }
203
251
  if (settings.hooks?.PreToolUse) {
204
252
  settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
205
- (h) => !h.hooks?.some((hh) => hh.command?.includes("workflow-nudge")),
253
+ (h) => !h.hooks?.some((hh) =>
254
+ hh.command?.includes("workflow-nudge") ||
255
+ hh.command?.includes("progress-before-ship") ||
256
+ hh.command?.includes("no-undocumented-check")
257
+ ),
206
258
  );
207
259
  }
208
260
  writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
209
261
  } catch { /* ignore */ }
210
262
 
211
263
  const hooksDir = join(projectRoot, ".claude", "hooks");
212
- for (const file of ["context-monitor.js", "session-start.sh", "workflow-nudge.js"]) {
264
+ for (const file of ["context-monitor.js", "session-start.sh", "workflow-nudge.js", "progress-before-ship.js", "docs-match-progress.js", "no-undocumented-check.js"]) {
213
265
  const target = join(hooksDir, file);
214
266
  if (existsSync(target)) rmSync(target);
215
267
  }
@@ -284,42 +336,7 @@ async function cmdInit() {
284
336
  }
285
337
  }
286
338
 
287
- // --- Step 3: Beads ---
288
- let initBeads = false;
289
- if (!skillsOnly) {
290
- const hasBeads = existsSync(join(projectRoot, ".beads"));
291
- const hasBdCli = commandExists("bd");
292
-
293
- if (hasBeads) {
294
- p.log.info("Beads already initialized");
295
- } else if (hasBdCli) {
296
- initBeads = handleCancel(await p.confirm({
297
- message: "Initialize Beads for persistent memory?",
298
- }));
299
- } else {
300
- const installBeads = handleCancel(await p.select({
301
- message: "Beads (persistent memory for agents):",
302
- options: [
303
- { label: "Install Beads (brew install beads)", value: "install" },
304
- { label: "Skip — I'll set up Beads later", value: "skip" },
305
- ],
306
- }));
307
-
308
- if (installBeads === "install") {
309
- p.log.step("Installing Beads...");
310
- try {
311
- execSync("brew install beads", { stdio: "inherit" });
312
- p.log.success("Beads installed");
313
- initBeads = true;
314
- } catch {
315
- p.log.warn("Install failed. Try manually: brew install beads");
316
- p.log.warn("or: npm install -g @beads/bd");
317
- }
318
- }
319
- }
320
- }
321
-
322
- // --- Step 4: Runtime ---
339
+ // --- Step 3: Runtime ---
323
340
  const runtime = handleCancel(await p.select({
324
341
  message: "Runtime:",
325
342
  options: [
@@ -328,7 +345,7 @@ async function cmdInit() {
328
345
  ],
329
346
  }));
330
347
 
331
- // --- Step 5: Hooks ---
348
+ // --- Step 4: Hooks ---
332
349
  let installHooksFlag = true;
333
350
  if (!skillsOnly && runtime === "claude-code") {
334
351
  installHooksFlag = handleCancel(await p.confirm({
@@ -352,7 +369,9 @@ async function cmdInit() {
352
369
  }
353
370
 
354
371
  // Config
355
- const configPath = join(projectRoot, "ystack.config.json");
372
+ const ystackDir = join(projectRoot, ".ystack");
373
+ mkdirSync(ystackDir, { recursive: true });
374
+ const configPath = join(ystackDir, "config.json");
356
375
  const configExisted = existsSync(configPath);
357
376
  if (!configExisted) {
358
377
  const monorepo = detectMonorepo();
@@ -376,15 +395,15 @@ async function cmdInit() {
376
395
  writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
377
396
  }
378
397
 
379
- ensureGitignore(projectRoot);
380
-
381
- // Beads
382
- if (initBeads) {
383
- try {
384
- execSync("bd init", { stdio: "pipe", cwd: projectRoot });
385
- } catch { /* handled below */ }
398
+ // Progress directory
399
+ const progressDir = join(ystackDir, "progress");
400
+ mkdirSync(progressDir, { recursive: true });
401
+ if (!existsSync(join(progressDir, "_overview.md"))) {
402
+ writeFileSync(join(progressDir, "_overview.md"), `# Project Progress\n\n## Module Status\n\n| Module | Done | Total | Status |\n|--------|------|-------|--------|\n\n## Ready Front\n\n_No modules registered yet. Run \`/import\` or \`/scaffold\` to get started._\n`);
386
403
  }
387
404
 
405
+ ensureGitignore(projectRoot);
406
+
388
407
  s.stop("Setup complete");
389
408
 
390
409
  // Summary
@@ -393,13 +412,11 @@ async function cmdInit() {
393
412
  p.log.success("Hooks: context-monitor, workflow-nudge");
394
413
  }
395
414
  if (configExisted) {
396
- p.log.info("Config: ystack.config.json preserved");
415
+ p.log.info("Config: .ystack/config.json preserved");
397
416
  } else {
398
- p.log.success("Config: created ystack.config.json");
399
- }
400
- if (initBeads) {
401
- p.log.success("Beads initialized");
417
+ p.log.success("Config: created .ystack/config.json");
402
418
  }
419
+ p.log.success("Progress: .ystack/progress/ ready");
403
420
 
404
421
  p.note(
405
422
  [
@@ -443,7 +460,7 @@ async function cmdRemove() {
443
460
  p.intro("ystack remove");
444
461
 
445
462
  const proceed = handleCancel(await p.confirm({
446
- message: "Remove ystack skills and hooks? (keeps config, beads, docs)",
463
+ message: "Remove ystack skills and hooks? (keeps .ystack/, docs)",
447
464
  }));
448
465
  if (!proceed) {
449
466
  p.outro("Cancelled.");
@@ -456,7 +473,7 @@ async function cmdRemove() {
456
473
  removeHooks(projectRoot);
457
474
  s.stop("Removed skills and hooks");
458
475
 
459
- p.log.info("Kept: ystack.config.json, .beads/, docs/");
476
+ p.log.info("Kept: .ystack/, docs/");
460
477
  p.outro("Done!");
461
478
  }
462
479
 
@@ -504,11 +521,12 @@ async function cmdCreate() {
504
521
  "docs/src/content",
505
522
  ".claude/skills",
506
523
  ".context",
524
+ ".ystack/progress",
507
525
  ];
508
526
  for (const dir of dirs) {
509
527
  mkdirSync(join(projectDir, dir), { recursive: true });
510
528
  }
511
- console.log(green(" ✓ apps/, packages/, docs/, .claude/, .context/\n"));
529
+ console.log(green(" ✓ apps/, packages/, docs/, .claude/, .context/, .ystack/\n"));
512
530
 
513
531
  // --- Generate files ---
514
532
  console.log(bold("Generating config files..."));
@@ -589,7 +607,7 @@ async function cmdCreate() {
589
607
  writeFileSync(join(projectDir, "biome.json"), JSON.stringify(biomeConfig, null, 2) + "\n");
590
608
  console.log(green(" ✓ biome.json"));
591
609
 
592
- // ystack.config.json
610
+ // .ystack/config.json
593
611
  const docsRoot = docsFramework === "fumadocs" ? "content/docs" : "docs/src/content";
594
612
  const ystackConfig = {
595
613
  project: name,
@@ -608,8 +626,12 @@ async function cmdCreate() {
608
626
  auto_docs_check: true,
609
627
  },
610
628
  };
611
- writeFileSync(join(projectDir, "ystack.config.json"), JSON.stringify(ystackConfig, null, 2) + "\n");
612
- console.log(green(" ✓ ystack.config.json"));
629
+ writeFileSync(join(projectDir, ".ystack/config.json"), JSON.stringify(ystackConfig, null, 2) + "\n");
630
+ console.log(green(" ✓ .ystack/config.json"));
631
+
632
+ // .ystack/progress/_overview.md
633
+ writeFileSync(join(projectDir, ".ystack/progress/_overview.md"), `# Project Progress\n\n## Module Status\n\n| Module | Done | Total | Status |\n|--------|------|-------|--------|\n\n## Ready Front\n\n_No modules registered yet. Run \`/import\` or \`/scaffold\` to get started._\n`);
634
+ console.log(green(" ✓ .ystack/progress/_overview.md"));
613
635
 
614
636
  // CLAUDE.md
615
637
  const claudeMd = `# ${name}
@@ -624,7 +646,7 @@ This project uses [ystack](https://github.com/yulonghe97/ystack) for doc-driven
624
646
 
625
647
  ## Module Registry
626
648
 
627
- Modules are defined in \`ystack.config.json\`. Each module maps code directories to documentation pages.
649
+ Modules are defined in \`.ystack/config.json\`. Each module maps code directories to documentation pages.
628
650
 
629
651
  ## Available Commands
630
652
 
@@ -662,7 +684,7 @@ This project uses [ystack](https://github.com/yulonghe97/ystack) for doc-driven
662
684
 
663
685
  ## Module Registry
664
686
 
665
- Modules are defined in \`ystack.config.json\`. Each module maps code directories to documentation pages.
687
+ Modules are defined in \`.ystack/config.json\`. Each module maps code directories to documentation pages.
666
688
 
667
689
  ## Workflow
668
690
 
@@ -932,7 +954,7 @@ function usage() {
932
954
  ${bold("ystack")} — agent harness for doc-driven development
933
955
 
934
956
  ${bold("Commands:")}
935
- ${green("init")} Interactive setup — configure docs, beads, runtime, hooks
957
+ ${green("init")} Interactive setup — configure docs, runtime, hooks
936
958
  ${green("init --skills-only")} Install skills only, skip everything else
937
959
  ${green("update")} Update skills and hooks to latest version
938
960
  ${green("remove")} Remove ystack skills and hooks (keeps data)
@@ -0,0 +1,100 @@
1
+ /**
2
+ * ystack docs-match-progress — PostToolUse hook
3
+ *
4
+ * After writing/editing doc files, checks that any [x] features in the
5
+ * module's progress file have real content in docs (not <!-- ystack:stub -->).
6
+ *
7
+ * Fires after Edit or Write on files under the docs root.
8
+ */
9
+
10
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
11
+ import { join, relative } from "node:path";
12
+
13
+ // Only check on doc file edits
14
+ const filePath = process.env.CLAUDE_TOOL_INPUT_FILE_PATH || "";
15
+ if (!filePath) {
16
+ process.exit(0);
17
+ }
18
+
19
+ // Must be a ystack project
20
+ if (!existsSync(".ystack/config.json")) {
21
+ process.exit(0);
22
+ }
23
+
24
+ // Read config to find docs root
25
+ let config;
26
+ try {
27
+ config = JSON.parse(readFileSync(".ystack/config.json", "utf-8"));
28
+ } catch {
29
+ process.exit(0);
30
+ }
31
+
32
+ const docsRoot = config.docs?.root;
33
+ if (!docsRoot || !filePath.includes(docsRoot)) {
34
+ process.exit(0);
35
+ }
36
+
37
+ // Find which module this doc file belongs to
38
+ const modules = config.modules || {};
39
+ let matchedModule = null;
40
+
41
+ for (const [key, mod] of Object.entries(modules)) {
42
+ if (mod.doc && filePath.includes(mod.doc)) {
43
+ matchedModule = key;
44
+ break;
45
+ }
46
+ }
47
+
48
+ if (!matchedModule) {
49
+ process.exit(0);
50
+ }
51
+
52
+ // Read the module's progress file
53
+ const progressPath = `.ystack/progress/${matchedModule}.md`;
54
+ if (!existsSync(progressPath)) {
55
+ process.exit(0);
56
+ }
57
+
58
+ const progressContent = readFileSync(progressPath, "utf-8");
59
+
60
+ // Find checked items with doc anchors
61
+ const checkedPattern = /^- \[x\] .+→\s*(.+)/gm;
62
+ const checkedAnchors = [];
63
+ let match;
64
+ while ((match = checkedPattern.exec(progressContent)) !== null) {
65
+ checkedAnchors.push(match[1].trim());
66
+ }
67
+
68
+ if (checkedAnchors.length === 0) {
69
+ process.exit(0);
70
+ }
71
+
72
+ // Read the doc file being edited
73
+ let docContent;
74
+ try {
75
+ docContent = readFileSync(filePath, "utf-8");
76
+ } catch {
77
+ process.exit(0);
78
+ }
79
+
80
+ // Check for stubs that should have been filled
81
+ const stubs = [];
82
+ for (const anchor of checkedAnchors) {
83
+ // Extract the anchor part (after #)
84
+ const anchorId = anchor.includes("#") ? anchor.split("#")[1] : null;
85
+ if (!anchorId) continue;
86
+
87
+ // Check if this anchor's section still has a stub marker
88
+ const anchorRegex = new RegExp(
89
+ `\\{#${anchorId}\\}[\\s\\S]*?<!-- ystack:stub -->`,
90
+ );
91
+ if (anchorRegex.test(docContent)) {
92
+ stubs.push(anchorId);
93
+ }
94
+ }
95
+
96
+ if (stubs.length > 0) {
97
+ console.log(
98
+ `[ystack] ${stubs.length} feature(s) marked done in progress but still stubbed in docs: ${stubs.join(", ")}`,
99
+ );
100
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * ystack no-undocumented-check — PreToolUse hook on Edit
3
+ *
4
+ * When someone checks a box in a progress file ([ ] → [x]),
5
+ * verifies the linked doc section doesn't still have <!-- ystack:stub -->.
6
+ *
7
+ * Prevents marking features as "done" when docs haven't been written.
8
+ * This is a warn, not a block — /go checks the box before /docs runs,
9
+ * so the stub is expected at that point. The warning reminds you to
10
+ * run /docs before /pr.
11
+ */
12
+
13
+ import { existsSync, readFileSync } from "node:fs";
14
+
15
+ // Only check edits to progress files
16
+ const filePath = process.env.CLAUDE_TOOL_INPUT_FILE_PATH || "";
17
+ if (!filePath.includes(".ystack/progress/") || filePath.includes("_overview")) {
18
+ process.exit(0);
19
+ }
20
+
21
+ // Check if the edit is checking a box
22
+ const newContent = process.env.CLAUDE_TOOL_INPUT_NEW_STRING || "";
23
+ if (!newContent.includes("- [x]")) {
24
+ process.exit(0);
25
+ }
26
+
27
+ // Must be a ystack project with config
28
+ if (!existsSync(".ystack/config.json")) {
29
+ process.exit(0);
30
+ }
31
+
32
+ let config;
33
+ try {
34
+ config = JSON.parse(readFileSync(".ystack/config.json", "utf-8"));
35
+ } catch {
36
+ process.exit(0);
37
+ }
38
+
39
+ const docsRoot = config.docs?.root;
40
+ if (!docsRoot) {
41
+ process.exit(0);
42
+ }
43
+
44
+ // Extract anchor references from the lines being checked
45
+ const anchorPattern = /- \[x\] .+→\s*(.+)/g;
46
+ let match;
47
+ const anchors = [];
48
+ while ((match = anchorPattern.exec(newContent)) !== null) {
49
+ anchors.push(match[1].trim());
50
+ }
51
+
52
+ if (anchors.length === 0) {
53
+ process.exit(0);
54
+ }
55
+
56
+ // Check each anchor for stubs in the doc files
57
+ const warnings = [];
58
+ for (const anchor of anchors) {
59
+ const parts = anchor.split("#");
60
+ const docPath = parts[0];
61
+ const anchorId = parts[1];
62
+
63
+ if (!docPath || !anchorId) continue;
64
+
65
+ // Try to find the doc file
66
+ const candidates = [
67
+ `${docsRoot}/${docPath}/index.mdx`,
68
+ `${docsRoot}/${docPath}/index.md`,
69
+ `${docsRoot}/${docPath}.mdx`,
70
+ `${docsRoot}/${docPath}.md`,
71
+ ];
72
+
73
+ for (const candidate of candidates) {
74
+ if (existsSync(candidate)) {
75
+ const content = readFileSync(candidate, "utf-8");
76
+ const stubCheck = new RegExp(
77
+ `\\{#${anchorId}\\}[\\s\\S]*?<!-- ystack:stub -->`,
78
+ );
79
+ if (stubCheck.test(content)) {
80
+ warnings.push(`${docPath}#${anchorId}`);
81
+ }
82
+ break;
83
+ }
84
+ }
85
+ }
86
+
87
+ if (warnings.length > 0) {
88
+ console.log(
89
+ `[ystack] Checking off feature(s) with stubbed doc sections: ${warnings.join(", ")}. Run /docs before /pr to fill them in.`,
90
+ );
91
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * ystack progress-before-ship — PreToolUse hook on Bash
3
+ *
4
+ * Warns when creating a PR or pushing code if the branch has code
5
+ * changes in module scopes but no corresponding .ystack/progress/ updates.
6
+ *
7
+ * Only fires on git push or gh pr create commands.
8
+ */
9
+
10
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
11
+ import { execSync } from "node:child_process";
12
+
13
+ // Only check on git push or gh pr create
14
+ const toolInput = process.env.CLAUDE_TOOL_INPUT || "";
15
+ if (!toolInput.includes("git push") && !toolInput.includes("gh pr create")) {
16
+ process.exit(0);
17
+ }
18
+
19
+ // Must be a ystack project
20
+ if (!existsSync(".ystack/config.json")) {
21
+ process.exit(0);
22
+ }
23
+
24
+ // Quick mode — skip progress checks for bug fixes and chores
25
+ if (existsSync(".context/.quick")) {
26
+ process.exit(0);
27
+ }
28
+
29
+ // Get changed files on this branch vs main
30
+ let changedFiles;
31
+ try {
32
+ changedFiles = execSync("git diff main...HEAD --name-only 2>/dev/null", {
33
+ encoding: "utf-8",
34
+ }).trim().split("\n").filter(Boolean);
35
+ } catch {
36
+ process.exit(0);
37
+ }
38
+
39
+ if (changedFiles.length === 0) {
40
+ process.exit(0);
41
+ }
42
+
43
+ // Check if any code files changed (not docs, not config, not progress)
44
+ const codeChanges = changedFiles.filter(
45
+ (f) =>
46
+ !f.startsWith("docs/") &&
47
+ !f.startsWith(".ystack/") &&
48
+ !f.startsWith(".claude/") &&
49
+ !f.startsWith(".context/") &&
50
+ !f.endsWith(".md") &&
51
+ !f.endsWith(".json") &&
52
+ !f.endsWith(".yaml") &&
53
+ !f.endsWith(".yml"),
54
+ );
55
+
56
+ if (codeChanges.length === 0) {
57
+ process.exit(0);
58
+ }
59
+
60
+ // Small diffs (≤ 5 code files) are likely bug fixes or chores — don't warn
61
+ if (codeChanges.length <= 5) {
62
+ process.exit(0);
63
+ }
64
+
65
+ // Check if progress files were updated
66
+ const progressChanges = changedFiles.filter((f) =>
67
+ f.startsWith(".ystack/progress/"),
68
+ );
69
+
70
+ if (progressChanges.length === 0) {
71
+ console.log(
72
+ `[ystack] ${codeChanges.length} code files changed but no .ystack/progress/ updates. If this is a feature, did /go check the boxes? If it's a bug fix, ignore this.`,
73
+ );
74
+ }
@@ -2,19 +2,24 @@
2
2
  # ystack session start — shows project status on session start
3
3
 
4
4
  # Check for ystack project
5
- if [ ! -f "ystack.config.json" ]; then
5
+ if [ ! -f ".ystack/config.json" ]; then
6
6
  exit 0
7
7
  fi
8
8
 
9
9
  echo "[ystack] Project detected"
10
10
 
11
- # Show Beads ready front
12
- if [ -d ".beads" ] && command -v bd &> /dev/null; then
13
- READY=$(bd ready --json 2>/dev/null | head -5)
14
- if [ -n "$READY" ]; then
11
+ # Show ready front from progress files
12
+ if [ -d ".ystack/progress" ]; then
13
+ # Count unchecked items across all progress files
14
+ UNCHECKED=$(grep -r '^\- \[ \]' .ystack/progress/*.md 2>/dev/null | head -10)
15
+ if [ -n "$UNCHECKED" ]; then
15
16
  echo ""
16
17
  echo "Ready to work on:"
17
- bd ready 2>/dev/null | head -10
18
+ grep -r '^\- \[ \]' .ystack/progress/*.md 2>/dev/null | head -10 | while read -r line; do
19
+ FILE=$(echo "$line" | cut -d: -f1 | xargs basename .md)
20
+ FEATURE=$(echo "$line" | sed 's/.*\- \[ \] //' | sed 's/ *→.*//')
21
+ echo " $FILE — $FEATURE"
22
+ done
18
23
  fi
19
24
  fi
20
25
 
@@ -31,8 +31,8 @@ if (state.nudged) {
31
31
  process.exit(0);
32
32
  }
33
33
 
34
- // Developer dismissed nudges
35
- if (existsSync(".context/.no-nudge")) {
34
+ // Developer dismissed nudges or is in quick mode
35
+ if (existsSync(".context/.no-nudge") || existsSync(".context/.quick")) {
36
36
  process.exit(0);
37
37
  }
38
38
 
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "ystack",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "An agent harness for doc-driven development",
5
5
  "type": "module",
6
6
  "bin": {
7
- "ystack": "./bin/cli.js"
7
+ "ystack": "bin/cli.js"
8
8
  },
9
9
  "files": [
10
10
  "skills/",
@@ -14,7 +14,6 @@
14
14
  "PHILOSOPHY.md",
15
15
  "LINTING.md",
16
16
  "RUNTIMES.md",
17
- "PLAN.md",
18
17
  "CHANGELOG.md",
19
18
  "LICENSE"
20
19
  ],
@@ -24,14 +23,13 @@
24
23
  "harness",
25
24
  "documentation",
26
25
  "claude-code",
27
- "beads",
28
26
  "workflow"
29
27
  ],
30
28
  "author": "Yulong He",
31
29
  "license": "MIT",
32
30
  "repository": {
33
31
  "type": "git",
34
- "url": "https://github.com/yulonghe97/ystack.git"
32
+ "url": "git+https://github.com/yulonghe97/ystack.git"
35
33
  },
36
34
  "dependencies": {
37
35
  "@clack/prompts": "^1.2.0"
@@ -185,7 +185,7 @@ For items marked WON'T FIX or FALSE POSITIVE, draft reply comments:
185
185
  ### Suggested Replies
186
186
 
187
187
  **Comment #5** (@sarah — refundRequestedAt timestamp):
188
- > Good idea — we've deferred this to a follow-up. Tracked in [bead/issue reference].
188
+ > Good idea — we've deferred this to a follow-up. Tracked in [progress file / issue reference].
189
189
 
190
190
  **Comment #6** (CI: e2e-tests):
191
191
  > Flaky test unrelated to this PR — `auth/login.spec.ts` timed out. Re-running CI.
@@ -19,9 +19,9 @@ You are the planning phase of the ystack agent harness. Your job is to understan
19
19
 
20
20
  Identify which module(s) this feature belongs to.
21
21
 
22
- 1. Read `ystack.config.json` if it exists. Match the feature to a module by checking `scope` globs:
22
+ 1. Read `.ystack/config.json` if it exists. Match the feature to a module by checking `scope` globs:
23
23
  ```bash
24
- cat ystack.config.json
24
+ cat .ystack/config.json
25
25
  ```
26
26
 
27
27
  2. If no config exists or no match found, scan the docs directory structure:
@@ -117,7 +117,7 @@ Create the directory and file:
117
117
  .context/<feature-id>/DECISIONS.md
118
118
  ```
119
119
 
120
- Use a short, descriptive ID for the feature (e.g., `refund-reason`, `oauth-support`, `dashboard-charts`). If Beads is available (`bd` CLI), use the bead ID instead.
120
+ Use a short, descriptive ID for the feature (e.g., `refund-reason`, `oauth-support`, `dashboard-charts`).
121
121
 
122
122
  **DECISIONS.md format:**
123
123
 
@@ -23,11 +23,13 @@ You update documentation to reflect completed, verified work. Docs describe what
23
23
 
24
24
  3. Read `.context/<feature-id>/PLAN.md` for the success criteria — these tell you what was built.
25
25
 
26
+ 4. **Read the progress file** — `.ystack/progress/<module>.md` for the affected module. Features marked `[x]` are completed and may need doc updates. Features still `[ ]` must NOT be documented. The progress file is the gate between implementation and documentation.
27
+
26
28
  ## Phase 1: Map Changes to Docs
27
29
 
28
30
  Identify which documentation pages are affected.
29
31
 
30
- 1. **Read the module registry** (`ystack.config.json`) to find the module-to-doc mapping. Match changed file paths against module `scope` globs. If no registry exists, fall back to scanning `docs/src/content/_meta.ts`.
32
+ 1. **Read the module registry** (`.ystack/config.json`) to find the module-to-doc mapping. Match changed file paths against module `scope` globs. If no registry exists, fall back to scanning `docs/src/content/_meta.ts`.
31
33
 
32
34
  2. **Map changed code paths to doc pages:**
33
35
 
@@ -97,7 +99,7 @@ export default {
97
99
  }
98
100
  ```
99
101
 
100
- Check `ystack.config.json` `docs.framework` to know which format to use.
102
+ Check `.ystack/config.json` `docs.framework` to know which format to use.
101
103
 
102
104
  ## Phase 4: Update Structural Files
103
105