Skip to content

Commit 29bf360

Browse files
author
ClaudiaFang
committed
Merge remote-tracking branch 'origin/claude/settings-ux-improvements-260713' into claude/fix-directory-symlink-pull-260713
# Conflicts: # src/settings.ts # tests/setup.ts
2 parents 8f0e6d5 + f5ae8ef commit 29bf360

19 files changed

Lines changed: 848 additions & 23 deletions

AGENTS.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
11
# Project Agent Design & Hierarchy (Synchronized with Skills)
22

3+
## Startup Workflow
4+
5+
Before writing code:
6+
- Read `feature_list.json` (active/next-up work — GitHub Issues on `firstsun-dev/git-files-sync`, Project #6, is the real source of truth; re-sync stale entries) and `progress.md` (what's currently open).
7+
- Read `session-handoff.md` for the previous session's exact stopping point.
8+
- Run `./init.sh` to confirm a clean, green baseline before editing.
9+
10+
## Definition of Done
11+
12+
A feature is done only when all of the following hold, with evidence recorded (command + result) in `progress.md`:
13+
- `npx eslint .` — 0 errors
14+
- `npm run build` — passes (tsc + Obsidian 1.11.0 compat typecheck + esbuild)
15+
- `npx vitest run` — passes
16+
- Manual verification in Obsidian, when the change has a runtime UI surface
17+
18+
## Stay in Scope
19+
20+
- One feature at a time: pick a single `feature_list.json` entry and finish it (with recorded evidence) before starting the next.
21+
- Don't expand scope mid-task — file a new GitHub issue via the `firstsun-pm` skill instead of quietly bundling unrelated work.
22+
23+
## End of Session
24+
25+
Before ending a session:
26+
- Overwrite `session-handoff.md` (don't append) with the new stopping point so the repo stays restartable.
27+
- Move finished items from `progress.md` into `archive/YYYY-MM.md` (current month) — next steps in `progress.md` should read as a short list, not a changelog.
28+
329
## Agent Tiers
430

531
### 1. High-Tier (Red/Orange Group)

CLAUDE.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5+
## Agent Workflow
6+
7+
- **Startup**: read `feature_list.json` (active/next-up work; GitHub Issues on `firstsun-dev/git-files-sync`, Project #6, is the actual source of truth — re-sync before trusting stale entries) and `progress.md` (what's open right now), then `session-handoff.md` for the previous session's exact stopping point.
8+
- **Before editing**: run `./init.sh` (installs deps, then lint + test + build) to confirm you're starting from a green baseline.
9+
- **Definition of done**: `npx eslint .` has 0 errors, `npm run build` passes (includes the Obsidian 1.11.0 compat typecheck), and `npx vitest run` passes, *and* evidence of that run is recorded (one line: command + result) in `progress.md` or the PR description — not just claimed.
10+
- **Scope**: work one `feature_list.json` entry at a time; don't start the next until the current one's evidence is recorded.
11+
- **End of session**: overwrite `session-handoff.md` with the new stopping point, move finished items from `progress.md` into `archive/YYYY-MM.md` (current month).
12+
- Issue/PR conventions (Conventional Commits titles, Project #6 fields, English-only for this public plugin repo) are defined in the `firstsun-pm` skill, not duplicated here.
13+
514
## Development Commands
615
- Build: `npm run build` (runs type check and esbuild in production mode)
716
- Dev: `npm run dev` (builds in watch mode using esbuild)

archive/2026-07.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Progress Archive — 2026-07
2+
3+
One archive file per calendar month. Each entry is one line: feature id/name +
4+
commit hash. Debugging narrative and design discussion belong in the commit
5+
message, not here. Start a new `archive/YYYY-MM.md` file when the month rolls
6+
over — don't let a single archive file grow without bound either.
7+
8+
- feat-001: Project Setup verified clean (npm install/lint/build/test) (commit 28f4f8e)
9+
- feat-002: Settings UX bundle implemented — conflict modal resize + tabs (#42), connection status badge (#41), local ignore patterns (#40); 254 tests pass, lint/build clean (commit 28f4f8e, branch claude/settings-ux-improvements-260713)

feature_list.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"_note": "GitHub Issues (firstsun-dev/git-files-sync, Project #6) is the source of truth for the full backlog and priority/estimate fields. This file mirrors only the active feature and the next few candidates so an agent session has a local, offline checkpoint — sync it against `gh issue list --repo firstsun-dev/git-files-sync --state open` at the start of a session rather than treating it as authoritative.",
3+
"features": [
4+
{
5+
"id": "feat-001",
6+
"name": "Project Setup",
7+
"description": "Confirm the project can install dependencies, run verification, and start from a clean checkout",
8+
"dependencies": [],
9+
"status": "done",
10+
"evidence": "Commit 28f4f8e - npm install/lint/build/test all pass from a clean checkout"
11+
},
12+
{
13+
"id": "feat-002",
14+
"name": "Settings UX bundle (issues #40, #41, #42)",
15+
"description": "Local ignore-pattern sync setting (#40), persistent connection status badge in settings (#41), and resized/tabbed conflict resolution modal (#42)",
16+
"dependencies": ["feat-001"],
17+
"status": "in-review",
18+
"evidence": "Commit 28f4f8e on branch claude/settings-ux-improvements-260713 - lint/build/test pass (254 tests); pushed to origin, PR not yet opened"
19+
},
20+
{
21+
"id": "feat-003",
22+
"name": "fix: symbolic link pull fails (issue #33)",
23+
"description": "Bug report with screenshot; needs repro/investigation before a fix can be scoped",
24+
"dependencies": [],
25+
"status": "not-started",
26+
"evidence": ""
27+
},
28+
{
29+
"id": "feat-004",
30+
"name": "fix: resolve sonarqube issues (issue #45)",
31+
"description": "Review current SonarQube findings and fix code smells/bugs/security hotspots where applicable",
32+
"dependencies": [],
33+
"status": "not-started",
34+
"evidence": ""
35+
},
36+
{
37+
"id": "feat-005",
38+
"name": "feat(settings): folder picker for root path / vault folder (issue #48)",
39+
"description": "Searchable folder suggester for the Root path and Vault folder settings fields, replacing free-text entry",
40+
"dependencies": [],
41+
"status": "not-started",
42+
"evidence": ""
43+
}
44+
],
45+
"_evidenceStyle": "Keep evidence to one line: commit hash + short pointer (e.g. 'Commit abc1234 - added X, tests pass'). Debugging narrative and design discussion belong in the commit message, not this file."
46+
}

init.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
set -e
3+
4+
echo "=== Harness Initialization ==="
5+
6+
echo "=== npm install ==="
7+
npm install
8+
9+
echo "=== npm run lint ==="
10+
npm run lint
11+
12+
echo "=== npm test ==="
13+
npm test
14+
15+
echo "=== npm run build ==="
16+
npm run build
17+
18+
echo "=== Verification Complete ==="
19+
echo ""
20+
echo "Next steps:"
21+
echo "1. Read feature_list.json to see current feature state"
22+
echo "2. Pick ONE unfinished feature to work on"
23+
echo "3. Implement only that feature"
24+
echo "4. Re-run verification before claiming done"

progress.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Session Progress Log
2+
3+
<!--
4+
CLEANUP CADENCE: this file tracks only what's still open. When a feature
5+
finishes, move its narrative to archive/YYYY-MM.md (current month) as a
6+
one-line entry (name + commit hash) and remove it from here. Archive once
7+
this file passes ~80 lines — "What's Done" is a snapshot, not a permanent
8+
changelog.
9+
-->
10+
11+
Completed work is archived in [archive/](./archive/), one file per calendar month — this file only tracks what's still open.
12+
13+
## Current State
14+
15+
**Last Updated:** 2026-07-13 11:40
16+
**Session ID:** session_01YYCTyZw7gUmJ7oh1VTmAqh
17+
**Active Feature:** feat-002 - Settings UX bundle (issues #40, #41, #42)
18+
19+
## Status
20+
21+
### What's Done
22+
23+
- [x] feat-001 - Project Setup verified (see archive/2026-07.md)
24+
- [x] feat-002 implementation - all three sub-features coded, tested, linted, built (see archive/2026-07.md)
25+
26+
### What's In Progress
27+
28+
- [ ] feat-002 - Open a PR for branch `claude/settings-ux-improvements-260713` and get it merged
29+
- Details: commit 28f4f8e is pushed to origin; no PR opened yet
30+
- Blockers: none, just needs `gh pr create`
31+
32+
### What's Next
33+
34+
1. Open PR for `claude/settings-ux-improvements-260713`, close issues #40/#41/#42 on merge
35+
2. Pick up feat-003 (issue #33, symlink pull fails) — needs repro investigation first
36+
3. Re-sync this file's backlog entries against `gh issue list --repo firstsun-dev/git-files-sync --state open` since new issues may have been filed (e.g. #48 folder picker was added mid-session)
37+
38+
## Blockers / Risks
39+
40+
- None currently.
41+
42+
## Decisions Made
43+
44+
- **Discarded a stale local WIP for Obsidian 1.12.x compatibility**: origin/main already shipped this via PR #46 (`applyDestructiveStyle`, `typecheck-compat.mjs`) with a different mechanism. Local main was fast-forwarded to origin/main and the new features (#40/#41/#42) were manually re-applied on top of the correct base rather than merged via `git stash pop` (which would have conflicted/duplicated the compat layer).
45+
- **feat-002 bundles three issues in one commit**: #40/#41/#42 touch overlapping files (`src/settings.ts`, `styles.css`, `tests/setup.ts`) closely enough that splitting into three atomic commits wasn't worth the risk of an intermediate broken state.
46+
47+
## Files Modified This Session
48+
49+
- `src/settings.ts` - connection status badge (#41), ignore patterns setting (#40)
50+
- `src/ui/SyncConflictModal.ts` - Diff/Local/Remote tab switcher (#42)
51+
- `src/logic/gitignore-manager.ts`, `src/main.ts` - local ignore pattern matching (#40)
52+
- `styles.css` - badge + modal + tab styles
53+
- `tests/setup.ts`, `tests/ui/setup-dom.ts` - expanded Obsidian mocks (TextAreaComponent, removeClass, configDir, etc.)
54+
- `tests/logic/gitignore-manager.test.ts`, `tests/logic/sync-manager*.test.ts`, `tests/ui/SyncConflictModal.test.ts`, `tests/ui/SettingsConnectionStatus.test.ts` - new/updated tests
55+
56+
## Evidence of Completion
57+
58+
- [x] Tests pass: `npx vitest run` → 254/254 passed
59+
- [x] Type check clean: `npm run build` (tsc + Obsidian 1.11.0 compat typecheck + esbuild) → clean
60+
- [x] Lint clean: `npx eslint .` → 0 errors
61+
- [ ] Manual verification in Obsidian: not done (no Obsidian instance available in this environment)
62+
63+
## Notes for Next Session
64+
65+
- Branch `claude/settings-ux-improvements-260713` is pushed but has no PR yet — check with the user before opening one (they were asked and hadn't confirmed as of session end).
66+
- `feature_list.json`'s backlog (feat-003..005) is a snapshot from 2026-07-13; re-check `gh issue list` before trusting it.

session-handoff.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Session Handoff
2+
3+
<!--
4+
OVERWRITE, don't append: this file describes only the most recent session.
5+
Rewrite it at end of session; older handoffs live in git history, and completed
6+
work belongs in archive/YYYY-MM.md. If this file grows past ~80 lines, it is
7+
accumulating history instead of handing off.
8+
-->
9+
10+
## Current Objective
11+
12+
- Goal: Implement issues #40 (local ignore patterns), #41 (settings connection status badge), #42 (resize conflict modal)
13+
- Current status: Implemented, tested, linted, built clean; committed and pushed. PR not yet opened (pending user confirmation).
14+
- Branch / commit: `claude/settings-ux-improvements-260713` @ 28f4f8e
15+
16+
## Completed This Session
17+
18+
- [x] #42 - Resized conflict modal (`min(1100px,92vw) x min(85vh,800px)`, flex layout, removed 280px content cap, added Diff/Local/Remote tab switcher on narrow screens)
19+
- [x] #41 - Persistent connection status badge in settings tab (Checking/Connected/Not connected), 800ms debounce on token/branch/URL/owner/repo edits, updates in place (no focus-stealing re-render)
20+
- [x] #40 - "Ignore patterns" setting (multi-line, .gitignore-style), applied in `GitignoreManager.isIgnored()` additively alongside remote/local `.gitignore`
21+
- [x] Filed issue #48 (folder picker for root path / vault folder settings) at user's request mid-session
22+
- [x] Rebased local `main` onto `origin/main` to adopt the already-shipped Obsidian 1.12.x compat work (PR #46), discarding a stale duplicate local WIP, then re-applied the above three features cleanly on top
23+
24+
## Verification Evidence
25+
26+
| Check | Command | Result | Notes |
27+
|---|---|---|---|
28+
| Lint | `npx eslint .` | 0 errors | Repo-wide, no exceptions |
29+
| Type check + compat | `npm run build` | Pass | Includes `typecheck-compat.mjs` against Obsidian 1.11.0 |
30+
| Tests | `npx vitest run` | 254/254 passed | 17 test files |
31+
| Manual (in Obsidian) || Not done | No Obsidian instance available in this environment |
32+
33+
## Files Changed
34+
35+
- `src/settings.ts`, `src/ui/SyncConflictModal.ts`, `src/logic/gitignore-manager.ts`, `src/main.ts`, `styles.css`
36+
- `tests/setup.ts`, `tests/ui/setup-dom.ts` (expanded Obsidian test mocks)
37+
- `tests/logic/gitignore-manager.test.ts`, `tests/logic/sync-manager.test.ts`, `tests/logic/sync-manager-mapping.test.ts`
38+
- `tests/ui/SyncConflictModal.test.ts`, `tests/ui/SettingsConnectionStatus.test.ts` (new)
39+
40+
## Decisions Made
41+
42+
- Bundled #40/#41/#42 into a single commit rather than three, since they touch overlapping files closely enough that atomic per-issue commits risked an intermediate broken build.
43+
- Discarded the local uncommitted 1.12.x-compat WIP (user confirmed it was superseded by origin/main's PR #46) rather than trying to merge two different compat mechanisms.
44+
45+
## Blockers / Risks
46+
47+
- None blocking. Open item: PR for the pushed branch hasn't been created — user was asked "要接著幫你開 PR 嗎?" and the session moved to harness setup before they answered.
48+
49+
## Next Session Startup
50+
51+
1. Read `CLAUDE.md`.
52+
2. Read `feature_list.json` and `progress.md`.
53+
3. Review this handoff.
54+
4. Run `./init.sh` before editing.
55+
5. Check whether the user wants a PR opened for `claude/settings-ux-improvements-260713` before starting new work.
56+
57+
## Recommended Next Step
58+
59+
- Ask the user whether to open the PR for the pushed branch; if yes, use `gh pr create` per the repo's PR conventions (see `CLAUDE.md` / firstsun-pm workflow) and reference issues #40/#41/#42 so they auto-close on merge.

src/logic/gitignore-manager.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,21 @@ export class GitignoreManager {
1111

1212
private readonly rootPath: string;
1313
private readonly vaultFolder: string;
14-
14+
// User-defined local ignore patterns (settings.ignorePatterns), applied on top of
15+
// remote/local .gitignore rules. Matched against the same vault/rootPath-relative
16+
// path passed into isIgnored().
17+
private readonly localIgnore: Ignore | null;
18+
1519
// Maps directory path (empty string for root) to Ignore instance
1620
private readonly ignoreMap: Map<string, Ignore> = new Map();
1721

18-
constructor(app: App, gitService: GitServiceInterface, branch: string, rootPath: string, vaultFolder: string = '') {
22+
constructor(app: App, gitService: GitServiceInterface, branch: string, rootPath: string, vaultFolder: string = '', ignorePatterns: string = '') {
1923
this.app = app;
2024
this.gitService = gitService;
2125
this.branch = branch;
2226
this.rootPath = rootPath.replace(/^\/|\/$/g, '');
2327
this.vaultFolder = vaultFolder.replace(/^\/|\/$/g, '');
28+
this.localIgnore = ignorePatterns.trim() ? ignore().add(ignorePatterns) : null;
2429
}
2530

2631
private getNormalizedPath(path: string): string {
@@ -159,6 +164,8 @@ export class GitignoreManager {
159164
* Checks if a given file path should be ignored based on loaded .gitignore rules.
160165
*/
161166
isIgnored(filePath: string): boolean {
167+
if (this.localIgnore?.ignores(filePath)) return true;
168+
162169
const fullPath = this.rootPath ? `${this.rootPath}/${filePath}` : filePath;
163170

164171
for (const [dirPath, ig] of this.ignoreMap.entries()) {

src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default class GitLabFilesPush extends Plugin {
3939
});
4040

4141
this.initializeGitService();
42-
this.gitignoreManager = new GitignoreManager(this.app, this.gitService, this.settings.branch, this.settings.rootPath, this.settings.vaultFolder);
42+
this.gitignoreManager = new GitignoreManager(this.app, this.gitService, this.settings.branch, this.settings.rootPath, this.settings.vaultFolder, this.settings.ignorePatterns);
4343
this.sync = new SyncManager(this.app, this.gitService, this.settings, this.saveSettings.bind(this));
4444

4545
this.pushRibbonEl = this.addRibbonIcon('upload-cloud', this.pushRibbonLabel(), async () => {

0 commit comments

Comments
 (0)