epiq 0.0.3 → 0.0.5
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/app.js +4 -5
- package/dist/board/components/Board.js +2 -2
- package/dist/board/components/CommandLine.js +11 -4
- package/dist/board/components/ContextBar.js +12 -3
- package/dist/board/components/Help.js +4 -4
- package/dist/board/components/Swimlane.d.ts +1 -0
- package/dist/board/components/Swimlane.js +3 -4
- package/dist/board/components/Swimlanes.js +8 -11
- package/dist/board/components/TicketListItem.js +3 -3
- package/dist/board/components/TicketUI.js +1 -1
- package/dist/board/hints/hints.d.ts +4 -4
- package/dist/board/hints/hints.js +7 -15
- package/dist/board/mock/board.js +500 -523
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +20 -61
- package/dist/debug-logger.d.ts +1 -1
- package/dist/debug-logger.js +5 -4
- package/dist/init-project.d.ts +1 -0
- package/dist/init-project.js +1 -0
- package/dist/navigation/actions/add-item/add-item-actions.js +21 -11
- package/dist/navigation/actions/board-action-map.js +3 -3
- package/dist/navigation/actions/default/default-action-utils.d.ts +8 -7
- package/dist/navigation/actions/default/default-action-utils.js +67 -17
- package/dist/navigation/actions/default/default-actions.js +30 -44
- package/dist/navigation/actions/default/navigation-action-utils.d.ts +16 -0
- package/dist/navigation/actions/default/navigation-action-utils.js +109 -0
- package/dist/navigation/actions/input/command-line-input.js +4 -4
- package/dist/navigation/actions/input/input-actions.js +13 -12
- package/dist/navigation/actions/move/move-actions-utils.d.ts +4 -5
- package/dist/navigation/actions/move/move-actions-utils.js +44 -31
- package/dist/navigation/actions/move/move-actions.js +17 -11
- package/dist/navigation/command-line/command-line-sequence-actions.d.ts +1 -1
- package/dist/navigation/command-line/command-line-sequence-actions.js +24 -11
- package/dist/navigation/command-line/command-line-sequence-intent.d.ts +3 -2
- package/dist/navigation/command-line/command-line-sequence-intent.js +13 -10
- package/dist/navigation/command-line/commands.d.ts +2 -0
- package/dist/navigation/command-line/commands.js +58 -0
- package/dist/navigation/keypress-listener.d.ts +1 -0
- package/dist/navigation/keypress-listener.js +33 -0
- package/dist/navigation/model/action-map.model.d.ts +4 -4
- package/dist/navigation/model/navigation-ctx.model.d.ts +1 -16
- package/dist/navigation/model/navigation-ctx.model.js +0 -1
- package/dist/navigation/model/navigation-tree.model.d.ts +2 -1
- package/dist/navigation/navigation.d.ts +3 -10
- package/dist/navigation/navigation.js +73 -79
- package/dist/navigation/state/command-line.state.d.ts +5 -0
- package/dist/navigation/state/command-line.state.js +21 -4
- package/dist/navigation/state/state.d.ts +17 -9
- package/dist/navigation/state/state.js +34 -35
- package/dist/navigation/utils/get-command-line-intent.d.ts +2 -2
- package/dist/navigation/utils/get-command-line-intent.js +7 -7
- package/dist/navigation/utils/key-intent.d.ts +5 -5
- package/dist/navigation/utils/key-intent.js +68 -74
- package/package.json +2 -2
- package/readme.md +54 -17
|
@@ -1,11 +1,23 @@
|
|
|
1
|
+
export const commandDelimiter = ' ';
|
|
1
2
|
export let commandLineState = {
|
|
2
3
|
commandHistory: [],
|
|
3
4
|
value: '',
|
|
4
5
|
commandHistoryIndex: -1,
|
|
5
6
|
};
|
|
7
|
+
const listeners = new Set();
|
|
8
|
+
const notify = () => {
|
|
9
|
+
for (const l of listeners)
|
|
10
|
+
l();
|
|
11
|
+
};
|
|
12
|
+
export const subscribeCommandLineState = (listener) => {
|
|
13
|
+
listeners.add(listener);
|
|
14
|
+
return () => listeners.delete(listener);
|
|
15
|
+
};
|
|
16
|
+
// ===== Updates =====
|
|
6
17
|
export const updateCommandLineState = (cb) => {
|
|
7
|
-
|
|
8
|
-
commandLineState =
|
|
18
|
+
const next = cb(structuredClone(commandLineState));
|
|
19
|
+
commandLineState = next;
|
|
20
|
+
notify();
|
|
9
21
|
};
|
|
10
22
|
export const updateCommandLineInput = (cb) => {
|
|
11
23
|
updateCommandLineState(state => ({
|
|
@@ -31,13 +43,18 @@ export const getPrevCommand = () => {
|
|
|
31
43
|
};
|
|
32
44
|
export const getNextCommand = () => {
|
|
33
45
|
updateCommandLineState(s => {
|
|
34
|
-
|
|
46
|
+
// typically you want to clamp down to -1 (meaning "no selection")
|
|
47
|
+
const nextIndex = Math.max(s.commandHistoryIndex - 1, -1);
|
|
35
48
|
return {
|
|
36
49
|
...s,
|
|
37
50
|
commandHistoryIndex: nextIndex,
|
|
38
|
-
value: s.commandHistory[nextIndex] ?? '',
|
|
51
|
+
value: nextIndex === -1 ? '' : s.commandHistory[nextIndex] ?? '',
|
|
39
52
|
};
|
|
40
53
|
});
|
|
41
54
|
};
|
|
42
55
|
export const clearCommandLine = () => updateCommandLineInput(() => '');
|
|
43
56
|
export const getCommandLineInput = () => commandLineState.value;
|
|
57
|
+
export const getCommandLineArgumentValue = () => {
|
|
58
|
+
const [_, ...rest] = commandLineState.value.split(commandDelimiter);
|
|
59
|
+
return rest.join(commandDelimiter);
|
|
60
|
+
};
|
|
@@ -1,12 +1,20 @@
|
|
|
1
|
+
import { Board } from '../../board/model/board.model.js';
|
|
1
2
|
import { ActionEntry, ModeUnion } from '../model/action-map.model.js';
|
|
2
3
|
import { NavigationTree } from '../model/navigation-tree.model.js';
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
export type AppState = {
|
|
5
|
+
readonly selectedIndex: number;
|
|
6
|
+
readonly mode: ModeUnion;
|
|
7
|
+
readonly availableActions: ActionEntry[];
|
|
8
|
+
readonly availableHints: readonly string[];
|
|
9
|
+
readonly currentNode: NavigationTree<NavigationTree>;
|
|
10
|
+
readonly breadCrumb: NavigationTree<NavigationTree>[];
|
|
11
|
+
readonly rootNode: Board;
|
|
9
12
|
};
|
|
10
|
-
export declare
|
|
11
|
-
export declare const
|
|
12
|
-
export declare const
|
|
13
|
+
export declare let appState: AppState;
|
|
14
|
+
export declare const initAppState: (board: Board) => void;
|
|
15
|
+
export declare const updateState: (cb: (oldState: typeof appState) => typeof appState, opts?: {
|
|
16
|
+
render?: boolean;
|
|
17
|
+
}) => void;
|
|
18
|
+
export declare const patchState: (patch: Partial<typeof appState>, opts?: {
|
|
19
|
+
render?: boolean;
|
|
20
|
+
}) => void;
|
|
@@ -1,39 +1,38 @@
|
|
|
1
1
|
import { Hints } from '../../board/hints/hints.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
import { renderBoard } from '../../cli.js';
|
|
3
|
+
import { ContextualActionMap } from '../actions/board-action-map.js';
|
|
4
|
+
import { DefaultActions } from '../actions/default/default-actions.js';
|
|
5
|
+
import { inputActions } from '../actions/input/input-actions.js';
|
|
6
|
+
export let appState;
|
|
7
|
+
const derived = (state) => {
|
|
8
|
+
const { currentNode, mode } = state;
|
|
9
|
+
const availableHints = Hints[currentNode.actionContext + mode] ?? Hints[currentNode.actionContext];
|
|
10
|
+
const actionContext = currentNode?.actionContext;
|
|
11
|
+
const availableActions = [
|
|
12
|
+
...DefaultActions,
|
|
13
|
+
...ContextualActionMap[actionContext],
|
|
14
|
+
...inputActions,
|
|
15
|
+
];
|
|
16
|
+
return {
|
|
17
|
+
...state,
|
|
18
|
+
availableHints,
|
|
19
|
+
availableActions,
|
|
20
|
+
};
|
|
9
21
|
};
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export const flashHint = async (hints) => {
|
|
19
|
-
await new Promise(resolve => {
|
|
20
|
-
setTimeout(() => {
|
|
21
|
-
updateState(state => {
|
|
22
|
-
const { currentNode, mode } = state;
|
|
23
|
-
const contextualHints = (currentNode &&
|
|
24
|
-
Hints[currentNode.actionContext + mode]) ??
|
|
25
|
-
(currentNode && Hints[currentNode.actionContext]) ?? [''];
|
|
26
|
-
return {
|
|
27
|
-
...state,
|
|
28
|
-
availableHints: contextualHints,
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
triggerRender();
|
|
32
|
-
resolve();
|
|
33
|
-
}, 3000);
|
|
34
|
-
patchState({
|
|
35
|
-
availableHints: hints,
|
|
36
|
-
});
|
|
37
|
-
triggerRender();
|
|
22
|
+
export const initAppState = (board) => {
|
|
23
|
+
appState = derived({
|
|
24
|
+
...appState,
|
|
25
|
+
rootNode: board,
|
|
26
|
+
breadCrumb: [board],
|
|
27
|
+
selectedIndex: 0,
|
|
28
|
+
mode: 'default',
|
|
29
|
+
currentNode: board,
|
|
38
30
|
});
|
|
31
|
+
renderBoard();
|
|
32
|
+
};
|
|
33
|
+
export const updateState = (cb, opts = { render: true }) => {
|
|
34
|
+
appState = derived(cb(appState));
|
|
35
|
+
if (opts.render)
|
|
36
|
+
renderBoard();
|
|
39
37
|
};
|
|
38
|
+
export const patchState = (patch, opts = { render: true }) => updateState(old => ({ ...old, ...patch }), opts);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Intent } from './key-intent.js';
|
|
2
2
|
import readline from 'readline';
|
|
3
|
-
export declare const getCommandLineIntent: (key: readline.Key) =>
|
|
3
|
+
export declare const getCommandLineIntent: (key: readline.Key) => Intent.Confirm | Intent.ToggleCommandLine | Intent.CaptureInput | Intent.EraseInput | Intent.GetLastCommandFromHistory | Intent.GetNextCommandFromHistory;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Intent } from './key-intent.js';
|
|
2
2
|
export const getCommandLineIntent = (key) => {
|
|
3
3
|
switch (key.name) {
|
|
4
4
|
case 'up':
|
|
5
|
-
return
|
|
5
|
+
return Intent.GetLastCommandFromHistory;
|
|
6
6
|
case 'down':
|
|
7
|
-
return
|
|
7
|
+
return Intent.GetNextCommandFromHistory;
|
|
8
8
|
case 'return':
|
|
9
|
-
return
|
|
9
|
+
return Intent.Confirm;
|
|
10
10
|
case 'backspace':
|
|
11
|
-
return
|
|
11
|
+
return Intent.EraseInput;
|
|
12
12
|
case 'escape':
|
|
13
|
-
return
|
|
13
|
+
return Intent.ToggleCommandLine;
|
|
14
14
|
default:
|
|
15
|
-
return
|
|
15
|
+
return Intent.CaptureInput;
|
|
16
16
|
}
|
|
17
17
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import readline from 'readline';
|
|
2
2
|
import { ModeUnion } from '../model/action-map.model.js';
|
|
3
|
-
|
|
4
|
-
export declare enum KeyIntent {
|
|
3
|
+
export declare enum Intent {
|
|
5
4
|
NavPreviousItem = "navPreviousItem",
|
|
6
5
|
NavNextItem = "navNextItem",
|
|
7
6
|
NavToPreviousContainer = "navToPreviousContainer",
|
|
@@ -13,8 +12,9 @@ export declare enum KeyIntent {
|
|
|
13
12
|
Confirm = "confirm",
|
|
14
13
|
Edit = "edit",
|
|
15
14
|
Exit = "exit",
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
ViewHelp = "viewHelp",
|
|
16
|
+
HideHelp = "hideHelp",
|
|
17
|
+
InitMove = "initMove",
|
|
18
18
|
ToggleCommandLine = "toggleCommandLine",
|
|
19
19
|
CaptureInput = "captureInput",
|
|
20
20
|
EraseInput = "eraseInput",
|
|
@@ -22,4 +22,4 @@ export declare enum KeyIntent {
|
|
|
22
22
|
GetLastCommandFromHistory = "getLastCommandFromHistory",
|
|
23
23
|
GetNextCommandFromHistory = "getNextCommandFromHistory"
|
|
24
24
|
}
|
|
25
|
-
export declare function getKeyIntent(key: readline.Key,
|
|
25
|
+
export declare function getKeyIntent(key: readline.Key, mode: ModeUnion): Intent | null;
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
import { Mode } from '../model/action-map.model.js';
|
|
2
|
+
import { appState } from '../state/state.js';
|
|
2
3
|
import { getCommandLineIntent } from './get-command-line-intent.js';
|
|
3
|
-
export var
|
|
4
|
-
(function (
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
4
|
+
export var Intent;
|
|
5
|
+
(function (Intent) {
|
|
6
|
+
Intent["NavPreviousItem"] = "navPreviousItem";
|
|
7
|
+
Intent["NavNextItem"] = "navNextItem";
|
|
8
|
+
Intent["NavToPreviousContainer"] = "navToPreviousContainer";
|
|
9
|
+
Intent["NavToNextContainer"] = "navToNextContainer";
|
|
10
|
+
Intent["MovePreviousItem"] = "movePreviousItem";
|
|
11
|
+
Intent["MoveNextItem"] = "moveNextItem";
|
|
12
|
+
Intent["MoveToPreviousContainer"] = "moveToPreviousContainer";
|
|
13
|
+
Intent["MoveToNextContainer"] = "moveToNextContainer";
|
|
14
|
+
Intent["Confirm"] = "confirm";
|
|
15
|
+
Intent["Edit"] = "edit";
|
|
16
|
+
Intent["Exit"] = "exit";
|
|
17
|
+
Intent["ViewHelp"] = "viewHelp";
|
|
18
|
+
Intent["HideHelp"] = "hideHelp";
|
|
19
|
+
Intent["InitMove"] = "initMove";
|
|
20
|
+
// Command line
|
|
21
|
+
Intent["ToggleCommandLine"] = "toggleCommandLine";
|
|
22
|
+
Intent["CaptureInput"] = "captureInput";
|
|
23
|
+
Intent["EraseInput"] = "eraseInput";
|
|
24
|
+
Intent["AddItem"] = "addItem";
|
|
25
|
+
Intent["GetLastCommandFromHistory"] = "getLastCommandFromHistory";
|
|
26
|
+
Intent["GetNextCommandFromHistory"] = "getNextCommandFromHistory";
|
|
27
|
+
})(Intent || (Intent = {}));
|
|
25
28
|
function getDir(key) {
|
|
26
29
|
switch (key.name) {
|
|
27
30
|
// arrows
|
|
@@ -43,12 +46,21 @@ function getDir(key) {
|
|
|
43
46
|
return null;
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
|
-
function mapDirectionalIntent(
|
|
49
|
+
function mapDirectionalIntent(axis, dir, intents) {
|
|
50
|
+
const enableAcrossContainers = appState.currentNode.enableChildNavigationAcrossContainers;
|
|
47
51
|
switch (dir) {
|
|
48
52
|
case 'up':
|
|
49
|
-
return axis === 'vertical'
|
|
53
|
+
return axis === 'vertical'
|
|
54
|
+
? intents.prevItem
|
|
55
|
+
: enableAcrossContainers
|
|
56
|
+
? intents.prevContainer
|
|
57
|
+
: null;
|
|
50
58
|
case 'down':
|
|
51
|
-
return axis === 'vertical'
|
|
59
|
+
return axis === 'vertical'
|
|
60
|
+
? intents.nextItem
|
|
61
|
+
: enableAcrossContainers
|
|
62
|
+
? intents.nextContainer
|
|
63
|
+
: null;
|
|
52
64
|
case 'left':
|
|
53
65
|
return axis === 'horizontal'
|
|
54
66
|
? intents.prevItem
|
|
@@ -63,62 +75,44 @@ function mapDirectionalIntent(dir, axis, enableAcrossContainers, intents) {
|
|
|
63
75
|
: null;
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
|
-
export function getKeyIntent(key,
|
|
67
|
-
if (key.sequence === ':')
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
if (mode === Mode.COMMAND_LINE) {
|
|
78
|
+
export function getKeyIntent(key, mode) {
|
|
79
|
+
if (key.sequence === ':')
|
|
80
|
+
return Intent.ToggleCommandLine;
|
|
81
|
+
if (mode === Mode.COMMAND_LINE)
|
|
71
82
|
return getCommandLineIntent(key);
|
|
72
|
-
|
|
73
|
-
const axis = ctx.navigationNode.childrenRenderAxis;
|
|
74
|
-
const enableAcrossContainers = ctx.navigationNode.enableChildNavigationAcrossContainers;
|
|
75
|
-
if (key.name === 'escape' && mode === Mode.HELP) {
|
|
76
|
-
return KeyIntent.ToggleHelp;
|
|
77
|
-
}
|
|
78
|
-
// Hard exits
|
|
79
|
-
if (key.ctrl && key.name === 'c')
|
|
80
|
-
return KeyIntent.Exit;
|
|
81
|
-
if (key.name === 'escape')
|
|
82
|
-
return KeyIntent.Exit;
|
|
83
|
-
// Move mode
|
|
84
|
-
if (mode === Mode.MOVE) {
|
|
85
|
-
if (key.name === 'y')
|
|
86
|
-
return KeyIntent.ToggleMove;
|
|
87
|
-
const dir = getDir(key);
|
|
88
|
-
if (!dir)
|
|
89
|
-
return null;
|
|
90
|
-
return mapDirectionalIntent(dir, axis, Boolean(enableAcrossContainers), {
|
|
91
|
-
prevItem: KeyIntent.MovePreviousItem,
|
|
92
|
-
nextItem: KeyIntent.MoveNextItem,
|
|
93
|
-
prevContainer: KeyIntent.MoveToPreviousContainer,
|
|
94
|
-
nextContainer: KeyIntent.MoveToNextContainer,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
// Normal mode
|
|
98
|
-
if (key.name === 'y')
|
|
99
|
-
return KeyIntent.ToggleMove;
|
|
100
|
-
// Edit (vim-ish)
|
|
101
|
-
if (key.name === 'i')
|
|
102
|
-
return KeyIntent.Edit;
|
|
103
|
-
// Navigation (arrows + vim hjkl)
|
|
83
|
+
// Navigation keys
|
|
104
84
|
const dir = getDir(key);
|
|
105
85
|
if (dir) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
86
|
+
let dirMap = mode === Mode.MOVE
|
|
87
|
+
? {
|
|
88
|
+
prevItem: Intent.MovePreviousItem,
|
|
89
|
+
nextItem: Intent.MoveNextItem,
|
|
90
|
+
prevContainer: Intent.MoveToPreviousContainer,
|
|
91
|
+
nextContainer: Intent.MoveToNextContainer,
|
|
92
|
+
}
|
|
93
|
+
: {
|
|
94
|
+
prevItem: Intent.NavPreviousItem,
|
|
95
|
+
nextItem: Intent.NavNextItem,
|
|
96
|
+
prevContainer: Intent.NavToPreviousContainer,
|
|
97
|
+
nextContainer: Intent.NavToNextContainer,
|
|
98
|
+
};
|
|
99
|
+
return mapDirectionalIntent(appState.currentNode.childrenRenderAxis, dir, dirMap);
|
|
112
100
|
}
|
|
113
|
-
//
|
|
101
|
+
// Hard exit
|
|
102
|
+
if (key.ctrl && key.name === 'c')
|
|
103
|
+
return Intent.Exit;
|
|
104
|
+
// Default actions
|
|
114
105
|
switch (key.name) {
|
|
115
|
-
case '
|
|
116
|
-
return
|
|
117
|
-
case '
|
|
106
|
+
case 'i':
|
|
107
|
+
return Intent.Edit;
|
|
108
|
+
case 'y':
|
|
109
|
+
return Intent.InitMove;
|
|
118
110
|
case 'e':
|
|
119
|
-
|
|
111
|
+
case 'return':
|
|
112
|
+
return Intent.Confirm;
|
|
120
113
|
case 'q':
|
|
121
|
-
|
|
114
|
+
case 'escape':
|
|
115
|
+
return Intent.Exit;
|
|
122
116
|
default:
|
|
123
117
|
return null;
|
|
124
118
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "epiq",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": "dist/cli.js",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"description": "CLI based issue
|
|
7
|
+
"description": "EPIQ - CLI based issue tracker",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=16"
|
|
10
10
|
},
|
package/readme.md
CHANGED
|
@@ -1,37 +1,74 @@
|
|
|
1
1
|
# epiq
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **The ultimate productivity tool** — a CLI-based issue tracking client powered by Git as its backend, living directly inside the repository you work in.
|
|
4
|
+
> No external services. No context switching. Just tickets, versioned and colocated with your code.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Why epiq?
|
|
9
|
+
|
|
10
|
+
- **Simplicity** — Skip the additional tooling complexity
|
|
11
|
+
- **Repo-native** — Lives inside your project directory
|
|
12
|
+
- **Offline-ready** — Works wherever Git works
|
|
13
|
+
- **Team-friendly** — Collaborate through normal Git workflows
|
|
14
|
+
- **Shareable** — ASCII board exported to `.md`, viewable in the CLI, on GitHub/GitLab, or as your project README.
|
|
15
|
+
- **CLI-first** — Fast, scriptable, and developer/agent-friendly
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 📦 Installation
|
|
20
|
+
|
|
21
|
+
Install globally using npm:
|
|
6
22
|
|
|
7
23
|
```bash
|
|
8
|
-
|
|
24
|
+
npm install --global epiq
|
|
9
25
|
```
|
|
10
26
|
|
|
11
|
-
|
|
27
|
+
Verify installation:
|
|
12
28
|
|
|
13
29
|
```bash
|
|
14
|
-
|
|
30
|
+
epiq --version
|
|
15
31
|
```
|
|
16
32
|
|
|
17
|
-
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 🚀 Getting Started
|
|
36
|
+
|
|
37
|
+
### Initialize a project
|
|
38
|
+
|
|
39
|
+
Create a new epiq workspace inside your current directory:
|
|
18
40
|
|
|
19
41
|
```bash
|
|
20
|
-
|
|
42
|
+
epiq --init "Project Name"
|
|
21
43
|
```
|
|
22
44
|
|
|
23
|
-
|
|
45
|
+
This sets up epiq in your repository and prepares it for issue tracking.
|
|
24
46
|
|
|
25
|
-
|
|
26
|
-
$ epiq --help
|
|
47
|
+
---
|
|
27
48
|
|
|
28
|
-
|
|
29
|
-
$ epiq
|
|
49
|
+
### Open the workspace
|
|
30
50
|
|
|
31
|
-
|
|
32
|
-
--name Your name
|
|
51
|
+
Run epiq inside any initialized repository:
|
|
33
52
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
Hello, Jane
|
|
53
|
+
```bash
|
|
54
|
+
epiq
|
|
37
55
|
```
|
|
56
|
+
|
|
57
|
+
This opens the interactive CLI workspace.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 🛠 Philosophy
|
|
62
|
+
|
|
63
|
+
epiq keeps issue tracking:
|
|
64
|
+
|
|
65
|
+
- Close to your code
|
|
66
|
+
- Versioned with your commits
|
|
67
|
+
- Simple and transparent
|
|
68
|
+
- Independent from external platforms
|
|
69
|
+
|
|
70
|
+
If you use Git, you already have everything you need.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
Built for developers who live in the terminal.
|