@solaqua/gji 0.4.1 → 0.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.
package/dist/go.js CHANGED
@@ -4,6 +4,7 @@ import { loadEffectiveConfig } from './config.js';
4
4
  import { readWorktreeHealth } from './git.js';
5
5
  import { isHeadless } from './headless.js';
6
6
  import { extractHooks, runHook } from './hooks.js';
7
+ import { appendHistory } from './history.js';
7
8
  import { detectRepository, listWorktrees, sortByCurrentFirst } from './repo.js';
8
9
  import { writeShellOutput } from './shell-handoff.js';
9
10
  const GO_OUTPUT_FILE_ENV = 'GJI_GO_OUTPUT_FILE';
@@ -36,6 +37,7 @@ export function createGoCommand(dependencies = {}) {
36
37
  const config = await loadEffectiveConfig(repository.repoRoot, undefined, options.stderr);
37
38
  const hooks = extractHooks(config);
38
39
  await runHook(hooks.afterEnter, resolvedPath, { branch: chosenWorktree?.branch ?? undefined, path: resolvedPath, repo: basename(repository.repoRoot) }, options.stderr);
40
+ appendHistory(resolvedPath, chosenWorktree?.branch ?? null).catch(() => undefined);
39
41
  await writeShellOutput(GO_OUTPUT_FILE_ENV, resolvedPath, options.stdout);
40
42
  return 0;
41
43
  };
@@ -0,0 +1,7 @@
1
+ export interface HistoryCommandOptions {
2
+ cwd: string;
3
+ home?: string;
4
+ json?: boolean;
5
+ stdout: (chunk: string) => void;
6
+ }
7
+ export declare function runHistoryCommand(options: HistoryCommandOptions): Promise<number>;
@@ -0,0 +1,15 @@
1
+ import { loadHistory } from './history.js';
2
+ import { formatHistoryList } from './back.js';
3
+ export async function runHistoryCommand(options) {
4
+ const history = await loadHistory(options.home);
5
+ if (options.json) {
6
+ options.stdout(`${JSON.stringify(history, null, 2)}\n`);
7
+ return 0;
8
+ }
9
+ if (history.length === 0) {
10
+ options.stdout('No navigation history.\n');
11
+ return 0;
12
+ }
13
+ options.stdout(formatHistoryList(history, options.cwd));
14
+ return 0;
15
+ }
@@ -0,0 +1,9 @@
1
+ export declare const HISTORY_FILE_NAME = "history.json";
2
+ export interface HistoryEntry {
3
+ branch: string | null;
4
+ path: string;
5
+ timestamp: number;
6
+ }
7
+ export declare function HISTORY_FILE_PATH(home?: string): string;
8
+ export declare function loadHistory(home?: string): Promise<HistoryEntry[]>;
9
+ export declare function appendHistory(path: string, branch: string | null, home?: string): Promise<void>;
@@ -0,0 +1,46 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { GLOBAL_CONFIG_DIRECTORY } from './config.js';
5
+ export const HISTORY_FILE_NAME = 'history.json';
6
+ const MAX_HISTORY_ENTRIES = 50;
7
+ export function HISTORY_FILE_PATH(home = homedir()) {
8
+ const configDir = process.env.GJI_CONFIG_DIR;
9
+ if (configDir) {
10
+ return join(resolve(configDir), HISTORY_FILE_NAME);
11
+ }
12
+ return join(home, GLOBAL_CONFIG_DIRECTORY, HISTORY_FILE_NAME);
13
+ }
14
+ export async function loadHistory(home = homedir()) {
15
+ const path = HISTORY_FILE_PATH(home);
16
+ try {
17
+ const raw = await readFile(path, 'utf8');
18
+ const parsed = JSON.parse(raw);
19
+ if (!Array.isArray(parsed))
20
+ return [];
21
+ return parsed.filter(isHistoryEntry);
22
+ }
23
+ catch {
24
+ return [];
25
+ }
26
+ }
27
+ export async function appendHistory(path, branch, home = homedir()) {
28
+ const historyPath = HISTORY_FILE_PATH(home);
29
+ const existing = await loadHistory(home);
30
+ // Skip if the most recent entry is the same path (no-op navigation)
31
+ if (existing.length > 0 && existing[0].path === path) {
32
+ return;
33
+ }
34
+ const entry = { branch, path, timestamp: Date.now() };
35
+ const next = [entry, ...existing].slice(0, MAX_HISTORY_ENTRIES);
36
+ await mkdir(dirname(historyPath), { recursive: true });
37
+ await writeFile(historyPath, `${JSON.stringify(next, null, 2)}\n`, 'utf8');
38
+ }
39
+ function isHistoryEntry(value) {
40
+ return (typeof value === 'object' &&
41
+ value !== null &&
42
+ 'path' in value &&
43
+ typeof value.path === 'string' &&
44
+ 'timestamp' in value &&
45
+ typeof value.timestamp === 'number');
46
+ }
package/dist/init.js CHANGED
@@ -21,6 +21,13 @@ const SHELL_WRAPPED_COMMANDS = [
21
21
  names: ['pr'],
22
22
  tempPrefix: 'gji-pr',
23
23
  },
