446 lines
13 KiB
Markdown
446 lines
13 KiB
Markdown
# GitLink CLI Workflow Agent Design
|
|
|
|
## Background
|
|
|
|
`gitlink-cli` already provides low-level and shortcut operations for GitLink repositories, issues, pull requests, releases, CI, organizations, search, and users. The repository also includes `skills/gitlink-workflow/SKILL.md`, which describes AI workflow patterns such as Issue triage, PR review, and Release Notes generation.
|
|
|
|
The current Go command tree does not include a `workflow` command group. The first competition PR should turn the documented workflow concept into concrete, deterministic CLI commands that can be used by human maintainers and AI Agents without calling an external LLM.
|
|
|
|
## Goals
|
|
|
|
First PR:
|
|
- Add `gitlink-cli workflow +triage`.
|
|
- Add `gitlink-cli workflow +health`.
|
|
- Keep write behavior dry-run by default.
|
|
- Produce stable JSON for Agents.
|
|
- Produce concise table output for terminal users.
|
|
- Produce markdown output for reports, PR comments, Issue comments, and competition materials.
|
|
- Support `--lang en` and `--lang zh-CN` with a lightweight message helper.
|
|
|
|
Additional workflow commands:
|
|
- `workflow +pr-summary`: done
|
|
- `workflow +release-notes`: planned
|
|
- `workflow +stale`: planned
|
|
|
|
Current implementation status:
|
|
- Rule engine: done
|
|
- Local command layer: done
|
|
- API fetch layer: done
|
|
- Boundary tests: expanded for empty responses, field normalization, unknown tolerance, and read-only error handling
|
|
- PR summary command: done with local JSON input, read-only fetch, rules, renderers, and tests
|
|
|
|
## Current Repository Findings
|
|
|
|
Command registration:
|
|
- `cmd/root.go` registers global flags and calls `shortcuts.RegisterAll(rootCmd)`.
|
|
- `shortcuts/register.go` maps command groups to shortcut slices.
|
|
- Each group exposes `Shortcuts() []*common.Shortcut`.
|
|
- `common.MountShortcut` maps a `Shortcut` into a Cobra command named `+<name>`.
|
|
|
|
Runtime and API calls:
|
|
- `common.NewRuntimeContext` creates `client.Client`, carries owner, repo, format, and command args.
|
|
- `ctx.ResolveOwnerRepo()` resolves `--owner` / `--repo` or Git remote context.
|
|
- `ctx.CallAPI` and `ctx.CallAPIWithQuery` call `internal/client`.
|
|
- `client.Do` appends `.json`, injects auth via transport, parses GitLink error-in-body responses, and returns `output.Envelope`.
|
|
|
|
Output:
|
|
- `internal/output` currently supports `json`, `yaml`, and generic `table`.
|
|
- Workflow requires `markdown`; the minimal-risk approach is a workflow-local renderer that prints stable workflow DTOs.
|
|
- A later cleanup can promote markdown support into `internal/output` if multiple command groups need it.
|
|
- Current workflow commands also expose workflow-local `json`, `table`, and `markdown` rendering without changing the global formatter.
|
|
|
|
Testing:
|
|
- Existing tests use pure unit tests plus `httptest.Server`.
|
|
- Shortcut tests instantiate `common.RuntimeContext` manually with a mocked `client.Client`.
|
|
- This pattern should be reused for workflow API tests.
|
|
|
|
## Command Design
|
|
|
|
### `workflow +triage`
|
|
|
|
Examples:
|
|
|
|
```bash
|
|
gitlink-cli workflow +triage --owner Gitlink --repo gitlink-cli --state open --limit 30 --dry-run --format json
|
|
gitlink-cli workflow +triage --owner Gitlink --repo gitlink-cli --state open --limit 30 --format table
|
|
gitlink-cli workflow +triage --owner Gitlink --repo gitlink-cli --state open --limit 30 --lang zh-CN --format markdown
|
|
```
|
|
|
|
Flags:
|
|
- `--state`: default `open`
|
|
- `--limit`: default `30`
|
|
- `--page`: default `1`
|
|
- `--dry-run`: default `true`
|
|
- `--from`: optional local JSON input
|
|
- `--title`, `--body`, `--number`, `--author`, `--url`, `--labels`: optional local single-issue input
|
|
- `--lang`: default `en`, allowed `en`, `zh-CN`
|
|
|
|
Stable JSON item fields:
|
|
- `issue_id`
|
|
- `number`
|
|
- `title`
|
|
- `url`
|
|
- `author`
|
|
- `state`
|
|
- `created_at`
|
|
- `updated_at`
|
|
- `detected_type`
|
|
- `priority`
|
|
- `confidence`
|
|
- `suggested_labels`
|
|
- `missing_information`
|
|
- `risk_flags`
|
|
- `recommended_action`
|
|
- `suggested_comment`
|
|
- `reasoning`
|
|
|
|
Rule categories:
|
|
- `bug`
|
|
- `feature`
|
|
- `question`
|
|
- `docs`
|
|
- `ci`
|
|
- `security`
|
|
- `performance`
|
|
- `refactor`
|
|
- `unknown`
|
|
|
|
Priority:
|
|
- `P0`: security incident, secret/token leak, auth bypass, repository unusable
|
|
- `P1`: core command unusable, install/login failure, CI/release blocker
|
|
- `P2`: normal bug, important feature, missing docs blocking usage
|
|
- `P3`: ordinary question, typo, minor improvement
|
|
|
|
Missing information for bug-like issues:
|
|
- reproduction steps
|
|
- expected behavior
|
|
- actual behavior
|
|
- version
|
|
- OS / platform
|
|
- command output
|
|
- logs
|
|
|
|
### `workflow +health`
|
|
|
|
Examples:
|
|
|
|
```bash
|
|
gitlink-cli workflow +health --owner Gitlink --repo gitlink-cli --format json
|
|
gitlink-cli workflow +health --owner Gitlink --repo gitlink-cli --format table
|
|
gitlink-cli workflow +health --owner Gitlink --repo gitlink-cli --lang zh-CN --format markdown
|
|
```
|
|
|
|
Flags:
|
|
- `--stale-days`: default `30`
|
|
- `--from`: optional local JSON input
|
|
- local metric flags such as `--repository`, `--open-issues`, `--open-prs`, `--has-readme`, `--has-license`, and `--agent-readiness-score`
|
|
- `--lang`: default `en`
|
|
|
|
Stable JSON fields:
|
|
- `repository`
|
|
- `open_issues`
|
|
- `open_prs`
|
|
- `stale_issues`
|
|
- `stale_prs`
|
|
- `recent_activity`
|
|
- `release_status`
|
|
- `ci_status`
|
|
- `documentation_status`
|
|
- `license_status`
|
|
- `contribution_status`
|
|
- `agent_readiness_score`
|
|
- `health_score`
|
|
- `risk_level`
|
|
- `recommendations`
|
|
- `scoring_notes`
|
|
|
|
Scoring:
|
|
- Issue backlog and response: 20
|
|
- PR backlog and merge state: 20
|
|
- Recent activity: 15
|
|
- Release status: 15
|
|
- Documentation completeness: 10
|
|
- License and contribution readiness: 10
|
|
- Agent readiness: 10
|
|
|
|
Unknown metric policy:
|
|
- Keep field present.
|
|
- Set status or score detail to `unknown`.
|
|
- Add one entry to `scoring_notes`.
|
|
- Either omit the metric from denominator or apply a conservative partial score; the first PR should prefer denominator adjustment to avoid fake precision.
|
|
|
|
Risk levels:
|
|
- `low`: 80-100
|
|
- `medium`: 60-79
|
|
- `high`: 40-59
|
|
- `critical`: 0-39
|
|
|
|
## Architecture
|
|
|
|
Proposed files:
|
|
|
|
```text
|
|
shortcuts/workflow/
|
|
workflow.go # Shortcuts() and command wiring
|
|
types.go # Stable DTOs
|
|
triage_rules.go # pure classifier, scoring, missing info detection
|
|
triage_fetch.go # GitLink issue fetching and response normalization
|
|
triage_render.go # json/table/markdown workflow rendering if needed
|
|
health_score.go # pure health scoring
|
|
health_fetch.go # repo, issue, PR, release, CI/doc/license probes
|
|
health_render.go # markdown/table rendering
|
|
messages.go # en and zh-CN strings
|
|
*_test.go
|
|
```
|
|
|
|
Registration:
|
|
- Add `workflow` import in `shortcuts/register.go`.
|
|
- Add `"workflow": workflow.Shortcuts()` to `groups`.
|
|
- Add description `"AI agent workflow analysis"`.
|
|
|
|
No new dependency is needed for this PR.
|
|
|
|
## Data Normalization
|
|
|
|
GitLink responses vary by endpoint. Workflow code should not depend on a single raw shape. Add small extraction helpers:
|
|
|
|
- `stringField(map, keys...)`
|
|
- `numberField(map, keys...)`
|
|
- `timeField(map, keys...)`
|
|
- `sliceField(map, keys...)`
|
|
- `extractItems(env, candidateKeys...)`
|
|
|
|
Candidate issue list keys:
|
|
- `issues`
|
|
- `data`
|
|
- direct array after future client improvements
|
|
|
|
Candidate issue fields:
|
|
- ID: `id`, `issue_id`
|
|
- Number: `project_issues_index`, `number`, `index`, `id`
|
|
- Title: `subject`, `title`
|
|
- Body: `description`, `body`
|
|
- Author: `author.login`, `user.login`, `login`
|
|
- URL: `html_url`, `url`, `issue_url`
|
|
|
|
Health activity fields currently tolerated:
|
|
- `updated_at`
|
|
- `updatedAt`
|
|
- `last_updated_at`
|
|
- `lastUpdatedAt`
|
|
- `last_activity_at`
|
|
- `lastActivityAt`
|
|
- `merged_at`
|
|
- `mergedAt`
|
|
- `closed_at`
|
|
- `closedAt`
|
|
|
|
## Safety Strategy
|
|
|
|
- `+triage` only reads by default.
|
|
- `--dry-run` defaults true.
|
|
- A future explicit write flag for posting comments must require `--dry-run=false` in a later PR.
|
|
- Generated comments are output as data, not posted remotely in the first PR.
|
|
- Health checks never mutate remote state.
|
|
- If an API probe fails, health continues with `unknown`.
|
|
- The implemented prototype is local-first and has no LLM dependency.
|
|
- Remote fetch mode remains read-only and does not post comments, labels, merges, or close actions.
|
|
- API failures should fall back to `unknown` metrics or a clear fetch error instead of fabricating healthy data.
|
|
|
|
## Core Pseudocode
|
|
|
|
### Triage
|
|
|
|
```go
|
|
issues := fetchIssues(owner, repo, state, limit, page)
|
|
results := []TriageResult{}
|
|
for _, issue := range issues {
|
|
text := normalize(issue.Title + "\n" + issue.Body)
|
|
scores := scoreKeywords(text, keywordRules)
|
|
detectedType := maxScoreType(scores)
|
|
priority := scorePriority(text, detectedType)
|
|
missing := detectMissingInfo(issue, detectedType)
|
|
confidence := confidenceFromScores(scores, missing)
|
|
result := TriageResult{
|
|
IssueID: issue.ID,
|
|
Number: issue.Number,
|
|
DetectedType: detectedType,
|
|
Priority: priority,
|
|
SuggestedLabels: labelsFor(detectedType, priority, riskFlags),
|
|
MissingInformation: missing,
|
|
RiskFlags: detectRiskFlags(text),
|
|
RecommendedAction: actionFor(detectedType, priority, missing, lang),
|
|
SuggestedComment: commentFor(missing, lang),
|
|
Reasoning: explainTopMatches(scores, priorityRules),
|
|
}
|
|
results = append(results, result)
|
|
}
|
|
render(results, format, lang)
|
|
```
|
|
|
|
### Health
|
|
|
|
```go
|
|
signals := collectHealthSignals(owner, repo)
|
|
score := NewWeightedScore(100)
|
|
score.Add("issues", 20, scoreIssueBacklog(signals.OpenIssues, signals.StaleIssues))
|
|
score.Add("prs", 20, scorePRBacklog(signals.OpenPRs, signals.StalePRs))
|
|
score.Add("activity", 15, scoreRecentActivity(signals.RecentActivity))
|
|
score.Add("release", 15, scoreReleaseStatus(signals.ReleaseStatus))
|
|
score.Add("docs", 10, scoreDocStatus(signals.DocumentationStatus))
|
|
score.Add("license", 10, scoreLicenseContribution(signals.LicenseStatus, signals.ContributionStatus))
|
|
score.Add("agent", 10, scoreAgentReadiness(signals))
|
|
result := HealthResult{
|
|
HealthScore: score.Percent(),
|
|
RiskLevel: riskLevel(score.Percent()),
|
|
Recommendations: recommendations(signals, score),
|
|
ScoringNotes: score.Notes(),
|
|
}
|
|
render(result, format, lang)
|
|
```
|
|
|
|
## Output Protocol
|
|
|
|
JSON:
|
|
- Use stable struct tags.
|
|
- Include empty arrays as `[]` where useful for Agent consumption.
|
|
- Avoid prose outside JSON.
|
|
|
|
Table:
|
|
- Triage columns: `NUMBER`, `TYPE`, `PRIORITY`, `CONFIDENCE`, `MISSING`, `ACTION`
|
|
- Health rows: `METRIC`, `STATUS`, `SCORE`, `NOTE`
|
|
|
|
Markdown:
|
|
- Triage: one summary table with type, priority, confidence, action, and missing information.
|
|
- Health: repository score, metric table, recommendations, and scoring notes.
|
|
- `zh-CN` changes rule messages and recommendation text, not JSON field names.
|
|
|
|
## Test Plan
|
|
|
|
Unit tests:
|
|
- Issue type classification.
|
|
- Priority scoring.
|
|
- Missing information detection.
|
|
- Risk flag detection.
|
|
- Suggested comment generation.
|
|
- Health weighted score and risk level.
|
|
- Unknown metric denominator adjustment.
|
|
- Markdown headings and required sections.
|
|
|
|
Mock API tests:
|
|
- `workflow +triage` fetches issues and normalizes raw response.
|
|
- `workflow +health` tolerates failing CI/release/doc probes.
|
|
|
|
Command tests:
|
|
- `--dry-run` defaults to true.
|
|
- `--lang zh-CN` accepted.
|
|
- invalid `--lang` falls back to `en`.
|
|
- `--format markdown` routes to markdown renderer.
|
|
|
|
## Later Extensions
|
|
|
|
### `workflow +pr-summary`
|
|
|
|
Inputs:
|
|
- `--number`
|
|
- `--from`
|
|
- `--lang`
|
|
- `--format`
|
|
- optional `--include-files`
|
|
- optional `--include-commits`
|
|
- optional `--max-files`
|
|
- optional `--max-commits`
|
|
|
|
Default format:
|
|
- `table` for human review when `--format` is omitted
|
|
|
|
Data:
|
|
- PR details
|
|
- changed files
|
|
- commits
|
|
|
|
Output:
|
|
- `change_type`
|
|
- `risk_level`
|
|
- `review_focus`
|
|
- `test_suggestions`
|
|
- `merge_checklist`
|
|
- `reasoning`
|
|
|
|
Implementation status:
|
|
- read-only local JSON mode: done
|
|
- read-only GitLink fetch mode: done
|
|
- rules and renderers: done
|
|
- tests: rules, fetch boundary, render, and command wiring
|
|
|
|
Safety:
|
|
- no comments
|
|
- no approve/reject
|
|
- no merge
|
|
- no remote write operation
|
|
|
|
### `workflow +release-notes`
|
|
|
|
Inputs:
|
|
- `--from`
|
|
- `--to`
|
|
- optional `--tag`
|
|
- optional `--lang`
|
|
|
|
Data:
|
|
- PR titles
|
|
- commit messages
|
|
|
|
Markdown categories:
|
|
- Features
|
|
- Bug Fixes
|
|
- Documentation
|
|
- Tests
|
|
- Refactoring
|
|
- Chores
|
|
- Breaking Changes
|
|
|
|
### `workflow +stale`
|
|
|
|
Inputs:
|
|
- `--stale-days`
|
|
- `--state`
|
|
- `--dry-run`
|
|
|
|
Behavior:
|
|
- Identify stale issues and PRs.
|
|
- Generate suggested comments or labels.
|
|
- Do not mutate remote state by default.
|
|
|
|
## API Fetch Layer
|
|
|
|
The current fetch layer uses:
|
|
|
|
- `triage_fetch.go`
|
|
- `health_fetch.go`
|
|
|
|
Design goals already applied:
|
|
|
|
- tolerate unknown or partial API fields
|
|
- map GitLink response shapes into stable workflow DTOs
|
|
- continue operating when optional signals fail
|
|
- keep remote-write actions disabled until explicitly enabled later
|
|
|
|
Planned fetch-layer extension:
|
|
|
|
- `triage_fetch.go` and `health_fetch.go` remain the normalization boundary for remote mode.
|
|
- `pr_fetch.go` now reuses the same stable DTO and message patterns for read-only PR metadata, changed files, and commits.
|
|
- Future `release-notes` should reuse the same normalization and renderer patterns.
|
|
- Unknown or missing fields should stay explicit in JSON output so Agents can decide how to proceed.
|
|
|
|
## Implementation Order
|
|
|
|
1. Pure DTOs and rule engine.
|
|
2. Pure health scoring.
|
|
3. Workflow renderers.
|
|
4. Command registration.
|
|
5. API fetch and normalization.
|
|
6. Tests.
|
|
7. README updates.
|
|
8. Competition docs and test report.
|