@phren/cli 0.1.13 → 0.1.14

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.
Files changed (34) hide show
  1. package/dist/cli/hooks-session.d.ts +18 -36
  2. package/dist/cli/hooks-session.js +21 -1482
  3. package/dist/cli/namespaces-findings.d.ts +1 -0
  4. package/dist/cli/namespaces-findings.js +208 -0
  5. package/dist/cli/namespaces-profile.d.ts +1 -0
  6. package/dist/cli/namespaces-profile.js +76 -0
  7. package/dist/cli/namespaces-projects.d.ts +1 -0
  8. package/dist/cli/namespaces-projects.js +370 -0
  9. package/dist/cli/namespaces-review.d.ts +1 -0
  10. package/dist/cli/namespaces-review.js +45 -0
  11. package/dist/cli/namespaces-skills.d.ts +4 -0
  12. package/dist/cli/namespaces-skills.js +550 -0
  13. package/dist/cli/namespaces-store.d.ts +2 -0
  14. package/dist/cli/namespaces-store.js +367 -0
  15. package/dist/cli/namespaces-tasks.d.ts +1 -0
  16. package/dist/cli/namespaces-tasks.js +369 -0
  17. package/dist/cli/namespaces-utils.d.ts +4 -0
  18. package/dist/cli/namespaces-utils.js +47 -0
  19. package/dist/cli/namespaces.d.ts +7 -11
  20. package/dist/cli/namespaces.js +8 -2011
  21. package/dist/cli/session-background.d.ts +3 -0
  22. package/dist/cli/session-background.js +176 -0
  23. package/dist/cli/session-git.d.ts +17 -0
  24. package/dist/cli/session-git.js +181 -0
  25. package/dist/cli/session-metrics.d.ts +2 -0
  26. package/dist/cli/session-metrics.js +67 -0
  27. package/dist/cli/session-start.d.ts +3 -0
  28. package/dist/cli/session-start.js +289 -0
  29. package/dist/cli/session-stop.d.ts +8 -0
  30. package/dist/cli/session-stop.js +468 -0
  31. package/dist/cli/session-tool-hook.d.ts +18 -0
  32. package/dist/cli/session-tool-hook.js +376 -0
  33. package/dist/tools/search.js +1 -1
  34. package/package.json +1 -1
