@yagni-app/code 0.3.1 → 0.3.3

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 (65) hide show
  1. package/dist/cli.js +13 -0
  2. package/dist/crashReport.d.ts +12 -0
  3. package/dist/crashReport.js +28 -1
  4. package/dist/extension/crashReport.d.ts +18 -0
  5. package/dist/extension/crashReport.js +35 -2
  6. package/dist/extension/footer.d.ts +1 -1
  7. package/dist/extension/hooks.d.ts +111 -0
  8. package/dist/extension/hooks.js +666 -0
  9. package/dist/extension/index.d.ts +13 -6
  10. package/dist/extension/index.js +57 -7
  11. package/dist/extension/{approvedPrefixes.js → permission/approvedPrefixes.js} +1 -1
  12. package/dist/extension/permission/dbReadPolicy.d.ts +90 -0
  13. package/dist/extension/permission/dbReadPolicy.js +227 -0
  14. package/dist/extension/{execPolicy.js → permission/execPolicy.js} +99 -8
  15. package/dist/extension/{permission.d.ts → permission/gate.d.ts} +10 -3
  16. package/dist/extension/{permission.js → permission/gate.js} +156 -9
  17. package/dist/extension/{guardian.d.ts → permission/guardian.d.ts} +2 -2
  18. package/dist/extension/{guardian.js → permission/guardian.js} +1 -1
  19. package/dist/extension/permission/index.d.ts +14 -0
  20. package/dist/extension/permission/index.js +14 -0
  21. package/dist/extension/permission/packageManagerPolicy.d.ts +55 -0
  22. package/dist/extension/permission/packageManagerPolicy.js +170 -0
  23. package/dist/extension/pipeline/activityFeed.js +19 -5
  24. package/dist/extension/pipeline/checker.d.ts +99 -0
  25. package/dist/extension/pipeline/checker.js +238 -0
  26. package/dist/extension/pipeline/fanout.d.ts +116 -0
  27. package/dist/extension/pipeline/fanout.js +248 -0
  28. package/dist/extension/pipeline/fanoutBeats.d.ts +31 -0
  29. package/dist/extension/pipeline/fanoutBeats.js +86 -0
  30. package/dist/extension/pipeline/goCommand.d.ts +14 -0
  31. package/dist/extension/pipeline/goCommand.js +38 -1
  32. package/dist/extension/pipeline/headlessGo.d.ts +163 -0
  33. package/dist/extension/pipeline/headlessGo.js +333 -0
  34. package/dist/extension/pipeline/invocation.d.ts +31 -3
  35. package/dist/extension/pipeline/invocation.js +37 -3
  36. package/dist/extension/pipeline/mission.d.ts +55 -0
  37. package/dist/extension/pipeline/mission.js +70 -0
  38. package/dist/extension/pipeline/orchestrator.d.ts +48 -3
  39. package/dist/extension/pipeline/orchestrator.js +450 -9
  40. package/dist/extension/pipeline/personas.d.ts +16 -1
  41. package/dist/extension/pipeline/personas.js +118 -7
  42. package/dist/extension/pipeline/runSession.d.ts +45 -1
  43. package/dist/extension/pipeline/runState.d.ts +57 -12
  44. package/dist/extension/pipeline/runState.js +60 -18
  45. package/dist/extension/pipeline/runner.js +10 -1
  46. package/dist/extension/pipeline/stages.d.ts +84 -7
  47. package/dist/extension/pipeline/stages.js +166 -0
  48. package/dist/extension/pipeline/tierCap.d.ts +32 -0
  49. package/dist/extension/pipeline/tierCap.js +57 -0
  50. package/dist/extension/pipeline/types.d.ts +130 -1
  51. package/dist/extension/pipeline/types.js +17 -0
  52. package/dist/extension/pipeline/verify.d.ts +86 -3
  53. package/dist/extension/pipeline/verify.js +175 -6
  54. package/dist/extension/subagents.js +13 -0
  55. package/dist/extension/turnLog.d.ts +38 -0
  56. package/dist/extension/turnLog.js +93 -0
  57. package/dist/goHeadless.d.ts +75 -0
  58. package/dist/goHeadless.js +132 -0
  59. package/dist/paths.d.ts +9 -0
  60. package/dist/paths.js +12 -0
  61. package/dist/promptEnrichment.d.ts +1 -1
  62. package/dist/promptEnrichment.js +1 -1
  63. package/package.json +2 -2
  64. /package/dist/extension/{approvedPrefixes.d.ts → permission/approvedPrefixes.d.ts} +0 -0
  65. /package/dist/extension/{execPolicy.d.ts → permission/execPolicy.d.ts} +0 -0
