ystack 0.1.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.
@@ -0,0 +1,30 @@
1
+ /**
2
+ * ystack context monitor — PostToolUse hook
3
+ *
4
+ * Warns when context usage gets high. Uses runtime-provided
5
+ * context metrics when available, stays silent otherwise.
6
+ *
7
+ * Supported env vars (set by the runtime):
8
+ * CLAUDE_CONTEXT_TOKENS_USED — tokens consumed so far
9
+ * CLAUDE_CONTEXT_TOKENS_MAX — total context window size
10
+ */
11
+
12
+ const used = Number.parseInt(process.env.CLAUDE_CONTEXT_TOKENS_USED || "0", 10);
13
+ const max = Number.parseInt(process.env.CLAUDE_CONTEXT_TOKENS_MAX || "0", 10);
14
+
15
+ // If the runtime doesn't expose context metrics, stay silent
16
+ if (!used || !max) {
17
+ process.exit(0);
18
+ }
19
+
20
+ const pct = Math.round((used / max) * 100);
21
+
22
+ if (pct >= 80) {
23
+ console.log(
24
+ `[ystack] Context ${pct}% full. Finish current task soon. Use /pause to save state if needed.`,
25
+ );
26
+ } else if (pct >= 60) {
27
+ console.log(
28
+ `[ystack] Context ${pct}% full. Consider spawning subagents for remaining work.`,
29
+ );
30
+ }
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+ # ystack session start — shows project status on session start
3
+
4
+ # Check for ystack project
5
+ if [ ! -f "ystack.config.json" ]; then
6
+ exit 0
7
+ fi
8
+
9
+ echo "[ystack] Project detected"
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
15
+ echo ""
16
+ echo "Ready to work on:"
17
+ bd ready 2>/dev/null | head -10
18
+ fi
19
+ fi
20
+
21
+ # Check for in-progress plans
22
+ if [ -d ".context" ]; then
23
+ PLANS=$(find .context -name "PLAN.md" -maxdepth 2 2>/dev/null)
24
+ if [ -n "$PLANS" ]; then
25
+ echo ""
26
+ echo "In-progress plans:"
27
+ for plan in $PLANS; do
28
+ FEATURE=$(dirname "$plan" | xargs basename)
29
+ TITLE=$(head -1 "$plan" | sed 's/^# //')
30
+ echo " $FEATURE — $TITLE"
31
+ done
32
+ echo ""
33
+ echo "Run /go to continue, or /build for new work."
34
+ fi
35
+ fi
@@ -0,0 +1,107 @@
1
+ /**
2
+ * ystack workflow nudge — PreToolUse hook on Edit/Write
3
+ *
4
+ * Soft warning when editing multiple source files without an active plan.
5
+ * Tracks edits per session — only nudges after 3+ distinct source files
6
+ * are edited without a plan. One-off fixes don't trigger it.
7
+ *
8
+ * Dismissible: creating a file at .context/.no-nudge silences it for
9
+ * the session (e.g., when the developer intentionally works without a plan).
10
+ */
11
+
12
+ import { existsSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
13
+ import { join } from "node:path";
14
+ import { tmpdir } from "node:os";
15
+
16
+ // Session state file — tracks edited files and nudge status
17
+ const STATE_FILE = join(tmpdir(), ".ystack-nudge-state.json");
18
+
19
+ // Load state
20
+ let state = { editedFiles: [], nudged: false };
21
+ try {
22
+ if (existsSync(STATE_FILE)) {
23
+ state = JSON.parse(readFileSync(STATE_FILE, "utf-8"));
24
+ }
25
+ } catch {
26
+ // Fresh state
27
+ }
28
+
29
+ // Already nudged this session
30
+ if (state.nudged) {
31
+ process.exit(0);
32
+ }
33
+
34
+ // Developer dismissed nudges
35
+ if (existsSync(".context/.no-nudge")) {
36
+ process.exit(0);
37
+ }
38
+
39
+ // Get the file being edited
40
+ const filePath = process.env.CLAUDE_TOOL_INPUT_FILE_PATH || "";
41
+ if (!filePath) {
42
+ process.exit(0);
43
+ }
44
+
45
+ // Skip non-source files — these never need a plan
46
+ const skipPatterns = [
47
+ /\.md$/,
48
+ /\.mdx$/,
49
+ /\.json$/,
50
+ /\.yaml$/,
51
+ /\.yml$/,
52
+ /\.toml$/,
53
+ /\.env/,
54
+ /\.config\./,
55
+ /\.css$/,
56
+ /_meta\.ts$/,
57
+ /meta\.json$/,
58
+ /\.gitignore$/,
59
+ /\.context\//,
60
+ /node_modules\//,
61
+ /\.next\//,
62
+ /dist\//,
63
+ /\.test\./,
64
+ /\.spec\./,
65
+ /__tests__\//,
66
+ ];
67
+
68
+ if (skipPatterns.some((p) => p.test(filePath))) {
69
+ process.exit(0);
70
+ }
71
+
72
+ // Check if there's an active plan
73
+ let hasActivePlan = false;
74
+ if (existsSync(".context")) {
75
+ try {
76
+ for (const entry of readdirSync(".context")) {
77
+ if (existsSync(join(".context", entry, "PLAN.md"))) {
78
+ hasActivePlan = true;
79
+ break;
80
+ }
81
+ }
82
+ } catch {
83
+ // Can't read .context
84
+ }
85
+ }
86
+
87
+ // If there's a plan, no nudge needed
88
+ if (hasActivePlan) {
89
+ process.exit(0);
90
+ }
91
+
92
+ // Track this file edit
93
+ if (!state.editedFiles.includes(filePath)) {
94
+ state.editedFiles.push(filePath);
95
+ }
96
+
97
+ // Only nudge after 3+ distinct source files edited without a plan
98
+ // One or two files = probably a quick fix, not feature work
99
+ if (state.editedFiles.length >= 3) {
100
+ console.log(
101
+ `[ystack] Editing ${state.editedFiles.length} source files without a plan. Consider /build for tracked changes, or touch .context/.no-nudge to dismiss.`,
102
+ );
103
+ state.nudged = true;
104
+ }
105
+
106
+ // Save state
107
+ writeFileSync(STATE_FILE, JSON.stringify(state));
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "ystack",
3
+ "version": "0.1.0",
4
+ "description": "An agent harness for doc-driven development",
5
+ "type": "module",
6
+ "bin": {
7
+ "ystack": "./bin/cli.js"
8
+ },
9
+ "files": [
10
+ "skills/",
11
+ "hooks/",
12
+ "bin/",
13
+ "README.md",
14
+ "PHILOSOPHY.md",
15
+ "LINTING.md",
16
+ "RUNTIMES.md",
17
+ "PLAN.md",
18
+ "CHANGELOG.md",
19
+ "LICENSE"
20
+ ],
21
+ "keywords": [
22
+ "ai",
23
+ "agent",
24
+ "harness",
25
+ "documentation",
26
+ "claude-code",
27
+ "beads",
28
+ "workflow"
29
+ ],
30
+ "author": "Yulong He",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/yulonghe97/ystack.git"
35
+ },
36
+ "dependencies": {
37
+ "@clack/prompts": "^1.2.0"
38
+ }
39
+ }
@@ -0,0 +1,244 @@
1
+ ---
2
+ name: address-review
3
+ description: >
4
+ Fetch PR review comments, triage them by priority, and address them. Use this skill when the user says 'address review', '/address-review', 'address comments',
5
+ 'fix review comments', 'address feedback', 'handle PR comments', 'review feedback',
6
+ 'what do reviewers say', 'check PR comments', or when a PR has review comments that
7
+ need to be addressed.
8
+ Works with comments from human reviewers, AI reviewers, and CI checks.
9
+ user-invocable: true
10
+ ---
11
+
12
+ # /address-review — Address Review Comments
13
+
14
+ You fetch review comments from a PR, triage them by priority and validity, present the triage to the user, then address the approved fixes.
15
+
16
+ ## Phase 0: Find the PR
17
+
18
+ 1. Detect the current PR:
19
+ ```bash
20
+ gh pr view --json number,title,url,state,reviewDecision 2>/dev/null
21
+ ```
22
+
23
+ 2. If no PR is found on the current branch:
24
+ > No PR found for this branch. Provide a PR number or URL, or run `/pr` first.
25
+
26
+ 3. If a PR number or URL was passed as an argument, use that instead.
27
+
28
+ ## Phase 1: Fetch All Comments
29
+
30
+ Gather every piece of feedback on the PR.
31
+
32
+ ### Review comments (inline on code)
33
+ ```bash
34
+ gh api repos/{owner}/{repo}/pulls/{number}/comments --jq '.[] | {id, path, line: .original_line, body, user: .user.login, created_at}'
35
+ ```
36
+
37
+ ### Review summaries (top-level reviews)
38
+ ```bash
39
+ gh api repos/{owner}/{repo}/pulls/{number}/reviews --jq '.[] | {id, state, body, user: .user.login}'
40
+ ```
41
+
42
+ ### General PR comments (conversation)
43
+ ```bash
44
+ gh api repos/{owner}/{repo}/issues/{number}/comments --jq '.[] | {id, body, user: .user.login, created_at}'
45
+ ```
46
+
47
+ ### CI check failures
48
+ ```bash
49
+ gh pr checks --json name,state,description --jq '.[] | select(.state != "SUCCESS")'
50
+ ```
51
+
52
+ Combine all sources into a single list of feedback items.
53
+
54
+ ## Phase 2: Triage
55
+
56
+ For each comment, evaluate and categorize it. Read the actual code the comment refers to before making a judgment.
57
+
58
+ ### Categories
59
+
60
+ **MUST FIX** — Blocking issues that must be addressed before merge:
61
+ - Correctness bugs (logic errors, wrong behavior)
62
+ - Security issues (injection, auth bypass, secret exposure)
63
+ - Failed CI checks (typecheck, lint, build, tests)
64
+ - Reviewer explicitly requested changes (`CHANGES_REQUESTED` review state)
65
+ - Missing functionality that was promised in the PR description
66
+
67
+ **SHOULD FIX** — Valid improvements worth making:
68
+ - Accessibility gaps (missing ARIA, no keyboard support)
69
+ - Missing error handling at system boundaries
70
+ - Naming that doesn't match project conventions
71
+ - Performance concerns with evidence
72
+ - Suggestions that genuinely improve clarity
73
+
74
+ **WON'T FIX** — Comments to acknowledge but not act on:
75
+ - Style preferences not backed by project rules
76
+ - "Consider refactoring" on code outside the PR's scope
77
+ - Feature requests disguised as review comments
78
+ - Suggestions that contradict the locked decisions from DECISIONS.md
79
+ - Nitpicks on unchanged lines (pre-existing issues)
80
+
81
+ **FALSE POSITIVE** — Incorrect or misguided feedback:
82
+ - Reviewer misunderstood the code (explain why)
83
+ - Comment about code that doesn't exist (outdated diff)
84
+ - Suggestion that would break existing functionality
85
+ - CI flake (test passed on re-run, or unrelated to this PR)
86
+
87
+ ### Triage output
88
+
89
+ Present the triage to the user:
90
+
91
+ ```markdown
92
+ ## PR Review Triage
93
+
94
+ ### PR: #123 — feat(payments): add refund reason tracking
95
+ Reviewers: @sarah (changes requested), @ci-bot (2 checks failed)
96
+
97
+ ---
98
+
99
+ ### MUST FIX (3)
100
+
101
+ 1. **@sarah** on `apps/api/src/routes/payments.ts:92`
102
+ > Missing validation for empty string — `reason: ""` would pass the enum check.
103
+
104
+ **Assessment:** Valid. Zod enum allows empty string if not explicitly excluded.
105
+ **Fix:** Add `.min(1)` or use `z.enum([...])` which already excludes empty. Quick fix.
106
+
107
+ 2. **CI: typecheck** — FAILED
108
+ > Type 'string' is not assignable to type 'RefundReason'
109
+
110
+ **Assessment:** Real type error introduced by this PR.
111
+ **Fix:** Update the type cast in `admin/src/app/transactions/[id]/page.tsx`.
112
+
113
+ 3. **@sarah** on `packages/db/src/schema.ts:48`
114
+ > This migration needs a default value for existing rows.
115
+
116
+ **Assessment:** Valid. Existing transactions have no refundReason. Column should be nullable or have a default.
117
+ **Fix:** Change column to `.default(null)` and update type to `RefundReason | null`.
118
+
119
+ ---
120
+
121
+ ### SHOULD FIX (1)
122
+
123
+ 4. **@sarah** on `apps/admin/src/components/RefundReasonBadge.tsx:12`
124
+ > Badge should have different colors per reason type for visual distinction.
125
+
126
+ **Assessment:** Good UX suggestion. Not blocking but improves the feature.
127
+ **Fix:** Add variant colors — maps to existing badge color system.
128
+
129
+ ---
130
+
131
+ ### WON'T FIX (1)
132
+
133
+ 5. **@sarah** on `packages/shared/src/types/payments.ts:25`
134
+ > Should we also add a `refundRequestedAt` timestamp?
135
+
136
+ **Assessment:** Feature request, not in scope. This was explicitly deferred in DECISIONS.md.
137
+ **Response:** Acknowledge and note it's tracked for future work.
138
+
139
+ ---
140
+
141
+ ### FALSE POSITIVE (1)
142
+
143
+ 6. **CI: e2e-tests** — FAILED
144
+ > Timeout on unrelated test: `auth/login.spec.ts`
145
+
146
+ **Assessment:** Flaky test, not related to this PR. Passed on previous run.
147
+ **Action:** Re-run or ignore.
148
+
149
+ ---
150
+
151
+ > **Recommended:** Fix items 1-3 (must fix) and item 4 (should fix).
152
+ > Skip items 5-6. I'll reply to item 5 explaining it's deferred and re-run CI for item 6.
153
+ >
154
+ > Proceed?
155
+ ```
156
+
157
+ **Wait for the user to confirm** which items to fix and which to skip.
158
+
159
+ ## Phase 3: Apply Fixes
160
+
161
+ For each approved fix:
162
+
163
+ 1. **Read the file** at the referenced line.
164
+
165
+ 2. **Make the fix.** Follow the same rules as `/go`:
166
+ - Match existing code patterns
167
+ - Minimal change — fix the issue, don't refactor surrounding code
168
+ - Run the linter after changes
169
+
170
+ 3. **Verify the fix** — re-run the check that flagged it:
171
+ - If it was a typecheck error → `pnpm typecheck`
172
+ - If it was a logic bug → check the behavior
173
+ - If it was a style issue → `pnpm check`
174
+
175
+ 4. **Commit.** Group related fixes into logical commits:
176
+ - One commit for all "must fix" code fixes: `fix(<scope>): address review — <summary>`
177
+ - Separate commit if a fix is substantial enough to stand alone
178
+ - Do not amend previous commits — always create new ones
179
+
180
+ ## Phase 4: Respond to Comments
181
+
182
+ For items marked WON'T FIX or FALSE POSITIVE, draft reply comments:
183
+
184
+ ```markdown
185
+ ### Suggested Replies
186
+
187
+ **Comment #5** (@sarah — refundRequestedAt timestamp):
188
+ > Good idea — we've deferred this to a follow-up. Tracked in [bead/issue reference].
189
+
190
+ **Comment #6** (CI: e2e-tests):
191
+ > Flaky test unrelated to this PR — `auth/login.spec.ts` timed out. Re-running CI.
192
+ ```
193
+
194
+ Ask the user before posting:
195
+ > Want me to post these replies on the PR?
196
+
197
+ If yes:
198
+ ```bash
199
+ gh api repos/{owner}/{repo}/pulls/{number}/comments/{id}/replies -f body="<reply>"
200
+ # or for general comments:
201
+ gh pr comment {number} --body "<reply>"
202
+ ```
203
+
204
+ ## Phase 5: Push and Report
205
+
206
+ 1. **Push fixes:**
207
+ ```bash
208
+ git push
209
+ ```
210
+
211
+ 2. **Re-run failed CI** if applicable:
212
+ ```bash
213
+ gh run rerun {run-id} --failed
214
+ ```
215
+
216
+ 3. **Report:**
217
+ ```markdown
218
+ ## Fix Summary
219
+
220
+ ### Applied (N)
221
+ - Fixed empty string validation in payments route
222
+ - Fixed type error in admin page
223
+ - Added nullable default for migration
224
+ - Added color variants to RefundReasonBadge
225
+
226
+ ### Replied (N)
227
+ - Acknowledged deferred timestamp feature
228
+ - Re-running flaky CI
229
+
230
+ ### Skipped (N)
231
+ - (none)
232
+
233
+ Pushed to branch. CI re-running.
234
+ ```
235
+
236
+ ---
237
+
238
+ ## What This Skill Does NOT Do
239
+
240
+ - **Does not auto-fix without user approval.** The triage is always presented first.
241
+ - **Does not argue with reviewers.** WON'T FIX replies are respectful acknowledgments, not rebuttals.
242
+ - **Does not refactor beyond the fix.** Fix the comment, not the surrounding code.
243
+ - **Does not merge the PR.** That's still a human decision.
244
+ - **Does not dismiss reviews.** Even after fixing, the reviewer re-approves manually.