@awiki/cli 0.0.1-beta.2

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 (119) hide show
  1. package/.github/workflows/release.yml +44 -0
  2. package/.goreleaser.yml +44 -0
  3. package/AGENTS.md +60 -0
  4. package/CLAUDE.md +192 -0
  5. package/README.md +2 -0
  6. package/docs/architecture/awiki-command-v2.md +955 -0
  7. package/docs/architecture/awiki-skill-architecture.md +475 -0
  8. package/docs/architecture/awiki-v2-architecture.md +1063 -0
  9. package/docs/architecture//345/217/202/350/200/203/346/226/207/346/241/243/cli-init.md +1008 -0
  10. package/docs/architecture//345/217/202/350/200/203/346/226/207/346/241/243/output-format.md +407 -0
  11. package/docs/architecture//345/217/202/350/200/203/346/226/207/346/241/243/overall-init.md +741 -0
  12. package/docs/harness/review-spec.md +474 -0
  13. package/docs/installation.md +372 -0
  14. package/docs/plan/awiki-v2-implementation-plan.md +903 -0
  15. package/docs/plan/phase-0/adr-index.md +56 -0
  16. package/docs/plan/phase-0/audit-findings.md +251 -0
  17. package/docs/plan/phase-0/capability-mapping.md +108 -0
  18. package/docs/plan/phase-0/implementation-constraints.md +363 -0
  19. package/docs/publish.md +169 -0
  20. package/go.mod +29 -0
  21. package/go.sum +73 -0
  22. package/internal/anpsdk/registry.go +63 -0
  23. package/internal/authsdk/session.go +351 -0
  24. package/internal/buildinfo/buildinfo.go +34 -0
  25. package/internal/cli/app.go +136 -0
  26. package/internal/cli/app_test.go +88 -0
  27. package/internal/cli/debug.go +104 -0
  28. package/internal/cli/group.go +263 -0
  29. package/internal/cli/id.go +473 -0
  30. package/internal/cli/init.go +134 -0
  31. package/internal/cli/msg.go +228 -0
  32. package/internal/cli/page.go +267 -0
  33. package/internal/cli/root.go +499 -0
  34. package/internal/cli/runtime.go +232 -0
  35. package/internal/cli/upgrade.go +60 -0
  36. package/internal/cmdmeta/catalog.go +203 -0
  37. package/internal/cmdmeta/catalog_test.go +21 -0
  38. package/internal/config/config.go +399 -0
  39. package/internal/config/config_test.go +104 -0
  40. package/internal/config/write.go +37 -0
  41. package/internal/content/service.go +314 -0
  42. package/internal/content/service_test.go +165 -0
  43. package/internal/content/types.go +44 -0
  44. package/internal/docs/topics.go +110 -0
  45. package/internal/doctor/doctor.go +306 -0
  46. package/internal/identity/client.go +267 -0
  47. package/internal/identity/did.go +85 -0
  48. package/internal/identity/did_test.go +50 -0
  49. package/internal/identity/layout.go +206 -0
  50. package/internal/identity/legacy.go +378 -0
  51. package/internal/identity/public.go +70 -0
  52. package/internal/identity/public_test.go +73 -0
  53. package/internal/identity/readiness.go +74 -0
  54. package/internal/identity/service.go +826 -0
  55. package/internal/identity/store.go +385 -0
  56. package/internal/identity/store_test.go +180 -0
  57. package/internal/identity/types.go +204 -0
  58. package/internal/message/auth.go +167 -0
  59. package/internal/message/group_service.go +838 -0
  60. package/internal/message/group_wire.go +350 -0
  61. package/internal/message/group_wire_test.go +67 -0
  62. package/internal/message/helpers.go +61 -0
  63. package/internal/message/http_client.go +334 -0
  64. package/internal/message/proof.go +156 -0
  65. package/internal/message/proof_test.go +61 -0
  66. package/internal/message/service.go +696 -0
  67. package/internal/message/service_test.go +97 -0
  68. package/internal/message/types.go +155 -0
  69. package/internal/message/wire.go +100 -0
  70. package/internal/message/wire_test.go +49 -0
  71. package/internal/message/ws_proxy_client.go +151 -0
  72. package/internal/output/output.go +350 -0
  73. package/internal/output/output_test.go +48 -0
  74. package/internal/runtime/config.go +117 -0
  75. package/internal/runtime/config_test.go +46 -0
  76. package/internal/runtime/listener/files.go +65 -0
  77. package/internal/runtime/listener/manager.go +142 -0
  78. package/internal/runtime/listener/server.go +983 -0
  79. package/internal/runtime/listener/server_test.go +319 -0
  80. package/internal/runtime/listener/sysproc_unix.go +17 -0
  81. package/internal/runtime/listener/sysproc_windows.go +13 -0
  82. package/internal/runtime/listener/types.go +21 -0
  83. package/internal/runtime/listener/wsclient.go +299 -0
  84. package/internal/runtime/listener/wsclient_test.go +41 -0
  85. package/internal/store/dao.go +632 -0
  86. package/internal/store/dao_test.go +87 -0
  87. package/internal/store/helpers.go +197 -0
  88. package/internal/store/import.go +499 -0
  89. package/internal/store/import_test.go +103 -0
  90. package/internal/store/open.go +71 -0
  91. package/internal/store/query.go +151 -0
  92. package/internal/store/schema.go +277 -0
  93. package/internal/store/schema_test.go +56 -0
  94. package/internal/store/types.go +177 -0
  95. package/internal/update/update.go +368 -0
  96. package/package.json +17 -0
  97. package/scripts/install.js +171 -0
  98. package/scripts/release/release-prerelease.sh +86 -0
  99. package/scripts/release/tag-release.sh +66 -0
  100. package/scripts/release/withdraw-release.sh +78 -0
  101. package/scripts/run.js +69 -0
  102. package/skills/README.md +32 -0
  103. package/skills/awiki-bundle/SKILL.md +76 -0
  104. package/skills/awiki-debug/SKILL.md +80 -0
  105. package/skills/awiki-group/SKILL.md +111 -0
  106. package/skills/awiki-id/SKILL.md +123 -0
  107. package/skills/awiki-msg/SKILL.md +131 -0
  108. package/skills/awiki-page/SKILL.md +93 -0
  109. package/skills/awiki-people/SKILL.md +66 -0
  110. package/skills/awiki-runtime/SKILL.md +137 -0
  111. package/skills/awiki-shared/SKILL.md +124 -0
  112. package/skills/awiki-workflow-discovery/SKILL.md +93 -0
  113. package/skills/awiki-workflow-onboarding/SKILL.md +119 -0
  114. package/skills/manifests/skills.yaml +260 -0
  115. package/skills/templates/bundle-skill-template.md +42 -0
  116. package/skills/templates/debug-skill-template.md +44 -0
  117. package/skills/templates/domain-skill-template.md +56 -0
  118. package/skills/templates/shared-skill-template.md +46 -0
  119. package/skills/templates/workflow-skill-template.md +46 -0