@@ -0,0 +1,132 @@
1
+ /**
2
+ * `yagni go --headless …` — the scriptable front door to the /go pipeline.
3
+ *
4
+ * The pipeline itself lives in the bundled extension (pi-extension-yagni's
5
+ * `pipeline/headlessGo.ts`). The launcher keeps its usual job: resolve the
6
+ * active environment's credentials, build the hermetic child environment, point
7
+ * the pipeline's child resolver at the pinned pi binary, and hand over. As
8
+ * everywhere else in this package, the extension is reached by FILE PATH and
9
+ * called through a locally-declared structural type — the two packages stay
10
+ * independent, and the CLI never takes a build-time dependency on a private
11
+ * workspace package.
12
+ *
13
+ * The mission sandbox invokes exactly this command (`--ticket-file`,
14
+ * `--plan-file`, optional `--memo-file`, `--run-id`, `--json`), so what Jack
15
+ * runs locally and what the fleet executes are the same binary and the same
16
+ * pipeline.
17
+ */
18
+ import { randomUUID } from "node:crypto";
19
+ import { mkdirSync } from "node:fs";
20
+ import { pathToFileURL } from "node:url";
21
+ import { agentDirEnvVar } from "./branding.js";
22
+ import { agentDir, credentialsDir } from "./credentials.js";
23
+ import { credentialsFromProfile, profilePath, readActiveProfile } from "./profiles.js";
24
+ import { resolveHeadlessGoPath, resolvePiCliPath } from "./paths.js";
25
+ /** Mirrors HEADLESS_GO_EXIT in the extension; duplicated to keep the packages independent. */
26
+ export const GO_EXIT = { verified: 0, unverified: 1, usage: 2 };
27
+ async function defaultLoadHeadless() {
28
+ const mod = (await import(pathToFileURL(resolveHeadlessGoPath()).href));
29
+ if (typeof mod?.runHeadlessGo !== "function") {
30
+ throw new Error("The bundled pipeline is missing its headless entry point.");
31
+ }
32
+ return mod;
33
+ }
34
+ /**
35
+ * Build the environment every pipeline stage child inherits: the proxy
36
+ * credentials (so the `yagni` provider and the grounding tools reach the
37
+ * backend), the hermetic pi agent dir, and pi's quiet flags. `YAGNI_CALLER` /
38
+ * `YAGNI_RUN_ID` are NOT set here — the pipeline stamps a per-stage caller onto
39
+ * every child, and the run id rides the `--run-id` flag.
40
+ */
41
+ export function buildHeadlessChildEnv(opts) {
42
+ const piAgentDir = agentDir(opts.profileName);
43
+ return {
44
+ ...opts.baseEnv,
45
+ YAGNI_SESSION_ID: opts.sessionId ?? randomUUID(),
46
+ YAGNI_TOKEN: opts.token,
47
+ YAGNI_BASE_URL: opts.baseUrl,
48
+ ...(opts.expiresAt ? { YAGNI_TOKEN_EXPIRES_AT: opts.expiresAt } : {}),
49
+ ...(opts.workspaceId ? { YAGNI_WORKSPACE_ID: opts.workspaceId } : {}),
50
+ YAGNI_PROFILE_PATH: profilePath(opts.profileName),
51
+ YAGNI_CODE_HOME: credentialsDir(),
52
+ ...(opts.cliVersion ? { YAGNI_CODE_VERSION: opts.cliVersion } : {}),
53
+ [agentDirEnvVar()]: piAgentDir,
54
+ PI_CODING_AGENT_DIR: piAgentDir,
55
+ PI_SKIP_VERSION_CHECK: "1",
56
+ PI_TELEMETRY: "0",
57
+ };
58
+ }
59
+ /**
60
+ * Run `yagni go`. Returns the process exit code: 0 only on a verified
61
+ * candidate, 1 on any other pipeline outcome, 2 on a usage or credential
62
+ * problem. Never throws.
63
+ */
64
+ export async function goCommand(args, deps = {}, cliVersion) {
65
+ const writeErr = deps.writeErr ?? ((line) => void process.stderr.write(`${line}\n`));
66
+ const baseEnv = deps.env ?? process.env;
67
+ const profile = await (deps.loadCredentials ?? (async () => {
68
+ const active = await readActiveProfile();
69
+ const creds = credentialsFromProfile(active);
70
+ return {
71
+ name: active.name,
72
+ baseUrl: creds?.baseUrl ?? active.baseUrl,
73
+ ...(creds?.token ? { token: creds.token } : {}),
74
+ ...(creds?.expiresAt ? { expiresAt: creds.expiresAt } : {}),
75
+ ...(creds?.workspaceId ? { workspaceId: creds.workspaceId } : {}),
76
+ };
77
+ }))();
78
+ // A mission sandbox has no profile on disk: it is handed a run-scoped
79
+ // capability token in the environment instead (the same `YAGNI_TOKEN` /
80
+ // `YAGNI_BASE_URL` pair every stage child already runs with). Fall back to it
81
+ // ONLY when nothing is logged in, so an exported token can never quietly
82
+ // hijack a developer's active profile.
83
+ const envToken = baseEnv.YAGNI_TOKEN?.trim();
84
+ const envBaseUrl = baseEnv.YAGNI_BASE_URL?.trim();
85
+ const token = profile.token ?? (envToken || undefined);
86
+ const baseUrl = profile.token ? profile.baseUrl : (envBaseUrl || profile.baseUrl);
87
+ if (!token) {
88
+ writeErr(`Not logged in to environment "${profile.name}" (${profile.baseUrl}). Run \`yagni login\` first.`);
89
+ return GO_EXIT.usage;
90
+ }
91
+ const childEnv = buildHeadlessChildEnv({
92
+ baseEnv,
93
+ token,
94
+ baseUrl,
95
+ profileName: profile.name,
96
+ ...(profile.expiresAt ? { expiresAt: profile.expiresAt } : {}),
97
+ ...(profile.workspaceId ? { workspaceId: profile.workspaceId } : {}),
98
+ ...(cliVersion ? { cliVersion } : {}),
99
+ });
100
+ // The hermetic agent dir must exist before a stage child tries to read it.
101
+ try {
102
+ mkdirSync(childEnv[agentDirEnvVar()], { recursive: true, mode: 0o700 });
103
+ }
104
+ catch {
105
+ /* best-effort: pi creates it too, and a failure here is not worth stopping for */
106
+ }
107
+ // The pipeline spawns each stage as `node <piCli> -e <extension> …`, resolving
108
+ // piCli from `process.argv[1]` (in a session that IS pi). Headlessly the entry
109
+ // process is this launcher, so point the resolver at pi's own CLI entry.
110
+ try {
111
+ process.argv[1] = (deps.piCliPath ?? resolvePiCliPath)();
112
+ }
113
+ catch (err) {
114
+ writeErr(`Could not locate the agent binary: ${err instanceof Error ? err.message : String(err)}`);
115
+ return GO_EXIT.usage;
116
+ }
117
+ let headless;
118
+ try {
119
+ headless = await (deps.loadHeadless ?? defaultLoadHeadless)();
120
+ }
121
+ catch (err) {
122
+ writeErr(err instanceof Error ? err.message : String(err));
123
+ return GO_EXIT.usage;
124
+ }
125
+ const outcome = await headless.runHeadlessGo(args, {
126
+ cwd: deps.cwd ?? process.cwd(),
127
+ env: baseEnv,
128
+ childEnv,
129
+ });
130
+ return outcome.exitCode;
131
+ }
132
+ //# sourceMappingURL=goHeadless.js.map
package/dist/paths.d.ts CHANGED
@@ -20,6 +20,15 @@
20
20
  * and a fresh checkout working before anything is bundled.
