Skip to content

Commit 8143d11

Browse files
authored
feat: Support for GitHub Sub-Issues
feat: integrate Obsidian Keychain bugfix: use global Template docs: create Wiki and remove inline template help Simplify settings descriptions
2 parents 785c8b5 + cba08fa commit 8143d11

24 files changed

Lines changed: 826 additions & 785 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ main.js
1818
data.json
1919

2020
.DS_Store
21+
22+
# Snyk Security Extension - AI Rules (auto-generated)
23+
.github/instructions/snyk_rules.instructions.md

README.md

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,65 +5,74 @@ An Obsidian plugin that integrates with GitHub to track issues and pull requests
55

66
>The configurations are heavily inspired by https://github.com/schaier-io, including some specific settings. However, I had already started working on my prototype before I discovered the plugin, and had initially even given it a similar name.
77
8-
## ✨ Features
8+
# Documentation
9+
Check out the [documentation](https://github.com/LonoxX/obsidian-github-issues/wiki) for detailed information on setup, configuration, and usage.
910

10-
### 🔄 Issue & Pull Request Tracking
11+
## Features
12+
13+
### Issue & Pull Request Tracking
1114
- Track issues and pull requests from multiple GitHub repositories
1215
- Automatically sync GitHub data on startup (configurable)
1316
- Background sync at configurable intervals
1417
- Filter by labels, assignees, and reviewers
1518
- Include or exclude closed issues/PRs
1619
- Automatic cleanup of old closed items
1720

18-
### 📊 GitHub Projects v2 Integration
21+
### GitHub Projects v2 Integration
1922
- Track GitHub Projects across repositories
2023
- Kanban board view for project visualization
2124
- Custom field support (status, priority, iteration)
2225
- Project-specific filtering and organization
2326

24-
### 📝 Markdown Notes
27+
### Sub-Issues Support
28+
- Track GitHub sub-issues (parent/child relationships)
29+
- Display sub-issues list with status indicators
30+
- Navigate between parent and child issues
31+
- Progress tracking with completion percentage
32+
33+
### Markdown Notes
2534
- Create markdown notes for each issue or PR
2635
- Customizable filename templates with variables
2736
- Custom content templates
2837
- YAML frontmatter with metadata
2938
- Preserve user content with persist blocks
3039
- Include comments in notes
3140

32-
## 🚀 Installation
41+
## Installation
42+
43+
### Via Community Plugins (Recommended)
3344

34-
### Via Obsidian Community Plugins
35-
1. Open Obsidian settings
45+
1. Open Obsidian Settings
3646
2. Navigate to **Community Plugins**
3747
3. Click **Browse** and search for "GitHub Issues"
3848
4. Click **Install** and then **Enable**
3949

4050
### Manual Installation
4151

42-
1. Download the latest release from the [GitHub Releases page](https://github.com/LonoxX/obsidian-github-issues/releases).
43-
2. Extract the contents into your Obsidian plugins folder:
44-
`<vault>/.obsidian/plugins/github-issues/`
45-
3. Enable the plugin in Obsidian under **Community Plugins**
46-
4. Reload or restart Obsidian
52+
1. Download the latest release from [GitHub Releases](https://github.com/LonoxX/obsidian-github-issues/releases)
53+
2. Extract to `<vault>/.obsidian/plugins/github-issues/`
54+
3. Enable the plugin in **Community Plugins**
55+
4. Reload Obsidian
4756

48-
## ⚙️ Configuration
57+
58+
## Configuration
4959

5060
1. Create a new GitHub token with the `repo` and `read:org` permissions
5161
[GitHub Settings > Developer Settings > Personal access tokens](https://github.com/settings/tokens)
5262
2. Configure the plugin in Obsidian settings:
5363
- Paste your GitHub token in the **GitHub Token** field
5464
- Adjust additional settings as needed
5565

56-
## 📦 Adding Repositories
66+
## Adding Repositories
5767

5868
1. Open the plugin settings in Obsidian
5969
2. Add repositories by entering the full GitHub repository path (e.g., `lonoxx/obsidian-github-issues`),
6070
or use the repository browser to select one or multiple repositories
6171
3. Click **Add Repository** or **Add Selected Repositories**
6272
4. The plugin will automatically fetch issues from the configured repositories
6373

64-
### ⭐ This repository if you like this project!
65-
74+
## Support
6675

67-
## 📄 License
76+
If you find this plugin useful and would like to support its development, you can star the repository or support me on Ko-fi or [GitHub Sponsors](https://github.com/sponsors/LonoxX):
6877

69-
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
78+
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/LonoxX)

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
"author": "LonoxX",
2020
"license": "MIT",
2121
"devDependencies": {
22-
"@types/node": "^25.0.3",
23-
"@typescript-eslint/eslint-plugin": "^8.52.0",
24-
"@typescript-eslint/parser": "^8.52.0",
22+
"@types/node": "^25.0.8",
23+
"@typescript-eslint/eslint-plugin": "^8.53.0",
24+
"@typescript-eslint/parser": "^8.53.0",
2525
"builtin-modules": "^5.0.0",
2626
"esbuild": "^0.27.2",
2727
"eslint": "^9.39.2",
28-
"obsidian": "latest",
28+
"obsidian": "^1.11.4",
2929
"tslib": "^2.8.1",
3030
"typescript": "^5.9.3"
3131
},

src/content-generator.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export class ContentGenerator {
2222
comments: any[],
2323
settings: GitHubTrackerSettings,
2424
projectData?: ProjectData[],
25+
subIssues?: any[],
26+
parentIssue?: any,
2527
): Promise<string> {
2628
// Determine whether to escape hash tags (repo setting takes precedence if ignoreGlobalSettings is true)
2729
const shouldEscapeHashTags = repo.ignoreGlobalSettings ? repo.escapeHashTags : settings.escapeHashTags;
@@ -37,7 +39,9 @@ export class ContentGenerator {
3739
settings.dateFormat,
3840
settings.escapeMode,
3941
shouldEscapeHashTags,
40-
projectData
42+
projectData,
43+
subIssues,
44+
parentIssue
4145
);
4246
return processContentTemplate(templateContent, templateData, settings.dateFormat);
4347
}
@@ -74,6 +78,24 @@ labels: [${(
7478
updateMode: "${repo.issueUpdateMode}"
7579
allowDelete: ${repo.allowDeleteIssue ? true : false}`;
7680

81+
// Add parent issue if available
82+
if (parentIssue) {
83+
frontmatter += `
84+
parent_issue: ${parentIssue.number}
85+
parent_issue_url: "${parentIssue.url}"`;
86+
}
87+
88+
// Add sub-issues metadata if available
89+
if (subIssues && subIssues.length > 0) {
90+
const closedCount = subIssues.filter((si: any) => si.state === "closed").length;
91+
const openCount = subIssues.length - closedCount;
92+
frontmatter += `
93+
sub_issues: [${subIssues.map((si: any) => si.number).join(", ")}]
94+
sub_issues_count: ${subIssues.length}
95+
sub_issues_open: ${openCount}
96+
sub_issues_closed: ${closedCount}`;
97+
}
98+
7799
// Add projectData if available
78100
if (projectData && projectData.length > 0) {
79101
frontmatter += `
@@ -96,6 +118,27 @@ ${
96118
97119
${this.fileHelpers.formatComments(comments, settings.escapeMode, settings.dateFormat, shouldEscapeHashTags)}`;
98120

121+
// Add sub-issues section if available
122+
if (subIssues && subIssues.length > 0) {
123+
frontmatter += `
124+
125+
## Sub-Issues
126+
${subIssues.map((si: any) => {
127+
const statusIcon = si.state === "closed"
128+
? '<span class="github-issues-sub-issue-closed">●</span>'
129+
: '<span class="github-issues-sub-issue-open">●</span>';
130+
return `- ${statusIcon} [#${si.number} ${si.title}](${si.url})`;
131+
}).join("\n")}`;
132+
}
133+
134+
// Add parent issue link if available
135+
if (parentIssue) {
136+
frontmatter += `
137+
138+
## Parent Issue
139+
- [#${parentIssue.number} ${parentIssue.title}](${parentIssue.url})`;
140+
}
141+
99142
return frontmatter;
100143
}
101144

src/file-manager.ts

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class FileManager {
2929
private app: App,
3030
private settings: GitHubTrackerSettings,
3131
private noticeManager: NoticeManager,
32-
gitHubClient: GitHubClient,
32+
private gitHubClient: GitHubClient,
3333
) {
3434
this.issueFileManager = new IssueFileManager(app, settings, noticeManager, gitHubClient);
3535
this.prFileManager = new PullRequestFileManager(app, settings, noticeManager, gitHubClient);
@@ -159,6 +159,29 @@ export class FileManager {
159159
const repository = this.extractRepositoryFromUrl(content.url) || `${project.owner}/unknown`;
160160
const projectData = this.convertFieldValuesToProjectData(project, status, item.fieldValues?.nodes || []);
161161

162+
// Fetch sub-issues and parent issue for template support (only if enabled for project)
163+
let subIssues: any[] = [];
164+
let parentIssue: any = null;
165+
166+
if (isIssue && project.includeSubIssues) {
167+
const [owner, repoName] = repository.split("/");
168+
if (owner && repoName) {
169+
subIssues = await this.gitHubClient.fetchSubIssues(owner, repoName, content.number);
170+
parentIssue = await this.gitHubClient.fetchParentIssue(owner, repoName, content.number);
171+
172+
// Enrich sub-issues with vault paths if they exist
173+
const noteTemplate = project.issueNoteTemplate || "Issue - {number} - {title}";
174+
subIssues = await this.fileHelpers.enrichSubIssuesWithVaultPaths(
175+
subIssues,
176+
folderPath,
177+
noteTemplate,
178+
repository,
179+
this.settings.dateFormat,
180+
this.settings.escapeMode
181+
);
182+
}
183+
}
184+
162185
const templateData = isIssue
163186
? createIssueTemplateData(
164187
this.convertToIssueFormat(content),
@@ -167,7 +190,9 @@ export class FileManager {
167190
this.settings.dateFormat,
168191
this.settings.escapeMode,
169192
this.settings.escapeHashTags,
170-
[projectData]
193+
[projectData],
194+
subIssues,
195+
parentIssue
171196
)
172197
: createPullRequestTemplateData(
173198
this.convertToPullRequestFormat(content),
@@ -193,7 +218,9 @@ export class FileManager {
193218
project,
194219
status,
195220
isIssue,
196-
item.fieldValues?.nodes || []
221+
item.fieldValues?.nodes || [],
222+
subIssues,
223+
parentIssue
197224
);
198225

199226
if (existingFile && existingFile instanceof TFile) {
@@ -226,6 +253,8 @@ export class FileManager {
226253
status: string,
227254
isIssue: boolean,
228255
fieldValues: any[],
256+
subIssues?: any[],
257+
parentIssue?: any,
229258
): Promise<string> {
230259
const shouldEscapeHashTags = this.settings.escapeHashTags;
231260

@@ -255,7 +284,9 @@ export class FileManager {
255284
this.settings.dateFormat,
256285
this.settings.escapeMode,
257286
shouldEscapeHashTags,
258-
[projectData]
287+
[projectData],
288+
subIssues,
289+
parentIssue
259290
)
260291
: createPullRequestTemplateData(
261292
this.convertToPullRequestFormat(content),
@@ -272,7 +303,7 @@ export class FileManager {
272303
}
273304

274305
// Fallback to default format
275-
return this.generateDefaultProjectItemContent(content, project, status, isIssue, fieldValues);
306+
return this.generateDefaultProjectItemContent(content, project, status, isIssue, fieldValues, subIssues, parentIssue);
276307
}
277308

278309
/**
@@ -284,6 +315,8 @@ export class FileManager {
284315
status: string,
285316
isIssue: boolean,
286317
fieldValues: any[],
318+
subIssues?: any[],
319+
parentIssue?: any,
287320
): string {
288321
const shouldEscapeHashTags = this.settings.escapeHashTags;
289322
const dateFormat = this.settings.dateFormat;
@@ -327,6 +360,24 @@ project_status: "${status}"`;
327360
requested_reviewers: [${reviewers.join(", ")}]`;
328361
}
329362

363+
// Add parent issue if available
364+
if (parentIssue) {
365+
frontmatter += `
366+
parent_issue: ${parentIssue.number}
367+
parent_issue_url: "${parentIssue.url}"`;
368+
}
369+
370+
// Add sub-issues metadata if available
371+
if (subIssues && subIssues.length > 0) {
372+
const closedCount = subIssues.filter((si: any) => si.state === "closed").length;
373+
const openCount = subIssues.length - closedCount;
374+
frontmatter += `
375+
sub_issues: [${subIssues.map((si: any) => si.number).join(", ")}]
376+
sub_issues_count: ${subIssues.length}
377+
sub_issues_open: ${openCount}
378+
sub_issues_closed: ${closedCount}`;
379+
}
380+
330381
frontmatter += `
331382
---
332383
@@ -337,6 +388,27 @@ ${
337388
: "_No description provided._"
338389
}`;
339390

391+
// Add sub-issues section if available
392+
if (subIssues && subIssues.length > 0) {
393+
frontmatter += `
394+
395+
## Sub-Issues
396+
${subIssues.map((si: any) => {
397+
const statusIcon = si.state === "closed"
398+
? '<span class="github-issues-sub-issue-closed">●</span>'
399+
: '<span class="github-issues-sub-issue-open">●</span>';
400+
return `- ${statusIcon} [#${si.number} ${si.title}](${si.url})`;
401+
}).join("\n")}`;
402+
}
403+
404+
// Add parent issue link if available
405+
if (parentIssue) {
406+
frontmatter += `
407+
408+
## Parent Issue
409+
- [#${parentIssue.number} ${parentIssue.title}](${parentIssue.url})`;
410+
}
411+
340412
return frontmatter;
341413
}
342414

0 commit comments

Comments
 (0)