Skip to content

Commit 040ce99

Browse files
committed
refactor: improve code quality with godoc and local flag variables
- Use PrintErrln for error output in main.go (stderr instead of stdout) - Replace package-level flag variables with local variables in Cmd() functions for add, agent, and compact packages - Add comprehensive godoc following canonical format with Parameters, Returns, and Fields sections - Add package documentation to bootstrap, claude, and add packages - Add copyright headers to new files - Split large command files into focused modules (run, extract, types, etc.) Signed-off-by: Jose Alekhinne <alekhinejose@gmail.com>
1 parent 4d4691e commit 040ce99

38 files changed

Lines changed: 1643 additions & 961 deletions

cmd/ctx/main.go

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,23 @@
11
// / Context: https://ctx.ist
22
// ,'`./ do you remember?
33
// `.,'\
4-
// \ Copyright 2025-present Context contributors.
4+
// \ Copyright 2026-present Context contributors.
55
// SPDX-License-Identifier: Apache-2.0
66

7+
// package main is the main entry point of the app.
78
package main
89

910
import (
10-
"fmt"
1111
"os"
1212

13-
"github.com/ActiveMemory/ctx/internal/cli"
14-
"github.com/spf13/cobra"
13+
"github.com/ActiveMemory/ctx/internal/bootstrap"
1514
)
1615

