alignfirst 0.1.0-beta.4 → 0.1.0-beta.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.
@@ -1,4 +1,3 @@
1
- import { execFileSync } from "node:child_process";
2
1
  import { existsSync, realpathSync } from "node:fs";
3
2
  import { createRequire } from "node:module";
4
3
  import { join } from "node:path";
@@ -6,12 +5,11 @@ import { fileURLToPath } from "node:url";
6
5
  import semver from "semver";
7
6
  import { resolveDefaultBranch } from "../default-branch.js";
8
7
  import { errorMessage } from "../errors.js";
9
- import { findExecutable } from "../executables.js";
10
8
  import { parseBareCommandArgs } from "../parse-args.js";
11
9
  import { resolvePlansMode } from "../plans/mode.js";
12
10
  import { findStoppedRebase } from "../plans/rebase.js";
13
- import { resolveProjectConfig } from "../project-config.js";
14
- import { findInstalledSkill, STUB_SKILLS } from "../skills.js";
11
+ import { PROJECT_CONFIG_FILENAME, resolveProjectConfig, } from "../project-config.js";
12
+ import { COMMAND_SKILLS, findInstalledSkill } from "../skills.js";
15
13
  import { cliRangeResult } from "../version-guard.js";
16
14
  export function runDoctor(ctx, args) {
17
15
  const usage = `Usage: ${ctx.form} doctor\n`;
@@ -19,7 +17,7 @@ export function runDoctor(ctx, args) {
19
17
  return 0;
20
18
  writeSection(ctx, "CLI", () => inspectCli(ctx));
21
19
  let resolved;
22
- writeSection(ctx, "Config", () => {
20
+ writeSection(ctx, PROJECT_CONFIG_FILENAME, () => {
23
21
  resolved = resolveProjectConfig(ctx.cwd);
24
22
  return inspectConfig(ctx, resolved);
25
23
  });
@@ -27,7 +25,6 @@ export function runDoctor(ctx, args) {
27
25
  writeSection(ctx, "Plans", () => inspectPlans(ctx));
28
26
  writeSection(ctx, "Docmap", () => inspectDocmap(ctx));
29
27
  writeSection(ctx, "Skills", () => inspectSkills(ctx));
30
- writeSection(ctx, "Companion", () => inspectCompanion(ctx));
31
28
  return 0;
32
29
  }
33
30
  function writeSection(ctx, section, inspect) {
@@ -54,7 +51,7 @@ function inspectCli(ctx) {
54
51
  ];
55
52
  }
56
53
  function inspectConfig(ctx, resolved) {
57
- const lines = [{ level: "ok", text: resolved?.source ?? "none" }];
54
+ const lines = [{ level: "ok", text: resolved === undefined ? "none" : "present" }];
58
55
  const result = cliRangeResult(resolved?.config, ctx.version);
59
56
  if (result === undefined) {
60
57
  lines.push({ level: "ok", text: "no cli range" });
@@ -104,42 +101,30 @@ function isRecord(value) {
104
101
  return typeof value === "object" && value !== null;
105
102
  }
106
103
  function inspectSkills(ctx) {
107
- return STUB_SKILLS.map((name) => {
108
- const installed = findInstalledSkill(ctx.home, name);
109
- if (installed === undefined)
110
- return { level: "warn", text: `${name} missing` };
111
- const version = installed.version ?? "unknown";
112
- const parsed = semver.parse(installed.version);
113
- if (parsed === null || parsed.major < 4)
114
- return {
115
- level: "warn",
116
- text: `${name} ${version} predates v4; update: npx -y skills update --global --yes`,
117
- };
118
- return { level: "ok", text: `${name} ${version} (${installed.root})` };
119
- });
104
+ const alignfirst = findInstalledSkill(ctx.home, "alignfirst");
105
+ const alignfirstLine = alignfirst === undefined
106
+ ? { level: "ok", text: "alignfirst none" }
107
+ : describeInstalledSkill("alignfirst", alignfirst);
108
+ const commands = COMMAND_SKILLS.map((name) => ({
109
+ name,
110
+ installed: findInstalledSkill(ctx.home, name),
111
+ }));
112
+ if (commands.every(({ installed }) => installed === undefined))
113
+ return [alignfirstLine, { level: "ok", text: "no command skill installed" }];
114
+ return [alignfirstLine, ...commands.map(describeCommandSkill)];
120
115
  }
121
- function inspectCompanion(ctx) {
122
- const executable = findExecutable(ctx.env, "alcode");
123
- if (executable === undefined)
124
- return [
125
- {
126
- level: "warn",
127
- text: "alcode not installed (optional; npm install -g @paleo/alcode)",
128
- },
129
- ];
130
- try {
131
- const version = execFileSync(executable, ["--version"], {
132
- encoding: "utf-8",
133
- env: ctx.env,
134
- }).trim();
135
- return [{ level: "ok", text: `alcode ${version} (${executable})` }];
136
- }
137
- catch (error) {
138
- return [{ level: "error", text: `alcode ${executable}: ${commandError(error)}` }];
139
- }
116
+ function describeCommandSkill({ name, installed }) {
117
+ if (installed === undefined)
118
+ return { level: "warn", text: `${name} missing` };
119
+ return describeInstalledSkill(name, installed);
140
120
  }
141
- function commandError(error) {
142
- if (isRecord(error) && typeof error.stderr === "string" && error.stderr !== "")
143
- return firstLine(error.stderr);
144
- return firstLine(errorMessage(error));
121
+ function describeInstalledSkill(name, installed) {
122
+ const version = installed.version ?? "unknown";
123
+ const parsed = semver.parse(installed.version);
124
+ if (parsed === null || parsed.major < 4)
125
+ return {
126
+ level: "warn",
127
+ text: `${name} ${version} predates v4; update: npx -y skills update --global --yes`,
128
+ };
129
+ return { level: "ok", text: `${name} ${version} (${installed.root})` };
145
130
  }
@@ -119,7 +119,7 @@ function resolveTicketId(ctx, positional, side, dryRun) {
119
119
  if (side)
120
120
  return { id: dryRun ? peekSideTicket(ctx.cwd) : reserveSideTicket(ctx.cwd) };
121
121
  if (pattern === undefined)
122
- throw new CliError("No ticket id given. Pass it.\nSetting ticketIdPattern in .alignfirst.json enables deduction from the current branch.");
122
+ throw new CliError("No ticket id given. Pass it.");
123
123
  const deduced = deduceTicketFromBranch(ctx.cwd, pattern);
124
124
  validateTicketId(deduced.id, pattern);
125
125
  return deduced;
@@ -165,7 +165,7 @@ function jsonReport(ctx, options, result) {
165
165
  }
166
166
  function renderReport(ctx, options, result) {
167
167
  const reservation = options.side && options.dryRun ? " (would be reserved)" : "";
168
- const deduction = options.branch === undefined ? "" : ` (deduced from branch ${options.branch})`;
168
+ const deduction = options.branch === undefined ? "" : ` (deduced from branch \`${options.branch}\`)`;
169
169
  const directoryState = renderDirectoryState(result.state, options.dryRun);
170
170
  const directory = `${relative(ctx.cwd, result.dir)}/`;
171
171
  const lines = [
@@ -173,15 +173,16 @@ function renderReport(ctx, options, result) {
173
173
  `- TICKET_DIR: \`${directory}\`${directoryState}`,
174
174
  ];
175
175
  if (result.entries.length === 0)
176
- lines.push("Entries: (none)");
176
+ lines.push("- Entries: (none)");
177
177
  else
178
- lines.push("Entries:", ...result.entries.map((entry) => ` ${renderEntry(entry)}`));
178
+ lines.push("- Entries:", ...result.entries.map((entry) => ` - ${renderEntry(entry)}`));
179
179
  return `${lines.join("\n")}\n`;
180
180
  }
181
181
  function renderEntry(entry) {
182
+ const name = `\`${entry.name}\``;
182
183
  if (entry.size === undefined)
183
- return entry.name;
184
- return `${entry.name} (${formatSize(entry.size)}, ${formatLocalTimestamp(entry.modifiedAt)})`;
184
+ return name;
185
+ return `${name} (${formatSize(entry.size)}, ${formatLocalTimestamp(entry.modifiedAt)})`;
185
186
  }
186
187
  function renderDirectoryState(state, dryRun) {
187
188
  if (state === "existing")
package/dist/skills.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const STUB_SKILLS: readonly ["alignfirst", "alspec", "alplan", "al", "alcatchup", "alcatchupaad", "alcatchupspec", "almerge", "alreview", "aldescription"];
1
+ export declare const COMMAND_SKILLS: readonly ["alspec", "alplan", "al", "alcatchup", "alcatchupaad", "alcatchupspec", "almerge", "alreview", "aldescription"];
2
2
  export declare const SKILL_ROOTS: readonly [".agents/skills", ".claude/skills", ".codex/skills"];
3
3
  export interface InstalledSkill {
4
4
  root: string;
package/dist/skills.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- export const STUB_SKILLS = [
4
- "alignfirst",
3
+ export const COMMAND_SKILLS = [
5
4
  "alspec",
6
5
  "alplan",
7
6
  "al",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alignfirst",
3
- "version": "0.1.0-beta.4",
3
+ "version": "0.1.0-beta.5",
4
4
  "license": "CC0-1.0",
5
5
  "author": "Thomas MUR",
6
6
  "description": "The AlignFirst CLI: protocols, plans and docs in one command.",
@@ -1 +0,0 @@
1
- export declare function findExecutable(env: NodeJS.ProcessEnv, name: string): string | undefined;
@@ -1,24 +0,0 @@
1
- import { accessSync, constants, statSync } from "node:fs";
2
- import { delimiter, join } from "node:path";
3
- export function findExecutable(env, name) {
4
- const path = env.PATH;
5
- if (path === undefined || path === "")
6
- return;
7
- for (const dir of path.split(delimiter)) {
8
- const candidate = join(dir, name);
9
- if (isExecutableFile(candidate))
10
- return candidate;
11
- }
12
- return;
13
- }
14
- function isExecutableFile(path) {
15
- try {
16
- if (!statSync(path).isFile())
17
- return false;
18
- accessSync(path, constants.X_OK);
19
- return true;
20
- }
21
- catch {
22
- return false;
23
- }
24
- }