21
21
  */
22
22
  export declare function resolveExtensionPath(): string;
23
+ /**
24
+ * Absolute path to the extension's headless pipeline entry
25
+ * (`pipeline/headlessGo.js`), the module `yagni go --headless` imports.
26
+ *
27
+ * Derived from the extension entry so the bundled-vs-workspace fallback above
28
+ * is resolved once. Only this one module is loaded (not the whole extension):
29
+ * it pulls in the pipeline alone, with none of pi's TUI surface.
30
+ */
31
+ export declare function resolveHeadlessGoPath(): string;
23
32
  /**
24
33
  * Absolute path to pi's package root — the dir whose package.json names the
25
34
  * package. The shadow package dir is built from this (we read its package.json
package/dist/paths.js CHANGED
@@ -31,6 +31,18 @@ export function resolveExtensionPath() {
31
31
  return bundled;
32
32
  return resolveModulePath("pi-extension-yagni");
33
33
  }
34
+ /**
35
+ * Absolute path to the extension's headless pipeline entry
36
+ * (`pipeline/headlessGo.js`), the module `yagni go --headless` imports.
37
+ *
38
+ * Derived from the extension entry so the bundled-vs-workspace fallback above
39
+ * is resolved once. Only this one module is loaded (not the whole extension):
40
+ * it pulls in the pipeline alone, with none of pi's TUI surface.
41
+ */
42
+ export function resolveHeadlessGoPath() {
43
+ const entry = resolveExtensionPath();
44
+ return join(dirname(entry), "pipeline", "headlessGo.js");
45
+ }
34
46
  /**
35
47
  * Absolute path to pi's package root — the dir whose package.json names the
36
48
  * package. The shadow package dir is built from this (we read its package.json
@@ -36,5 +36,5 @@ export declare function promptEnrichmentDisabled(env: NodeJS.ProcessEnv): boolea
36
36
  * model, and the load-bearing instructions (ask_yagni contract, delegation)
37
37
  * live elsewhere in the prompt.
38
38
  */
