@remits/remits-cli 0.1.76 → 0.1.79
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 +9 -2
- package/index.js +269 -12
- package/package.json +1 -1
- package/skills/remits-cli/SKILL.md +211 -26
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ remits-cli install --skills
|
|
|
21
21
|
remits-cli tools
|
|
22
22
|
remits-cli tool --base-url http://localhost:8080 --name "My Tool" --input '{"foo":"bar"}'
|
|
23
23
|
remits-cli components stage
|
|
24
|
+
remits-cli components status
|
|
25
|
+
remits-cli components clear
|
|
24
26
|
remits-cli test run --test 45
|
|
25
27
|
remits-cli test run --test "My New Test" --names "test case 1,test case 2"
|
|
26
28
|
git add -A
|
|
@@ -51,13 +53,17 @@ remits-cli install --skills --overwrite true
|
|
|
51
53
|
|
|
52
54
|
## How It Works
|
|
53
55
|
|
|
54
|
-
- `components stage` uploads the current working tree into the staging cache used by test-mode execution.
|
|
55
|
-
- `components
|
|
56
|
+
- `components stage` uploads the current working tree into the staging cache used by test-mode execution. It replaces the branch/user staging scope with the current manifest, so stale aliases from prior stages are removed.
|
|
57
|
+
- `components status` shows the branch/user staging entries that can shadow DB components during CLI-scoped test-mode execution.
|
|
58
|
+
- `components clear` clears staged entries. Scope it with `--component-type` and/or `--component-id`. Component ids are type-local, so an id alone clears that one component when the id is staged in only one family; if the same id is staged across multiple families it returns an ambiguity error asking you to add `--component-type`. With no filter it clears every staged entry for the current branch; pass `--all` to force the full-branch wipe explicitly.
|
|
59
|
+
- `components stage`, `components status`, and `components clear` print concise summaries by default. Add `--json` or `--verbose` to print the full server response.
|
|
60
|
+
- `components sync` performs a server-side sync from the git remote into the Remits platform for the selected branch. It does not run local git commands. After a successful sync, the platform clears the branch/user staging scope so staged aliases cannot keep shadowing the newly synced DB rows.
|
|
56
61
|
- `components commit` is a convenience wrapper that performs local git commit/push, then `components sync`, then local `git fetch`/`git pull --ff-only`.
|
|
57
62
|
- `components sync` returns the post-sync branch SHA produced by the platform. `components commit` verifies that `origin/<branch>` and local `HEAD` both match that exact SHA after the final pull.
|
|
58
63
|
- `components push` is deprecated and currently behaves the same as `components stage`.
|
|
59
64
|
- `accountId` resolution for CLI commands: explicit `--account-id` flag wins, then the current repo's `account-info.json`, then the active session. For `remits-cli tool` calls the server applies a further precedence — explicit `--account-id` > `input.accountId` > session/repo default — so a tool can execute against a different account than the surrounding repo.
|
|
60
65
|
- Auth sessions are stored per `accountId + dataMode + baseUrl`, so the same account can stay authenticated against both localhost and production without overwriting the other session.
|
|
66
|
+
- `--base-url` and `--data-mode` are independent. `--base-url` chooses the Remits host (`http://localhost:8080` vs deployed prod), while `--data-mode` chooses the data segment on that host (`test` vs `prod`). Do not assume `--data-mode prod` means the deployed prod host, or that `--data-mode test` means localhost.
|
|
61
67
|
- `remits-cli start` scans the machine for `account-info.json` files and rebuilds `~/.remits-cli/account-repos.json` before bringing up the background service.
|
|
62
68
|
- Front-stage guides are **not** bundled with the CLI npm package. For account-work commands (`components`, `test`, `token`, `tools`, `tool`) and on `auth`, `remits-cli` downloads the latest guides from the authenticated `/cli/guides` endpoint and writes them into the current account repo: root `platform-overview.md` / `development-guide.md`, `guides/*.md`, `guides/features/*.md`, and the agent-guidance files `CLAUDE.md` / `AGENTS.md` / `GEMINI.md` (from `docs/guides/remits-components-zip-readme.md`).
|
|
63
69
|
- Guide sync requires authentication. If no valid session exists and the terminal is interactive, the CLI auto-authenticates first; in non-interactive contexts (CI, agent sandboxes) it skips silently so guides are only ever delivered to users who can authenticate to the platform.
|
|
@@ -66,6 +72,7 @@ remits-cli install --skills --overwrite true
|
|
|
66
72
|
- Authenticated users also get the **core Remits platform repo** locally. After guide sync (and on `auth` / discovery), `remits-cli` first reuses any existing local copy it already knows about, can detect directly (`REMITS_PLATFORM_DIR`, the running CLI source in dev, `~/remits`), or can discover under the configured scan roots; only if none is found does it clone `git@github.com:tmillhouse/remits.git` (default `~/remits`, override with `REMITS_PLATFORM_DIR`; cloning only runs in an interactive terminal). On each authenticated run it also fast-forwards the repo (`git pull --ff-only`, once per process) so back-stage analysis runs against current code — skipped automatically if the working tree is dirty, so local work is never clobbered. The repo is tracked in `~/.remits-cli/account-repos.json` under the reserved `platform` entry so agents can analyze back-stage seams and open a fix PR when a front-stage failure turns out to be platform brittleness.
|
|
67
73
|
- Branch defaults to the current local git branch.
|
|
68
74
|
- Data mode defaults to `test`. For `remits-cli test run`, that default is now enforced even if the most-recent authenticated session for the account is `prod`; a production test run therefore requires an explicit `--data-mode prod` on the command line. Use `remits-cli data-mode set prod` only for production investigation.
|
|
75
|
+
- If the same account is authenticated against more than one host and you omit `--base-url`, the CLI auto-resolves the best matching session and now prints the resolved host. Pass `--base-url` explicitly whenever the target host matters.
|
|
69
76
|
- Avoid commas in individual test names. The `--names` filter is comma-delimited, so a single test case whose name contains commas cannot be targeted cleanly through `remits-cli test run --names ...`.
|
|
70
77
|
- Test activity is streamed from websocket `TestSuite` events while final status is also polled from `/cli/test`.
|
|
71
78
|
- Tool execution writes the full response to a separate file so large payloads do not bloat the session log.
|
package/index.js
CHANGED
|
@@ -92,6 +92,10 @@ function parseArgs(argv) {
|
|
|
92
92
|
return out;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
function flagEnabled(value) {
|
|
96
|
+
return value === true || value === 'true' || value === '1' || value === 'yes';
|
|
97
|
+
}
|
|
98
|
+
|
|
95
99
|
function ensureSessionDir() {
|
|
96
100
|
if (!fs.existsSync(SESSION_DIR)) {
|
|
97
101
|
fs.mkdirSync(SESSION_DIR, { recursive: true });
|
|
@@ -217,6 +221,30 @@ function readSession(match = {}, options = {}) {
|
|
|
217
221
|
return candidates[0] || null;
|
|
218
222
|
}
|
|
219
223
|
|
|
224
|
+
function buildSessionResolutionWarning(accountId, requestedBaseUrl, session) {
|
|
225
|
+
if (requestedBaseUrl || !accountId || !session) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const entries = listSessionsFromStore(readAllSessions());
|
|
230
|
+
const accountSessions = entries.filter((entry) => Number(entry.accountId) === Number(accountId));
|
|
231
|
+
const distinctBaseUrls = [...new Set(
|
|
232
|
+
accountSessions
|
|
233
|
+
.map((entry) => normalizeBaseUrl(entry.baseUrl || DEFAULT_BASE_URL))
|
|
234
|
+
.filter(Boolean)
|
|
235
|
+
)];
|
|
236
|
+
|
|
237
|
+
if (distinctBaseUrls.length <= 1) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const resolvedBaseUrl = normalizeBaseUrl(session.baseUrl || DEFAULT_BASE_URL);
|
|
242
|
+
const resolvedDataMode = normalizeDataMode(session.dataMode);
|
|
243
|
+
return 'Multiple authenticated base URLs exist for account ' + accountId +
|
|
244
|
+
' (' + distinctBaseUrls.join(', ') + '). Resolved this command to ' + resolvedBaseUrl +
|
|
245
|
+
' using dataMode ' + resolvedDataMode + '. Pass --base-url to target a different host.';
|
|
246
|
+
}
|
|
247
|
+
|
|
220
248
|
function writeSession(accountId, data, options = {}) {
|
|
221
249
|
const sessions = readAllSessions();
|
|
222
250
|
const session = {
|
|
@@ -230,6 +258,16 @@ function writeSession(accountId, data, options = {}) {
|
|
|
230
258
|
writeAllSessions(sessions);
|
|
231
259
|
}
|
|
232
260
|
|
|
261
|
+
function printSessionResolutionWarning(sessionContext) {
|
|
262
|
+
if (sessionContext && sessionContext.sessionResolutionWarning) {
|
|
263
|
+
console.error('Warning:', sessionContext.sessionResolutionWarning);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function printResolvedBaseUrl(baseUrl) {
|
|
268
|
+
console.log('Base URL:', normalizeBaseUrl(baseUrl || DEFAULT_BASE_URL));
|
|
269
|
+
}
|
|
270
|
+
|
|
233
271
|
function readConfig() {
|
|
234
272
|
if (!fs.existsSync(CONFIG_FILE)) {
|
|
235
273
|
return {};
|
|
@@ -1047,7 +1085,14 @@ function resolveSessionContext(cwd, flags) {
|
|
|
1047
1085
|
accountId = session.accountId;
|
|
1048
1086
|
}
|
|
1049
1087
|
|
|
1050
|
-
|
|
1088
|
+
const sessionResolutionWarning = buildSessionResolutionWarning(accountId, requestedBaseUrl, session);
|
|
1089
|
+
|
|
1090
|
+
return {
|
|
1091
|
+
session,
|
|
1092
|
+
accountId,
|
|
1093
|
+
resolvedBaseUrl: normalizeBaseUrl(session.baseUrl || requestedBaseUrl || DEFAULT_BASE_URL),
|
|
1094
|
+
sessionResolutionWarning
|
|
1095
|
+
};
|
|
1051
1096
|
}
|
|
1052
1097
|
|
|
1053
1098
|
function collectComponents(cwd) {
|
|
@@ -1514,14 +1559,17 @@ async function authCommand(flags) {
|
|
|
1514
1559
|
console.log('Data mode:', session.dataMode);
|
|
1515
1560
|
|
|
1516
1561
|
try {
|
|
1562
|
+
const branchName = currentBranch(cwd);
|
|
1517
1563
|
const toolsData = await loggedPost(api, cwd, '/cli/tools', {
|
|
1518
1564
|
token: session.token,
|
|
1519
1565
|
accountId: session.accountId,
|
|
1566
|
+
branchName,
|
|
1520
1567
|
dataMode: session.dataMode
|
|
1521
1568
|
}).then((r) => r.data);
|
|
1522
1569
|
if (toolsData && toolsData.success) {
|
|
1523
1570
|
const toolsFile = saveToolsSnapshot(cwd, {
|
|
1524
1571
|
accountId: session.accountId,
|
|
1572
|
+
branchName,
|
|
1525
1573
|
dataMode: toolsData.dataMode || session.dataMode,
|
|
1526
1574
|
fetchedAt: new Date().toISOString(),
|
|
1527
1575
|
tools: toolsData.tools || []
|
|
@@ -1567,7 +1615,8 @@ async function authCommand(flags) {
|
|
|
1567
1615
|
async function pushComponentsCommand(flags) {
|
|
1568
1616
|
const cwd = process.cwd();
|
|
1569
1617
|
ensureLocalState(cwd);
|
|
1570
|
-
const
|
|
1618
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
1619
|
+
const { session, accountId } = sessionContext;
|
|
1571
1620
|
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
1572
1621
|
const branchName = flags.branch || currentBranch(cwd);
|
|
1573
1622
|
const dataMode = resolveDataMode(flags, session);
|
|
@@ -1586,18 +1635,178 @@ async function pushComponentsCommand(flags) {
|
|
|
1586
1635
|
branchName,
|
|
1587
1636
|
dataMode,
|
|
1588
1637
|
mode,
|
|
1638
|
+
replace: true,
|
|
1589
1639
|
components
|
|
1590
1640
|
}).then((r) => r.data);
|
|
1591
1641
|
|
|
1642
|
+
if (flagEnabled(flags.json)) {
|
|
1643
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1644
|
+
return response;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
printSessionResolutionWarning(sessionContext);
|
|
1648
|
+
printResolvedBaseUrl(baseUrl);
|
|
1592
1649
|
console.log('Data mode:', response.dataMode || dataMode);
|
|
1593
1650
|
console.log('Mode:', response.mode || mode);
|
|
1594
|
-
|
|
1651
|
+
printStageSummary(response, flags);
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
function shouldPrintFullComponentResponse(flags) {
|
|
1655
|
+
return flagEnabled(flags.verbose);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
function printFullResponseHint() {
|
|
1659
|
+
console.log('Full response: use --json or --verbose');
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
function componentTypeCounts(entries) {
|
|
1663
|
+
const counts = {};
|
|
1664
|
+
for (const entry of entries || []) {
|
|
1665
|
+
const type = entry.componentType || entry.type || 'Unknown';
|
|
1666
|
+
counts[type] = (counts[type] || 0) + 1;
|
|
1667
|
+
}
|
|
1668
|
+
return counts;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
function printComponentTypeCounts(entries) {
|
|
1672
|
+
const counts = componentTypeCounts(entries);
|
|
1673
|
+
const summary = Object.keys(counts)
|
|
1674
|
+
.sort()
|
|
1675
|
+
.map((type) => type + ':' + counts[type])
|
|
1676
|
+
.join(', ');
|
|
1677
|
+
if (summary) {
|
|
1678
|
+
console.log('By type:', summary);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
function printComponentCommandResponse(label, response, flags) {
|
|
1683
|
+
if (shouldPrintFullComponentResponse(flags)) {
|
|
1684
|
+
console.log(label + ':', JSON.stringify(response, null, 2));
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
printFullResponseHint();
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
function printStageSummary(response, flags) {
|
|
1691
|
+
console.log('Updated:', response.updated || 0);
|
|
1692
|
+
console.log('Unchanged:', response.unchanged || 0);
|
|
1693
|
+
console.log('Skipped:', Array.isArray(response.skipped) ? response.skipped.length : 0);
|
|
1694
|
+
if (response.reconcile) {
|
|
1695
|
+
console.log('Reconciled stale keys:', response.reconcile.removedCount || 0);
|
|
1696
|
+
}
|
|
1697
|
+
if (response.staging) {
|
|
1698
|
+
console.log('Remaining staged count:', response.staging.remainingCount || 0);
|
|
1699
|
+
}
|
|
1700
|
+
printComponentCommandResponse('Components stage', response, flags);
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
function printStatusSummary(response, flags) {
|
|
1704
|
+
console.log('Staged count:', response.stagedCount || 0);
|
|
1705
|
+
printComponentTypeCounts(response.entries || []);
|
|
1706
|
+
printComponentCommandResponse('Components staging', response, flags);
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
function printClearSummary(response, flags) {
|
|
1710
|
+
console.log('Cleared keys:', response.clearedCount || 0);
|
|
1711
|
+
console.log('Remaining staged count:', response.remainingCount || 0);
|
|
1712
|
+
if (Array.isArray(response.clearedKeys) && response.clearedKeys.length && response.clearedKeys.length <= 10) {
|
|
1713
|
+
console.log('Cleared:', response.clearedKeys.join(', '));
|
|
1714
|
+
}
|
|
1715
|
+
printComponentCommandResponse('Components clear', response, flags);
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
async function statusComponentsCommand(flags) {
|
|
1719
|
+
const cwd = process.cwd();
|
|
1720
|
+
ensureLocalState(cwd);
|
|
1721
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
1722
|
+
const { session, accountId } = sessionContext;
|
|
1723
|
+
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
1724
|
+
const branchName = flags.branch || currentBranch(cwd);
|
|
1725
|
+
const dataMode = resolveDataMode(flags, session);
|
|
1726
|
+
const api = buildAxios(baseUrl, session.token);
|
|
1727
|
+
|
|
1728
|
+
const response = await loggedPost(api, cwd, '/cli/components', {
|
|
1729
|
+
token: session.token,
|
|
1730
|
+
accountId,
|
|
1731
|
+
branchName,
|
|
1732
|
+
dataMode,
|
|
1733
|
+
mode: 'status',
|
|
1734
|
+
componentType: flags['component-type'] || flags.type,
|
|
1735
|
+
componentId: flags['component-id'] || flags.id
|
|
1736
|
+
}).then((r) => r.data);
|
|
1737
|
+
|
|
1738
|
+
if (!response.success) {
|
|
1739
|
+
throw new Error(response.message || 'Staging status failed');
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
if (flagEnabled(flags.json)) {
|
|
1743
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1744
|
+
return response;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
printSessionResolutionWarning(sessionContext);
|
|
1748
|
+
printResolvedBaseUrl(baseUrl);
|
|
1749
|
+
console.log('Data mode:', response.dataMode || dataMode);
|
|
1750
|
+
console.log('Mode:', response.mode || 'status');
|
|
1751
|
+
printStatusSummary(response, flags);
|
|
1752
|
+
return response;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
async function clearComponentsCommand(flags) {
|
|
1756
|
+
const cwd = process.cwd();
|
|
1757
|
+
ensureLocalState(cwd);
|
|
1758
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
1759
|
+
const { session, accountId } = sessionContext;
|
|
1760
|
+
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
1761
|
+
const branchName = flags.branch || currentBranch(cwd);
|
|
1762
|
+
const dataMode = resolveDataMode(flags, session);
|
|
1763
|
+
const api = buildAxios(baseUrl, session.token);
|
|
1764
|
+
const componentType = flags['component-type'] || flags.type;
|
|
1765
|
+
const componentId = flags['component-id'] || flags.id;
|
|
1766
|
+
|
|
1767
|
+
// A clear is SCOPED when a type and/or id is given. Component ids are type-local, so an id alone can
|
|
1768
|
+
// match more than one family; the server clears it only when unambiguous and otherwise returns an
|
|
1769
|
+
// ambiguity error asking for --component-type (pass the type to target exactly one). Only escalate to
|
|
1770
|
+
// clearAll (the whole branch/user scope) when NO filter is supplied — never because a filter was
|
|
1771
|
+
// partial. Passing `--all` forces the full-scope wipe.
|
|
1772
|
+
const hasType = Boolean(componentType && String(componentType).trim());
|
|
1773
|
+
const hasId = componentId !== undefined && componentId !== null && String(componentId).trim() !== '';
|
|
1774
|
+
const scoped = hasType || hasId;
|
|
1775
|
+
const clearAll = flagEnabled(flags.all) || flagEnabled(flags['clear-all']) || !scoped;
|
|
1776
|
+
|
|
1777
|
+
const response = await loggedPost(api, cwd, '/cli/components', {
|
|
1778
|
+
token: session.token,
|
|
1779
|
+
accountId,
|
|
1780
|
+
branchName,
|
|
1781
|
+
dataMode,
|
|
1782
|
+
mode: 'clear',
|
|
1783
|
+
clearAll,
|
|
1784
|
+
componentType,
|
|
1785
|
+
componentId
|
|
1786
|
+
}).then((r) => r.data);
|
|
1787
|
+
|
|
1788
|
+
if (!response.success) {
|
|
1789
|
+
throw new Error(response.message || 'Staging clear failed');
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
if (flagEnabled(flags.json)) {
|
|
1793
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1794
|
+
return response;
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
printSessionResolutionWarning(sessionContext);
|
|
1798
|
+
printResolvedBaseUrl(baseUrl);
|
|
1799
|
+
console.log('Data mode:', response.dataMode || dataMode);
|
|
1800
|
+
console.log('Mode:', response.mode || 'clear');
|
|
1801
|
+
printClearSummary(response, flags);
|
|
1802
|
+
return response;
|
|
1595
1803
|
}
|
|
1596
1804
|
|
|
1597
1805
|
async function syncComponentsCommand(flags) {
|
|
1598
1806
|
const cwd = process.cwd();
|
|
1599
1807
|
ensureLocalState(cwd);
|
|
1600
|
-
const
|
|
1808
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
1809
|
+
const { session, accountId } = sessionContext;
|
|
1601
1810
|
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
1602
1811
|
const branchName = flags.branch || currentBranch(cwd);
|
|
1603
1812
|
const dataMode = resolveDataMode(flags, session);
|
|
@@ -1616,6 +1825,13 @@ async function syncComponentsCommand(flags) {
|
|
|
1616
1825
|
throw new Error(response.message || 'Server sync failed');
|
|
1617
1826
|
}
|
|
1618
1827
|
|
|
1828
|
+
if (flagEnabled(flags.json)) {
|
|
1829
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1830
|
+
return response;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
printSessionResolutionWarning(sessionContext);
|
|
1834
|
+
printResolvedBaseUrl(baseUrl);
|
|
1619
1835
|
console.log('Data mode:', response.dataMode || dataMode);
|
|
1620
1836
|
console.log('Mode:', response.mode || 'sync');
|
|
1621
1837
|
console.log('Components sync:', JSON.stringify(response, null, 2));
|
|
@@ -1755,7 +1971,8 @@ function watchWebsocket(baseUrl, topicId, taskId) {
|
|
|
1755
1971
|
async function testCommand(flags) {
|
|
1756
1972
|
const cwd = process.cwd();
|
|
1757
1973
|
ensureLocalState(cwd);
|
|
1758
|
-
const
|
|
1974
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
1975
|
+
const { session, accountId } = sessionContext;
|
|
1759
1976
|
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
1760
1977
|
const branchName = flags.branch || currentBranch(cwd);
|
|
1761
1978
|
// Test runs must default to test dataMode even when the most-recent authenticated
|
|
@@ -1787,6 +2004,8 @@ async function testCommand(flags) {
|
|
|
1787
2004
|
throw new Error('Failed to start test run');
|
|
1788
2005
|
}
|
|
1789
2006
|
|
|
2007
|
+
printSessionResolutionWarning(sessionContext);
|
|
2008
|
+
printResolvedBaseUrl(baseUrl);
|
|
1790
2009
|
console.log('Test run started:', start.taskId);
|
|
1791
2010
|
runtimeState.currentTestTaskId = start.taskId;
|
|
1792
2011
|
|
|
@@ -1817,7 +2036,8 @@ async function testCommand(flags) {
|
|
|
1817
2036
|
async function tokenCommand(flags) {
|
|
1818
2037
|
const cwd = process.cwd();
|
|
1819
2038
|
ensureLocalState(cwd);
|
|
1820
|
-
const
|
|
2039
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
2040
|
+
const { session, accountId } = sessionContext;
|
|
1821
2041
|
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
1822
2042
|
const branchName = flags.branch || currentBranch(cwd);
|
|
1823
2043
|
const dataMode = resolveDataMode(flags, session);
|
|
@@ -1842,6 +2062,7 @@ async function tokenCommand(flags) {
|
|
|
1842
2062
|
|
|
1843
2063
|
const output = {
|
|
1844
2064
|
success: true,
|
|
2065
|
+
baseUrl: normalizeBaseUrl(baseUrl),
|
|
1845
2066
|
tokenKey: data.tokenKey,
|
|
1846
2067
|
accountId: data.accountId,
|
|
1847
2068
|
branchName: data.branchName,
|
|
@@ -1850,20 +2071,24 @@ async function tokenCommand(flags) {
|
|
|
1850
2071
|
embeddableUrl
|
|
1851
2072
|
};
|
|
1852
2073
|
|
|
2074
|
+
printSessionResolutionWarning(sessionContext);
|
|
1853
2075
|
console.log(JSON.stringify(output, null, 2));
|
|
1854
2076
|
}
|
|
1855
2077
|
|
|
1856
2078
|
async function toolsCommand(flags) {
|
|
1857
2079
|
const cwd = process.cwd();
|
|
1858
2080
|
ensureLocalState(cwd);
|
|
1859
|
-
const
|
|
2081
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
2082
|
+
const { session, accountId } = sessionContext;
|
|
1860
2083
|
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
2084
|
+
const branchName = flags.branch || currentBranch(cwd);
|
|
1861
2085
|
const dataMode = resolveDataMode(flags, session);
|
|
1862
2086
|
const api = buildAxios(baseUrl, session.token);
|
|
1863
2087
|
|
|
1864
2088
|
const data = await loggedPost(api, cwd, '/cli/tools', {
|
|
1865
2089
|
token: session.token,
|
|
1866
2090
|
accountId,
|
|
2091
|
+
branchName,
|
|
1867
2092
|
dataMode
|
|
1868
2093
|
}).then((r) => r.data);
|
|
1869
2094
|
|
|
@@ -1873,11 +2098,15 @@ async function toolsCommand(flags) {
|
|
|
1873
2098
|
|
|
1874
2099
|
const toolsFile = saveToolsSnapshot(cwd, {
|
|
1875
2100
|
accountId,
|
|
2101
|
+
baseUrl: normalizeBaseUrl(baseUrl),
|
|
2102
|
+
branchName,
|
|
1876
2103
|
dataMode: data.dataMode || dataMode,
|
|
1877
2104
|
fetchedAt: new Date().toISOString(),
|
|
1878
2105
|
tools: data.tools || []
|
|
1879
2106
|
});
|
|
1880
2107
|
|
|
2108
|
+
printSessionResolutionWarning(sessionContext);
|
|
2109
|
+
printResolvedBaseUrl(baseUrl);
|
|
1881
2110
|
console.log('Tools refreshed.');
|
|
1882
2111
|
console.log('Data mode:', data.dataMode || dataMode);
|
|
1883
2112
|
console.log('Tools file:', toolsFile);
|
|
@@ -1898,7 +2127,8 @@ function parseToolInput(flags) {
|
|
|
1898
2127
|
async function toolCommand(flags) {
|
|
1899
2128
|
const cwd = process.cwd();
|
|
1900
2129
|
ensureLocalState(cwd);
|
|
1901
|
-
const
|
|
2130
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
2131
|
+
const { session, accountId } = sessionContext;
|
|
1902
2132
|
|
|
1903
2133
|
const toolName = flags.name || flags.tool;
|
|
1904
2134
|
if (!toolName) {
|
|
@@ -1933,6 +2163,8 @@ async function toolCommand(flags) {
|
|
|
1933
2163
|
throw new Error(data.message || 'Tool execution failed');
|
|
1934
2164
|
}
|
|
1935
2165
|
|
|
2166
|
+
printSessionResolutionWarning(sessionContext);
|
|
2167
|
+
printResolvedBaseUrl(baseUrl);
|
|
1936
2168
|
console.log('Tool call succeeded.');
|
|
1937
2169
|
console.log('Call ID:', callId);
|
|
1938
2170
|
console.log('Data mode:', dataMode);
|
|
@@ -4201,6 +4433,7 @@ function releaseAutoUpdateLock() {
|
|
|
4201
4433
|
function shouldAutoUpdate(command, flags) {
|
|
4202
4434
|
if (!command || command === 'help' || command === '--help') return false;
|
|
4203
4435
|
if (flags && flags['no-auto-update']) return false;
|
|
4436
|
+
if (flags && flagEnabled(flags.json)) return false;
|
|
4204
4437
|
if (process.env.REMITS_CLI_AUTO_UPDATE && AUTO_UPDATE_DISABLED_VALUES.has(String(process.env.REMITS_CLI_AUTO_UPDATE).toLowerCase())) {
|
|
4205
4438
|
return false;
|
|
4206
4439
|
}
|
|
@@ -4270,6 +4503,8 @@ async function main() {
|
|
|
4270
4503
|
const originalArgv = process.argv.slice(2);
|
|
4271
4504
|
const args = parseArgs(originalArgv);
|
|
4272
4505
|
const [command, subcommand] = args._;
|
|
4506
|
+
const wantsHelp = args.help === true || subcommand === 'help' || subcommand === '--help' || args._.includes('--help');
|
|
4507
|
+
const wantsJson = flagEnabled(args.json);
|
|
4273
4508
|
|
|
4274
4509
|
if (shouldAutoUpdate(command, args)) {
|
|
4275
4510
|
const requireSuccessfulAutoUpdate = command === 'start';
|
|
@@ -4282,8 +4517,8 @@ async function main() {
|
|
|
4282
4517
|
// Auto-start the background service if not already running.
|
|
4283
4518
|
// Skip for lifecycle subcommands and help.
|
|
4284
4519
|
const isLifecycleCmd = command === 'listen' || command === 'start' || command === 'stop' || command === 'status';
|
|
4285
|
-
const isHelpCmd = !command || command === 'help' || command === '--help';
|
|
4286
|
-
if (!isHelpCmd && !isLifecycleCmd && !isListenerRunning()) {
|
|
4520
|
+
const isHelpCmd = !command || command === 'help' || command === '--help' || wantsHelp;
|
|
4521
|
+
if (!wantsJson && !isHelpCmd && !isLifecycleCmd && !isListenerRunning()) {
|
|
4287
4522
|
try {
|
|
4288
4523
|
const dashboardUrl = await autoStartServiceIfNeeded();
|
|
4289
4524
|
if (dashboardUrl) {
|
|
@@ -4298,7 +4533,7 @@ async function main() {
|
|
|
4298
4533
|
// only platform-authenticated users ever receive them. `auth` syncs its own
|
|
4299
4534
|
// guides, so it is excluded here.
|
|
4300
4535
|
const GUIDE_SYNC_COMMANDS = new Set(['components', 'test', 'token', 'tools', 'tool']);
|
|
4301
|
-
if (GUIDE_SYNC_COMMANDS.has(command)) {
|
|
4536
|
+
if (!wantsJson && !isHelpCmd && GUIDE_SYNC_COMMANDS.has(command)) {
|
|
4302
4537
|
try { await ensureGuidesSynced(process.cwd(), args); } catch (_) { /* best-effort */ }
|
|
4303
4538
|
}
|
|
4304
4539
|
|
|
@@ -4316,7 +4551,9 @@ async function main() {
|
|
|
4316
4551
|
console.log(' remits-cli install --skills [--target codex|claude|gemini|all] [--overwrite true]');
|
|
4317
4552
|
console.log(' remits-cli tools [--base-url URL] [--account-id ID] [--data-mode test|prod]');
|
|
4318
4553
|
console.log(' remits-cli tool --name <toolName> [--base-url URL] [--input \"{...}\"|--input-file file.json] [--data-mode test|prod]');
|
|
4319
|
-
console.log(' remits-cli components stage [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod]');
|
|
4554
|
+
console.log(' remits-cli components stage [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod] [--json|--verbose]');
|
|
4555
|
+
console.log(' remits-cli components status [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE --component-id ID] [--json|--verbose]');
|
|
4556
|
+
console.log(' remits-cli components clear [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE] [--component-id ID] [--all] [--json|--verbose]');
|
|
4320
4557
|
console.log(' remits-cli components sync [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod]');
|
|
4321
4558
|
console.log(' remits-cli components commit [--message \"msg\"] [--allow-empty true|false] [--skip-git true|false] [--branch BRANCH] [--data-mode test|prod]');
|
|
4322
4559
|
console.log(' remits-cli test run --test <id|name> [--base-url URL] [--names name1,name2] [--watch true|false] [--data-mode test|prod]');
|
|
@@ -4324,6 +4561,16 @@ async function main() {
|
|
|
4324
4561
|
process.exit(0);
|
|
4325
4562
|
}
|
|
4326
4563
|
|
|
4564
|
+
if (command === 'components' && wantsHelp) {
|
|
4565
|
+
console.log('Usage: remits-cli components <stage|status|clear|sync|commit>');
|
|
4566
|
+
console.log(' remits-cli components stage [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod] [--json|--verbose]');
|
|
4567
|
+
console.log(' remits-cli components status [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE --component-id ID] [--json|--verbose]');
|
|
4568
|
+
console.log(' remits-cli components clear [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE] [--component-id ID] [--all] [--json|--verbose]');
|
|
4569
|
+
console.log(' remits-cli components sync [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod]');
|
|
4570
|
+
console.log(' remits-cli components commit [--message \"msg\"] [--allow-empty true|false] [--skip-git true|false] [--branch BRANCH] [--data-mode test|prod]');
|
|
4571
|
+
process.exit(0);
|
|
4572
|
+
}
|
|
4573
|
+
|
|
4327
4574
|
if (command === 'auth') {
|
|
4328
4575
|
await authCommand(args);
|
|
4329
4576
|
return;
|
|
@@ -4337,6 +4584,16 @@ async function main() {
|
|
|
4337
4584
|
return;
|
|
4338
4585
|
}
|
|
4339
4586
|
|
|
4587
|
+
if (command === 'components' && subcommand === 'status') {
|
|
4588
|
+
await statusComponentsCommand(args);
|
|
4589
|
+
return;
|
|
4590
|
+
}
|
|
4591
|
+
|
|
4592
|
+
if (command === 'components' && subcommand === 'clear') {
|
|
4593
|
+
await clearComponentsCommand(args);
|
|
4594
|
+
return;
|
|
4595
|
+
}
|
|
4596
|
+
|
|
4340
4597
|
if (command === 'components' && subcommand === 'sync') {
|
|
4341
4598
|
await syncComponentsCommand(args);
|
|
4342
4599
|
return;
|
package/package.json
CHANGED
|
@@ -30,6 +30,113 @@ Apply these rules before making remote tool calls:
|
|
|
30
30
|
|
|
31
31
|
Use `remits-cli` for everything else: staging changes, running tests, generating embeddable tokens, committing work, and diagnosing production issues.
|
|
32
32
|
|
|
33
|
+
## Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
|
|
34
|
+
|
|
35
|
+
Component source-of-truth mistakes are the highest-impact failure in remits-cli. A repo/DB mismatch can
|
|
36
|
+
hard-delete live components, spawn duplicates, renumber files, or leave the database and repo describing
|
|
37
|
+
different implementations. These rules override the normal fast loop whenever they conflict.
|
|
38
|
+
|
|
39
|
+
### How the platform reconciles the repo into the database (the mechanism you must understand)
|
|
40
|
+
|
|
41
|
+
`remits-cli components sync` (and the sync phase of `components commit`) calls
|
|
42
|
+
`GitHubClient.syncFromRepository`. It is a **full two-way reconcile in which the GitHub remote is authoritative
|
|
43
|
+
over the database.** It reads the **remote repo ZIP — not your local working tree** — and:
|
|
44
|
+
|
|
45
|
+
- **Match / update:** each component is matched to a DB row by the **numeric id prefix of its files**
|
|
46
|
+
(`58_x.groovy` → component 58), not by name. Matching files overwrite that component's DB fields
|
|
47
|
+
(source/schema/html/etc.).
|
|
48
|
+
- **Create:** a file whose id prefix is **not** a live component on the account — including any `new_*` file —
|
|
49
|
+
is created as a **brand-new DB row with a fresh server-assigned id**, and the platform renames the repo files
|
|
50
|
+
to that id (the `Rename X→Y after component creation` / `Delete old file` commits).
|
|
51
|
+
- **Delete:** after the create/update pass, **any live DB component whose id has no matching repo file is
|
|
52
|
+
hard-deleted** (`deleteMissingComponentsFor`), in reverse-dependency order. This runs only when the ZIP
|
|
53
|
+
"looks healthy" (contains `account-info.json` or `README.md`). Exempt from deletion: `auxiliary` components,
|
|
54
|
+
README-purpose prompts, and AGENT-purpose prompts.
|
|
55
|
+
|
|
56
|
+
**The single most important consequence:** the id in a component's **filename is load-bearing**. If a file's id
|
|
57
|
+
no longer matches its DB row (a rename/renumber), the next sync will **create a duplicate at the new id and
|
|
58
|
+
hard-delete the original at the old id**. If a component's files are missing from the repo at sync time, that
|
|
59
|
+
component is **hard-deleted from the DB**. This is exactly how a prior session deleted live schemas and an
|
|
60
|
+
embeddable.
|
|
61
|
+
|
|
62
|
+
**Therefore: never renumber, rename-across-ids, or remove component files as a side effect.** Before any sync,
|
|
63
|
+
the repo must already mirror the live DB: every live component present at its real id, and nothing extra.
|
|
64
|
+
|
|
65
|
+
### The surfaces and their intended behavior
|
|
66
|
+
|
|
67
|
+
| Command / tool | What it touches | Danger |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. | none |
|
|
70
|
+
| `mcp_component_edit` `mode:'stage'` | Redis staging cache for one component/field. | none |
|
|
71
|
+
| `mcp_component_edit` `mode:'commit'` / `mcp_component_commit` `commit` | Writes the field(s) to the **live DB** for that exact existing component and clears its staging. Does not create/delete other components. | low (scoped to one known component) |
|
|
72
|
+
| `remits-cli components sync` | **Server-side git→DB reconcile of the whole account** (create/update/**delete**/rename). Reads the pushed remote; ignores local files. | **high** |
|
|
73
|
+
| `remits-cli components commit` | **One shot:** `git add -A` + commit + `git push` + **`components sync`** + `git pull --ff-only`. Blindly stages the *entire* working tree (including any drift) and reconciles it into prod. | **highest** |
|
|
74
|
+
| `mcp_component_create` | Writes a **new** component row **directly to the DB** (immediate, new id). | medium (can create duplicates) |
|
|
75
|
+
|
|
76
|
+
Key implications:
|
|
77
|
+
- **`stage` is always safe** — stage and test as much as you want; it never reconciles or deletes.
|
|
78
|
+
- **`components commit` is the most dangerous command**, not a mere convenience wrapper: it `git add -A`
|
|
79
|
+
commits and pushes whatever is in the working tree, then immediately syncs. Never run it while the tree
|
|
80
|
+
contains drift or unexplained changes. Prefer the explicit, observable
|
|
81
|
+
`git commit → git push → components sync → git pull` sequence so each phase can be inspected.
|
|
82
|
+
- **`components sync` acts on the pushed remote**, so local edits are invisible to it until committed **and
|
|
83
|
+
pushed**, and a drifted **remote** is dangerous even when your local tree looks fine.
|
|
84
|
+
- After a successful `components sync` / `components commit`, the server clears the full branch/user staging
|
|
85
|
+
scope. This is the expected clean state: old Redis aliases should not keep shadowing the newly synced DB rows.
|
|
86
|
+
|
|
87
|
+
### Intended workflows
|
|
88
|
+
|
|
89
|
+
**Change existing components (normal path):**
|
|
90
|
+
1. Edit files under `components/` **keeping each component's existing numeric id** (use `new_*` only for
|
|
91
|
+
genuinely new components).
|
|
92
|
+
2. `remits-cli components stage` → verify in test mode. Iterate (edit → stage → run).
|
|
93
|
+
3. When ready to promote: pass the pre-sync safety check below, then
|
|
94
|
+
`git add -A && git commit && git push`, `remits-cli components sync`, `git pull --ff-only`.
|
|
95
|
+
|
|
96
|
+
**Create a new component:** add `new_Name.groovy` (+ `.json` / `.meta.yml` as applicable). Sync assigns the
|
|
97
|
+
durable id and renames the files. `mcp_component_create` is the direct-DB alternative for no-repo or
|
|
98
|
+
remote-account work — do not use it to work around an id/name mismatch, and never to replace a component that
|
|
99
|
+
was unexpectedly deleted or renumbered.
|
|
100
|
+
|
|
101
|
+
**Delete a component (deliberate only):** remove **all** of that component's files from the repo, confirm via
|
|
102
|
+
`git status` that only those files are gone, then sync — the delete phase removes exactly that DB row. Deletion
|
|
103
|
+
is a real, supported outcome of a missing file, which is precisely why an *accidentally* missing or renamed
|
|
104
|
+
file is catastrophic.
|
|
105
|
+
|
|
106
|
+
### Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
|
|
107
|
+
|
|
108
|
+
- The user intends durable platform promotion now — not just local edits, staging, or verification.
|
|
109
|
+
- `git status --short` shows only intended changes; every rename/delete is explained. **No component file has
|
|
110
|
+
been renumbered to a different id.**
|
|
111
|
+
- Local branch is committed and pushed; sync will read the intended remote commit.
|
|
112
|
+
- Local filenames and live inventory (`mcp_account_view`) **agree on id and name for every component**: no live
|
|
113
|
+
component appears locally under a different id, and no expected component is missing a repo file.
|
|
114
|
+
- You can state the expected create/update/delete set. **If any delete or renumber is unexpected, stop.**
|
|
115
|
+
|
|
116
|
+
### If something looks wrong — stop, don't paper over
|
|
117
|
+
|
|
118
|
+
If sync reports unexpected `deleted` / `created` / `renamed`, uniqueness errors, or missing components — or you
|
|
119
|
+
discover id drift — **stop. Do not re-run sync, do not `components commit`, and do not use
|
|
120
|
+
`mcp_component_create` to "make ids line up" or to replace a deleted component.** Those actions compound the
|
|
121
|
+
corruption. Preserve the repo-local session log and tool responses, and reconcile source-of-truth first.
|
|
122
|
+
|
|
123
|
+
**Safe recovery pattern (repo ↔ DB drift):**
|
|
124
|
+
1. Establish DB truth: `mcp_account_view` for the full live inventory; `mcp_component_view` to confirm and read
|
|
125
|
+
exact sources.
|
|
126
|
+
2. Fix the **local tree to mirror the live DB** — rename component files back to their real DB ids, reassemble
|
|
127
|
+
any split components, remove orphan/duplicate files, and **refresh any local source that differs from the
|
|
128
|
+
live DB** (the DB is the running truth; a stale local file would overwrite good DB source on sync).
|
|
129
|
+
3. Keep files for any components that were wrongly deleted so sync **re-creates** them (new ids are fine —
|
|
130
|
+
schemas are keyed by title, agents link tools by name).
|
|
131
|
+
4. Make the remote authoritative **non-destructively**: commit the corrected tree, then
|
|
132
|
+
`git merge -s ours origin/<branch>` (keeps your tree, supersedes drifted remote history) and a fast-forward
|
|
133
|
+
`git push` — no force-push.
|
|
134
|
+
5. Run **one** `components sync`: it updates everything to identical, creates the missing components, and
|
|
135
|
+
deletes nothing. Verify with `mcp_account_view`.
|
|
136
|
+
|
|
137
|
+
If the mismatch is a genuine platform/tooling defect (not agent drift), follow the Back-Stage Escalation
|
|
138
|
+
Workflow instead of improvising.
|
|
139
|
+
|
|
33
140
|
## Required Local Index Reads
|
|
34
141
|
|
|
35
142
|
These files are decision inputs. Read them when the related decision depends on them.
|
|
@@ -411,6 +518,25 @@ remits-cli auth --account-id <ACCOUNT_ID> --base-url http://localhost:8080
|
|
|
411
518
|
- `--base-url` targets a specific platform instance (e.g., localhost for development). Sessions are stored per account + base URL + data mode, so authenticating against localhost does not overwrite a production session for the same account.
|
|
412
519
|
- If any command returns a 401 error, re-run `remits-cli auth` (with the same `--base-url` if you were targeting a non-default instance).
|
|
413
520
|
|
|
521
|
+
### Host vs Data Mode
|
|
522
|
+
|
|
523
|
+
Treat host selection and data mode as two separate decisions:
|
|
524
|
+
|
|
525
|
+
- `--base-url` chooses the Remits host: localhost vs a deployed environment.
|
|
526
|
+
- `--data-mode` chooses the data segment on that host: `test` vs `prod`.
|
|
527
|
+
|
|
528
|
+
Examples:
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
# Deployed prod host, but test data segment
|
|
532
|
+
remits-cli tools --base-url https://your-prod-host --data-mode test
|
|
533
|
+
|
|
534
|
+
# Localhost host, but prod data segment on that localhost instance
|
|
535
|
+
remits-cli tool --base-url http://localhost:8080 --name mcp_account_view --data-mode prod
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Do not assume `--data-mode prod` implies the deployed prod host, or that `--data-mode test` implies localhost. If host matters, read `~/.remits-cli/sessions.json` first and pass `--base-url` explicitly.
|
|
539
|
+
|
|
414
540
|
### Data Mode
|
|
415
541
|
|
|
416
542
|
Controls whether you work with test data or production data. **Default is `test`.**
|
|
@@ -534,29 +660,41 @@ Before committing, update metadata so the next session understands what changed:
|
|
|
534
660
|
|
|
535
661
|
`account-info.json` is read-only — never edit it. It regenerates automatically after sync.
|
|
536
662
|
|
|
537
|
-
#### Step 7: Commit and Sync
|
|
663
|
+
#### Step 7: Commit and Durable Sync
|
|
538
664
|
|
|
539
|
-
Once verified and documented:
|
|
665
|
+
Once verified and documented, create a normal git commit first:
|
|
540
666
|
|
|
541
667
|
```bash
|
|
542
668
|
git add -A
|
|
543
669
|
git commit -m "description of what changed and why"
|
|
544
670
|
git push origin <branch>
|
|
545
|
-
remits-cli components sync
|
|
546
|
-
git pull --ff-only origin <branch>
|
|
547
671
|
```
|
|
548
672
|
|
|
549
|
-
This separates the failure boundaries cleanly:
|
|
673
|
+
This separates the local failure boundaries cleanly:
|
|
550
674
|
1. Local git commit
|
|
551
675
|
2. Remote push
|
|
552
|
-
3. Server sync from the git remote
|
|
553
|
-
4. Local fast-forward pull of the exact platform-generated commit, including updates such as `account-info.json`
|
|
554
676
|
|
|
555
|
-
|
|
677
|
+
After the push, run the **Component Integrity Rules** safety checks before any durable platform sync. Do not run
|
|
678
|
+
`remits-cli components sync` when local files, `account-info.json`, and live inventory disagree about component
|
|
679
|
+
IDs or when unexpected deletes/renumbers are present.
|
|
680
|
+
|
|
681
|
+
Only after those checks pass, and only when the user intends to promote the repo to the platform database:
|
|
682
|
+
|
|
683
|
+
```bash
|
|
684
|
+
remits-cli components sync
|
|
685
|
+
git pull --ff-only origin <branch>
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
`remits-cli components sync` is the authoritative platform-sync step. It does not perform local git operations,
|
|
689
|
+
and it is capable of reconciling creates/deletes/renames from the remote repository into the database. Treat it
|
|
690
|
+
as a gated promote/reconciliation command, not as an exploratory command or fallback.
|
|
556
691
|
|
|
557
692
|
The CLI now returns the post-sync branch SHA from the platform and verifies that your `git fetch` and final `git pull --ff-only` land on that exact commit. If that SHA does not match `origin/<branch>` or local `HEAD`, stop immediately and investigate the race or branch drift instead of guessing.
|
|
558
693
|
|
|
559
|
-
`remits-cli components commit` still exists as a convenience wrapper, but agents should
|
|
694
|
+
`remits-cli components commit` still exists as a convenience wrapper, but agents should not call unsupported
|
|
695
|
+
subcommand help variants such as `remits-cli components commit --help` to discover behavior. Consult this skill,
|
|
696
|
+
the CLI source, or `remits-cli components` documentation instead. If an exploratory or commit command behaves
|
|
697
|
+
unexpectedly, stop and inspect the repo-local session log before running any mutating follow-up command.
|
|
560
698
|
|
|
561
699
|
**Git is required for durable sync.** The platform syncs by pulling from the git remote (`GitHubClient.syncFromRepository`). If `git push` fails, the server has nothing new to sync. You can still **stage** and **test** without git — only durable sync requires it.
|
|
562
700
|
|
|
@@ -620,7 +758,8 @@ component's OWN type enum such as `ObjectType`/`RuleType`, or absent — **never
|
|
|
620
758
|
At compile time the platform computes a **signature** that tells you which layer won:
|
|
621
759
|
|
|
622
760
|
- Staged override present → `compileSignature = "cli:<hash>"` (the staged content hash).
|
|
623
|
-
- No staged override → `compileSignature = "version:<N>"` (the DB row version
|
|
761
|
+
- No staged override → `compileSignature = "version:<N>:<sourceHash12>"` (the DB row version plus a source hash
|
|
762
|
+
prefix, so source changes cannot reuse a stale compile entry on the same instance).
|
|
624
763
|
|
|
625
764
|
That signature is logged. Querying for it is the single most reliable way to know what ran:
|
|
626
765
|
|
|
@@ -629,15 +768,31 @@ remits-cli tool --name mcp_system_logs --input '{"node":"remitsAdmin-east5","tim
|
|
|
629
768
|
```
|
|
630
769
|
|
|
631
770
|
`Using Cached BCD [ID: 230, Type: Action, Signature: cli:08cc...]` → ran a **staged** override.
|
|
632
|
-
`...Signature: version:37]` → ran the **committed DB** version.
|
|
771
|
+
`...Signature: version:37:abc123def456]` → ran the **committed DB** version.
|
|
633
772
|
|
|
634
773
|
### When staged overrides apply
|
|
635
774
|
|
|
636
|
-
Staged overrides resolve
|
|
637
|
-
and `TestMode.cliUserId` are set
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
775
|
+
Staged overrides resolve whenever the execution carries a **CLI-scoped TestMode** — i.e.
|
|
776
|
+
`TestMode.branchName` and `TestMode.cliUserId` are set. That includes:
|
|
777
|
+
|
|
778
|
+
- `remits-cli test run`
|
|
779
|
+
- `remits-cli tool`
|
|
780
|
+
- `remits-cli tools`
|
|
781
|
+
- `mcp_run_test`
|
|
782
|
+
- tokenized runs minted with a branch-aware CLI token
|
|
783
|
+
|
|
784
|
+
For `remits-cli tool`, the CLI TestMode now stays active for the **entire tool execution**, not just the
|
|
785
|
+
top-level tool lookup. That means nested `reader()`, `action()`, `utility()`, `account.getTool()`, and
|
|
786
|
+
similar component resolution inside the tool also see the staged branch context.
|
|
787
|
+
|
|
788
|
+
`dataMode` is separate from staged resolution:
|
|
789
|
+
|
|
790
|
+
- `branchName` + `cliUserId` decide whether staged components can resolve.
|
|
791
|
+
- `dataMode:test|prod` decides which data surface the tool/test/token runs against.
|
|
792
|
+
|
|
793
|
+
So a `remits-cli tool --data-mode prod` call can intentionally execute **staged code against prod data**
|
|
794
|
+
for investigation or recall testing, while a normal live webhook / non-CLI runtime path with no CLI
|
|
795
|
+
TestMode still uses the committed DB source. Staging remains a dev/verification surface, not a deploy.
|
|
641
796
|
|
|
642
797
|
### Diagnosing which version is in play
|
|
643
798
|
|
|
@@ -655,6 +810,10 @@ surface, not a deploy.
|
|
|
655
810
|
`No enum constant ObjectType.reader`).
|
|
656
811
|
- **Confirm the DB version:** `mcp_component_view` reads the live DB source directly (no staging, no compile
|
|
657
812
|
cache), so it is the source of truth for "what was committed."
|
|
813
|
+
- **Ask the CLI what is staged:** `remits-cli components status` lists the current branch/user staged entries,
|
|
814
|
+
including staged fields, aliases, hashes, and TTLs. The default terminal output is concise; pass `--json` or
|
|
815
|
+
`--verbose` when you need the full staged-entry payload. `remits-cli components clear` removes those entries
|
|
816
|
+
when you intentionally want to fall back to DB source.
|
|
658
817
|
|
|
659
818
|
### Stage / commit / clear with the MCP tools
|
|
660
819
|
|
|
@@ -676,7 +835,9 @@ surface, not a deploy.
|
|
|
676
835
|
later test runs.
|
|
677
836
|
- `remits-cli components sync` (the `commit`/`sync` mode on the `components` endpoint) syncs the DB from the
|
|
678
837
|
git remote and then **also clears the staged entries** for the synced components, so a clean commit leaves a
|
|
679
|
-
clean staging cache on both the CLI and MCP-tool surfaces.
|
|
838
|
+
clean staging cache on both the CLI and MCP-tool surfaces. Because sync reconciles the remote repo into the
|
|
839
|
+
live component database, it must pass the Component Integrity Rules first. Never use sync to clear staging, to
|
|
840
|
+
recover from a mismatched ID, or to retry after an unexpected create/delete/rename response.
|
|
680
841
|
|
|
681
842
|
### Stale after sync / commit (the in-memory compile cache)
|
|
682
843
|
|
|
@@ -915,6 +1076,12 @@ Query Cloud Run service logs.
|
|
|
915
1076
|
### `mcp_component_view`
|
|
916
1077
|
Read component field content with line numbers.
|
|
917
1078
|
|
|
1079
|
+
Note: component source-management tools such as `mcp_component_view`, `mcp_component_grep`,
|
|
1080
|
+
`mcp_component_edit`, `mcp_component_create`, and `mcp_component_commit` are primarily for MCP-only
|
|
1081
|
+
clients that do not have a local account repo. In a normal `remits-cli` coding workflow, prefer local
|
|
1082
|
+
repo files plus `remits-cli components stage` / `remits-cli components sync`; if these component tools
|
|
1083
|
+
are absent from `remits-cli tools`, that is expected.
|
|
1084
|
+
|
|
918
1085
|
| Parameter | Required | Description |
|
|
919
1086
|
|-----------|----------|-------------|
|
|
920
1087
|
| `accountId` | yes | Account ID |
|
|
@@ -1010,6 +1177,20 @@ auxiliary components). See "Component Resolution" for stage-vs-DB behavior.
|
|
|
1010
1177
|
| `editMode` | no | `targeted` (default, anchor-verified line edit via `lineStart`/`lineEnd`/`anchorContent`/`newContent`) or `replace` (full-field `newContent`) |
|
|
1011
1178
|
| `auxiliary`/`category`/`partnerSlug` | no | Companion metadata applied alongside the edit |
|
|
1012
1179
|
|
|
1180
|
+
### `mcp_component_create`
|
|
1181
|
+
Create a new component row directly in the database. This is a direct-DB operation, not the normal repo-backed
|
|
1182
|
+
component creation workflow.
|
|
1183
|
+
|
|
1184
|
+
Use it only when:
|
|
1185
|
+
- there is no local account repo available, or
|
|
1186
|
+
- the user explicitly asks for direct remote creation, or
|
|
1187
|
+
- an emergency repair plan intentionally creates a new row after live inventory and git history have been
|
|
1188
|
+
reconciled.
|
|
1189
|
+
|
|
1190
|
+
In a repo-backed account, prefer `new_` component files plus stage/test/git/sync after the Component Integrity
|
|
1191
|
+
Rules pass. Never use `mcp_component_create` to compensate for an unexpected deletion, a renamed numeric file,
|
|
1192
|
+
or a local/live ID mismatch.
|
|
1193
|
+
|
|
1013
1194
|
### `mcp_component_commit`
|
|
1014
1195
|
Inspect, flush, or clear the CLI staging cache for components.
|
|
1015
1196
|
|
|
@@ -1032,7 +1213,7 @@ to remove staged entries).
|
|
|
1032
1213
|
|
|
1033
1214
|
## Multi-Session Support
|
|
1034
1215
|
|
|
1035
|
-
The CLI supports multiple authenticated sessions simultaneously. Sessions are stored in `~/.remits-cli/sessions.json` as a map keyed by `accountId + dataMode + baseUrl`, so the same account can stay authenticated against both localhost and production without one session overwriting the other. When you run any command from an account repo, the CLI automatically resolves the best matching session based on the `account-info.json` in that directory and any `--base-url` or `--data-mode` flags provided.
|
|
1216
|
+
The CLI supports multiple authenticated sessions simultaneously. Sessions are stored in `~/.remits-cli/sessions.json` as a map keyed by `accountId + dataMode + baseUrl`, so the same account can stay authenticated against both localhost and production without one session overwriting the other. When you run any command from an account repo, the CLI automatically resolves the best matching session based on the `account-info.json` in that directory and any `--base-url` or `--data-mode` flags provided. If the same account is authenticated against multiple hosts and you omit `--base-url`, the CLI may legitimately choose either localhost or a deployed host depending on the best session match, so agents should treat `--base-url` as mandatory whenever host matters.
|
|
1036
1217
|
|
|
1037
1218
|
```bash
|
|
1038
1219
|
# Authenticate for an account (run from its repo, or pass --account-id)
|
|
@@ -1191,13 +1372,15 @@ remits-cli stop
|
|
|
1191
1372
|
remits-cli status
|
|
1192
1373
|
remits-cli listen [stop|status] [--foreground true] # compatibility alias
|
|
1193
1374
|
remits-cli data-mode [set test|prod]
|
|
1194
|
-
remits-cli components stage [--branch <name>] [--data-mode test|prod]
|
|
1195
|
-
remits-cli components
|
|
1375
|
+
remits-cli components stage [--branch <name>] [--data-mode test|prod] [--json|--verbose]
|
|
1376
|
+
remits-cli components status [--branch <name>] [--component-type <type>] [--component-id <id>] [--json|--verbose]
|
|
1377
|
+
remits-cli components clear [--branch <name>] [--component-type <type>] [--component-id <id>] [--all] [--json|--verbose] # id alone scopes to one component when unambiguous (ids are type-local; add --component-type if the same id is staged in multiple families); no filter clears the whole branch scope; --all forces the full wipe
|
|
1378
|
+
remits-cli components sync [--branch <name>] [--data-mode test|prod] # gated durable repo->DB reconciliation only
|
|
1196
1379
|
remits-cli components commit [--message "msg"] [--data-mode test|prod]
|
|
1197
1380
|
remits-cli test run --test <id|name> [--names "a,b"] [--watch true|false] [--data-mode test|prod]
|
|
1198
1381
|
remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod]
|
|
1199
|
-
remits-cli tools [--data-mode test|prod]
|
|
1200
|
-
remits-cli tool --name <toolName> [--input "{...}"] [--data-mode test|prod]
|
|
1382
|
+
remits-cli tools [--branch <name>] [--data-mode test|prod]
|
|
1383
|
+
remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod]
|
|
1201
1384
|
```
|
|
1202
1385
|
|
|
1203
1386
|
For tests specifically:
|
|
@@ -1212,15 +1395,17 @@ For tests specifically:
|
|
|
1212
1395
|
| `account-info.json not found` | Run from the account repo root |
|
|
1213
1396
|
| Test not found (404) | New component not staged yet. Run `remits-cli components stage` first. For new tests (no ID), run by name not ID. |
|
|
1214
1397
|
| Stage shows 0 updated | No changes since last stage (hash dedup) |
|
|
1215
|
-
| Test runs old code after edit | You forgot to stage. Run `remits-cli components
|
|
1398
|
+
| Test runs old code after edit | You forgot to stage, or you are looking at DB while a staged entry is still active. Run `remits-cli components status`; then `remits-cli components stage` to update staged code or `remits-cli components clear` to fall back to DB. |
|
|
1216
1399
|
| "Git credentials" or `git push` error | Git auth is required for commit (server pulls from git remote). Fix git authentication (SSH keys or HTTPS credentials) before retrying. Staging and testing still work without git. |
|
|
1400
|
+
| Need to learn command behavior | Read this skill, repo docs, or CLI source. Do not run unsupported mutating subcommand variants such as `remits-cli components commit --help`; if a command unexpectedly mutates or commits, stop and inspect the repo-local session log before doing anything else. |
|
|
1401
|
+
| Sync response reports unexpected deletes, creates, renames, uniqueness errors, or component ID drift | Stop immediately. Do not rerun sync, do not patch around it with `mcp_component_create`, and do not promote more changes. Preserve session logs/tool responses, compare `account-info.json`, local filenames, live inventory, and git history, then prepare a repair/escalation summary. |
|
|
1217
1402
|
| 500 / `Internal Server Error` from Remits | Stop normal task work. Capture the exact command, error, and response artifact, then escalate to a Remits system admin. Do not invent a workaround. |
|
|
1218
1403
|
| Tool parameters rejected | Tool schemas may be cached. Run `remits-cli tools` to refresh `.remits-cli/tools/tools.json` with latest schemas. |
|
|
1219
1404
|
| Tool response missing | Check `./.remits-cli/tool-responses/` |
|
|
1220
|
-
| Staged change has no effect in a live (non-
|
|
1221
|
-
| New source shown by `mcp_component_view` but old behavior persists after sync/commit |
|
|
1405
|
+
| Staged change has no effect in a live (non-CLI) run | Staged overrides resolve only under a CLI TestMode (`branchName`+`cliUserId`). Live webhooks and other non-CLI runtime paths still use the DB. `commit` to make it durable. See "Component Resolution". |
|
|
1406
|
+
| New source shown by `mcp_component_view` but old behavior persists after sync/commit | The compile cache (`CLOSURE_CACHE`) is keyed by `version:<N>:<sourceHash12>`, so a source change on the same version now invalidates it automatically — a run right after sync/commit picks up the new source. If old behavior still persists, confirm the run actually hit the synced instance and that no staged override is still shadowing DB (`remits-cli components status`). |
|
|
1222
1407
|
| Staged Reader test run throws `No enum constant ObjectType.<family>` | The staged entry has the component family in `type` (should be `kind`). Clear + re-stage; if it persists, the `mcp_component_edit` tool on that instance is on an old/cached version. Inspect with `mcp_cache`. |
|
|
1223
|
-
| Unsure whether a run used staged vs DB source | Query `mcp_system_logs` for `Using Cached BCD` — `Signature: cli:<hash>` = staged, `version:<N>` = DB. |
|
|
1408
|
+
| Unsure whether a run used staged vs DB source | Query `mcp_system_logs` for `Using Cached BCD` — `Signature: cli:<hash>` = staged, `version:<N>:<sourceHash12>` = DB. CLI/MCP tool results also report `componentSource` and `componentSignature` when available. |
|
|
1224
1409
|
| Service already running | Run `remits-cli status` to get the dashboard URL, or `remits-cli stop` before restarting. |
|
|
1225
1410
|
| tmux not installed | Install tmux (`brew install tmux` on macOS). Required for agent dispatch. |
|
|
1226
1411
|
| Control center URL unknown | Read `~/.remits-cli/service-state.json` or run `remits-cli status` |
|