@popoverai/dotrequirements 0.25.0 → 0.26.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.
Files changed (53) hide show
  1. package/dist/cli.js +8 -1
  2. package/dist/codebase-to-spec/present.d.ts +9 -0
  3. package/dist/codebase-to-spec/present.js +23 -2
  4. package/dist/codebase-to-spec/skill-install.d.ts +16 -6
  5. package/dist/codebase-to-spec/skill-install.js +109 -47
  6. package/dist/codebase-to-spec/version-check.d.ts +31 -0
  7. package/dist/codebase-to-spec/version-check.js +56 -0
  8. package/dist/commands/ai-setup.d.ts +12 -1
  9. package/dist/commands/ai-setup.js +65 -33
  10. package/dist/commands/codebase-to-spec/pack.d.ts +4 -0
  11. package/dist/commands/codebase-to-spec/pack.js +17 -0
  12. package/dist/commands/init.js +6 -1
  13. package/dist/commands/link-resolution.d.ts +79 -0
  14. package/dist/commands/link-resolution.js +141 -0
  15. package/dist/commands/link.d.ts +14 -4
  16. package/dist/commands/link.js +369 -16
  17. package/dist/commands/pull.js +19 -2
  18. package/dist/commands/push.js +36 -2
  19. package/dist/convex.d.ts +5 -3
  20. package/dist/convex.js +5 -3
  21. package/dist/harness/cache.d.ts +0 -14
  22. package/dist/harness/cache.js +1 -41
  23. package/dist/harness/finalize.js +2 -2
  24. package/dist/harness/prepare.js +1 -3
  25. package/dist/harness/requirementsLoader.d.ts +3 -3
  26. package/dist/harness/requirementsLoader.js +13 -8
  27. package/dist/mcp/handlers/authoring.d.ts +5 -5
  28. package/dist/mcp/handlers/authoring.js +9 -9
  29. package/dist/mcp/handlers/push.d.ts +2 -2
  30. package/dist/mcp/handlers/push.js +36 -3
  31. package/dist/mcp/handlers/review.d.ts +4 -4
  32. package/dist/mcp/handlers/review.js +4 -4
  33. package/dist/mcp/handlers/search.d.ts +1 -1
  34. package/dist/mcp/handlers/search.js +1 -1
  35. package/dist/mcp/index.js +29 -0
  36. package/dist/push/core.d.ts +18 -0
  37. package/dist/push/core.js +70 -3
  38. package/dist/push/index.d.ts +1 -1
  39. package/dist/push/index.js +1 -1
  40. package/dist/schema/parser-core.js +5 -1
  41. package/dist/schema/parser.js +5 -1
  42. package/dist/schema/run-marker.d.ts +38 -0
  43. package/dist/schema/run-marker.js +138 -0
  44. package/dist/schema/schemas.d.ts +12 -0
  45. package/dist/schema/schemas.js +1 -0
  46. package/dist/templates/agents/cts-worker.md +1 -1
  47. package/dist/templates/skills/codebase-to-spec/SKILL.md +33 -11
  48. package/dist/templates/workflows/specify-codebase.js +4 -2
  49. package/dist/utils/own-package.d.ts +10 -0
  50. package/dist/utils/own-package.js +13 -0
  51. package/dist/utils/project-selector.d.ts +5 -0
  52. package/dist/utils/project-selector.js +4 -0
  53. package/package.json +2 -2
@@ -7,8 +7,10 @@
7
7
  * - CTS-CLI-3: Pipeline refuses to spec a codebase that exceeds the working context budget
8
8
  * - CTS-PACK-1: Pack stage produces compressed and uncompressed views of the codebase
9
9
  * - CTS-PACK-2: Engineer can extend the ignore list per project
10
+ * - CTS-PACK-4: Pack surfaces when a newer CLI version is available
10
11
  * - CTS-OBSERVE-1: CLI emits structured progress as the pipeline runs
11
12
  */
13
+ import { checkForNewerVersion } from "../../codebase-to-spec/version-check.js";
12
14
  export interface PackOptions {
13
15
  scope?: string;
14
16
  /**
@@ -28,6 +30,8 @@ export interface PackOptions {
28
30
  progress?: import("../../codebase-to-spec/progress.js").ProgressEmitter;
29
31
  /** Override the project root, mainly for tests. */
30
32
  projectRoot?: string;
33
+ /** Override the registry version check, mainly for tests. */
34
+ checkVersion?: typeof checkForNewerVersion;
31
35
  }
32
36
  export declare function packCommand(rawOptions?: PackOptions): Promise<void>;
33
37
  //# sourceMappingURL=pack.d.ts.map
@@ -7,6 +7,7 @@
7
7
  * - CTS-CLI-3: Pipeline refuses to spec a codebase that exceeds the working context budget
