s.go

 1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5package cmd
 6
 7import (
 8	"errors"
 9	"fmt"
10	"os"
11
12	"git.secluded.site/np/internal/session"
13	"github.com/spf13/cobra"
14)
15
16var sCmd = &cobra.Command{
17	Use:   "s",
18	Short: "Start session",
19	Long:  `Start a new working-directory-scoped session`,
20	RunE:  runStartSession,
21}
22
23func runStartSession(cmd *cobra.Command, args []string) error {
24	env, err := requireEnvironment()
25	if err != nil {
26		return err
27	}
28
29	ctx := cmd.Context()
30	cwd, err := os.Getwd()
31	if err != nil {
32		return fmt.Errorf("determine working directory: %w", err)
33	}
34
35	doc, err := env.SessionStore.Start(ctx, cwd)
36	if err != nil {
37		var already session.AlreadyActiveError
38		if errors.As(err, &already) {
39			return printExistingSession(cmd, already.Session)
40		}
41		return err
42	}
43
44	printSessionStarted(cmd, doc)
45	return nil
46}
47
48func printExistingSession(cmd *cobra.Command, existing session.Document) error {
49	out := cmd.OutOrStdout()
50	_, _ = fmt.Fprintf(out, "Session %s is already active for %s.\n", existing.SID, existing.DirPath)
51	_, _ = fmt.Fprintln(out, "Ask your operator whether they want to resume (`np r`) or archive (`np a`) it.")
52	return nil
53}
54
55func printSessionStarted(cmd *cobra.Command, doc session.Document) {
56	out := cmd.OutOrStdout()
57	_, _ = fmt.Fprintf(out, "Session %s is now active for %s.\n\n", doc.SID, doc.DirPath)
58	_, _ = fmt.Fprintln(out, "If you haven't already, read any provided issue/ticket/file/webpage/commit/etc. references and thoroughly mull them over. If you already see the contents above, don't re-read them. If there were no referenced files or you don't have a clear picture of the issue, you may selectively read additional relevant files until you do have a clear picture.")
59	_, _ = fmt.Fprintln(out, "Set the goal with `np g s -t \"goal title\" -d \"goal description\"`.")
60	_, _ = fmt.Fprintln(out, "Capture the most concise form of the overarching goal using no more than 20 words in the title. Elaborate _usefully_ in the description; don't just repeat the title in more flowery language. In case we're interrupted and need to pick up from this plan later, include:")
61	_, _ = fmt.Fprintln(out, "- Summaries of only the relevant portions of the referenced content. Include their URLs/IDs/hashes. Copy the user's language around the references: if they say 'look at bug REF', use 'Bug: REF' near the summary. If 'issue NUM', then 'Issue: NUM.'")
62	_, _ = fmt.Fprintln(out, "- Paths to the relevant files, and if there are particularly relevant symbols from those files, include them too. DO NOT summarise the files or symbols. Only list them if they're relevant")
63	_, _ = fmt.Fprintln(out, "- An 'Immediate thoughts:' line at the bottom. You should have enough files to have some idea of the issue and its resolution; briefly capture your immediate thoughts in this line, couching with appropriate uncertainty.")
64	_, _ = fmt.Fprintln(out, "Add single tasks with `np t a -t \"task\" -d \"details\"`, but prefer batching. For multi-line descriptions, use literal newlines:")
65	_, _ = fmt.Fprintln(out, "    np t a -t \"first task\" -d \"step 1 details\" -t \"second task\" -d \"step 2 with")
66	_, _ = fmt.Fprintln(out, "    \nmore details\" -t \"third task\" -d \"step three\"")
67	_, _ = fmt.Fprintln(out, "Keep task statuses up to date as you work:")
68	_, _ = fmt.Fprintln(out, "  Single update:  `np t u -i task-id -s in_progress|completed|failed|cancelled`")
69	_, _ = fmt.Fprintln(out, "  But prefer batching:  `np t u -i abc123 -s completed -i def456 -s in_progress`")
70	_, _ = fmt.Fprintln(out, "Use `np p` if you need to review the full plan.")
71}