@yagni-app/code-staging 1.2.9-staging.2089.1 → 1.2.10-staging.2095.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.
@@ -25,7 +25,25 @@ export interface PlanFile {
25
25
  }>;
26
26
  /** The package folders the plan touches, repo-relative. */
27
27
  paths: string[];
28
+ /**
29
+ * The repositories the change writes to, in build order, the ticket's own
30
+ * included, when the plan judges the change spans more than one of the
31
+ * Team's repositories (YAG-1058). Empty for a one-repository change. The
32
+ * host fences the names to the Team's attached repositories.
33
+ */
34
+ repositoriesNeeded: Array<{
35
+ repository: string;
36
+ summary: string;
37
+ }>;
28
38
  }
39
+ /** An `owner/name` repository the plan names, or null when it is not one. */
40
+ export declare function repositoryName(value: unknown): string | null;
41
+ /**
42
+ * The plan's `repositoriesNeeded`, one entry per distinct `owner/name` in the
43
+ * order written. An entry that names no repository is dropped; a second entry
44
+ * for the same repository (case-insensitive) is dropped too.
45
+ */
46
+ export declare function readRepositoriesNeeded(value: unknown): PlanFile["repositoriesNeeded"];
29
47
  /** The plan's ```plan-json block as an object, or null when it has none that parses. */
30
48
  export declare function extractPlanJson(text: string): Record<string, unknown> | null;
31
49
  /** The plan for a person to read: the text without its machine-readable block. */
@@ -15,7 +15,40 @@ const PLAN_JSON_BLOCK = /```plan-json[^\n]*\n([\s\S]*?)```/gi;
15
15
  const MAX_PLAN_QUESTIONS = 20;
16
16
  const MAX_LIST = 40;
17
17
  const MAX_TEXT = 500;
