@ivotoby/postgram-cli 1.40.0 → 1.40.1
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/client.js +3 -0
- package/dist/pgm.js +1 -0
- package/dist/tracking/feature-commands.js +4 -1
- package/dist/tracking/feature-sync.js +10 -2
- package/dist/tracking/here.js +7 -0
- package/dist/tracking/scan.js +15 -5
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -120,6 +120,9 @@ export function createPgmClient(options) {
|
|
|
120
120
|
},
|
|
121
121
|
listTasks(input = {}) {
|
|
122
122
|
const params = new URLSearchParams();
|
|
123
|
+
if (input.kind) {
|
|
124
|
+
params.set('kind', input.kind);
|
|
125
|
+
}
|
|
123
126
|
if (input.status) {
|
|
124
127
|
params.set('status', input.status);
|
|
125
128
|
}
|
package/dist/pgm.js
CHANGED
|
@@ -616,6 +616,7 @@ taskCommand
|
|
|
616
616
|
await runWithClient(command, async (client, json) => {
|
|
617
617
|
validateToonOptions(options, json);
|
|
618
618
|
const body = await client.listTasks({
|
|
619
|
+
kind: 'task',
|
|
619
620
|
status: options.status,
|
|
620
621
|
context: options.context,
|
|
621
622
|
limit: Number(options.limit),
|
|
@@ -40,7 +40,7 @@ async function resolveTaskId(id, deps) {
|
|
|
40
40
|
for (let offset = 0;; offset += 100) {
|
|
41
41
|
const page = await deps.pgm.listTasks({ limit: 100, offset });
|
|
42
42
|
for (const item of page.items) {
|
|
43
|
-
if (item.id.startsWith(prefix)) {
|
|
43
|
+
if (item.id.startsWith(prefix) && item.metadata.kind === 'feature') {
|
|
44
44
|
matches.push(item.id);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -64,6 +64,9 @@ async function resolveTaskId(id, deps) {
|
|
|
64
64
|
export async function runFeatureDone(id, deps) {
|
|
65
65
|
const taskId = await resolveTaskId(id, deps);
|
|
66
66
|
const current = await deps.pgm.recallEntity(taskId);
|
|
67
|
+
if (current.entity.metadata.kind !== 'feature') {
|
|
68
|
+
throw new UsageError(`${shortId(taskId)} is not a feature`);
|
|
69
|
+
}
|
|
67
70
|
if (current.entity.status === 'done') {
|
|
68
71
|
return deps.json
|
|
69
72
|
? { id: taskId, status: 'done', changed: false }
|
|
@@ -3,6 +3,8 @@ import { execFile } from 'node:child_process';
|
|
|
3
3
|
import { AppError, ErrorCode } from '../errors.js';
|
|
4
4
|
import { shortId } from '../shared.js';
|
|
5
5
|
import { UsageError } from './config.js';
|
|
6
|
+
const FULL_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7
|
+
const SHORT_HEX = /^[0-9a-f]{8}$/i;
|
|
6
8
|
const GH_STATES = {
|
|
7
9
|
OPEN: 'open',
|
|
8
10
|
MERGED: 'merged',
|
|
@@ -78,10 +80,16 @@ export async function runFeatureSync(id, deps) {
|
|
|
78
80
|
let features = collectFeatures(response);
|
|
79
81
|
if (id !== undefined) {
|
|
80
82
|
const wanted = id.toLowerCase();
|
|
81
|
-
|
|
83
|
+
if (!FULL_UUID.test(wanted) && !SHORT_HEX.test(wanted)) {
|
|
84
|
+
throw new UsageError('feature id must be a full UUID or an 8-hex short id');
|
|
85
|
+
}
|
|
86
|
+
features = features.filter((feature) => FULL_UUID.test(wanted) ? feature.id === wanted : feature.id.startsWith(wanted));
|
|
82
87
|
if (features.length === 0) {
|
|
83
88
|
throw new AppError(ErrorCode.NOT_FOUND, `no feature matches ${id}`);
|
|
84
89
|
}
|
|
90
|
+
if (features.length > 1) {
|
|
91
|
+
throw new AppError(ErrorCode.CONFLICT, `feature id ${id} is ambiguous`);
|
|
92
|
+
}
|
|
85
93
|
}
|
|
86
94
|
const result = { checked: 0, unchanged: 0, changed: [], errors: [] };
|
|
87
95
|
const answers = new Map();
|
|
@@ -146,6 +154,6 @@ export function formatSync(result) {
|
|
|
146
154
|
for (const error of result.errors) {
|
|
147
155
|
lines.push(`error ${shortId(error.feature_id)} ${error.url}: ${error.error}`);
|
|
148
156
|
}
|
|
149
|
-
lines.push(`checked ${result.checked} pull requests: ${result.changed.length} changed, ${result.unchanged} unchanged, ${result.errors.length} failed`);
|
|
157
|
+
lines.push(`checked ${result.checked} pull ${result.checked === 1 ? 'request' : 'requests'}: ${result.changed.length} changed, ${result.unchanged} unchanged, ${result.errors.length} failed`);
|
|
150
158
|
return lines;
|
|
151
159
|
}
|
package/dist/tracking/here.js
CHANGED
|
@@ -149,6 +149,13 @@ export async function runHere(options, deps) {
|
|
|
149
149
|
if (isIgnoredPath(local.path, config.ignorePaths)) {
|
|
150
150
|
return ignoredResult(options, local.path);
|
|
151
151
|
}
|
|
152
|
+
// A directory outside git holds no project code, so it is never a workspace.
|
|
153
|
+
if (local.repoRoot === null) {
|
|
154
|
+
return {
|
|
155
|
+
stdout: options.hook ? '' : `skipped ${local.path} (not a git repository)\n`,
|
|
156
|
+
exitCode: 0
|
|
157
|
+
};
|
|
158
|
+
}
|
|
152
159
|
const marker = await deps.readMarker(local.path);
|
|
153
160
|
const bootId = await deps.bootId();
|
|
154
161
|
const session = sessionId !== null || event !== null
|
package/dist/tracking/scan.js
CHANGED
|
@@ -11,6 +11,16 @@ import { readMarker, resolveLocalWorkspace, runGit, writeMarker } from './local.
|
|
|
11
11
|
import { discoverWorktrees, findClaudeSessions, findPiSessions } from './scan-discovery.js';
|
|
12
12
|
import { syncMissing } from './where.js';
|
|
13
13
|
export const BRANCH_SHORT_ID = /(?:^|\/)([0-9a-f]{8})(?:-|$)/;
|
|
14
|
+
/** Keeps the feature tasks of a task listing, so plain tasks are never offered or linked. */
|
|
15
|
+
export function featureTasks(items) {
|
|
16
|
+
return items
|
|
17
|
+
.filter((item) => item.metadata.kind === 'feature')
|
|
18
|
+
.map((item) => ({
|
|
19
|
+
id: item.id,
|
|
20
|
+
title: firstLineTitle(item.content),
|
|
21
|
+
done: item.status === 'done'
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
14
24
|
const DEFAULT_DEPTH = 3;
|
|
15
25
|
function parseDepth(raw) {
|
|
16
26
|
if (raw === undefined) {
|
|
@@ -57,7 +67,9 @@ async function buildProposals(worktree, deps, tasks, yesExact) {
|
|
|
57
67
|
catch {
|
|
58
68
|
hits = [];
|
|
59
69
|
}
|
|
60
|
-
|
|
70
|
+
// Search covers every task; only open features are worth proposing.
|
|
71
|
+
const open = new Set(tasks.filter((task) => task.done !== true).map((task) => task.id));
|
|
72
|
+
for (const hit of hits.filter((h) => open.has(h.id)).slice(0, 3)) {
|
|
61
73
|
proposals.push({ kind: 'search', featureId: hit.id, title: hit.title });
|
|
62
74
|
}
|
|
63
75
|
}
|
|
@@ -519,9 +531,7 @@ export function registerScanCommand(program) {
|
|
|
519
531
|
const items = [];
|
|
520
532
|
for (let offset = 0;; offset += 100) {
|
|
521
533
|
const page = await context.pgm.listTasks({ limit: 100, offset });
|
|
522
|
-
|
|
523
|
-
items.push({ id: item.id, title: firstLineTitle(item.content) });
|
|
524
|
-
}
|
|
534
|
+
items.push(...featureTasks(page.items));
|
|
525
535
|
if (page.items.length < 100) {
|
|
526
536
|
break;
|
|
527
537
|
}
|
|
@@ -532,7 +542,7 @@ export function registerScanCommand(program) {
|
|
|
532
542
|
const response = await context.pgm.searchEntities({
|
|
533
543
|
query,
|
|
534
544
|
type: 'task',
|
|
535
|
-
limit:
|
|
545
|
+
limit: 10
|
|
536
546
|
});
|
|
537
547
|
return response.results.map((result) => ({
|
|
538
548
|
id: result.entity.id,
|