8
8
  * - CTS-PACK-1: Pack stage produces compressed and uncompressed views of the codebase
9
9
  * - CTS-PACK-2: Engineer can extend the ignore list per project
10
+ * - CTS-PACK-4: Pack surfaces when a newer CLI version is available
10
11
  * - CTS-OBSERVE-1: CLI emits structured progress as the pipeline runs
11
12
  */
12
13
  import { resolve } from "node:path";
@@ -15,11 +16,16 @@ import { clearCache, ensureCacheDir } from "../../codebase-to-spec/cache.js";
15
16
  import { ExitCode } from "../../codebase-to-spec/exit-codes.js";
16
17
  import { runPack } from "../../codebase-to-spec/pack.js";
17
18
  import { StdoutProgress } from "../../codebase-to-spec/progress.js";
19
+ import { checkForNewerVersion } from "../../codebase-to-spec/version-check.js";
18
20
  import { findProjectRoot } from "../../utils/project-settings.js";
19
21
  export async function packCommand(rawOptions = {}) {
20
22
  const progress = rawOptions.progress ?? new StdoutProgress();
21
23
  const projectRoot = rawOptions.projectRoot ?? findProjectRoot(process.cwd()) ?? process.cwd();
22
24
  const budgetTokens = rawOptions.budgetTokens ?? DEFAULT_BUDGET_TOKENS;
25
+ // Fail-soft staleness check (CTS-PACK-4), started now so the registry
26
+ // round-trip overlaps the pack work and adds no wall-clock on the happy
27
+ // path. Resolves to undefined on any failure or timeout.
28
+ const versionCheck = (rawOptions.checkVersion ?? checkForNewerVersion)();
23
29
  if (rawOptions.fresh) {
24
30
  progress.emit({
25
31
  stage: "pack",
@@ -54,6 +60,17 @@ export async function packCommand(rawOptions = {}) {
54
60
  message: `Packed ${packResult.fileCount} files → ${paths.overview} (compressed), ${paths.source} (uncompressed)`,
55
61
  data: { fileCount: packResult.fileCount },
56
62
  });
63
+ // Surface a stale install (CTS-PACK-4). Informational only: outputs and
64
+ // exit code are unaffected, and a failed check emits nothing.
65
+ const versionInfo = await versionCheck.catch(() => undefined);
66
+ if (versionInfo?.stale) {
67
+ progress.emit({
68
+ stage: "pack",
69
+ step: "stale-version",
70
+ message: `A newer dotrequirements CLI is available: ${versionInfo.latest} (running ${versionInfo.current}). Re-run skill-install to refresh.`,
71
+ data: { current: versionInfo.current, latest: versionInfo.latest },
72
+ });
73
+ }
57
74
  // Budget check (CTS-CLI-3)
58
75
  const measured = countTokensInFileExact(paths.overview);
59
76
  const budget = checkBudget(measured, budgetTokens);
@@ -353,8 +353,13 @@ async function inviteFlow(cwd, token) {
353
353
  console.log(`✓ Joined team "${inviteInfo.teamName}"`);
354
354
  }
355
355
  catch (err) {
356
+ // Production redacts ConvexError messages to "Server Error" but keeps
357
+ // error.data — check both, or this graceful path only works in dev
358
+ const data = err.data;
359
+ const dataMessage = typeof data?.message === "string" ? data.message : "";
356
360
  const errorMessage = err instanceof Error ? err.message : String(err);
357
- if (errorMessage.includes("already a member")) {
361
+ if (dataMessage.includes("already a member") ||
362
+ errorMessage.includes("already a member")) {
358
363
  console.log(`Already a member of "${inviteInfo.teamName}"`);
359
364
  }
360
365
  else {
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Pure decision logic for `dotrequirements link`'s non-interactive mode
3
+ * (LINK-10/11/12). No I/O: the link command gathers state (teams, projects)
4
+ * and these functions decide what to do — use a project, create one, rename,
5
+ * or stop with a decision-needed result that carries everything an AI
6
+ * assistant needs to ask the user and retry with a deviation flag.
7
+ */
8
+ export interface TeamOption {
9
+ teamId: string;
10
+ name: string;
11
+ tier: string;
12
+ tierName: string;
13
+ projectCount: number;
14
+ projectLimit: number;
15
+ }
16
+ export interface ProjectOption {
17
+ projectId: string;
18
+ projectName: string;
19
+ projectSlug: string | null;
20
+ isDefault: boolean;
21
+ hasUserContent: boolean;
22
+ }
23
+ export interface LinkFlags {
24
+ yes?: boolean;
25
+ team?: string;
26
+ connect?: string;
27
+ create?: boolean;
28
+ name?: string;
29
+ json?: boolean;
30
+ }
31
+ /**
32
+ * LINK-12.3: any deviation flag implies non-interactive mode.
33
+ */
34
+ export declare function isNonInteractive(flags: LinkFlags): boolean;
35
+ export declare function hasCapacity(team: TeamOption): boolean;
36
+ /** A choice the user must make, with everything needed to ask and retry. */
37
+ export interface DecisionNeeded {
38
+ kind: "decision";
39
+ decision: "team" | "project" | "capacity";
40
+ message: string;
41
+ options: Array<{
42
+ label: string;
43
+ /** Exact flag to pass on retry (LINK-11.4), when retryable via link */
44
+ retryFlag?: string;
45
+ }>;
46
+ }
47
+ export interface NoMatch {
48
+ kind: "no-match";
49
+ flag: "team" | "connect";
50
+ provided: string;
51
+ alternatives: string[];
52
+ }
53
+ export type TeamResolution = {
54
+ kind: "use";
55
+ team: TeamOption;
56
+ } | DecisionNeeded | NoMatch;
57
+ /**
58
+ * LINK-10.1, LINK-11.0, LINK-12.0, LINK-12.4
59
+ */
60
+ export declare function resolveTeam(teams: TeamOption[], flags: LinkFlags): TeamResolution;
61
+ export type ProjectResolution = {
62
+ kind: "connect";
63
+ project: ProjectOption;
64
+ rename: boolean;
65
+ } | {
66
+ kind: "create";
67
+ } | DecisionNeeded | NoMatch;
68
+ /**
69
+ * LINK-10.2/10.3 (default-project rule), LINK-11.1/11.2 (decision menus),
70
+ * LINK-12.1/12.2/12.4 (deviation flags).
71
+ */
72
+ export declare function resolveProject(projects: ProjectOption[], team: TeamOption, flags: LinkFlags): ProjectResolution;
73
+ /**
74
+ * Prompt-free project naming for non-interactive mode (LINK-9.0): explicit
75
+ * flag, then package metadata, then directory name, then a fixed fallback —
76
+ * never a prompt.
77
+ */
78
+ export declare function resolveProjectName(flags: LinkFlags, packageName: string | undefined, directoryName: string | undefined): string;
79
+ //# sourceMappingURL=link-resolution.d.ts.map
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Pure decision logic for `dotrequirements link`'s non-interactive mode
3
+ * (LINK-10/11/12). No I/O: the link command gathers state (teams, projects)
4
+ * and these functions decide what to do — use a project, create one, rename,
5
+ * or stop with a decision-needed result that carries everything an AI
6
+ * assistant needs to ask the user and retry with a deviation flag.
7
+ */
8
+ /**
9
+ * LINK-12.3: any deviation flag implies non-interactive mode.
10
+ */
11
+ export function isNonInteractive(flags) {
12
+ return Boolean(flags.yes || flags.team || flags.connect || flags.create);
13
+ }
14
+ export function hasCapacity(team) {
15
+ return team.projectLimit === -1 || team.projectCount < team.projectLimit;
16
+ }
17
+ /**
18
+ * LINK-10.1, LINK-11.0, LINK-12.0, LINK-12.4
19
+ */
20
+ export function resolveTeam(teams, flags) {
21
+ if (flags.team) {
22
+ const wanted = flags.team.toLowerCase();
23
+ const match = teams.find((t) => t.teamId === flags.team || t.name.toLowerCase() === wanted);
24
+ if (match)
25
+ return { kind: "use", team: match };
26
+ return {
27
+ kind: "no-match",
28
+ flag: "team",
29
+ provided: flags.team,
30
+ alternatives: teams.map((t) => `${t.name} (${t.teamId})`),
31
+ };
32
+ }
33
+ if (teams.length === 1) {
34
+ return { kind: "use", team: teams[0] };
35
+ }
36
+ return {
37
+ kind: "decision",
38
+ decision: "team",
39
+ message: "You belong to multiple teams — which should this project join?",
40
+ options: teams.map((t) => ({
41
+ label: `${t.name} (${t.tierName}, ${formatCapacity(t)})`,
42
+ retryFlag: `--team ${t.teamId}`,
43
+ })),
44
+ };
45
+ }
46
+ /**
47
+ * LINK-10.2/10.3 (default-project rule), LINK-11.1/11.2 (decision menus),
48
+ * LINK-12.1/12.2/12.4 (deviation flags).
49
+ */
50
+ export function resolveProject(projects, team, flags) {
51
+ if (flags.connect) {
52
+ const match = projects.find((p) => p.projectSlug === flags.connect);
53
+ if (match)
54
+ return { kind: "connect", project: match, rename: false };
55
+ return {
56
+ kind: "no-match",
57
+ flag: "connect",
58
+ provided: flags.connect,
59
+ alternatives: projects
60
+ .filter((p) => p.projectSlug)
61
+ .map((p) => `${p.projectName} (${p.projectSlug})`),
62
+ };
63
+ }
64
+ if (flags.create) {
65
+ if (hasCapacity(team))
66
+ return { kind: "create" };
67
+ return atCapacityDecision(projects, team);
68
+ }
69
+ if (projects.length === 0) {
70
+ // A decision with a single possible answer is not a decision: create.
71
+ if (hasCapacity(team))
72
+ return { kind: "create" };
73
+ return atCapacityDecision(projects, team);
74
+ }
75
+ if (projects.length === 1 && projects[0].isDefault) {
76
+ // LINK-10.2: rename the untouched default project to match this repo;
77
+ // LINK-10.3: if the user has put content in it, connect without renaming
78
+ return {
79
+ kind: "connect",
80
+ project: projects[0],
81
+ rename: !projects[0].hasUserContent,
82
+ };
83
+ }
84
+ // LINK-11.1: existing projects the user chose to create — their call
85
+ return {
86
+ kind: "decision",
87
+ decision: "project",
88
+ message: "This team already has projects — connect to one, or create a new one?",
89
+ options: [
90
+ ...projects.map((p) => ({
91
+ label: `${p.projectName}${p.hasUserContent ? "" : " (empty)"}`,
92
+ retryFlag: p.projectSlug ? `--connect ${p.projectSlug}` : undefined,
93
+ })),
94
+ ...(hasCapacity(team)
95
+ ? [{ label: "Create a new project", retryFlag: "--create" }]
96
+ : []),
97
+ ],
98
+ };
99
+ }
100
+ /**
101
+ * LINK-11.2: creating is required (or requested) but the team is at its
102
+ * project limit. None of the remedies is a plain link retry flag.
103
+ */
104
+ function atCapacityDecision(projects, team) {
105
+ return {
106
+ kind: "decision",
107
+ decision: "capacity",
108
+ message: `Team "${team.name}" is at its ${team.tierName} project limit (${formatCapacity(team)}).`,
109
+ options: [
110
+ ...projects
111
+ .filter((p) => p.projectSlug)
112
+ .map((p) => ({
113
+ label: `Connect to existing project ${p.projectName}`,
114
+ retryFlag: `--connect ${p.projectSlug}`,
115
+ })),
116
+ { label: "Delete a project you no longer need (in the web app)" },
117
+ { label: "Upgrade the team for more projects" },
118
+ ],
119
+ };
120
+ }
121
+ function formatCapacity(team) {
122
+ return team.projectLimit === -1
123
+ ? `${team.projectCount}/unlimited projects`
124
+ : `${team.projectCount}/${team.projectLimit} projects`;
125
+ }
126
+ /**
127
+ * Prompt-free project naming for non-interactive mode (LINK-9.0): explicit
128
+ * flag, then package metadata, then directory name, then a fixed fallback —
129
+ * never a prompt.
130
+ */
131
+ export function resolveProjectName(flags, packageName, directoryName) {
132
+ if (flags.name)
133
+ return flags.name;
134
+ if (packageName)
135
+ return packageName;
136
+ if (directoryName && directoryName !== "/" && directoryName !== ".") {
137
+ return directoryName;
138
+ }
139
+ return "my-project";
140
+ }
141
+ //# sourceMappingURL=link-resolution.js.map
@@ -1,14 +1,24 @@
1
+ import { type LinkFlags } from "./link-resolution.js";
2
+ export interface LinkCommandOptions extends LinkFlags {
3
+ }
1
4
  /**
2
- * Link command - connect or reconnect local project to cloud.
5
+ * Link command - connect or reconnect local project to cloud, creating the
6
+ * cloud project when needed. Link owns the "upgrade local to cloud"
7
+ * transition.
3
8
  *
4
9
  * Requirements:
5
10
  * - LINK-1: Requires existing .requirements/ folder
6
11
  * - LINK-2: Offers refresh when credentials exist
7
12
  * - LINK-3: Uses inline OAuth (no persistent tokens)
8
- * - LINK-4, LINK-5: Team and project selection
13
+ * - LINK-4, LINK-5: Team and project selection (including the create option)
9
14
  * - LINK-6: Saves credentials to project-settings.json
10
- * - LINK-7: Optionally pulls requirements
15
+ * - LINK-7: Optionally pulls requirements (interactive only)
11
16
  * - LINK-8: Handles network errors gracefully
17
+ * - LINK-9: Creates the cloud project when chosen
18
+ * - LINK-10/11/12: Non-interactive mode for AI-driven setup
19
+ * - LINK-13: Non-interactive success includes share artifacts (team-invite
20
+ * URL + read-only pull command) so one "yes" puts the spec in front of
21
+ * the team
12
22
  */
13
- export declare function linkCommand(): Promise<void>;
23
+ export declare function linkCommand(options?: LinkCommandOptions): Promise<void>;
14
24
  //# sourceMappingURL=link.d.ts.map