issue-scribe-mcp 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["**"]
6
+ pull_request:
7
+
8
+ jobs:
9
+ build-and-test:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Setup Node.js
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: "20"
20
+ cache: npm
21
+
22
+ - name: Install dependencies
23
+ run: npm ci
24
+
25
+ - name: Build
26
+ run: npm run build
27
+
28
+ - name: Run tests
29
+ run: npm test
package/README.md CHANGED
@@ -11,11 +11,13 @@
11
11
 
12
12
  ## ✨ 주요 기능
13
13
 
14
- - 🔍 **컨텍스트 조회**: IssuePR의 상세 정보, 댓글, 커밋 내역 전체 컨텍스트 수집
15
- - 📝 **Issue 관리**: 새로운 Issue 생성 기존 Issue 업데이트
16
- - 🔀 **PR 생성**: Pull Request 자동 생성 Draft PR 지원
17
- - 🤖 **AI 통합**: Claude Desktop MCP를 지원하는 AI 도구와 완벽 통합
18
- - 🔐 **간편 인증**: GitHub Personal Access Token을 통한 안전한 API 접근
14
+ - 🔍 **고급 컨텍스트 조회**: Issue/PR의 본문, 댓글, 커밋, 리뷰, 리뷰 코멘트, 변경 파일, CI 상태까지 수집
15
+ - 🔎 **검색 고도화**: GitHub Search API 기반 이슈/PR 검색 + qualifier 지원 (`author:`, `label:`, `is:` 등)
16
+ - 📄 **페이지네이션 지원**: `page`, `per_page`, `fetch_all`로 대규모 저장소 데이터 안정적 조회
17
+ - 🛡️ **안전 실행 모드**: merge/delete 계열 작업에 `dry_run`, `expected_*`, `confirm_token` 보호장치
18
+ - 📝 **Issue/PR 관리**: 이슈 생성/수정, PR 생성, 코멘트/리액션, 라벨/브랜치 관리
19
+ - 🤖 **AI 통합**: Claude Desktop 등 MCP를 지원하는 AI 도구와 통합
20
+ - 🔐 **간편 인증**: GitHub Personal Access Token 기반 인증
19
21
 
20
22
  ## 📋 사전 준비
21
23
 
@@ -39,6 +41,8 @@
39
41
  GITHUB_TOKEN=your_github_personal_access_token_here
