From smartvault-organizer
Organizes SmartVault accounting client vaults: renames files using standardized naming conventions, moves documents into correct subfolders, and renames client root folders to the canonical "Name - Email" format. Processes one client at a time to prevent disk exhaustion.
How this skill is triggered — by the user, by Claude, or both
Slash command
/smartvault-organizer:smartvault-organizerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are executing a multi-phase document organization workflow. Follow each phase
You are executing a multi-phase document organization workflow. Follow each phase precisely and in order. Never process multiple clients simultaneously.
Run Step 0 first, on every trigger — "organize smartvault", "configure smartvault", "set smartvault credentials", an authorization request, "pause smartvault", "revert [client]", anything. There is no separate pause/resume code path: Step 0 is the single source of truth for resumability, and it runs no matter which phrase the user used.
| Client Type | Format | Example |
|---|---|---|
| Individual | Last Name, First Name - [email protected] | Smith, John - [email protected] |
| Business | Business Name - [email protected] | ABC Holdings LLC - [email protected] |
EIN Letter
Receipts
Tax Documents
Entity Documents
Organizer
Miscellaneous
Per-file naming/classification rules live in references/classification-rules.md
(used by Phase 3 batch subagents).
python3 scripts/main.py check 2>/dev/null || python scripts/main.py check
The python3 || python fallback handles environments where only one is on PATH — no
guessing needed. This single command: ensures the DB schema exists, records Python/OS
details to .mcp_state/env.json (diagnostics for "python not found" type issues),
checks config + auth state, and reports DB progress / resumability. One JSON reply,
parse recommended_action:
recommended_action | Meaning | Go to |
|---|---|---|
fill_config | .mcp_state/config.json missing/incomplete | A. Fill config |
authorize | not authenticated (or token expired, refresh failed) | B. Authorize |
sync_clients | no client data yet, or all clients already done | Phase 2, then Phase 3 |
resume | clients.ready > 0 — unfinished work exists (fresh sync, paused run, or crash) | C. Resume, then Phase 3 |
report | clients synced, none ready left | Phase 4 |
recommended_action is the path for organize / continue / resume / a bare invocation.
If the user asked for something more specific, handle that request first — using the
Step 0 result you already have, don't re-run it:
| User said | Do this |
|---|---|
| "configure smartvault" / "set smartvault credentials" / "update smartvault config" | Go to /configure below, regardless of recommended_action |
an authorization request, or recommended_action == authorize | Go to B. Authorize |
| "pause smartvault" / "stop after this client" | Go to Pausing a run intentionally |
| "continue smartvault" / "resume smartvault" | Follow recommended_action (will be resume if there's work left) |
| "revert [client]" | Go to Reverting a client |
| anything else (organize, bare invocation, etc.) | Follow recommended_action table above |
Resumability note: after handling a /configure, auth, pause, or revert request, if
Step 0's clients.ready > 0, tell the user (this is what makes pause/resume visible
without the user having to say "continue smartvault"):
"You also have an in-progress SmartVault run — N client(s) remaining. Say 'continue smartvault' (or 'organize smartvault') to resume."
fill_config)STOP and tell the user:
"
.mcp_state/config.jsonneeds these fields filled in:
client_id— your SmartVault OAuth client IDclient_secret— your SmartVault OAuth client secretredirect_uri— OAuth callback URL (default:http://localhost:8000/oauth/callback/)api_base— API base URL (default:https://rest.smartvault.com)Reply when done."
After the user confirms → re-run Step 0.
authorize)Show the user:
"Please open this URL in your browser to authorize SmartVault access:
<auth_url from check output>After authorizing, copy the
codeparameter from the redirect URL and paste it here."
After user provides the code:
python scripts/main.py auth --code <CODE_FROM_USER>
Verify "status": "authenticated", then re-run Step 0.
resume)python scripts/main.py resume
Parse client_ids — this is the list to process in Phase 3 (skip Phase 2; the DB already
has client data from a prior sync). If clients.ready in the Step 0 output equals
clients.total - completed - not_ready - failed from a fresh sync (i.e. this is the
first run, not a resume), this is the same list sync_clients would produce — no
information is lost by skipping it.
Reached via Step 0's routing table — either recommended_action == fill_config, or the
user explicitly asked to configure/set credentials. Use it to inspect or update
.mcp_state/config.json without editing the file directly.
View current config (secret masked):
python scripts/main.py configure
Set one or more values:
python scripts/main.py configure \
--client-id <CLIENT_ID> \
--client-secret <CLIENT_SECRET> \
--email <ACCOUNT_EMAIL> \
--redirect-uri <REDIRECT_URI> \
--api-base <API_BASE_URL>
All flags are optional — only supplied flags are written. Unspecified keys are left unchanged.
| Flag | Description | Default |
|---|---|---|
--client-id | SmartVault OAuth application client ID | — |
--client-secret | OAuth client secret | — |
--email | SmartVault account login email | — |
--redirect-uri | OAuth callback URL | http://localhost:8000/oauth/callback/ |
--api-base | SmartVault REST API base URL | https://rest.smartvault.com |
After updating credentials, re-run auth to obtain a fresh token:
python scripts/main.py auth
Then apply the resumability note from Step 0 if it applies.
Only run when Step 0 returned recommended_action: sync_clients.
python scripts/main.py sync_clients
This fetches all FirmClient entities from SmartVault, stores them in the local database,
and classifies each as ready (has email + vault path) or not_ready.
Parse JSON output — extract ready_client_ids list.
Example output:
{
"status": "ok",
"total_clients": 150,
"ready_client_ids": ["abc123", "def456", "ghi789"]
}
Report to the user:
"Sync complete. Found
total_clientstotal clients.len(ready_client_ids)are ready for processing."
If ready_client_ids is empty:
python scripts/main.py generate_reports
Report results and stop.
Reporting in this phase: caveman-ultra. Per client: one header line. Per batch: one ordinal line ("1st batch finish", "2nd batch finish", ...). Conflicts and questions to the user: full verbose, no compression — drop ultra for those, resume after.
Process each client one at a time. For each client_id in ready_client_ids:
At the start of each client iteration, check whether the user has requested a pause:
python scripts/main.py run_status
If "run_status": "paused" → stop immediately and tell the user:
"Run is paused. X clients still remaining. Say 'continue smartvault' when ready to resume."
Do not process any more clients until the user resumes.
python scripts/main.py stage_client_files --client_id <client_id> --list-only
python scripts/main.py get_client_info --client_id <client_id>
stage_client_files walks the client vault, writes file metadata to the DB, and returns
a JSON array of unorganized files. Files already in the 6 standard subfolders are
intentionally skipped. No files are downloaded at this step.
Example output:
[
{"file_id": 42, "name": "ein_letter.pdf", "folder_path": "...", "size": 51234},
{"file_id": 43, "name": "receipt_amazon.pdf", "folder_path": "...", "size": 88210}
]
get_client_info returns {display_name, original_display_name, type_qualifier, email, persons} — keep this for batch prompts and Step 3.6.
If the file array is empty → skip to Step 3.4 (cleanup) then Step 3.6 (rename).
Store the full list in memory. Split it into consecutive batches of 3 file entries
(B = total batches, N = total files). Report:
"Processing client
<display_name><client_id>- total files:N-Bbatches"
For each batch i (1-indexed, of B), spawn a general-purpose subagent (Agent
tool) with this prompt:
SmartVault batch <i>/<B> for client <client_id>.
Client info: <get_client_info JSON from Step 3.1>
Files (file_id|name|folder_path|size):
<id>|<name>|<folder_path>|<size>
... (up to 3 lines)
1. Read references/classification-rules.md — follow its pipeline, naming rules,
process_file/duplicate handling, and reply format exactly.
2. Run: python scripts/main.py download_file_batch --client_id <client_id> --file-ids "<id1>,<id2>,<id3>"
3. Classify and process each file per the rules doc.
4. Run the rules doc's end-of-batch `cleanup_files` call (never raw `rm`).
5. Reply using the rules doc's reply format ONLY — no other text.
The subagent does its own download → classify → process_file → cleanup. PDF/image
content it reads never enters the main thread's context — only its short reply does.
Handling the subagent's reply:
<n>/<n> ok (no CONFLICT lines) → report "<ordinal> batch finish"
(e.g. "1st batch finish", "2nd batch finish", ...). Move to next batch.CONFLICT block(s) → drop ultra mode. Print the conflict block(s)
to the user verbatim (full verbose — source/dest descriptions, file_id, target path).
Ask how to proceed. Pause — do not start the next batch until the user responds.
Resume ultra reporting once resolved.processing in the DB and will be picked
up automatically on the next resume. Continue to the next batch.Continue until all B batches are complete.
After all batches are done, check whether any non-standard folders (folders that are not one of the 6 standard subfolders) are now empty and can be deleted:
python scripts/main.py cleanup_empty_folders --client_id <client_id>
Parse JSON output:
deleted — list of {name, path} objects for folders that were empty and deleted via API.
Report to the user: "Removed empty folders: [names]"
non_empty — list of {name, path, child_count} objects for folders that still contain files.
If this list is not empty, report to the user:
"The following non-standard folders still contain files and were not deleted: [name — N file(s)] … How would you like me to handle them?"
Pause and wait for the user's instruction before continuing to the next client. Do not proceed to Step 3.5 until the user responds.
python scripts/main.py cleanup_temp --client_id <client_id>
This removes ./temp_docs/<client_id>/ (handles any remaining stragglers) and frees
disk space before the next client.
Using the client data collected during this loop iteration, determine the canonical folder name:
Determine client type:
type_qualifier from DB = "Individual", or the persons list has clear first/last
names → Individual format: Last Name, First Name - emailBusiness Name - emailUse the email stored in the DB. Use the client name from DB display_name or from
document analysis (whichever is more accurate/complete).
python scripts/main.py rename_client \
--client_id <client_id> \
--format "Smith, John - [email protected]"
"status": "ok" → continue to next client."status": "error" → log the error, continue to next client. The client will appear
in failed_or_unready_clients.csv.After completing Steps 3.0–3.6 for one client, move to the next client_id in the list.
Continue until all ready clients are processed or a pause is detected.
No action needed. Every completed file and client is persisted in the local database immediately after processing. If the run is interrupted for any reason:
recommended_action will be resume (any client still ready counts,
regardless of why the run stopped)The explicit pause command is only needed when the user intentionally wants to stop.
If the user says "pause" or "stop after this client" at any point:
python scripts/main.py pause
Claude will finish the current client, then stop and report:
"Run paused. X clients remaining. Say 'continue smartvault' to resume."
If the user says "continue smartvault" or "resume smartvault" — go to Step 0.
recommended_action will be resume regardless of run_status (paused or not); Step 0
also resets run_state to running via resume. No separate path needed.
If the user says "revert [client name or ID]", identify the client_id from the DB and run:
python scripts/main.py revert_client --client_id <client_id>
This will:
ready so it can be re-processedParse the output and report:
"Reverted X files for [client name]. Folder rename also reverted: yes/no."
If failed list is non-empty, report each error.
After the loop completes for all clients:
python scripts/main.py generate_reports
Parse the output and summarize for the user:
"Processing complete.
Outcome Count Completed X Failed or Not Ready Y Reports saved:
completed_clients.csvfailed_or_unready_clients.csv"
If failed_or_unready > 0, offer:
"Would you like me to display the reasons from
failed_or_unready_clients.csv?"
If yes, read and display the file with reasons grouped by not_ready_reason.
| Situation | Action |
|---|---|
| Auth expired mid-run | Re-run auth; if auth_required, show URL again |
| Single file download fails | Log and skip; continue to next file |
process_file → duplicate_resolved_by_size | Same-size file at dest confirmed duplicate; source deleted automatically |
process_file → duplicate_uncertain | Subagent downloads dest, compares content; resolves or returns CONFLICT for user |
Single process_file fails | Subagent logs and skips; continues to next file |
Subagent reply has CONFLICT block(s) | Drop ultra mode, print full verbose to user, pause before next batch |
| Subagent reply malformed | Print full subagent output (debug); files stay processing, picked up on resume; continue |
rename_client fails | Log error; mark client failed; continue to next client |
stage_client_files returns empty | Skip 3.2–3.4; still run 3.5 and 3.6 |
| Network error (5xx) | Retry once after 5 seconds; if still failing, log and continue |
Never abort the entire run due to a single client or file failure.
This skill addresses the following tasks automatically:
Client Name - Email formatEIN Letter with standardized namesReceipts with Vendor, Amount, and DateTax Documents with form type and yearEntity DocumentsOrganizer with type, year, and client namefailed_or_unready_clients.csvCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub eaby7210/smartvault-organizer-skil --plugin smartvault-organizer