39
- export declare const ENGINEERING_PRACTICE_SECTION = "Engineering practice:\n\nBias to action: when the user asks you to implement, fix, or change something, use your tools to make the actual edits and run the actual commands \u2014 do not answer with a description of what you would do, or with code for the user to apply themselves. When the user asks HOW to approach something, answer the question first; do not jump into making changes they have not asked for.\n\nConventions:\n- Never assume a library is available, however well known. Before using one, confirm the project already depends on it (its package manifest, or imports in neighboring files).\n- When editing, read the surrounding code and its imports first; match the file's existing style, naming, and patterns rather than introducing your own.\n- When creating a new file or component, study an existing sibling first and follow its structure.\n- Never write code that logs or exposes secrets, keys, or credentials.\n\nVerification:\n- Consider what the code you are changing is supposed to do (from its name, location, and callers) before you change it.\n- Verify changes with the project's own tests when possible. Never assume a test framework or command \u2014 check the README, package scripts, or neighboring tests for the real one.\n- After completing a task, run the project's lint and typecheck commands if you know them; if you cannot find them, ask the user and suggest recording them in AGENTS.md for next time.\n\nVersion control:\n- No unsolicited commits: commit only when the user asked for one or the task at hand clearly calls for it.\n\nGit safety:\n- You may be in a dirty git worktree. Never revert existing changes you did not make unless explicitly asked \u2014 these were made by the user.\n- If there are unrelated changes in files you are touching, read and work with them rather than reverting.\n- If changes appear in unrelated files, ignore them and do not revert.\n- Do not amend a commit unless explicitly asked.\n- If you notice unexpected changes you did not make while working, stop immediately and ask the user.\n- Never use destructive git commands (git reset --hard, git checkout --) unless the user explicitly requests or approves them.\n\nTodo discipline:\n- Track multi-step work with todo_write: keep exactly one item in_progress at a time, mark items completed the moment they are done, and add newly discovered steps as pending.\n- Do not batch-complete items or create single-step plans. Skip planning for trivially small work (~25% of tasks).\n\nMode awareness:\n- In auto mode, proactively run tests, lint, and typecheck after your changes.\n- In review mode, propose verification steps but wait for approval before running them.\n- In plan mode, explore and design only \u2014 the gate holds all writes.\n\nCommunication:\n- Answer directly, without preamble or postamble (\"Here is what I will do next...\", \"Based on the information provided...\"). Match the length of your answer to the question.\n- After making edits, report the outcome briefly; do not restate the diff or explain the code you just wrote unless asked.\n- Do not add code comments that narrate what you changed or why the change is correct; comments are for future readers of the code.\n- Reference code as file_path:line_number so the user can jump to it.\n- Before running a non-trivial command that changes state, say in one line what it does and why.\n- Never guess or fabricate URLs. Only use URLs the user provided or that appear in local files.\n- No emojis unless the user asks for them.";
39
+ export declare const ENGINEERING_PRACTICE_SECTION = "Engineering practice:\n\nAnswering vs acting: distinguish what the user is asking for before responding. When the user asks you to analyze, investigate, find a root cause, study how something works, explore an approach, or asks a strategic or advisory question (\"should we...\", \"what's your read on...\", \"go/no-go on...\"), answer in prose \u2014 do not start coding or editing files. When the user asks you to implement, fix, or change something, use your tools to make the actual edits and run the actual commands \u2014 do not answer with a description of what you would do, or with code for the user to apply themselves.\n\nConventions:\n- Never assume a library is available, however well known. Before using one, confirm the project already depends on it (its package manifest, or imports in neighboring files).\n- When editing, read the surrounding code and its imports first; match the file's existing style, naming, and patterns rather than introducing your own.\n- When creating a new file or component, study an existing sibling first and follow its structure.\n- Never write code that logs or exposes secrets, keys, or credentials.\n\nVerification:\n- Consider what the code you are changing is supposed to do (from its name, location, and callers) before you change it.\n- Verify changes with the project's own tests when possible. Never assume a test framework or command \u2014 check the README, package scripts, or neighboring tests for the real one.\n- After completing a task, run the project's lint and typecheck commands if you know them; if you cannot find them, ask the user and suggest recording them in AGENTS.md for next time.\n\nVersion control:\n- No unsolicited commits: commit only when the user asked for one or the task at hand clearly calls for it.\n\nGit safety:\n- You may be in a dirty git worktree. Never revert existing changes you did not make unless explicitly asked \u2014 these were made by the user.\n- If there are unrelated changes in files you are touching, read and work with them rather than reverting.\n- If changes appear in unrelated files, ignore them and do not revert.\n- Do not amend a commit unless explicitly asked.\n- If you notice unexpected changes you did not make while working, stop immediately and ask the user.\n- Never use destructive git commands (git reset --hard, git checkout --) unless the user explicitly requests or approves them.\n\nTodo discipline:\n- Track multi-step work with todo_write: keep exactly one item in_progress at a time, mark items completed the moment they are done, and add newly discovered steps as pending.\n- Do not batch-complete items or create single-step plans. Skip planning for trivially small work (~25% of tasks).\n\nMode awareness:\n- In auto mode, proactively run tests, lint, and typecheck after your changes.\n- In review mode, propose verification steps but wait for approval before running them.\n- In plan mode, explore and design only \u2014 the gate holds all writes.\n\nCommunication:\n- Answer directly, without preamble or postamble (\"Here is what I will do next...\", \"Based on the information provided...\"). Match the length of your answer to the question.\n- After making edits, report the outcome briefly; do not restate the diff or explain the code you just wrote unless asked.\n- Do not add code comments that narrate what you changed or why the change is correct; comments are for future readers of the code.\n- Reference code as file_path:line_number so the user can jump to it.\n- Before running a non-trivial command that changes state, say in one line what it does and why.\n- Never guess or fabricate URLs. Only use URLs the user provided or that appear in local files.\n- No emojis unless the user asks for them.";
40
40
  //# sourceMappingURL=promptEnrichment.d.ts.map