40
42
  ```
41
43
 
44
+ 서버 실행 시 `dotenv`로 `.env`를 자동 로드합니다.
45
+
42
46
  ## 🚀 설치
43
47
 
44
48
  ### NPM을 통한 전역 설치
@@ -80,6 +84,9 @@ npm install
80
84
  # 빌드
81
85
  npm run build
82
86
 
87
+ # 테스트
88
+ npm test
89
+
83
90
  # 환경변수 설정
84
91
  cp .env.example .env
85
92
  # .env 파일에 GitHub Token 입력
@@ -131,6 +138,11 @@ Claude Desktop의 설정 파일(`claude_desktop_config.json`)에 다음 내용
131
138
 
132
139
  ## 🛠️ 제공 Tools
133
140
 
141
+ ### 공통 옵션
142
+ - 대부분의 목록/검색 Tool은 `page`, `per_page`, `fetch_all`을 지원합니다.
143
+ - 위험 작업(`github_merge_pr`, `github_delete_comment`, `github_delete_branch`, `github_delete_label`)은 `dry_run`을 지원하며, 실제 실행 시 `confirm_token: "CONFIRM"`이 필요합니다.
144
+ - 검색 Tool(`github_search_issues`, `github_search_prs`)은 `qualifiers`를 지원합니다 (예: `author:octocat`, `label:bug`, `is:draft`).
145
+
134
146
  ### github_get_issue_context
135
147
  GitHub Issue의 전체 컨텍스트를 조회합니다.
136
148
 
@@ -138,6 +150,9 @@ GitHub Issue의 전체 컨텍스트를 조회합니다.
138
150
  - `owner` (string, 필수): 저장소 소유자
139
151
  - `repo` (string, 필수): 저장소 이름
140
152
  - `issue_number` (number, 필수): 이슈 번호
153
+ - `comments_page` (number, 옵션): 댓글 페이지 번호
154
+ - `comments_per_page` (number, 옵션): 댓글 페이지당 개수 (최대 100)
155
+ - `comments_fetch_all` (boolean, 옵션): 모든 댓글 페이지 조회 여부 (기본: `true`)
141
156
 
142
157
  **반환 정보:**
143
158
  - Issue 제목, 본문, 상태
@@ -152,6 +167,11 @@ GitHub Pull Request의 전체 컨텍스트를 조회합니다 (커밋 포함).
152
167
  - `owner` (string, 필수): 저장소 소유자
153
168
  - `repo` (string, 필수): 저장소 이름
154
169
  - `pull_number` (number, 필수): PR 번호
170
+ - `include_reviews` (boolean, 옵션): 리뷰 정보 포함 여부 (기본: `true`)
171
+ - `include_review_comments` (boolean, 옵션): 라인 리뷰 코멘트 포함 여부 (기본: `true`)
172
+ - `include_files` (boolean, 옵션): 변경 파일 정보 포함 여부 (기본: `true`)
173
+ - `include_ci` (boolean, 옵션): CI/check status 포함 여부 (기본: `true`)
174
+ - `page` / `per_page` / `fetch_all` (옵션): PR 컨텍스트 내부 목록 페이지네이션
155
175
 
156
176
  **반환 정보:**
157
177
  - PR 제목, 본문, 상태
@@ -225,6 +245,9 @@ GitHub Issue 또는 Pull Request에 댓글을 추가합니다.
225
245
  - `owner` (string, 필수): 저장소 소유자
226
246
  - `repo` (string, 필수): 저장소 이름
227
247
  - `comment_id` (number, 필수): 삭제할 댓글 ID
248
+ - `dry_run` (boolean, 옵션): 실제 삭제 없이 미리보기
249
+ - `confirm_token` (string, 옵션): 실제 삭제 시 `"CONFIRM"` 필요
250
+ - `expected_body_substring` (string, 옵션): 댓글 본문 보호 조건 (부분 문자열 일치 시에만 삭제)
228
251
 
229
252
  ### github_add_reaction
230
253
  댓글 또는 Issue/PR에 이모지 반응을 추가합니다.
@@ -239,6 +262,77 @@ GitHub Issue 또는 Pull Request에 댓글을 추가합니다.
239
262
 
240
263
  **참고**: `comment_id` 또는 `issue_number` 중 하나를 반드시 제공해야 합니다.
241
264
 
265
+ ### github_search_issues
266
+ GitHub Search API 기반으로 이슈를 검색합니다.
267
+
268
+ **파라미터:**
269
+ - `owner` (string, 필수): 저장소 소유자
270
+ - `repo` (string, 필수): 저장소 이름
271
+ - `query` (string, 옵션): 검색어
272
+ - `state` (string, 옵션): `"open"`, `"closed"`, `"all"`
273
+ - `labels` (string[], 옵션): 라벨 필터
274
+ - `qualifiers` (string[], 옵션): 추가 검색 qualifier (예: `author:octocat`)
275
+ - `sort` (string, 옵션): `"created"`, `"updated"`, `"comments"`, `"best-match"`
276
+ - `direction` (string, 옵션): `"asc"`, `"desc"`
277
+ - `page` / `per_page` / `fetch_all` (옵션): 페이지네이션
278
+
279
+ ### github_search_prs
280
+ GitHub Search API 기반으로 PR을 검색합니다.
281
+
282
+ **파라미터:**
283
+ - `owner` (string, 필수): 저장소 소유자
284
+ - `repo` (string, 필수): 저장소 이름
285
+ - `query` (string, 옵션): 검색어
286
+ - `state` (string, 옵션): `"open"`, `"closed"`, `"all"`
287
+ - `qualifiers` (string[], 옵션): 추가 검색 qualifier (예: `author:octocat`, `is:draft`)
288
+ - `sort` (string, 옵션): `"created"`, `"updated"`, `"comments"`, `"best-match"`
289
+ - `direction` (string, 옵션): `"asc"`, `"desc"`
290
+ - `page` / `per_page` / `fetch_all` (옵션): 페이지네이션
291
+
292
+ ### github_list_recent_issues
293
+ 최근 이슈 목록을 조회합니다.
294
+
295
+ **파라미터:**
296
+ - `owner` (string, 필수): 저장소 소유자
297
+ - `repo` (string, 필수): 저장소 이름
298
+ - `state` (string, 옵션): `"open"`, `"closed"`, `"all"`
299
+ - `sort` (string, 옵션): `"created"`, `"updated"`, `"comments"`
300
+ - `direction` (string, 옵션): `"asc"`, `"desc"`
301
+ - `page` / `per_page` / `fetch_all` (옵션): 페이지네이션
302
+
303
+ ### github_merge_pr
304
+ PR을 머지합니다.
305
+
306
+ **파라미터:**
307
+ - `owner` (string, 필수): 저장소 소유자
308
+ - `repo` (string, 필수): 저장소 이름
309
+ - `pull_number` (number, 필수): PR 번호
310
+ - `merge_method` (string, 옵션): `"merge"`, `"squash"`, `"rebase"`
311
+ - `commit_title` (string, 옵션): 머지 커밋 제목
312
+ - `commit_message` (string, 옵션): 머지 커밋 메시지
313
+ - `dry_run` (boolean, 옵션): 실제 머지 없이 미리보기
314
+ - `expected_head_sha` (string, 옵션): PR HEAD SHA 보호 조건
315
+ - `confirm_token` (string, 옵션): 실제 머지 시 `"CONFIRM"` 필요
316
+
317
+ ### github_get_pr_diff
318
+ PR의 diff를 조회합니다.
319
+
320
+ **파라미터:**
321
+ - `owner` (string, 필수): 저장소 소유자
322
+ - `repo` (string, 필수): 저장소 이름
323
+ - `pull_number` (number, 필수): PR 번호
324
+ - `max_chars` (number, 옵션): diff 최대 출력 길이
325
+
326
+ ### github_get_pr_files
327
+ PR 변경 파일 목록을 조회합니다.
328
+
329
+ **파라미터:**
330
+ - `owner` (string, 필수): 저장소 소유자
331
+ - `repo` (string, 필수): 저장소 이름
332
+ - `pull_number` (number, 필수): PR 번호
333
+ - `include_patch` (boolean, 옵션): 파일별 patch 포함 여부
334
+ - `page` / `per_page` / `fetch_all` (옵션): 페이지네이션
335
+
242
336
  ### github_create_label
243
337
  저장소에 새로운 라벨을 생성합니다.
244
338
 
@@ -271,6 +365,8 @@ GitHub Issue 또는 Pull Request에 댓글을 추가합니다.
271
365
  - `owner` (string, 필수): 저장소 소유자
272
366
  - `repo` (string, 필수): 저장소 이름
273
367
  - `name` (string, 필수): 삭제할 라벨 이름
368
+ - `dry_run` (boolean, 옵션): 실제 삭제 없이 미리보기
369
+ - `confirm_token` (string, 옵션): 실제 삭제 시 `"CONFIRM"` 필요
274
370
 
275
371
  ### github_list_labels
276
372
  저장소의 모든 라벨 목록을 조회합니다.
@@ -278,7 +374,9 @@ GitHub Issue 또는 Pull Request에 댓글을 추가합니다.
278
374
  **파라미터:**
279
375
  - `owner` (string, 필수): 저장소 소유자
280
376
  - `repo` (string, 필수): 저장소 이름
377
+ - `page` (number, 옵션): 페이지 번호
281
378
  - `per_page` (number, 옵션): 페이지당 결과 수, 최대 100 (기본값: 30)
379
+ - `fetch_all` (boolean, 옵션): 모든 페이지 조회 여부
282
380
 
283
381
  **반환 정보:**
284
382
  - 라벨 개수
@@ -291,7 +389,9 @@ GitHub Issue 또는 Pull Request에 댓글을 추가합니다.
291
389
  - `owner` (string, 필수): 저장소 소유자
292
390
  - `repo` (string, 필수): 저장소 이름
293
391
  - `protected` (boolean, 옵션): 보호된 브랜치만 필터링
392
+ - `page` (number, 옵션): 페이지 번호
294
393
  - `per_page` (number, 옵션): 페이지당 결과 수, 최대 100 (기본값: 30)
394
+ - `fetch_all` (boolean, 옵션): 모든 페이지 조회 여부
295
395
 
296
396
  **반환 정보:**
297
397
  - 브랜치 개수
@@ -317,6 +417,9 @@ GitHub Issue 또는 Pull Request에 댓글을 추가합니다.
317
417
  - `owner` (string, 필수): 저장소 소유자
318
418
  - `repo` (string, 필수): 저장소 이름
319
419
  - `branch` (string, 필수): 삭제할 브랜치 이름
420
+ - `dry_run` (boolean, 옵션): 실제 삭제 없이 미리보기
421
+ - `expected_sha` (string, 옵션): 브랜치 HEAD SHA 보호 조건
422
+ - `confirm_token` (string, 옵션): 실제 삭제 시 `"CONFIRM"` 필요
320
423
 
321
424
  ### github_compare_branches
322
425
  두 브랜치 간의 차이를 비교합니다.
@@ -326,6 +429,8 @@ GitHub Issue 또는 Pull Request에 댓글을 추가합니다.
326
429
  - `repo` (string, 필수): 저장소 이름
327
430
  - `base` (string, 필수): 기준 브랜치 이름
328
431
  - `head` (string, 필수): 비교할 브랜치 이름
432
+ - `max_commits` (number, 옵션): 응답에 포함할 최대 커밋 수
433
+ - `max_files` (number, 옵션): 응답에 포함할 최대 파일 수
329
434
 
330
435
  **반환 정보:**
331
436
  - 비교 상태 (ahead/behind)
package/README_EN.md CHANGED
@@ -11,9 +11,11 @@
11
11
 
12
12
  ## ✨ Features
13
13
 
14
- - 🔍 **Context Retrieval**: Collect complete context including issue/PR details, comments, and commit history
15
- - 📝 **Issue Management**: Create new issues and update existing ones
16
- - 🔀 **PR Creation**: Automatically create Pull Requests with Draft PR support
14
+ - 🔍 **Deep Context Retrieval**: Collect issue/PR body, comments, commits, reviews, review comments, changed files, and CI status
15
+ - 🔎 **Advanced Search**: Search issues/PRs through GitHub Search API with custom qualifiers (`author:`, `label:`, `is:`, etc.)
16
+ - 📄 **Pagination Support**: Use `page`, `per_page`, and `fetch_all` for large repositories
17
+ - 🛡️ **Safe Execution Mode**: `dry_run`, `expected_*`, and `confirm_token` safeguards for merge/delete operations
18
+ - 📝 **Issue/PR Management**: Create/update issues, create PRs, manage comments/reactions/labels/branches
17
19
  - 🤖 **AI Integration**: Seamless integration with MCP-compatible AI tools like Claude Desktop
18
20
  - 🔐 **Simple Authentication**: Secure API access via GitHub Personal Access Token
19
21
 
@@ -39,6 +41,8 @@ Create a `.env` file in the project root:
39
41
  GITHUB_TOKEN=your_github_personal_access_token_here
40
42
  ```
