@stithy/mileage 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.
- package/LICENSE +26 -0
- package/README.md +75 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +43 -0
- package/dist/cli.js.map +1 -0
- package/dist/db.d.ts +3 -0
- package/dist/db.js +55 -0
- package/dist/db.js.map +1 -0
- package/dist/install.d.ts +2 -0
- package/dist/install.js +168 -0
- package/dist/install.js.map +1 -0
- package/dist/rates.d.ts +22 -0
- package/dist/rates.js +31 -0
- package/dist/rates.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +348 -0
- package/dist/server.js.map +1 -0
- package/dist/store.d.ts +126 -0
- package/dist/store.js +289 -0
- package/dist/store.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Parameters
|
|
4
|
+
|
|
5
|
+
Licensor: Stithy
|
|
6
|
+
Licensed Work: @stithy/memory
|
|
7
|
+
Copyright (c) 2026 Stithy
|
|
8
|
+
Additional Use Grant: You may use the Licensed Work for non-production
|
|
9
|
+
use, internal evaluation, and personal projects
|
|
10
|
+
free of charge. Production use by organizations
|
|
11
|
+
with annual revenue over $1M USD requires a
|
|
12
|
+
commercial license purchased through the MCP
|
|
13
|
+
Marketplace or directly from Stithy.
|
|
14
|
+
|
|
15
|
+
Change Date: 2028-04-12
|
|
16
|
+
Change License: Apache License, Version 2.0
|
|
17
|
+
|
|
18
|
+
For full BSL 1.1 terms see: https://mariadb.com/bsl11/
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
Notice
|
|
23
|
+
|
|
24
|
+
The Business Source License (this document, or the "License") is not an
|
|
25
|
+
Open Source license. However, the Licensed Work will eventually be made
|
|
26
|
+
available under an Open Source License, as stated in this License.
|
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# @stithy/mileage
|
|
2
|
+
|
|
3
|
+
> IRS-audit-proof mileage tracking. Log every business mile in your AI client. Get a Schedule C export at tax time. No app, no cloud, no monthly fee.
|
|
4
|
+
|
|
5
|
+
Stithy Mileage is a local-first MCP server for tracking deductible vehicle mileage. Every trip lives in a SQLite database on your laptop. Tax-year deductions are computed using the actual IRS standard mileage rate for that year — public-domain data, baked in.
|
|
6
|
+
|
|
7
|
+
## Outcomes
|
|
8
|
+
|
|
9
|
+
- **Stop missing deductions.** The average self-employed driver leaves $1,500-$3,000 on the table because they didn't log trips. Talk to Claude. Trip's logged.
|
|
10
|
+
- **Stop paying $5-10/mo for an app you barely open.** MileIQ, Everlance, Driversnote — replaced by one MCP install.
|
|
11
|
+
- **Audit-proof your log.** Per-trip date, miles, category, purpose, start/end, and vehicle — exactly what the IRS asks for during an audit.
|
|
12
|
+
- **No cloud sync issues.** Your trip data lives on your machine. You own it. You back it up.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
npx -y @stithy/mileage install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then in your AI client:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
add_vehicle name="My truck" make=Toyota model=Tacoma year=2019 is_default=true
|
|
24
|
+
add_trip trip_date=2026-04-01 miles=23 category=business purpose="client visit, downtown"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
At tax time:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
annual_deduction year=2026
|
|
31
|
+
export_csv from_date=2026-01-01 to_date=2026-12-31
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Tools (14)
|
|
35
|
+
|
|
36
|
+
| Tool | Purpose |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `add_vehicle` | Register a vehicle (year/make/model optional) |
|
|
39
|
+
| `list_vehicles` | All vehicles, default first |
|
|
40
|
+
| `set_default_vehicle` | Pick which vehicle add_trip uses by default |
|
|
41
|
+
| `remove_vehicle` | Delete (blocks if trips reference it) |
|
|
42
|
+
| `add_trip` | Log a trip — date, miles, category, purpose |
|
|
43
|
+
| `update_trip` | Patch a trip |
|
|
44
|
+
| `delete_trip` | Remove a trip |
|
|
45
|
+
| `list_trips` | List with optional date/category/vehicle filters |
|
|
46
|
+
| `log_odometer` | Year-start / year-end odometer reading |
|
|
47
|
+
| `list_odometer` | All readings for a vehicle, oldest first |
|
|
48
|
+
| `mileage_summary` | Total miles per category for any date range |
|
|
49
|
+
| `annual_deduction` | IRS-rate × miles for a tax year (Schedule C number) |
|
|
50
|
+
| `export_csv` | Audit-trail CSV: Date, Miles, Category, Purpose, Start, End, Vehicle, Notes |
|
|
51
|
+
| `irs_rates` | Reference: IRS standard mileage rates by year |
|
|
52
|
+
|
|
53
|
+
## IRS Standard Mileage Rates Built-In
|
|
54
|
+
|
|
55
|
+
Categories supported: **business**, **medical**, **moving** (active-duty military only since TCJA 2017), **charitable**.
|
|
56
|
+
|
|
57
|
+
Rates baked in for years 2022-2026 (from public IRS publications). When the IRS publishes the next year's rate (typically December), `@stithy/mileage` ships a one-line update. No subscription required to receive it.
|
|
58
|
+
|
|
59
|
+
## Privacy
|
|
60
|
+
|
|
61
|
+
Everything is local. Writes to `~/.stithy/mileage.db` (override with `STITHY_MILEAGE_DB`). Nothing is sent to Stithy or any third party. No telemetry. No cloud backup. No analytics.
|
|
62
|
+
|
|
63
|
+
## Compliance Notes
|
|
64
|
+
|
|
65
|
+
- This tool computes mileage deductions using the IRS standard-mileage method.
|
|
66
|
+
- The IRS requires you choose between standard-mileage and actual-expense in Year 1 of placing a vehicle in service. Standard-mileage is binding only if elected first; otherwise you can switch only with restrictions. Talk to your CPA.
|
|
67
|
+
- This is software, not tax advice. Always verify deductions with a qualified preparer before filing.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
[BUSL-1.1](./LICENSE). Free for personal and commercial use under $1M annual revenue. Commercial license via mcp-marketplace.io.
|
|
72
|
+
|
|
73
|
+
## Maintained
|
|
74
|
+
|
|
75
|
+
Active maintenance, 7-day bug-fix turnaround, 30-day refund. Annual IRS rate refresh shipped within 1 week of IRS publication. Issues: https://github.com/resolceo-ai/stithy-mcp/issues
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const cmd = process.argv[2];
|
|
3
|
+
if (cmd === "install" || cmd === "uninstall" || cmd === "list") {
|
|
4
|
+
await import("./install.js");
|
|
5
|
+
}
|
|
6
|
+
else if (cmd === "license") {
|
|
7
|
+
const { verifyLicense } = await import("@mcp_marketplace/license");
|
|
8
|
+
const r = await verifyLicense({ slug: "stithy-mileage" });
|
|
9
|
+
if (r.valid) {
|
|
10
|
+
console.log(`Licensed (server_id: ${r.server_id ?? "n/a"})`);
|
|
11
|
+
if (r.expires_at)
|
|
12
|
+
console.log(`Expires: ${r.expires_at}`);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
console.log(`Not licensed: ${r.reason ?? "unknown"}`);
|
|
16
|
+
console.log(`Set MCP_LICENSE_KEY in your environment.`);
|
|
17
|
+
console.log(`Get a key: https://mcp-marketplace.io/stithy-mileage`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
else if (cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
21
|
+
console.log(`stithy-mileage — IRS-audit-proof mileage tracker MCP server
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
npx @stithy/mileage Run the MCP server (stdio transport)
|
|
25
|
+
npx @stithy/mileage install Install into all detected AI clients
|
|
26
|
+
npx @stithy/mileage install --force Overwrite existing config
|
|
27
|
+
npx @stithy/mileage list Show install status for each client
|
|
28
|
+
npx @stithy/mileage uninstall Remove from all detected clients
|
|
29
|
+
npx @stithy/mileage license Check license status
|
|
30
|
+
|
|
31
|
+
Environment:
|
|
32
|
+
MCP_LICENSE_KEY Marketplace license key (required to run server)
|
|
33
|
+
STITHY_MILEAGE_DB Path to SQLite db (default: ~/.stithy/mileage.db)
|
|
34
|
+
STITHY_SKIP_LICENSE Set to 1 to bypass license check (dev/test only)
|
|
35
|
+
|
|
36
|
+
Docs: https://github.com/resolceo-ai/stithy-mcp
|
|
37
|
+
`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
await import("./server.js");
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;IAC/D,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;AAC/B,CAAC;KAAM,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;IAC7B,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IACnE,MAAM,CAAC,GAAG,MAAM,aAAa,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC1D,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,SAAS,IAAI,KAAK,GAAG,CAAC,CAAC;QAC7D,IAAI,CAAC,CAAC,UAAU;YAAE,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;KAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;CAgBb,CAAC,CAAC;AACH,CAAC;KAAM,CAAC;IACN,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;AAC9B,CAAC"}
|
package/dist/db.d.ts
ADDED
package/dist/db.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
export function openDb(path) {
|
|
5
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
6
|
+
const db = new Database(path);
|
|
7
|
+
db.pragma("journal_mode = WAL");
|
|
8
|
+
db.pragma("synchronous = NORMAL");
|
|
9
|
+
db.pragma("foreign_keys = ON");
|
|
10
|
+
migrate(db);
|
|
11
|
+
return db;
|
|
12
|
+
}
|
|
13
|
+
function migrate(db) {
|
|
14
|
+
db.exec(`
|
|
15
|
+
CREATE TABLE IF NOT EXISTS vehicles (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
name TEXT NOT NULL,
|
|
18
|
+
year INTEGER,
|
|
19
|
+
make TEXT,
|
|
20
|
+
model TEXT,
|
|
21
|
+
license_plate TEXT,
|
|
22
|
+
is_default INTEGER NOT NULL DEFAULT 0,
|
|
23
|
+
placed_in_service_date INTEGER,
|
|
24
|
+
created_at INTEGER NOT NULL
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE TABLE IF NOT EXISTS trips (
|
|
28
|
+
id TEXT PRIMARY KEY,
|
|
29
|
+
vehicle_id TEXT NOT NULL REFERENCES vehicles(id) ON DELETE RESTRICT,
|
|
30
|
+
trip_date TEXT NOT NULL, -- ISO yyyy-mm-dd, no timezone games
|
|
31
|
+
miles REAL NOT NULL,
|
|
32
|
+
category TEXT NOT NULL, -- business | medical | moving | charitable
|
|
33
|
+
purpose TEXT NOT NULL,
|
|
34
|
+
start_location TEXT,
|
|
35
|
+
end_location TEXT,
|
|
36
|
+
notes TEXT,
|
|
37
|
+
created_at INTEGER NOT NULL
|
|
38
|
+
);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_trips_date ON trips(trip_date);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_trips_vehicle ON trips(vehicle_id);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_trips_category ON trips(category);
|
|
42
|
+
|
|
43
|
+
CREATE TABLE IF NOT EXISTS odometer_snapshots (
|
|
44
|
+
id TEXT PRIMARY KEY,
|
|
45
|
+
vehicle_id TEXT NOT NULL REFERENCES vehicles(id) ON DELETE CASCADE,
|
|
46
|
+
snapshot_date TEXT NOT NULL,
|
|
47
|
+
reading INTEGER NOT NULL,
|
|
48
|
+
note TEXT,
|
|
49
|
+
created_at INTEGER NOT NULL
|
|
50
|
+
);
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_odo_vehicle_date ON odometer_snapshots(vehicle_id, snapshot_date);
|
|
52
|
+
`);
|
|
53
|
+
}
|
|
54
|
+
export const VALID_CATEGORIES = ["business", "medical", "moving", "charitable"];
|
|
55
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9B,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,OAAO,CAAC,EAAqB;IACpC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCP,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAU,CAAC"}
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, renameSync } from "node:fs";
|
|
3
|
+
import { homedir, platform } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
function targets() {
|
|
6
|
+
const home = homedir();
|
|
7
|
+
const p = platform();
|
|
8
|
+
const list = [];
|
|
9
|
+
if (p === "darwin") {
|
|
10
|
+
list.push({
|
|
11
|
+
name: "Claude Desktop",
|
|
12
|
+
configPath: join(home, "Library/Application Support/Claude/claude_desktop_config.json"),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
else if (p === "win32") {
|
|
16
|
+
const appdata = process.env.APPDATA ?? join(home, "AppData/Roaming");
|
|
17
|
+
list.push({
|
|
18
|
+
name: "Claude Desktop",
|
|
19
|
+
configPath: join(appdata, "Claude/claude_desktop_config.json"),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
list.push({
|
|
24
|
+
name: "Claude Desktop",
|
|
25
|
+
configPath: join(home, ".config/Claude/claude_desktop_config.json"),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
list.push({ name: "Cursor", configPath: join(home, ".cursor/mcp.json") });
|
|
29
|
+
list.push({
|
|
30
|
+
name: "Windsurf",
|
|
31
|
+
configPath: join(home, ".codeium/windsurf/mcp_config.json"),
|
|
32
|
+
});
|
|
33
|
+
list.push({ name: "Claude Code", configPath: join(home, ".claude.json") });
|
|
34
|
+
return list;
|
|
35
|
+
}
|
|
36
|
+
function serverEntry() {
|
|
37
|
+
return { command: "npx", args: ["-y", "@stithy/mileage"] };
|
|
38
|
+
}
|
|
39
|
+
function readJson(path) {
|
|
40
|
+
if (!existsSync(path))
|
|
41
|
+
return { ok: true, data: {} };
|
|
42
|
+
try {
|
|
43
|
+
const raw = readFileSync(path, "utf8");
|
|
44
|
+
if (raw.trim().length === 0)
|
|
45
|
+
return { ok: true, data: {} };
|
|
46
|
+
const parsed = JSON.parse(raw);
|
|
47
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
48
|
+
return { ok: false, reason: "Existing config is not a JSON object — refusing to overwrite" };
|
|
49
|
+
}
|
|
50
|
+
return { ok: true, data: parsed };
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
reason: `Existing config is not valid JSON (${e instanceof Error ? e.message : String(e)}) — refusing to overwrite`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function writeJsonAtomic(path, data) {
|
|
60
|
+
const tmp = `${path}.stithy.tmp`;
|
|
61
|
+
const json = JSON.stringify(data, null, 2);
|
|
62
|
+
writeFileSync(tmp, json);
|
|
63
|
+
JSON.parse(readFileSync(tmp, "utf8"));
|
|
64
|
+
renameSync(tmp, path);
|
|
65
|
+
}
|
|
66
|
+
function backup(path) {
|
|
67
|
+
if (!existsSync(path))
|
|
68
|
+
return;
|
|
69
|
+
const bak = `${path}.stithy.bak.${Date.now()}`;
|
|
70
|
+
copyFileSync(path, bak);
|
|
71
|
+
console.log(` backup: ${bak}`);
|
|
72
|
+
}
|
|
73
|
+
function installFor(t, name, force) {
|
|
74
|
+
try {
|
|
75
|
+
const result = readJson(t.configPath);
|
|
76
|
+
if (!result.ok) {
|
|
77
|
+
console.error(`✗ ${t.name}: ${result.reason}`);
|
|
78
|
+
return "error";
|
|
79
|
+
}
|
|
80
|
+
const cfg = result.data;
|
|
81
|
+
const servers = cfg["mcpServers"] ?? {};
|
|
82
|
+
if (servers[name] && !force) {
|
|
83
|
+
console.log(`✓ ${t.name}: already configured (use --force to overwrite)`);
|
|
84
|
+
return "skipped";
|
|
85
|
+
}
|
|
86
|
+
servers[name] = serverEntry();
|
|
87
|
+
cfg["mcpServers"] = servers;
|
|
88
|
+
if (existsSync(t.configPath))
|
|
89
|
+
backup(t.configPath);
|
|
90
|
+
mkdirSync(dirname(t.configPath), { recursive: true });
|
|
91
|
+
writeJsonAtomic(t.configPath, cfg);
|
|
92
|
+
console.log(`✓ ${t.name}: installed at ${t.configPath}`);
|
|
93
|
+
return "installed";
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
console.error(`✗ ${t.name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
97
|
+
return "error";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function uninstallFor(t, name) {
|
|
101
|
+
try {
|
|
102
|
+
if (!existsSync(t.configPath))
|
|
103
|
+
return false;
|
|
104
|
+
const result = readJson(t.configPath);
|
|
105
|
+
if (!result.ok) {
|
|
106
|
+
console.error(`✗ ${t.name}: ${result.reason}`);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const cfg = result.data;
|
|
110
|
+
const servers = cfg["mcpServers"] ?? {};
|
|
111
|
+
if (!servers[name])
|
|
112
|
+
return false;
|
|
113
|
+
delete servers[name];
|
|
114
|
+
cfg["mcpServers"] = servers;
|
|
115
|
+
backup(t.configPath);
|
|
116
|
+
writeJsonAtomic(t.configPath, cfg);
|
|
117
|
+
console.log(`✓ ${t.name}: removed`);
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
console.error(`✗ ${t.name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function main() {
|
|
126
|
+
const args = process.argv.slice(2);
|
|
127
|
+
const cmd = args[0] ?? "install";
|
|
128
|
+
const force = args.includes("--force");
|
|
129
|
+
const name = "stithy-mileage";
|
|
130
|
+
if (cmd === "uninstall") {
|
|
131
|
+
console.log("Removing stithy-mileage from detected clients...");
|
|
132
|
+
for (const t of targets())
|
|
133
|
+
uninstallFor(t, name);
|
|
134
|
+
console.log("\nDone. Restart your AI client to pick up the change.");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (cmd === "list") {
|
|
138
|
+
for (const t of targets()) {
|
|
139
|
+
const present = existsSync(t.configPath);
|
|
140
|
+
let has = false;
|
|
141
|
+
let bad = false;
|
|
142
|
+
if (present) {
|
|
143
|
+
const r = readJson(t.configPath);
|
|
144
|
+
if (!r.ok) {
|
|
145
|
+
bad = true;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const servers = r.data["mcpServers"] ?? {};
|
|
149
|
+
has = Boolean(servers[name]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const marker = bad ? "?" : has ? "✓" : present ? "·" : "—";
|
|
153
|
+
console.log(`${marker} ${t.name.padEnd(16)} ${t.configPath}${bad ? " (config unparseable)" : ""}`);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
console.log("Installing stithy-mileage across detected AI clients...\n");
|
|
158
|
+
let installed = 0;
|
|
159
|
+
for (const t of targets()) {
|
|
160
|
+
const result = installFor(t, name, force);
|
|
161
|
+
if (result === "installed")
|
|
162
|
+
installed++;
|
|
163
|
+
}
|
|
164
|
+
console.log(`\nInstalled to ${installed} client${installed === 1 ? "" : "s"}.`);
|
|
165
|
+
console.log("Restart your AI client(s) to load stithy-mileage.");
|
|
166
|
+
}
|
|
167
|
+
main();
|
|
168
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
|
package/dist/rates.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IRS standard mileage rates by tax year and category, in cents per mile.
|
|
3
|
+
*
|
|
4
|
+
* Source: IRS-published standard mileage rates. These are public-domain US tax data.
|
|
5
|
+
* Update annually when the IRS publishes the new year's rates (typically December).
|
|
6
|
+
*
|
|
7
|
+
* Categories:
|
|
8
|
+
* business — Schedule C line 9 deduction for self-employed business miles
|
|
9
|
+
* medical — Schedule A medical/dental travel
|
|
10
|
+
* moving — Active-duty military only (post-2017 TCJA)
|
|
11
|
+
* charitable — Statutory rate set by Congress (rarely changes)
|
|
12
|
+
*/
|
|
13
|
+
export type Category = "business" | "medical" | "moving" | "charitable";
|
|
14
|
+
export interface YearRates {
|
|
15
|
+
business: number;
|
|
16
|
+
medical: number;
|
|
17
|
+
moving: number;
|
|
18
|
+
charitable: number;
|
|
19
|
+
}
|
|
20
|
+
export declare const RATES: Record<number, YearRates>;
|
|
21
|
+
export declare function rateCentsPerMile(year: number, category: Category): number;
|
|
22
|
+
export declare function knownYears(): number[];
|
package/dist/rates.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IRS standard mileage rates by tax year and category, in cents per mile.
|
|
3
|
+
*
|
|
4
|
+
* Source: IRS-published standard mileage rates. These are public-domain US tax data.
|
|
5
|
+
* Update annually when the IRS publishes the new year's rates (typically December).
|
|
6
|
+
*
|
|
7
|
+
* Categories:
|
|
8
|
+
* business — Schedule C line 9 deduction for self-employed business miles
|
|
9
|
+
* medical — Schedule A medical/dental travel
|
|
10
|
+
* moving — Active-duty military only (post-2017 TCJA)
|
|
11
|
+
* charitable — Statutory rate set by Congress (rarely changes)
|
|
12
|
+
*/
|
|
13
|
+
export const RATES = {
|
|
14
|
+
2022: { business: 58.5, medical: 18, moving: 18, charitable: 14 },
|
|
15
|
+
2023: { business: 65.5, medical: 22, moving: 22, charitable: 14 },
|
|
16
|
+
2024: { business: 67, medical: 21, moving: 21, charitable: 14 },
|
|
17
|
+
2025: { business: 70, medical: 21, moving: 21, charitable: 14 },
|
|
18
|
+
2026: { business: 72.5, medical: 21, moving: 21, charitable: 14 },
|
|
19
|
+
};
|
|
20
|
+
export function rateCentsPerMile(year, category) {
|
|
21
|
+
const yr = RATES[year];
|
|
22
|
+
if (!yr) {
|
|
23
|
+
const known = Object.keys(RATES).map(Number).sort();
|
|
24
|
+
throw new Error(`No IRS rate published yet for year ${year}. Known years: ${known.join(", ")}. Use the most recent year as a placeholder if filing late.`);
|
|
25
|
+
}
|
|
26
|
+
return yr[category];
|
|
27
|
+
}
|
|
28
|
+
export function knownYears() {
|
|
29
|
+
return Object.keys(RATES).map(Number).sort();
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=rates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rates.js","sourceRoot":"","sources":["../src/rates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,MAAM,CAAC,MAAM,KAAK,GAA8B;IAC9C,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACjE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACjE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IAC/D,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IAC/D,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;CAClE,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,QAAkB;IAC/D,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CACb,sCAAsC,IAAI,kBAAkB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,6DAA6D,CAC1I,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC"}
|
package/dist/server.d.ts
ADDED