@@ -0,0 +1,104 @@
1
+ package cli
2
+
3
+ import (
4
+ "context"
5
+ "database/sql"
6
+ "errors"
7
+ "strings"
8
+
9
+ appconfig "github.com/agentconnect/awiki-cli/internal/config"
10
+ "github.com/agentconnect/awiki-cli/internal/identity"
11
+ "github.com/agentconnect/awiki-cli/internal/output"
12
+ "github.com/agentconnect/awiki-cli/internal/store"
13
+ "github.com/spf13/cobra"
14
+ )
15
+
16
+ func (a *App) openStore() (*appconfig.Resolved, *sql.DB, output.Format, error) {
17
+ resolved, err := a.resolveConfig()
18
+ if err != nil {
19
+ return nil, nil, output.FormatJSON, err
20
+ }
21
+ format := normalizedFormat(resolved.OutputFormat)
22
+ db, err := store.Open(resolved.Paths)
23
+ if err != nil {
24
+ return resolved, nil, format, err
25
+ }
26
+ return resolved, db, format, nil
27
+ }
28
+
29
+ func (a *App) storeExit(err error, hint string) error {
30
+ if err == nil {
31
+ return nil
32
+ }
33
+ switch {
34
+ case errors.Is(err, store.ErrLegacyDatabaseNotFound), errors.Is(err, sql.ErrNoRows):
35
+ return output.NewExitError("not_found", 5, err.Error(), hint)
36
+ case errors.Is(err, store.ErrUnsafeSQL), errors.Is(err, store.ErrUnsupportedLegacySchema):
37
+ return output.NewExitError("invalid_argument", 2, err.Error(), hint)
38
+ default:
39
+ return output.NewExitError("internal_error", 1, err.Error(), hint)
40
+ }
41
+ }
42
+
43
+ func (a *App) runDebugDBQuery(cmd *cobra.Command, args []string) error {
44
+ if len(args) != 1 {
45
+ return output.NewExitError("invalid_argument", 2, "debug db query requires exactly one SQL statement.", "Usage: awiki-cli debug db query \"SELECT * FROM messages LIMIT 5\"")
46
+ }
47
+ resolved, db, format, err := a.openStore()
48
+ if err != nil {
49
+ return a.storeExit(err, "Run `awiki-cli doctor` to inspect the database path and configuration.")
50
+ }
51
+ defer db.Close()
52
+ if err := store.EnsureSchema(context.Background(), db); err != nil {
53
+ return a.storeExit(err, "Initialize the local store before querying it.")
54
+ }
55
+ rows, err := store.ExecuteSQL(context.Background(), db, args[0])
56
+ if err != nil {
57
+ return a.storeExit(err, "Only single-statement safe SQL is allowed. Avoid destructive statements.")
58
+ }
59
+ data := map[string]any{
60
+ "database_file": resolved.Paths.DatabaseFile,
61
+ "rows": rows,
62
+ }
63
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, data, "SQLite query executed", nil, identityMetaFromResolved(resolved))
64
+ }
65
+
66
+ func (a *App) runDebugDBImportV1(cmd *cobra.Command, args []string) error {
67
+ pathOverride, _ := cmd.Flags().GetString("path")
68
+ resolved, db, format, err := a.openStore()
69
+ if err != nil {
70
+ return a.storeExit(err, "Run `awiki-cli doctor` to inspect the database path and configuration.")
71
+ }
72
+ defer db.Close()
73
+ if err := store.EnsureSchema(context.Background(), db); err != nil {
74
+ return a.storeExit(err, "Initialize the local store before importing legacy data.")
75
+ }
76
+ paths := resolved.Paths
77
+ if strings.TrimSpace(pathOverride) != "" {
78
+ paths.LegacyDataDir = strings.TrimSpace(pathOverride)
79
+ }
80
+ manager := identity.NewManager(paths)
81
+ if a.globals.DryRun {
82
+ scan, err := store.ScanLegacyDatabase(context.Background(), paths)
83
+ if err != nil {
84
+ return a.storeExit(err, "Make sure the legacy database path is correct.")
85
+ }
86
+ data := map[string]any{
87
+ "plan": map[string]any{
88
+ "action": "import_v1_sqlite",
89
+ "source_scan": scan,
90
+ "target": resolved.Paths.DatabaseFile,
91
+ },
92
+ }
93
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, data, "Dry run: legacy SQLite import planned", nil, identityMetaFromResolved(resolved))
94
+ }
95
+ report, err := store.ImportLegacyDatabase(context.Background(), db, paths, manager)
96
+ if err != nil {
97
+ return a.storeExit(err, "Make sure the v1 database exists and identities were imported first.")
98
+ }
99
+ data := map[string]any{
100
+ "database_file": resolved.Paths.DatabaseFile,
101
+ "import_report": report,
102
+ }
103
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, data, "Legacy SQLite import completed", report.Warnings, identityMetaFromResolved(resolved))
104
+ }
@@ -0,0 +1,263 @@
1
+ package cli
2
+
3
+ import (
4
+ "context"
5
+ "strconv"
6
+ "strings"
7
+
8
+ "github.com/agentconnect/awiki-cli/internal/message"
9
+ "github.com/spf13/cobra"
10
+ )
11
+
12
+ func (a *App) runGroupCreate(cmd *cobra.Command, args []string) error {
13
+ name, _ := cmd.Flags().GetString("name")
14
+ description, _ := cmd.Flags().GetString("description")
15
+ discoverability, _ := cmd.Flags().GetString("discoverability")
16
+ admissionMode, _ := cmd.Flags().GetString("admission-mode")
17
+ slug, _ := cmd.Flags().GetString("slug")
18
+ goal, _ := cmd.Flags().GetString("goal")
19
+ rules, _ := cmd.Flags().GetString("rules")
20
+ messagePrompt, _ := cmd.Flags().GetString("message-prompt")
21
+ docURL, _ := cmd.Flags().GetString("doc-url")
22
+ maxMembers, _ := cmd.Flags().GetString("max-members")
23
+ attachmentsAllowed := boolFlagPtr(cmd, "attachments-allowed")
24
+ memberMaxMessages := int64FlagPtr(cmd, "member-max-messages")
25
+ memberMaxTotalChars := int64FlagPtr(cmd, "member-max-total-chars")
26
+ service, format, err := a.messageService()
27
+ if err != nil {
28
+ return a.messageExit(err, "Run `awiki-cli doctor` to inspect configuration and identity state.")
29
+ }
30
+ request := message.GroupCreateRequest{
31
+ IdentityName: a.globals.Identity,
32
+ Name: name,
33
+ Description: description,
34
+ Discoverability: discoverability,
35
+ AdmissionMode: admissionMode,
36
+ Slug: slug,
37
+ Goal: goal,
38
+ Rules: rules,
39
+ MessagePrompt: messagePrompt,
40
+ DocURL: docURL,
41
+ AttachmentsAllowed: attachmentsAllowed,
42
+ MaxMembers: maxMembers,
43
+ MemberMaxMessages: memberMaxMessages,
44
+ MemberMaxTotalChars: memberMaxTotalChars,
45
+ }
46
+ if a.globals.DryRun {
47
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, map[string]any{"plan": map[string]any{"action": "group.create", "identity": a.globals.Identity, "runtime_mode": service.Config().RuntimeMode, "request": request}}, "Dry run: group create planned", nil, a.identityMeta())
48
+ }
49
+ result, err := service.CreateGroup(context.Background(), request)
50
+ if err != nil {
51
+ return a.messageExit(err, "Ensure the active identity is registered and the message service is reachable.")
52
+ }
53
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, result.Data, result.Summary, result.Warnings, a.identityMeta())
54
+ }
55
+
56
+ func (a *App) runGroupShow(cmd *cobra.Command, args []string) error {
57
+ group, _ := cmd.Flags().GetString("group")
58
+ service, format, err := a.messageService()
59
+ if err != nil {
60
+ return a.messageExit(err, "Run `awiki-cli doctor` to inspect configuration and identity state.")
61
+ }
62
+ request := message.GroupGetRequest{IdentityName: a.globals.Identity, Group: group}
63
+ if a.globals.DryRun {
64
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, map[string]any{"plan": map[string]any{"action": "group.show", "identity": a.globals.Identity, "runtime_mode": service.Config().RuntimeMode, "group": group}}, "Dry run: group show planned", nil, a.identityMeta())
65
+ }
66
+ result, err := service.GetGroup(context.Background(), request)
67
+ if err != nil {
68
+ return a.messageExit(err, "Make sure the group exists and the active identity can access it.")
69
+ }
70
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, result.Data, result.Summary, result.Warnings, a.identityMeta())
71
+ }
72
+
73
+ func (a *App) runGroupJoin(cmd *cobra.Command, args []string) error {
74
+ group, _ := cmd.Flags().GetString("group")
75
+ reason, _ := cmd.Flags().GetString("reason")
76
+ service, format, err := a.messageService()
77
+ if err != nil {
78
+ return a.messageExit(err, "Run `awiki-cli doctor` to inspect configuration and identity state.")
79
+ }
80
+ request := message.GroupJoinRequest{IdentityName: a.globals.Identity, Group: group, ReasonText: reason}
81
+ if a.globals.DryRun {
82
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, map[string]any{"plan": map[string]any{"action": "group.join", "identity": a.globals.Identity, "runtime_mode": service.Config().RuntimeMode, "request": request}}, "Dry run: group join planned", nil, a.identityMeta())
83
+ }
84
+ result, err := service.JoinGroup(context.Background(), request)
85
+ if err != nil {
86
+ return a.messageExit(err, "Make sure the group exists and allows open join for the active identity.")
87
+ }
88
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, result.Data, result.Summary, result.Warnings, a.identityMeta())
89
+ }
90
+
91
+ func (a *App) runGroupAdd(cmd *cobra.Command, args []string) error {
92
+ return a.runGroupMemberMutation(cmd, "add", "add")
93
+ }
94
+
95
+ func (a *App) runGroupKick(cmd *cobra.Command, args []string) error {
96
+ return a.runGroupMemberMutation(cmd, "kick", "remove")
97
+ }
98
+
99
+ func (a *App) runGroupMemberMutation(cmd *cobra.Command, publicAction string, memberAction string) error {
100
+ group, _ := cmd.Flags().GetString("group")
101
+ member, _ := cmd.Flags().GetString("member")
102
+ role, _ := cmd.Flags().GetString("role")
103
+ reason, _ := cmd.Flags().GetString("reason")
104
+ service, format, err := a.messageService()
105
+ if err != nil {
106
+ return a.messageExit(err, "Run `awiki-cli doctor` to inspect configuration and identity state.")
107
+ }
108
+ request := message.GroupMemberRequest{IdentityName: a.globals.Identity, Group: group, Member: member, Role: role, ReasonText: reason}
109
+ if a.globals.DryRun {
110
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, map[string]any{"plan": map[string]any{"action": "group." + publicAction, "identity": a.globals.Identity, "runtime_mode": service.Config().RuntimeMode, "request": request}}, "Dry run: group membership change planned", nil, a.identityMeta())
111
+ }
112
+ var result *message.CommandResult
113
+ if memberAction == "add" {
114
+ result, err = service.AddGroupMember(context.Background(), request)
115
+ } else {
116
+ result, err = service.RemoveGroupMember(context.Background(), request)
117
+ }
118
+ if err != nil {
119
+ return a.messageExit(err, "Make sure the group and member exist and the active identity has the required role.")
120
+ }
121
+ switch publicAction {
122
+ case "add":
123
+ result.Summary = "Added member to group"
124
+ case "kick":
125
+ result.Summary = "Removed member from group"
126
+ }
127
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, result.Data, result.Summary, result.Warnings, a.identityMeta())
128
+ }
129
+
130
+ func (a *App) runGroupLeave(cmd *cobra.Command, args []string) error {
131
+ group, _ := cmd.Flags().GetString("group")
132
+ service, format, err := a.messageService()
133
+ if err != nil {
134
+ return a.messageExit(err, "Run `awiki-cli doctor` to inspect configuration and identity state.")
135
+ }
136
+ request := message.GroupLeaveRequest{IdentityName: a.globals.Identity, Group: group}
137
+ if a.globals.DryRun {
138
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, map[string]any{"plan": map[string]any{"action": "group.leave", "identity": a.globals.Identity, "runtime_mode": service.Config().RuntimeMode, "request": request}}, "Dry run: group leave planned", nil, a.identityMeta())
139
+ }
140
+ result, err := service.LeaveGroup(context.Background(), request)
141
+ if err != nil {
142
+ return a.messageExit(err, "Make sure the group exists and the active identity is still a member.")
143
+ }
144
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, result.Data, result.Summary, result.Warnings, a.identityMeta())
145
+ }
146
+
147
+ func (a *App) runGroupUpdate(cmd *cobra.Command, args []string) error {
148
+ group, _ := cmd.Flags().GetString("group")
149
+ name, _ := cmd.Flags().GetString("name")
150
+ description, _ := cmd.Flags().GetString("description")
151
+ discoverability, _ := cmd.Flags().GetString("discoverability")
152
+ admissionMode, _ := cmd.Flags().GetString("admission-mode")
153
+ slug, _ := cmd.Flags().GetString("slug")
154
+ goal, _ := cmd.Flags().GetString("goal")
155
+ rules, _ := cmd.Flags().GetString("rules")
156
+ messagePrompt, _ := cmd.Flags().GetString("message-prompt")
157
+ docURL, _ := cmd.Flags().GetString("doc-url")
158
+ maxMembers, _ := cmd.Flags().GetString("max-members")
159
+ attachmentsAllowed := boolFlagPtr(cmd, "attachments-allowed")
160
+ memberMaxMessages := int64FlagPtr(cmd, "member-max-messages")
161
+ memberMaxTotalChars := int64FlagPtr(cmd, "member-max-total-chars")
162
+ service, format, err := a.messageService()
163
+ if err != nil {
164
+ return a.messageExit(err, "Run `awiki-cli doctor` to inspect configuration and identity state.")
165
+ }
166
+ request := message.GroupUpdateRequest{
167
+ IdentityName: a.globals.Identity,
168
+ Group: group,
169
+ Name: name,
170
+ Description: description,
171
+ Discoverability: discoverability,
172
+ AdmissionMode: admissionMode,
173
+ Slug: slug,
174
+ Goal: goal,
175
+ Rules: rules,
176
+ MessagePrompt: messagePrompt,
177
+ DocURL: docURL,
178
+ AttachmentsAllowed: attachmentsAllowed,
179
+ MaxMembers: maxMembers,
180
+ MemberMaxMessages: memberMaxMessages,
181
+ MemberMaxTotalChars: memberMaxTotalChars,
182
+ }
183
+ if a.globals.DryRun {
184
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, map[string]any{"plan": map[string]any{"action": "group.update", "identity": a.globals.Identity, "runtime_mode": service.Config().RuntimeMode, "request": request}}, "Dry run: group update planned", nil, a.identityMeta())
185
+ }
186
+ result, err := service.UpdateGroup(context.Background(), request)
187
+ if err != nil {
188
+ return a.messageExit(err, "Make sure the active identity has permission to update the target group.")
189
+ }
190
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, result.Data, result.Summary, result.Warnings, a.identityMeta())
191
+ }
192
+
193
+ func (a *App) runGroupMembers(cmd *cobra.Command, args []string) error {
194
+ group, _ := cmd.Flags().GetString("group")
195
+ limit, _ := cmd.Flags().GetInt("limit")
196
+ service, format, err := a.messageService()
197
+ if err != nil {
198
+ return a.messageExit(err, "Run `awiki-cli doctor` to inspect configuration and identity state.")
199
+ }
200
+ request := message.GroupMembersRequest{IdentityName: a.globals.Identity, Group: group, Limit: limit}
201
+ if a.globals.DryRun {
202
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, map[string]any{"plan": map[string]any{"action": "group.list_members", "identity": a.globals.Identity, "runtime_mode": service.Config().RuntimeMode, "request": request}}, "Dry run: group members planned", nil, a.identityMeta())
203
+ }
204
+ result, err := service.GroupMembers(context.Background(), request)
205
+ if err != nil {
206
+ return a.messageExit(err, "Make sure the group exists and the active identity can access its member list.")
207
+ }
208
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, result.Data, result.Summary, result.Warnings, a.identityMeta())
209
+ }
210
+
211
+ func (a *App) runGroupMessages(cmd *cobra.Command, args []string) error {
212
+ group, _ := cmd.Flags().GetString("group")
213
+ limit, _ := cmd.Flags().GetInt("limit")
214
+ cursor, _ := cmd.Flags().GetString("cursor")
215
+ service, format, err := a.messageService()
216
+ if err != nil {
217
+ return a.messageExit(err, "Run `awiki-cli doctor` to inspect configuration and identity state.")
218
+ }
219
+ request := message.GroupMessagesRequest{IdentityName: a.globals.Identity, Group: group, Limit: limit, Cursor: cursor}
220
+ if a.globals.DryRun {
221
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, map[string]any{"plan": map[string]any{"action": "group.list_messages", "identity": a.globals.Identity, "runtime_mode": service.Config().RuntimeMode, "request": request}}, "Dry run: group messages planned", nil, a.identityMeta())
222
+ }
223
+ result, err := service.GroupMessages(context.Background(), request)
224
+ if err != nil {
225
+ return a.messageExit(err, "Make sure the group exists and the active identity can access its messages.")
226
+ }
227
+ return a.renderSuccess(cmd.CommandPath(), format, a.globals.JQ, result.Data, result.Summary, result.Warnings, a.identityMeta())
228
+ }
229
+
230
+ func boolFlagPtr(cmd *cobra.Command, name string) *bool {
231
+ if !cmd.Flags().Changed(name) {
232
+ return nil
233
+ }
234
+ value, err := cmd.Flags().GetBool(name)
235
+ if err != nil {
236
+ return nil
237
+ }
238
+ return &value
239
+ }
240
+
241
+ func int64FlagPtr(cmd *cobra.Command, name string) *int64 {
242
+ if !cmd.Flags().Changed(name) {
243
+ return nil
244
+ }
245
+ value, err := cmd.Flags().GetInt(name)
246
+ if err != nil {
247
+ return nil
248
+ }
249
+ result := int64(value)
250
+ return &result
251
+ }
252
+
253
+ func parseInt64OrEmpty(value string) *int64 {
254
+ value = strings.TrimSpace(value)
255
+ if value == "" {
256
+ return nil
257
+ }
258
+ parsed, err := strconv.ParseInt(value, 10, 64)
259
+ if err != nil {
260
+ return nil
261
+ }
262
+ return &parsed
263
+ }