chief-clancy 0.2.0 → 0.3.1

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 (141) hide show
  1. package/README.md +13 -24
  2. package/dist/bundle/clancy-afk.js +101 -0
  3. package/dist/bundle/clancy-once.js +13902 -0
  4. package/dist/installer/file-ops/file-ops.d.ts +32 -0
  5. package/dist/installer/file-ops/file-ops.d.ts.map +1 -0
  6. package/dist/installer/file-ops/file-ops.js +58 -0
  7. package/dist/installer/file-ops/file-ops.js.map +1 -0
  8. package/dist/installer/hook-installer/hook-installer.d.ts +29 -0
  9. package/dist/installer/hook-installer/hook-installer.d.ts.map +1 -0
  10. package/dist/installer/hook-installer/hook-installer.js +96 -0
  11. package/dist/installer/hook-installer/hook-installer.js.map +1 -0
  12. package/dist/installer/install.d.ts +3 -0
  13. package/dist/installer/install.d.ts.map +1 -0
  14. package/dist/installer/install.js +248 -0
  15. package/dist/installer/install.js.map +1 -0
  16. package/dist/installer/manifest/manifest.d.ts +41 -0
  17. package/dist/installer/manifest/manifest.d.ts.map +1 -0
  18. package/dist/installer/manifest/manifest.js +97 -0
  19. package/dist/installer/manifest/manifest.js.map +1 -0
  20. package/dist/installer/prompts/prompts.d.ts +33 -0
  21. package/dist/installer/prompts/prompts.d.ts.map +1 -0
  22. package/dist/installer/prompts/prompts.js +55 -0
  23. package/dist/installer/prompts/prompts.js.map +1 -0
  24. package/dist/schemas/env.d.ts +75 -0
  25. package/dist/schemas/env.d.ts.map +1 -0
  26. package/dist/schemas/env.js +40 -0
  27. package/dist/schemas/env.js.map +1 -0
  28. package/dist/schemas/github.d.ts +27 -0
  29. package/dist/schemas/github.d.ts.map +1 -0
  30. package/dist/schemas/github.js +17 -0
  31. package/dist/schemas/github.js.map +1 -0
  32. package/dist/schemas/index.d.ts +9 -0
  33. package/dist/schemas/index.d.ts.map +1 -0
  34. package/dist/schemas/index.js +5 -0
  35. package/dist/schemas/index.js.map +1 -0
  36. package/dist/schemas/jira.d.ts +37 -0
  37. package/dist/schemas/jira.d.ts.map +1 -0
  38. package/dist/schemas/jira.js +37 -0
  39. package/dist/schemas/jira.js.map +1 -0
  40. package/dist/schemas/linear.d.ts +67 -0
  41. package/dist/schemas/linear.d.ts.map +1 -0
  42. package/dist/schemas/linear.js +50 -0
  43. package/dist/schemas/linear.js.map +1 -0
  44. package/dist/scripts/afk/afk.d.ts +21 -0
  45. package/dist/scripts/afk/afk.d.ts.map +1 -0
  46. package/dist/scripts/afk/afk.js +124 -0
  47. package/dist/scripts/afk/afk.js.map +1 -0
  48. package/dist/scripts/board/github/github.d.ts +40 -0
  49. package/dist/scripts/board/github/github.d.ts.map +1 -0
  50. package/dist/scripts/board/github/github.js +121 -0
  51. package/dist/scripts/board/github/github.js.map +1 -0
  52. package/dist/scripts/board/jira/jira.d.ts +90 -0
  53. package/dist/scripts/board/jira/jira.d.ts.map +1 -0
  54. package/dist/scripts/board/jira/jira.js +251 -0
  55. package/dist/scripts/board/jira/jira.js.map +1 -0
  56. package/dist/scripts/board/linear/linear.d.ts +85 -0
  57. package/dist/scripts/board/linear/linear.d.ts.map +1 -0
  58. package/dist/scripts/board/linear/linear.js +209 -0
  59. package/dist/scripts/board/linear/linear.js.map +1 -0
  60. package/dist/scripts/once/once.d.ts +12 -0
  61. package/dist/scripts/once/once.d.ts.map +1 -0
  62. package/dist/scripts/once/once.js +330 -0
  63. package/dist/scripts/once/once.js.map +1 -0
  64. package/dist/scripts/shared/branch/branch.d.ts +50 -0
  65. package/dist/scripts/shared/branch/branch.d.ts.map +1 -0
  66. package/dist/scripts/shared/branch/branch.js +61 -0
  67. package/dist/scripts/shared/branch/branch.js.map +1 -0
  68. package/dist/scripts/shared/claude-cli/claude-cli.d.ts +17 -0
  69. package/dist/scripts/shared/claude-cli/claude-cli.d.ts.map +1 -0
  70. package/dist/scripts/shared/claude-cli/claude-cli.js +35 -0
  71. package/dist/scripts/shared/claude-cli/claude-cli.js.map +1 -0
  72. package/dist/scripts/shared/env-parser/env-parser.d.ts +30 -0
  73. package/dist/scripts/shared/env-parser/env-parser.d.ts.map +1 -0
  74. package/dist/scripts/shared/env-parser/env-parser.js +64 -0
  75. package/dist/scripts/shared/env-parser/env-parser.js.map +1 -0
  76. package/dist/scripts/shared/env-schema/env-schema.d.ts +27 -0
  77. package/dist/scripts/shared/env-schema/env-schema.d.ts.map +1 -0
  78. package/dist/scripts/shared/env-schema/env-schema.js +46 -0
  79. package/dist/scripts/shared/env-schema/env-schema.js.map +1 -0
  80. package/dist/scripts/shared/git-ops/git-ops.d.ts +52 -0
  81. package/dist/scripts/shared/git-ops/git-ops.d.ts.map +1 -0
  82. package/dist/scripts/shared/git-ops/git-ops.js +107 -0
  83. package/dist/scripts/shared/git-ops/git-ops.js.map +1 -0
  84. package/dist/scripts/shared/http/http.d.ts +52 -0
  85. package/dist/scripts/shared/http/http.d.ts.map +1 -0
  86. package/dist/scripts/shared/http/http.js +74 -0
  87. package/dist/scripts/shared/http/http.js.map +1 -0
  88. package/dist/scripts/shared/notify/notify.d.ts +46 -0
  89. package/dist/scripts/shared/notify/notify.d.ts.map +1 -0
  90. package/dist/scripts/shared/notify/notify.js +88 -0
  91. package/dist/scripts/shared/notify/notify.js.map +1 -0
  92. package/dist/scripts/shared/preflight/preflight.d.ts +40 -0
  93. package/dist/scripts/shared/preflight/preflight.d.ts.map +1 -0
  94. package/dist/scripts/shared/preflight/preflight.js +84 -0
  95. package/dist/scripts/shared/preflight/preflight.js.map +1 -0
  96. package/dist/scripts/shared/progress/progress.d.ts +25 -0
  97. package/dist/scripts/shared/progress/progress.d.ts.map +1 -0
  98. package/dist/scripts/shared/progress/progress.js +46 -0
  99. package/dist/scripts/shared/progress/progress.js.map +1 -0
  100. package/dist/scripts/shared/prompt/prompt.d.ts +38 -0
  101. package/dist/scripts/shared/prompt/prompt.d.ts.map +1 -0
  102. package/dist/scripts/shared/prompt/prompt.js +77 -0
  103. package/dist/scripts/shared/prompt/prompt.js.map +1 -0
  104. package/dist/types/board.d.ts +13 -0
  105. package/dist/types/board.d.ts.map +1 -0
  106. package/dist/types/board.js +5 -0
  107. package/dist/types/board.js.map +1 -0
  108. package/dist/types/index.d.ts +3 -0
  109. package/dist/types/index.d.ts.map +1 -0
  110. package/dist/types/index.js +2 -0
  111. package/dist/types/index.js.map +1 -0
  112. package/dist/utils/ansi/ansi.d.ts +55 -0
  113. package/dist/utils/ansi/ansi.d.ts.map +1 -0
  114. package/dist/utils/ansi/ansi.js +55 -0
  115. package/dist/utils/ansi/ansi.js.map +1 -0
  116. package/dist/utils/parse-json/parse-json.d.ts +20 -0
  117. package/dist/utils/parse-json/parse-json.d.ts.map +1 -0
  118. package/dist/utils/parse-json/parse-json.js +27 -0
  119. package/dist/utils/parse-json/parse-json.js.map +1 -0
  120. package/hooks/clancy-check-update.js +2 -2
  121. package/hooks/clancy-credential-guard.js +8 -1
  122. package/package.json +48 -8
  123. package/registry/boards.json +3 -6
  124. package/src/templates/CLAUDE.md +1 -1
  125. package/src/workflows/doctor.md +32 -23
  126. package/src/workflows/init.md +101 -26
  127. package/src/workflows/logs.md +13 -6
  128. package/src/workflows/map-codebase.md +17 -16
  129. package/src/workflows/once.md +22 -12
  130. package/src/workflows/review.md +40 -27
  131. package/src/workflows/run.md +20 -12
  132. package/src/workflows/scaffold.md +5 -1034
  133. package/src/workflows/settings.md +9 -6
  134. package/src/workflows/status.md +17 -8
  135. package/src/workflows/uninstall.md +11 -6
  136. package/src/workflows/update.md +20 -13
  137. package/bin/install.js +0 -362
  138. package/src/templates/scripts/clancy-afk.sh +0 -111
  139. package/src/templates/scripts/clancy-once-github.sh +0 -249
  140. package/src/templates/scripts/clancy-once-linear.sh +0 -320
  141. package/src/templates/scripts/clancy-once.sh +0 -322
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Clancy AFK runner — loop mode.
3
+ *
4
+ * Runs the board-specific `clancy-once` script up to `MAX_ITERATIONS` times.
5
+ * Detects stop conditions by parsing the script's stdout output.
6
+ * Does NOT know about boards — board logic lives entirely in the once script.
7
+ */
8
+ import { spawnSync } from 'node:child_process';
9
+ import { existsSync } from 'node:fs';
10
+ import { dirname, join, resolve } from 'node:path';
11
+ import { setTimeout as sleep } from 'node:timers/promises';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { bold, dim, green, red } from '../../utils/ansi/ansi.js';
14
+ function formatDuration(ms) {
15
+ const secs = Math.floor(ms / 1000);
16
+ if (secs < 60)
17
+ return `${secs}s`;
18
+ const mins = Math.floor(secs / 60);
19
+ const remSecs = secs % 60;
20
+ return remSecs > 0 ? `${mins}m ${remSecs}s` : `${mins}m`;
21
+ }
22
+ /** Stop condition patterns matched against script output. */
23
+ const STOP_PATTERNS = {
24
+ noTickets: /No tickets found|No issues found|All done/,
25
+ skipped: /Ticket skipped/,
26
+ preflightFail: /^✗ /m,
27
+ };
28
+ /**
29
+ * Check whether the script output contains a stop condition.
30
+ *
31
+ * @param output - The captured stdout from a `clancy-once` run.
32
+ * @returns An object with `stop` flag and optional `reason`.
33
+ */
34
+ export function checkStopCondition(output) {
35
+ if (STOP_PATTERNS.noTickets.test(output)) {
36
+ return { stop: true, reason: 'No more tickets — all done' };
37
+ }
38
+ if (STOP_PATTERNS.skipped.test(output)) {
39
+ return {
40
+ stop: true,
41
+ reason: 'Ticket was skipped — update the ticket and re-run',
42
+ };
43
+ }
44
+ if (STOP_PATTERNS.preflightFail.test(output)) {
45
+ return { stop: true, reason: 'Preflight check failed' };
46
+ }
47
+ return { stop: false };
48
+ }
49
+ /**
50
+ * Run the AFK loop.
51
+ *
52
+ * Executes the `clancy-once` script repeatedly up to `maxIterations` times,
53
+ * checking for stop conditions after each run.
54
+ *
55
+ * @param scriptDir - The directory containing `clancy-once.js`.
56
+ * @param maxIterations - Maximum number of iterations (default: 5).
57
+ */
58
+ export async function runAfkLoop(scriptDir, maxIterations = 5) {
59
+ const onceScript = join(scriptDir, 'clancy-once.js');
60
+ if (!existsSync(onceScript)) {
61
+ console.error(red('✗ clancy-once.js not found in'), scriptDir);
62
+ return;
63
+ }
64
+ console.log(dim('┌──────────────────────────────────────────────────────────┐'));
65
+ console.log(dim('│') +
66
+ bold(' 🤖 Clancy — AFK mode ') +
67
+ dim('│'));
68
+ console.log(dim('│') +
69
+ dim(' "I\'m on it. Proceed to the abandoned warehouse." ') +
70
+ dim('│'));
71
+ console.log(dim('└──────────────────────────────────────────────────────────┘'));
72
+ const loopStart = Date.now();
73
+ for (let i = 1; i <= maxIterations; i++) {
74
+ const iterStart = Date.now();
75
+ console.log('');
76
+ console.log(bold(`🔁 Iteration ${i}/${maxIterations}`));
77
+ // stderr is inherited so errors are visible to the user in real time.
78
+ // Exit codes are not checked — once.ts always exits 0 by design so that
79
+ // a transient failure in one iteration does not halt the entire AFK run.
80
+ // Stop conditions are explicit board-level signals parsed from stdout.
81
+ const result = spawnSync('node', [onceScript], {
82
+ encoding: 'utf8',
83
+ stdio: ['inherit', 'pipe', 'inherit'],
84
+ cwd: process.cwd(),
85
+ });
86
+ const output = result.stdout ?? '';
87
+ if (output) {
88
+ process.stdout.write(output);
89
+ }
90
+ const iterElapsed = formatDuration(Date.now() - iterStart);
91
+ if (result.error) {
92
+ console.error(red(`✗ Failed to run clancy-once: ${result.error.message}`));
93
+ return;
94
+ }
95
+ const condition = checkStopCondition(output);
96
+ if (condition.stop) {
97
+ const totalElapsed = formatDuration(Date.now() - loopStart);
98
+ console.log('');
99
+ console.log(dim(` Iteration ${i} took ${iterElapsed}`));
100
+ console.log(`\n${condition.reason}`);
101
+ console.log(dim(` Total: ${i} iteration${i > 1 ? 's' : ''} in ${totalElapsed}`));
102
+ return;
103
+ }
104
+ console.log(dim(` Iteration ${i} took ${iterElapsed}`));
105
+ // Brief pause between iterations
106
+ if (i < maxIterations) {
107
+ await sleep(2000);
108
+ }
109
+ }
110
+ const totalElapsed = formatDuration(Date.now() - loopStart);
111
+ console.log('');
112
+ console.log(green(`🏁 Completed ${maxIterations} iterations`) +
113
+ dim(` (${totalElapsed})`));
114
+ console.log(dim(' "That\'s some good police work."'));
115
+ console.log(dim(' Run clancy-afk again to continue.'));
116
+ }
117
+ // Main guard — self-execute when run directly (e.g. node .clancy/clancy-afk.js)
118
+ if (process.argv[1] &&
119
+ fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
120
+ const scriptDir = dirname(fileURLToPath(import.meta.url));
121
+ const maxIterations = parseInt(process.env.MAX_ITERATIONS ?? '5', 10) || 5;
122
+ runAfkLoop(scriptDir, maxIterations);
123
+ }
124
+ //# sourceMappingURL=afk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"afk.js","sourceRoot":"","sources":["../../../src/scripts/afk/afk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { PingResult } from '../../../scripts/shared/http/http.js';
2
+ import type { Ticket } from '../../../types/index.js';
3
+ /**
4
+ * Validate that a GitHub repo string is in `owner/repo` format.
5
+ *
6
+ * @param repo - The repository string to validate.
7
+ * @returns `true` if the string matches `owner/repo` with safe characters.
8
+ */
9
+ export declare function isValidRepo(repo: string): boolean;
10
+ /**
11
+ * Ping the GitHub API to verify connectivity and credentials.
12
+ *
13
+ * @param token - The GitHub personal access token.
14
+ * @param repo - The repository in `owner/repo` format.
15
+ * @returns An object with `ok` and optional `error` message.
16
+ */
17
+ export declare function pingGitHub(token: string, repo: string): Promise<PingResult>;
18
+ /**
19
+ * Fetch the next available issue from GitHub Issues.
20
+ *
21
+ * Requests extra results to account for PR pollution (GitHub Issues endpoint
22
+ * returns PRs too), then filters to real issues only.
23
+ *
24
+ * @param token - The GitHub personal access token.
25
+ * @param repo - The repository in `owner/repo` format.
26
+ * @returns The fetched ticket with optional milestone, or `undefined` if none available.
27
+ */
28
+ export declare function fetchIssue(token: string, repo: string, label?: string): Promise<(Ticket & {
29
+ milestone?: string;
30
+ }) | undefined>;
31
+ /**
32
+ * Close a GitHub issue.
33
+ *
34
+ * @param token - The GitHub personal access token.
35
+ * @param repo - The repository in `owner/repo` format.
36
+ * @param issueNumber - The issue number to close.
37
+ * @returns `true` if the issue was closed successfully.
38
+ */
39
+ export declare function closeIssue(token: string, repo: string, issueNumber: number): Promise<boolean>;
40
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../../src/scripts/board/github/github.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAK/C;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,CAAC,CAWrB;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,MAAM,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAAC,CAyDxD;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CAoBlB"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Clancy GitHub Issues board script.
3
+ *
4
+ * Fetches an issue from GitHub Issues REST API, creates branches,
5
+ * invokes Claude, squash merges, closes the issue, and sends notifications.
6
+ *
7
+ * Note: GitHub Issues endpoint returns both issues AND pull requests.
8
+ * This script filters out PRs explicitly.
9
+ */
10
+ import { githubIssuesResponseSchema } from '../../../schemas/github.js';
11
+ import { githubHeaders, pingEndpoint } from '../../../scripts/shared/http/http.js';
12
+ const SAFE_REPO_PATTERN = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/;
13
+ const GITHUB_API = 'https://api.github.com';
14
+ /**
15
+ * Validate that a GitHub repo string is in `owner/repo` format.
16
+ *
17
+ * @param repo - The repository string to validate.
18
+ * @returns `true` if the string matches `owner/repo` with safe characters.
19
+ */
20
+ export function isValidRepo(repo) {
21
+ return SAFE_REPO_PATTERN.test(repo);
22
+ }
23
+ /**
24
+ * Ping the GitHub API to verify connectivity and credentials.
25
+ *
26
+ * @param token - The GitHub personal access token.
27
+ * @param repo - The repository in `owner/repo` format.
28
+ * @returns An object with `ok` and optional `error` message.
29
+ */
30
+ export async function pingGitHub(token, repo) {
31
+ return pingEndpoint(`${GITHUB_API}/repos/${repo}`, githubHeaders(token), {
32
+ 401: '✗ GitHub auth failed — check GITHUB_TOKEN',
33
+ 403: '✗ GitHub permission denied',
34
+ 404: `✗ GitHub repo "${repo}" not found`,
35
+ }, '✗ Could not reach GitHub — check network');
36
+ }
37
+ /**
38
+ * Fetch the next available issue from GitHub Issues.
39
+ *
40
+ * Requests extra results to account for PR pollution (GitHub Issues endpoint
41
+ * returns PRs too), then filters to real issues only.
42
+ *
43
+ * @param token - The GitHub personal access token.
44
+ * @param repo - The repository in `owner/repo` format.
45
+ * @returns The fetched ticket with optional milestone, or `undefined` if none available.
46
+ */
47
+ export async function fetchIssue(token, repo, label) {
48
+ let response;
49
+ const params = new URLSearchParams({
50
+ state: 'open',
51
+ assignee: '@me',
52
+ per_page: '10',
53
+ });
54
+ if (label)
55
+ params.set('labels', label);
56
+ try {
57
+ response = await fetch(`${GITHUB_API}/repos/${repo}/issues?${params}`, {
58
+ headers: githubHeaders(token),
59
+ });
60
+ }
61
+ catch (err) {
62
+ console.warn(`⚠ GitHub API request failed: ${err instanceof Error ? err.message : String(err)}`);
63
+ return undefined;
64
+ }
65
+ if (!response.ok) {
66
+ console.warn(`⚠ GitHub API returned HTTP ${response.status}`);
67
+ return undefined;
68
+ }
69
+ let json;
70
+ try {
71
+ json = await response.json();
72
+ }
73
+ catch {
74
+ console.warn('⚠ GitHub API returned invalid JSON');
75
+ return undefined;
76
+ }
77
+ const parsed = githubIssuesResponseSchema.safeParse(json);
78
+ if (!parsed.success) {
79
+ console.warn(`⚠ Unexpected GitHub response shape: ${parsed.error.message}`);
80
+ return undefined;
81
+ }
82
+ // Filter out pull requests
83
+ const issues = parsed.data.filter((item) => !item.pull_request);
84
+ if (!issues.length)
85
+ return undefined;
86
+ const issue = issues[0];
87
+ return {
88
+ key: `#${issue.number}`,
89
+ title: issue.title,
90
+ description: issue.body ?? '',
91
+ provider: 'github',
92
+ milestone: issue.milestone?.title,
93
+ };
94
+ }
95
+ /**
96
+ * Close a GitHub issue.
97
+ *
98
+ * @param token - The GitHub personal access token.
99
+ * @param repo - The repository in `owner/repo` format.
100
+ * @param issueNumber - The issue number to close.
101
+ * @returns `true` if the issue was closed successfully.
102
+ */
103
+ export async function closeIssue(token, repo, issueNumber) {
104
+ if (!isValidRepo(repo))
105
+ return false;
106
+ try {
107
+ const response = await fetch(`${GITHUB_API}/repos/${repo}/issues/${issueNumber}`, {
108
+ method: 'PATCH',
109
+ headers: {
110
+ ...githubHeaders(token),
111
+ 'Content-Type': 'application/json',
112
+ },
113
+ body: JSON.stringify({ state: 'closed' }),
114
+ });
115
+ return response.ok;
116
+ }
117
+ catch {
118
+ return false;
119
+ }
120
+ }
121
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../../../src/scripts/board/github/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import type { PingResult } from '../../../scripts/shared/http/http.js';
2
+ import type { Ticket } from '../../../types/index.js';
3
+ /**
4
+ * Build Jira Basic auth header value.
5
+ *
6
+ * @param user - The Jira username (email).
7
+ * @param token - The Jira API token.
8
+ * @returns The Base64-encoded `user:token` string for Basic auth.
9
+ */
10
+ export declare function buildAuthHeader(user: string, token: string): string;
11
+ /**
12
+ * Validate that a user-controlled value is safe for JQL injection.
13
+ *
14
+ * @param value - The value to validate.
15
+ * @returns `true` if the value matches the safe pattern.
16
+ */
17
+ export declare function isSafeJqlValue(value: string): boolean;
18
+ /**
19
+ * Ping the Jira API to verify connectivity and credentials.
20
+ *
21
+ * @param baseUrl - The Jira Cloud base URL.
22
+ * @param projectKey - The Jira project key.
23
+ * @param auth - The Base64-encoded Basic auth string.
24
+ * @returns An object with `ok` and optional `error` message.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const result = await pingJira('https://example.atlassian.net', 'PROJ', authHeader);
29
+ * if (!result.ok) console.error(result.error);
30
+ * ```
31
+ */
32
+ export declare function pingJira(baseUrl: string, projectKey: string, auth: string): Promise<PingResult>;
33
+ /**
34
+ * Build the JQL query for fetching the next ticket.
35
+ *
36
+ * @param projectKey - The Jira project key.
37
+ * @param status - The JQL status to filter by (default: `"To Do"`).
38
+ * @param sprint - If set, adds an open sprint filter.
39
+ * @param label - If set, adds a label filter.
40
+ * @returns The JQL query string.
41
+ */
42
+ export declare function buildJql(projectKey: string, status: string, sprint?: string, label?: string): string;
43
+ /**
44
+ * Extract all text strings from a Jira ADF (Atlassian Document Format) description.
45
+ *
46
+ * Recursively walks the ADF tree and collects all string values.
47
+ *
48
+ * @param adf - The ADF description object (or `undefined`).
49
+ * @returns A single string with all text content joined by spaces.
50
+ */
51
+ export declare function extractAdfText(adf: unknown): string;
52
+ /**
53
+ * Fetch the next available ticket from Jira.
54
+ *
55
+ * @param baseUrl - The Jira Cloud base URL.
56
+ * @param auth - The Base64-encoded Basic auth string.
57
+ * @param projectKey - The Jira project key.
58
+ * @param status - The JQL status to filter by.
59
+ * @param sprint - Optional sprint filter.
60
+ * @param label - Optional label filter.
61
+ * @returns The fetched ticket, or `undefined` if no tickets are available.
62
+ */
63
+ export declare function fetchTicket(baseUrl: string, auth: string, projectKey: string, status: string, sprint?: string, label?: string): Promise<(Ticket & {
64
+ epicKey?: string;
65
+ blockers: string[];
66
+ }) | undefined>;
67
+ /**
68
+ * Look up a Jira transition ID by status name.
69
+ *
70
+ * @param baseUrl - The Jira Cloud base URL.
71
+ * @param auth - The Base64-encoded Basic auth string.
72
+ * @param issueKey - The Jira issue key (e.g., `'PROJ-123'`).
73
+ * @param statusName - The target status name (e.g., `'In Progress'`).
74
+ * @returns The transition ID, or `undefined` if not found.
75
+ */
76
+ export declare function lookupTransitionId(baseUrl: string, auth: string, issueKey: string, statusName: string): Promise<string | undefined>;
77
+ /**
78
+ * Transition a Jira issue to a new status.
79
+ *
80
+ * Fetches available transitions and executes the one matching the target status name.
81
+ * Best-effort — never throws on failure.
82
+ *
83
+ * @param baseUrl - The Jira Cloud base URL.
84
+ * @param auth - The Base64-encoded Basic auth string.
85
+ * @param issueKey - The Jira issue key (e.g., `'PROJ-123'`).
86
+ * @param statusName - The target status name (e.g., `'In Progress'`).
87
+ * @returns `true` if the transition succeeded.
88
+ */
89
+ export declare function transitionIssue(baseUrl: string, auth: string, issueKey: string, statusName: string): Promise<boolean>;
90
+ //# sourceMappingURL=jira.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../../../src/scripts/board/jira/jira.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAK/C;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,CAAC,CAWrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAWR;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CA0BnD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CACN,CAAC,MAAM,GAAG;IACR,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC,GACF,SAAS,CACZ,CA0EA;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAwC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAgClB"}
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Clancy Jira board script.
3
+ *
4
+ * Fetches a ticket from Jira Cloud, creates branches, invokes Claude,
5
+ * squash merges, transitions status, and sends notifications.
6
+ *
7
+ * Uses the new POST `/rest/api/3/search/jql` endpoint (old GET `/search`
8
+ * was removed by Atlassian in August 2025).
9
+ */
10
+ import { jiraSearchResponseSchema, jiraTransitionsResponseSchema, } from '../../../schemas/jira.js';
11
+ import { jiraHeaders, pingEndpoint } from '../../../scripts/shared/http/http.js';
12
+ const SAFE_VALUE_PATTERN = /^[a-zA-Z0-9 _\-'.]+$/;
13
+ const ISSUE_KEY_PATTERN = /^[A-Z][A-Z0-9]+-\d+$/;
14
+ /**
15
+ * Build Jira Basic auth header value.
16
+ *
17
+ * @param user - The Jira username (email).
18
+ * @param token - The Jira API token.
19
+ * @returns The Base64-encoded `user:token` string for Basic auth.
20
+ */
21
+ export function buildAuthHeader(user, token) {
22
+ return Buffer.from(`${user}:${token}`).toString('base64');
23
+ }
24
+ /**
25
+ * Validate that a user-controlled value is safe for JQL injection.
26
+ *
27
+ * @param value - The value to validate.
28
+ * @returns `true` if the value matches the safe pattern.
29
+ */
30
+ export function isSafeJqlValue(value) {
31
+ return SAFE_VALUE_PATTERN.test(value);
32
+ }
33
+ /**
34
+ * Ping the Jira API to verify connectivity and credentials.
35
+ *
36
+ * @param baseUrl - The Jira Cloud base URL.
37
+ * @param projectKey - The Jira project key.
38
+ * @param auth - The Base64-encoded Basic auth string.
39
+ * @returns An object with `ok` and optional `error` message.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * const result = await pingJira('https://example.atlassian.net', 'PROJ', authHeader);
44
+ * if (!result.ok) console.error(result.error);
45
+ * ```
46
+ */
47
+ export async function pingJira(baseUrl, projectKey, auth) {
48
+ return pingEndpoint(`${baseUrl}/rest/api/3/project/${projectKey}`, jiraHeaders(auth), {
49
+ 401: '✗ Jira auth failed — check credentials',
50
+ 403: '✗ Jira permission denied for this project',
51
+ 404: `✗ Jira project "${projectKey}" not found`,
52
+ }, '✗ Could not reach Jira — check network');
53
+ }
54
+ /**
55
+ * Build the JQL query for fetching the next ticket.
56
+ *
57
+ * @param projectKey - The Jira project key.
58
+ * @param status - The JQL status to filter by (default: `"To Do"`).
59
+ * @param sprint - If set, adds an open sprint filter.
60
+ * @param label - If set, adds a label filter.
61
+ * @returns The JQL query string.
62
+ */
63
+ export function buildJql(projectKey, status, sprint, label) {
64
+ const parts = [`project="${projectKey}"`];
65
+ if (sprint)
66
+ parts.push('sprint in openSprints()');
67
+ if (label)
68
+ parts.push(`labels = "${label}"`);
69
+ parts.push(`assignee=currentUser()`);
70
+ parts.push(`status="${status}"`);
71
+ parts.push('ORDER BY priority ASC');
72
+ return parts.join(' AND ');
73
+ }
74
+ /**
75
+ * Extract all text strings from a Jira ADF (Atlassian Document Format) description.
76
+ *
77
+ * Recursively walks the ADF tree and collects all string values.
78
+ *
79
+ * @param adf - The ADF description object (or `undefined`).
80
+ * @returns A single string with all text content joined by spaces.
81
+ */
82
+ export function extractAdfText(adf) {
83
+ if (!adf || typeof adf !== 'object')
84
+ return '';
85
+ const strings = [];
86
+ function walk(node) {
87
+ if (typeof node === 'string') {
88
+ strings.push(node);
89
+ return;
90
+ }
91
+ if (Array.isArray(node)) {
92
+ for (const item of node)
93
+ walk(item);
94
+ return;
95
+ }
96
+ if (node && typeof node === 'object') {
97
+ for (const value of Object.values(node)) {
98
+ walk(value);
99
+ }
100
+ }
101
+ }
102
+ walk(adf);
103
+ return strings.join(' ');
104
+ }
105
+ /**
106
+ * Fetch the next available ticket from Jira.
107
+ *
108
+ * @param baseUrl - The Jira Cloud base URL.
109
+ * @param auth - The Base64-encoded Basic auth string.
110
+ * @param projectKey - The Jira project key.
111
+ * @param status - The JQL status to filter by.
112
+ * @param sprint - Optional sprint filter.
113
+ * @param label - Optional label filter.
114
+ * @returns The fetched ticket, or `undefined` if no tickets are available.
115
+ */
116
+ export async function fetchTicket(baseUrl, auth, projectKey, status, sprint, label) {
117
+ const jql = buildJql(projectKey, status, sprint, label);
118
+ let response;
119
+ try {
120
+ response = await fetch(`${baseUrl}/rest/api/3/search/jql`, {
121
+ method: 'POST',
122
+ headers: {
123
+ ...jiraHeaders(auth),
124
+ 'Content-Type': 'application/json',
125
+ },
126
+ body: JSON.stringify({
127
+ jql,
128
+ maxResults: 1,
129
+ fields: [
130
+ 'summary',
131
+ 'description',
132
+ 'issuelinks',
133
+ 'parent',
134
+ 'customfield_10014',
135
+ ],
136
+ }),
137
+ });
138
+ }
139
+ catch (err) {
140
+ console.warn(`⚠ Jira API request failed: ${err instanceof Error ? err.message : String(err)}`);
141
+ return undefined;
142
+ }
143
+ if (!response.ok) {
144
+ console.warn(`⚠ Jira API returned HTTP ${response.status}`);
145
+ return undefined;
146
+ }
147
+ let json;
148
+ try {
149
+ json = await response.json();
150
+ }
151
+ catch {
152
+ console.warn('⚠ Jira API returned invalid JSON');
153
+ return undefined;
154
+ }
155
+ const parsed = jiraSearchResponseSchema.safeParse(json);
156
+ if (!parsed.success) {
157
+ console.warn(`⚠ Unexpected Jira response shape: ${parsed.error.message}`);
158
+ return undefined;
159
+ }
160
+ if (!parsed.data.issues.length)
161
+ return undefined;
162
+ const issue = parsed.data.issues[0];
163
+ const fields = issue.fields;
164
+ // Extract blockers
165
+ const blockers = (fields.issuelinks ?? [])
166
+ .filter((link) => link.type?.name === 'Blocks' && link.inwardIssue?.key)
167
+ .map((link) => link.inwardIssue?.key)
168
+ .filter((key) => Boolean(key));
169
+ // Extract epic (next-gen parent OR classic customfield)
170
+ const epicKey = fields.parent?.key ?? fields.customfield_10014 ?? undefined;
171
+ return {
172
+ key: issue.key,
173
+ title: fields.summary,
174
+ description: extractAdfText(fields.description),
175
+ provider: 'jira',
176
+ epicKey,
177
+ blockers,
178
+ };
179
+ }
180
+ /**
181
+ * Look up a Jira transition ID by status name.
182
+ *
183
+ * @param baseUrl - The Jira Cloud base URL.
184
+ * @param auth - The Base64-encoded Basic auth string.
185
+ * @param issueKey - The Jira issue key (e.g., `'PROJ-123'`).
186
+ * @param statusName - The target status name (e.g., `'In Progress'`).
187
+ * @returns The transition ID, or `undefined` if not found.
188
+ */
189
+ export async function lookupTransitionId(baseUrl, auth, issueKey, statusName) {
190
+ if (!ISSUE_KEY_PATTERN.test(issueKey))
191
+ return undefined;
192
+ let response;
193
+ try {
194
+ response = await fetch(`${baseUrl}/rest/api/3/issue/${issueKey}/transitions`, { headers: jiraHeaders(auth) });
195
+ }
196
+ catch (err) {
197
+ console.warn(`⚠ Jira transitions request failed: ${err instanceof Error ? err.message : String(err)}`);
198
+ return undefined;
199
+ }
200
+ if (!response.ok)
201
+ return undefined;
202
+ let json;
203
+ try {
204
+ json = await response.json();
205
+ }
206
+ catch {
207
+ console.warn('⚠ Jira transitions returned invalid JSON');
208
+ return undefined;
209
+ }
210
+ const parsed = jiraTransitionsResponseSchema.safeParse(json);
211
+ if (!parsed.success) {
212
+ console.warn(`⚠ Unexpected Jira transitions response: ${parsed.error.message}`);
213
+ return undefined;
214
+ }
215
+ const transition = parsed.data.transitions.find((t) => t.name === statusName);
216
+ return transition?.id;
217
+ }
218
+ /**
219
+ * Transition a Jira issue to a new status.
220
+ *
221
+ * Fetches available transitions and executes the one matching the target status name.
222
+ * Best-effort — never throws on failure.
223
+ *
224
+ * @param baseUrl - The Jira Cloud base URL.
225
+ * @param auth - The Base64-encoded Basic auth string.
226
+ * @param issueKey - The Jira issue key (e.g., `'PROJ-123'`).
227
+ * @param statusName - The target status name (e.g., `'In Progress'`).
228
+ * @returns `true` if the transition succeeded.
229
+ */
230
+ export async function transitionIssue(baseUrl, auth, issueKey, statusName) {
231
+ try {
232
+ const transitionId = await lookupTransitionId(baseUrl, auth, issueKey, statusName);
233
+ if (!transitionId) {
234
+ console.warn(`⚠ Jira transition "${statusName}" not found for ${issueKey}`);
235
+ return false;
236
+ }
237
+ const response = await fetch(`${baseUrl}/rest/api/3/issue/${issueKey}/transitions`, {
238
+ method: 'POST',
239
+ headers: {
240
+ ...jiraHeaders(auth),
241
+ 'Content-Type': 'application/json',
242
+ },
243
+ body: JSON.stringify({ transition: { id: transitionId } }),
244
+ });
245
+ return response.ok;
246
+ }
247
+ catch {
248
+ return false;
249
+ }
250
+ }
251
+ //# sourceMappingURL=jira.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira.js","sourceRoot":"","sources":["../../../../src/scripts/board/jira/jira.ts"],"names":[],"mappings":"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"}