@stylusnexus/work-plan 2026.6.10 → 2026.6.11
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/README.md +13 -7
- package/VERSION +1 -1
- package/package.json +1 -1
- package/skills/work-plan/SKILL.md +6 -4
- package/skills/work-plan/commands/canonicalize.py +7 -92
- package/skills/work-plan/commands/handoff.py +15 -6
- package/skills/work-plan/commands/init.py +13 -3
- package/skills/work-plan/commands/init_repo.py +8 -2
- package/skills/work-plan/commands/new_track.py +7 -0
- package/skills/work-plan/commands/notes_vcs.py +172 -0
- package/skills/work-plan/commands/refresh_md.py +106 -37
- package/skills/work-plan/commands/rename_track.py +243 -0
- package/skills/work-plan/commands/set_notes_root.py +8 -4
- package/skills/work-plan/commands/suggest_priorities.py +12 -2
- package/skills/work-plan/lib/config.py +11 -0
- package/skills/work-plan/lib/frontmatter.py +12 -3
- package/skills/work-plan/lib/git_state.py +61 -52
- package/skills/work-plan/lib/github_state.py +46 -13
- package/skills/work-plan/lib/notes_vcs.py +276 -0
- package/skills/work-plan/lib/prompts.py +12 -1
- package/skills/work-plan/lib/status_table.py +95 -5
- package/skills/work-plan/lib/tracks.py +9 -4
- package/skills/work-plan/tests/fixtures/notes_root/{critforge → myproject}/archive/shipped/old.md +1 -1
- package/skills/work-plan/tests/fixtures/notes_root/{critforge → myproject}/example.md +1 -1
- package/skills/work-plan/tests/fixtures/track_with_frontmatter.md +1 -1
- package/skills/work-plan/tests/test_config.py +12 -12
- package/skills/work-plan/tests/test_github_state.py +3 -3
- package/skills/work-plan/tests/test_init_repo.py +12 -7
- package/skills/work-plan/tests/test_new_track.py +7 -7
- package/skills/work-plan/tests/test_notes_vcs.py +426 -0
- package/skills/work-plan/tests/test_notes_vcs_command.py +312 -0
- package/skills/work-plan/tests/test_plan_status_issues.py +2 -2
- package/skills/work-plan/tests/test_refresh_md.py +159 -61
- package/skills/work-plan/tests/test_rename_track.py +351 -0
- package/skills/work-plan/tests/test_repo_filter.py +6 -6
- package/skills/work-plan/tests/test_security_cli_hardening.py +142 -0
- package/skills/work-plan/tests/test_set_notes_root.py +6 -2
- package/skills/work-plan/tests/test_status_table.py +61 -0
- package/skills/work-plan/tests/test_track_resolution.py +2 -2
- package/skills/work-plan/tests/test_tracks.py +4 -4
- package/skills/work-plan/work_plan.py +97 -17
- /package/skills/work-plan/tests/fixtures/notes_root/{critforge → myproject}/no_frontmatter.md +0 -0
|
@@ -53,7 +53,9 @@ SUBCOMMANDS = {
|
|
|
53
53
|
"export": "commands.export",
|
|
54
54
|
"set": "commands.set_field",
|
|
55
55
|
"new-track": "commands.new_track",
|
|
56
|
+
"rename-track": "commands.rename_track",
|
|
56
57
|
"set-notes-root": "commands.set_notes_root",
|
|
58
|
+
"notes-vcs": "commands.notes_vcs",
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
DESCRIPTIONS = [
|
|
@@ -61,9 +63,9 @@ DESCRIPTIONS = [
|
|
|
61
63
|
("brief", "[--repo=<key>]",
|
|
62
64
|
"Multi-track snapshot with time-aware framing. --repo scopes the brief (and the archived-reopen callouts) to one configured repo.",
|
|
63
65
|
"Starting a work session, after a gap, or any time you want a status snapshot. Use --repo when you only want to think about one project today.",
|
|
64
|
-
"/work-plan brief --repo=
|
|
66
|
+
"/work-plan brief --repo=myproject"),
|
|
65
67
|
("handoff", "[track] [--set-next 1,2,3 | --auto-next] [--interactive]",
|
|
66
|
-
"Wrap up a session: capture touched/next/blockers, update body status table. Use --set-next to set the next_up list explicitly. Use --auto-next to suggest a priority-sorted list from open issues (interactive: apply / edit / skip).",
|
|
68
|
+
"Wrap up a session: capture touched/next/blockers, update body status table. Use --set-next to set the next_up list explicitly — note this is a full handoff, so it also appends a session-log entry (use `set next_up=` for a field-only change with no log). Use --auto-next to suggest a priority-sorted list from open issues (interactive: apply / edit / skip).",
|
|
67
69
|
"Ending a work block — before stepping away, going to bed, or switching tracks. Use --auto-next when you don't want to hand-pick issue numbers.",
|
|
68
70
|
"/work-plan handoff tabletop --auto-next"),
|
|
69
71
|
("where-was-i", "[track | track@repo] [--pick] [--repo=<key>]",
|
|
@@ -87,9 +89,9 @@ DESCRIPTIONS = [
|
|
|
87
89
|
"When a track is done, paused, or won't ship — frees mental space.",
|
|
88
90
|
"/work-plan close tabletop"),
|
|
89
91
|
("refresh-md", "<track> | --all | --repo=<key> [--yes]",
|
|
90
|
-
"
|
|
92
|
+
"Sync issue STATE (open/closed, status labels) from GitHub into the track body's status table. Does not change track membership. For a canonical table it re-derives the whole block from live data, milestone-ordered, so the table self-heals and stays grouped; narrative tables are updated in place.",
|
|
91
93
|
"Usually NOT needed directly: `handoff` already refreshes the body table for its own track, and `brief` reads GitHub live. Reach for this when a sibling track has drifted because you haven't `handoff`'d it lately. `--all` sweeps every active track; `--repo=<key>` scopes the sweep to one repo (also runs as part of weekly `hygiene`).",
|
|
92
|
-
"/work-plan refresh-md --repo=
|
|
94
|
+
"/work-plan refresh-md --repo=myproject"),
|
|
93
95
|
("list", "[--all] [--sort=recent|priority]",
|
|
94
96
|
"List active tracks (or all including parked/archived).",
|
|
95
97
|
"Quick scan of what tracks exist; --all to see archived. --sort orders by last_touched recency or launch_priority.",
|
|
@@ -113,11 +115,11 @@ DESCRIPTIONS = [
|
|
|
113
115
|
("auto-triage", "[--repo=<key>] [--apply] [--limit=N]",
|
|
114
116
|
"AI-assign untracked open issues to existing tracks. Step 1 (no --apply): fetches untracked issues + existing tracks, prints AI prompt. Step 2 (--apply): reads AI's JSON answers and slots each assignment into track frontmatter. Complements `group` (which creates new tracks); `auto-triage` assigns to tracks that already exist. --limit controls how many untracked issues are shown (default 100).",
|
|
115
117
|
"Periodically — when new issues have piled up outside the track model. Run /work-plan coverage first to confirm there's a gap worth triaging.",
|
|
116
|
-
"/work-plan auto-triage --repo=
|
|
118
|
+
"/work-plan auto-triage --repo=myproject"),
|
|
117
119
|
("reconcile", "<track> | --all | --repo=<key> [--draft] [--yes]",
|
|
118
|
-
"Update track MEMBERSHIP (the `github.issues` list in frontmatter) by syncing it against a GitHub label. Default label is `track/<slug>`; override per-track via `github.labels: [...]` in frontmatter. Read-only on GitHub. In an --all/--repo sweep it also detects MOVEs — an issue relabeled from one track to another in the same repo is moved (removed from the old track, added to the new). Add --draft to preview proposed ADDs/MOVEs/FLAGs without prompting or writing; add --yes to apply without prompting (non-interactive, e.g. from the VS Code extension; PUBLIC-repo move destinations are skipped under --yes). NOT for hand-curated tracks — see `refresh-md` if you only want to update issue state.",
|
|
120
|
+
"Update track MEMBERSHIP (the `github.issues` list in frontmatter) by syncing it against a GitHub label. Default label is `track/<slug>`; override per-track via `github.labels: [...]` in frontmatter. Read-only on GitHub. In an --all/--repo sweep it also detects MOVEs — an issue relabeled from one track to another in the same repo is moved (removed from the old track, added to the new). Add --draft to preview the label drift (proposed ADDs/MOVEs/FLAGs) without prompting or writing; add --yes to apply without prompting (non-interactive, e.g. from the VS Code extension; PUBLIC-repo move destinations are skipped under --yes). NOT for hand-curated tracks — see `refresh-md` if you only want to update issue state.",
|
|
119
121
|
"WEEKLY hygiene on label-driven tracks — pulls labeled issues into their tracks, flags un-labeled ones. Use --repo=<key> to scope the sweep to one repo. Skip on hand-curated tracks (it'll propose dropping curated issues every run).",
|
|
120
|
-
"/work-plan reconcile --repo=
|
|
122
|
+
"/work-plan reconcile --repo=myproject --draft"),
|
|
121
123
|
("duplicates", "[--min-similarity=0.7] [--limit=20] [--state=open] [--timeout=N]",
|
|
122
124
|
"Find likely-duplicate issues by title similarity.",
|
|
123
125
|
"WEEKLY hygiene, or before a milestone planning session — find consolidation candidates.",
|
|
@@ -125,35 +127,43 @@ DESCRIPTIONS = [
|
|
|
125
127
|
("coverage", "[--repo=<key>] [--list] [--limit=N]",
|
|
126
128
|
"Report how many open issues are not referenced by any track (per repo). --list prints issue titles (default: show 20; override with --limit=N). Read-only; derives live from gh.",
|
|
127
129
|
"On-demand: measure how much of a repo's backlog has fallen outside the planning layer. Pairs with /work-plan group to bulk-cluster the orphans.",
|
|
128
|
-
"/work-plan coverage --repo=
|
|
130
|
+
"/work-plan coverage --repo=myproject --list"),
|
|
129
131
|
("canonicalize", "<track | track@repo> | --all [--force] [--repo=<key>]",
|
|
130
|
-
"Insert a canonical master issue table at the top of a track. Refresh-md then targets ONLY this table, leaving narrative tables alone. Use --repo=<key> or track@repo to disambiguate; with --all, --repo=<key> scopes to one repo.",
|
|
132
|
+
"Insert a canonical master issue table at the top of a track. The table has a Milestone column and is ordered active-milestone-first (the track's milestone_alignment milestone, then other milestones grouped with a blank divider row, then no-milestone last) so near-term work sits above someday work (#101). Refresh-md then targets ONLY this table, re-deriving it (so the order self-heals) and leaving narrative tables alone. Use --repo=<key> or track@repo to disambiguate; with --all, --repo=<key> scopes to one repo.",
|
|
131
133
|
"ONE-TIME for hand-written tracks with multiple narrative tables, OR after restructuring a track.",
|
|
132
134
|
"/work-plan canonicalize ux-redesign"),
|
|
133
135
|
("hygiene", "[--yes] [--no-duplicates] [--repo=<key>] [--timeout=N]",
|
|
134
136
|
"Weekly cleanup wrapper: refresh-md + reconcile + duplicates. With --repo=<key>, steps 1 and 2 scope to that repo; the duplicates step (a global similarity scan) is skipped. --timeout=N sets the gh subprocess timeout for the duplicates step (default 30s).",
|
|
135
137
|
"WEEKLY — runs all three hygiene commands in sequence so you don't have to remember each. Use --repo=<key> to clean up one project without touching the others.",
|
|
136
|
-
"/work-plan hygiene --repo=
|
|
138
|
+
"/work-plan hygiene --repo=myproject"),
|
|
137
139
|
("export", "--json",
|
|
138
140
|
"Emit the viewer-ready JSON read surface (schema 1): every frontmatter'd track with repo, tier, status, visibility, blockers, next_up, an open/closed rollup, and per-issue state/assignee/milestone. Read-only; derives live from gh. Consumed by the VS Code extension.",
|
|
139
141
|
"When a tool (the VS Code viewer, or any script) needs structured track state instead of the human-facing brief/orient text.",
|
|
140
142
|
"/work-plan export --json"),
|
|
141
143
|
("set", "<track | track@repo> field=value [field=value …] [--repo=<key>] [--confirm=<token>]",
|
|
142
|
-
"Guarded edit of a track's frontmatter fields (status, launch_priority, milestone_alignment, blockers, next_up). Validates field names + status values; blockers/next_up take comma-separated issue numbers. Writes into a PUBLIC repo only with a confirm token: without one it prints {needs_confirm, reason, token} and makes no change (the VS Code viewer surfaces that as a modal, then re-invokes with --confirm=<token>).",
|
|
144
|
+
"Guarded edit of a track's frontmatter fields (status, launch_priority, milestone_alignment, blockers, next_up). Validates field names + status values; blockers/next_up take comma-separated issue numbers. Setting `next_up` here writes ONLY the frontmatter field — for next_up plus a session-log entry (and a body refresh), use `handoff --set-next` instead. Writes into a PUBLIC repo only with a confirm token: without one it prints {needs_confirm, reason, token} and makes no change (the VS Code viewer surfaces that as a modal, then re-invokes with --confirm=<token>).",
|
|
143
145
|
"Programmatic/GUI edits that have no dedicated verb — e.g. the VS Code extension changing a status or blockers list. On the terminal you'll usually use the named verbs instead.",
|
|
144
146
|
"/work-plan set ux-redesign status=parked"),
|
|
145
147
|
("new-track", "<repo> <slug> [--priority=P0..P3] [--milestone=<m>] [--private] [--confirm=<token>]",
|
|
146
|
-
"Create a brand-new track file under notes_root in one headless call. <repo> is either a configured key (e.g. '
|
|
148
|
+
"Create a brand-new track file under notes_root in one headless call. <repo> is either a configured key (e.g. 'myproject') or a bare org/repo slug (e.g. 'your-org/myproject'). Writes frontmatter with status=active and optional priority/milestone. Gates on public repos — prints {needs_confirm, token} and exits cleanly; re-run with --confirm=<token> to proceed.",
|
|
147
149
|
"When a new feature branch or initiative starts and you want the track file created immediately — especially from a non-terminal caller like the VS Code extension that can't interactively run init.",
|
|
148
150
|
"/work-plan new-track stylusnexus/work-plan-toolkit my-feature"),
|
|
151
|
+
("rename-track", "<old-slug | old@repo> <new-slug> [--repo=<key>] [--fix-refs] [--commit] [--confirm=<token>]",
|
|
152
|
+
"Rename an active track's slug: moves the .md file, updates the frontmatter `track` field + last_touched. Resolve <old-slug> with track@repo or --repo when ambiguous. Validates <new-slug> like new-track and rejects a name already taken in the same repo/tier. For shared tracks, --commit stages + commits the move (else prints a 'commit to share' hint). --fix-refs rewrites sibling tracks' depends_on that reference the old slug (otherwise they're just warned about). Gates on public repos — prints {needs_confirm, token} and exits cleanly; re-run with --confirm=<token>.",
|
|
153
|
+
"When a project pivots, a track name turns out misleading, or a slug needs norming — instead of hand-editing the file + frontmatter. Archived tracks are immutable (not renamable).",
|
|
154
|
+
"/work-plan rename-track old-project-name new-project-name"),
|
|
149
155
|
("plan-status", "[--repo=<key>] [--json] [--stamp [--draft]] [--llm [--apply]] [--archive | --issues] [--draft] [--since-days=N] [--type=plan|spec]",
|
|
150
156
|
"Reach a verdict on every plan/spec doc in a repo by correlating each plan's declared file-manifest (Create/Modify/Test paths) against the filesystem + git — not the unreliable checkboxes. Read-only: reports ✅ shipped / 🟡 partial / 💀 dead / 👻 manifest-less. --json for machine output. Add --stamp to write each verdict into its doc as an idempotent status header (--draft previews without writing). Add --llm for a two-step AI pass that judges prose/ambiguous docs (writes a prompt; you save JSON to the cache; re-run with --llm --apply). --archive moves dead plans to archive/abandoned/ (gated); --issues opens a GitHub issue per partial plan listing its unsatisfied files (gated). Both honor --draft.",
|
|
151
157
|
"When you point at a repo and need to know what's actually done vs. half-done vs. dead among accumulated plans. Run from inside the repo, or use --repo=<key> for a configured one.",
|
|
152
|
-
"/work-plan plan-status --repo=
|
|
158
|
+
"/work-plan plan-status --repo=myproject"),
|
|
153
159
|
("set-notes-root", "<path>",
|
|
154
160
|
"Update notes_root in ~/.claude/work-plan/config.yml to an absolute path. Creates the target directory if absent. Prints a WARN if existing frontmatter'd tracks live at the old location (they won't be moved — manual migration required). Non-interactive: safe to call from a GUI or script.",
|
|
155
161
|
"VS Code viewer cold-start: user has picked a folder for their private track notes and the extension invokes this to persist the choice. Also useful on the CLI to relocate notes without hand-editing config.yml.",
|
|
156
162
|
"/work-plan set-notes-root ~/Documents/work-plan-notes"),
|
|
163
|
+
("notes-vcs", "<init|enable|disable|status|undo> [<sha>] [--no-enable] [--json]",
|
|
164
|
+
"Opt-in LOCAL version control for the private notes_root tier — history/undo for tracks you keep on your machine, never pushed. `init` git-inits notes_root as a personal repo (initial commit of existing tracks) and turns on auto-commit; with --no-enable it inits without enabling. For safety it REFUSES a notes_root that already has a git remote or is a repo work-plan didn't create, and only ever commits the files a command changed — private notes stay un-pushable and your unrelated edits are never swept in. `enable`/`disable` toggle auto-commit (history is kept either way). `status` reports whether notes_root is a repo, whether auto-commit is on, and the last commit (add --json for the machine-readable shape the VS Code viewer polls). `undo [<sha>]` reverts a commit (default HEAD) — the last edit, by default. When auto-commit is on, every track-mutating command (slot/group/handoff/close/set/…) writes an undoable commit; the shared tier is unaffected (it's versioned by its own repo).",
|
|
165
|
+
"ONE-TIME setup when you want a git safety net for private tracks — so a bulk slot or a bad edit is reversible by default instead of needing a manual /tmp backup. `undo` reverses the last edit.",
|
|
166
|
+
"/work-plan notes-vcs init"),
|
|
157
167
|
]
|
|
158
168
|
|
|
159
169
|
|
|
@@ -185,14 +195,14 @@ def _print_help() -> int:
|
|
|
185
195
|
print(" All-in-one (recommended) → /work-plan --hygiene")
|
|
186
196
|
print(" Scope to one repo → /work-plan hygiene --repo=<key>")
|
|
187
197
|
print(" Or individually:")
|
|
188
|
-
print("
|
|
189
|
-
print("
|
|
198
|
+
print(" Sync issue states → /work-plan refresh-md --all (or --repo=<key>)")
|
|
199
|
+
print(" Check label drift → /work-plan reconcile --all (or --repo=<key>)")
|
|
190
200
|
print(" Find duplicate issues → /work-plan duplicates")
|
|
191
201
|
print()
|
|
192
202
|
print("FOCUS ON ONE PROJECT\n")
|
|
193
203
|
print(" Daily snapshot, one repo → /work-plan brief --repo=<key>")
|
|
194
204
|
print(" Weekly cleanup, one repo → /work-plan hygiene --repo=<key>")
|
|
195
|
-
print(" (<key> is the folder name under notes_root, e.g. '
|
|
205
|
+
print(" (<key> is the folder name under notes_root, e.g. 'myproject'.)")
|
|
196
206
|
print()
|
|
197
207
|
print("ONE-TIME SETUP\n")
|
|
198
208
|
print(" Bulk-cluster milestone → /work-plan group --milestone='v1.0.0 — Public Launch'")
|
|
@@ -223,7 +233,77 @@ def main(argv: list[str]) -> int:
|
|
|
223
233
|
except ImportError as e:
|
|
224
234
|
print(f"subcommand '{sub}' not implemented yet ({e})", file=sys.stderr)
|
|
225
235
|
return 1
|
|
226
|
-
|
|
236
|
+
# Snapshot notes_root's dirty set BEFORE the command so we can commit only
|
|
237
|
+
# what this command changes (and never fold in pre-existing edits, #244-vcs).
|
|
238
|
+
pre = _notes_precommit_state(sub)
|
|
239
|
+
rc = module.run(argv[2:])
|
|
240
|
+
if rc == 0 and pre is not None:
|
|
241
|
+
_commit_changed_notes(pre, argv[1:])
|
|
242
|
+
return rc
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# Read-only commands never write notes_root — skip the snapshot/commit entirely.
|
|
246
|
+
# (Flag aliases like --brief/--plan-status normalise by stripping leading dashes.)
|
|
247
|
+
_READONLY_SUBCOMMANDS = frozenset({
|
|
248
|
+
"brief", "orient", "where-was-i", "list", "coverage", "duplicates",
|
|
249
|
+
"plan-status", "export", "notes-vcs",
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _notes_precommit_state(sub: str):
|
|
254
|
+
"""Snapshot notes_root's dirty paths BEFORE a command, when opt-in VCS is on
|
|
255
|
+
and the command may mutate notes_root. Returns (notes_root, before_paths) or
|
|
256
|
+
None. Best-effort; never raises — VCS must never change the command's flow.
|
|
257
|
+
|
|
258
|
+
`notes-vcs` manages the repo itself; read-only verbs change nothing — both
|
|
259
|
+
skip. We only ever commit a repo work-plan OWNS that has NO remote, so a
|
|
260
|
+
pre-existing or remote-backed repo the user pointed notes_root at is left
|
|
261
|
+
alone (private notes stay un-pushable).
|
|
262
|
+
"""
|
|
263
|
+
if sub.lstrip("-") in _READONLY_SUBCOMMANDS:
|
|
264
|
+
return None
|
|
265
|
+
try:
|
|
266
|
+
from lib.config import load_config, notes_vcs_auto_commit
|
|
267
|
+
from lib import notes_vcs
|
|
268
|
+
from pathlib import Path
|
|
269
|
+
|
|
270
|
+
cfg = load_config()
|
|
271
|
+
if not notes_vcs_auto_commit(cfg):
|
|
272
|
+
return None
|
|
273
|
+
notes_root = Path(cfg["notes_root"]).expanduser()
|
|
274
|
+
if not notes_vcs.is_git_root(notes_root):
|
|
275
|
+
if not notes_vcs.is_under_git(notes_root):
|
|
276
|
+
print("ℹ auto-commit is on but notes_root isn't a git repo — "
|
|
277
|
+
"run `work-plan notes-vcs init` to enable local history.",
|
|
278
|
+
file=sys.stderr)
|
|
279
|
+
return None
|
|
280
|
+
if not notes_vcs.is_owned(notes_root) or notes_vcs.has_remotes(notes_root):
|
|
281
|
+
return None
|
|
282
|
+
return (notes_root, notes_vcs.dirty_paths(notes_root))
|
|
283
|
+
except Exception:
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _commit_changed_notes(pre, parts: list[str]) -> None:
|
|
288
|
+
"""Commit ONLY the paths a command newly changed (after − before), leaving
|
|
289
|
+
any pre-existing dirty files untouched. Best-effort; never raises and never
|
|
290
|
+
changes the command's exit code.
|
|
291
|
+
"""
|
|
292
|
+
notes_root, before = pre
|
|
293
|
+
try:
|
|
294
|
+
from lib import notes_vcs
|
|
295
|
+
|
|
296
|
+
changed = sorted(notes_vcs.dirty_paths(notes_root) - before)
|
|
297
|
+
if not changed:
|
|
298
|
+
return
|
|
299
|
+
message = "work-plan " + " ".join(parts)
|
|
300
|
+
sha = notes_vcs.auto_commit(notes_root, message, paths=changed)
|
|
301
|
+
if sha:
|
|
302
|
+
print(f"⏺ notes_root committed {sha} ({len(changed)} file(s)) — "
|
|
303
|
+
f"undo with: git -C {notes_root} revert {sha}", file=sys.stderr)
|
|
304
|
+
except Exception:
|
|
305
|
+
# VCS is a safety net, never a failure mode for the command itself.
|
|
306
|
+
return
|
|
227
307
|
|
|
228
308
|
|
|
229
309
|
if __name__ == "__main__":
|
/package/skills/work-plan/tests/fixtures/notes_root/{critforge → myproject}/no_frontmatter.md
RENAMED
|
File without changes
|