返回 DeepSeek-Reasonix
render.go
根目录 / internal / capdiag / render.go
1 package capdiag
2
3 import (
4 "encoding/json"
5 "fmt"
6 "strings"
7 )
8
9 // RenderText formats a human-readable report: Summary → Issues → subsystems.
10 func RenderText(r Report) string {
11 var b strings.Builder
12 fmt.Fprintf(&b, "reasonix doctor capabilities\n")
13 fmt.Fprintf(&b, "root: %s live: %v schema: %d\n\n", r.Root, r.Live, r.SchemaVersion)
14
15 fmt.Fprintf(&b, "Summary\n")
16 fmt.Fprintf(&b, " errors=%d warnings=%d infos=%d\n", r.Summary.Errors, r.Summary.Warnings, r.Summary.Infos)
17 fmt.Fprintf(&b, " instructions=%d skills=%d commands=%d hooks=%d plugins=%d mcp=%d\n\n",
18 r.Summary.Instructions, r.Summary.Skills, r.Summary.Commands,
19 r.Summary.Hooks, r.Summary.Plugins, r.Summary.MCPServers)
20
21 fmt.Fprintf(&b, "Issues (%d)\n", len(r.Issues))
22 if len(r.Issues) == 0 {
23 b.WriteString(" (none)\n")
24 }
25 for _, is := range r.Issues {
26 fmt.Fprintf(&b, " [%s] %s", is.Severity, is.Code)
27 if is.Name != "" {
28 fmt.Fprintf(&b, " %s", is.Name)
29 }
30 fmt.Fprintf(&b, ": %s\n", is.Message)
31 if is.Source != "" {
32 fmt.Fprintf(&b, " source: %s\n", is.Source)
33 }
34 if is.Remediation != "" {
35 fmt.Fprintf(&b, " fix: %s\n", is.Remediation)
36 }
37 }
38 b.WriteByte('\n')
39
40 // Instructions
41 fmt.Fprintf(&b, "Instructions (%d)\n", len(r.Instructions.Docs))
42 for _, d := range r.Instructions.Docs {
43 fmt.Fprintf(&b, " %d. [%s depth=%d] %s", d.Order, d.Scope, d.Depth, d.Path)
44 if d.Directory != "" {
45 fmt.Fprintf(&b, " (applies to %s)", d.Directory)
46 }
47 b.WriteByte('\n')
48 }
49 if len(r.Instructions.Docs) == 0 {
50 b.WriteString(" (none)\n")
51 }
52 b.WriteByte('\n')
53
54 writeAsset(&b, "Skills", r.Skills)
55 writeAsset(&b, "Commands", r.Commands)
56
57 fmt.Fprintf(&b, "Hooks (project_defines=%v entries=%d)\n",
58 r.Hooks.ProjectDefines, len(r.Hooks.Entries))
59 for _, s := range r.Hooks.Sources {
60 fmt.Fprintf(&b, " source [%s] %s status=%s hooks=%d\n", s.Scope, s.Path, s.Status, s.HookCount)
61 }
62 for _, e := range r.Hooks.Entries {
63 fmt.Fprintf(&b, " - %s scope=%s match=%q blocking=%v\n", e.Event, e.Scope, e.Match, e.Blocking)
64 }
65 b.WriteByte('\n')
66
67 fmt.Fprintf(&b, "Plugins (%d)\n", len(r.Plugins.Packages))
68 for _, p := range r.Plugins.Packages {
69 fmt.Fprintf(&b, " - %s enabled=%v status=%s skills=%d commands=%d prompts=%d hooks=%d mcp=%d themes=%d\n",
70 p.Name, p.Enabled, p.Status, p.Skills, p.Commands, p.Prompts, p.Hooks, p.MCPServers, p.Themes)
71 if p.Runtime {
72 b.WriteString(" runtime: FULL TRUST (declares a runtime process)\n")
73 }
74 fmt.Fprintf(&b, " root: %s\n", p.Root)
75 }
76 if len(r.Plugins.Packages) == 0 {
77 b.WriteString(" (none)\n")
78 }
79 b.WriteByte('\n')
80
81 fmt.Fprintf(&b, "MCP (%d)\n", len(r.MCP.Servers))
82 for _, s := range r.MCP.Servers {
83 fmt.Fprintf(&b, " - %s transport=%s intent=%s source=%s effective=%v", s.Name, s.Transport, s.StartIntent, s.Source, s.Effective)
84 if s.RuntimeStatus != "" {
85 fmt.Fprintf(&b, " runtime=%s", s.RuntimeStatus)
86 }
87 if s.ToolCount > 0 {
88 fmt.Fprintf(&b, " tools=%d", s.ToolCount)
89 }
90 b.WriteByte('\n')
91 if s.SourcePath != "" {
92 fmt.Fprintf(&b, " source_path: %s\n", s.SourcePath)
93 }
94 if s.StartupStage != "" {
95 fmt.Fprintf(&b, " startup: stage=%s elapsed_ms=%d\n", s.StartupStage, s.StartupElapsedMS)
96 }
97 if s.Error != "" {
98 fmt.Fprintf(&b, " error: %s\n", s.Error)
99 }
100 if s.Stderr != "" {
101 fmt.Fprintf(&b, " stderr: %s\n", s.Stderr)
102 }
103 }
104 if len(r.MCP.Servers) == 0 {
105 b.WriteString(" (none)\n")
106 }
107 return b.String()
108 }
109
110 func writeAsset(b *strings.Builder, title string, a AssetReport) {
111 fmt.Fprintf(b, "%s (winners=%d shadowed=%d", title, a.Winners, a.Shadowed)
112 if a.Disabled > 0 {
113 fmt.Fprintf(b, " disabled=%d", a.Disabled)
114 }
115 if a.ParseErrors > 0 {
116 fmt.Fprintf(b, " errors=%d", a.ParseErrors)
117 }
118 b.WriteString(")\n")
119 for _, r := range a.Roots {
120 fmt.Fprintf(b, " root %s status=%s", r.Path, r.Status)
121 if r.Scope != "" {
122 fmt.Fprintf(b, " scope=%s", r.Scope)
123 }
124 b.WriteByte('\n')
125 }
126 for _, e := range a.Entries {
127 if e.Status != "winner" && e.Status != "error" {
128 continue // keep text output compact; full list is in JSON
129 }
130 fmt.Fprintf(b, " - %s [%s] %s\n", e.Name, e.Status, e.Path)
131 }
132 b.WriteByte('\n')
133 }
134
135 // RenderJSON writes indented JSON to a string (for tests / copy).
136 func RenderJSON(r Report) (string, error) {
137 b, err := json.MarshalIndent(r, "", " ")
138 if err != nil {
139 return "", err
140 }
141 return string(b) + "\n", nil
142 }
143
143 lines GO