@yagni-app/code-staging 1.0.0-staging.1185.1 → 1.0.0-staging.1190.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.
|
@@ -59,6 +59,19 @@ export declare const YAGNI_IDENTITY_DRIVER = "You are YAGNI Code, an autonomous
|
|
|
59
59
|
* open with a `- ` bullet line, no emojis.
|
|
60
60
|
*/
|
|
61
61
|
export declare const TICKET_IMAGE_RULE: string;
|
|
62
|
+
/**
|
|
63
|
+
* GitHub operations recipe. A standing directive that teaches the
|
|
64
|
+
* model the one correct way to reply to inline PR review comments and how to
|
|
65
|
+
* pass multi-line bodies to `gh`/`git` without shell-quoting failures. It is
|
|
66
|
+
* GitHub-mechanics knowledge every session needs — the two failures it fixes
|
|
67
|
+
* (hoisting several inline replies into one top-level comment; a shell-
|
|
68
|
+
* quoting dance that mangles multi-line bodies) are universal, not
|
|
69
|
+
* workflow-specific.
|
|
70
|
+
*
|
|
71
|
+
* Content constraints (parity with {@link TICKET_IMAGE_RULE}): must not contain
|
|
72
|
+
* the standalone word "pi", must not open with a `- ` bullet line, no emojis.
|
|
73
|
+
*/
|
|
74
|
+
export declare const GITHUB_OPERATIONS: string;
|
|
62
75
|
/**
|
|
63
76
|
* The injected-reminder framing (YAG-574, Change A prerequisite). Claude Code
|
|
64
77
|
* carries this exact sentence in every system prompt so its whole reminder
|
|
@@ -95,6 +95,66 @@ export const TICKET_IMAGE_RULE = "When you read or fetch a ticket from any track
|
|
|
95
95
|
"ticket whose images you have not actually looked at.";
|
|
96
96
|
/** Stable header that starts the ticket-image rule section (idempotency anchor). */
|
|
97
97
|
const TICKET_IMAGE_RULE_HEADER = "## Rule: always read a ticket's images";
|
|
98
|
+
/**
|
|
99
|
+
* GitHub operations recipe. A standing directive that teaches the
|
|
100
|
+
* model the one correct way to reply to inline PR review comments and how to
|
|
101
|
+
* pass multi-line bodies to `gh`/`git` without shell-quoting failures. It is
|
|
102
|
+
* GitHub-mechanics knowledge every session needs — the two failures it fixes
|
|
103
|
+
* (hoisting several inline replies into one top-level comment; a shell-
|
|
104
|
+
* quoting dance that mangles multi-line bodies) are universal, not
|
|
105
|
+
* workflow-specific.
|
|
106
|
+
*
|
|
107
|
+
* Content constraints (parity with {@link TICKET_IMAGE_RULE}): must not contain
|
|
108
|
+
* the standalone word "pi", must not open with a `- ` bullet line, no emojis.
|
|
109
|
+
*/
|
|
110
|
+
export const GITHUB_OPERATIONS = "## GitHub Operations\n" +
|
|
111
|
+
"\n" +
|
|
112
|
+
"Use `gh` via bash for GitHub reads and writes (pull requests, issues, " +
|
|
113
|
+
"reviews, checks, releases). Given a GitHub URL, run `gh` to get the info.\n" +
|
|
114
|
+
"\n" +
|
|
115
|
+
"## Reply to inline review comments in-thread\n" +
|
|
116
|
+
"\n" +
|
|
117
|
+
"When a PR review leaves inline comments and you have addressed them, reply " +
|
|
118
|
+
"to each inline comment in its own thread, describing how it was handled. " +
|
|
119
|
+
"Do not hoist replies to several inline threads into a single top-level " +
|
|
120
|
+
"comment — each inline comment earns its own reply.\n" +
|
|
121
|
+
"\n" +
|
|
122
|
+
"Top-level comments are for matters with no inline anchor: an overall " +
|
|
123
|
+
"summary, a question about the change as a whole, or a review that produced " +
|
|
124
|
+
"no inline comments (for example, the only failure was on a CI run).\n" +
|
|
125
|
+
"\n" +
|
|
126
|
+
"## How to reply to an inline comment\n" +
|
|
127
|
+
"\n" +
|
|
128
|
+
"List inline comments on a PR:\n" +
|
|
129
|
+
" gh api repos/{owner}/{repo}/pulls/{number}/comments\n" +
|
|
130
|
+
"\n" +
|
|
131
|
+
"Reply to a specific inline comment (write a multi-line reply to a file " +
|
|
132
|
+
"first, then pass the path — see Bodies below):\n" +
|
|
133
|
+
" gh api repos/{owner}/{repo}/pulls/{number}/comments/{comment_id}/replies \\\n" +
|
|
134
|
+
" -F body=@<path>\n" +
|
|
135
|
+
"\n" +
|
|
136
|
+
"This is the one reply path. Do not use `in_reply_to` on the comment-creation " +
|
|
137
|
+
"endpoint — it works only on top-level comments and does not nest.\n" +
|
|
138
|
+
"\n" +
|
|
139
|
+
"## Bodies: write to a file, pass the path\n" +
|
|
140
|
+
"\n" +
|
|
141
|
+
"Pass multi-line body text (PR description, comment, review reply, commit " +
|
|
142
|
+
"message) via a file, never inline it in the shell command. Inline forms " +
|
|
143
|
+
"(`--body \"$(cat <<'EOF' … EOF)\"`, bare `-f body='…'`) break on some " +
|
|
144
|
+
"platforms and on apostrophes/backticks/`$` in the body; a file has no " +
|
|
145
|
+
"quoting surface. Use the session scratchpad for the file.\n" +
|
|
146
|
+
"\n" +
|
|
147
|
+
"Write the body with the `write` tool, then:\n" +
|
|
148
|
+
" gh pr create --title \"…\" --body-file <path>\n" +
|
|
149
|
+
" gh pr comment <n> --body-file <path>\n" +
|
|
150
|
+
" gh api repos/{owner}/{repo}/pulls/{number}/comments/{comment_id}/replies -F body=@<path>\n" +
|
|
151
|
+
" git commit -F <path>\n" +
|
|
152
|
+
"\n" +
|
|
153
|
+
"For a short single-line body with no apostrophe, `-f body='…'` and " +
|
|
154
|
+
"`-m '…'` are fine; the file form is the default for anything " +
|
|
155
|
+
"multi-paragraph.";
|
|
156
|
+
/** Stable header that starts the GitHub operations section (idempotency anchor). */
|
|
157
|
+
const GITHUB_OPERATIONS_HEADER = "## GitHub Operations";
|
|
98
158
|
/**
|
|
99
159
|
* The injected-reminder framing (YAG-574, Change A prerequisite). Claude Code
|
|
100
160
|
* carries this exact sentence in every system prompt so its whole reminder
|
|
@@ -249,6 +309,13 @@ export function brandSystemPrompt(original, opts = {}) {
|
|
|
249
309
|
if (!s.includes(TICKET_IMAGE_RULE_HEADER)) {
|
|
250
310
|
s = `${s}\n\n${TICKET_IMAGE_RULE_HEADER}\n${TICKET_IMAGE_RULE}`;
|
|
251
311
|
}
|
|
312
|
+
// 5b2. GitHub operations — a standing directive added alongside
|
|
313
|
+
// the ticket-image rule (same placement, same idempotency shape). It is
|
|
314
|
+
// universal knowledge, not user policy, so it rides the branded prompt
|
|
315
|
+
// instead of the repository-rules section.
|
|
316
|
+
if (!s.includes(GITHUB_OPERATIONS_HEADER)) {
|
|
317
|
+
s = `${s}\n\n${GITHUB_OPERATIONS}`;
|
|
318
|
+
}
|
|
252
319
|
// 5c. YAG-574: the injected-reminder framing (which makes the silent-turn
|
|
253
320
|
// nudge legible) and the two communication lines (Change B). Appended after
|
|
254
321
|
// everything user-provided but before the closing reminder, each guarded by
|
|
@@ -727,12 +727,59 @@ function isPipeToShell(command) {
|
|
|
727
727
|
* backticks, including inside double quotes). Matches FORBIDDEN rules only —
|
|
728
728
|
* can upgrade the classification, never relax it.
|
|
729
729
|
*/
|
|
730
|
+
const QUOTED_HEREDOC_OPEN = /^cat[ \t]+<<[ \t]*(?:'([^']+)'|"([^"]+)"|\\([A-Za-z_][A-Za-z0-9_]*))[ \t]*\r?\n/;
|
|
731
|
+
/**
|
|
732
|
+
* Recognize the `cat <<'QUOTED' … QUOTED` idiom inside a command substitution.
|
|
733
|
+
*
|
|
734
|
+
* This is the one way to pass multi-line body content to `gh`/`git` as a
|
|
735
|
+
* literal string: the quoted delimiter (`<<'EOF'`, `<<"EOF"`, or `<<\EOF`)
|
|
736
|
+
* makes the heredoc body literal — no `$`/backtick expansion — and `cat` merely
|
|
737
|
+
* prints it. A body like `rm -rf /tmp/test-db` inside such a heredoc is PROSE,
|
|
738
|
+
* not a command; sweeping it for forbidden rules is a false positive.
|
|
739
|
+
*
|
|
740
|
+
* Returns true ONLY for the exact safe shape: the whole substitution is `cat`
|
|
741
|
+
* with no arguments, followed by a heredoc whose delimiter is quoted and whose
|
|
742
|
+
* terminator is the delimiter on its own final line. Any deviation (unquoted
|
|
743
|
+
* delimiter, extra args to `cat`, a second command after the terminator)
|
|
744
|
+
* returns false and falls through to the ordinary danger scan. Safety does not
|
|
745
|
+
* hinge on this exemption: the outer command (the `gh pr create`, the `rm`) is
|
|
746
|
+
* still classified on its own merits, heredoc or not.
|
|
747
|
+
*/
|
|
748
|
+
function isLiteralQuotedCatHeredoc(inner) {
|
|
749
|
+
const text = inner.trim();
|
|
750
|
+
const m = QUOTED_HEREDOC_OPEN.exec(text);
|
|
751
|
+
if (!m)
|
|
752
|
+
return false;
|
|
753
|
+
const delim = m[1] ?? m[2] ?? m[3];
|
|
754
|
+
const bodyStart = m[0].length;
|
|
755
|
+
const lines = text.slice(bodyStart).split(/\r?\n/);
|
|
756
|
+
if (lines.length === 0)
|
|
757
|
+
return false;
|
|
758
|
+
// The final line must be the bare delimiter (the closing `)` is already
|
|
759
|
+
// stripped by extractSubstitutions).
|
|
760
|
+
if (lines[lines.length - 1].trim() !== delim)
|
|
761
|
+
return false;
|
|
762
|
+
// No line BEFORE the terminator may equal the delimiter: in real bash, a
|
|
763
|
+
// delimiter line terminates the heredoc there, turning everything after it
|
|
764
|
+
// into an executed command (e.g. a body with a bare `EOF` line followed by
|
|
765
|
+
// `rm -rf /x` would run the `rm`). Reject that shape and fall to the scan.
|
|
766
|
+
for (const line of lines.slice(0, -1)) {
|
|
767
|
+
if (line.trim() === delim)
|
|
768
|
+
return false;
|
|
769
|
+
}
|
|
770
|
+
return true;
|
|
771
|
+
}
|
|
730
772
|
function dangerScanSubstitutions(command, policy, depth) {
|
|
731
773
|
if (depth > MAX_SCAN_DEPTH)
|
|
732
774
|
return null;
|
|
733
775
|
for (const inner of extractSubstitutions(command)) {
|
|
734
776
|
if (inner.trim().length === 0)
|
|
735
777
|
continue;
|
|
778
|
+
// Quoted-heredoc cat: the body is literal data, not an executable
|
|
779
|
+
// command — skipping it avoids false "forbidden" hits on prose that happens
|
|
780
|
+
// to contain a forbidden-looking token. The outer command still classifies.
|
|
781
|
+
if (isLiteralQuotedCatHeredoc(inner))
|
|
782
|
+
continue;
|
|
736
783
|
if (isPipeToShell(inner)) {
|
|
737
784
|
return {
|
|
738
785
|
decision: "forbidden",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "1.0.0-staging.
|
|
3
|
+
"version": "1.0.0-staging.1190.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)",
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"turndown": "^7.2.4",
|
|
42
42
|
"typebox": "^1.3.15"
|
|
43
43
|
},
|
|
44
|
-
"yagniSourceSha": "
|
|
44
|
+
"yagniSourceSha": "482fa38ca9aa9105673fd305e6e252305aaa1ed5"
|
|
45
45
|
}
|