41
43
 
44
+ The server automatically loads `.env` via `dotenv` at startup.
45
+
42
46
  ## 🚀 Installation
43
47
 
44
48
  ### Global Installation via NPM
@@ -80,6 +84,9 @@ npm install
80
84
  # Build
81
85
  npm run build
82
86
 
87
+ # Test
88
+ npm test
89
+
83
90
  # Set up environment variables
84
91
  cp .env.example .env
85
92
  # Add your GitHub Token to .env file
@@ -131,6 +138,11 @@ Restart Claude Desktop after configuration.
131
138
 
132
139
  ## 🛠️ Available Tools
133
140
 
141
+ ### Shared Options
142
+ - Most list/search tools support `page`, `per_page`, and `fetch_all`.
143
+ - Risky operations (`github_merge_pr`, `github_delete_comment`, `github_delete_branch`, `github_delete_label`) support `dry_run`; live execution requires `confirm_token: "CONFIRM"`.
144
+ - Search tools (`github_search_issues`, `github_search_prs`) support `qualifiers` (for example `author:octocat`, `label:bug`, `is:draft`).
145
+
134
146
  ### github_get_issue_context
135
147
  Retrieve full context of a GitHub Issue.
136
148
 
@@ -138,6 +150,9 @@ Retrieve full context of a GitHub Issue.
138
150
  - `owner` (string, required): Repository owner
