alpha
Login
or
Join now
quilling.dev
/
tg
forked from
aly.codes/tg
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
A terminal client for Tangled.
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
cli/issue: add write commands
author
Aly Raffauf
date
1 week ago
(Jul 15, 2026, 12:13 PM -0400)
commit
cce6d60b
cce6d60ba12921ccdaef781a6bbdff010c523af6
parent
7ad41a81
7ad41a81efc76c560e3b30af9b391e7ff7503bab
+203
3 changed files
Expand all
Collapse all
Unified
Split
internal
cli
issue_comment.go
issue_create.go
issue_state.go
+64
internal/cli/issue_comment.go
View file
Reviewed
···
1
1
+
package cli
2
2
+
3
3
+
import (
4
4
+
"fmt"
5
5
+
6
6
+
"github.com/alyraffauf/tg/tangled"
7
7
+
"github.com/spf13/cobra"
8
8
+
)
9
9
+
10
10
+
var (
11
11
+
issueCommentBody string
12
12
+
issueCommentBodyFile string
13
13
+
issueCommentRepo string
14
14
+
)
15
15
+
16
16
+
var issueCommentCmd = &cobra.Command{
17
17
+
Use: "comment <rkey>",
18
18
+
Short: "Add a comment to an issue",
19
19
+
Args: cobra.ExactArgs(1),
20
20
+
RunE: func(cmd *cobra.Command, args []string) error {
21
21
+
body, err := commandBody(issueCommentBody, issueCommentBodyFile)
22
22
+
if err != nil {
23
23
+
return err
24
24
+
}
25
25
+
if body == "" {
26
26
+
return fmt.Errorf("set --body or --body-file")
27
27
+
}
28
28
+
ctx := cmd.Context()
29
29
+
targetArgs := []string{}
30
30
+
if issueCommentRepo != "" {
31
31
+
targetArgs = []string{issueCommentRepo}
32
32
+
}
33
33
+
handle, name, err := resolveTarget(ctx, targetArgs)
34
34
+
if err != nil {
35
35
+
return err
36
36
+
}
37
37
+
repoDid, err := findRepoDid(ctx, handle, name)
38
38
+
if err != nil {
39
39
+
return err
40
40
+
}
41
41
+
issues, err := client.ListIssues(ctx, repoDid, tangled.ListOpts{Limit: defaultListLimit})
42
42
+
if err != nil {
43
43
+
return fmt.Errorf("list issues for %s/%s: %w", handle, name, err)
44
44
+
}
45
45
+
issue, err := findByRKey(issues.Items, args[0], "issue")
46
46
+
if err != nil {
47
47
+
return err
48
48
+
}
49
49
+
50
50
+
result, err := createIssueComment(ctx, issue.URI, body)
51
51
+
if err != nil {
52
52
+
return err
53
53
+
}
54
54
+
return output(result, func(result createdRecordResult) {
55
55
+
fmt.Printf("Added comment %s\n", result.URI)
56
56
+
})
57
57
+
},
58
58
+
}
59
59
+
60
60
+
func init() {
61
61
+
issueCommentCmd.Flags().StringVarP(&issueCommentBody, "body", "b", "", "Comment body")
62
62
+
issueCommentCmd.Flags().StringVarP(&issueCommentBodyFile, "body-file", "F", "", "Read comment body from file")
63
63
+
issueCommentCmd.Flags().StringVarP(&issueCommentRepo, "repo", "R", "", "Target repository as handle/repo")
64
64
+
}
+74
internal/cli/issue_create.go
View file
Reviewed
···
1
1
+
package cli
2
2
+
3
3
+
import (
4
4
+
"fmt"
5
5
+
"time"
6
6
+
7
7
+
"github.com/alyraffauf/tg/atproto"
8
8
+
"github.com/alyraffauf/tg/tangled"
9
9
+
"github.com/bluesky-social/indigo/atproto/syntax"
10
10
+
"github.com/spf13/cobra"
11
11
+
)
12
12
+
13
13
+
var (
14
14
+
issueCreateBody string
15
15
+
issueCreateBodyFile string
16
16
+
issueCreateRepo string
17
17
+
)
18
18
+
19
19
+
var issueCreateCmd = &cobra.Command{
20
20
+
Use: "create <title>",
21
21
+
Short: "Create an issue on a Tangled repository",
22
22
+
Args: cobra.ExactArgs(1),
23
23
+
RunE: func(cmd *cobra.Command, args []string) error {
24
24
+
ctx := cmd.Context()
25
25
+
body, err := commandBody(issueCreateBody, issueCreateBodyFile)
26
26
+
if err != nil {
27
27
+
return err
28
28
+
}
29
29
+
atClient, did, err := authenticatedATProto(ctx)
30
30
+
if err != nil {
31
31
+
return err
32
32
+
}
33
33
+
34
34
+
targetArgs := []string{}
35
35
+
if issueCreateRepo != "" {
36
36
+
targetArgs = []string{issueCreateRepo}
37
37
+
}
38
38
+
handle, name, err := resolveTarget(ctx, targetArgs)
39
39
+
if err != nil {
40
40
+
return err
41
41
+
}
42
42
+
repoDid, err := findRepoDid(ctx, handle, name)
43
43
+
if err != nil {
44
44
+
return err
45
45
+
}
46
46
+
47
47
+
rkey := string(syntax.NewTIDNow(0))
48
48
+
uri, _, err := atClient.PutRecord(ctx, atproto.PutRecordInput{
49
49
+
Repo: did,
50
50
+
Collection: "sh.tangled.repo.issue",
51
51
+
Rkey: rkey,
52
52
+
Record: tangled.IssueRecord{
53
53
+
Type: "sh.tangled.repo.issue",
54
54
+
Repo: repoDid,
55
55
+
Title: args[0],
56
56
+
Body: body,
57
57
+
CreatedAt: time.Now().UTC().Format(time.RFC3339),
58
58
+
},
59
59
+
})
60
60
+
if err != nil {
61
61
+
return fmt.Errorf("create issue: %w", err)
62
62
+
}
63
63
+
64
64
+
return output(createdRecordResult{Rkey: rkey, URI: uri}, func(result createdRecordResult) {
65
65
+
fmt.Printf("Created issue %s\n", result.URI)
66
66
+
})
67
67
+
},
68
68
+
}
69
69
+
70
70
+
func init() {
71
71
+
issueCreateCmd.Flags().StringVarP(&issueCreateBody, "body", "b", "", "Issue body")
72
72
+
issueCreateCmd.Flags().StringVarP(&issueCreateBodyFile, "body-file", "F", "", "Read issue body from file")
73
73
+
issueCreateCmd.Flags().StringVarP(&issueCreateRepo, "repo", "R", "", "Target repository as handle/repo")
74
74
+
}
+65
internal/cli/issue_state.go
View file
Reviewed
···
1
1
+
package cli
2
2
+
3
3
+
import (
4
4
+
"fmt"
5
5
+
6
6
+
"github.com/spf13/cobra"
7
7
+
)
8
8
+
9
9
+
var (
10
10
+
issueStateRepo string
11
11
+
issueEditTitle string
12
12
+
issueEditBody string
13
13
+
)
14
14
+
15
15
+
var issueCloseCmd = newIssueStateCmd("close", "closed")
16
16
+
var issueReopenCmd = newIssueStateCmd("reopen", "open")
17
17
+
18
18
+
var issueEditCmd = &cobra.Command{
19
19
+
Use: "edit <rkey>",
20
20
+
Short: "Edit an issue",
21
21
+
Args: cobra.ExactArgs(1),
22
22
+
RunE: func(cmd *cobra.Command, args []string) error {
23
23
+
setTitle := cmd.Flags().Changed("title")
24
24
+
setBody := cmd.Flags().Changed("body")
25
25
+
if !setTitle && !setBody {
26
26
+
return fmt.Errorf("set --title or --body")
27
27
+
}
28
28
+
atClient, did, err := authenticatedATProto(cmd.Context())
29
29
+
if err != nil {
30
30
+
return err
31
31
+
}
32
32
+
return editRecord(cmd.Context(), atClient, did, issueCollection, args[0], issueEditTitle, issueEditBody, setTitle, setBody)
33
33
+
},
34
34
+
}
35
35
+
36
36
+
func newIssueStateCmd(use, state string) *cobra.Command {
37
37
+
return &cobra.Command{
38
38
+
Use: use + " <rkey>",
39
39
+
Short: use + " an issue",
40
40
+
Args: cobra.ExactArgs(1),
41
41
+
RunE: func(cmd *cobra.Command, args []string) error {
42
42
+
atClient, did, err := authenticatedATProto(cmd.Context())
43
43
+
if err != nil {
44
44
+
return err
45
45
+
}
46
46
+
target, _, err := targetRecord(cmd.Context(), issueStateRepo, issueCollection, args[0])
47
47
+
if err != nil {
48
48
+
return err
49
49
+
}
50
50
+
if err := putState(cmd.Context(), atClient, did, args[0], issueCollection, target, state); err != nil {
51
51
+
return fmt.Errorf("%s issue: %w", use, err)
52
52
+
}
53
53
+
return output(stateResult{Rkey: args[0], State: state}, func(result stateResult) {
54
54
+
fmt.Printf("Issue %s %s\n", result.Rkey, result.State)
55
55
+
})
56
56
+
},
57
57
+
}
58
58
+
}
59
59
+
60
60
+
func init() {
61
61
+
issueCloseCmd.Flags().StringVarP(&issueStateRepo, "repo", "R", "", "Target repository as handle/repo")
62
62
+
issueReopenCmd.Flags().StringVarP(&issueStateRepo, "repo", "R", "", "Target repository as handle/repo")
63
63
+
issueEditCmd.Flags().StringVarP(&issueEditTitle, "title", "t", "", "New title")
64
64
+
issueEditCmd.Flags().StringVarP(&issueEditBody, "body", "b", "", "New body")
65
65
+
}