24
+ {
25
+ bypassOption: '--print',
26
+ commandName: 'back',
27
+ envVar: 'GJI_BACK_OUTPUT_FILE',
28
+ names: ['back'],
29
+ tempPrefix: 'gji-back',
30
+ },
24
31
  {
25
32
  bypassOption: '--print',
26
33
  commandName: 'go',
package/dist/new.js CHANGED
@@ -6,6 +6,7 @@ import { isCancel, text } from '@clack/prompts';
6
6
  import { loadEffectiveConfig, resolveConfigString } from './config.js';
7
7
  import { syncFiles } from './file-sync.js';
8
8
  import { extractHooks, runHook } from './hooks.js';
9
+ import { appendHistory } from './history.js';
9
10
  import { isHeadless } from './headless.js';
10
11
  import { maybeRunInstallPrompt } from './install-prompt.js';
11
12
  import { pathExists, promptForPathConflict } from './conflict.js';
@@ -105,6 +106,7 @@ export function createNewCommand(dependencies = {}) {
105
106
  else {
106
107
  const choice = await prompt(worktreePath);
107
108
  if (choice === 'reuse') {
109
+ appendHistory(worktreePath, worktreeName).catch(() => undefined);
108
110
  await writeOutput(worktreePath, options.stdout);
109
111
  return 0;
110
112
  }
@@ -147,6 +149,7 @@ export function createNewCommand(dependencies = {}) {
147
149
  options.stdout(`${JSON.stringify({ branch: worktreeName, path: worktreePath }, null, 2)}\n`);
148
150
  }
149
151
  else {
152
+ await appendHistory(worktreePath, worktreeName);
150
153
  await writeOutput(worktreePath, options.stdout);
151
154
  }
152
155
  return 0;
package/dist/pr.js CHANGED
@@ -6,6 +6,7 @@ import { loadEffectiveConfig, resolveConfigString } from './config.js';
6
6
  import { syncFiles } from './file-sync.js';
7
7
  import { pathExists, promptForPathConflict } from './conflict.js';
8
8
  import { extractHooks, runHook } from './hooks.js';
9
+ import { appendHistory } from './history.js';
9
10
  import { isHeadless } from './headless.js';
10
11
  import { maybeRunInstallPrompt } from './install-prompt.js';
11
12
  import { detectRepository, resolveWorktreePath } from './repo.js';
@@ -58,6 +59,7 @@ export function createPrCommand(dependencies = {}) {
58
59
  }
59
60
  const choice = await prompt(worktreePath);
60
61
  if (choice === 'reuse') {
62
+ appendHistory(worktreePath, branchName).catch(() => undefined);
61
63
  await writeOutput(worktreePath, options.stdout);
62
64
  return 0;
63
65
  }
@@ -112,6 +114,7 @@ export function createPrCommand(dependencies = {}) {
112
114
  options.stdout(`${JSON.stringify({ branch: branchName, path: worktreePath }, null, 2)}\n`);
113
115
  }
114
116
  else {
117
+ await appendHistory(worktreePath, branchName);
115
118
  await writeOutput(worktreePath, options.stdout);
116
119
  }
117
120
  return 0;
@@ -0,0 +1,13 @@
1
+ .TH GJI\-BACK 1 "May 2026" "gji 0.5.0" "User Commands"
2
+ .SH NAME
3
+ gji\-back \- navigate to the previously visited worktree, optionally N steps back
4
+ .SH SYNOPSIS
5
+ .B gji back [\fIoptions\fR] [options] [n]
6
+ .SH DESCRIPTION
7
+ navigate to the previously visited worktree, optionally N steps back
8
+ .SH OPTIONS
9
+ .TP
10
+ .B \-\-print
11
+ print the resolved worktree path explicitly
12
+ .SH "SEE ALSO"
13
+ .BR gji (1)
@@ -1,4 +1,4 @@
1
- .TH GJI\-CLEAN 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-CLEAN 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-clean \- interactively prune linked worktrees
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-COMPLETION 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-COMPLETION 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-completion \- print shell completion definitions
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-CONFIG 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-CONFIG 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-config \- manage global config defaults
4
4
  .SH SYNOPSIS
package/man/man1/gji-go.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI\-GO 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-GO 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-go \- print or select a worktree path
4
4
  .SH SYNOPSIS
@@ -0,0 +1,13 @@
1
+ .TH GJI\-HISTORY 1 "May 2026" "gji 0.5.0" "User Commands"
2
+ .SH NAME
3
+ gji\-history \- show navigation history
4
+ .SH SYNOPSIS
5
+ .B gji history [\fIoptions\fR] [options]
6
+ .SH DESCRIPTION
7
+ show navigation history
8
+ .SH OPTIONS
9
+ .TP
10
+ .B \-\-json
11
+ print history as JSON
12
+ .SH "SEE ALSO"
13
+ .BR gji (1)
@@ -1,4 +1,4 @@
1
- .TH GJI\-INIT 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-INIT 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-init \- print or install shell integration
4
4
  .SH SYNOPSIS
package/man/man1/gji-ls.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI\-LS 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-LS 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-ls \- list active worktrees
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-NEW 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-NEW 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-new \- create a new branch or detached linked worktree
4
4
  .SH SYNOPSIS
package/man/man1/gji-pr.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI\-PR 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-PR 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-pr \- fetch a pull request by number, #number, or URL into a linked worktree
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-REMOVE 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-REMOVE 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-remove \- remove a linked worktree and delete its branch when present
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-ROOT 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-ROOT 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-root \- print the main repository root path
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-STATUS 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-STATUS 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-status \- summarize repository and worktree health
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-SYNC 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-SYNC 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-sync \- fetch and update one or all worktrees
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-TRIGGER\-HOOK 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI\-TRIGGER\-HOOK 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-trigger\-hook \- run a named hook (afterCreate, afterEnter, beforeRemove) in the current worktree
4
4
  .SH SYNOPSIS
package/man/man1/gji.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI 1 "May 2026" "gji 0.4.1" "User Commands"
1
+ .TH GJI 1 "May 2026" "gji 0.5.0" "User Commands"
2
2
  .SH NAME
3
3
  gji \- Context switching without the mess.
4
4
  .SH SYNOPSIS
@@ -23,6 +23,12 @@ print shell completion definitions
23
23
  .B pr [options] <ref>
24
24
  fetch a pull request by number, #number, or URL into a linked worktree
25
25
  .TP
26
+ .B back [options] [n]
27
+ navigate to the previously visited worktree, optionally N steps back
28
+ .TP
29
+ .B history [options]
30
+ show navigation history
31
+ .TP
26
32
  .B go [options] [branch]
27
33
  print or select a worktree path
28
34
  .TP
@@ -60,6 +66,8 @@ output the version number
60
66
  .BR gji\-init (1),
61
67
  .BR gji\-completion (1),
62
68
  .BR gji\-pr (1),
69
+ .BR gji\-back (1),
70
+ .BR gji\-history (1),
63
71
  .BR gji\-go (1),
64
72
  .BR gji\-root (1),
65
73
  .BR gji\-status (1),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solaqua/gji",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Git worktree CLI for fast context switching.",
5
5
  "license": "MIT",
6
6
  "author": "sjquant",