139
151
  - `repo` (string, required): Repository name
140
152
  - `issue_number` (number, required): Issue number
153
+ - `comments_page` (number, optional): Comment page number
154
+ - `comments_per_page` (number, optional): Comments per page (max 100)
155
+ - `comments_fetch_all` (boolean, optional): Fetch all comment pages (default: `true`)
141
156
 
142
157
  **Returns:**
143
158
  - Issue title, body, state
@@ -152,6 +167,11 @@ Retrieve full context of a GitHub Pull Request (including commits).
152
167
  - `owner` (string, required): Repository owner
153
168
  - `repo` (string, required): Repository name
154
169
  - `pull_number` (number, required): PR number
170
+ - `include_reviews` (boolean, optional): Include reviews + approval summary (default: `true`)
171
+ - `include_review_comments` (boolean, optional): Include line-level review comments (default: `true`)
172
+ - `include_files` (boolean, optional): Include changed files (default: `true`)
173
+ - `include_ci` (boolean, optional): Include CI/check status (default: `true`)
174
+ - `page` / `per_page` / `fetch_all` (optional): Pagination for PR context collections
155
175
 
156
176
  **Returns:**
157
177
  - PR title, body, state
@@ -227,6 +247,9 @@ Delete a comment.
227
247
  - `owner` (string, required): Repository owner