@@ -0,0 +1,369 @@
1
+ import { getPhrenPath } from "../shared.js";
2
+ import { addTask, completeTask, updateTask, reorderTask, pinTask, removeTask, workNextTask, tidyDoneTasks, linkTaskIssue, promoteTask, resolveTaskItem } from "../data/tasks.js";
3
+ import { buildTaskIssueBody, createGithubIssueForTask, parseGithubIssueUrl, resolveProjectGithubRepo } from "../task/github.js";
4
+ function printTaskUsage() {
5
+ console.log("Usage:");
6
+ console.log(' phren task add <project> "<text>"');
7
+ console.log(' phren task complete <project> "<text>"');
8
+ console.log(' phren task remove <project> "<text>"');
9
+ console.log(' phren task next [project]');
10
+ console.log(' phren task promote <project> "<text>" [--active]');
11
+ console.log(' phren task tidy [project] [--keep=<n>] [--dry-run]');
12
+ console.log(' phren task link <project> "<text>" --issue <number> [--url <url>]');
13
+ console.log(' phren task link <project> "<text>" --unlink');
14
+ console.log(' phren task create-issue <project> "<text>" [--repo <owner/name>] [--title "<title>"] [--done]');
15
+ console.log(' phren task update <project> "<text>" [--priority=high|medium|low] [--section=Active|Queue|Done] [--context="..."]');
16
+ console.log(' phren task pin <project> "<text>"');
17
+ console.log(' phren task reorder <project> "<text>" --rank=<n>');
18
+ }
19
+ export async function handleTaskNamespace(args) {
20
+ const subcommand = args[0];
21
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
22
+ printTaskUsage();
23
+ return;
24
+ }
25
+ if (subcommand === "list") {
26
+ // Delegate to the cross-project task view (same as `phren tasks`)
27
+ const { handleTaskView } = await import("./ops.js");
28
+ return handleTaskView(args[1] || "default");
29
+ }
30
+ if (subcommand === "add") {
31
+ const project = args[1];
32
+ const text = args.slice(2).join(" ");
33
+ if (!project || !text) {
34
+ console.error('Usage: phren task add <project> "<text>"');
35
+ process.exit(1);
36
+ }
37
+ const result = addTask(getPhrenPath(), project, text);
38
+ if (!result.ok) {
39
+ console.error(result.error);
40
+ process.exit(1);
41
+ }
42
+ console.log(`Task added: ${result.data.line}`);
43
+ return;
44
+ }
45
+ if (subcommand === "complete") {
46
+ const project = args[1];
47
+ const match = args.slice(2).join(" ");
48
+ if (!project || !match) {
49
+ console.error('Usage: phren task complete <project> "<text>"');
50
+ process.exit(1);
51
+ }
52
+ const result = completeTask(getPhrenPath(), project, match);
53
+ if (!result.ok) {
54
+ console.error(result.error);
55
+ process.exit(1);
56
+ }
57
+ console.log(result.data);
58
+ return;
59
+ }
60
+ if (subcommand === "update") {
61
+ const project = args[1];
62
+ if (!project) {
63
+ printTaskUsage();
64
+ process.exit(1);
65
+ }
66
+ // Collect non-flag args as the match text, flags as updates
67
+ const positional = [];
68
+ const updates = {};
69
+ for (const arg of args.slice(2)) {
70
+ if (arg.startsWith("--priority=")) {
71
+ updates.priority = arg.slice("--priority=".length);
72
+ }
73
+ else if (arg.startsWith("--section=")) {
74
+ updates.section = arg.slice("--section=".length);
75
+ }
76
+ else if (arg.startsWith("--context=")) {
77
+ updates.context = arg.slice("--context=".length);
78
+ }
79
+ else if (!arg.startsWith("--")) {
80
+ positional.push(arg);
81
+ }
82
+ }
83
+ const match = positional.join(" ");
84
+ if (!match) {
85
+ printTaskUsage();
86
+ process.exit(1);
87
+ }
88
+ const result = updateTask(getPhrenPath(), project, match, updates);
89
+ if (!result.ok) {
90
+ console.error(result.error);
91
+ process.exit(1);
92
+ }
93
+ console.log(result.data);
94
+ return;
95
+ }
96
+ if (subcommand === "pin") {
97
+ const project = args[1];
98
+ const match = args.slice(2).join(" ");
99
+ if (!project || !match) {
100
+ console.error('Usage: phren task pin <project> "<text>"');
101
+ process.exit(1);
102
+ }
103
+ const result = pinTask(getPhrenPath(), project, match);
104
+ if (!result.ok) {
105
+ console.error(result.error);
106
+ process.exit(1);
107
+ }
108
+ console.log(result.data);
109
+ return;
110
+ }
111
+ if (subcommand === "reorder") {
112
+ const project = args[1];
113
+ if (!project) {
114
+ printTaskUsage();
115
+ process.exit(1);
116
+ }
117
+ const positional = [];
118
+ let rankArg;
119
+ for (const arg of args.slice(2)) {
120
+ if (arg.startsWith("--rank=")) {
121
+ rankArg = arg.slice("--rank=".length);
122
+ }
123
+ else if (!arg.startsWith("--")) {
124
+ positional.push(arg);
125
+ }
126
+ }
127
+ const match = positional.join(" ");
128
+ const rank = rankArg ? Number.parseInt(rankArg, 10) : Number.NaN;
129
+ if (!match || !rankArg || !Number.isFinite(rank) || rank < 1) {
130
+ console.error('Usage: phren task reorder <project> "<text>" --rank=<n>');
131
+ process.exit(1);
132
+ }
133
+ const result = reorderTask(getPhrenPath(), project, match, rank);
134
+ if (!result.ok) {
135
+ console.error(result.error);
136
+ process.exit(1);
137
+ }
138
+ console.log(result.data);
139
+ return;
140
+ }
141
+ if (subcommand === "remove") {
142
+ const project = args[1];
143
+ const match = args.slice(2).join(" ");
144
+ if (!project || !match) {
145
+ console.error('Usage: phren task remove <project> "<text>"');
146
+ process.exit(1);
147
+ }
148
+ const result = removeTask(getPhrenPath(), project, match);
149
+ if (!result.ok) {
150
+ console.error(result.error);
151
+ process.exit(1);
152
+ }
153
+ console.log(result.data);
154
+ return;
155
+ }
156
+ if (subcommand === "next") {
157
+ const project = args[1];
158
+ if (!project) {
159
+ console.error("Usage: phren task next <project>");
160
+ process.exit(1);
161
+ }
162
+ const result = workNextTask(getPhrenPath(), project);
163
+ if (!result.ok) {
164
+ console.error(result.error);
165
+ process.exit(1);
166
+ }
167
+ console.log(result.data);
168
+ return;
169
+ }
170
+ if (subcommand === "promote") {
171
+ const project = args[1];
172
+ if (!project) {
173
+ printTaskUsage();
174
+ process.exit(1);
175
+ }
176
+ const positional = [];
177
+ let moveToActive = false;
178
+ for (const arg of args.slice(2)) {
179
+ if (arg === "--active") {
180
+ moveToActive = true;
181
+ }
182
+ else if (!arg.startsWith("--")) {
183
+ positional.push(arg);
184
+ }
185
+ }
186
+ const match = positional.join(" ");
187
+ if (!match) {
188
+ console.error('Usage: phren task promote <project> "<text>" [--active]');
189
+ process.exit(1);
190
+ }
191
+ const result = promoteTask(getPhrenPath(), project, match, moveToActive);
192
+ if (!result.ok) {
193
+ console.error(result.error);
194
+ process.exit(1);
195
+ }
196
+ console.log(`Promoted task "${result.data.line}" in ${project}${moveToActive ? " (moved to Active)" : ""}.`);
197
+ return;
198
+ }
199
+ if (subcommand === "tidy") {
200
+ const project = args[1];
201
+ if (!project) {
202
+ console.error("Usage: phren task tidy <project> [--keep=<n>] [--dry-run]");
203
+ process.exit(1);
204
+ }
205
+ let keep = 30;
206
+ let dryRun = false;
207
+ for (const arg of args.slice(2)) {
208
+ if (arg.startsWith("--keep=")) {
209
+ const n = Number.parseInt(arg.slice("--keep=".length), 10);
210
+ if (Number.isFinite(n) && n > 0)
211
+ keep = n;
212
+ }
213
+ else if (arg === "--dry-run") {
214
+ dryRun = true;
215
+ }
216
+ }
217
+ const result = tidyDoneTasks(getPhrenPath(), project, keep, dryRun);
218
+ if (!result.ok) {
219
+ console.error(result.error);
220
+ process.exit(1);
221
+ }
222
+ console.log(result.data);
223
+ return;
224
+ }
225
+ if (subcommand === "link") {
226
+ const project = args[1];
227
+ if (!project) {
228
+ printTaskUsage();
229
+ process.exit(1);
230
+ }
231
+ const positional = [];
232
+ let issueArg;
233
+ let urlArg;
234
+ let unlink = false;
235
+ const rest = args.slice(2);
236
+ for (let i = 0; i < rest.length; i++) {
237
+ const arg = rest[i];
238
+ if (arg === "--issue" || arg === "-i") {
239
+ issueArg = rest[++i];
240
+ }
241
+ else if (arg.startsWith("--issue=")) {
242
+ issueArg = arg.slice("--issue=".length);
243
+ }
244
+ else if (arg === "--url") {
245
+ urlArg = rest[++i];
246
+ }
247
+ else if (arg.startsWith("--url=")) {
248
+ urlArg = arg.slice("--url=".length);
249
+ }
250
+ else if (arg === "--unlink") {
251
+ unlink = true;
252
+ }
253
+ else if (!arg.startsWith("--")) {
254
+ positional.push(arg);
255
+ }
256
+ }
257
+ const match = positional.join(" ");
258
+ if (!match) {
259
+ console.error('Usage: phren task link <project> "<text>" --issue <number>');
260
+ process.exit(1);
261
+ }
262
+ if (!unlink && !issueArg && !urlArg) {
263
+ console.error("Provide --issue <number> or --url <url> to link, or --unlink to remove the link.");
264
+ process.exit(1);
265
+ }
266
+ if (urlArg) {
267
+ const parsed = parseGithubIssueUrl(urlArg);
268
+ if (!parsed) {
269
+ console.error("--url must be a valid GitHub issue URL.");
270
+ process.exit(1);
271
+ }
272
+ }
273
+ const result = linkTaskIssue(getPhrenPath(), project, match, {
274
+ github_issue: issueArg,
275
+ github_url: urlArg,
276
+ unlink: unlink,
277
+ });
278
+ if (!result.ok) {
279
+ console.error(result.error);
280
+ process.exit(1);
281
+ }
282
+ if (unlink) {
283
+ console.log(`Removed GitHub link from ${project} task.`);
284
+ }
285
+ else {
286
+ console.log(`Linked ${project} task to ${result.data.githubIssue ? `#${result.data.githubIssue}` : result.data.githubUrl}.`);
287
+ }
288
+ return;
289
+ }
290
+ if (subcommand === "create-issue") {
291
+ const project = args[1];
292
+ if (!project) {
293
+ printTaskUsage();
294
+ process.exit(1);
295
+ }
296
+ const positional = [];
297
+ let repoArg;
298
+ let titleArg;
299
+ let markDone = false;
300
+ const rest = args.slice(2);
301
+ for (let i = 0; i < rest.length; i++) {
302
+ const arg = rest[i];
303
+ if (arg === "--repo") {
304
+ repoArg = rest[++i];
305
+ }
306
+ else if (arg.startsWith("--repo=")) {
307
+ repoArg = arg.slice("--repo=".length);
308
+ }
309
+ else if (arg === "--title") {
310
+ titleArg = rest[++i];
311
+ }
312
+ else if (arg.startsWith("--title=")) {
313
+ titleArg = arg.slice("--title=".length);
314
+ }
315
+ else if (arg === "--done") {
316
+ markDone = true;
317
+ }
318
+ else if (!arg.startsWith("--")) {
319
+ positional.push(arg);
320
+ }
321
+ }
322
+ const match = positional.join(" ");
323
+ if (!match) {
324
+ console.error('Usage: phren task create-issue <project> "<text>" [--repo <owner/name>] [--title "<title>"] [--done]');
325
+ process.exit(1);
326
+ }
327
+ const phrenPath = getPhrenPath();
328
+ const resolved = resolveTaskItem(phrenPath, project, match);
329
+ if (!resolved.ok) {
330
+ console.error(resolved.error);
331
+ process.exit(1);
332
+ }
333
+ const targetRepo = repoArg || resolveProjectGithubRepo(phrenPath, project);
334
+ if (!targetRepo) {
335
+ console.error("Could not infer a GitHub repo. Provide --repo <owner/name> or add a GitHub URL to CLAUDE.md/summary.md.");
336
+ process.exit(1);
337
+ }
338
+ const created = createGithubIssueForTask({
339
+ repo: targetRepo,
340
+ title: titleArg?.trim() || resolved.data.line.replace(/\s*\[(high|medium|low)\]\s*$/i, "").trim(),
341
+ body: buildTaskIssueBody(project, resolved.data),
342
+ });
343
+ if (!created.ok) {
344
+ console.error(created.error);
345
+ process.exit(1);
346
+ }
347
+ const linked = linkTaskIssue(phrenPath, project, resolved.data.stableId ? `bid:${resolved.data.stableId}` : resolved.data.id, {
348
+ github_issue: created.data.issueNumber,
349
+ github_url: created.data.url,
350
+ });
351
+ if (!linked.ok) {
352
+ console.error(linked.error);
353
+ process.exit(1);
354
+ }
355
+ if (markDone) {
356
+ const completionMatch = linked.data.stableId ? `bid:${linked.data.stableId}` : linked.data.id;
357
+ const completed = completeTask(phrenPath, project, completionMatch);
358
+ if (!completed.ok) {
359
+ console.error(completed.error);
360
+ process.exit(1);
361
+ }
362
+ }
363
+ console.log(`Created GitHub issue ${created.data.issueNumber ? `#${created.data.issueNumber}` : created.data.url} for ${project} task.`);
364
+ return;
365
+ }
366
+ console.error(`Unknown task subcommand: ${subcommand}`);
367
+ printTaskUsage();
368
+ process.exit(1);
369
+ }
@@ -0,0 +1,4 @@
1
+ export declare function resolveProjectStorePath(phrenPath: string, project: string): string;
2
+ export declare function getOptionValue(args: string[], name: string): string | undefined;
3
+ export declare function parseMcpToggle(raw: string | undefined): boolean | undefined;
4
+ export declare function openInEditor(filePath: string): void;
@@ -0,0 +1,47 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { execFileSync } from "child_process";
4
+ import { errorMessage } from "../utils.js";
5
+ import { logger } from "../logger.js";
6
+ export function resolveProjectStorePath(phrenPath, project) {
7
+ try {
8
+ const { getNonPrimaryStores } = require("../store-registry.js");
9
+ if (fs.existsSync(path.join(phrenPath, project)))
10
+ return phrenPath;
11
+ for (const store of getNonPrimaryStores(phrenPath)) {
12
+ if (fs.existsSync(path.join(store.path, project)))
13
+ return store.path;
14
+ }
15
+ }
16
+ catch { /* fall through */ }
17
+ return phrenPath;
18
+ }
19
+ export function getOptionValue(args, name) {
20
+ const exactIdx = args.indexOf(name);
21
+ if (exactIdx !== -1)
22
+ return args[exactIdx + 1];
23
+ const prefixed = args.find((arg) => arg.startsWith(`${name}=`));
24
+ return prefixed ? prefixed.slice(name.length + 1) : undefined;
25
+ }
26
+ export function parseMcpToggle(raw) {
27
+ if (!raw)
28
+ return undefined;
29
+ const normalized = raw.trim().toLowerCase();
30
+ if (normalized === "on" || normalized === "true" || normalized === "enabled")
31
+ return true;
32
+ if (normalized === "off" || normalized === "false" || normalized === "disabled")
33
+ return false;
34
+ return undefined;
35
+ }
36
+ export function openInEditor(filePath) {
37
+ const editor = process.env.EDITOR || process.env.VISUAL || "nano";
38
+ try {
39
+ execFileSync(editor, [filePath], { stdio: "inherit" });
40
+ }
41
+ catch (err) {
42
+ if ((process.env.PHREN_DEBUG))
43
+ logger.debug("cli-namespaces", `openInEditor: ${errorMessage(err)}`);
44
+ console.error(`Editor "${editor}" failed. Set $EDITOR to your preferred editor.`);
45
+ process.exit(1);
46
+ }
47
+ }
@@ -1,11 +1,7 @@
1
- export declare function handleSkillsNamespace(args: string[], profile: string): void;
2
- export declare function handleHooksNamespace(args: string[]): void;
3
- export declare function handleSkillList(profile: string, project?: string): void;
4
- export declare function handleDetectSkills(args: string[], profile: string): void;
5
- export declare function handleProjectsNamespace(args: string[], profile: string): Promise<void>;
6
- export declare function handleTaskNamespace(args: string[]): Promise<void>;
7
- export declare function handleFindingNamespace(args: string[]): Promise<void>;
8
- export declare function handleStoreNamespace(args: string[]): Promise<void>;
9
- export declare function handleProfileNamespace(args: string[]): void;
10
- export declare function handlePromoteNamespace(args: string[]): Promise<void>;
11
- export declare function handleReviewNamespace(args: string[]): Promise<void>;
1
+ export { handleSkillsNamespace, handleHooksNamespace, handleSkillList, handleDetectSkills } from "./namespaces-skills.js";
2
+ export { handleProjectsNamespace } from "./namespaces-projects.js";
3
+ export { handleTaskNamespace } from "./namespaces-tasks.js";
4
+ export { handleFindingNamespace } from "./namespaces-findings.js";
5
+ export { handleStoreNamespace, handlePromoteNamespace } from "./namespaces-store.js";
6
+ export { handleProfileNamespace } from "./namespaces-profile.js";
7
+ export { handleReviewNamespace } from "./namespaces-review.js";