17-
// Version is set at build time via ldflags
18-
var Version = "dev"
19-
20-
var rootCmd = &cobra.Command{
21-
Use: "ctx",
22-
Short: "Context - persistent context for AI coding assistants",
23-
Long: `Context (ctx) maintains persistent context files that help
24-
AI coding assistants understand your project's architecture, conventions,
25-
decisions, and current tasks.
26-
27-
Use 'ctx init' to create a .context/ directory in your project,
28-
then use 'ctx status', 'ctx load', and 'ctx agent' to work with context.`,
29-
Version: Version,
30-
}
31-
32-
func init() {
33-
rootCmd.AddCommand(cli.InitCmd())
34-
rootCmd.AddCommand(cli.StatusCmd())
35-
rootCmd.AddCommand(cli.LoadCmd())
36-
rootCmd.AddCommand(cli.AddCmd())
37-
rootCmd.AddCommand(cli.CompleteCmd())
38-
rootCmd.AddCommand(cli.AgentCmd())
39-
rootCmd.AddCommand(cli.DriftCmd())
40-
rootCmd.AddCommand(cli.SyncCmd())
41-
rootCmd.AddCommand(cli.CompactCmd())
42-
rootCmd.AddCommand(cli.WatchCmd())
43-
rootCmd.AddCommand(cli.HookCmd())
44-
rootCmd.AddCommand(cli.SessionCmd())
45-
rootCmd.AddCommand(cli.TasksCmd())
46-
rootCmd.AddCommand(cli.LoopCmd())
47-
}
48-
4916
func main() {
50-
if err := rootCmd.Execute(); err != nil {
51-
_, err := fmt.Fprintln(os.Stderr, err)
52-
if err != nil {
53-
return
54-
}
17+
cmd := bootstrap.Initialize(bootstrap.RootCmd())
18+
19+
if err := cmd.Execute(); err != nil {
20+
cmd.PrintErrln("Error:", err)
5521
os.Exit(1)
5622
}
5723
}

internal/bootstrap/bootstrap.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// / Context: https://ctx.ist
2+
// ,'`./ do you remember?
3+
// `.,'\
4+
// \ Copyright 2026-present Context contributors.
5+
// SPDX-License-Identifier: Apache-2.0
6+
7+
// Package bootstrap initializes the ctx CLI application.
8+
//
9+
// It provides functions to create the root command and register all
10+
// subcommands. The typical usage pattern is:
11+
//
12+
// cmd := bootstrap.Initialize(bootstrap.RootCmd())
13+
// if err := cmd.Execute(); err != nil {
14+
// // handle error
15+
// }
16+
package bootstrap
17+
18+
import (
19+
"github.com/ActiveMemory/ctx/internal/cli/add"
20+
"github.com/ActiveMemory/ctx/internal/cli/agent"
21+
"github.com/ActiveMemory/ctx/internal/cli/compact"
22+
"github.com/ActiveMemory/ctx/internal/cli/complete"
23+
"github.com/ActiveMemory/ctx/internal/cli/drift"
24+
"github.com/ActiveMemory/ctx/internal/cli/hook"
25+
"github.com/ActiveMemory/ctx/internal/cli/init"
26+
"github.com/ActiveMemory/ctx/internal/cli/load"
27+
"github.com/ActiveMemory/ctx/internal/cli/loop"
28+
"github.com/ActiveMemory/ctx/internal/cli/session"
29+
"github.com/ActiveMemory/ctx/internal/cli/status"
30+
"github.com/ActiveMemory/ctx/internal/cli/sync"
31+
"github.com/ActiveMemory/ctx/internal/cli/task"
32+
"github.com/ActiveMemory/ctx/internal/cli/watch"
33+
"github.com/spf13/cobra"
34+
)
35+
36+
// version is set at build time via ldflags
37+
const version = "dev"
38+
39+
// RootCmd creates and returns the root cobra command for the ctx CLI.
40+
//
41+
// The root command provides the entry point for all ctx subcommands and
42+
// displays help information when invoked without arguments.
43+
//
44+
// Returns:
45+
// - *cobra.Command: The configured root command with usage and version info
46+
func RootCmd() *cobra.Command {
47+
return &cobra.Command{
48+
Use: "ctx",
49+
Short: "Context - persistent context for AI coding assistants",
50+
Long: `Context (ctx) maintains persistent context files that help
51+
AI coding assistants understand your project's architecture, conventions,
52+
decisions, and current tasks.
53+
54+
Use 'ctx init' to create a .context/ directory in your project,
55+
then use 'ctx status', 'ctx load', and 'ctx agent' to work with context.`,
56+
Version: version,
57+
}
58+
}
59+
60+
// Initialize registers all ctx subcommands with the root command.
61+
//
62+
// This function attaches all available subcommands (init, status, load, add,
63+
// complete, agent, drift, sync, compact, watch, hook, session, tasks, loop)
64+
// to the provided root command.
65+
//
66+
// Parameters:
67+
// - cmd: The root cobra command to attach subcommands to
68+
//
69+
// Returns:
70+
// - *cobra.Command: The same command with all subcommands registered
71+
func Initialize(cmd *cobra.Command) *cobra.Command {
72+
cmd.AddCommand(init.InitCmd())
73+
cmd.AddCommand(status.StatusCmd())
74+
cmd.AddCommand(load.LoadCmd())
75+
cmd.AddCommand(add.Cmd())
76+
cmd.AddCommand(complete.CompleteCmd())
77+
cmd.AddCommand(agent.Cmd())
78+
cmd.AddCommand(drift.DriftCmd())
79+
cmd.AddCommand(sync.SyncCmd())
80+
cmd.AddCommand(compact.Cmd())
81+
cmd.AddCommand(watch.WatchCmd())
82+
cmd.AddCommand(hook.HookCmd())
83+
cmd.AddCommand(session.SessionCmd())
84+
cmd.AddCommand(task.TasksCmd())
85+
cmd.AddCommand(loop.LoopCmd())
86+
87+
return cmd
88+
}

internal/claude/doc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package claude

internal/claude/embed.go

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
// / Context: https://ctx.ist
22
// ,'`./ do you remember?
33
// `.,'\
4-
// \ Copyright 2025-present Context contributors.
4+
// \ Copyright 2026-present Context contributors.
55
// SPDX-License-Identifier: Apache-2.0
66

7-
// Package claude provides Claude Code integration templates and utilities.
87
package claude
98

109
import (
@@ -15,7 +14,14 @@ import (
1514
//go:embed tpl/auto-save-session.sh tpl/block-non-path-ctx.sh tpl/commands/*.md
1615
var FS embed.FS
1716

18-
// GetAutoSaveScript returns the auto-save session script.
17+
// GetAutoSaveScript returns the auto-save session script content.
18+
//
19+
// The script automatically saves Claude Code session transcripts when a
20+
// session ends. It is installed to .claude/hooks/ during ctx init --claude.
21+
//
22+
// Returns:
23+
// - []byte: Raw bytes of the auto-save-session.sh script
24+
// - error: Non-nil if the embedded file cannot be read
1925
func GetAutoSaveScript() ([]byte, error) {
2026
content, err := FS.ReadFile("tpl/auto-save-session.sh")
2127
if err != nil {
@@ -24,7 +30,16 @@ func GetAutoSaveScript() ([]byte, error) {
2430
return content, nil
2531
}
2632

27-
// GetBlockNonPathCtxScript returns the script that blocks non-PATH ctx invocations.
33+
// GetBlockNonPathCtxScript returns the script that blocks non-PATH ctx
34+
// invocations.
35+
//
36+
// The script prevents Claude from running ctx via relative paths (./ctx,
37+
// ./dist/ctx) or "go run", ensuring only the installed PATH version is used.
38+
// It is installed to .claude/hooks/ during ctx init --claude.
39+
//
40+
// Returns:
41+
// - []byte: Raw bytes of the block-non-path-ctx.sh script
42+
// - error: Non-nil if the embedded file cannot be read
2843
func GetBlockNonPathCtxScript() ([]byte, error) {
2944
content, err := FS.ReadFile("tpl/block-non-path-ctx.sh")
3045
if err != nil {
@@ -34,6 +49,14 @@ func GetBlockNonPathCtxScript() ([]byte, error) {
3449
}
3550

3651
// ListCommands returns the list of embedded command file names.
52+
//
53+
// These are Claude Code slash command definitions (e.g., "ctx-status.md",
54+
// "commit-local.md") from the tpl/commands directory. They can be installed
55+
// to .claude/commands/ via "ctx init --claude".
56+
//
57+
// Returns:
58+
// - []string: Filenames of available command definitions
59+
// - error: Non-nil if the commands directory cannot be read
3760
func ListCommands() ([]string, error) {
3861
entries, err := FS.ReadDir("tpl/commands")
3962
if err != nil {
@@ -49,7 +72,14 @@ func ListCommands() ([]string, error) {
4972
return names, nil
5073
}
5174

52-
// GetCommand returns the content of a command file.
75+
// GetCommand returns the content of a command file by name.
76+
//
77+
// Parameters:
78+
// - name: Filename as returned by [ListCommands] (e.g., "ctx-status.md")
79+
//
80+
// Returns:
81+
// - []byte: Raw bytes of the command definition file
82+
// - error: Non-nil if the command file does not exist or cannot be read
5383
func GetCommand(name string) ([]byte, error) {
5484
content, err := FS.ReadFile("tpl/commands/" + name)
5585
if err != nil {
@@ -58,40 +88,27 @@ func GetCommand(name string) ([]byte, error) {
5888
return content, nil
5989
}
6090

61-
// SettingsHooks represents the hooks section of settings.local.json
62-
type SettingsHooks struct {
63-
PreToolUse []HookMatcher `json:"PreToolUse,omitempty"`
64-
SessionEnd []HookMatcher `json:"SessionEnd,omitempty"`
65-
}
66-
67-
// HookMatcher represents a hook matcher with optional pattern
68-
type HookMatcher struct {
69-
Matcher string `json:"matcher,omitempty"`
70-
Hooks []Hook `json:"hooks"`
71-
}
72-
73-
// Hook represents a single hook command
74-
type Hook struct {
75-
Type string `json:"type"`
76-
Command string `json:"command"`
77-
}
78-
79-
// Settings represents the full settings.local.json structure
80-
type Settings struct {
81-
Hooks SettingsHooks `json:"hooks,omitempty"`
82-
Permissions map[string]interface{} `json:"permissions,omitempty"`
83-
}
84-
85-
// CreateDefaultHooks returns the default ctx hooks configuration.
86-
// Hooks use "ctx" expecting it to be in PATH.
87-
func CreateDefaultHooks(projectDir string) SettingsHooks {
91+
// CreateDefaultHooks returns the default ctx hooks configuration for
92+
// Claude Code.
93+
//
94+
// The returned hooks configure PreToolUseHooks to block non-PATH ctx
95+
// invocations and auto-load context on every tool use, and SessionEndHooks
96+
// to run auto-save-session.sh for persisting session transcripts.
97+
//
98+
// Parameters:
99+
// - projectDir: Project root directory for absolute hook paths; if empty,
100+
// paths are relative (e.g., ".claude/hooks/")
101+
//
102+
// Returns:
103+
// - HookConfig: Configured hooks for PreToolUse and SessionEnd events
104+
func CreateDefaultHooks(projectDir string) HookConfig {
88105
hooksDir := ".claude/hooks"
89106
if projectDir != "" {
90107
hooksDir = fmt.Sprintf("%s/.claude/hooks", projectDir)
91108
}
92109

93-
return SettingsHooks{
94-
PreToolUse: []HookMatcher{
110+
return HookConfig{
111+
PreToolUseHooks: []HookMatcher{
95112
{
96113
// Block non-PATH ctx invocations (./ctx, ./dist/ctx, go run ./cmd/ctx)
97114
Matcher: "Bash",
@@ -103,7 +120,7 @@ func CreateDefaultHooks(projectDir string) SettingsHooks {
103120
},
104121
},
105122
{
106-
// Auto-load context on every tool use
123+
// Autoload context on every tool use
107124
Matcher: ".*",
108125
Hooks: []Hook{
109126
{
@@ -113,7 +130,7 @@ func CreateDefaultHooks(projectDir string) SettingsHooks {
113130
},
114131
},
115132
},
116-
SessionEnd: []HookMatcher{
133+
SessionEndHooks: []HookMatcher{
117134
{
118135
Hooks: []Hook{
119136
{

internal/claude/types.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// / Context: https://ctx.ist
2+
// ,'`./ do you remember?
3+
// `.,'\
4+
// \ Copyright 2026-present Context contributors.
5+
// SPDX-License-Identifier: Apache-2.0
6+
7+
package claude
8+
9+
// HookConfig represents the hooks section of Claude Code's
10+
// settings.local.json.
11+
//
12+
// Hooks are shell commands that Claude Code executes at specific lifecycle
13+
// events. See https://docs.anthropic.com/en/docs/claude-code/hooks for details.
14+
//
15+
// Fields:
16+
// - PreToolUseHooks: Matchers that run before each tool invocation
17+
// - SessionEndHooks: Matchers that run when a session ends
18+
type HookConfig struct {
19+
PreToolUseHooks []HookMatcher `json:"PreToolUseHooks,omitempty"`
20+
SessionEndHooks []HookMatcher `json:"SessionEndHooks,omitempty"`
21+
}
22+
23+
// HookMatcher associates a regex pattern with hooks to execute.
24+
//
25+
// For PreToolUseHooks, the Matcher pattern matches against the tool name
26+
// (e.g., "Bash", "Read"). Use ".*" to match all tools.
27+
//
28+
// Fields:
29+
// - Matcher: Regex pattern to match; empty string matches all
30+
// - Hooks: Commands to execute when the pattern matches
31+
type HookMatcher struct {
32+
Matcher string `json:"matcher,omitempty"`
33+
Hooks []Hook `json:"hooks"`
34+
}
35+
36+
// Hook represents a single hook command to execute.
37+
//
38+
// Fields:
39+
// - Type: Hook type, typically "command"
40+
// - Command: Shell command or script path to execute
41+
type Hook struct {
42+
Type string `json:"type"`
43+
Command string `json:"command"`
44+
}
45+
46+
// Settings represents the full Claude Code settings.local.json structure.
47+
//
48+
// This is used when reading or writing project-level Claude Code configuration.
49+
//
50+
// Fields:
51+
// - Hooks: Hook configuration for lifecycle events
52+
// - Permissions: Tool permission overrides (key: tool pattern, value: permission)
53+
type Settings struct {
54+
Hooks HookConfig `json:"hooks,omitempty"`
55+
Permissions map[string]interface{} `json:"permissions,omitempty"`
56+
}

0 commit comments

Comments
 (0)