@nickchristensen/cliftin 1.0.2 → 1.2.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/README.md +30 -22
- package/dist/commands/programs/show.d.ts +1 -4
- package/dist/commands/programs/show.js +7 -16
- package/dist/commands/workouts/show.d.ts +1 -1
- package/dist/commands/workouts/show.js +12 -5
- package/dist/hooks/init.d.ts +3 -0
- package/dist/hooks/init.js +8 -0
- package/dist/lib/config.js +3 -5
- package/dist/lib/repositories/programs.js +1 -1
- package/dist/lib/repositories/workouts.js +6 -1
- package/oclif.manifest.json +4 -16
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -5,28 +5,40 @@ CLIftin: A read-only CLI for Liftin'
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
[](https://oclif.io)
|
|
8
|
-
[](https://npmjs.org/package/cliftin)
|
|
9
|
-
[](https://npmjs.org/package/cliftin)
|
|
8
|
+
[](https://npmjs.org/package/cliftin)
|
|
9
|
+
[](https://npmjs.org/package/cliftin)
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
<!-- toc -->
|
|
13
|
+
* [Configuration](#configuration)
|
|
13
14
|
* [Usage](#usage)
|
|
14
15
|
* [Commands](#commands)
|
|
15
16
|
<!-- tocstop -->
|
|
17
|
+
|
|
18
|
+
# Configuration
|
|
19
|
+
|
|
20
|
+
cliftin reads your Liftin database directly. By default it looks for the Liftin app's SQLite file at:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
$HOME/Library/Containers/com.nstrm.Bello/Data/Library/Application Support/Liftin/BelloDataModel.sqlite
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
To use a different path, set `LIFTIN_DB_PATH` in your environment or in a `.env.local` file at the project root:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
LIFTIN_DB_PATH=/path/to/BelloDataModel.sqlite
|
|
30
|
+
```
|
|
31
|
+
|
|
16
32
|
# Usage
|
|
17
|
-
<!-- usage -->
|
|
18
33
|
```sh-session
|
|
19
34
|
$ npm install -g @nickchristensen/cliftin
|
|
20
35
|
$ cliftin COMMAND
|
|
21
36
|
running command...
|
|
22
|
-
$ cliftin (--version)
|
|
23
|
-
@nickchristensen/cliftin/1.0.2 darwin-arm64 node-v25.6.0
|
|
24
37
|
$ cliftin --help [COMMAND]
|
|
25
38
|
USAGE
|
|
26
39
|
$ cliftin COMMAND
|
|
27
40
|
...
|
|
28
41
|
```
|
|
29
|
-
<!-- usagestop -->
|
|
30
42
|
# Commands
|
|
31
43
|
<!-- commands -->
|
|
32
44
|
* [`cliftin exercises list`](#cliftin-exercises-list)
|
|
@@ -35,7 +47,7 @@ USAGE
|
|
|
35
47
|
* [`cliftin programs list`](#cliftin-programs-list)
|
|
36
48
|
* [`cliftin programs show [SELECTOR]`](#cliftin-programs-show-selector)
|
|
37
49
|
* [`cliftin workouts list`](#cliftin-workouts-list)
|
|
38
|
-
* [`cliftin workouts show WORKOUTID`](#cliftin-workouts-show-workoutid)
|
|
50
|
+
* [`cliftin workouts show [WORKOUTID]`](#cliftin-workouts-show-workoutid)
|
|
39
51
|
|
|
40
52
|
## `cliftin exercises list`
|
|
41
53
|
|
|
@@ -60,7 +72,7 @@ DESCRIPTION
|
|
|
60
72
|
List exercises
|
|
61
73
|
```
|
|
62
74
|
|
|
63
|
-
_See code: [src/commands/exercises/list.ts](https://github.com/nickchristensen/cliftin/blob/v1.0
|
|
75
|
+
_See code: [src/commands/exercises/list.ts](https://github.com/nickchristensen/cliftin/blob/v1.2.0/src/commands/exercises/list.ts)_
|
|
64
76
|
|
|
65
77
|
## `cliftin exercises show SELECTOR`
|
|
66
78
|
|
|
@@ -94,7 +106,7 @@ DESCRIPTION
|
|
|
94
106
|
Show one exercise detail and history
|
|
95
107
|
```
|
|
96
108
|
|
|
97
|
-
_See code: [src/commands/exercises/show.ts](https://github.com/nickchristensen/cliftin/blob/v1.0
|
|
109
|
+
_See code: [src/commands/exercises/show.ts](https://github.com/nickchristensen/cliftin/blob/v1.2.0/src/commands/exercises/show.ts)_
|
|
98
110
|
|
|
99
111
|
## `cliftin help [COMMAND]`
|
|
100
112
|
|
|
@@ -131,7 +143,7 @@ DESCRIPTION
|
|
|
131
143
|
List programs
|
|
132
144
|
```
|
|
133
145
|
|
|
134
|
-
_See code: [src/commands/programs/list.ts](https://github.com/nickchristensen/cliftin/blob/v1.0
|
|
146
|
+
_See code: [src/commands/programs/list.ts](https://github.com/nickchristensen/cliftin/blob/v1.2.0/src/commands/programs/list.ts)_
|
|
135
147
|
|
|
136
148
|
## `cliftin programs show [SELECTOR]`
|
|
137
149
|
|
|
@@ -139,14 +151,10 @@ Show one program hierarchy
|
|
|
139
151
|
|
|
140
152
|
```
|
|
141
153
|
USAGE
|
|
142
|
-
$ cliftin programs show [SELECTOR] [--json]
|
|
154
|
+
$ cliftin programs show [SELECTOR] [--json]
|
|
143
155
|
|
|
144
156
|
ARGUMENTS
|
|
145
|
-
[SELECTOR] program id or name
|
|
146
|
-
|
|
147
|
-
FLAGS
|
|
148
|
-
--active Show active program detail
|
|
149
|
-
--current Alias for --active
|
|
157
|
+
[SELECTOR] program id or name (default: active program)
|
|
150
158
|
|
|
151
159
|
GLOBAL FLAGS
|
|
152
160
|
--json Format output as json.
|
|
@@ -155,7 +163,7 @@ DESCRIPTION
|
|
|
155
163
|
Show one program hierarchy
|
|
156
164
|
```
|
|
157
165
|
|
|
158
|
-
_See code: [src/commands/programs/show.ts](https://github.com/nickchristensen/cliftin/blob/v1.0
|
|
166
|
+
_See code: [src/commands/programs/show.ts](https://github.com/nickchristensen/cliftin/blob/v1.2.0/src/commands/programs/show.ts)_
|
|
159
167
|
|
|
160
168
|
## `cliftin workouts list`
|
|
161
169
|
|
|
@@ -182,18 +190,18 @@ DESCRIPTION
|
|
|
182
190
|
List workouts
|
|
183
191
|
```
|
|
184
192
|
|
|
185
|
-
_See code: [src/commands/workouts/list.ts](https://github.com/nickchristensen/cliftin/blob/v1.0
|
|
193
|
+
_See code: [src/commands/workouts/list.ts](https://github.com/nickchristensen/cliftin/blob/v1.2.0/src/commands/workouts/list.ts)_
|
|
186
194
|
|
|
187
|
-
## `cliftin workouts show WORKOUTID`
|
|
195
|
+
## `cliftin workouts show [WORKOUTID]`
|
|
188
196
|
|
|
189
197
|
Show one workout with exercises and sets
|
|
190
198
|
|
|
191
199
|
```
|
|
192
200
|
USAGE
|
|
193
|
-
$ cliftin workouts show WORKOUTID [--json]
|
|
201
|
+
$ cliftin workouts show [WORKOUTID] [--json]
|
|
194
202
|
|
|
195
203
|
ARGUMENTS
|
|
196
|
-
WORKOUTID workout id
|
|
204
|
+
[WORKOUTID] workout id (default: latest workout)
|
|
197
205
|
|
|
198
206
|
GLOBAL FLAGS
|
|
199
207
|
--json Format output as json.
|
|
@@ -202,5 +210,5 @@ DESCRIPTION
|
|
|
202
210
|
Show one workout with exercises and sets
|
|
203
211
|
```
|
|
204
212
|
|
|
205
|
-
_See code: [src/commands/workouts/show.ts](https://github.com/nickchristensen/cliftin/blob/v1.0
|
|
213
|
+
_See code: [src/commands/workouts/show.ts](https://github.com/nickchristensen/cliftin/blob/v1.2.0/src/commands/workouts/show.ts)_
|
|
206
214
|
<!-- commandsstop -->
|
|
@@ -5,9 +5,6 @@ export default class ProgramsShow extends Command {
|
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static enableJsonFlag: boolean;
|
|
8
|
-
static flags: {
|
|
9
|
-
active: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
current: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
-
};
|
|
8
|
+
static flags: {};
|
|
12
9
|
run(): Promise<unknown | void>;
|
|
13
10
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Args, Command
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
2
|
import { closeDb, openDb } from '../../lib/db.js';
|
|
3
3
|
import { serializeProgramDetailWithWeightUnits } from '../../lib/json-weight.js';
|
|
4
4
|
import { renderTable } from '../../lib/output.js';
|
|
@@ -30,26 +30,16 @@ function buildProgramRows(weeks, unitLabel) {
|
|
|
30
30
|
}
|
|
31
31
|
export default class ProgramsShow extends Command {
|
|
32
32
|
static args = {
|
|
33
|
-
selector: Args.string({ description: 'program id or name', ignoreStdin: true, required: false }),
|
|
33
|
+
selector: Args.string({ description: 'program id or name (default: active program)', ignoreStdin: true, required: false }),
|
|
34
34
|
};
|
|
35
35
|
static description = 'Show one program hierarchy';
|
|
36
36
|
static enableJsonFlag = true;
|
|
37
|
-
static flags = {
|
|
38
|
-
active: Flags.boolean({ description: 'Show active program detail' }),
|
|
39
|
-
current: Flags.boolean({ description: 'Alias for --active' }),
|
|
40
|
-
};
|
|
37
|
+
static flags = {};
|
|
41
38
|
async run() {
|
|
42
|
-
const { args
|
|
39
|
+
const { args } = await this.parse(ProgramsShow);
|
|
43
40
|
const context = openDb();
|
|
44
41
|
try {
|
|
45
|
-
const
|
|
46
|
-
if (args.selector && useActive) {
|
|
47
|
-
throw new Error('Use either a selector or --active/--current, not both.');
|
|
48
|
-
}
|
|
49
|
-
if (!args.selector && !useActive) {
|
|
50
|
-
throw new Error('Provide a selector or use --active/--current.');
|
|
51
|
-
}
|
|
52
|
-
const programId = await resolveProgramSelector(context.db, args.selector, Boolean(useActive));
|
|
42
|
+
const programId = await resolveProgramSelector(context.db, args.selector, !args.selector);
|
|
53
43
|
const detail = await getProgramDetail(context.db, programId);
|
|
54
44
|
const unitPreference = await resolveProgramWeightUnit(context.db, detail.program.id);
|
|
55
45
|
const unitLabel = weightUnitLabel(unitPreference);
|
|
@@ -57,7 +47,8 @@ export default class ProgramsShow extends Command {
|
|
|
57
47
|
return serializeProgramDetailWithWeightUnits(detail, unitPreference);
|
|
58
48
|
}
|
|
59
49
|
this.log(`[${detail.program.id}] ${detail.program.name}`);
|
|
60
|
-
this.log(`Active: ${detail.program.isActive}
|
|
50
|
+
this.log(`Active: ${detail.program.isActive}`);
|
|
51
|
+
this.log(`Template: ${detail.program.isTemplate}`);
|
|
61
52
|
this.log('');
|
|
62
53
|
const programRows = buildProgramRows(detail.weeks, unitLabel);
|
|
63
54
|
const renderedTable = renderTable(programRows).replace(/^\n+/, '');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class WorkoutsShow extends Command {
|
|
3
3
|
static args: {
|
|
4
|
-
workoutId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
4
|
+
workoutId: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static enableJsonFlag: boolean;
|
|
@@ -3,7 +3,7 @@ import { format, isValid, parseISO } from 'date-fns';
|
|
|
3
3
|
import { closeDb, openDb } from '../../lib/db.js';
|
|
4
4
|
import { serializeWorkoutDetailWithWeightUnits } from '../../lib/json-weight.js';
|
|
5
5
|
import { renderTable } from '../../lib/output.js';
|
|
6
|
-
import { getWorkoutDetail } from '../../lib/repositories/workouts.js';
|
|
6
|
+
import { getWorkoutDetail, listWorkouts } from '../../lib/repositories/workouts.js';
|
|
7
7
|
import { resolveGlobalWeightUnit, weightUnitLabel } from '../../lib/units.js';
|
|
8
8
|
function formatDurationMinutes(durationSeconds) {
|
|
9
9
|
if (durationSeconds === null)
|
|
@@ -20,7 +20,7 @@ function formatWorkoutDate(dateIso) {
|
|
|
20
20
|
}
|
|
21
21
|
export default class WorkoutsShow extends Command {
|
|
22
22
|
static args = {
|
|
23
|
-
workoutId: Args.string({ description: 'workout id', required:
|
|
23
|
+
workoutId: Args.string({ description: 'workout id (default: latest workout)', required: false }),
|
|
24
24
|
};
|
|
25
25
|
static description = 'Show one workout with exercises and sets';
|
|
26
26
|
static enableJsonFlag = true;
|
|
@@ -28,10 +28,17 @@ export default class WorkoutsShow extends Command {
|
|
|
28
28
|
const { args } = await this.parse(WorkoutsShow);
|
|
29
29
|
const context = openDb();
|
|
30
30
|
try {
|
|
31
|
-
if (!/^\d+$/.test(args.workoutId)) {
|
|
32
|
-
throw new Error('Workout
|
|
31
|
+
if (args.workoutId !== undefined && !/^\d+$/.test(args.workoutId)) {
|
|
32
|
+
throw new Error('Workout id must be numeric.');
|
|
33
33
|
}
|
|
34
|
-
const
|
|
34
|
+
const workoutId = args.workoutId
|
|
35
|
+
? Number(args.workoutId)
|
|
36
|
+
: await listWorkouts(context.db, { limit: 1 }).then((rows) => {
|
|
37
|
+
if (rows.length === 0)
|
|
38
|
+
throw new Error('No workouts found.');
|
|
39
|
+
return rows[0].id;
|
|
40
|
+
});
|
|
41
|
+
const detail = await getWorkoutDetail(context.db, workoutId);
|
|
35
42
|
const unitPreference = await resolveGlobalWeightUnit(context.db);
|
|
36
43
|
const unitLabel = weightUnitLabel(unitPreference);
|
|
37
44
|
if (this.jsonEnabled()) {
|
package/dist/lib/config.js
CHANGED
|
@@ -10,15 +10,13 @@ function loadEnv() {
|
|
|
10
10
|
}
|
|
11
11
|
export function getDbPath() {
|
|
12
12
|
loadEnv();
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
throw new Error('Missing LIFTIN_DB_PATH. Add it to .env.local, for example: LIFTIN_DB_PATH=/Users/nick/code/cliftin/data/BelloDataModel.sqlite');
|
|
16
|
-
}
|
|
13
|
+
const defaultPath = `${process.env.HOME}/Library/Containers/com.nstrm.Bello/Data/Library/Application Support/Liftin/BelloDataModel.sqlite`;
|
|
14
|
+
const path = process.env.LIFTIN_DB_PATH ?? defaultPath;
|
|
17
15
|
try {
|
|
18
16
|
accessSync(path, constants.R_OK);
|
|
19
17
|
}
|
|
20
18
|
catch {
|
|
21
|
-
throw new Error(`Database file is not readable at
|
|
19
|
+
throw new Error(`Database file is not readable at path=${path}`);
|
|
22
20
|
}
|
|
23
21
|
return path;
|
|
24
22
|
}
|
|
@@ -54,7 +54,7 @@ export async function resolveProgramSelector(db, selector, activeOnly) {
|
|
|
54
54
|
return fallbackRows[0].id;
|
|
55
55
|
}
|
|
56
56
|
if (!selector)
|
|
57
|
-
throw new Error('Program selector is required
|
|
57
|
+
throw new Error('Program selector is required.');
|
|
58
58
|
const programId = await resolveIdOrName(db, 'ZWORKOUTPLAN', selector);
|
|
59
59
|
const nonDeleted = await db
|
|
60
60
|
.selectFrom('ZWORKOUTPLAN')
|
|
@@ -73,7 +73,12 @@ export async function getWorkoutDetail(db, workoutId) {
|
|
|
73
73
|
.selectFrom('ZEXERCISERESULT as er')
|
|
74
74
|
.leftJoin('ZEXERCISECONFIGURATION as ec', 'ec.Z_PK', 'er.ZCONFIGURATION')
|
|
75
75
|
.leftJoin('ZEXERCISEINFORMATION as ei', 'ei.Z_PK', 'ec.ZINFORMATION')
|
|
76
|
-
.select([
|
|
76
|
+
.select([
|
|
77
|
+
'er.Z_PK as exerciseResultId',
|
|
78
|
+
'ei.Z_PK as exerciseId',
|
|
79
|
+
'ei.ZISUSERCREATED as isUserCreated',
|
|
80
|
+
'ei.ZNAME as exerciseName',
|
|
81
|
+
])
|
|
77
82
|
.where('er.ZWORKOUT', '=', workoutId)
|
|
78
83
|
.orderBy('er.Z_FOK_WORKOUT', 'asc')
|
|
79
84
|
.orderBy('er.Z_PK', 'asc')
|
package/oclif.manifest.json
CHANGED
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
"aliases": [],
|
|
211
211
|
"args": {
|
|
212
212
|
"selector": {
|
|
213
|
-
"description": "program id or name",
|
|
213
|
+
"description": "program id or name (default: active program)",
|
|
214
214
|
"name": "selector",
|
|
215
215
|
"required": false
|
|
216
216
|
}
|
|
@@ -223,18 +223,6 @@
|
|
|
223
223
|
"name": "json",
|
|
224
224
|
"allowNo": false,
|
|
225
225
|
"type": "boolean"
|
|
226
|
-
},
|
|
227
|
-
"active": {
|
|
228
|
-
"description": "Show active program detail",
|
|
229
|
-
"name": "active",
|
|
230
|
-
"allowNo": false,
|
|
231
|
-
"type": "boolean"
|
|
232
|
-
},
|
|
233
|
-
"current": {
|
|
234
|
-
"description": "Alias for --active",
|
|
235
|
-
"name": "current",
|
|
236
|
-
"allowNo": false,
|
|
237
|
-
"type": "boolean"
|
|
238
226
|
}
|
|
239
227
|
},
|
|
240
228
|
"hasDynamicHelp": false,
|
|
@@ -350,9 +338,9 @@
|
|
|
350
338
|
"aliases": [],
|
|
351
339
|
"args": {
|
|
352
340
|
"workoutId": {
|
|
353
|
-
"description": "workout id",
|
|
341
|
+
"description": "workout id (default: latest workout)",
|
|
354
342
|
"name": "workoutId",
|
|
355
|
-
"required":
|
|
343
|
+
"required": false
|
|
356
344
|
}
|
|
357
345
|
},
|
|
358
346
|
"description": "Show one workout with exercises and sets",
|
|
@@ -382,5 +370,5 @@
|
|
|
382
370
|
]
|
|
383
371
|
}
|
|
384
372
|
},
|
|
385
|
-
"version": "1.0
|
|
373
|
+
"version": "1.2.0"
|
|
386
374
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nickchristensen/cliftin",
|
|
3
3
|
"description": "CLIftin: A read-only CLI for Liftin'",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"author": "Nick Christensen",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cliftin": "./bin/run.js"
|
|
@@ -57,6 +57,9 @@
|
|
|
57
57
|
"flagSortOrder": "none"
|
|
58
58
|
},
|
|
59
59
|
"commands": "./dist/commands",
|
|
60
|
+
"hooks": {
|
|
61
|
+
"init": "./dist/hooks/init"
|
|
62
|
+
},
|
|
60
63
|
"plugins": [
|
|
61
64
|
"@oclif/plugin-help"
|
|
62
65
|
],
|