moshcode 0.70.0 → 0.71.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/README.md +88 -0
- package/bin/moshcode.mjs +34 -0
- package/package.json +1 -1
- package/prd/0012-billing-baked-into-the-agent-cli.md +129 -0
- package/src/billing.mjs +363 -0
- package/src/business-store.mjs +155 -0
- package/src/cli-schema.mjs +245 -0
- package/src/clients.mjs +328 -0
- package/src/commands.mjs +9 -0
- package/src/payments.mjs +258 -0
- package/src/rates.mjs +368 -0
- package/src/teams.mjs +459 -0
- package/src/timer.mjs +354 -0
- package/src/tui.mjs +54 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Where the business layer keeps its records.
|
|
2
|
+
//
|
|
3
|
+
// moshcode already knows what your agents are doing; the business layer is the
|
|
4
|
+
// half that knows *who it is for* and *what it costs*. That is two different
|
|
5
|
+
// kinds of file, so it is two files:
|
|
6
|
+
//
|
|
7
|
+
// ~/.moshcode/business.json clients, teams, rates, gateways, invoices
|
|
8
|
+
// ~/.moshcode/timers.json the running timer and the entries it has closed
|
|
9
|
+
//
|
|
10
|
+
// Split because they are written at different rates and for different reasons.
|
|
11
|
+
// business.json is configuration — edited by hand often enough that it has to
|
|
12
|
+
// stay readable, and small enough that rewriting it whole costs nothing.
|
|
13
|
+
// timers.json is a ledger that grows every time somebody stops a timer, and a
|
|
14
|
+
// half-written ledger must never be able to take the config down with it.
|
|
15
|
+
//
|
|
16
|
+
// Neither file is a secret store. A client's phone number lives here; a Stripe
|
|
17
|
+
// key does not — see src/payments.mjs for where those go instead. The files are
|
|
18
|
+
// still 0600, because a client list is nobody else's business on a shared box.
|
|
19
|
+
import fs from "node:fs";
|
|
20
|
+
import os from "node:os";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
|
|
23
|
+
const FILE_MODE = 0o600;
|
|
24
|
+
const DIR_MODE = 0o700;
|
|
25
|
+
|
|
26
|
+
/** Current on-disk shape. Bumped only when a migration is actually needed. */
|
|
27
|
+
export const SCHEMA_VERSION = 1;
|
|
28
|
+
|
|
29
|
+
const EMPTY_BUSINESS = { version: SCHEMA_VERSION, clients: {}, teams: {}, rates: {}, payments: {}, invoices: {} };
|
|
30
|
+
const EMPTY_TIMERS = { version: SCHEMA_VERSION, active: null, entries: [] };
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The config directory, derived per call so tests can move $HOME.
|
|
34
|
+
*
|
|
35
|
+
* Deliberately not `MOSHCODE_HOME`: that variable already means the directory
|
|
36
|
+
* moshcode is *installed* in — install.sh exports it and src/upgrade.mjs reads
|
|
37
|
+
* it — so honouring it here would file a client list inside the package on any
|
|
38
|
+
* machine that has it set. Same path src/aliases.mjs uses.
|
|
39
|
+
*/
|
|
40
|
+
export function moshcodeDir() {
|
|
41
|
+
return path.join(os.homedir(), ".moshcode");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function businessFile() {
|
|
45
|
+
return process.env.MOSHCODE_BUSINESS_FILE || path.join(moshcodeDir(), "business.json");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function timersFile() {
|
|
49
|
+
return process.env.MOSHCODE_TIMERS_FILE || path.join(moshcodeDir(), "timers.json");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Read a JSON file, or the given default.
|
|
54
|
+
*
|
|
55
|
+
* Every failure reads as "nothing recorded yet": missing, unreadable, truncated
|
|
56
|
+
* by a crash mid-write, or hand-edited into something that is not an object.
|
|
57
|
+
* These files are read on command paths a person is sitting in front of, and
|
|
58
|
+
* throwing a SyntaxError at somebody who wanted `/timer status` tells them
|
|
59
|
+
* nothing they can act on. `/client list` on an empty list does.
|
|
60
|
+
*/
|
|
61
|
+
function readJson(file, fallback) {
|
|
62
|
+
let raw;
|
|
63
|
+
try { raw = fs.readFileSync(file, "utf8"); }
|
|
64
|
+
catch { return structuredClone(fallback); }
|
|
65
|
+
let parsed;
|
|
66
|
+
try { parsed = JSON.parse(raw); }
|
|
67
|
+
catch { return structuredClone(fallback); }
|
|
68
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return structuredClone(fallback);
|
|
69
|
+
return { ...structuredClone(fallback), ...parsed };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Write a JSON file atomically.
|
|
74
|
+
*
|
|
75
|
+
* Rename-over rather than write-in-place: two pits are a normal way to use
|
|
76
|
+
* moshcode, and `/timer off` in one while `/client set` runs in the other must
|
|
77
|
+
* not be able to leave either file half-written. The temp file is created in
|
|
78
|
+
* the same directory so the rename stays on one filesystem.
|
|
79
|
+
*/
|
|
80
|
+
function writeJson(file, data) {
|
|
81
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: DIR_MODE });
|
|
82
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
83
|
+
fs.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}\n`, { mode: FILE_MODE });
|
|
84
|
+
fs.renameSync(tmp, file);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function loadBusiness() {
|
|
88
|
+
const data = readJson(businessFile(), EMPTY_BUSINESS);
|
|
89
|
+
// A hand edit that empties one section must not make every reader defensive.
|
|
90
|
+
for (const key of ["clients", "teams", "rates", "payments", "invoices"]) {
|
|
91
|
+
if (!data[key] || typeof data[key] !== "object" || Array.isArray(data[key])) data[key] = {};
|
|
92
|
+
}
|
|
93
|
+
return data;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function saveBusiness(data) {
|
|
97
|
+
writeJson(businessFile(), { ...data, version: SCHEMA_VERSION });
|
|
98
|
+
return data;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Read, mutate, write — the shape every business verb wants. Returns fn's result. */
|
|
102
|
+
export function updateBusiness(fn) {
|
|
103
|
+
const data = loadBusiness();
|
|
104
|
+
const result = fn(data);
|
|
105
|
+
saveBusiness(data);
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function loadTimers() {
|
|
110
|
+
const data = readJson(timersFile(), EMPTY_TIMERS);
|
|
111
|
+
if (!Array.isArray(data.entries)) data.entries = [];
|
|
112
|
+
if (data.active && typeof data.active !== "object") data.active = null;
|
|
113
|
+
return data;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function saveTimers(data) {
|
|
117
|
+
writeJson(timersFile(), { ...data, version: SCHEMA_VERSION });
|
|
118
|
+
return data;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function updateTimers(fn) {
|
|
122
|
+
const data = loadTimers();
|
|
123
|
+
const result = fn(data);
|
|
124
|
+
saveTimers(data);
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* A stable handle for a name: "Acme Inc." → "acme-inc".
|
|
130
|
+
*
|
|
131
|
+
* Ids are derived rather than random because they are typed constantly —
|
|
132
|
+
* `/timer on acme`, `/billing acme` — and a name is what a person remembers.
|
|
133
|
+
* Collisions are the caller's problem to report; this only does the transform.
|
|
134
|
+
*/
|
|
135
|
+
export function slugify(name) {
|
|
136
|
+
return String(name ?? "")
|
|
137
|
+
.toLowerCase()
|
|
138
|
+
.normalize("NFKD")
|
|
139
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
140
|
+
.replace(/^-+|-+$/g, "")
|
|
141
|
+
.slice(0, 64);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Short, sortable, collision-resistant enough for a personal ledger.
|
|
146
|
+
*
|
|
147
|
+
* Time-prefixed so `/timer log` reads in order even after a hand edit, and so
|
|
148
|
+
* an id carries a hint of when it was made. Not a UUID: these are meant to be
|
|
149
|
+
* retyped (`/timer rm k3f9a2`).
|
|
150
|
+
*/
|
|
151
|
+
export function newId(prefix = "", now = Date.now()) {
|
|
152
|
+
const stamp = now.toString(36);
|
|
153
|
+
const noise = Math.floor(Math.random() * 36 ** 3).toString(36).padStart(3, "0");
|
|
154
|
+
return `${prefix}${stamp.slice(-5)}${noise}`;
|
|
155
|
+
}
|
package/src/cli-schema.mjs
CHANGED
|
@@ -22,6 +22,7 @@ export const COMMAND_GROUPS = [
|
|
|
22
22
|
{ key: "engines", title: "engines" },
|
|
23
23
|
{ key: "runtime", title: "runtime" },
|
|
24
24
|
{ key: "tools", title: "tools" },
|
|
25
|
+
{ key: "business", title: "business" },
|
|
25
26
|
{ key: "extend", title: "extend" },
|
|
26
27
|
{ key: "script", title: "script" },
|
|
27
28
|
{ key: "arcade", title: "arcade" },
|
|
@@ -649,6 +650,179 @@ export const CORE_CLI_COMMANDS = [
|
|
|
649
650
|
note: "needs an interactive terminal. ↑↓/jk move · ⏎ read · o open in a browser · "
|
|
650
651
|
+ "tab the feed list · / search · r refresh · q quit.",
|
|
651
652
|
},
|
|
653
|
+
{
|
|
654
|
+
name: "timer",
|
|
655
|
+
group: "business",
|
|
656
|
+
description: "track time — on, off, and what it added up to",
|
|
657
|
+
synopsis: [
|
|
658
|
+
["moshcode timer on [client] [--task …]", "start the clock"],
|
|
659
|
+
["moshcode timer off", "stop it and write the entry"],
|
|
660
|
+
["moshcode timer status", "what is running, and what it is worth so far"],
|
|
661
|
+
["moshcode timer log [--week] [--json]", "the entries behind an invoice"],
|
|
662
|
+
],
|
|
663
|
+
verbs: "TIMER_VERBS",
|
|
664
|
+
flags: [
|
|
665
|
+
["--task <what>", "what this stretch of work is", "the words after the client"],
|
|
666
|
+
["--agents <n|auto>", "how many engines are working; auto counts the herd", "1"],
|
|
667
|
+
["--note <text>", "anything else worth keeping", ""],
|
|
668
|
+
["--client <id>", "filter the log to one client", "all of them"],
|
|
669
|
+
["--today, --week, --month", "window the log", "everything"],
|
|
670
|
+
["--since <date>", "window the log from a date", ""],
|
|
671
|
+
["--unbilled", "only entries no invoice has claimed", ""],
|
|
672
|
+
["--limit <n>", "how many entries to print", "50"],
|
|
673
|
+
["--json", "machine-readable", ""],
|
|
674
|
+
],
|
|
675
|
+
examples: [
|
|
676
|
+
["moshcode timer on acme --task \"batch payments\" --agents auto", "clock in, counting the herd"],
|
|
677
|
+
["moshcode timer off", "clock out — prints the time and what it earned"],
|
|
678
|
+
["moshcode timer add acme 2h30m --task \"code review\"", "log time you forgot to track"],
|
|
679
|
+
["moshcode timer log --week", "this week's timesheet"],
|
|
680
|
+
],
|
|
681
|
+
seeAlso: ["billing", "rate", "client"],
|
|
682
|
+
note: "the ledger is ~/.moshcode/timers.json and knows nothing about money — a rate is applied later, "
|
|
683
|
+
+ "by `moshcode billing`, so the timer is useful with no client, no rate and no gateway.",
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
name: "client",
|
|
687
|
+
group: "business",
|
|
688
|
+
description: "who the work is for — clients, businesses, merchants",
|
|
689
|
+
synopsis: [
|
|
690
|
+
["moshcode client create <name>[, url][, phone]", "the comma form, as pasted"],
|
|
691
|
+
["moshcode client create <name> --contact.telephone <n>", "the dotted form, for scripts"],
|
|
692
|
+
["moshcode client list | show <id> | set <id> --field <v>", ""],
|
|
693
|
+
["moshcode client payee <id> <chain:address>", "where their payments land"],
|
|
694
|
+
],
|
|
695
|
+
verbs: "CLIENT_VERBS",
|
|
696
|
+
flags: [
|
|
697
|
+
["--url, --email, --phone <v>", "the fields with obvious names", ""],
|
|
698
|
+
["--<a>.<b> <value>", "any dotted path — --contact.telephone, --billing.po", ""],
|
|
699
|
+
["--payee <chain:address>", "settlement address for this client", ""],
|
|
700
|
+
["--json", "machine-readable", ""],
|
|
701
|
+
],
|
|
702
|
+
examples: [
|
|
703
|
+
['moshcode client create "Acme Inc", https://acme.com, +1-555-0100', "one line, three fields"],
|
|
704
|
+
["moshcode client create globex --contact.name Jane --contact.telephone +1-555-0200", "nested fields"],
|
|
705
|
+
["moshcode client payee acme solana:9xQe…", "so an invoice has somewhere to settle"],
|
|
706
|
+
],
|
|
707
|
+
seeAlso: ["rate", "billing", "team"],
|
|
708
|
+
note: "`business`, `merchant` and `customer` are the same command — one room, three doors.",
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
name: "team",
|
|
712
|
+
group: "business",
|
|
713
|
+
description: "who may do what on this machine",
|
|
714
|
+
synopsis: [
|
|
715
|
+
["moshcode team create <name>", ""],
|
|
716
|
+
["moshcode team add <team> <handle> [--role …]", "owner, admin, member or client"],
|
|
717
|
+
["moshcode team grant <team> <handle> <permission…>", "tools:coinpay, agents:*, billing:read"],
|
|
718
|
+
["moshcode team can <team>/<handle> <permission>", "answer it without running anything"],
|
|
719
|
+
],
|
|
720
|
+
verbs: "TEAM_VERBS",
|
|
721
|
+
flags: [
|
|
722
|
+
["--role <name>", "owner, admin, member or client", "member"],
|
|
723
|
+
["--rate <spec>", "what this person costs, as a rate", ""],
|
|
724
|
+
["--grant <a,b>", "permissions at the same time as the invite", ""],
|
|
725
|
+
["--email, --name <v>", "how to reach them", ""],
|
|
726
|
+
["--json", "machine-readable", ""],
|
|
727
|
+
],
|
|
728
|
+
examples: [
|
|
729
|
+
["moshcode team create Profullstack", ""],
|
|
730
|
+
["moshcode team add profullstack preshy --role member --rate $80/hour", ""],
|
|
731
|
+
["moshcode team grant profullstack preshy tools:coinpay", "one tool, not the rest"],
|
|
732
|
+
["moshcode team whoami", "who this pit is acting as"],
|
|
733
|
+
],
|
|
734
|
+
seeAlso: ["client", "rate"],
|
|
735
|
+
note: "a pit gates itself only when MOSHCODE_MEMBER=<team>/<handle> is set; with it unset the owner "
|
|
736
|
+
+ "is at the keyboard and nothing is checked. This is a guardrail against the wrong command, not a "
|
|
737
|
+
+ "security boundary — anyone who can type /team can also edit ~/.moshcode/business.json.",
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
name: "rate",
|
|
741
|
+
group: "business",
|
|
742
|
+
description: "what an hour of agent time costs",
|
|
743
|
+
synopsis: [
|
|
744
|
+
["moshcode rate set <client|default> <spec>", "$100/hour/agent/upto:4"],
|
|
745
|
+
["moshcode rate [list] | show <client> | rm <client>", ""],
|
|
746
|
+
],
|
|
747
|
+
verbs: "RATE_VERBS",
|
|
748
|
+
flags: [
|
|
749
|
+
["--prefer <a,b>", "settlement currencies you would rather have", ""],
|
|
750
|
+
["--accept <a,b>", "what you will take as well", ""],
|
|
751
|
+
["--json", "machine-readable", ""],
|
|
752
|
+
],
|
|
753
|
+
examples: [
|
|
754
|
+
["moshcode rate set default $100/hour/agent/upto:4", "four agents cost four hundred; six also cost four hundred"],
|
|
755
|
+
["moshcode rate set acme 0.5 SOL/day --prefer SOL,USDC --accept fiat", ""],
|
|
756
|
+
["moshcode rate set acme $5000/project", "a flat fee, added once per invoice"],
|
|
757
|
+
],
|
|
758
|
+
seeAlso: ["billing", "timer", "client"],
|
|
759
|
+
note: "spec grammar: <price>/<period>/<unit>[/upto:N][/min:N]. Periods are hour, day, week, month, "
|
|
760
|
+
+ "project or task; units are agent, seat, person or team. Order after the price does not matter.",
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
name: "billing",
|
|
764
|
+
group: "business",
|
|
765
|
+
description: "turn tracked time into an invoice",
|
|
766
|
+
synopsis: [
|
|
767
|
+
["moshcode billing <client>", "a preview — writes nothing"],
|
|
768
|
+
["moshcode billing <client> --mark", "claim the time and record the invoice"],
|
|
769
|
+
["moshcode billing <client> --send [--yes]", "hand it to the connected gateway"],
|
|
770
|
+
["moshcode billing list | show <id> | void <id>", ""],
|
|
771
|
+
],
|
|
772
|
+
verbs: "BILLING_VERBS",
|
|
773
|
+
flags: [
|
|
774
|
+
["--mark", "mark the entries billed and record an invoice", ""],
|
|
775
|
+
["--send", "claim the time and compose the gateway command; --yes runs it", ""],
|
|
776
|
+
["--yes", "run the gateway command instead of printing it", ""],
|
|
777
|
+
["--all", "include time already billed", ""],
|
|
778
|
+
["--today, --week, --month, --since <date>", "window the time", "everything unbilled"],
|
|
779
|
+
["--gateway <name>", "override the default rail", ""],
|
|
780
|
+
["--due <YYYY-MM-DD>", "due date to put on the invoice", ""],
|
|
781
|
+
["--json", "machine-readable", ""],
|
|
782
|
+
],
|
|
783
|
+
examples: [
|
|
784
|
+
["moshcode billing acme", "what you would invoice, and from which entries"],
|
|
785
|
+
["moshcode billing acme --month --mark", "close the month"],
|
|
786
|
+
["moshcode billing acme --send", "print the CoinPay command line for review"],
|
|
787
|
+
["moshcode billing void inv-abc123", "un-claim the time; the record stays"],
|
|
788
|
+
],
|
|
789
|
+
seeAlso: ["timer", "rate", "payments"],
|
|
790
|
+
note: "an invoice never settles to a guess: with no client payee and no wallet rail it refuses. "
|
|
791
|
+
+ "`--send` implies `--mark`. moshcode composes; CoinPay (or Stripe, or a wallet) delivers — and a "
|
|
792
|
+
+ "rate priced in SOL or BTC is refused rather than converted, because a CoinPay invoice carries a "
|
|
793
|
+
+ "fiat amount and nobody computed that number.",
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
name: "payments",
|
|
797
|
+
group: "business",
|
|
798
|
+
description: "the rail invoices go out on",
|
|
799
|
+
synopsis: [
|
|
800
|
+
["moshcode payments [list]", "gateways, and which one is chosen"],
|
|
801
|
+
["moshcode payments connect <gateway>", "coinpay, stripe, paypal, coinbase, wallet"],
|
|
802
|
+
["moshcode payments default <gateway> | disconnect <gateway>", ""],
|
|
803
|
+
],
|
|
804
|
+
verbs: "PAYMENT_VERBS",
|
|
805
|
+
flags: [
|
|
806
|
+
["--chain <name>", "for a bare wallet: solana, ethereum, …", ""],
|
|
807
|
+
["--address <addr>", "for a bare wallet: where money lands", ""],
|
|
808
|
+
["--vault <name>", "for an OAuth gateway: which vault holds the keys", ""],
|
|
809
|
+
["--json", "machine-readable", ""],
|
|
810
|
+
],
|
|
811
|
+
examples: [
|
|
812
|
+
["moshcode payments connect coinpay", "runs `coinpay login` — the CLI keeps the credential"],
|
|
813
|
+
["moshcode payments connect wallet --chain solana --address 9xQe…", "no gateway at all"],
|
|
814
|
+
["moshcode payments", "what is connected, and what settles in what"],
|
|
815
|
+
],
|
|
816
|
+
seeAlso: ["billing", "client"],
|
|
817
|
+
note: "no secret is stored here. A CLI gateway holds its own session; an OAuth gateway gets a "
|
|
818
|
+
+ "reference to the vault its keys live in (`moshcode tools secrets`), never the keys.",
|
|
819
|
+
},
|
|
820
|
+
{ name: "business", aliasOf: "client", description: "alias for client" },
|
|
821
|
+
{ name: "merchant", aliasOf: "client", description: "alias for client" },
|
|
822
|
+
{ name: "customer", aliasOf: "client", description: "alias for client" },
|
|
823
|
+
{ name: "teams", aliasOf: "team", description: "alias for team" },
|
|
824
|
+
{ name: "rates", aliasOf: "rate", description: "alias for rate" },
|
|
825
|
+
{ name: "invoice", aliasOf: "billing", description: "alias for billing" },
|
|
652
826
|
{
|
|
653
827
|
name: "plugin",
|
|
654
828
|
group: "extend",
|
|
@@ -1138,8 +1312,67 @@ export const HERD_VERBS = [
|
|
|
1138
1312
|
+ "CI has to tell a worse agent from a broken box." },
|
|
1139
1313
|
];
|
|
1140
1314
|
|
|
1315
|
+
// The business layer's verbs. Flatter than the herd's on purpose: these are
|
|
1316
|
+
// commands somebody types between other work, and a verb that needs a paragraph
|
|
1317
|
+
// to explain itself is a verb in the wrong place.
|
|
1318
|
+
export const TIMER_VERBS = [
|
|
1319
|
+
{ name: "on", description: "start the clock", synopsis: [["moshcode timer on [client] [--task …] [--agents N|auto]", ""]] },
|
|
1320
|
+
{ name: "off", description: "stop it and write the entry", synopsis: [["moshcode timer off [--note …]", ""]] },
|
|
1321
|
+
{ name: "switch", description: "stop one and start another in a breath", synopsis: [["moshcode timer switch <client>", ""]] },
|
|
1322
|
+
{ name: "status", description: "what is running, and what it is worth so far", synopsis: [["moshcode timer status [--json]", ""]] },
|
|
1323
|
+
{ name: "log", description: "the entries behind an invoice", synopsis: [["moshcode timer log [--client <id>] [--week] [--unbilled] [--json]", ""]] },
|
|
1324
|
+
{ name: "add", description: "log time you forgot to track", synopsis: [["moshcode timer add <client> <2h30m> [--task …] [--at <date>]", ""]] },
|
|
1325
|
+
{ name: "rm", description: "drop an entry", synopsis: [["moshcode timer rm <id>", ""]] },
|
|
1326
|
+
];
|
|
1327
|
+
|
|
1328
|
+
export const CLIENT_VERBS = [
|
|
1329
|
+
{ name: "create", description: "add one", synopsis: [['moshcode client create "Acme Inc", https://acme.com, +1-555-0100', ""]] },
|
|
1330
|
+
{ name: "list", description: "all of them", synopsis: [["moshcode client list [--json]", ""]] },
|
|
1331
|
+
{ name: "show", description: "one in full", synopsis: [["moshcode client show <id> [--json]", ""]] },
|
|
1332
|
+
{ name: "set", description: "change a field", synopsis: [["moshcode client set <id> --url https://… --contact.telephone …", ""]] },
|
|
1333
|
+
{ name: "payee", description: "where their payments land", synopsis: [["moshcode client payee <id> <chain:address>", ""]] },
|
|
1334
|
+
{ name: "rm", description: "forget one (tracked time is kept)", synopsis: [["moshcode client rm <id>", ""]] },
|
|
1335
|
+
];
|
|
1336
|
+
|
|
1337
|
+
export const TEAM_VERBS = [
|
|
1338
|
+
{ name: "create", description: "start a team", synopsis: [["moshcode team create <name> [--client <id>]", ""]] },
|
|
1339
|
+
{ name: "add", description: "put somebody on it", synopsis: [["moshcode team add <team> <handle> [--role …] [--rate …]", ""]] },
|
|
1340
|
+
{ name: "grant", description: "widen what they may do", synopsis: [["moshcode team grant <team> <handle> <permission…>", ""]] },
|
|
1341
|
+
{ name: "revoke", description: "narrow it again", synopsis: [["moshcode team revoke <team> <handle> <permission…>", ""]] },
|
|
1342
|
+
{ name: "show", description: "the roster and what each may do", synopsis: [["moshcode team show <team> [--json]", ""]] },
|
|
1343
|
+
{ name: "can", description: "answer a permission question without running anything", synopsis: [["moshcode team can <team>/<handle> <permission>", ""]] },
|
|
1344
|
+
{ name: "whoami", description: "who this pit is acting as", synopsis: [["moshcode team whoami", ""]] },
|
|
1345
|
+
{ name: "rm", description: "drop a member, or the whole team", synopsis: [["moshcode team rm <team> [handle]", ""]] },
|
|
1346
|
+
];
|
|
1347
|
+
|
|
1348
|
+
export const RATE_VERBS = [
|
|
1349
|
+
{ name: "set", description: "write a rate down", synopsis: [["moshcode rate set <client|default> <spec> [--prefer SOL,USDC]", ""]] },
|
|
1350
|
+
{ name: "list", description: "every rate you have set", synopsis: [["moshcode rate list [--json]", ""]] },
|
|
1351
|
+
{ name: "show", description: "the one that applies to a client", synopsis: [["moshcode rate show <client>", ""]] },
|
|
1352
|
+
{ name: "rm", description: "drop one", synopsis: [["moshcode rate rm <client|default>", ""]] },
|
|
1353
|
+
];
|
|
1354
|
+
|
|
1355
|
+
export const BILLING_VERBS = [
|
|
1356
|
+
{ name: "list", description: "invoices you have recorded", synopsis: [["moshcode billing list [--json]", ""]] },
|
|
1357
|
+
{ name: "show", description: "one invoice, with its lines", synopsis: [["moshcode billing show <id> [--json]", ""]] },
|
|
1358
|
+
{ name: "void", description: "un-claim the time; the record stays", synopsis: [["moshcode billing void <id>", ""]] },
|
|
1359
|
+
];
|
|
1360
|
+
|
|
1361
|
+
export const PAYMENT_VERBS = [
|
|
1362
|
+
{ name: "list", description: "gateways, and which one is chosen", synopsis: [["moshcode payments list [--json]", ""]] },
|
|
1363
|
+
{ name: "connect", description: "choose a rail and log into it", synopsis: [["moshcode payments connect <gateway> [--chain … --address …] [--vault …]", ""]] },
|
|
1364
|
+
{ name: "default", description: "which rail invoices go out on", synopsis: [["moshcode payments default <gateway>", ""]] },
|
|
1365
|
+
{ name: "disconnect", description: "forget a rail (the CLI stays logged in)", synopsis: [["moshcode payments disconnect <gateway>", ""]] },
|
|
1366
|
+
];
|
|
1367
|
+
|
|
1141
1368
|
export const VERB_TABLES = {
|
|
1142
1369
|
HERD_VERBS,
|
|
1370
|
+
TIMER_VERBS,
|
|
1371
|
+
CLIENT_VERBS,
|
|
1372
|
+
TEAM_VERBS,
|
|
1373
|
+
RATE_VERBS,
|
|
1374
|
+
BILLING_VERBS,
|
|
1375
|
+
PAYMENT_VERBS,
|
|
1143
1376
|
MCP_VERBS,
|
|
1144
1377
|
SKILL_VERBS,
|
|
1145
1378
|
UPGRADE_TARGETS,
|
|
@@ -1197,6 +1430,18 @@ export const PIT_COMMANDS = [
|
|
|
1197
1430
|
description: "headlines from your feeds, or a search" },
|
|
1198
1431
|
{ name: "rss", aliases: ["reader"], cli: "rss",
|
|
1199
1432
|
description: "read the same headlines in a full-screen reader" },
|
|
1433
|
+
{ name: "timer", args: "on|off|status|log|add|rm [args…]", cli: "timer",
|
|
1434
|
+
description: "track time — on, off, and what it added up to" },
|
|
1435
|
+
{ name: "client", aliases: ["business", "merchant", "customer"], args: "<verb> [args…]", cli: "client",
|
|
1436
|
+
description: "who the work is for" },
|
|
1437
|
+
{ name: "team", aliases: ["teams"], args: "<verb> [args…]", cli: "team",
|
|
1438
|
+
description: "who may do what on this machine" },
|
|
1439
|
+
{ name: "rate", aliases: ["rates"], args: "set <client|default> <spec>", cli: "rate",
|
|
1440
|
+
description: "what an hour of agent time costs" },
|
|
1441
|
+
{ name: "billing", aliases: ["invoice"], args: "<client> [--mark] [--send]", cli: "billing",
|
|
1442
|
+
description: "turn tracked time into an invoice" },
|
|
1443
|
+
{ name: "payments", args: "[connect <gateway>]", cli: "payments",
|
|
1444
|
+
description: "the rail invoices go out on" },
|
|
1200
1445
|
{ name: "plugin", aliases: ["plugins"], args: "<verb> [name]", cli: "plugin",
|
|
1201
1446
|
description: "install moshcode's slash commands into Claude Code" },
|
|
1202
1447
|
{ name: "games", aliases: ["game", "arcade", "play"], args: "[game]", cli: "games",
|