@doist/twist-cli 2.36.3 → 2.36.5

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.
Files changed (126) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/commands/auth/helpers.d.ts +8 -1
  3. package/dist/commands/auth/helpers.d.ts.map +1 -1
  4. package/dist/commands/auth/helpers.js +12 -7
  5. package/dist/commands/auth/helpers.js.map +1 -1
  6. package/dist/commands/auth/index.d.ts.map +1 -1
  7. package/dist/commands/auth/index.js +12 -12
  8. package/dist/commands/auth/index.js.map +1 -1
  9. package/dist/commands/auth/login.d.ts +3 -3
  10. package/dist/commands/auth/login.d.ts.map +1 -1
  11. package/dist/commands/auth/login.js +24 -62
  12. package/dist/commands/auth/login.js.map +1 -1
  13. package/dist/commands/auth/logout.d.ts +10 -1
  14. package/dist/commands/auth/logout.d.ts.map +1 -1
  15. package/dist/commands/auth/logout.js +18 -6
  16. package/dist/commands/auth/logout.js.map +1 -1
  17. package/dist/commands/auth/status.d.ts +15 -3
  18. package/dist/commands/auth/status.d.ts.map +1 -1
  19. package/dist/commands/auth/status.js +87 -22
  20. package/dist/commands/auth/status.js.map +1 -1
  21. package/dist/commands/changelog.d.ts +0 -5
  22. package/dist/commands/changelog.d.ts.map +1 -1
  23. package/dist/commands/changelog.js +9 -103
  24. package/dist/commands/changelog.js.map +1 -1
  25. package/dist/commands/channel/list.js +5 -5
  26. package/dist/commands/channel/list.js.map +1 -1
  27. package/dist/commands/channel/threads.d.ts.map +1 -1
  28. package/dist/commands/channel/threads.js +5 -3
  29. package/dist/commands/channel/threads.js.map +1 -1
  30. package/dist/commands/config/view.d.ts.map +1 -1
  31. package/dist/commands/config/view.js +3 -3
  32. package/dist/commands/config/view.js.map +1 -1
  33. package/dist/commands/conversation/helpers.d.ts.map +1 -1
  34. package/dist/commands/conversation/helpers.js +4 -3
  35. package/dist/commands/conversation/helpers.js.map +1 -1
  36. package/dist/commands/conversation/unread.d.ts.map +1 -1
  37. package/dist/commands/conversation/unread.js +5 -4
  38. package/dist/commands/conversation/unread.js.map +1 -1
  39. package/dist/commands/conversation/view.d.ts.map +1 -1
  40. package/dist/commands/conversation/view.js +8 -6
  41. package/dist/commands/conversation/view.js.map +1 -1
  42. package/dist/commands/inbox.d.ts.map +1 -1
  43. package/dist/commands/inbox.js +9 -7
  44. package/dist/commands/inbox.js.map +1 -1
  45. package/dist/commands/thread/view.d.ts.map +1 -1
  46. package/dist/commands/thread/view.js +14 -12
  47. package/dist/commands/thread/view.js.map +1 -1
  48. package/dist/commands/update/index.d.ts.map +1 -1
  49. package/dist/commands/update/index.js +43 -14
  50. package/dist/commands/update/index.js.map +1 -1
  51. package/dist/index.js +27 -4
  52. package/dist/index.js.map +1 -1
  53. package/dist/lib/api.d.ts +23 -5
  54. package/dist/lib/api.d.ts.map +1 -1
  55. package/dist/lib/api.js +61 -3
  56. package/dist/lib/api.js.map +1 -1
  57. package/dist/lib/auth-pages.d.ts +3 -0
  58. package/dist/lib/auth-pages.d.ts.map +1 -0
  59. package/dist/lib/{oauth-server.js → auth-pages.js} +3 -120
  60. package/dist/lib/auth-pages.js.map +1 -0
  61. package/dist/lib/auth-provider.d.ts +28 -0
  62. package/dist/lib/auth-provider.d.ts.map +1 -0
  63. package/dist/lib/auth-provider.js +241 -0
  64. package/dist/lib/auth-provider.js.map +1 -0
  65. package/dist/lib/auth.d.ts +16 -10
  66. package/dist/lib/auth.d.ts.map +1 -1
  67. package/dist/lib/auth.js +18 -0
  68. package/dist/lib/auth.js.map +1 -1
  69. package/dist/lib/config.d.ts +23 -2
  70. package/dist/lib/config.d.ts.map +1 -1
  71. package/dist/lib/config.js +95 -47
  72. package/dist/lib/config.js.map +1 -1
  73. package/dist/lib/errors.d.ts +15 -6
  74. package/dist/lib/errors.d.ts.map +1 -1
  75. package/dist/lib/errors.js +14 -9
  76. package/dist/lib/errors.js.map +1 -1
  77. package/dist/lib/global-args.d.ts +35 -21
  78. package/dist/lib/global-args.d.ts.map +1 -1
  79. package/dist/lib/global-args.js +84 -64
  80. package/dist/lib/global-args.js.map +1 -1
  81. package/dist/lib/markdown.d.ts +1 -0
  82. package/dist/lib/markdown.d.ts.map +1 -1
  83. package/dist/lib/markdown.js +9 -14
  84. package/dist/lib/markdown.js.map +1 -1
  85. package/dist/lib/options.d.ts +2 -3
  86. package/dist/lib/options.d.ts.map +1 -1
  87. package/dist/lib/output.d.ts +5 -3
  88. package/dist/lib/output.d.ts.map +1 -1
  89. package/dist/lib/output.js +15 -17
  90. package/dist/lib/output.js.map +1 -1
  91. package/dist/lib/skills/content.d.ts +1 -1
  92. package/dist/lib/skills/content.d.ts.map +1 -1
  93. package/dist/lib/skills/content.js +16 -6
  94. package/dist/lib/skills/content.js.map +1 -1
  95. package/dist/lib/spinner.d.ts +2 -23
  96. package/dist/lib/spinner.d.ts.map +1 -1
  97. package/dist/lib/spinner.js +6 -107
  98. package/dist/lib/spinner.js.map +1 -1
  99. package/dist/lib/update.d.ts +9 -13
  100. package/dist/lib/update.d.ts.map +1 -1
  101. package/dist/lib/update.js +17 -43
  102. package/dist/lib/update.js.map +1 -1
  103. package/package.json +6 -6
  104. package/dist/commands/update/action.d.ts +0 -12
  105. package/dist/commands/update/action.d.ts.map +0 -1
  106. package/dist/commands/update/action.js +0 -104
  107. package/dist/commands/update/action.js.map +0 -1
  108. package/dist/commands/update/switch.d.ts +0 -5
  109. package/dist/commands/update/switch.d.ts.map +0 -1
  110. package/dist/commands/update/switch.js +0 -28
  111. package/dist/commands/update/switch.js.map +0 -1
  112. package/dist/lib/oauth-server.d.ts +0 -13
  113. package/dist/lib/oauth-server.d.ts.map +0 -1
  114. package/dist/lib/oauth-server.js.map +0 -1
  115. package/dist/lib/oauth.d.ts +0 -31
  116. package/dist/lib/oauth.d.ts.map +0 -1
  117. package/dist/lib/oauth.js +0 -150
  118. package/dist/lib/oauth.js.map +0 -1
  119. package/dist/lib/pkce.d.ts +0 -16
  120. package/dist/lib/pkce.d.ts.map +0 -1
  121. package/dist/lib/pkce.js +0 -35
  122. package/dist/lib/pkce.js.map +0 -1
  123. package/dist/test-helpers/empty-output.d.ts +0 -18
  124. package/dist/test-helpers/empty-output.d.ts.map +0 -1
  125. package/dist/test-helpers/empty-output.js +0 -43
  126. package/dist/test-helpers/empty-output.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [2.36.5](https://github.com/Doist/twist-cli/compare/v2.36.4...v2.36.5) (2026-05-13)
2
+
3
+ ### Bug Fixes
4
+
5
+ - guard unreadResp.data against null in channel threads command ([#224](https://github.com/Doist/twist-cli/issues/224)) ([617a617](https://github.com/Doist/twist-cli/commit/617a61770188615d7ce2bfbc63f0329c5ee8ba2f))
6
+
7
+ ## [2.36.4](https://github.com/Doist/twist-cli/compare/v2.36.3...v2.36.4) (2026-05-13)
8
+
9
+ ### Bug Fixes
10
+
11
+ - surface batch API errors in commands ([#159](https://github.com/Doist/twist-cli/issues/159)) ([3b30c3c](https://github.com/Doist/twist-cli/commit/3b30c3ccc17138b257e850562b4e80e500c68d34))
12
+
1
13
  ## [2.36.3](https://github.com/Doist/twist-cli/compare/v2.36.2...v2.36.3) (2026-05-09)
2
14
 
3
15
  ### Bug Fixes
@@ -1,3 +1,10 @@
1
1
  import type { TokenStorageResult } from '../../lib/auth.js';
2
- export declare function logTokenStorageResult(result: TokenStorageResult, secureStoreMessage: string): void;
2
+ /**
3
+ * Surface a `TokenStorageResult` from a save/clear operation: the
4
+ * human-readable confirmation goes to stdout, any keyring-fallback warning
5
+ * goes to stderr. Pass `isMachineOutput: true` when the command is in
6
+ * `--json` / `--ndjson` mode so the stdout confirmation is suppressed and
7
+ * the warning still reaches the operator on stderr.
8
+ */
9
+ export declare function logTokenStorageResult(result: TokenStorageResult, secureStoreMessage: string, isMachineOutput?: boolean): void;
3
10
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE3D,wBAAgB,qBAAqB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,kBAAkB,EAAE,MAAM,GAC3B,IAAI,CAUN"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE3D;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,kBAAkB,EAAE,MAAM,EAC1B,eAAe,UAAQ,GACxB,IAAI,CAON"}
@@ -1,12 +1,17 @@
1
1
  import chalk from 'chalk';
2
- export function logTokenStorageResult(result, secureStoreMessage) {
3
- if (result.storage === 'secure-store') {
2
+ /**
3
+ * Surface a `TokenStorageResult` from a save/clear operation: the
4
+ * human-readable confirmation goes to stdout, any keyring-fallback warning
5
+ * goes to stderr. Pass `isMachineOutput: true` when the command is in
6
+ * `--json` / `--ndjson` mode so the stdout confirmation is suppressed and
7
+ * the warning still reaches the operator on stderr.
8
+ */
9
+ export function logTokenStorageResult(result, secureStoreMessage, isMachineOutput = false) {
10
+ if (!isMachineOutput && result.storage === 'secure-store') {
4
11
  console.log(chalk.dim(secureStoreMessage));
5
- if (result.warning) {
6
- console.error(chalk.yellow('Warning:'), result.warning);
7
- }
8
- return;
9
12
  }
10
- console.error(chalk.yellow('Warning:'), result.warning);
13
+ if (result.warning) {
14
+ console.error(chalk.yellow('Warning:'), result.warning);
15
+ }
11
16
  }
12
17
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/commands/auth/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,UAAU,qBAAqB,CACjC,MAA0B,EAC1B,kBAA0B;IAE1B,IAAI,MAAM,CAAC,OAAO,KAAK,cAAc,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAA;QAC1C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;QAC3D,CAAC;QACD,OAAM;IACV,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;AAC3D,CAAC"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/commands/auth/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACjC,MAA0B,EAC1B,kBAA0B,EAC1B,eAAe,GAAG,KAAK;IAEvB,IAAI,CAAC,eAAe,IAAI,MAAM,CAAC,OAAO,KAAK,cAAc,EAAE,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAA;IAC9C,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;IAC3D,CAAC;AACL,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAMnC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkB1D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAOnC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB1D"}
@@ -1,20 +1,20 @@
1
- import { loginWithOAuth } from './login.js';
2
- import { logout } from './logout.js';
3
- import { showStatus } from './status.js';
1
+ import { createTwistTokenStore } from '../../lib/auth-provider.js';
2
+ import { attachTwistLoginCommand } from './login.js';
3
+ import { attachTwistLogoutCommand } from './logout.js';
4
+ import { attachTwistStatusCommand } from './status.js';
4
5
  import { loginWithToken } from './token.js';
5
6
  export function registerAuthCommand(program) {
6
7
  const auth = program.command('auth').description('Manage authentication');
7
- auth.command('login')
8
- .description('Authenticate using OAuth (opens browser)')
9
- .option('--read-only', 'Authenticate with read-only scope (no write operations)')
10
- .action(loginWithOAuth);
8
+ // Shared store instance: login stashes the post-`set` storage result for
9
+ // its success handler, logout reads the post-`clear` result for the same
10
+ // keyring-fallback warning surface. Status uses `active()` as the
11
+ // authenticated-snapshot gate.
12
+ const store = createTwistTokenStore();
13
+ attachTwistLoginCommand(auth, store);
14
+ attachTwistLogoutCommand(auth, store);
15
+ attachTwistStatusCommand(auth, store);
11
16
  auth.command('token [token]')
12
17
  .description('Save API token for CLI authentication')
13
18
  .action(loginWithToken);
14
- auth.command('status')
15
- .description('Show current authentication status')
16
- .option('--json', 'Output as JSON')
17
- .action(showStatus);
18
- auth.command('logout').description('Remove saved authentication token').action(logout);
19
19
  }
20
20
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAA;IAEzE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,0CAA0C,CAAC;SACvD,MAAM,CAAC,aAAa,EAAE,yDAAyD,CAAC;SAChF,MAAM,CAAC,cAAc,CAAC,CAAA;IAE3B,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,uCAAuC,CAAC;SACpD,MAAM,CAAC,cAAc,CAAC,CAAA;IAE3B,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,oCAAoC,CAAC;SACjD,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,UAAU,CAAC,CAAA;IAEvB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,mCAAmC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;AAC1F,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAA;IAEzE,yEAAyE;IACzE,yEAAyE;IACzE,kEAAkE;IAClE,+BAA+B;IAC/B,MAAM,KAAK,GAAG,qBAAqB,EAAE,CAAA;IAErC,uBAAuB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACpC,wBAAwB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACrC,wBAAwB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAErC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,uCAAuC,CAAC;SACpD,MAAM,CAAC,cAAc,CAAC,CAAA;AAC/B,CAAC"}
@@ -1,4 +1,4 @@
1
- export declare function loginWithOAuth(options: {
2
- readOnly?: boolean;
3
- }): Promise<void>;
1
+ import type { Command } from 'commander';
2
+ import { type TwistTokenStore } from '../../lib/auth-provider.js';
3
+ export declare function attachTwistLoginCommand(parent: Command, store: TwistTokenStore): Command;
4
4
  //# sourceMappingURL=login.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAcA,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiEnF"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAIH,KAAK,eAAe,EACvB,MAAM,4BAA4B,CAAA;AAKnC,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CA0BxF"}
@@ -1,67 +1,29 @@
1
+ import { attachLoginCommand } from '@doist/cli-core/auth';
1
2
  import chalk from 'chalk';
2
- import { saveApiToken } from '../../lib/auth.js';
3
- import { CliError } from '../../lib/errors.js';
4
- import { startCallbackServer } from '../../lib/oauth-server.js';
5
- import { buildAuthorizationUrl, exchangeCodeForToken, READ_ONLY_SCOPES, READ_WRITE_SCOPES, registerDynamicClient, } from '../../lib/oauth.js';
6
- import { generateCodeChallenge, generateCodeVerifier, generateState } from '../../lib/pkce.js';
3
+ import { renderError, renderSuccess } from '../../lib/auth-pages.js';
4
+ import { createTwistAuthProvider, READ_ONLY_SCOPES, READ_WRITE_SCOPES, } from '../../lib/auth-provider.js';
7
5
  import { logTokenStorageResult } from './helpers.js';
8
- export async function loginWithOAuth(options) {
9
- const modeLabel = options.readOnly ? 'read-only' : 'read-write';
10
- console.log(chalk.blue(`Starting OAuth authentication (${modeLabel})...`));
11
- try {
12
- // Register dynamic client
13
- console.log(chalk.dim('Registering OAuth client...'));
14
- const client = await registerDynamicClient();
15
- // Generate PKCE parameters
16
- const codeVerifier = generateCodeVerifier();
17
- const codeChallenge = generateCodeChallenge(codeVerifier);
18
- const state = generateState();
19
- // Start callback server
20
- console.log(chalk.dim('Starting local callback server...'));
21
- let cleanup;
22
- try {
23
- const open = (await import('open')).default;
24
- // Open browser in background after a delay
25
- setTimeout(async () => {
26
- try {
27
- const authUrl = buildAuthorizationUrl(client.client_id, codeChallenge, state, {
28
- readOnly: options.readOnly,
29
- });
30
- console.log(chalk.dim('Opening browser for authorization...'));
31
- console.log(chalk.dim(`If the browser doesn't open, visit: ${authUrl}`));
32
- await open(authUrl);
33
- }
34
- catch {
35
- // Browser opening failure is not critical - user can use the URL manually
36
- }
37
- }, 1000);
38
- // Wait for callback - this gives us both code and cleanup
39
- const result = await startCallbackServer(state);
40
- cleanup = result.cleanup;
41
- console.log(chalk.dim('Exchanging authorization code for token...'));
42
- const accessToken = await exchangeCodeForToken(result.code, codeVerifier, client);
43
- const saveResult = await saveApiToken(accessToken, {
44
- authMode: options.readOnly ? 'read-only' : 'read-write',
45
- authScope: options.readOnly ? READ_ONLY_SCOPES : READ_WRITE_SCOPES,
46
- });
47
- console.log(chalk.green('✓'), 'OAuth authentication successful!');
48
- logTokenStorageResult(saveResult, 'Token stored securely in the system credential manager');
49
- }
50
- finally {
51
- // Always cleanup the server
52
- if (cleanup) {
53
- cleanup();
6
+ const PREFERRED_CALLBACK_PORT = 8766;
7
+ export function attachTwistLoginCommand(parent, store) {
8
+ const provider = createTwistAuthProvider();
9
+ return attachLoginCommand(parent, {
10
+ provider,
11
+ store,
12
+ preferredPort: PREFERRED_CALLBACK_PORT,
13
+ resolveScopes: ({ readOnly }) => (readOnly ? READ_ONLY_SCOPES : READ_WRITE_SCOPES),
14
+ renderSuccess,
15
+ renderError,
16
+ onSuccess({ view, account }) {
17
+ const isMachineOutput = view.json || view.ndjson;
18
+ if (!isMachineOutput) {
19
+ console.log(chalk.green('✓'), 'OAuth authentication successful!');
20
+ console.log(chalk.dim(`Logged in as ${account.label}`));
54
21
  }
55
- }
56
- }
57
- catch (error) {
58
- if (error instanceof CliError)
59
- throw error;
60
- const detail = error instanceof Error ? `: ${error.message}` : '';
61
- throw new CliError('AUTH_FAILED', `OAuth authentication failed${detail}`, [
62
- 'Try again: tw auth login',
63
- 'Or set TWIST_API_TOKEN environment variable',
64
- ]);
65
- }
22
+ const result = store.getLastStorageResult();
23
+ if (result) {
24
+ logTokenStorageResult(result, 'Token stored securely in the system credential manager', isMachineOutput);
25
+ }
26
+ },
27
+ }).description('Authenticate using OAuth (opens browser)');
66
28
  }
67
29
  //# sourceMappingURL=login.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACpE,OAAO,EACH,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,GAEpB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD,MAAM,uBAAuB,GAAG,IAAI,CAAA;AAEpC,MAAM,UAAU,uBAAuB,CAAC,MAAe,EAAE,KAAsB;IAC3E,MAAM,QAAQ,GAAG,uBAAuB,EAAE,CAAA;IAE1C,OAAO,kBAAkB,CAAC,MAAM,EAAE;QAC9B,QAAQ;QACR,KAAK;QACL,aAAa,EAAE,uBAAuB;QACtC,aAAa,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,iBAAiB,CAAC;QAClF,aAAa;QACb,WAAW;QACX,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;YACvB,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAA;YAChD,IAAI,CAAC,eAAe,EAAE,CAAC;gBACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,kCAAkC,CAAC,CAAA;gBACjE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;YAC3D,CAAC;YACD,MAAM,MAAM,GAAG,KAAK,CAAC,oBAAoB,EAAE,CAAA;YAC3C,IAAI,MAAM,EAAE,CAAC;gBACT,qBAAqB,CACjB,MAAM,EACN,wDAAwD,EACxD,eAAe,CAClB,CAAA;YACL,CAAC;QACL,CAAC;KACJ,CAAC,CAAC,WAAW,CAAC,0CAA0C,CAAC,CAAA;AAC9D,CAAC"}
@@ -1,2 +1,11 @@
1
- export declare function logout(): Promise<void>;
1
+ import type { Command } from 'commander';
2
+ import type { TwistTokenStore } from '../../lib/auth-provider.js';
3
+ /**
4
+ * Attach `tw auth logout` via cli-core's generic `attachLogoutCommand`. The
5
+ * registrar emits the success line (`✓ Logged out` / `{ok:true}` / silent
6
+ * ndjson); `onCleared` only surfaces the keyring-fallback warning carried by
7
+ * `TokenStorageResult` — cli-core's `TokenStore.clear: void` contract can't
8
+ * expose it directly, so we stash it on the adapter (`getLastClearResult`).
9
+ */
10
+ export declare function attachTwistLogoutCommand(auth: Command, store: TwistTokenStore): Command;
2
11
  //# sourceMappingURL=logout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"AAIA,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAI5C"}
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAG/E;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAavF"}
@@ -1,9 +1,21 @@
1
- import chalk from 'chalk';
2
- import { clearApiToken } from '../../lib/auth.js';
1
+ import { attachLogoutCommand } from '@doist/cli-core/auth';
3
2
  import { logTokenStorageResult } from './helpers.js';
4
- export async function logout() {
5
- const clearResult = await clearApiToken();
6
- console.log(chalk.green('✓'), 'Logged out');
7
- logTokenStorageResult(clearResult, 'Stored token removed from the system credential manager');
3
+ /**
4
+ * Attach `tw auth logout` via cli-core's generic `attachLogoutCommand`. The
5
+ * registrar emits the success line (`✓ Logged out` / `{ok:true}` / silent
6
+ * ndjson); `onCleared` only surfaces the keyring-fallback warning carried by
7
+ * `TokenStorageResult` — cli-core's `TokenStore.clear: void` contract can't
8
+ * expose it directly, so we stash it on the adapter (`getLastClearResult`).
9
+ */
10
+ export function attachTwistLogoutCommand(auth, store) {
11
+ return attachLogoutCommand(auth, {
12
+ store,
13
+ onCleared: ({ view }) => {
14
+ const result = store.getLastClearResult();
15
+ if (!result)
16
+ return;
17
+ logTokenStorageResult(result, 'Stored token removed from the system credential manager', view.json || view.ndjson);
18
+ },
19
+ });
8
20
  }
9
21
  //# sourceMappingURL=logout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD,MAAM,CAAC,KAAK,UAAU,MAAM;IACxB,MAAM,WAAW,GAAG,MAAM,aAAa,EAAE,CAAA;IACzC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC,CAAA;IAC3C,qBAAqB,CAAC,WAAW,EAAE,yDAAyD,CAAC,CAAA;AACjG,CAAC"}
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAG1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAa,EAAE,KAAsB;IAC1E,OAAO,mBAAmB,CAAe,IAAI,EAAE;QAC3C,KAAK;QACL,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YACpB,MAAM,MAAM,GAAG,KAAK,CAAC,kBAAkB,EAAE,CAAA;YACzC,IAAI,CAAC,MAAM;gBAAE,OAAM;YACnB,qBAAqB,CACjB,MAAM,EACN,yDAAyD,EACzD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAC3B,CAAA;QACL,CAAC;KACJ,CAAC,CAAA;AACN,CAAC"}
@@ -1,4 +1,16 @@
1
- export declare function showStatus(options: {
2
- json?: boolean;
3
- }): Promise<void>;
1
+ import type { Command } from 'commander';
2
+ import type { TwistTokenStore } from '../../lib/auth-provider.js';
3
+ /**
4
+ * Attach `tw auth status` via cli-core's generic `attachStatusCommand`.
5
+ *
6
+ * `TwistTokenStore.active()` returns a snapshot whenever a token resolves
7
+ * (per the adapter's documented contract — see `auth-provider.ts`), so
8
+ * `fetchLive` covers every token-present path: secure-store, plaintext
9
+ * config fallback, env-token mode, and manual `tw auth token`. The
10
+ * snapshot's token is reused inside `gatherStatusData` so credentials are
11
+ * read once per invocation. `onNotAuthenticated` only fires when nothing
12
+ * is stored — it throws `NoTokenError` so the standard CliError envelope
13
+ * reaches the operator unchanged.
14
+ */
15
+ export declare function attachTwistStatusCommand(auth: Command, store: TwistTokenStore): Command;
4
16
  //# sourceMappingURL=status.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/status.ts"],"names":[],"mappings":"AAOA,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B3E"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/status.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAiE/E;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CA+BvF"}
@@ -1,17 +1,34 @@
1
+ import { attachStatusCommand } from '@doist/cli-core/auth';
1
2
  import { TwistRequestError } from '@doist/twist-sdk';
2
3
  import chalk from 'chalk';
3
- import { getSessionUser } from '../../lib/api.js';
4
- import { NoTokenError, getAuthMetadata } from '../../lib/auth.js';
4
+ import { createWrappedTwistClient } from '../../lib/api.js';
5
+ import { getAuthMetadata, NoTokenError } from '../../lib/auth.js';
5
6
  import { CliError } from '../../lib/errors.js';
6
- import { formatJson } from '../../lib/output.js';
7
- export async function showStatus(options) {
8
- let user;
7
+ function formatAuthMode(authMode, authScope) {
8
+ if (authMode === 'read-only') {
9
+ return `read-only (scope: ${authScope ?? 'unknown'})`;
10
+ }
11
+ if (authMode === 'read-write') {
12
+ return 'read-write';
13
+ }
14
+ return 'unknown (manual token or env var; assuming write access)';
15
+ }
16
+ /**
17
+ * Fetch the live session user (via the snapshot token) and the local auth
18
+ * metadata concurrently — the API call dominates and the metadata read is
19
+ * independent. 401-translation lives here so both the snapshot path and any
20
+ * future callers emit the same `NO_TOKEN` envelope when the token is
21
+ * rejected by the API.
22
+ */
23
+ async function gatherStatusData(token) {
9
24
  try {
10
- user = await getSessionUser();
25
+ const [user, metadata] = await Promise.all([
26
+ createWrappedTwistClient(token).users.getSessionUser(),
27
+ getAuthMetadata(),
28
+ ]);
29
+ return { user, metadata };
11
30
  }
12
31
  catch (error) {
13
- if (error instanceof NoTokenError)
14
- throw error;
15
32
  if (error instanceof TwistRequestError && error.httpStatusCode === 401) {
16
33
  throw new CliError('NO_TOKEN', 'Not authenticated (token expired or invalid)', [
17
34
  'Run `tw auth login` to re-authenticate',
@@ -19,19 +36,67 @@ export async function showStatus(options) {
19
36
  }
20
37
  throw error;
21
38
  }
22
- if (options.json) {
23
- console.log(formatJson({ id: user.id, email: user.email, name: user.name }));
24
- return;
25
- }
26
- const metadata = await getAuthMetadata();
27
- const modeLabel = metadata.authMode === 'read-only'
28
- ? `read-only (scope: ${metadata.authScope ?? 'unknown'})`
29
- : metadata.authMode === 'read-write'
30
- ? 'read-write'
31
- : 'unknown (manual token or env var; assuming write access)';
32
- console.log(chalk.green('✓'), 'Authenticated');
33
- console.log(` Email: ${user.email}`);
34
- console.log(` Name: ${user.name}`);
35
- console.log(` Mode: ${modeLabel}`);
39
+ }
40
+ function buildStatusText({ user, metadata }) {
41
+ const modeLabel = formatAuthMode(metadata.authMode, metadata.authScope);
42
+ return [
43
+ `${chalk.green('✓')} Authenticated`,
44
+ ` Email: ${user.email}`,
45
+ ` Name: ${user.name}`,
46
+ ` Mode: ${modeLabel}`,
47
+ ];
48
+ }
49
+ function buildStatusJson({ user, metadata }) {
50
+ return {
51
+ id: user.id,
52
+ email: user.email,
53
+ name: user.name,
54
+ authMode: metadata.authMode,
55
+ authScope: metadata.authScope,
56
+ source: metadata.source,
57
+ };
58
+ }
59
+ /**
60
+ * Attach `tw auth status` via cli-core's generic `attachStatusCommand`.
61
+ *
62
+ * `TwistTokenStore.active()` returns a snapshot whenever a token resolves
63
+ * (per the adapter's documented contract — see `auth-provider.ts`), so
64
+ * `fetchLive` covers every token-present path: secure-store, plaintext
65
+ * config fallback, env-token mode, and manual `tw auth token`. The
66
+ * snapshot's token is reused inside `gatherStatusData` so credentials are
67
+ * read once per invocation. `onNotAuthenticated` only fires when nothing
68
+ * is stored — it throws `NoTokenError` so the standard CliError envelope
69
+ * reaches the operator unchanged.
70
+ */
71
+ export function attachTwistStatusCommand(auth, store) {
72
+ let data = null;
73
+ return attachStatusCommand(auth, {
74
+ store,
75
+ description: 'Show current authentication status',
76
+ fetchLive: async ({ token }) => {
77
+ data = await gatherStatusData(token);
78
+ return {
79
+ id: String(data.user.id),
80
+ label: data.user.name,
81
+ authMode: data.metadata.authMode,
82
+ authScope: data.metadata.authScope ?? '',
83
+ };
84
+ },
85
+ renderText: () => {
86
+ if (!data) {
87
+ throw new CliError('INTERNAL_ERROR', 'status renderText called before fetchLive');
88
+ }
89
+ return buildStatusText(data);
90
+ },
91
+ renderJson: () => {
92
+ if (!data) {
93
+ throw new CliError('INTERNAL_ERROR', 'status renderJson called before fetchLive');
94
+ }
95
+ return buildStatusJson(data);
96
+ },
97
+ onNotAuthenticated: () => {
98
+ throw new NoTokenError();
99
+ },
100
+ });
36
101
  }
37
102
  //# sourceMappingURL=status.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/auth/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAEhD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAA2B;IACxD,IAAI,IAAI,CAAA;IACR,IAAI,CAAC;QACD,IAAI,GAAG,MAAM,cAAc,EAAE,CAAA;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,KAAK,YAAY,YAAY;YAAE,MAAM,KAAK,CAAA;QAC9C,IAAI,KAAK,YAAY,iBAAiB,IAAI,KAAK,CAAC,cAAc,KAAK,GAAG,EAAE,CAAC;YACrE,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,8CAA8C,EAAE;gBAC3E,wCAAwC;aAC3C,CAAC,CAAA;QACN,CAAC;QACD,MAAM,KAAK,CAAA;IACf,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QAC5E,OAAM;IACV,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,eAAe,EAAE,CAAA;IACxC,MAAM,SAAS,GACX,QAAQ,CAAC,QAAQ,KAAK,WAAW;QAC7B,CAAC,CAAC,qBAAqB,QAAQ,CAAC,SAAS,IAAI,SAAS,GAAG;QACzD,CAAC,CAAC,QAAQ,CAAC,QAAQ,KAAK,YAAY;YAClC,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,0DAA0D,CAAA;IAEtE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC,CAAA;IAC9C,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;IACrC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IACpC,OAAO,CAAC,GAAG,CAAC,YAAY,SAAS,EAAE,CAAC,CAAA;AACxC,CAAC"}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/auth/status.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,3 @@
1
1
  import type { Command } from 'commander';
2
- interface ChangelogOptions {
3
- count: string;
4
- }
5
- export declare function changelogAction(options: ChangelogOptions): Promise<void>;
6
2
  export declare function registerChangelogCommand(program: Command): void;
7
- export {};
8
3
  //# sourceMappingURL=changelog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/commands/changelog.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA4FxC,UAAU,gBAAgB;IACtB,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB9E;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAM/D"}
1
+ {"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/commands/changelog.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKxC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAS/D"}
@@ -1,110 +1,16 @@
1
- import { readFile } from 'node:fs/promises';
2
1
  import { dirname, join } from 'node:path';
3
2
  import { fileURLToPath } from 'node:url';
4
- import chalk from 'chalk';
3
+ import { registerChangelogCommand as registerCoreChangelogCommand } from '@doist/cli-core/commands';
5
4
  import packageJson from '../../package.json' with { type: 'json' };
6
- import { CliError } from '../lib/errors.js';
7
5
  const CHANGELOG_PATH = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'CHANGELOG.md');
8
- const CHANGELOG_URL = `https://github.com/Doist/twist-cli/blob/v${packageJson.version}/CHANGELOG.md`;
9
- function formatInline(text) {
10
- return text
11
- .replace(/\*\*([^*]+)\*\*/g, (_, content) => chalk.bold(content))
12
- .replace(/`([^`]+)`/g, (_, code) => chalk.cyan(code));
13
- }
14
- function formatForTerminal(text) {
15
- let inBullet = false;
16
- return text
17
- .split('\n')
18
- .map((line) => {
19
- // Version headers: # 1.25.0 or ## 1.25.0 (date) → bold green
20
- if (/^#{1,2} \d/.test(line)) {
21
- inBullet = false;
22
- const content = line.replace(/^#{1,2} /, '');
23
- return chalk.green.bold(content);
24
- }
25
- // Section headers: ### Features → bold
26
- if (line.startsWith('### ')) {
27
- inBullet = false;
28
- return chalk.bold(line.slice(4));
29
- }
30
- // Bullet items: * description → dimmed bullet + text
31
- if (line.startsWith('* ')) {
32
- inBullet = true;
33
- return ` ${chalk.dim('•')} ${formatInline(line.slice(2))}`;
34
- }
35
- // Continuation lines of a bullet item → indent to match
36
- if (inBullet && line.length > 0) {
37
- return ` ${formatInline(line)}`;
38
- }
39
- if (line.length === 0) {
40
- inBullet = false;
41
- }
42
- return formatInline(line);
43
- })
44
- .join('\n');
45
- }
46
- function cleanChangelog(text) {
47
- return (text
48
- // Version headers: # [1.25.0](https://...) or ## [1.25.0](https://...) → keep heading level
49
- .replace(/(#{1,2}) \[([^\]]+)\]\([^)]*\)/g, '$1 $2')
50
- // Remove commit hash links: ([abc1234](https://...))
51
- .replace(/ \([a-f0-9]{7}\)/g, '')
52
- .replace(/ \(\[[a-f0-9]{7}\]\([^)]*\)\)/g, '')
53
- // Issue/PR links: [#151](https://...) → #151
54
- .replace(/\[#(\d+)\]\([^)]*\)/g, '#$1')
55
- // Remove **deps:** dependency update lines (not useful to end users)
56
- .replace(/^\* \*\*deps:\*\*.*$/gm, '')
57
- // Remove **scope:** prefixes but keep the text: **task:** foo → foo
58
- .replace(/\*\*[\w-]+:\*\* /g, '')
59
- // Clean up blank lines left by removed dep lines
60
- .replace(/\n{3,}/g, '\n\n')
61
- // Remove section headers left empty after filtering (e.g. ### Bug Fixes with no items)
62
- .replace(/### [\w ]+\n\n(?=#{1,3} |$)/gm, ''));
63
- }
64
- function isEmptyAfterClean(section) {
65
- const cleaned = cleanChangelog(section);
66
- // A section is empty if it only contains the version header line(s) and whitespace
67
- const withoutHeader = cleaned.replace(/^#{1,2} .+$/m, '').trim();
68
- return withoutHeader.length === 0;
69
- }
70
- function parseChangelog(content, count) {
71
- const sections = content.split(/\n(?=#{1,2} \[)/);
72
- // Skip preamble (non-version content) if present
73
- const allVersions = /^#{1,2} \[/.test(sections[0]) ? sections : sections.slice(1);
74
- // Filter out versions that become empty after cleaning (e.g. deps-only releases)
75
- const versionSections = allVersions.filter((s) => !isEmptyAfterClean(s));
76
- const selected = versionSections.slice(0, count);
77
- if (selected.length === 0) {
78
- return { text: 'No changelog entries found.', hasMore: false };
79
- }
80
- return {
81
- text: cleanChangelog(selected.join('\n').trimEnd()),
82
- hasMore: versionSections.length > count,
83
- };
84
- }
85
- export async function changelogAction(options) {
86
- const count = parseInt(options.count, 10);
87
- if (Number.isNaN(count) || count < 1) {
88
- throw new CliError('INVALID_TYPE', 'Count must be a positive number');
89
- }
90
- let content;
91
- try {
92
- content = await readFile(CHANGELOG_PATH, 'utf-8');
93
- }
94
- catch {
95
- throw new CliError('FILE_READ_ERROR', 'Could not read changelog file');
96
- }
97
- const { text, hasMore } = parseChangelog(content, count);
98
- console.log(formatForTerminal(text));
99
- if (hasMore) {
100
- console.log(chalk.dim(`\nView full changelog: ${CHANGELOG_URL}`));
101
- }
102
- }
103
6
  export function registerChangelogCommand(program) {
104
- program
105
- .command('changelog')
106
- .description('Show recent changelog entries')
107
- .option('-n, --count <number>', 'Number of versions to show', '5')
108
- .action(changelogAction);
7
+ registerCoreChangelogCommand(program, {
8
+ path: CHANGELOG_PATH,
9
+ repoUrl: 'https://github.com/Doist/twist-cli',
10
+ version: packageJson.version,
11
+ headingLevel: 'flexible',
12
+ continuationIndent: true,
13
+ filterEmptyVersions: true,
14
+ });
109
15
  }
110
16
  //# sourceMappingURL=changelog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/commands/changelog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,WAAW,MAAM,oBAAoB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAE3C,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;AAChG,MAAM,aAAa,GAAG,4CAA4C,WAAW,CAAC,OAAO,eAAe,CAAA;AAEpG,SAAS,YAAY,CAAC,IAAY;IAC9B,OAAO,IAAI;SACN,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;SAChE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;AAC7D,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY;IACnC,IAAI,QAAQ,GAAG,KAAK,CAAA;IACpB,OAAO,IAAI;SACN,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACV,6DAA6D;QAC7D,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,QAAQ,GAAG,KAAK,CAAA;YAChB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;YAC5C,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACpC,CAAC;QACD,uCAAuC;QACvC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,QAAQ,GAAG,KAAK,CAAA;YAChB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QACpC,CAAC;QACD,qDAAqD;QACrD,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,QAAQ,GAAG,IAAI,CAAA;YACf,OAAO,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAC/D,CAAC;QACD,wDAAwD;QACxD,IAAI,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,CAAA;QACtC,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpB,QAAQ,GAAG,KAAK,CAAA;QACpB,CAAC;QACD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;AACnB,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAChC,OAAO,CACH,IAAI;QACA,4FAA4F;SAC3F,OAAO,CAAC,iCAAiC,EAAE,OAAO,CAAC;QACpD,qDAAqD;SACpD,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;SAChC,OAAO,CAAC,gCAAgC,EAAE,EAAE,CAAC;QAC9C,6CAA6C;SAC5C,OAAO,CAAC,sBAAsB,EAAE,KAAK,CAAC;QACvC,qEAAqE;SACpE,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC;QACtC,oEAAoE;SACnE,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;QACjC,iDAAiD;SAChD,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,uFAAuF;SACtF,OAAO,CAAC,+BAA+B,EAAE,EAAE,CAAC,CACpD,CAAA;AACL,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAe;IACtC,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;IACvC,mFAAmF;IACnF,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAChE,OAAO,aAAa,CAAC,MAAM,KAAK,CAAC,CAAA;AACrC,CAAC;AAED,SAAS,cAAc,CAAC,OAAe,EAAE,KAAa;IAClD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACjD,iDAAiD;IACjD,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACjF,iFAAiF;IACjF,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAA;IACxE,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IAEhD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;IAClE,CAAC;IAED,OAAO;QACH,IAAI,EAAE,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACnD,OAAO,EAAE,eAAe,CAAC,MAAM,GAAG,KAAK;KAC1C,CAAA;AACL,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAyB;IAC3D,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACzC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAA;IACzE,CAAC;IAED,IAAI,OAAe,CAAA;IACnB,IAAI,CAAC;QACD,OAAO,GAAG,MAAM,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC,CAAA;IACrD,CAAC;IAAC,MAAM,CAAC;QACL,MAAM,IAAI,QAAQ,CAAC,iBAAiB,EAAE,+BAA+B,CAAC,CAAA;IAC1E,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACxD,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAA;IAEpC,IAAI,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,0BAA0B,aAAa,EAAE,CAAC,CAAC,CAAA;IACrE,CAAC;AACL,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACrD,OAAO;SACF,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,+BAA+B,CAAC;SAC5C,MAAM,CAAC,sBAAsB,EAAE,4BAA4B,EAAE,GAAG,CAAC;SACjE,MAAM,CAAC,eAAe,CAAC,CAAA;AAChC,CAAC"}
1
+ {"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/commands/changelog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,wBAAwB,IAAI,4BAA4B,EAAE,MAAM,0BAA0B,CAAA;AAEnG,OAAO,WAAW,MAAM,oBAAoB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAElE,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;AAEhG,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACrD,4BAA4B,CAAC,OAAO,EAAE;QAClC,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,oCAAoC;QAC7C,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,YAAY,EAAE,UAAU;QACxB,kBAAkB,EAAE,IAAI;QACxB,mBAAmB,EAAE,IAAI;KAC5B,CAAC,CAAA;AACN,CAAC"}