@@ -41,7 +41,7 @@ export function promptEnrichmentDisabled(env) {
41
41
  */
42
42
  export const ENGINEERING_PRACTICE_SECTION = `Engineering practice:
43
43
 
44
- Bias to action: when the user asks you to implement, fix, or change something, use your tools to make the actual edits and run the actual commands — do not answer with a description of what you would do, or with code for the user to apply themselves. When the user asks HOW to approach something, answer the question first; do not jump into making changes they have not asked for.
44
+ Answering vs acting: distinguish what the user is asking for before responding. When the user asks you to analyze, investigate, find a root cause, study how something works, explore an approach, or asks a strategic or advisory question ("should we...", "what's your read on...", "go/no-go on..."), answer in prose — do not start coding or editing files. When the user asks you to implement, fix, or change something, use your tools to make the actual edits and run the actual commands — do not answer with a description of what you would do, or with code for the user to apply themselves.
45
45
 
46
46
  Conventions:
47
47
  - Never assume a library is available, however well known. Before using one, confirm the project already depends on it (its package manifest, or imports in neighboring files).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yagni-app/code",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
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)",
@@ -39,5 +39,5 @@
39
39
  "smol-toml": "^1.8.0",
40
40
  "typebox": "^1.3.11"
41
41
  },
42
- "yagniSourceSha": "27482c0e8f72e0f171de23bd39e774d9661c0bc6"
42
+ "yagniSourceSha": "36ebf50cf01b962741558ccbf04485ef2231d1e1"
43
43
  }