228
248
  - `repo` (string, required): Repository name
229
249
  - `comment_id` (number, required): Comment ID to delete
250
+ - `dry_run` (boolean, optional): Preview deletion without executing
251
+ - `confirm_token` (string, optional): Required as `"CONFIRM"` for live deletion
252
+ - `expected_body_substring` (string, optional): Guard condition; delete only if body contains this substring
230
253
 
231
254
  ### github_add_reaction
232
255
  Add an emoji reaction to a comment or directly to an issue/PR.
@@ -241,6 +264,77 @@ Add an emoji reaction to a comment or directly to an issue/PR.
241
264
 
242
265
  **Note**: Either `comment_id` OR `issue_number` must be provided.
243
266
 
267
+ ### github_search_issues
268
+ Search repository issues using GitHub Search API.
269
+
270
+ **Parameters:**
271
+ - `owner` (string, required): Repository owner
272
+ - `repo` (string, required): Repository name
273
+ - `query` (string, optional): Search text
274
+ - `state` (string, optional): `"open"`, `"closed"`, `"all"`
275
+ - `labels` (string[], optional): Label filters
276
+ - `qualifiers` (string[], optional): Extra qualifiers (for example `author:octocat`)
277
+ - `sort` (string, optional): `"created"`, `"updated"`, `"comments"`, `"best-match"`
278
+ - `direction` (string, optional): `"asc"`, `"desc"`
279
+ - `page` / `per_page` / `fetch_all` (optional): Pagination controls
280
+
281
+ ### github_search_prs
282
+ Search repository pull requests using GitHub Search API.
283
+
284
+ **Parameters:**
285
+ - `owner` (string, required): Repository owner
286
+ - `repo` (string, required): Repository name
287
+ - `query` (string, optional): Search text
288
+ - `state` (string, optional): `"open"`, `"closed"`, `"all"`
289
+ - `qualifiers` (string[], optional): Extra qualifiers (for example `author:octocat`, `is:draft`)
290
+ - `sort` (string, optional): `"created"`, `"updated"`, `"comments"`, `"best-match"`
291
+ - `direction` (string, optional): `"asc"`, `"desc"`
292
+ - `page` / `per_page` / `fetch_all` (optional): Pagination controls
293
+
294
+ ### github_list_recent_issues
295
+ List recent issues in a repository.
296
+
297
+ **Parameters:**
298
+ - `owner` (string, required): Repository owner
299
+ - `repo` (string, required): Repository name
300
+ - `state` (string, optional): `"open"`, `"closed"`, `"all"`
301
+ - `sort` (string, optional): `"created"`, `"updated"`, `"comments"`
302
+ - `direction` (string, optional): `"asc"`, `"desc"`
303
+ - `page` / `per_page` / `fetch_all` (optional): Pagination controls
304
+
305
+ ### github_merge_pr
306
+ Merge a pull request.
307
+
308
+ **Parameters:**
309
+ - `owner` (string, required): Repository owner
310
+ - `repo` (string, required): Repository name
311
+ - `pull_number` (number, required): PR number
312
+ - `merge_method` (string, optional): `"merge"`, `"squash"`, `"rebase"`
313
+ - `commit_title` (string, optional): Merge commit title
314
+ - `commit_message` (string, optional): Merge commit message
315
+ - `dry_run` (boolean, optional): Preview merge without executing
316
+ - `expected_head_sha` (string, optional): Guard condition for PR head SHA
317
+ - `confirm_token` (string, optional): Required as `"CONFIRM"` for live merge
318
+
319
+ ### github_get_pr_diff
320
+ Get the PR diff.
321
+
322
+ **Parameters:**
323
+ - `owner` (string, required): Repository owner
324
+ - `repo` (string, required): Repository name
325
+ - `pull_number` (number, required): PR number
326
+ - `max_chars` (number, optional): Maximum diff length in characters
327
+
328
+ ### github_get_pr_files
329
+ List PR changed files.
330
+
331
+ **Parameters:**
332
+ - `owner` (string, required): Repository owner
333
+ - `repo` (string, required): Repository name
334
+ - `pull_number` (number, required): PR number
335
+ - `include_patch` (boolean, optional): Include patch text per file
336
+ - `page` / `per_page` / `fetch_all` (optional): Pagination controls
337
+
244
338
  ### github_create_label