18
+ /** The host splits a ticket into at most this many repositories; more is a Project. The
19
+ * same number as `MAX_SPLIT_CHILDREN` in @yagni/shared, kept here because this package
20
+ * does not depend on it. */
21
+ const MAX_REPOSITORIES_NEEDED = 12;
18
22
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
23
+ /** An `owner/name` repository the plan names, or null when it is not one. */
24
+ export function repositoryName(value) {
25
+ const name = text(value, 200)
26
+ ?.replace(/^https?:\/\/github\.com\//i, "")
27
+ .replace(/\.git$/i, "")
28
+ .replace(/^\/+|\/+$/g, "");
29
+ if (!name || !/^[\w.-]+\/[\w.-]+$/.test(name))
30
+ return null;
31
+ return name;
32
+ }
33
+ /**
34
+ * The plan's `repositoriesNeeded`, one entry per distinct `owner/name` in the
35
+ * order written. An entry that names no repository is dropped; a second entry
36
+ * for the same repository (case-insensitive) is dropped too.
37
+ */
38
+ export function readRepositoriesNeeded(value) {
39
+ const out = [];
40
+ const seen = new Set();
41
+ for (const entry of list(value)) {
42
+ const repository = isRecord(entry) ? repositoryName(entry.repository) : repositoryName(entry);
43
+ if (!repository || seen.has(repository.toLowerCase()))
44
+ continue;
45
+ seen.add(repository.toLowerCase());
46
+ out.push({ repository, summary: (isRecord(entry) && text(entry.summary)) || "" });
47
+ if (out.length === MAX_REPOSITORIES_NEEDED)
48
+ break;
49
+ }
50
+ return out;
51
+ }
19
52
  /** The plan's ```plan-json block as an object, or null when it has none that parses. */
20
53
  export function extractPlanJson(text) {
21
54
  let found = null;
@@ -89,6 +122,10 @@ export function buildPlanFile(planText, fallbackPaths) {
89
122
  .map(repoPath)
90
123
  .filter((path) => path !== null);
91
124
  const paths = [...new Set(named.length > 0 ? named : fallbackPaths())].slice(0, MAX_LIST);
92
- return { file: { version: 1, questions, decisionsCited, checks, paths }, found: block !== null };
125
+ const repositoriesNeeded = readRepositoriesNeeded(block?.repositoriesNeeded);
126
+ return {
127
+ file: { version: 1, questions, decisionsCited, checks, paths, repositoriesNeeded },
128
+ found: block !== null,
129
+ };
93
130
  }
94
131
  //# sourceMappingURL=planOutput.js.map
@@ -156,7 +156,8 @@ export const PLAN_JSON_CONTRACT = "End your answer with a fenced ```plan-json bl
156
156
  '"checked": [{"status": "checked|not_checked", "text": "..."}]}],\n' +
157
157
  ' "decisionsCited": [{"id": "<the decision id the read returned>", "why": "one line"}],\n' +
158
158
  ' "checks": [{"name": "lint", "command": "pnpm lint", "cwd": "apps/web"}],\n' +
159
- ' "paths": ["apps/web"]\n' +
159
+ ' "paths": ["apps/web"],\n' +
160
+ ' "repositoriesNeeded": [{"repository": "owner/name", "summary": "one line on what changes there"}]\n' +
160
161
  "}\n" +
161
162
  "```\n" +
162
163
  "Rules the host enforces, so getting them wrong loses the question:\n" +
@@ -167,7 +168,12 @@ export const PLAN_JSON_CONTRACT = "End your answer with a fenced ```plan-json bl
167
168
  "- At most two alternatives per question. A planChange only adds or rewords; it never removes.\n" +
168
169
  '- "checks" are the commands that prove the change, each run from "cwd" (repo-relative, "." for the root). ' +
169
170
  '"paths" are the package folders the plan touches.\n' +
170
- '- "decisionsCited" lists only decisions the read returned that the plan relies on. Omit what you do not use.';
171
+ '- "decisionsCited" lists only decisions the read returned that the plan relies on. Omit what you do not use.\n' +
172
+ '- "repositoriesNeeded" is for a change that needs writes in more than one repository: list every ' +
173
+ "repository the change writes to, in the order they must be built and merged, this one included, each " +
174
+ "with one line on what changes there. Only a repository the ticket's Other repositories section lists " +
175
+ "counts; the host drops any other name. Leave it out for a change inside this repository alone, and " +
176
+ "never list a repository the change only reads.";
171
177
  /**
172
178
  * The cloud plan stage's task: the input folder the host wrote, read first,
173
179
  * then a plan grounded in the installed checkout, inside the stage's own
@@ -199,7 +205,11 @@ function cloudPlanTask(inputDir) {
199
205
  'If a command fails with "no space left on device", the disk is full: stop there. Do not delete files, ' +
200
206
  "prune Docker or retry. Write the plan and say what you could not check.\n\n" +
201
207
  "When the work depends on another repository the ticket's Other repositories section lists, clone it " +
202
- "read-only on demand with `git clone --filter=blob:none --depth 1` into ~/context/<repo> and read it there.\n\n" +
208
+ "read-only on demand with `git clone --filter=blob:none --depth 1` into ~/context/<repo> and read it there. " +
209
+ "When the change itself needs writes in one of those repositories (an API this repository calls, a " +
210
+ "package it consumes), say so in the plan's repositoriesNeeded, in build order: the ticket is then " +
211
+ "split into one ticket per repository, built in that order, and each step for another repository " +
212
+ "names it so the host can sort the steps.\n\n" +
203
213
  `Call team_read with tool "${DECISIONS_READ_TOOL}" before you ask a person anything: a question an earlier ` +
204
214
  "decision on this repository already answers is not a question. Cite the decisions the plan relies on.\n\n" +
205
215
  "When the change shows in a page, plan a render check the build runs (open the page in the sandbox's " +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yagni-app/code-staging",
3
- "version": "1.2.9-staging.2089.1",
3
+ "version": "1.2.10-staging.2095.1",
4
4
  "description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
@@ -58,5 +58,5 @@
58
58
  "turndown": "^7.2.4",
59
59
  "typebox": "^1.3.15"
60
60
  },
61
- "yagniSourceSha": "0a52413870fd8ac25bc138527a51d189b5ee5322"
61
+ "yagniSourceSha": "ec85145bd5d4dcd937024a643692270a5c5a0beb"
62
62
  }