@remits/remits-cli 0.1.75 → 0.1.77

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 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,16 @@ 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 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.
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 the branch/user staging scope. Use `--component-type` + `--component-id` to clear one component; omit them to clear all staged entries for the current branch.
59
+ - `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
60
  - `components commit` is a convenience wrapper that performs local git commit/push, then `components sync`, then local `git fetch`/`git pull --ff-only`.
57
61
  - `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
62
  - `components push` is deprecated and currently behaves the same as `components stage`.
59
63
  - `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
64
  - 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.
65
+ - `--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
66
  - `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
67
  - 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
68
  - 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.
@@ -65,7 +70,9 @@ remits-cli install --skills --overwrite true
65
70
  - The platform repo's `docs/guides/` tree remains the single source of truth; the platform serves it from the classpath, so published CLI versions never carry guide content.
66
71
  - 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
72
  - Branch defaults to the current local git branch.
68
- - Data mode defaults to `test`. Use `remits-cli data-mode set prod` only for production investigation.
73
+ - 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.
74
+ - 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.
75
+ - 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 ...`.
69
76
  - Test activity is streamed from websocket `TestSuite` events while final status is also polled from `/cli/test`.
70
77
  - Tool execution writes the full response to a separate file so large payloads do not bloat the session log.
71
78
 
package/index.js CHANGED
@@ -217,6 +217,30 @@ function readSession(match = {}, options = {}) {
217
217
  return candidates[0] || null;
218
218
  }
219
219
 