245
339
  Create a new label in the repository.
246
340
 
@@ -273,6 +367,8 @@ Delete a label from the repository.
273
367
  - `owner` (string, required): Repository owner
274
368
  - `repo` (string, required): Repository name
275
369
  - `name` (string, required): Label name to delete
370
+ - `dry_run` (boolean, optional): Preview deletion without executing
371
+ - `confirm_token` (string, optional): Required as `"CONFIRM"` for live deletion
276
372
 
277
373
  ### github_list_labels
278
374
  List all labels in the repository.
@@ -280,7 +376,9 @@ List all labels in the repository.
280
376
  **Parameters:**
281
377
  - `owner` (string, required): Repository owner
282
378
  - `repo` (string, required): Repository name
379
+ - `page` (number, optional): Page number
283
380
  - `per_page` (number, optional): Results per page, max 100 (default: 30)
381
+ - `fetch_all` (boolean, optional): Fetch all pages
284
382
 
285
383
  **Returns:**
286
384
  - Label count
@@ -293,7 +391,9 @@ List all branches in the repository.
293
391
  - `owner` (string, required): Repository owner
294
392
  - `repo` (string, required): Repository name
295
393
  - `protected` (boolean, optional): Filter by protected status
394
+ - `page` (number, optional): Page number
296
395
  - `per_page` (number, optional): Results per page, max 100 (default: 30)
396
+ - `fetch_all` (boolean, optional): Fetch all pages
297
397
 
298
398
  **Returns:**
299
399
  - Branch count
@@ -319,6 +419,9 @@ Delete a branch from the repository.
319
419
  - `owner` (string, required): Repository owner
320
420
  - `repo` (string, required): Repository name
321
421
  - `branch` (string, required): Branch name to delete
422
+ - `dry_run` (boolean, optional): Preview deletion without executing
423
+ - `expected_sha` (string, optional): Guard condition for branch HEAD SHA
424
+ - `confirm_token` (string, optional): Required as `"CONFIRM"` for live deletion
322
425
 
323
426
  ### github_compare_branches
324
427
  Compare two branches and show the differences.
@@ -328,6 +431,8 @@ Compare two branches and show the differences.
328
431
  - `repo` (string, required): Repository name
329
432
  - `base` (string, required): Base branch name
330
433
  - `head` (string, required): Head branch name to compare
434
+ - `max_commits` (number, optional): Maximum commits returned in response
435
+ - `max_files` (number, optional): Maximum files returned in response
331
436
 
332
437
  **Returns:**
333
438
  - Comparison status (ahead/behind)