@nickchristensen/cliftin 3.0.0 → 4.0.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 +110 -9
- package/dist/commands/routines/from-workout.d.ts +9 -0
- package/dist/commands/routines/from-workout.js +51 -0
- package/dist/commands/routines/latest.d.ts +6 -0
- package/dist/commands/routines/latest.js +39 -0
- package/dist/commands/routines/list.d.ts +11 -0
- package/dist/commands/routines/list.js +40 -0
- package/dist/commands/routines/next.d.ts +6 -0
- package/dist/commands/routines/next.js +37 -0
- package/dist/commands/routines/show.d.ts +9 -0
- package/dist/commands/routines/show.js +34 -0
- package/dist/commands/workouts/next.js +8 -36
- package/dist/lib/json-weight.d.ts +2 -2
- package/dist/lib/json-weight.js +15 -2
- package/dist/lib/repositories/routines.d.ts +13 -0
- package/dist/lib/repositories/routines.js +171 -0
- package/dist/lib/repositories/workouts.js +1 -0
- package/dist/lib/routine-output.d.ts +2 -0
- package/dist/lib/routine-output.js +19 -0
- package/dist/lib/types.d.ts +18 -8
- package/oclif.manifest.json +180 -2
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -46,6 +46,11 @@ USAGE
|
|
|
46
46
|
* [`cliftin help [COMMAND]`](#cliftin-help-command)
|
|
47
47
|
* [`cliftin programs list`](#cliftin-programs-list)
|
|
48
48
|
* [`cliftin programs show [SELECTOR]`](#cliftin-programs-show-selector)
|
|
49
|
+
* [`cliftin routines from-workout [WORKOUTID]`](#cliftin-routines-from-workout-workoutid)
|
|
50
|
+
* [`cliftin routines latest`](#cliftin-routines-latest)
|
|
51
|
+
* [`cliftin routines list`](#cliftin-routines-list)
|
|
52
|
+
* [`cliftin routines next`](#cliftin-routines-next)
|
|
53
|
+
* [`cliftin routines show SELECTOR`](#cliftin-routines-show-selector)
|
|
49
54
|
* [`cliftin workouts list`](#cliftin-workouts-list)
|
|
50
55
|
* [`cliftin workouts next`](#cliftin-workouts-next)
|
|
51
56
|
* [`cliftin workouts show [WORKOUTID]`](#cliftin-workouts-show-workoutid)
|
|
@@ -73,7 +78,7 @@ DESCRIPTION
|
|
|
73
78
|
List exercises
|
|
74
79
|
```
|
|
75
80
|
|
|
76
|
-
_See code: [src/commands/exercises/list.ts](https://github.com/nickchristensen/cliftin/blob/
|
|
81
|
+
_See code: [src/commands/exercises/list.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/exercises/list.ts)_
|
|
77
82
|
|
|
78
83
|
## `cliftin exercises show SELECTOR`
|
|
79
84
|
|
|
@@ -107,7 +112,7 @@ DESCRIPTION
|
|
|
107
112
|
Show one exercise detail and history
|
|
108
113
|
```
|
|
109
114
|
|
|
110
|
-
_See code: [src/commands/exercises/show.ts](https://github.com/nickchristensen/cliftin/blob/
|
|
115
|
+
_See code: [src/commands/exercises/show.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/exercises/show.ts)_
|
|
111
116
|
|
|
112
117
|
## `cliftin help [COMMAND]`
|
|
113
118
|
|
|
@@ -144,7 +149,7 @@ DESCRIPTION
|
|
|
144
149
|
List programs
|
|
145
150
|
```
|
|
146
151
|
|
|
147
|
-
_See code: [src/commands/programs/list.ts](https://github.com/nickchristensen/cliftin/blob/
|
|
152
|
+
_See code: [src/commands/programs/list.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/programs/list.ts)_
|
|
148
153
|
|
|
149
154
|
## `cliftin programs show [SELECTOR]`
|
|
150
155
|
|
|
@@ -164,7 +169,103 @@ DESCRIPTION
|
|
|
164
169
|
Show one program hierarchy
|
|
165
170
|
```
|
|
166
171
|
|
|
167
|
-
_See code: [src/commands/programs/show.ts](https://github.com/nickchristensen/cliftin/blob/
|
|
172
|
+
_See code: [src/commands/programs/show.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/programs/show.ts)_
|
|
173
|
+
|
|
174
|
+
## `cliftin routines from-workout [WORKOUTID]`
|
|
175
|
+
|
|
176
|
+
Show the planned routine for a completed workout
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
USAGE
|
|
180
|
+
$ cliftin routines from-workout [WORKOUTID] [--json]
|
|
181
|
+
|
|
182
|
+
ARGUMENTS
|
|
183
|
+
[WORKOUTID] workout id (default: latest workout)
|
|
184
|
+
|
|
185
|
+
GLOBAL FLAGS
|
|
186
|
+
--json Format output as json.
|
|
187
|
+
|
|
188
|
+
DESCRIPTION
|
|
189
|
+
Show the planned routine for a completed workout
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
_See code: [src/commands/routines/from-workout.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/routines/from-workout.ts)_
|
|
193
|
+
|
|
194
|
+
## `cliftin routines latest`
|
|
195
|
+
|
|
196
|
+
Show the planned routine for the latest workout
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
USAGE
|
|
200
|
+
$ cliftin routines latest [--json]
|
|
201
|
+
|
|
202
|
+
GLOBAL FLAGS
|
|
203
|
+
--json Format output as json.
|
|
204
|
+
|
|
205
|
+
DESCRIPTION
|
|
206
|
+
Show the planned routine for the latest workout
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
_See code: [src/commands/routines/latest.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/routines/latest.ts)_
|
|
210
|
+
|
|
211
|
+
## `cliftin routines list`
|
|
212
|
+
|
|
213
|
+
List planned routines
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
USAGE
|
|
217
|
+
$ cliftin routines list [--json] [--name <value>] [--program <value>] [--week <value>]
|
|
218
|
+
|
|
219
|
+
FLAGS
|
|
220
|
+
--name=<value> Filter by routine name contains
|
|
221
|
+
--program=<value> Filter by program id or name
|
|
222
|
+
--week=<value> Filter by week number
|
|
223
|
+
|
|
224
|
+
GLOBAL FLAGS
|
|
225
|
+
--json Format output as json.
|
|
226
|
+
|
|
227
|
+
DESCRIPTION
|
|
228
|
+
List planned routines
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
_See code: [src/commands/routines/list.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/routines/list.ts)_
|
|
232
|
+
|
|
233
|
+
## `cliftin routines next`
|
|
234
|
+
|
|
235
|
+
Show the up-next routine from the active program
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
USAGE
|
|
239
|
+
$ cliftin routines next [--json]
|
|
240
|
+
|
|
241
|
+
GLOBAL FLAGS
|
|
242
|
+
--json Format output as json.
|
|
243
|
+
|
|
244
|
+
DESCRIPTION
|
|
245
|
+
Show the up-next routine from the active program
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
_See code: [src/commands/routines/next.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/routines/next.ts)_
|
|
249
|
+
|
|
250
|
+
## `cliftin routines show SELECTOR`
|
|
251
|
+
|
|
252
|
+
Show one planned routine
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
USAGE
|
|
256
|
+
$ cliftin routines show SELECTOR [--json]
|
|
257
|
+
|
|
258
|
+
ARGUMENTS
|
|
259
|
+
SELECTOR routine id or name
|
|
260
|
+
|
|
261
|
+
GLOBAL FLAGS
|
|
262
|
+
--json Format output as json.
|
|
263
|
+
|
|
264
|
+
DESCRIPTION
|
|
265
|
+
Show one planned routine
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
_See code: [src/commands/routines/show.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/routines/show.ts)_
|
|
168
269
|
|
|
169
270
|
## `cliftin workouts list`
|
|
170
271
|
|
|
@@ -191,11 +292,11 @@ DESCRIPTION
|
|
|
191
292
|
List workouts
|
|
192
293
|
```
|
|
193
294
|
|
|
194
|
-
_See code: [src/commands/workouts/list.ts](https://github.com/nickchristensen/cliftin/blob/
|
|
295
|
+
_See code: [src/commands/workouts/list.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/workouts/list.ts)_
|
|
195
296
|
|
|
196
297
|
## `cliftin workouts next`
|
|
197
298
|
|
|
198
|
-
|
|
299
|
+
Redirect to routines next
|
|
199
300
|
|
|
200
301
|
```
|
|
201
302
|
USAGE
|
|
@@ -205,10 +306,10 @@ GLOBAL FLAGS
|
|
|
205
306
|
--json Format output as json.
|
|
206
307
|
|
|
207
308
|
DESCRIPTION
|
|
208
|
-
|
|
309
|
+
Redirect to routines next
|
|
209
310
|
```
|
|
210
311
|
|
|
211
|
-
_See code: [src/commands/workouts/next.ts](https://github.com/nickchristensen/cliftin/blob/
|
|
312
|
+
_See code: [src/commands/workouts/next.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/workouts/next.ts)_
|
|
212
313
|
|
|
213
314
|
## `cliftin workouts show [WORKOUTID]`
|
|
214
315
|
|
|
@@ -228,5 +329,5 @@ DESCRIPTION
|
|
|
228
329
|
Show one workout with exercises and sets
|
|
229
330
|
```
|
|
230
331
|
|
|
231
|
-
_See code: [src/commands/workouts/show.ts](https://github.com/nickchristensen/cliftin/blob/
|
|
332
|
+
_See code: [src/commands/workouts/show.ts](https://github.com/nickchristensen/cliftin/blob/v4.0.0/src/commands/workouts/show.ts)_
|
|
232
333
|
<!-- commandsstop -->
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class RoutinesFromWorkout extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
workoutId: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static enableJsonFlag: boolean;
|
|
8
|
+
run(): Promise<unknown | void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import { closeDb, openDb } from '../../lib/db.js';
|
|
3
|
+
import { toJsonErrorPayload } from '../../lib/json-error.js';
|
|
4
|
+
import { serializeRoutineDetailWithWeightUnits } from '../../lib/json-weight.js';
|
|
5
|
+
import { renderTable } from '../../lib/output.js';
|
|
6
|
+
import { getLatestRoutineDetail, getRoutineDetailFromWorkout } from '../../lib/repositories/routines.js';
|
|
7
|
+
import { buildRoutineRows } from '../../lib/routine-output.js';
|
|
8
|
+
import { resolveProgramWeightUnit, weightUnitLabel } from '../../lib/units.js';
|
|
9
|
+
export default class RoutinesFromWorkout extends Command {
|
|
10
|
+
static args = {
|
|
11
|
+
workoutId: Args.string({
|
|
12
|
+
description: 'workout id (default: latest workout)',
|
|
13
|
+
ignoreStdin: true,
|
|
14
|
+
required: false,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
static description = 'Show the planned routine for a completed workout';
|
|
18
|
+
static enableJsonFlag = true;
|
|
19
|
+
async run() {
|
|
20
|
+
const { args } = await this.parse(RoutinesFromWorkout);
|
|
21
|
+
const context = openDb();
|
|
22
|
+
try {
|
|
23
|
+
if (args.workoutId !== undefined && !/^\d+$/.test(args.workoutId)) {
|
|
24
|
+
throw new Error('Workout id must be numeric.');
|
|
25
|
+
}
|
|
26
|
+
const detail = args.workoutId
|
|
27
|
+
? await getRoutineDetailFromWorkout(context.db, Number(args.workoutId))
|
|
28
|
+
: await getLatestRoutineDetail(context.db);
|
|
29
|
+
const unitPreference = await resolveProgramWeightUnit(context.db, detail.program.id);
|
|
30
|
+
const unitLabel = weightUnitLabel(unitPreference);
|
|
31
|
+
if (this.jsonEnabled()) {
|
|
32
|
+
return serializeRoutineDetailWithWeightUnits(detail, unitPreference);
|
|
33
|
+
}
|
|
34
|
+
this.log(`[${detail.routine.id}] ${detail.routine.name ?? 'Routine'}`);
|
|
35
|
+
this.log(`Program: ${detail.program.name}`);
|
|
36
|
+
this.log(`Week: ${detail.week.number}`);
|
|
37
|
+
if (detail.workout)
|
|
38
|
+
this.log(`Workout: [${detail.workout.id}] ${detail.workout.routine ?? 'Workout'}`);
|
|
39
|
+
this.log('');
|
|
40
|
+
this.log(renderTable(buildRoutineRows(detail.routine, unitLabel)).replace(/^\n+/, ''));
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (this.jsonEnabled())
|
|
44
|
+
return toJsonErrorPayload(error);
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
await closeDb(context);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { closeDb, openDb } from '../../lib/db.js';
|
|
3
|
+
import { toJsonErrorPayload } from '../../lib/json-error.js';
|
|
4
|
+
import { serializeRoutineDetailWithWeightUnits } from '../../lib/json-weight.js';
|
|
5
|
+
import { renderTable } from '../../lib/output.js';
|
|
6
|
+
import { getLatestRoutineDetail } from '../../lib/repositories/routines.js';
|
|
7
|
+
import { buildRoutineRows } from '../../lib/routine-output.js';
|
|
8
|
+
import { resolveProgramWeightUnit, weightUnitLabel } from '../../lib/units.js';
|
|
9
|
+
export default class RoutinesLatest extends Command {
|
|
10
|
+
static description = 'Show the planned routine for the latest workout';
|
|
11
|
+
static enableJsonFlag = true;
|
|
12
|
+
async run() {
|
|
13
|
+
await this.parse(RoutinesLatest);
|
|
14
|
+
const context = openDb();
|
|
15
|
+
try {
|
|
16
|
+
const detail = await getLatestRoutineDetail(context.db);
|
|
17
|
+
const unitPreference = await resolveProgramWeightUnit(context.db, detail.program.id);
|
|
18
|
+
const unitLabel = weightUnitLabel(unitPreference);
|
|
19
|
+
if (this.jsonEnabled()) {
|
|
20
|
+
return serializeRoutineDetailWithWeightUnits(detail, unitPreference);
|
|
21
|
+
}
|
|
22
|
+
this.log(`[${detail.routine.id}] ${detail.routine.name ?? 'Routine'}`);
|
|
23
|
+
this.log(`Program: ${detail.program.name}`);
|
|
24
|
+
this.log(`Week: ${detail.week.number}`);
|
|
25
|
+
if (detail.workout)
|
|
26
|
+
this.log(`Workout: [${detail.workout.id}] ${detail.workout.routine ?? 'Workout'}`);
|
|
27
|
+
this.log('');
|
|
28
|
+
this.log(renderTable(buildRoutineRows(detail.routine, unitLabel)).replace(/^\n+/, ''));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (this.jsonEnabled())
|
|
32
|
+
return toJsonErrorPayload(error);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
await closeDb(context);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class RoutinesList extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static enableJsonFlag: boolean;
|
|
5
|
+
static flags: {
|
|
6
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
program: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
week: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<unknown | void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { closeDb, openDb } from '../../lib/db.js';
|
|
3
|
+
import { renderTable } from '../../lib/output.js';
|
|
4
|
+
import { listRoutines } from '../../lib/repositories/routines.js';
|
|
5
|
+
export default class RoutinesList extends Command {
|
|
6
|
+
static description = 'List planned routines';
|
|
7
|
+
static enableJsonFlag = true;
|
|
8
|
+
static flags = {
|
|
9
|
+
name: Flags.string({ description: 'Filter by routine name contains' }),
|
|
10
|
+
program: Flags.string({ description: 'Filter by program id or name' }),
|
|
11
|
+
week: Flags.integer({ description: 'Filter by week number' }),
|
|
12
|
+
};
|
|
13
|
+
async run() {
|
|
14
|
+
const { flags } = await this.parse(RoutinesList);
|
|
15
|
+
const context = openDb();
|
|
16
|
+
try {
|
|
17
|
+
const routines = await listRoutines(context.db, {
|
|
18
|
+
name: flags.name,
|
|
19
|
+
program: flags.program,
|
|
20
|
+
week: flags.week,
|
|
21
|
+
});
|
|
22
|
+
if (this.jsonEnabled()) {
|
|
23
|
+
return routines.map(({ id, ...routine }) => ({
|
|
24
|
+
...routine,
|
|
25
|
+
routineId: id,
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
this.log(renderTable(routines.map((routine) => ({
|
|
29
|
+
id: routine.id,
|
|
30
|
+
isNext: routine.isNext,
|
|
31
|
+
name: routine.name,
|
|
32
|
+
program: routine.program,
|
|
33
|
+
week: routine.week,
|
|
34
|
+
}))));
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
await closeDb(context);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { closeDb, openDb } from '../../lib/db.js';
|
|
3
|
+
import { toJsonErrorPayload } from '../../lib/json-error.js';
|
|
4
|
+
import { serializeRoutineDetailWithWeightUnits } from '../../lib/json-weight.js';
|
|
5
|
+
import { renderTable } from '../../lib/output.js';
|
|
6
|
+
import { getNextRoutineDetail } from '../../lib/repositories/routines.js';
|
|
7
|
+
import { buildRoutineRows } from '../../lib/routine-output.js';
|
|
8
|
+
import { resolveProgramWeightUnit, weightUnitLabel } from '../../lib/units.js';
|
|
9
|
+
export default class RoutinesNext extends Command {
|
|
10
|
+
static description = 'Show the up-next routine from the active program';
|
|
11
|
+
static enableJsonFlag = true;
|
|
12
|
+
async run() {
|
|
13
|
+
await this.parse(RoutinesNext);
|
|
14
|
+
const context = openDb();
|
|
15
|
+
try {
|
|
16
|
+
const detail = await getNextRoutineDetail(context.db);
|
|
17
|
+
const unitPreference = await resolveProgramWeightUnit(context.db, detail.program.id);
|
|
18
|
+
const unitLabel = weightUnitLabel(unitPreference);
|
|
19
|
+
if (this.jsonEnabled()) {
|
|
20
|
+
return serializeRoutineDetailWithWeightUnits(detail, unitPreference);
|
|
21
|
+
}
|
|
22
|
+
this.log(`[${detail.routine.id}] ${detail.routine.name ?? 'Routine'}`);
|
|
23
|
+
this.log(`Program: ${detail.program.name}`);
|
|
24
|
+
this.log(`Week: ${detail.week.number}`);
|
|
25
|
+
this.log('');
|
|
26
|
+
this.log(renderTable(buildRoutineRows(detail.routine, unitLabel)).replace(/^\n+/, ''));
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (this.jsonEnabled())
|
|
30
|
+
return toJsonErrorPayload(error);
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await closeDb(context);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class RoutinesShow extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
selector: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static enableJsonFlag: boolean;
|
|
8
|
+
run(): Promise<unknown | void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import { closeDb, openDb } from '../../lib/db.js';
|
|
3
|
+
import { serializeRoutineDetailWithWeightUnits } from '../../lib/json-weight.js';
|
|
4
|
+
import { renderTable } from '../../lib/output.js';
|
|
5
|
+
import { getRoutineDetail } from '../../lib/repositories/routines.js';
|
|
6
|
+
import { buildRoutineRows } from '../../lib/routine-output.js';
|
|
7
|
+
import { resolveProgramWeightUnit, weightUnitLabel } from '../../lib/units.js';
|
|
8
|
+
export default class RoutinesShow extends Command {
|
|
9
|
+
static args = {
|
|
10
|
+
selector: Args.string({ description: 'routine id or name', ignoreStdin: true, required: true }),
|
|
11
|
+
};
|
|
12
|
+
static description = 'Show one planned routine';
|
|
13
|
+
static enableJsonFlag = true;
|
|
14
|
+
async run() {
|
|
15
|
+
const { args } = await this.parse(RoutinesShow);
|
|
16
|
+
const context = openDb();
|
|
17
|
+
try {
|
|
18
|
+
const detail = await getRoutineDetail(context.db, args.selector);
|
|
19
|
+
const unitPreference = await resolveProgramWeightUnit(context.db, detail.program.id);
|
|
20
|
+
const unitLabel = weightUnitLabel(unitPreference);
|
|
21
|
+
if (this.jsonEnabled()) {
|
|
22
|
+
return serializeRoutineDetailWithWeightUnits(detail, unitPreference);
|
|
23
|
+
}
|
|
24
|
+
this.log(`[${detail.routine.id}] ${detail.routine.name ?? 'Routine'}`);
|
|
25
|
+
this.log(`Program: ${detail.program.name}`);
|
|
26
|
+
this.log(`Week: ${detail.week.number}`);
|
|
27
|
+
this.log('');
|
|
28
|
+
this.log(renderTable(buildRoutineRows(detail.routine, unitLabel)).replace(/^\n+/, ''));
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
await closeDb(context);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,44 +1,16 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import { closeDb, openDb } from '../../lib/db.js';
|
|
3
|
-
import { toJsonErrorPayload } from '../../lib/json-error.js';
|
|
4
|
-
import { serializeNextWorkoutDetailWithWeightUnits } from '../../lib/json-weight.js';
|
|
5
|
-
import { renderTable } from '../../lib/output.js';
|
|
6
|
-
import { getNextWorkoutDetail } from '../../lib/repositories/workouts.js';
|
|
7
|
-
import { resolveProgramWeightUnit, weightUnitLabel } from '../../lib/units.js';
|
|
8
2
|
export default class WorkoutsNext extends Command {
|
|
9
|
-
static description = '
|
|
3
|
+
static description = 'Redirect to routines next';
|
|
10
4
|
static enableJsonFlag = true;
|
|
11
5
|
async run() {
|
|
12
6
|
await this.parse(WorkoutsNext);
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return serializeNextWorkoutDetailWithWeightUnits(detail, unitPreference);
|
|
20
|
-
}
|
|
21
|
-
this.log(`[${detail.routine.id}] ${detail.routine.name ?? 'Workout'}`);
|
|
22
|
-
this.log(`Program: ${detail.program.name}`);
|
|
23
|
-
this.log(`Week: ${detail.week.number}`);
|
|
24
|
-
for (const exercise of detail.routine.exercises) {
|
|
25
|
-
this.log('');
|
|
26
|
-
this.log(`[${exercise.id ?? exercise.exerciseConfigId}] ${exercise.name ?? '(unnamed)'}`);
|
|
27
|
-
this.log(renderTable(exercise.sets.map((set) => ({
|
|
28
|
-
reps: set.reps,
|
|
29
|
-
rpe: set.rpe,
|
|
30
|
-
timeSeconds: set.timeSeconds,
|
|
31
|
-
weight: set.weight === null ? null : `${set.weight} ${unitLabel}`,
|
|
32
|
-
}))));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
if (this.jsonEnabled())
|
|
37
|
-
return toJsonErrorPayload(error);
|
|
38
|
-
throw error;
|
|
39
|
-
}
|
|
40
|
-
finally {
|
|
41
|
-
await closeDb(context);
|
|
7
|
+
const message = 'workouts next has moved. Use "cliftin routines next" instead.';
|
|
8
|
+
if (this.jsonEnabled()) {
|
|
9
|
+
return {
|
|
10
|
+
command: 'routines next',
|
|
11
|
+
message,
|
|
12
|
+
};
|
|
42
13
|
}
|
|
14
|
+
this.log(message);
|
|
43
15
|
}
|
|
44
16
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ExerciseHistoryRow,
|
|
1
|
+
import { ExerciseHistoryRow, ProgramDetailTree, RoutineDetail, WorkoutDetail } from './types.js';
|
|
2
2
|
import { UnitPreference, withWeightUnit } from './units.js';
|
|
3
3
|
export declare function serializeExerciseHistoryRowsWithWeightUnits(rows: ExerciseHistoryRow[], unitPreference: UnitPreference): Array<Omit<ExerciseHistoryRow, 'topWeight'> & {
|
|
4
4
|
topWeight: ReturnType<typeof withWeightUnit>;
|
|
5
5
|
}>;
|
|
6
6
|
export declare function serializeProgramDetailWithWeightUnits(detail: ProgramDetailTree, unitPreference: UnitPreference): unknown;
|
|
7
|
-
export declare function
|
|
7
|
+
export declare function serializeRoutineDetailWithWeightUnits(detail: RoutineDetail, unitPreference: UnitPreference): unknown;
|
|
8
8
|
export declare function serializeWorkoutDetailWithWeightUnits(detail: WorkoutDetail, unitPreference: UnitPreference): unknown;
|
package/dist/lib/json-weight.js
CHANGED
|
@@ -31,9 +31,9 @@ export function serializeProgramDetailWithWeightUnits(detail, unitPreference) {
|
|
|
31
31
|
})),
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
export function
|
|
34
|
+
export function serializeRoutineDetailWithWeightUnits(detail, unitPreference) {
|
|
35
35
|
const program = omitId(detail.program);
|
|
36
|
-
|
|
36
|
+
const payload = {
|
|
37
37
|
program: { ...program, programId: detail.program.id },
|
|
38
38
|
routine: {
|
|
39
39
|
...omitId(detail.routine),
|
|
@@ -54,6 +54,19 @@ export function serializeNextWorkoutDetailWithWeightUnits(detail, unitPreference
|
|
|
54
54
|
weekId: detail.week.id,
|
|
55
55
|
},
|
|
56
56
|
};
|
|
57
|
+
if (detail.workout) {
|
|
58
|
+
payload.workout = {
|
|
59
|
+
date: detail.workout.date,
|
|
60
|
+
duration: {
|
|
61
|
+
unit: 'seconds',
|
|
62
|
+
value: detail.workout.duration,
|
|
63
|
+
},
|
|
64
|
+
program: detail.workout.program,
|
|
65
|
+
routine: detail.workout.routine,
|
|
66
|
+
workoutId: detail.workout.id,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return payload;
|
|
57
70
|
}
|
|
58
71
|
export function serializeWorkoutDetailWithWeightUnits(detail, unitPreference) {
|
|
59
72
|
return {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Kysely } from 'kysely';
|
|
2
|
+
import { DatabaseSchema } from '../db.js';
|
|
3
|
+
import { RoutineDetail, RoutineSummary } from '../types.js';
|
|
4
|
+
export type RoutineFilters = {
|
|
5
|
+
name?: string;
|
|
6
|
+
program?: string;
|
|
7
|
+
week?: number;
|
|
8
|
+
};
|
|
9
|
+
export declare function listRoutines(db: Kysely<DatabaseSchema>, filters: RoutineFilters): Promise<RoutineSummary[]>;
|
|
10
|
+
export declare function getRoutineDetail(db: Kysely<DatabaseSchema>, selector: string): Promise<RoutineDetail>;
|
|
11
|
+
export declare function getNextRoutineDetail(db: Kysely<DatabaseSchema>): Promise<RoutineDetail>;
|
|
12
|
+
export declare function getRoutineDetailFromWorkout(db: Kysely<DatabaseSchema>, workoutId: number): Promise<RoutineDetail>;
|
|
13
|
+
export declare function getLatestRoutineDetail(db: Kysely<DatabaseSchema>): Promise<RoutineDetail>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { sql } from 'kysely';
|
|
2
|
+
import { appleSecondsToIso } from '../time.js';
|
|
3
|
+
import { getProgramDetail, resolveProgramSelector } from './programs.js';
|
|
4
|
+
import { resolveIdOrName } from './selectors.js';
|
|
5
|
+
async function getRoutinePlanContext(db, routineId) {
|
|
6
|
+
const row = await db
|
|
7
|
+
.selectFrom('ZROUTINE as r')
|
|
8
|
+
.leftJoin('ZPERIOD as p', 'p.Z_PK', 'r.ZPERIOD')
|
|
9
|
+
.leftJoin('ZWORKOUTPLAN as planDirect', 'planDirect.Z_PK', 'r.ZWORKOUTPLAN')
|
|
10
|
+
.leftJoin('ZWORKOUTPLAN as planFromPeriod', 'planFromPeriod.Z_PK', 'p.ZWORKOUTPLAN')
|
|
11
|
+
.select([
|
|
12
|
+
'r.Z_PK as id',
|
|
13
|
+
'p.Z_PK as weekId',
|
|
14
|
+
'planDirect.Z_PK as programIdDirect',
|
|
15
|
+
'planFromPeriod.Z_PK as programIdFromPeriod',
|
|
16
|
+
])
|
|
17
|
+
.where('r.Z_PK', '=', routineId)
|
|
18
|
+
.where('r.ZSOFTDELETED', 'is not', 1)
|
|
19
|
+
.where((eb) => eb.or([eb('planDirect.ZSOFTDELETED', 'is not', 1), eb('planFromPeriod.ZSOFTDELETED', 'is not', 1)]))
|
|
20
|
+
.executeTakeFirst();
|
|
21
|
+
if (!row)
|
|
22
|
+
throw new Error(`Routine not found: ${routineId}`);
|
|
23
|
+
const programId = row.programIdDirect ?? row.programIdFromPeriod;
|
|
24
|
+
if (programId === null)
|
|
25
|
+
throw new Error(`Routine ${routineId} is not linked to a program.`);
|
|
26
|
+
if (row.weekId === null)
|
|
27
|
+
throw new Error(`Routine ${routineId} is not linked to a week.`);
|
|
28
|
+
return { programId, weekId: row.weekId };
|
|
29
|
+
}
|
|
30
|
+
async function getRoutineDetailById(db, routineId, workout) {
|
|
31
|
+
const context = await getRoutinePlanContext(db, routineId);
|
|
32
|
+
const programDetail = await getProgramDetail(db, context.programId);
|
|
33
|
+
const weekIndex = programDetail.weeks.findIndex((week) => week.id === context.weekId);
|
|
34
|
+
if (weekIndex === -1) {
|
|
35
|
+
throw new Error(`Routine ${routineId} is linked to unknown week ${context.weekId}.`);
|
|
36
|
+
}
|
|
37
|
+
const week = programDetail.weeks[weekIndex];
|
|
38
|
+
const routine = week.routines.find((entry) => entry.id === routineId);
|
|
39
|
+
if (!routine) {
|
|
40
|
+
throw new Error(`Routine ${routineId} was not found in program ${programDetail.program.name}.`);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
program: programDetail.program,
|
|
44
|
+
routine,
|
|
45
|
+
week: {
|
|
46
|
+
id: week.id,
|
|
47
|
+
number: weekIndex + 1,
|
|
48
|
+
},
|
|
49
|
+
workout,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function listRoutines(db, filters) {
|
|
53
|
+
let query = db
|
|
54
|
+
.selectFrom('ZROUTINE as r')
|
|
55
|
+
.leftJoin('ZPERIOD as p', 'p.Z_PK', 'r.ZPERIOD')
|
|
56
|
+
.leftJoin('ZWORKOUTPLAN as planDirect', 'planDirect.Z_PK', 'r.ZWORKOUTPLAN')
|
|
57
|
+
.leftJoin('ZWORKOUTPLAN as planFromPeriod', 'planFromPeriod.Z_PK', 'p.ZWORKOUTPLAN')
|
|
58
|
+
.select([
|
|
59
|
+
'r.Z_PK as id',
|
|
60
|
+
'r.ZNAME as name',
|
|
61
|
+
'r.ZUPNEXT as upNext',
|
|
62
|
+
'p.Z_PK as weekId',
|
|
63
|
+
'planDirect.Z_PK as programIdDirect',
|
|
64
|
+
'planFromPeriod.Z_PK as programIdFromPeriod',
|
|
65
|
+
'planDirect.ZNAME as programNameDirect',
|
|
66
|
+
'planFromPeriod.ZNAME as programNameFromPeriod',
|
|
67
|
+
])
|
|
68
|
+
.where('r.ZSOFTDELETED', 'is not', 1)
|
|
69
|
+
.where((eb) => eb.or([eb('planDirect.ZSOFTDELETED', 'is not', 1), eb('planFromPeriod.ZSOFTDELETED', 'is not', 1)]));
|
|
70
|
+
if (filters.program) {
|
|
71
|
+
const programId = await resolveProgramSelector(db, filters.program, false);
|
|
72
|
+
query = query.where((eb) => eb.or([eb('r.ZWORKOUTPLAN', '=', programId), eb('p.ZWORKOUTPLAN', '=', programId)]));
|
|
73
|
+
}
|
|
74
|
+
if (filters.name) {
|
|
75
|
+
const lowered = `%${filters.name.toLowerCase()}%`;
|
|
76
|
+
query = query.where(sql `lower(r.ZNAME) like ${lowered}`);
|
|
77
|
+
}
|
|
78
|
+
query = query.orderBy('planFromPeriod.ZDATEADDED', 'desc').orderBy('planDirect.ZDATEADDED', 'desc').orderBy('p.Z_FOK_WORKOUTPLAN', 'asc').orderBy('r.Z_FOK_PERIOD', 'asc').orderBy('r.Z_PK', 'asc');
|
|
79
|
+
const rows = await query.execute();
|
|
80
|
+
const weekNumbers = new Map();
|
|
81
|
+
const countsByProgram = new Map();
|
|
82
|
+
for (const row of rows) {
|
|
83
|
+
const programId = row.programIdDirect ?? row.programIdFromPeriod;
|
|
84
|
+
if (programId === null || row.weekId === null)
|
|
85
|
+
continue;
|
|
86
|
+
const key = row.weekId;
|
|
87
|
+
if (!weekNumbers.has(key)) {
|
|
88
|
+
const count = (countsByProgram.get(programId) ?? 0) + 1;
|
|
89
|
+
countsByProgram.set(programId, count);
|
|
90
|
+
weekNumbers.set(key, count);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return rows
|
|
94
|
+
.map((row) => ({
|
|
95
|
+
id: row.id,
|
|
96
|
+
isNext: row.upNext === 1,
|
|
97
|
+
name: row.name,
|
|
98
|
+
program: row.programNameDirect ?? row.programNameFromPeriod,
|
|
99
|
+
week: row.weekId === null ? null : weekNumbers.get(row.weekId) ?? null,
|
|
100
|
+
}))
|
|
101
|
+
.filter((row) => (filters.week === undefined ? true : row.week === filters.week));
|
|
102
|
+
}
|
|
103
|
+
export async function getRoutineDetail(db, selector) {
|
|
104
|
+
const routineId = await resolveIdOrName(db, 'ZROUTINE', selector);
|
|
105
|
+
return getRoutineDetailById(db, routineId, null);
|
|
106
|
+
}
|
|
107
|
+
export async function getNextRoutineDetail(db) {
|
|
108
|
+
const programId = await resolveProgramSelector(db, undefined, true);
|
|
109
|
+
const nextRoutines = await db
|
|
110
|
+
.selectFrom('ZROUTINE as r')
|
|
111
|
+
.leftJoin('ZPERIOD as p', 'p.Z_PK', 'r.ZPERIOD')
|
|
112
|
+
.select(['r.Z_PK as id'])
|
|
113
|
+
.where('r.ZUPNEXT', '=', 1)
|
|
114
|
+
.where('r.ZSOFTDELETED', 'is not', 1)
|
|
115
|
+
.where((eb) => eb.or([eb('p.ZWORKOUTPLAN', '=', programId), eb('r.ZWORKOUTPLAN', '=', programId)]))
|
|
116
|
+
.orderBy('p.Z_FOK_WORKOUTPLAN', 'asc')
|
|
117
|
+
.orderBy('r.Z_FOK_PERIOD', 'asc')
|
|
118
|
+
.orderBy('r.Z_PK', 'asc')
|
|
119
|
+
.execute();
|
|
120
|
+
if (nextRoutines.length === 0) {
|
|
121
|
+
const programDetail = await getProgramDetail(db, programId);
|
|
122
|
+
throw new Error(`No up-next routine found for active program ${programDetail.program.name}.`);
|
|
123
|
+
}
|
|
124
|
+
if (nextRoutines.length > 1) {
|
|
125
|
+
const programDetail = await getProgramDetail(db, programId);
|
|
126
|
+
throw new Error(`Expected exactly one up-next routine for active program ${programDetail.program.name}. Found ${nextRoutines.length}.`);
|
|
127
|
+
}
|
|
128
|
+
return getRoutineDetailById(db, nextRoutines[0].id, null);
|
|
129
|
+
}
|
|
130
|
+
export async function getRoutineDetailFromWorkout(db, workoutId) {
|
|
131
|
+
const workout = await db
|
|
132
|
+
.selectFrom('ZWORKOUTRESULT as wr')
|
|
133
|
+
.leftJoin('ZROUTINE as r', 'r.Z_PK', 'wr.ZROUTINE')
|
|
134
|
+
.leftJoin('ZPERIOD as p', 'p.Z_PK', 'r.ZPERIOD')
|
|
135
|
+
.leftJoin('ZWORKOUTPLAN as planDirect', 'planDirect.Z_PK', 'r.ZWORKOUTPLAN')
|
|
136
|
+
.leftJoin('ZWORKOUTPLAN as planFromPeriod', 'planFromPeriod.Z_PK', 'p.ZWORKOUTPLAN')
|
|
137
|
+
.select([
|
|
138
|
+
'wr.Z_PK as id',
|
|
139
|
+
'wr.ZDURATION as duration',
|
|
140
|
+
'wr.ZSTARTDATE as startDate',
|
|
141
|
+
'wr.ZROUTINE as routineId',
|
|
142
|
+
'wr.ZROUTINENAME as routineNameFromResult',
|
|
143
|
+
'r.ZNAME as routineNameFromPlan',
|
|
144
|
+
'planDirect.ZNAME as programNameDirect',
|
|
145
|
+
'planFromPeriod.ZNAME as programNameFromPeriod',
|
|
146
|
+
])
|
|
147
|
+
.where('wr.Z_PK', '=', workoutId)
|
|
148
|
+
.executeTakeFirst();
|
|
149
|
+
if (!workout)
|
|
150
|
+
throw new Error(`Workout not found: ${workoutId}`);
|
|
151
|
+
if (workout.routineId === null)
|
|
152
|
+
throw new Error(`Workout ${workoutId} is not linked to a planned routine.`);
|
|
153
|
+
return getRoutineDetailById(db, workout.routineId, {
|
|
154
|
+
date: appleSecondsToIso(workout.startDate),
|
|
155
|
+
duration: workout.duration,
|
|
156
|
+
id: workout.id,
|
|
157
|
+
program: workout.programNameDirect ?? workout.programNameFromPeriod,
|
|
158
|
+
routine: workout.routineNameFromResult ?? workout.routineNameFromPlan,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
export async function getLatestRoutineDetail(db) {
|
|
162
|
+
const latestWorkout = await db
|
|
163
|
+
.selectFrom('ZWORKOUTRESULT')
|
|
164
|
+
.select('Z_PK as id')
|
|
165
|
+
.orderBy('ZSTARTDATE', 'desc')
|
|
166
|
+
.limit(1)
|
|
167
|
+
.executeTakeFirst();
|
|
168
|
+
if (!latestWorkout)
|
|
169
|
+
throw new Error('No workouts found.');
|
|
170
|
+
return getRoutineDetailFromWorkout(db, latestWorkout.id);
|
|
171
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function buildRoutineRows(routine, unitLabel) {
|
|
2
|
+
return routine.exercises.flatMap((exercise) => {
|
|
3
|
+
const headingRow = {
|
|
4
|
+
exercise: `[${exercise.id ?? exercise.exerciseConfigId}] ${exercise.name ?? '(unnamed)'}`,
|
|
5
|
+
reps: null,
|
|
6
|
+
rpe: null,
|
|
7
|
+
timeSeconds: null,
|
|
8
|
+
weight: null,
|
|
9
|
+
};
|
|
10
|
+
const setRows = exercise.sets.map((set) => ({
|
|
11
|
+
exercise: '',
|
|
12
|
+
reps: set.reps,
|
|
13
|
+
rpe: set.rpe,
|
|
14
|
+
timeSeconds: set.timeSeconds,
|
|
15
|
+
weight: set.weight === null ? null : `${set.weight} ${unitLabel}`,
|
|
16
|
+
}));
|
|
17
|
+
return [headingRow, ...setRows];
|
|
18
|
+
});
|
|
19
|
+
}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -39,6 +39,23 @@ export type ProgramDetailTree = {
|
|
|
39
39
|
program: ProgramSummary;
|
|
40
40
|
weeks: ProgramWeek[];
|
|
41
41
|
};
|
|
42
|
+
export type RoutineWeekRef = {
|
|
43
|
+
id: number;
|
|
44
|
+
number: number;
|
|
45
|
+
};
|
|
46
|
+
export type RoutineSummary = {
|
|
47
|
+
id: number;
|
|
48
|
+
isNext: boolean;
|
|
49
|
+
name: null | string;
|
|
50
|
+
program: null | string;
|
|
51
|
+
week: null | number;
|
|
52
|
+
};
|
|
53
|
+
export type RoutineDetail = {
|
|
54
|
+
program: ProgramSummary;
|
|
55
|
+
routine: ProgramRoutine;
|
|
56
|
+
week: RoutineWeekRef;
|
|
57
|
+
workout: null | WorkoutSummary;
|
|
58
|
+
};
|
|
42
59
|
export type WorkoutSummary = {
|
|
43
60
|
date: null | string;
|
|
44
61
|
duration: null | number;
|
|
@@ -69,14 +86,7 @@ export type WorkoutDetail = {
|
|
|
69
86
|
program: null | string;
|
|
70
87
|
routine: null | string;
|
|
71
88
|
};
|
|
72
|
-
export type NextWorkoutDetail =
|
|
73
|
-
program: ProgramSummary;
|
|
74
|
-
routine: ProgramRoutine;
|
|
75
|
-
week: {
|
|
76
|
-
id: number;
|
|
77
|
-
number: number;
|
|
78
|
-
};
|
|
79
|
-
};
|
|
89
|
+
export type NextWorkoutDetail = RoutineDetail;
|
|
80
90
|
export type ExerciseSummary = {
|
|
81
91
|
equipment: null | string;
|
|
82
92
|
id: number;
|
package/oclif.manifest.json
CHANGED
|
@@ -241,6 +241,184 @@
|
|
|
241
241
|
"show.js"
|
|
242
242
|
]
|
|
243
243
|
},
|
|
244
|
+
"routines:from-workout": {
|
|
245
|
+
"aliases": [],
|
|
246
|
+
"args": {
|
|
247
|
+
"workoutId": {
|
|
248
|
+
"description": "workout id (default: latest workout)",
|
|
249
|
+
"name": "workoutId",
|
|
250
|
+
"required": false
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"description": "Show the planned routine for a completed workout",
|
|
254
|
+
"flags": {
|
|
255
|
+
"json": {
|
|
256
|
+
"description": "Format output as json.",
|
|
257
|
+
"helpGroup": "GLOBAL",
|
|
258
|
+
"name": "json",
|
|
259
|
+
"allowNo": false,
|
|
260
|
+
"type": "boolean"
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"hasDynamicHelp": false,
|
|
264
|
+
"hiddenAliases": [],
|
|
265
|
+
"id": "routines:from-workout",
|
|
266
|
+
"pluginAlias": "@nickchristensen/cliftin",
|
|
267
|
+
"pluginName": "@nickchristensen/cliftin",
|
|
268
|
+
"pluginType": "core",
|
|
269
|
+
"strict": true,
|
|
270
|
+
"enableJsonFlag": true,
|
|
271
|
+
"isESM": true,
|
|
272
|
+
"relativePath": [
|
|
273
|
+
"dist",
|
|
274
|
+
"commands",
|
|
275
|
+
"routines",
|
|
276
|
+
"from-workout.js"
|
|
277
|
+
]
|
|
278
|
+
},
|
|
279
|
+
"routines:latest": {
|
|
280
|
+
"aliases": [],
|
|
281
|
+
"args": {},
|
|
282
|
+
"description": "Show the planned routine for the latest workout",
|
|
283
|
+
"flags": {
|
|
284
|
+
"json": {
|
|
285
|
+
"description": "Format output as json.",
|
|
286
|
+
"helpGroup": "GLOBAL",
|
|
287
|
+
"name": "json",
|
|
288
|
+
"allowNo": false,
|
|
289
|
+
"type": "boolean"
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
"hasDynamicHelp": false,
|
|
293
|
+
"hiddenAliases": [],
|
|
294
|
+
"id": "routines:latest",
|
|
295
|
+
"pluginAlias": "@nickchristensen/cliftin",
|
|
296
|
+
"pluginName": "@nickchristensen/cliftin",
|
|
297
|
+
"pluginType": "core",
|
|
298
|
+
"strict": true,
|
|
299
|
+
"enableJsonFlag": true,
|
|
300
|
+
"isESM": true,
|
|
301
|
+
"relativePath": [
|
|
302
|
+
"dist",
|
|
303
|
+
"commands",
|
|
304
|
+
"routines",
|
|
305
|
+
"latest.js"
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
"routines:list": {
|
|
309
|
+
"aliases": [],
|
|
310
|
+
"args": {},
|
|
311
|
+
"description": "List planned routines",
|
|
312
|
+
"flags": {
|
|
313
|
+
"json": {
|
|
314
|
+
"description": "Format output as json.",
|
|
315
|
+
"helpGroup": "GLOBAL",
|
|
316
|
+
"name": "json",
|
|
317
|
+
"allowNo": false,
|
|
318
|
+
"type": "boolean"
|
|
319
|
+
},
|
|
320
|
+
"name": {
|
|
321
|
+
"description": "Filter by routine name contains",
|
|
322
|
+
"name": "name",
|
|
323
|
+
"hasDynamicHelp": false,
|
|
324
|
+
"multiple": false,
|
|
325
|
+
"type": "option"
|
|
326
|
+
},
|
|
327
|
+
"program": {
|
|
328
|
+
"description": "Filter by program id or name",
|
|
329
|
+
"name": "program",
|
|
330
|
+
"hasDynamicHelp": false,
|
|
331
|
+
"multiple": false,
|
|
332
|
+
"type": "option"
|
|
333
|
+
},
|
|
334
|
+
"week": {
|
|
335
|
+
"description": "Filter by week number",
|
|
336
|
+
"name": "week",
|
|
337
|
+
"hasDynamicHelp": false,
|
|
338
|
+
"multiple": false,
|
|
339
|
+
"type": "option"
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
"hasDynamicHelp": false,
|
|
343
|
+
"hiddenAliases": [],
|
|
344
|
+
"id": "routines:list",
|
|
345
|
+
"pluginAlias": "@nickchristensen/cliftin",
|
|
346
|
+
"pluginName": "@nickchristensen/cliftin",
|
|
347
|
+
"pluginType": "core",
|
|
348
|
+
"strict": true,
|
|
349
|
+
"enableJsonFlag": true,
|
|
350
|
+
"isESM": true,
|
|
351
|
+
"relativePath": [
|
|
352
|
+
"dist",
|
|
353
|
+
"commands",
|
|
354
|
+
"routines",
|
|
355
|
+
"list.js"
|
|
356
|
+
]
|
|
357
|
+
},
|
|
358
|
+
"routines:next": {
|
|
359
|
+
"aliases": [],
|
|
360
|
+
"args": {},
|
|
361
|
+
"description": "Show the up-next routine from the active program",
|
|
362
|
+
"flags": {
|
|
363
|
+
"json": {
|
|
364
|
+
"description": "Format output as json.",
|
|
365
|
+
"helpGroup": "GLOBAL",
|
|
366
|
+
"name": "json",
|
|
367
|
+
"allowNo": false,
|
|
368
|
+
"type": "boolean"
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
"hasDynamicHelp": false,
|
|
372
|
+
"hiddenAliases": [],
|
|
373
|
+
"id": "routines:next",
|
|
374
|
+
"pluginAlias": "@nickchristensen/cliftin",
|
|
375
|
+
"pluginName": "@nickchristensen/cliftin",
|
|
376
|
+
"pluginType": "core",
|
|
377
|
+
"strict": true,
|
|
378
|
+
"enableJsonFlag": true,
|
|
379
|
+
"isESM": true,
|
|
380
|
+
"relativePath": [
|
|
381
|
+
"dist",
|
|
382
|
+
"commands",
|
|
383
|
+
"routines",
|
|
384
|
+
"next.js"
|
|
385
|
+
]
|
|
386
|
+
},
|
|
387
|
+
"routines:show": {
|
|
388
|
+
"aliases": [],
|
|
389
|
+
"args": {
|
|
390
|
+
"selector": {
|
|
391
|
+
"description": "routine id or name",
|
|
392
|
+
"name": "selector",
|
|
393
|
+
"required": true
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
"description": "Show one planned routine",
|
|
397
|
+
"flags": {
|
|
398
|
+
"json": {
|
|
399
|
+
"description": "Format output as json.",
|
|
400
|
+
"helpGroup": "GLOBAL",
|
|
401
|
+
"name": "json",
|
|
402
|
+
"allowNo": false,
|
|
403
|
+
"type": "boolean"
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
"hasDynamicHelp": false,
|
|
407
|
+
"hiddenAliases": [],
|
|
408
|
+
"id": "routines:show",
|
|
409
|
+
"pluginAlias": "@nickchristensen/cliftin",
|
|
410
|
+
"pluginName": "@nickchristensen/cliftin",
|
|
411
|
+
"pluginType": "core",
|
|
412
|
+
"strict": true,
|
|
413
|
+
"enableJsonFlag": true,
|
|
414
|
+
"isESM": true,
|
|
415
|
+
"relativePath": [
|
|
416
|
+
"dist",
|
|
417
|
+
"commands",
|
|
418
|
+
"routines",
|
|
419
|
+
"show.js"
|
|
420
|
+
]
|
|
421
|
+
},
|
|
244
422
|
"workouts:list": {
|
|
245
423
|
"aliases": [],
|
|
246
424
|
"args": {},
|
|
@@ -337,7 +515,7 @@
|
|
|
337
515
|
"workouts:next": {
|
|
338
516
|
"aliases": [],
|
|
339
517
|
"args": {},
|
|
340
|
-
"description": "
|
|
518
|
+
"description": "Redirect to routines next",
|
|
341
519
|
"flags": {
|
|
342
520
|
"json": {
|
|
343
521
|
"description": "Format output as json.",
|
|
@@ -399,5 +577,5 @@
|
|
|
399
577
|
]
|
|
400
578
|
}
|
|
401
579
|
},
|
|
402
|
-
"version": "
|
|
580
|
+
"version": "4.0.0"
|
|
403
581
|
}
|
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": "
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"author": "Nick Christensen",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cliftin": "./bin/run.js"
|
|
@@ -71,6 +71,9 @@
|
|
|
71
71
|
"programs": {
|
|
72
72
|
"description": "Program hierarchy and planning"
|
|
73
73
|
},
|
|
74
|
+
"routines": {
|
|
75
|
+
"description": "Planned routine detail and navigation"
|
|
76
|
+
},
|
|
74
77
|
"workouts": {
|
|
75
78
|
"description": "Workout history and detail"
|
|
76
79
|
}
|