supipowers 1.2.6 → 1.5.0

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 (137) hide show
  1. package/README.md +118 -56
  2. package/bin/install.ts +48 -128
  3. package/package.json +11 -3
  4. package/skills/code-review/SKILL.md +137 -40
  5. package/skills/context-mode/SKILL.md +67 -56
  6. package/skills/creating-supi-agents/SKILL.md +204 -0
  7. package/skills/debugging/SKILL.md +86 -40
  8. package/skills/fix-pr/SKILL.md +96 -65
  9. package/skills/planning/SKILL.md +103 -46
  10. package/skills/qa-strategy/SKILL.md +68 -46
  11. package/skills/receiving-code-review/SKILL.md +60 -53
  12. package/skills/release/SKILL.md +111 -39
  13. package/skills/tdd/SKILL.md +118 -67
  14. package/skills/verification/SKILL.md +71 -37
  15. package/src/bootstrap.ts +27 -5
  16. package/src/commands/agents.ts +249 -0
  17. package/src/commands/ai-review.ts +1113 -0
  18. package/src/commands/config.ts +224 -95
  19. package/src/commands/doctor.ts +19 -13
  20. package/src/commands/fix-pr.ts +8 -11
  21. package/src/commands/generate.ts +200 -0
  22. package/src/commands/model-picker.ts +5 -15
  23. package/src/commands/model.ts +4 -5
  24. package/src/commands/optimize-context.ts +202 -0
  25. package/src/commands/plan.ts +148 -92
  26. package/src/commands/qa.ts +14 -23
  27. package/src/commands/release.ts +504 -275
  28. package/src/commands/review.ts +643 -86
  29. package/src/commands/status.ts +44 -17
  30. package/src/commands/supi.ts +69 -41
  31. package/src/commands/update.ts +57 -2
  32. package/src/config/defaults.ts +6 -39
  33. package/src/config/loader.ts +388 -40
  34. package/src/config/model-resolver.ts +26 -22
  35. package/src/config/schema.ts +113 -48
  36. package/src/context/analyzer.ts +61 -2
  37. package/src/context/optimizer.ts +199 -0
  38. package/src/context-mode/compressor.ts +14 -11
  39. package/src/context-mode/detector.ts +16 -54
  40. package/src/context-mode/event-extractor.ts +45 -16
  41. package/src/context-mode/event-store.ts +225 -16
  42. package/src/context-mode/hooks.ts +195 -22
  43. package/src/context-mode/knowledge/chunker.ts +235 -0
  44. package/src/context-mode/knowledge/store.ts +187 -0
  45. package/src/context-mode/routing.ts +12 -23
  46. package/src/context-mode/sandbox/executor.ts +183 -0
  47. package/src/context-mode/sandbox/runners.ts +40 -0
  48. package/src/context-mode/snapshot-builder.ts +243 -7
  49. package/src/context-mode/tools.ts +440 -0
  50. package/src/context-mode/web/fetcher.ts +117 -0
  51. package/src/context-mode/web/html-to-md.ts +293 -0
  52. package/src/debug/logger.ts +107 -0
  53. package/src/deps/registry.ts +0 -20
  54. package/src/docs/drift.ts +454 -0
  55. package/src/fix-pr/fetch-comments.ts +66 -0
  56. package/src/git/commit-msg.ts +2 -1
  57. package/src/git/commit.ts +123 -141
  58. package/src/git/conventions.ts +2 -2
  59. package/src/git/status.ts +4 -1
  60. package/src/lsp/bridge.ts +138 -12
  61. package/src/planning/approval-flow.ts +125 -19
  62. package/src/planning/plan-writer-prompt.ts +4 -11
  63. package/src/planning/planning-ask-tool.ts +81 -0
  64. package/src/planning/prompt-builder.ts +9 -169
  65. package/src/planning/system-prompt.ts +290 -0
  66. package/src/platform/omp.ts +50 -4
  67. package/src/platform/progress.ts +182 -0
  68. package/src/platform/test-utils.ts +4 -1
  69. package/src/platform/tui-colors.ts +30 -0
  70. package/src/platform/types.ts +1 -0
  71. package/src/qa/detect-app-type.ts +102 -0
  72. package/src/qa/discover-routes.ts +353 -0
  73. package/src/quality/ai-session.ts +96 -0
  74. package/src/quality/ai-setup.ts +86 -0
  75. package/src/quality/gates/ai-review.ts +129 -0
  76. package/src/quality/gates/build.ts +8 -0
  77. package/src/quality/gates/command.ts +150 -0
  78. package/src/quality/gates/format.ts +28 -0
  79. package/src/quality/gates/lint.ts +22 -0
  80. package/src/quality/gates/lsp-diagnostics.ts +84 -0
  81. package/src/quality/gates/test-suite.ts +8 -0
  82. package/src/quality/gates/typecheck.ts +22 -0
  83. package/src/quality/registry.ts +25 -0
  84. package/src/quality/review-gates.ts +33 -0
  85. package/src/quality/runner.ts +268 -0
  86. package/src/quality/schemas.ts +48 -0
  87. package/src/quality/setup.ts +227 -0
  88. package/src/release/changelog.ts +7 -3
  89. package/src/release/channels/custom.ts +43 -0
  90. package/src/release/channels/gitea.ts +35 -0
  91. package/src/release/channels/github.ts +35 -0
  92. package/src/release/channels/gitlab.ts +35 -0
  93. package/src/release/channels/registry.ts +52 -0
  94. package/src/release/channels/types.ts +27 -0
  95. package/src/release/detector.ts +10 -63
  96. package/src/release/executor.ts +61 -51
  97. package/src/release/prompt.ts +38 -38
  98. package/src/release/version.ts +129 -10
  99. package/src/review/agent-loader.ts +331 -0
  100. package/src/review/consolidator.ts +180 -0
  101. package/src/review/default-agents/correctness.md +72 -0
  102. package/src/review/default-agents/maintainability.md +64 -0
  103. package/src/review/default-agents/security.md +67 -0
  104. package/src/review/fixer.ts +219 -0
  105. package/src/review/multi-agent-runner.ts +135 -0
  106. package/src/review/output.ts +147 -0
  107. package/src/review/prompts/agent-review-wrapper.md +36 -0
  108. package/src/review/prompts/fix-findings.md +32 -0
  109. package/src/review/prompts/fix-output-schema.md +18 -0
  110. package/src/review/prompts/invalid-output-retry.md +22 -0
  111. package/src/review/prompts/output-instructions.md +14 -0
  112. package/src/review/prompts/review-output-schema.md +38 -0
  113. package/src/review/prompts/single-review.md +53 -0
  114. package/src/review/prompts/validation-review.md +30 -0
  115. package/src/review/runner.ts +128 -0
  116. package/src/review/scope.ts +353 -0
  117. package/src/review/template.ts +15 -0
  118. package/src/review/types.ts +296 -0
  119. package/src/review/validator.ts +160 -0
  120. package/src/storage/plans.ts +5 -3
  121. package/src/storage/reports.ts +50 -7
  122. package/src/storage/review-sessions.ts +117 -0
  123. package/src/text.ts +19 -0
  124. package/src/types.ts +336 -26
  125. package/src/utils/paths.ts +39 -0
  126. package/src/visual/companion.ts +5 -3
  127. package/src/visual/start-server.ts +101 -0
  128. package/src/visual/stop-server.ts +39 -0
  129. package/bin/ctx-mode-wrapper.mjs +0 -66
  130. package/src/config/profiles.ts +0 -64
  131. package/src/context-mode/installer.ts +0 -38
  132. package/src/quality/ai-review-gate.ts +0 -43
  133. package/src/quality/gate-runner.ts +0 -67
  134. package/src/quality/lsp-gate.ts +0 -24
  135. package/src/quality/test-gate.ts +0 -39
  136. package/src/visual/scripts/start-server.sh +0 -98
  137. package/src/visual/scripts/stop-server.sh +0 -21
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env node
2
- // Wrapper for context-mode MCP server that preserves the project directory.
3
- // OMP launches MCP servers with cwd set to the project directory, but
4
- // context-mode's start.mjs immediately does process.chdir(__dirname),
5
- // losing the project path. This wrapper captures cwd first.
6
- //
7
- // Setting CLAUDE_PROJECT_DIR causes start.mjs to also write a CLAUDE.md
8
- // in the project root. This is harmless (OMP doesn't read it) but the
9
- // user should gitignore it. We also write .omp/APPEND_SYSTEM.md with routing
10
- // rules since that's what OMP actually loads as system prompt.
11
-
12
- import { resolve, join, dirname } from "node:path";
13
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
14
- import { fileURLToPath } from "node:url";
15
- import { homedir } from "node:os";
16
-
17
- const __wrapperDir = dirname(fileURLToPath(import.meta.url));
18
-
19
- // Capture the real project directory BEFORE start.mjs clobbers it
20
- const projectDir = resolve(process.cwd());
21
- process.env.CLAUDE_PROJECT_DIR = projectDir;
22
-
23
- // Note: context-mode uses per-PID ephemeral FTS5 databases that are cleaned
24
- // up on process exit. Within a session, indexed content persists and ctx_search
25
- // can query it. Across sessions, content must be re-indexed.
26
- // The SKILL.md routing rules emphasize using ctx_search for follow-ups.
27
-
28
- // Resolve start.mjs path from the first CLI argument
29
- const startMjs = process.argv[2];
30
- if (!startMjs) {
31
- process.stderr.write("ctx-mode-wrapper: missing start.mjs path argument\n");
32
- process.exit(1);
33
- }
34
-
35
- // Write .omp/APPEND_SYSTEM.md with routing rules (idempotent — only if marker absent)
36
- const ROUTING_MARKER = "# context-mode — MANDATORY routing rules";
37
- try {
38
- // Find SKILL.md from multiple possible locations
39
- const skillCandidates = [
40
- join(homedir(), ".omp", "agent", "skills", "context-mode", "SKILL.md"),
41
- join(__wrapperDir, "..", "skills", "context-mode", "SKILL.md"),
42
- ];
43
- let skillContent = null;
44
- for (const candidate of skillCandidates) {
45
- try { skillContent = readFileSync(candidate, "utf-8"); break; } catch { /* next */ }
46
- }
47
-
48
- if (skillContent && skillContent.includes(ROUTING_MARKER)) {
49
- const ompDir = join(projectDir, ".omp");
50
- const systemMdPath = join(ompDir, "APPEND_SYSTEM.md");
51
-
52
- if (!existsSync(ompDir)) mkdirSync(ompDir, { recursive: true });
53
-
54
- if (!existsSync(systemMdPath)) {
55
- writeFileSync(systemMdPath, skillContent);
56
- } else {
57
- const existing = readFileSync(systemMdPath, "utf-8");
58
- if (!existing.includes(ROUTING_MARKER)) {
59
- writeFileSync(systemMdPath, existing.trimEnd() + "\n\n" + skillContent);
60
- }
61
- }
62
- }
63
- } catch { /* best effort — don't block server startup */ }
64
-
65
- // Dynamic import to run start.mjs (server starts here)
66
- await import(startMjs);
@@ -1,64 +0,0 @@
1
- // src/config/profiles.ts
2
- import * as fs from "node:fs";
3
- import * as path from "node:path";
4
- import type { Profile, SupipowersConfig } from "../types.js";
5
- import type { PlatformPaths } from "../platform/types.js";
6
- import { BUILTIN_PROFILES } from "./defaults.js";
7
-
8
- function getProfilesDir(paths: PlatformPaths, cwd: string): string {
9
- return paths.project(cwd, "profiles");
10
- }
11
-
12
- /** Load a profile by name. Checks project dir first, then built-ins. */
13
- export function loadProfile(paths: PlatformPaths, cwd: string, name: string): Profile | null {
14
- // Check project-level custom profiles
15
- const customPath = path.join(getProfilesDir(paths, cwd), `${name}.json`);
16
- if (fs.existsSync(customPath)) {
17
- try {
18
- return JSON.parse(fs.readFileSync(customPath, "utf-8")) as Profile;
19
- } catch {
20
- // fall through to built-in
21
- }
22
- }
23
- return BUILTIN_PROFILES[name] ?? null;
24
- }
25
-
26
- /** Resolve the active profile from config, with optional override */
27
- export function resolveProfile(
28
- paths: PlatformPaths,
29
- cwd: string,
30
- config: SupipowersConfig,
31
- override?: string
32
- ): Profile {
33
- const name = override ?? config.defaultProfile;
34
- const profile = loadProfile(paths, cwd, name);
35
- if (!profile) {
36
- // Fallback to thorough if configured profile doesn't exist
37
- return BUILTIN_PROFILES["thorough"];
38
- }
39
- return profile;
40
- }
41
-
42
- /** List all available profiles (built-in + custom) */
43
- export function listProfiles(paths: PlatformPaths, cwd: string): string[] {
44
- const names = new Set(Object.keys(BUILTIN_PROFILES));
45
- const dir = getProfilesDir(paths, cwd);
46
- if (fs.existsSync(dir)) {
47
- for (const file of fs.readdirSync(dir)) {
48
- if (file.endsWith(".json")) {
49
- names.add(file.replace(".json", ""));
50
- }
51
- }
52
- }
53
- return [...names].sort();
54
- }
55
-
56
- /** Save a custom profile */
57
- export function saveProfile(paths: PlatformPaths, cwd: string, profile: Profile): void {
58
- const dir = getProfilesDir(paths, cwd);
59
- fs.mkdirSync(dir, { recursive: true });
60
- fs.writeFileSync(
61
- path.join(dir, `${profile.name}.json`),
62
- JSON.stringify(profile, null, 2) + "\n"
63
- );
64
- }
@@ -1,38 +0,0 @@
1
- // src/context-mode/installer.ts
2
- import { detectContextMode } from "./detector.js";
3
- import { DEPENDENCIES, installDep } from "../deps/registry.js";
4
- import type { ExecResult } from "../platform/types.js";
5
-
6
- type ExecFn = (cmd: string, args: string[]) => Promise<ExecResult>;
7
-
8
- /** Installation status */
9
- export interface ContextModeInstallStatus {
10
- cliInstalled: boolean;
11
- mcpConfigured: boolean;
12
- toolsAvailable: boolean;
13
- version: string | null;
14
- }
15
-
16
- /** Check context-mode installation status */
17
- export async function checkInstallation(
18
- exec: ExecFn,
19
- activeTools: string[],
20
- ): Promise<ContextModeInstallStatus> {
21
- const status = detectContextMode(activeTools);
22
- const dep = DEPENDENCIES.find((d) => d.binary === "context-mode");
23
- const check = dep ? await dep.checkFn(exec) : { installed: false };
24
-
25
- return {
26
- cliInstalled: check.installed,
27
- mcpConfigured: status.available,
28
- toolsAvailable: status.available,
29
- version: check.version ?? null,
30
- };
31
- }
32
-
33
- /** Install context-mode globally */
34
- export async function installContextMode(
35
- exec: ExecFn,
36
- ): Promise<{ success: boolean; error?: string }> {
37
- return installDep(exec, "context-mode");
38
- }
@@ -1,43 +0,0 @@
1
- import type { GateResult } from "../types.js";
2
-
3
- export function buildAiReviewPrompt(
4
- changedFiles: string[],
5
- depth: "quick" | "deep"
6
- ): string {
7
- const depthInstructions =
8
- depth === "quick"
9
- ? "Do a quick scan: check for obvious bugs, security issues, and naming problems."
10
- : [
11
- "Do a thorough review covering:",
12
- "- Correctness and edge cases",
13
- "- Security vulnerabilities (OWASP top 10)",
14
- "- Performance concerns",
15
- "- Code clarity and maintainability",
16
- "- Error handling completeness",
17
- "- Test coverage gaps",
18
- ].join("\n");
19
-
20
- return [
21
- "Review the following changed files:",
22
- ...changedFiles.map((f) => `- ${f}`),
23
- "",
24
- depthInstructions,
25
- "",
26
- "For each issue found, report:",
27
- "- Severity: error | warning | info",
28
- "- File and line number",
29
- "- Description of the issue",
30
- "- Suggested fix",
31
- ].join("\n");
32
- }
33
-
34
- export function createAiReviewResult(
35
- issues: { severity: "error" | "warning" | "info"; message: string; file?: string; line?: number }[]
36
- ): GateResult {
37
- const hasErrors = issues.some((i) => i.severity === "error");
38
- return {
39
- gate: "ai-review",
40
- passed: !hasErrors,
41
- issues,
42
- };
43
- }
@@ -1,67 +0,0 @@
1
- import type { Profile, GateResult, ReviewReport } from "../types.js";
2
- import { buildLspGatePrompt } from "./lsp-gate.js";
3
- import { buildAiReviewPrompt } from "./ai-review-gate.js";
4
- import { buildTestGatePrompt } from "./test-gate.js";
5
-
6
- export interface GateRunnerOptions {
7
- profile: Profile;
8
- changedFiles: string[];
9
- testCommand: string | null;
10
- lspAvailable: boolean;
11
- }
12
-
13
- export function getActiveGates(profile: Profile, lspAvailable: boolean): string[] {
14
- const gates: string[] = [];
15
- if (profile.gates.lspDiagnostics && lspAvailable) gates.push("lsp-diagnostics");
16
- if (profile.gates.aiReview.enabled) gates.push("ai-review");
17
- if (profile.gates.codeQuality) gates.push("code-quality");
18
- if (profile.gates.testSuite) gates.push("test-suite");
19
- if (profile.gates.e2e) gates.push("e2e");
20
- return gates;
21
- }
22
-
23
- export function buildReviewPrompt(options: GateRunnerOptions): string {
24
- const { profile, changedFiles, testCommand, lspAvailable } = options;
25
- const sections: string[] = [
26
- "# Code Review",
27
- "",
28
- `Profile: ${profile.name}`,
29
- "",
30
- "Run the following quality checks and report results for each:",
31
- "",
32
- ];
33
-
34
- if (profile.gates.lspDiagnostics && lspAvailable) {
35
- sections.push("## 1. LSP Diagnostics", buildLspGatePrompt(changedFiles), "");
36
- }
37
-
38
- if (profile.gates.aiReview.enabled) {
39
- sections.push(
40
- "## 2. Code Review",
41
- buildAiReviewPrompt(changedFiles, profile.gates.aiReview.depth),
42
- ""
43
- );
44
- }
45
-
46
- if (profile.gates.testSuite) {
47
- sections.push(
48
- "## 3. Test Suite",
49
- buildTestGatePrompt(testCommand, false),
50
- ""
51
- );
52
- }
53
-
54
- return sections.join("\n");
55
- }
56
-
57
- export function createReviewReport(
58
- profile: string,
59
- gates: GateResult[]
60
- ): ReviewReport {
61
- return {
62
- profile,
63
- timestamp: new Date().toISOString(),
64
- gates,
65
- passed: gates.every((g) => g.passed),
66
- };
67
- }
@@ -1,24 +0,0 @@
1
- import type { GateResult } from "../types.js";
2
-
3
- export function buildLspGatePrompt(changedFiles: string[]): string {
4
- return [
5
- "Run LSP diagnostics on these files and report results:",
6
- ...changedFiles.map((f) => `- ${f}`),
7
- "",
8
- 'Use the lsp tool with action "diagnostics" for each file.',
9
- "Summarize: total errors, total warnings, and list each issue.",
10
- ].join("\n");
11
- }
12
-
13
- export function createLspGateResult(
14
- hasErrors: boolean,
15
- errorCount: number,
16
- warningCount: number,
17
- issues: { severity: "error" | "warning" | "info"; message: string; file?: string; line?: number }[]
18
- ): GateResult {
19
- return {
20
- gate: "lsp-diagnostics",
21
- passed: !hasErrors,
22
- issues,
23
- };
24
- }
@@ -1,39 +0,0 @@
1
- import type { GateResult } from "../types.js";
2
-
3
- export function buildTestGatePrompt(
4
- testCommand: string | null,
5
- changedOnly: boolean,
6
- changedFiles?: string[]
7
- ): string {
8
- const cmd = testCommand ?? "npm test";
9
- const scope = changedOnly && changedFiles
10
- ? `Only run tests related to: ${changedFiles.join(", ")}`
11
- : "Run the full test suite.";
12
-
13
- return [
14
- scope,
15
- "",
16
- `Command: ${cmd}`,
17
- "",
18
- "Report the results:",
19
- "- Total tests, passed, failed, skipped",
20
- "- For each failure: test name, file, error message",
21
- ].join("\n");
22
- }
23
-
24
- export function createTestGateResult(
25
- passed: boolean,
26
- totalTests: number,
27
- failedTests: number,
28
- failures: { message: string; file?: string }[]
29
- ): GateResult {
30
- return {
31
- gate: "test-suite",
32
- passed,
33
- issues: failures.map((f) => ({
34
- severity: "error" as const,
35
- message: `Test failed: ${f.message}`,
36
- file: f.file,
37
- })),
38
- };
39
- }
@@ -1,98 +0,0 @@
1
- #!/bin/bash
2
- # Start the visual companion server and output connection info
3
- # Usage: start-server.sh [--host <bind-host>] [--url-host <display-host>] [--foreground]
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
-
7
- # Parse arguments
8
- FOREGROUND="false"
9
- BIND_HOST="127.0.0.1"
10
- URL_HOST=""
11
- while [[ $# -gt 0 ]]; do
12
- case "$1" in
13
- --host)
14
- BIND_HOST="$2"
15
- shift 2
16
- ;;
17
- --url-host)
18
- URL_HOST="$2"
19
- shift 2
20
- ;;
21
- --foreground|--no-daemon)
22
- FOREGROUND="true"
23
- shift
24
- ;;
25
- *)
26
- echo "{\"error\": \"Unknown argument: $1\"}"
27
- exit 1
28
- ;;
29
- esac
30
- done
31
-
32
- if [[ -z "$URL_HOST" ]]; then
33
- if [[ "$BIND_HOST" == "127.0.0.1" || "$BIND_HOST" == "localhost" ]]; then
34
- URL_HOST="localhost"
35
- else
36
- URL_HOST="$BIND_HOST"
37
- fi
38
- fi
39
-
40
- # Session dir must be set via environment
41
- SCREEN_DIR="${SUPI_VISUAL_DIR}"
42
- if [[ -z "$SCREEN_DIR" ]]; then
43
- echo '{"error": "SUPI_VISUAL_DIR environment variable not set"}'
44
- exit 1
45
- fi
46
-
47
- PID_FILE="${SCREEN_DIR}/.server.pid"
48
- LOG_FILE="${SCREEN_DIR}/.server.log"
49
-
50
- # Create session directory if needed
51
- mkdir -p "$SCREEN_DIR"
52
-
53
- # Kill any existing server for this session
54
- if [[ -f "$PID_FILE" ]]; then
55
- old_pid=$(cat "$PID_FILE")
56
- kill "$old_pid" 2>/dev/null
57
- rm -f "$PID_FILE"
58
- fi
59
-
60
- cd "$SCRIPT_DIR"
61
-
62
- # Foreground mode
63
- if [[ "$FOREGROUND" == "true" ]]; then
64
- echo "$$" > "$PID_FILE"
65
- env SUPI_VISUAL_DIR="$SCREEN_DIR" SUPI_VISUAL_HOST="$BIND_HOST" SUPI_VISUAL_URL_HOST="$URL_HOST" node index.js
66
- exit $?
67
- fi
68
-
69
- # Background mode
70
- nohup env SUPI_VISUAL_DIR="$SCREEN_DIR" SUPI_VISUAL_HOST="$BIND_HOST" SUPI_VISUAL_URL_HOST="$URL_HOST" node index.js > "$LOG_FILE" 2>&1 &
71
- SERVER_PID=$!
72
- disown "$SERVER_PID" 2>/dev/null
73
- echo "$SERVER_PID" > "$PID_FILE"
74
-
75
- # Wait for server-started message
76
- for i in {1..50}; do
77
- if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
78
- # Verify server is still alive
79
- alive="true"
80
- for _ in {1..20}; do
81
- if ! kill -0 "$SERVER_PID" 2>/dev/null; then
82
- alive="false"
83
- break
84
- fi
85
- sleep 0.1
86
- done
87
- if [[ "$alive" != "true" ]]; then
88
- echo "{\"error\": \"Server started but was killed. Retry with --foreground\"}"
89
- exit 1
90
- fi
91
- grep "server-started" "$LOG_FILE" | head -1
92
- exit 0
93
- fi
94
- sleep 0.1
95
- done
96
-
97
- echo '{"error": "Server failed to start within 5 seconds"}'
98
- exit 1
@@ -1,21 +0,0 @@
1
- #!/bin/bash
2
- # Stop the visual companion server
3
- # Usage: stop-server.sh <screen_dir>
4
-
5
- SCREEN_DIR="$1"
6
-
7
- if [[ -z "$SCREEN_DIR" ]]; then
8
- echo '{"error": "Usage: stop-server.sh <screen_dir>"}'
9
- exit 1
10
- fi
11
-
12
- PID_FILE="${SCREEN_DIR}/.server.pid"
13
-
14
- if [[ -f "$PID_FILE" ]]; then
15
- pid=$(cat "$PID_FILE")
16
- kill "$pid" 2>/dev/null
17
- rm -f "$PID_FILE" "${SCREEN_DIR}/.server.log"
18
- echo '{"status": "stopped"}'
19
- else
20
- echo '{"status": "not_running"}'
21
- fi