220
+ function buildSessionResolutionWarning(accountId, requestedBaseUrl, session) {
221
+ if (requestedBaseUrl || !accountId || !session) {
222
+ return null;
223
+ }
224
+
225
+ const entries = listSessionsFromStore(readAllSessions());
226
+ const accountSessions = entries.filter((entry) => Number(entry.accountId) === Number(accountId));
227
+ const distinctBaseUrls = [...new Set(
228
+ accountSessions
229
+ .map((entry) => normalizeBaseUrl(entry.baseUrl || DEFAULT_BASE_URL))
230
+ .filter(Boolean)
231
+ )];
232
+
233
+ if (distinctBaseUrls.length <= 1) {
234
+ return null;
235
+ }
236
+
237
+ const resolvedBaseUrl = normalizeBaseUrl(session.baseUrl || DEFAULT_BASE_URL);
238
+ const resolvedDataMode = normalizeDataMode(session.dataMode);
239
+ return 'Multiple authenticated base URLs exist for account ' + accountId +
240
+ ' (' + distinctBaseUrls.join(', ') + '). Resolved this command to ' + resolvedBaseUrl +
241
+ ' using dataMode ' + resolvedDataMode + '. Pass --base-url to target a different host.';
242
+ }
243
+
220
244
  function writeSession(accountId, data, options = {}) {
221
245
  const sessions = readAllSessions();
222
246
  const session = {
@@ -230,6 +254,16 @@ function writeSession(accountId, data, options = {}) {
230
254
  writeAllSessions(sessions);
231
255
  }
232
256
 
257
+ function printSessionResolutionWarning(sessionContext) {
258
+ if (sessionContext && sessionContext.sessionResolutionWarning) {
259
+ console.error('Warning:', sessionContext.sessionResolutionWarning);
260
+ }
261
+ }
262
+
263
+ function printResolvedBaseUrl(baseUrl) {
264
+ console.log('Base URL:', normalizeBaseUrl(baseUrl || DEFAULT_BASE_URL));
265
+ }
266
+
233
267
  function readConfig() {
234
268
  if (!fs.existsSync(CONFIG_FILE)) {
235
269
  return {};
@@ -1047,7 +1081,14 @@ function resolveSessionContext(cwd, flags) {
1047
1081
  accountId = session.accountId;
1048
1082
  }
1049
1083
 
1050
- return { session, accountId };
1084
+ const sessionResolutionWarning = buildSessionResolutionWarning(accountId, requestedBaseUrl, session);
1085
+
1086
+ return {
1087
+ session,
1088
+ accountId,
1089
+ resolvedBaseUrl: normalizeBaseUrl(session.baseUrl || requestedBaseUrl || DEFAULT_BASE_URL),
1090
+ sessionResolutionWarning
1091
+ };
1051
1092
  }
1052
1093
 
1053
1094
  function collectComponents(cwd) {
@@ -1514,14 +1555,17 @@ async function authCommand(flags) {
1514
1555
  console.log('Data mode:', session.dataMode);
1515
1556
 
1516
1557
  try {
1558
+ const branchName = currentBranch(cwd);
1517
1559
  const toolsData = await loggedPost(api, cwd, '/cli/tools', {
1518
1560
  token: session.token,
1519
1561
  accountId: session.accountId,
1562
+ branchName,
1520
1563
  dataMode: session.dataMode
1521
1564
  }).then((r) => r.data);
1522
1565
  if (toolsData && toolsData.success) {
1523
1566
  const toolsFile = saveToolsSnapshot(cwd, {
1524
1567
  accountId: session.accountId,
1568
+ branchName,
1525
1569
  dataMode: toolsData.dataMode || session.dataMode,
1526
1570
  fetchedAt: new Date().toISOString(),
1527
1571
  tools: toolsData.tools || []
@@ -1567,7 +1611,8 @@ async function authCommand(flags) {
1567
1611
  async function pushComponentsCommand(flags) {
1568
1612
  const cwd = process.cwd();
1569
1613
  ensureLocalState(cwd);
1570
- const { session, accountId } = resolveSessionContext(cwd, flags);
1614
+ const sessionContext = resolveSessionContext(cwd, flags);
1615
+ const { session, accountId } = sessionContext;
1571
1616
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
1572
1617
  const branchName = flags.branch || currentBranch(cwd);
1573
1618
  const dataMode = resolveDataMode(flags, session);
@@ -1586,18 +1631,92 @@ async function pushComponentsCommand(flags) {
1586
1631
  branchName,
1587
1632
  dataMode,
1588
1633
  mode,
1634
+ replace: true,
1589
1635
  components
1590
1636
  }).then((r) => r.data);
1591
1637
 
1638
+ printSessionResolutionWarning(sessionContext);
1639
+ printResolvedBaseUrl(baseUrl);
1592
1640
  console.log('Data mode:', response.dataMode || dataMode);
1593
1641
  console.log('Mode:', response.mode || mode);
1594
1642
  console.log('Components sync:', JSON.stringify(response, null, 2));
1595
1643
  }
1596
1644
 
1645
+ async function statusComponentsCommand(flags) {
1646
+ const cwd = process.cwd();
1647
+ ensureLocalState(cwd);
1648
+ const sessionContext = resolveSessionContext(cwd, flags);
1649
+ const { session, accountId } = sessionContext;
1650
+ const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
1651
+ const branchName = flags.branch || currentBranch(cwd);
1652
+ const dataMode = resolveDataMode(flags, session);
1653
+ const api = buildAxios(baseUrl, session.token);
1654
+
1655
+ const response = await loggedPost(api, cwd, '/cli/components', {
1656
+ token: session.token,
1657
+ accountId,
1658
+ branchName,
1659
+ dataMode,
1660
+ mode: 'status',
1661
+ componentType: flags['component-type'] || flags.type,
1662
+ componentId: flags['component-id'] || flags.id
1663
+ }).then((r) => r.data);
1664
+
1665
+ if (!response.success) {
1666
+ throw new Error(response.message || 'Staging status failed');
1667
+ }
1668
+
1669
+ printSessionResolutionWarning(sessionContext);
1670
+ printResolvedBaseUrl(baseUrl);
1671
+ console.log('Data mode:', response.dataMode || dataMode);
1672
+ console.log('Mode:', response.mode || 'status');
1673
+ console.log('Staged count:', response.stagedCount || 0);
1674
+ console.log('Components staging:', JSON.stringify(response, null, 2));
1675
+ return response;
1676
+ }
1677
+
1678
+ async function clearComponentsCommand(flags) {
1679
+ const cwd = process.cwd();
1680
+ ensureLocalState(cwd);
1681
+ const sessionContext = resolveSessionContext(cwd, flags);
1682
+ const { session, accountId } = sessionContext;
1683
+ const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
1684
+ const branchName = flags.branch || currentBranch(cwd);
1685
+ const dataMode = resolveDataMode(flags, session);
1686
+ const api = buildAxios(baseUrl, session.token);
1687
+ const componentType = flags['component-type'] || flags.type;
1688
+ const componentId = flags['component-id'] || flags.id;
1689
+
1690
+ const response = await loggedPost(api, cwd, '/cli/components', {
1691
+ token: session.token,
1692
+ accountId,
1693
+ branchName,
1694
+ dataMode,
1695
+ mode: 'clear',
1696
+ clearAll: !(componentType && componentId),
1697
+ componentType,
1698
+ componentId
1699
+ }).then((r) => r.data);
1700
+
1701
+ if (!response.success) {
1702
+ throw new Error(response.message || 'Staging clear failed');
1703
+ }
1704
+
1705
+ printSessionResolutionWarning(sessionContext);
1706
+ printResolvedBaseUrl(baseUrl);
1707
+ console.log('Data mode:', response.dataMode || dataMode);
1708
+ console.log('Mode:', response.mode || 'clear');
1709
+ console.log('Cleared count:', response.clearedCount || 0);
1710
+ console.log('Remaining staged count:', response.remainingCount || 0);
1711
+ console.log('Components staging:', JSON.stringify(response, null, 2));
1712
+ return response;
1713
+ }
1714
+
1597
1715
  async function syncComponentsCommand(flags) {
1598
1716
  const cwd = process.cwd();
1599
1717
  ensureLocalState(cwd);
1600
- const { session, accountId } = resolveSessionContext(cwd, flags);
1718
+ const sessionContext = resolveSessionContext(cwd, flags);
1719
+ const { session, accountId } = sessionContext;
1601
1720
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
1602
1721
  const branchName = flags.branch || currentBranch(cwd);
1603
1722
  const dataMode = resolveDataMode(flags, session);
@@ -1616,6 +1735,8 @@ async function syncComponentsCommand(flags) {
1616
1735
  throw new Error(response.message || 'Server sync failed');
1617
1736
  }
1618
1737
 
1738
+ printSessionResolutionWarning(sessionContext);
1739
+ printResolvedBaseUrl(baseUrl);
1619
1740
  console.log('Data mode:', response.dataMode || dataMode);
1620
1741
  console.log('Mode:', response.mode || 'sync');
1621
1742
  console.log('Components sync:', JSON.stringify(response, null, 2));
@@ -1755,10 +1876,16 @@ function watchWebsocket(baseUrl, topicId, taskId) {
1755
1876
  async function testCommand(flags) {
1756
1877
  const cwd = process.cwd();
1757
1878
  ensureLocalState(cwd);
1758
- const { session, accountId } = resolveSessionContext(cwd, flags);
1879
+ const sessionContext = resolveSessionContext(cwd, flags);
1880
+ const { session, accountId } = sessionContext;
1759
1881
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
1760
1882
  const branchName = flags.branch || currentBranch(cwd);
1761
- const dataMode = resolveDataMode(flags, session);
1883
+ // Test runs must default to test dataMode even when the most-recent authenticated
1884
+ // session for the account is prod. Production test runs therefore require an explicit
1885
+ // --data-mode prod on the command line.
1886
+ const dataMode = hasExplicitDataModeFlag(flags)
1887
+ ? resolveDataMode(flags, null)
1888
+ : DEFAULT_DATA_MODE;
1762
1889
  const testRef = flags.test || flags['test-id'] || flags.name;
1763
1890
 
1764
1891
  if (!testRef) {
@@ -1782,6 +1909,8 @@ async function testCommand(flags) {
1782
1909
  throw new Error('Failed to start test run');
1783
1910
  }
1784
1911
 
1912
+ printSessionResolutionWarning(sessionContext);
1913
+ printResolvedBaseUrl(baseUrl);
1785
1914
  console.log('Test run started:', start.taskId);
1786
1915
  runtimeState.currentTestTaskId = start.taskId;
1787
1916
 
@@ -1812,7 +1941,8 @@ async function testCommand(flags) {
1812
1941
  async function tokenCommand(flags) {
1813
1942
  const cwd = process.cwd();
1814
1943
  ensureLocalState(cwd);
1815
- const { session, accountId } = resolveSessionContext(cwd, flags);
1944
+ const sessionContext = resolveSessionContext(cwd, flags);
1945
+ const { session, accountId } = sessionContext;
1816
1946
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
1817
1947
  const branchName = flags.branch || currentBranch(cwd);
1818
1948
  const dataMode = resolveDataMode(flags, session);
@@ -1837,6 +1967,7 @@ async function tokenCommand(flags) {
1837
1967
 
1838
1968
  const output = {
1839
1969
  success: true,
1970
+ baseUrl: normalizeBaseUrl(baseUrl),
1840
1971
  tokenKey: data.tokenKey,
1841
1972
  accountId: data.accountId,
1842
1973
  branchName: data.branchName,
@@ -1845,20 +1976,24 @@ async function tokenCommand(flags) {
1845
1976
  embeddableUrl
1846
1977
  };
1847
1978
 
1979
+ printSessionResolutionWarning(sessionContext);
1848
1980
  console.log(JSON.stringify(output, null, 2));
1849
1981
  }
1850
1982
 
1851
1983
  async function toolsCommand(flags) {
1852
1984
  const cwd = process.cwd();
1853
1985
  ensureLocalState(cwd);
1854
- const { session, accountId } = resolveSessionContext(cwd, flags);
1986
+ const sessionContext = resolveSessionContext(cwd, flags);
1987
+ const { session, accountId } = sessionContext;
1855
1988
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
1989
+ const branchName = flags.branch || currentBranch(cwd);
1856
1990
  const dataMode = resolveDataMode(flags, session);
1857
1991
  const api = buildAxios(baseUrl, session.token);
1858
1992
 
1859
1993
  const data = await loggedPost(api, cwd, '/cli/tools', {
1860
1994
  token: session.token,
1861
1995
  accountId,
1996
+ branchName,
1862
1997
  dataMode
1863
1998
  }).then((r) => r.data);
1864
1999
 
@@ -1868,11 +2003,15 @@ async function toolsCommand(flags) {
1868
2003
 
1869
2004
  const toolsFile = saveToolsSnapshot(cwd, {
1870
2005
  accountId,
2006
+ baseUrl: normalizeBaseUrl(baseUrl),
2007
+ branchName,
1871
2008
  dataMode: data.dataMode || dataMode,
1872
2009
  fetchedAt: new Date().toISOString(),
1873
2010
  tools: data.tools || []
1874
2011
  });
1875
2012
 
2013
+ printSessionResolutionWarning(sessionContext);
2014
+ printResolvedBaseUrl(baseUrl);
1876
2015
  console.log('Tools refreshed.');
1877
2016
  console.log('Data mode:', data.dataMode || dataMode);
1878
2017
  console.log('Tools file:', toolsFile);
@@ -1893,7 +2032,8 @@ function parseToolInput(flags) {
1893
2032
  async function toolCommand(flags) {
1894
2033
  const cwd = process.cwd();
1895
2034
  ensureLocalState(cwd);
1896
- const { session, accountId } = resolveSessionContext(cwd, flags);
2035
+ const sessionContext = resolveSessionContext(cwd, flags);
2036
+ const { session, accountId } = sessionContext;
1897
2037
 
1898
2038
  const toolName = flags.name || flags.tool;
1899
2039
  if (!toolName) {
@@ -1928,6 +2068,8 @@ async function toolCommand(flags) {
1928
2068
  throw new Error(data.message || 'Tool execution failed');
1929
2069
  }
1930
2070
 
2071
+ printSessionResolutionWarning(sessionContext);
2072
+ printResolvedBaseUrl(baseUrl);
1931
2073
  console.log('Tool call succeeded.');
1932
2074
  console.log('Call ID:', callId);
1933
2075
  console.log('Data mode:', dataMode);
@@ -4265,6 +4407,7 @@ async function main() {
4265
4407
  const originalArgv = process.argv.slice(2);
4266
4408
  const args = parseArgs(originalArgv);
4267
4409
  const [command, subcommand] = args._;
4410
+ const wantsHelp = args.help === true || subcommand === 'help' || subcommand === '--help' || args._.includes('--help');
4268
4411
 
4269
4412
  if (shouldAutoUpdate(command, args)) {
4270
4413
  const requireSuccessfulAutoUpdate = command === 'start';
@@ -4277,7 +4420,7 @@ async function main() {
4277
4420
  // Auto-start the background service if not already running.
4278
4421
  // Skip for lifecycle subcommands and help.
4279
4422
  const isLifecycleCmd = command === 'listen' || command === 'start' || command === 'stop' || command === 'status';
4280
- const isHelpCmd = !command || command === 'help' || command === '--help';
4423
+ const isHelpCmd = !command || command === 'help' || command === '--help' || wantsHelp;
4281
4424
  if (!isHelpCmd && !isLifecycleCmd && !isListenerRunning()) {
4282
4425
  try {
4283
4426
  const dashboardUrl = await autoStartServiceIfNeeded();
@@ -4293,7 +4436,7 @@ async function main() {
4293
4436
  // only platform-authenticated users ever receive them. `auth` syncs its own
4294
4437
  // guides, so it is excluded here.
4295
4438
  const GUIDE_SYNC_COMMANDS = new Set(['components', 'test', 'token', 'tools', 'tool']);
4296
- if (GUIDE_SYNC_COMMANDS.has(command)) {
4439
+ if (!isHelpCmd && GUIDE_SYNC_COMMANDS.has(command)) {
4297
4440
  try { await ensureGuidesSynced(process.cwd(), args); } catch (_) { /* best-effort */ }
4298
4441
  }
4299
4442
 
@@ -4312,6 +4455,8 @@ async function main() {
4312
4455
  console.log(' remits-cli tools [--base-url URL] [--account-id ID] [--data-mode test|prod]');
4313
4456
  console.log(' remits-cli tool --name <toolName> [--base-url URL] [--input \"{...}\"|--input-file file.json] [--data-mode test|prod]');
4314
4457
  console.log(' remits-cli components stage [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod]');
4458
+ console.log(' remits-cli components status [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE --component-id ID]');
4459
+ console.log(' remits-cli components clear [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE --component-id ID]');
4315
4460
  console.log(' remits-cli components sync [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod]');
4316
4461
  console.log(' remits-cli components commit [--message \"msg\"] [--allow-empty true|false] [--skip-git true|false] [--branch BRANCH] [--data-mode test|prod]');
4317
4462
  console.log(' remits-cli test run --test <id|name> [--base-url URL] [--names name1,name2] [--watch true|false] [--data-mode test|prod]');
@@ -4319,6 +4464,16 @@ async function main() {
4319
4464
  process.exit(0);
4320
4465
  }
4321
4466
 
4467
+ if (command === 'components' && wantsHelp) {
4468
+ console.log('Usage: remits-cli components <stage|status|clear|sync|commit>');
4469
+ console.log(' remits-cli components stage [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod]');
4470
+ console.log(' remits-cli components status [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE --component-id ID]');
4471
+ console.log(' remits-cli components clear [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE --component-id ID]');
4472
+ console.log(' remits-cli components sync [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod]');
4473
+ console.log(' remits-cli components commit [--message \"msg\"] [--allow-empty true|false] [--skip-git true|false] [--branch BRANCH] [--data-mode test|prod]');
4474
+ process.exit(0);
4475
+ }
4476
+
4322
4477
  if (command === 'auth') {
4323
4478
  await authCommand(args);
4324
4479
  return;
@@ -4332,6 +4487,16 @@ async function main() {
4332
4487
  return;
4333
4488
  }
4334
4489
 
4490
+ if (command === 'components' && subcommand === 'status') {
4491
+ await statusComponentsCommand(args);
4492
+ return;
4493
+ }
4494
+
4495
+ if (command === 'components' && subcommand === 'clear') {
4496
+ await clearComponentsCommand(args);
4497
+ return;
4498
+ }
4499
+
4335
4500
  if (command === 'components' && subcommand === 'sync') {
4336
4501
  await syncComponentsCommand(args);
4337
4502
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remits/remits-cli",
3
- "version": "0.1.75",
3
+ "version": "0.1.77",
4
4
  "description": "Local CLI for auth, component sync, and live test execution against Remits",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -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`.**
@@ -471,6 +597,10 @@ remits-cli test run --test "Invoice Tests" --names "specific test case"
471
597
 
472
598
  Tests run on the platform against your staged snapshot. They stream results in real-time. If they fail, fix the code, re-stage, and re-run.
473
599
 
600
+ Important test-runner constraints:
601
+ - `remits-cli test run` now defaults to `test` dataMode unless you explicitly pass `--data-mode prod`.
602
+ - Do not put commas in individual `test("...")` names. The CLI `--names` filter is comma-delimited, so comma-bearing test names cannot be targeted cleanly as a single selected case.
603
+
474
604
  If no relevant Test component exists yet, consider creating one. Test components live in `components/tests/` and follow the same component structure. They provide permanent regression protection — every test you write today saves debugging time tomorrow.
475
605
 
476
606
  New test files use the `new_` prefix (e.g., `new_MyTest.groovy`). New tests have no database ID, so you must run them **by name**: `remits-cli test run --test "My Test"`. After committing, the platform assigns an ID and renames the file (e.g., `4_MyTest.groovy`) — then you can run by either ID or name.
@@ -530,29 +660,41 @@ Before committing, update metadata so the next session understands what changed:
530
660
 
531
661
  `account-info.json` is read-only — never edit it. It regenerates automatically after sync.
532
662
 
533
- #### Step 7: Commit and Sync
663
+ #### Step 7: Commit and Durable Sync
534
664
 
535
- Once verified and documented:
665
+ Once verified and documented, create a normal git commit first:
536
666
 
537
667
  ```bash
538
668
  git add -A
539
669
  git commit -m "description of what changed and why"
540
670
  git push origin <branch>
541
- remits-cli components sync
542
- git pull --ff-only origin <branch>
543
671
  ```
544
672
 
545
- This separates the failure boundaries cleanly:
673
+ This separates the local failure boundaries cleanly:
546
674
  1. Local git commit
547
675
  2. Remote push
548
- 3. Server sync from the git remote
549
- 4. Local fast-forward pull of the exact platform-generated commit, including updates such as `account-info.json`
550
676
 
551
- `remits-cli components sync` is the authoritative platform-sync step. It does not perform local git operations.
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.
552
691
 
553
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.
554
693
 
555
- `remits-cli components commit` still exists as a convenience wrapper, but agents should prefer the explicit `git -> remits-cli components sync -> git pull` sequence so each phase is observable and retryable on its own.
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.
556
698
 
557
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.
558
700
 
@@ -616,7 +758,8 @@ component's OWN type enum such as `ObjectType`/`RuleType`, or absent — **never
616
758
  At compile time the platform computes a **signature** that tells you which layer won:
617
759
 
618
760
  - Staged override present → `compileSignature = "cli:<hash>"` (the staged content hash).
619
- - 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).
620
763
 
621
764
  That signature is logged. Querying for it is the single most reliable way to know what ran:
622
765
 
@@ -625,15 +768,31 @@ remits-cli tool --name mcp_system_logs --input '{"node":"remitsAdmin-east5","tim
625
768
  ```
626
769
 
627
770
  `Using Cached BCD [ID: 230, Type: Action, Signature: cli:08cc...]` → ran a **staged** override.
628
- `...Signature: version:37]` → ran the **committed DB** version.
771
+ `...Signature: version:37:abc123def456]` → ran the **committed DB** version.
629
772
 
630
773
  ### When staged overrides apply
631
774
 
632
- Staged overrides resolve **only when the execution carries a CLI TestMode** — i.e. `TestMode.branchName`
633
- and `TestMode.cliUserId` are set (a `remits-cli test run`, a `mcp_run_test`, or a tokenized run minted with
634
- those). A **pure production run with no TestMode always uses the DB source**, ignoring staging entirely. So
635
- "I staged it but the live webhook still runs the old code" is expected — staging is a dev/verification
636
- surface, not a deploy.
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.
637
796
 
638
797
  ### Diagnosing which version is in play
639
798
 
@@ -651,6 +810,9 @@ surface, not a deploy.
651
810
  `No enum constant ObjectType.reader`).
652
811
  - **Confirm the DB version:** `mcp_component_view` reads the live DB source directly (no staging, no compile
653
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 `componentSource`, staged fields, aliases, hashes, and TTLs. `remits-cli components clear` removes
815
+ those entries when you intentionally want to fall back to DB source.
654
816
 
655
817
  ### Stage / commit / clear with the MCP tools
656
818
 
@@ -672,7 +834,9 @@ surface, not a deploy.
672
834
  later test runs.
673
835
  - `remits-cli components sync` (the `commit`/`sync` mode on the `components` endpoint) syncs the DB from the
674
836
  git remote and then **also clears the staged entries** for the synced components, so a clean commit leaves a
675
- clean staging cache on both the CLI and MCP-tool surfaces.
837
+ clean staging cache on both the CLI and MCP-tool surfaces. Because sync reconciles the remote repo into the
838
+ live component database, it must pass the Component Integrity Rules first. Never use sync to clear staging, to
839
+ recover from a mismatched ID, or to retry after an unexpected create/delete/rename response.
676
840
 
677
841
  ### Stale after sync / commit (the in-memory compile cache)
678
842
 
@@ -911,6 +1075,12 @@ Query Cloud Run service logs.
911
1075
  ### `mcp_component_view`
912
1076
  Read component field content with line numbers.
913
1077
 
1078
+ Note: component source-management tools such as `mcp_component_view`, `mcp_component_grep`,
1079
+ `mcp_component_edit`, `mcp_component_create`, and `mcp_component_commit` are primarily for MCP-only
1080
+ clients that do not have a local account repo. In a normal `remits-cli` coding workflow, prefer local
1081
+ repo files plus `remits-cli components stage` / `remits-cli components sync`; if these component tools
1082
+ are absent from `remits-cli tools`, that is expected.
1083
+
914
1084
  | Parameter | Required | Description |
915
1085
  |-----------|----------|-------------|
916
1086
  | `accountId` | yes | Account ID |
@@ -1006,6 +1176,20 @@ auxiliary components). See "Component Resolution" for stage-vs-DB behavior.
1006
1176
  | `editMode` | no | `targeted` (default, anchor-verified line edit via `lineStart`/`lineEnd`/`anchorContent`/`newContent`) or `replace` (full-field `newContent`) |
1007
1177
  | `auxiliary`/`category`/`partnerSlug` | no | Companion metadata applied alongside the edit |
1008
1178
 
1179
+ ### `mcp_component_create`
1180
+ Create a new component row directly in the database. This is a direct-DB operation, not the normal repo-backed
1181
+ component creation workflow.
1182
+
1183
+ Use it only when:
1184
+ - there is no local account repo available, or
1185
+ - the user explicitly asks for direct remote creation, or
1186
+ - an emergency repair plan intentionally creates a new row after live inventory and git history have been
1187
+ reconciled.
1188
+
1189
+ In a repo-backed account, prefer `new_` component files plus stage/test/git/sync after the Component Integrity
1190
+ Rules pass. Never use `mcp_component_create` to compensate for an unexpected deletion, a renamed numeric file,
1191
+ or a local/live ID mismatch.
1192
+
1009
1193
  ### `mcp_component_commit`
1010
1194
  Inspect, flush, or clear the CLI staging cache for components.
1011
1195
 
@@ -1028,7 +1212,7 @@ to remove staged entries).
1028
1212
 
1029
1213
  ## Multi-Session Support
1030
1214
 
1031
- 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.
1215
+ 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.
1032
1216
 
1033
1217
  ```bash
1034
1218
  # Authenticate for an account (run from its repo, or pass --account-id)
@@ -1188,14 +1372,20 @@ remits-cli status
1188
1372
  remits-cli listen [stop|status] [--foreground true] # compatibility alias
1189
1373
  remits-cli data-mode [set test|prod]
1190
1374
  remits-cli components stage [--branch <name>] [--data-mode test|prod]
1191
- remits-cli components sync [--branch <name>] [--data-mode test|prod]
1375
+ remits-cli components status [--branch <name>] [--component-type <type>] [--component-id <id>]
1376
+ remits-cli components clear [--branch <name>] [--component-type <type>] [--component-id <id>]
1377
+ remits-cli components sync [--branch <name>] [--data-mode test|prod] # gated durable repo->DB reconciliation only
1192
1378
  remits-cli components commit [--message "msg"] [--data-mode test|prod]
1193
1379
  remits-cli test run --test <id|name> [--names "a,b"] [--watch true|false] [--data-mode test|prod]
1194
1380
  remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod]
1195
- remits-cli tools [--data-mode test|prod]
1196
- remits-cli tool --name <toolName> [--input "{...}"] [--data-mode test|prod]
1381
+ remits-cli tools [--branch <name>] [--data-mode test|prod]
1382
+ remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod]
1197
1383
  ```
1198
1384
 
1385
+ For tests specifically:
1386
+ - If `--data-mode` is omitted, `remits-cli test run` uses `test`.
1387
+ - `--names` is comma-delimited, so keep individual test names comma-free.
1388
+
1199
1389
  ## Troubleshooting
1200
1390
 
1201
1391
  | Symptom | Fix |
@@ -1204,15 +1394,17 @@ remits-cli tool --name <toolName> [--input "{...}"] [--data-mode test|prod]
1204
1394
  | `account-info.json not found` | Run from the account repo root |
1205
1395
  | 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. |
1206
1396
  | Stage shows 0 updated | No changes since last stage (hash dedup) |
1207
- | Test runs old code after edit | You forgot to stage. Run `remits-cli components stage` THEN re-run the test. The platform runs whatever was last staged, not your local files. |
1397
+ | 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. |
1208
1398
  | "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. |
1399
+ | 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. |
1400
+ | 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. |
1209
1401
  | 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. |
1210
1402
  | Tool parameters rejected | Tool schemas may be cached. Run `remits-cli tools` to refresh `.remits-cli/tools/tools.json` with latest schemas. |
1211
1403
  | Tool response missing | Check `./.remits-cli/tool-responses/` |
1212
- | Staged change has no effect in a live (non-test) run | Staged overrides resolve only under a CLI TestMode (`branchName`+`cliUserId`). A pure prod run always uses the DB. `commit` to make it durable. See "Component Resolution". |
1404
+ | 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". |
1213
1405
  | New source shown by `mcp_component_view` but old behavior persists after sync/commit | In-memory compile cache (`CLOSURE_CACHE`, version-keyed, per instance) still holds the prior closure. Environmental; the platform owner recycles the instance. |
1214
1406
  | 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`. |
1215
- | Unsure whether a run used staged vs DB source | Query `mcp_system_logs` for `Using Cached BCD` — `Signature: cli:<hash>` = staged, `version:<N>` = DB. |
1407
+ | 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. |
1216
1408
  | Service already running | Run `remits-cli status` to get the dashboard URL, or `remits-cli stop` before restarting. |
1217
1409
  | tmux not installed | Install tmux (`brew install tmux` on macOS). Required for agent dispatch. |
1218
1410
  | Control center URL unknown | Read `~/.remits-cli/service-state.json` or run `remits-cli status` |