Skip to content

Commit c9099c9

Browse files
pajomaclaude
andcommitted
docs: update architecture for package-by-feature (#234)
AGENTS.md Architecture section + docs/PLAN.md now describe the src/{app,shared,features} layout, the Container DI composition root, named imports (J barrel gone), the split Configuration facade, and the shared/events bus. PLAN.md 2.1/2.3 marked complete, 2.6 partial, new 2.7 tracks restructure phases (1-4 done via #235-#238, Phase 5 = #239). Refs #234. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent def4c2a commit c9099c9

2 files changed

Lines changed: 64 additions & 43 deletions

File tree

AGENTS.md

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -40,35 +40,42 @@ CI (`.github/workflows/ci.yml`) runs lint → compile → compile-tests → `xvf
4040

4141
## Architecture
4242

43-
Entry: `src/extension.ts``Startup(config).run(context)` (in `src/vscode/startup.ts`) which initializes the `Ctrl` service locator and registers commands, code actions, and optional syntax highlighting.
43+
The source is organized **package-by-feature** (restructured in #234). Three top-level zones:
4444

45-
**Service locator pattern.** `Ctrl` (`src/util/controller.ts`) is two-phase: (1) constructor creates `Configuration` only; (2) `initServices(logger: ILogger)` creates all services (`Inject`, `Writer`, `Parser`, `Dialogues`, `Reader`) in dependency order. `Startup.registerLoggingChannel` triggers phase 2 by constructing `ConsoleLogger` then calling `initServices`. Every command/provider still receives `Ctrl` and accesses services via getters — narrowing that layer is Phase 2.3. Action/UI classes now accept narrow sub-interface params; see `src/model/interfaces.ts` for `IConfiguration`, `ILogger`, `IParser`, `IWriter`, `IInject`, `IDialogues`.
45+
```
46+
src/app/ composition root — wiring only
47+
src/shared/ kernel — no feature dependencies
48+
src/features/ one folder per capability
49+
```
50+
51+
Entry: `src/extension.ts``Startup(config).run(context)` (in `src/app/startup.ts`) which builds the `Container` composition root and registers commands, code actions, and optional syntax highlighting.
52+
53+
**Dependency injection.** `Container` (`src/app/container.ts`) implements the `JournalController` interface and constructs the full service graph in **one pass**: its constructor takes `(configSource, loggerFactory)` and builds `Configuration`, `Inject`, `Parser`, `Dialogues`, `Writer`, `Reader`, `JournalEvents` in dependency order (no two-phase `initServices`, no `!` fields). Commands/providers receive the `JournalController` interface (never the concrete `Container`); only `src/app/` instantiates it. Service interfaces live in `src/shared/model/interfaces.ts` (`IConfiguration`, `ILogger`, `IParser`, `IWriter`, `IReader`, `IInject`, `IDialogues`, `IFileSystem`, `IJournalEvents`). `src/app/register.ts` does the command/provider registration.
4654

47-
**Namespace barrel imports.** `src/index.ts` re-exports submodules as `J.VSCode`, `J.Journal`, `J.Model`, `J.Util`, `J.Commands`, `J.UI`, `J.Features`. Existing code does `import * as J from '..'` and references `J.Util.Ctrl`, `J.Journal.Writer`, etc. `docs/PLAN.md` Phase 2.3 marks this for replacement with named imports — prefer named imports in new files.
55+
**Named imports only.** The old `J.*` namespace barrel was removed in #234`src/index.ts` is empty (`export {}`). Import named symbols directly from submodule barrels / files.
4856

4957
**Module responsibilities** (need multiple files to grasp):
5058

51-
- `src/vscode/` — VS Code surface integration. `Configuration` (`conf.ts`) reads `journal.*` settings and resolves templates/scopes. `Dialogues` drives QuickPick/InputBox. `Startup` wires everything. i18n uses `vscode.l10n` — manifest strings in `package.nls.json` (English-only); runtime strings in `l10n/bundle.l10n.json`. Per-locale `package.nls.<loc>.json` and `l10n/bundle.l10n.<loc>.json` were removed in 1.1.0 (audience is English-speaking; vscode falls back to the default bundle for any locale).
52-
- `src/journal/` — Core domain logic, no direct command bindings.
53-
- `Parser` — turns user input/URIs into structured `Input` (date, note, memo, task, weekly).
54-
- `Reader` — loads entries/notes from the configured base directory using `vscode.workspace.fs`.
55-
- `Writer` — creates new files (entry, note, weekly) and opens text documents.
56-
- `Inject` — modifies existing documents (insert memo/task/file link, shift task).
57-
- `MatchInput` (smart-input resolver) — moved here from the old `src/provider/features/`.
58-
- `paths.ts` — date-from-URI path utilities (moved here from `src/util/`).
59-
- `template-engine.ts``resolveDate(template, date, locale?)` and `toMomentFormat(template)`. Single `TEMPLATE_VARIABLE_MAP` registry; use these instead of anything from `dates.ts`. Custom `${d:fmt}` capture group includes the `d:` prefix — strip with `.slice(2)` to get the format string.
60-
- `src/model/` — Plain data types: `Input`, `FileEntry`, `HeaderTemplate`/`InlineTemplate`/`ScopedTemplate`, scope/quickpick types.
61-
- `src/commands/` — one file per registered command (`journal.today`, `journal.note`, `journal.printDuration`, etc.); each exports a static `create(ctrl)` that returns the `Disposable`.
62-
- `src/ui/` — VS Code UI providers.
63-
- `codeactions/` — markdown code actions for completed and open task lines.
64-
- `codelens/` — task migration/shift CodeLens providers (not all registered yet — see `Startup.registerCodeLens`).
65-
- `src/features/` — reusable cross-cutting building blocks.
66-
- `entries/``ScanEntries` (directory walker + cache for QuickPick).
67-
- `sync/``SyncNoteLinks`, `SyncDailyLinks`.
68-
- `LoadNotes` and other higher-level feature helpers.
69-
- `src/util/``Ctrl`, `Logger` (OutputChannel-backed), `dates.ts` (ISO week + locale helpers only; template replacement functions removed in #211), `strings.ts`. Note: `paths.ts` moved to `src/journal/paths.ts`.
70-
71-
**Smart-input flow.** User triggers `journal.day` (`Ctrl+Shift+J`) → `Dialogues` shows InputBox → `MatchInput.parseInput()` classifies the text (date expression, weekday, "memo:", "task:", "note ...", week reference) → command dispatches to `Reader`/`Writer`/`Inject`. The default path/file patterns (`${base}/${year}/${month}/${day}` for notes, `${base}/${year}/${month}/${day}.${ext}` for entries) come from `journal.patterns` in `package.json`.
59+
- `src/shared/` — the kernel, no feature dependencies:
60+
- `config/``Configuration` (`configuration.ts`) is a thin `IConfiguration` facade composing `SettingsReader` (scalar settings, base paths, scopes), `PathResolver` (entry/note/weekly path & file patterns), `TemplateProvider` (header/inline/time templates). `patterns.ts` holds the pattern types + defaults.
61+
- `model/` — plain data types: `Input`, `FileEntry`, `HeaderTemplate`/`InlineTemplate`/`ScopedTemplate`, scope/quickpick types, and all service interfaces (`interfaces.ts`).
62+
- `fs/``IFileSystem` impl `VscodeFileSystem` + `fileExists`.
63+
- `logging/``Logger`/`ConsoleLogger` (OutputChannel-backed).
64+
- `dates/`, `strings/`, `lang.ts` — date (ISO week/locale), string, and primitive helpers.
65+
- `templates/template-engine.ts``resolveDate(template, date, locale?)` and `toMomentFormat(template)`. Single `TEMPLATE_VARIABLE_MAP` registry. Custom `${d:fmt}` capture group includes the `d:` prefix — strip with `.slice(2)`.
66+
- `paths.ts` — date-from-URI path utilities (`getDateFromURIAndConfig`, `getWeekFromURIAndConfig`, `resolvePath`, `inferType`).
67+
- `events/``JournalEvents`, a typed `vscode.EventEmitter` bus for cross-feature signals (e.g. `entryOpened`).
68+
- `src/features/<feature>/` — each owns its `commands/` (one file per registered command, each exports a static `create(ctrl)` returning a `Disposable`), domain logic, and `ui/` providers:
69+
- `entries/` — entry/weekly `Reader`/`Writer`/`Inject`, `ScanEntries` (QuickPick walker+cache), the `show-entry-for-*` commands, and the shared `AbstractLoadEntryForDateCommand`.
70+
- `notes/``show-note` command, `LoadNotes`, `SyncNoteLinks`.
71+
- `weekly/``WeeklyEntryWatcher`, `SyncDailyLinks` (subscribes to `entryOpened`).
72+
- `tasks/` — task code actions + migrate/shift CodeLens, `copy-task`.
73+
- `navigation/` — prev/next entry commands + `navigation.ts`.
74+
- `smart-input/``MatchInput`, `Parser`, `Dialogues` (QuickPick/InputBox).
75+
- `tools/``print-time` / `print-duration` / `print-sum` / open-workspace.
76+
- A feature must not import another feature's internals — cross-feature signals go through `shared/events/`. (Three legacy edges remain pending #239.)
77+
78+
**Smart-input flow.** User triggers `journal.day` (`Ctrl+Shift+J`) → `Dialogues` shows InputBox → `MatchInput.parseInput()` classifies the text (date expression, weekday, "memo:", "task:", "note ...", week reference) → command dispatches via the `JournalController` to `Reader`/`Writer`/`Inject`. The default path/file patterns (`${base}/${year}/${month}/${day}` for notes, `${base}/${year}/${month}/${day}.${ext}` for entries) come from `journal.patterns` in `package.json`.
7279

7380
**Filesystem.** Always go through `vscode.workspace.fs` (the extension declares `extensionKind: ["workspace"]` so it runs on the remote host for Remote SSH/Codespaces). Avoid raw `fs` / `fs.promises` in new code — `docs/PLAN.md` Phase 1.3 finished migrating the old `fs` call sites; do not reintroduce them.
7481

@@ -96,9 +103,9 @@ Entry: `src/extension.ts` → `Startup(config).run(context)` (in `src/vscode/sta
96103

97104
## Reusable building blocks
98105

99-
- `J.Util.fileExists(uri)` (`src/util/fs-exists.ts`) — stat-first existence check; converts `FileSystemError.FileNotFound` to `false`, re-throws others. Use instead of "open and catch the rejection".
100-
- `AbstractLoadEntryForDateCommand` (`src/commands/show-entry-for-date.ts`) — new "open a specific date" commands should extend it and call `this.execute(input)` with `input.offset` set. Reuses the local-vs-remote prompt and `loadPageForInput` plumbing.
101-
- `getDateFromURIAndConfig` (`src/journal/paths.ts`) — parses a `Date` from a journal entry file path. Anchor detection for navigation features.
106+
- `fileExists(fs, uri)` (`src/shared/fs/fs-exists.ts`) — stat-first existence check; converts `FileSystemError.FileNotFound` to `false`, re-throws others. Use instead of "open and catch the rejection".
107+
- `AbstractLoadEntryForDateCommand` (`src/features/entries/commands/show-entry-for-date.ts`) — new "open a specific date" commands should extend it and call `this.execute(input)` with `input.offset` set. Reuses the local-vs-remote prompt and `loadPageForInput` plumbing.
108+
- `getDateFromURIAndConfig` (`src/shared/paths.ts`) — parses a `Date` from a journal entry file path. Anchor detection for navigation features.
102109
- `vscode.Uri.joinPath` for composing FS URIs. `vscode.workspace.fs.readDirectory` returns `[name, FileType][]`.
103110

104111
## i18n

docs/PLAN.md

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,11 @@ Make sure that, when running on a remote host, the extension can still access th
7070
- [x] Update barrel `src/index.ts`: `J.Extension``J.VSCode`, `J.Actions``J.Journal`, `J.Provider``J.Commands` / `J.UI` / `J.Features`
7171
- [x] Update all import paths and barrel-key consumer sites throughout the codebase
7272

73-
### 2.1 — Replace Service Locator with Dependency Injection
74-
- [ ] Define interfaces for each service: `IConfiguration`, `IDialogues`, `IParser`, `IReader`, `IWriter`, `IInject`, `ILogger`
75-
- [ ] Remove `Ctrl` and use constructor injection
76-
- [ ] Each class receives only the interfaces it depends on (not the full `Ctrl`)
77-
- [ ] Rewrite all tests to use dependency injection, make sure you achieve high test coverage
78-
- [ ] This enables proper unit testing with mocks
73+
### 2.1 — Replace Service Locator with Dependency Injection ✓ COMPLETE (#234 Phase 2, #236)
74+
- [x] Define interfaces for each service: `IConfiguration`, `IDialogues`, `IParser`, `IReader`, `IWriter`, `IInject`, `ILogger` (`src/shared/model/interfaces.ts`)
75+
- [x] Remove `Ctrl` service-locator; introduce `src/app/Container` composition root (single-pass ctor + logger factory, no two-phase `initServices`/`!`)
76+
- [x] Consumers depend on the `JournalController` interface (not the concrete controller). Per-command minimal interfaces deferred — facade interface chosen to limit churn (maintainer decision on #234)
77+
- [x] Tests construct `new Container(cfg, () => logger)`; mocks satisfy `JournalController`
7978

8079
### 2.2 — Eliminate `new Promise()` Anti-Pattern
8180
- [ ] Refactor ~25 methods that wrap their body in `new Promise()` to use native `async/await`
@@ -91,10 +90,10 @@ Make sure that, when running on a remote host, the extension can still access th
9190
- [ ] Validate that async/await is used throughout complete codebase
9291
- [ ] Validate error propagation through unit tests
9392

94-
### 2.3 — Replace `import * as J from '..'` Pattern
95-
- [ ] Replace namespace-style imports with explicit named imports
96-
- [ ] This improves tree-shaking, readability, and IDE support
97-
- [ ] Example: `import { Ctrl } from '../util/controller'` instead of `J.Util.Ctrl`
93+
### 2.3 — Replace `import * as J from '..'` Pattern ✓ COMPLETE (#234 Phase 1, #235)
94+
- [x] Replaced namespace-style imports with explicit named imports across all files
95+
- [x] Root `src/index.ts` barrel emptied (`export {}`) — broke the root import cycle
96+
- [x] Improves tree-shaking, readability, and IDE support
9897

9998
### 2.4 — Modernize Activation
10099
- [x] Remove explicit `activationEvents` from `package.json` (VS Code 1.74+ supports implicit activation from `contributes.commands`)
@@ -106,12 +105,27 @@ Make sure that, when running on a remote host, the extension can still access th
106105
- [ ] Use `contributes.grammars` (already partially in place) and optional `contributes.themes` for color customization
107106
- [ ] Provide a dedicated color theme as an optional install instead of injecting TextMate rules
108107

109-
### 2.6 — Decouple Configuration
110-
- [ ] Extract configuration reading into a dedicated service with caching and change detection
111-
- [ ] Use `vscode.workspace.onDidChangeConfiguration` to invalidate cache
112-
- [ ] Remove deprecated `getInlineTemplateCached()` method
113-
- [ ] Remove legacy `tpl-*` setting support (they've been deprecated since pre-1.0)
114-
- [ ] Type the return of `getWeekFilePattern()` and `getWeekPathPattern()` (currently `any`)
108+
### 2.6 — Decouple Configuration ◐ PARTIAL (#234 Phase 3, #237)
109+
- [x] Split the 700-line `Configuration` god-class into `SettingsReader` / `PathResolver` / `TemplateProvider` (`src/shared/config/`); `conf.ts`~105-line `IConfiguration` facade
110+
- [x] Removed `getInlineTemplateCached()` (dropped with the old `TemplateService`)
111+
- [x] Typed the return of `getWeekFilePattern()` / `getWeekPathPattern()` (`Promise<ScopedTemplate>`)
112+
- [ ] Add caching + `vscode.workspace.onDidChangeConfiguration` invalidation (still reads live each call)
113+
- [ ] Remove legacy `tpl-*` setting support (deprecated since pre-1.0)
114+
115+
### 2.7 — Package-by-feature restructure (#234) ◐ Phases 1–4 done, Phase 5 open
116+
Spec: [docs/specs/2026-06-02-234-package-by-feature.md](specs/2026-06-02-234-package-by-feature.md) · Plan: [docs/plans/2026-06-02-234-package-by-feature.md](plans/2026-06-02-234-package-by-feature.md)
117+
118+
Source moved from package-by-layer to **package-by-feature**:
119+
```
120+
src/shared/ kernel — model, config, fs, logging, dates, strings, templates, paths, events, lang
121+
src/features/ entries, notes, weekly, tasks, navigation, smart-input, tools
122+
src/app/ composition root (Container / register / startup)
123+
```
124+
- [x] Phase 1 (#235): kill `J` barrel → named imports
125+
- [x] Phase 2 (#236): `Ctrl``app/Container` DI
126+
- [x] Phase 3 (#237): split `Configuration`
127+
- [x] Phase 4 (#238): feature folders + `shared/events/` (`entryOpened` replaces inline weekly sync)
128+
- [ ] Phase 5 (#239): vscode-free `domain/` via `shared/editor/` adapter; remove the 3 remaining cross-feature edges (`entries→notes`, `navigation→entries`, `smart-input→entries`)
115129

116130
---
117131

0 commit comments

Comments
 (0)