@4pm/cli 1.32.0-b → 1.34.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.
- package/dist/index.js +612 -276
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28569,6 +28569,365 @@ async function fetchCliVersion(serverUrl, version) {
|
|
|
28569
28569
|
}
|
|
28570
28570
|
}
|
|
28571
28571
|
|
|
28572
|
+
// vendor/@4pm/constants/ai-prompt-defaults.json
|
|
28573
|
+
var ai_prompt_defaults_default = {
|
|
28574
|
+
"server.knowledge.distill": `You are in the working directory of a software project named "{{projectName}}".
|
|
28575
|
+
Read the project (code, docs, config) and write a concise, shareable KNOWLEDGE ARTICLE that
|
|
28576
|
+
distils it for other engineers. Use Markdown and this structure:
|
|
28577
|
+
- a single top-level title line starting with '# '
|
|
28578
|
+
- a one-paragraph summary
|
|
28579
|
+
- '## The problem' \u2014 what problem the project solves
|
|
28580
|
+
- '## The approach' \u2014 the key decisions and how it works
|
|
28581
|
+
- '## What we learned' \u2014 reusable takeaways
|
|
28582
|
+
Output ONLY the Markdown article, no preamble or code fences around the whole thing.{{focusExtra}}`,
|
|
28583
|
+
"cli.research.policy": `You are a research assistant for the 4PM platform. Answer the user's question thoroughly and
|
|
28584
|
+
accurately, in Markdown. Use the read-only web tools (WebSearch / WebFetch) to find current, factual
|
|
28585
|
+
information when helpful, and cite sources inline where relevant.
|
|
28586
|
+
|
|
28587
|
+
STRICT CONTENT POLICY \u2014 you MUST refuse, and must not answer, if the question (or a faithful answer
|
|
28588
|
+
to it) would involve any of:
|
|
28589
|
+
- sexual, abusive, hateful, or illegal content;
|
|
28590
|
+
- political persuasion, campaigning, or partisan advocacy;
|
|
28591
|
+
- discrimination or demeaning content targeting a race, ethnicity, religion, gender, nationality, or
|
|
28592
|
+
other protected group;
|
|
28593
|
+
- probing, scanning, exploiting, or attacking the security of ANY system, network, account, device,
|
|
28594
|
+
or person you do not own (no vulnerability hunting, no exploit/malware creation, no credential or
|
|
28595
|
+
PII harvesting, no evasion of security controls). General defensive-security education is allowed.
|
|
28596
|
+
- any other seriously harmful activity.
|
|
28597
|
+
|
|
28598
|
+
When you must refuse, reply with EXACTLY one line starting with "REFUSED:" followed by a short,
|
|
28599
|
+
neutral reason, and output nothing else.
|
|
28600
|
+
|
|
28601
|
+
You have READ-ONLY web tools only. Never attempt to read local files, run shell commands, or interact
|
|
28602
|
+
with other systems.
|
|
28603
|
+
|
|
28604
|
+
---
|
|
28605
|
+
|
|
28606
|
+
Question:
|
|
28607
|
+
{{question}}`,
|
|
28608
|
+
"cli.memory.compact": "You maintain a COMPACT running memory of an ongoing assistant conversation so it can continue\nacross sessions and accounts. Merge the latest exchange into the existing memory and return the\nUPDATED memory. Keep it UNDER {{budgetChars}} characters. Use terse Markdown bullets grouped\nas: Decisions, Constraints, Open tasks, Glossary. Keep durable facts; drop chit-chat and anything\nnow obsolete. Output ONLY the updated memory text \u2014 no preamble, no code fences.\n\n=== EXISTING MEMORY ===\n{{oldMemory}}\n\n=== LATEST USER PROMPT ===\n{{prompt}}\n\n=== LATEST ASSISTANT ANSWER ===\n{{answer}}",
|
|
28609
|
+
"cli.support.answer": `You are the 4PM product support assistant. Answer the user's question about how to use 4PM
|
|
28610
|
+
using ONLY the documentation and FAQ provided below. If the answer is not in the docs, say
|
|
28611
|
+
you don't have that information and suggest contacting human support \u2014 do not invent details.
|
|
28612
|
+
{{roleNote}}
|
|
28613
|
+
Answer concisely in the user's language; reference the relevant doc heading/path when useful.
|
|
28614
|
+
|
|
28615
|
+
Return ONLY a single JSON object (no markdown fences, no prose around it) with these keys:
|
|
28616
|
+
"onTopic": boolean \u2014 false if the question is NOT about how to use the 4PM product,
|
|
28617
|
+
"sensitive": boolean \u2014 true if the question is inappropriate / abusive OR asks how to hack,
|
|
28618
|
+
bypass security, gain unauthorized access, take over or steal another
|
|
28619
|
+
user's/org's account or data, obtain credentials, or any malicious intent,
|
|
28620
|
+
"reason": string \u2014 a short reason when onTopic is false or sensitive is true, else "",
|
|
28621
|
+
"answer": string \u2014 your reply to the user, in markdown (in the user's language).
|
|
28622
|
+
Refusal policy (ADR-0237): if the question is off-topic (onTopic false) OR sensitive, DO NOT
|
|
28623
|
+
answer it. Set "answer" to this EXACT same polite refusal for BOTH cases (in the user's
|
|
28624
|
+
language, do not reveal which category or hint at the reason): "Sorry, I can only help with
|
|
28625
|
+
questions about how to use 4PM. For anything else, please contact human support." Only answer
|
|
28626
|
+
normally when onTopic is true AND sensitive is false.
|
|
28627
|
+
|
|
28628
|
+
===== DOCUMENTATION =====
|
|
28629
|
+
{{docs}}
|
|
28630
|
+
|
|
28631
|
+
===== QUESTION =====
|
|
28632
|
+
{{question}}`,
|
|
28633
|
+
"cli.support.reply_draft": "You are drafting a reply FROM the 4PM support team TO the customer in the support ticket below.\nAddress the customer's latest message, using the whole conversation for context. Be accurate,\nfriendly and concise. Write in the language the customer uses in the ticket.\nUse the documentation and FAQ below for any product facts; never invent features, prices or\ncommitments that are not in the docs \u2014 leave a clear [placeholder] for the admin to fill instead.\nFollow the admin's instructions when given. A human admin reviews and edits your draft before\nsending it.\n\nOutput ONLY the message body in markdown \u2014 no subject line, no preamble, no explanation, no code\nfences around the whole message.\n\n===== DOCUMENTATION =====\n{{docs}}\n\n===== CONTEXT =====\n{{context}}",
|
|
28634
|
+
"cli.support.outreach_draft": "You are drafting an outreach message FROM the 4PM platform team TO the customer organizations.\nUse the subject and any current draft below as the starting point. Be clear, friendly and concise.\nWrite in the language of the subject/draft (or of the admin's instructions when those are empty).\nUse the documentation and FAQ below for any product facts; never invent features, prices or\ncommitments that are not in the docs \u2014 leave a clear [placeholder] for the admin to fill instead.\nFollow the admin's instructions when given. A human admin reviews and edits your draft before\nsending it.\n\nOutput ONLY the message body in markdown \u2014 no subject line, no preamble, no explanation, no code\nfences around the whole message.\n\n===== DOCUMENTATION =====\n{{docs}}\n\n===== CONTEXT =====\n{{context}}",
|
|
28635
|
+
"cli.support.legal_draft": "You are drafting the body of one of 4PM's legal documents (Terms of Service, Privacy Policy,\nAdd-on Terms, Rented Machine Addendum, \u2026) for the platform admin to review and publish.\nWrite or revise the WHOLE document body in the target locale given in the context. Keep the\nexisting heading structure and numbering unless the admin's instructions say otherwise.\nWhen the target locale is not English and an English reference is given, follow its meaning closely.\nRules:\n- Every {{placeholder}} (e.g. {{companyName}}) must be kept verbatim \u2014 never fill or rename it.\n- Describe plans, prices, upgrades, downgrades, cancellations, renewals, refunds and add-ons ONLY\n as stated in the PLAN CATALOG and BILLING FACTS below or the documentation; never invent\n commitments, prices or legal guarantees. Where something is unknown write `[TODO: \u2026]`.\n- Plain, precise legal English (or the target language); short numbered sections and bullet lists.\n- This is a draft, not legal advice; do not add a disclaimer about that inside the document.\n\nOutput ONLY the document body in markdown \u2014 no title line, no preamble, no explanation, no code\nfences around the whole document.\n\n===== DOCUMENTATION =====\n{{docs}}\n\n===== CONTEXT =====\n{{context}}",
|
|
28636
|
+
"cli.faq.compose": "You are maintaining the 4PM product FAQ, stored as markdown in THIS git repository (your CWD).\nFrom the resolved support tickets below, distil reusable, product-general FAQ entries and add or\nupdate the appropriate markdown file(s) in this repo (e.g. an FAQ.md, or the closest existing\ntopic file \u2014 inspect the repo first and match its structure/style).\n\nRules:\n- Write generalized Q&A, NOT ticket-specific replies; never include customer names, emails, ids,\n org names or any personal data from the tickets.\n- Merge with existing entries instead of duplicating; keep the existing formatting and headings.\n- If a ticket yields no reusable FAQ value, skip it. If nothing is worth adding, make NO changes.\n- Only edit markdown documentation files; do not touch code, CI, or unrelated files.\n- Do NOT run git commit/push or open a PR yourself \u2014 just leave the edited files in the working\n tree; the surrounding tooling handles commit + PR.{{customInstruction}}\n\n===== RESOLVED TICKETS =====\n{{transcript}}\n\n===== REQUIRED FINAL OUTPUT =====\nAfter finishing all file edits, output \u2014 as the LAST thing, on its own \u2014 a single fenced JSON\nblock summarizing what you did PER TICKET (created/updated which FAQ entry, or skipped + why):\n```json\n{{ticketJsonSkeleton}}\n```\nInclude exactly one entry per ticket id above; keep each summary to one or two sentences.",
|
|
28637
|
+
"cli.scaffold.readme": "Write a concise README.md (Markdown only, no preamble) for this project from its spec JSON:\n{{specJson}}",
|
|
28638
|
+
"cli.scaffold.guide": "Write a {{guideFile}} (Markdown only, no preamble) with guidance/conventions for AI agents working in this project, derived from its spec JSON:\n{{specJson}}{{guideInstructionsBlock}}",
|
|
28639
|
+
"cli.autonomous.intake": "You are the 4PM autonomous INTAKE step (headless). Edit ONLY `USER_TODO.md`, `USER_QA.md` and\n`AI_TODO.md`. Do NOT run git, do NOT touch `AI_PROGRESS.md` / `AI_DONE.md` / any code \u2014 the 4PM cli\ncommits and publishes your book edits. Write in the language the project's `CLAUDE.md` specifies (default English).\n\n{{booksIntro}}\n\n## 1 \u2014 Approved user requests \u2192 tasks\nRead `USER_TODO.md` (columns `ID | Group | Depends | Request`). For each APPROVED `REQ-\u2026` whose `Depends`\nare already handled:\n- CLEAR request \u21D2 split it into tasks appended to `AI_TODO.md` (columns\n `ID | Priority | Tag | Depends | Group | Task description | Notes`), ids `TSK-{groupid:0000}-{taskid:0000}`\n (groupid UNIQUE + INCREASING across all history \u2014 the largest ever used in `AI_TODO.md`/`AI_DONE.md`/\n `AI_PROGRESS.md`/their git history, +1; taskid from 0001). `Depends` = the `TSK-\u2026` it needs.\n Remove the analysed request row (empty \u21D2 reset to the empty template).\n- UNCLEAR request \u21D2 do NOT guess: append a `USER_QA.md` row (`ID | Group | Depends | Original request |\n Question / options | Answer`) with a new `QA-{groupid:0000}-{qaid:0000}`, blank `Answer`; remove the request row.\nAn unapproved request stays.\n\n## 2 \u2014 Answered intake questions\nFor each `USER_QA.md` row with an `Answer`, APPROVED, and whose id is NOT in any `AI_TODO.md` `Depends`\n(those are task questions \u2014 the cli handles them; leave them alone): re-analyse the original request with the\nanswer into tasks (same rules), then remove that QA row.\n\n## Task size (MANDATORY \u2014 every task must be finishable in ONE run)\nSize each task by SCOPE and write `size: S` or `size: M` at the start of `Notes`:\n- **S** \u2014 one small change in one module/layer (\u2248 \u2264 {{sMaxFiles}} files, \u2248 \u2264 {{sMaxLines}} lines), e.g. fix a bug, add a field.\n- **M** \u2014 one feature slice across 2\u20133 layers with ONE purpose (\u2248 \u2264 {{mMaxFiles}} files, \u2248 \u2264 {{mMaxLines}} lines), e.g. one CRUD screen, one API + its UI + tests.\n- **L** \u2014 several features, a cross-cutting change, a big migration, a new subsystem, or an architecture decision.\n **Never write an L task**: split it into S/M tasks chained with `Depends`, or ask in `USER_QA.md` when the split needs a decision.\nThe counts are hints, not hard limits. Each task's `Task description` states the acceptance criteria and how to test it.\n\nWhen done, reply with one short line summarising what you added.",
|
|
28640
|
+
"cli.autonomous.implement": 'You are the 4PM autonomous IMPLEMENT step (headless). Implement exactly ONE task, then STOP.\n\nTask {{taskId}} (group {{group}}, attempt {{attempt}}):\n{{desc}}\nNotes: {{notes}}\n{{resumedNote}}\nBranches (already checked out by the 4PM cli \u2014 stay on them):\n- project root is on `{{branch}}`\n{{subLines}}\n\nRules:\n- Follow the project\'s architecture and `CLAUDE.md`. Add/update tests; run the project\'s test command and WAIT for the result.\n- Commit your work (`git add` + `git commit`) in the root and/or inside each submodule you changed.\n- Do NOT push, do NOT open pull requests, do NOT switch/create branches, do NOT edit the book files\n (USER_TODO/USER_QA/AI_TODO/AI_PROGRESS/AI_DONE) \u2014 the 4PM cli delivers and records everything.\n- If you need a human DECISION to continue (ambiguous requirement, missing information, a choice with real\n trade-offs), do NOT guess: commit what you have and report `needs-input` with the question + options.\n\nFinish with ONLY one fenced `json` block:\n{"status": "done" | "needs-input" | "failed", "summary": "<what you did / why it failed>", "question": "<only for needs-input: the question and the options>"}',
|
|
28641
|
+
"cli.autonomous.split": 'You are the 4PM autonomous SPLIT step (read-only \u2014 do not change any file). Task {{taskId}} failed to\nfinish after repeated attempts, so it is probably too big for one run. Split it into 2\u20136 smaller child tasks\nthat are each finishable in ONE run (size S or M), in execution order (each depends on the previous).\n\nTask: {{desc}}\nNotes: {{notes}}\nWhy the attempts ended: {{reasons}}\nWork already done on its WIP branch `{{branch}}` (diff against the base, may be truncated):\n```\n{{diff}}\n```\nThe first child continues from that WIP branch: note which parts are already done so they are not redone.\nEach child description must state its acceptance criteria and how to test it.\n\nReply with ONLY one fenced `json` block:\n{"children": [{"desc": "<task + acceptance criteria>", "priority": "High|Medium|Low", "size": "S|M", "notes": "<optional>"}]}',
|
|
28642
|
+
"web.generators": "You are producing {{what}} for a software project, derived strictly from its specification (the source of truth). Evaluate (when relevant): business logic, project security, AI-usage security, technical soundness, methodology/approach, and overall feasibility & coherence. Write a clear, well-structured **Markdown** document. Output ONLY the Markdown document \u2014 no preamble and no wrapping code fence.{{templateHint}}{{paramsBlock}}\nProject spec \u2014 self-describing JSON (fields with id/name/description/type/options/value):\n{{specContext}}",
|
|
28643
|
+
"web.guide": 'You are producing a {{file}} file \u2014 guidance and conventions for AI coding agents working in this project \u2014 derived strictly from its specification (the source of truth). Evaluate (when relevant): business logic, project security, AI-usage security, technical soundness, methodology/approach, and overall feasibility & coherence. Write a clear, well-structured **Markdown** document. Output ONLY the Markdown document \u2014 no preamble and no wrapping code fence.\nThe document MUST include a prominent, mandatory section near the top (e.g. "## Security") that instructs the agent to READ and STRICTLY FOLLOW the project\'s security policy file `AI_SECURITY.md` (at the repository root) before doing any work, and that its rules \u2014 secret handling, data masking, and commit rules \u2014 override anything else when they conflict.{{baseBlock}}{{extraBlock}}\nProject spec \u2014 self-describing JSON (fields with id/name/description/type/options/value):\n{{specContext}}',
|
|
28644
|
+
"web.draft_tasks": 'You are a senior software project manager. From the project specification below (the source of truth), produce a concise, well-ordered list of concrete, actionable engineering tasks to build this project. Prefer vertical slices, keep each task independently shippable, and order by dependency. Output ONLY a JSON array \u2014 no preamble, no code fence. Each element is an object with exactly these string fields:\n- "priority": one of "High", "Medium", "Low"\n- "group": a short phase/area label (e.g. "Setup", "Auth", "API", "UI")\n- "depends": comma-separated titles of tasks in THIS list it depends on (or "")\n- "description": the task as a clear imperative sentence\n- "notes": optional extra detail (or "")\n\nProject spec \u2014 self-describing JSON (fields with id/name/description/type/options/value):\n{{specContext}}',
|
|
28645
|
+
"web.spec.review": 'Review this software project specification WITHOUT changing it \u2014 you only advise. Evaluate (when relevant): business logic, project security, AI-usage security, technical soundness, methodology/approach, and overall feasibility & coherence. Reply in EXACTLY this format:\nASSESSMENT:\n<a short overall assessment of feasibility, coherence and reasonableness, in Markdown>\nISSUES:\n<one point that is unclear, inconsistent or unreasonable per line, or "none">\nDo not rewrite or output the spec \u2014 reply with only the assessment and issues. Spec \u2014 self-describing JSON (fields with id/name/description/type/options/value):\n{{specContext}}',
|
|
28646
|
+
"web.spec.compose": `You are revising a software project specification for consistency and feasibility, guided by the review in its "meta._aiReview" field. Make MINIMAL edits: keep the author's original intent and wording, only tighten coherence, fill clear gaps and link the fields together sensibly. Do NOT rewrite drastically or add scope the author did not intend. Evaluate (when relevant): business logic, project security, AI-usage security, technical soundness, methodology/approach, and overall feasibility & coherence. Then judge whether the revised spec has enough coherent information to create the project. Reply with ONLY a single-line minified JSON object (no preamble, no code fence, no pretty-printing/indentation) of the form {"changes": [{"field": "<id>", "before": "<old value>", "after": "<new value>"}], "tree": "<source directory tree>", "ok": <true|false>, "assessment": "<one short Markdown paragraph on the result>", "blockers": ["<one must-fix gap per item; empty when ok>"]}. Include a "changes" entry ONLY for each field you actually edit \u2014 do NOT echo unchanged fields and do NOT return the whole spec. Use the SAME field ids as the input; "before"/"after" carry that field's value in its native JSON type (a string for text/select fields, a JSON array of strings for multiselect fields) and MUST keep select/multiselect values within their allowed options. Set "ok" true ONLY when nothing blocks creating the project. The "tree" is a plain-text ASCII directory tree for the project's source layout, derived from the chosen architecture, source pattern, tech stack and modules (use \u251C\u2500 \u2502 \u2514\u2500 connectors; a trailing "/" marks folders). Current spec \u2014 self-describing JSON (fields with id/name/description/type/options/value):
|
|
28647
|
+
{{specContext}}`,
|
|
28648
|
+
"web.spec.suggest": "You are helping fill a software project specification. Suggest a value for {{target}} Evaluate (when relevant): business logic, project security, AI-usage security, technical soundness, methodology/approach, and overall feasibility & coherence. {{seed}}{{shape}} {{instructionBlock}}Context \u2014 the full spec so far as a self-describing JSON (each field has id/name/description/type/options/value; use it to stay coherent):\n{{specContext}}",
|
|
28649
|
+
"web.spec.group_suggest": `You are helping fill a software project specification. Suggest values for ALL fields of the "{{groupKey}}" section below, in one pass, keeping them coherent with each other and with the rest of the spec. Evaluate (when relevant): business logic, project security, AI-usage security, technical soundness, methodology/approach, and overall feasibility & coherence. For each field: if it already has a non-empty value, treat it as the author's intent \u2014 keep it and only refine/expand; otherwise fill it. For select fields return exactly one of the allowed options (or "" if none fits); for multiselect return an array of allowed options; for textarea return detailed clean Markdown; for text return a concise single-line string. Fields:
|
|
28650
|
+
{{fieldLines}}
|
|
28651
|
+
{{instructionBlock}}Reply with ONLY a single-line minified JSON object mapping each field id to its value \u2014 no preamble, no code fence, no markdown, no extra keys, no pretty-printing/indentation. Context \u2014 the spec as self-describing JSON:
|
|
28652
|
+
{{specContext}}`,
|
|
28653
|
+
"web.spec.subagents_suggest": 'You are helping declare the custom AI subagents of a software project. Each subagent becomes a ".claude/agents/<name>.md" file the coding assistant can delegate to. Propose a small, focused set (typically 2\u20135) of subagents that fit THIS project, coherent with its architecture, tech stack and modules. Evaluate (when relevant): business logic, project security, AI-usage security, technical soundness, methodology/approach, and overall feasibility & coherence. Keep every already-declared subagent (refine its description if useful) and add the missing ones. Each subagent has a "name" (a short kebab-case identifier, e.g. "code-reviewer") and a "description" (one or two sentences on its role and when to use it). Already declared: {{current}}. {{instructionBlock}}Reply with ONLY a single-line minified JSON array of {"name","description"} objects \u2014 no preamble, no code fence, no markdown, no extra keys, no pretty-printing/indentation. Context \u2014 the spec as self-describing JSON:\n{{specContext}}',
|
|
28654
|
+
"web.spec.subagent_desc": `You are helping declare the custom AI subagents of a software project. Each subagent becomes a ".claude/agents/<name>.md" file the coding assistant can delegate to. Write the description for {{who}}: one or two sentences on its role and when to use it, specific to THIS project's architecture, tech stack and modules. Evaluate (when relevant): business logic, project security, AI-usage security, technical soundness, methodology/approach, and overall feasibility & coherence. {{seed}}{{others}}{{instructionBlock}}Reply with ONLY the description text \u2014 no preamble, no code fence, no name, no JSON, no quotes. Context \u2014 the spec as self-describing JSON:
|
|
28655
|
+
{{specContext}}`,
|
|
28656
|
+
"web.template.analyze": "You are assessing a 4PM project-template (scaffold) update WITHOUT changing anything \u2014 advisory only. This project was scaffolded from template version {{localVersion}}; the latest is {{latestVersion}}. The template changelog for the versions this project would gain:\n{{changelog}}\n\nInspect the project's current files (they may have been customized since scaffolding) and report, in clear Markdown:\n1. What the update changes (config, hooks, conventions, layout).\n2. Which of THIS project's files are affected.\n3. Likely conflicts with local customizations (call out anything risky to overwrite).\n4. Overall effort and risk.\nDo NOT edit, create, delete, or move any file, and do NOT run git write commands \u2014 output only the report.{{extras}}",
|
|
28657
|
+
"web.template.update": 'You are applying a 4PM project-template (scaffold) update as a REVIEWED change \u2014 never a blind overwrite, and never a merge into the main branch. This project was scaffolded from template version {{localVersion}}; update it to {{latestVersion}}. The template changelog for the versions to apply:\n{{changelog}}\n\nSteps (do them in order; stop and report if one cannot be completed):\n{{resolveBase}}1. Run "git fetch origin", then create and switch to a new git branch "{{branch}}" FROM "origin/{{baseRef}}" (never from whatever branch is currently checked out): git checkout -B "{{branch}}" "origin/{{baseRef}}".\n2. Apply the template changes above to this project. The files may have been customized \u2014 MERGE the changes, preserving local customizations; never clobber intentional edits. Add new template files/hooks/config as needed.\n3. Update (or create) "./.4pm/.4pm.json" so its "templateVersion" is "{{latestVersion}}", keeping any other fields.\n4. Commit everything on "{{branch}}" with a clear message, then PUSH the branch to the remote.\n5. Open a PULL REQUEST from "{{branch}}" INTO "{{baseRef}}" using the host CLI that matches the remote \u2014 "gh pr create --base {{baseRef}} --head {{branch}}" for GitHub, "glab mr create --target-branch {{baseRef}} --source-branch {{branch}}" for GitLab (do NOT merge it). Capture the created PR/MR number and its URL.\nDo NOT stop to ask for confirmation \u2014 you are authorized to run these git and PR commands.{{extras}}\n\nWhen finished, reply with ONLY a single fenced code block tagged json (no prose before or after) of the form {"status": "success" | "blocked" | "failed", "pullRequestId": "<the PR/MR number, or null>", "pullRequestUrl": "<the PR/MR URL, or null>", "branch": "<the branch you created, e.g. {{branch}}>", "message": "<one short line: what changed on success, or WHY it could not complete>"}. Use "success" ONLY if the pull request was actually created and you have its id/number; if you were blocked (e.g. a command needed approval, or "gh"/"glab" is missing or not authenticated) use "blocked"; use "failed" for an outright error. Never report "success" without a real pull request id.',
|
|
28658
|
+
"web.checklist.eval": `You are checking a checklist against the {{contextLabel}} of this project. You MAY read the project's files (read-only) but MUST NOT write anything.
|
|
28659
|
+
|
|
28660
|
+
Checklist "{{name}}" \u2014 decide a verdict for EACH item: pass, fail, skip, or na.
|
|
28661
|
+
Items:
|
|
28662
|
+
{{lines}}
|
|
28663
|
+
|
|
28664
|
+
{{contextLabel}}:
|
|
28665
|
+
{{contextText}}
|
|
28666
|
+
|
|
28667
|
+
Reply with EXACTLY one line per item, in the form:
|
|
28668
|
+
[<ID>] <pass|fail|skip|na> \u2014 <one-line reason>
|
|
28669
|
+
No other text, no code fences.`,
|
|
28670
|
+
"web.checklist.author": `You are helping author a reusable project checklist (a "definition of ready"-style rubric). Produce the checklist's items based on the author's instruction, keeping the result coherent and actionable. Treat the current items as the author's intent: keep the ones that should stay, refine wording where it helps, and add the missing ones.
|
|
28671
|
+
Current items (JSON): {{current}}
|
|
28672
|
+
Author's instruction:
|
|
28673
|
+
"""
|
|
28674
|
+
{{instruction}}
|
|
28675
|
+
"""
|
|
28676
|
+
Reply with ONLY a single-line minified JSON array of the FULL new item list \u2014 each element an object with "text" (string, required), optional "description" (string) and optional "required" (boolean). No ids, no preamble, no code fence, no pretty-printing.`,
|
|
28677
|
+
"web.memo.compose": `You are turning a set of a teammate's project notes into ONE community forum post for the team. The notes may reference pasted screenshots/diagrams as [Image#N] tokens \u2014 for each, use the Read tool on the file path the token was rewritten to, look at the image, and weave what it shows into the post where relevant. Write a clear, helpful, self-contained post (not a raw copy of the notes). Reply with ONLY a single fenced code block tagged json containing an object of the form {"title": "<a short title, at most 200 characters>", "body": "<the post body as plain text, at most 10000 characters>"} \u2014 no prose before or after the fence.
|
|
28678
|
+
|
|
28679
|
+
Notes:
|
|
28680
|
+
{{notes}}`,
|
|
28681
|
+
"web.verify": "You are verifying entries in this project's {{verifyBook}} book. You MAY read the project's files (read-only) but MUST NOT write anything.\n\nSelected entries (use all of them together as shared context, then answer each one):\n{{entries}}\n\nQuestion to answer for EACH entry: {{question}}\n\nReply with EXACTLY one line per entry, in the form:\n[<ID>] <concise one-line answer>\nNo other text, no code fences.",
|
|
28682
|
+
"web.git.resolve": "Resolve all git merge conflicts in {{repoScope}} of the current project. Edit each conflicted file to a correct merged result (remove all conflict markers), then run `git add` on each. Do NOT commit \u2014 leave the merge staged for review."
|
|
28683
|
+
};
|
|
28684
|
+
|
|
28685
|
+
// vendor/@4pm/constants/ai-prompts.ts
|
|
28686
|
+
var DEFAULTS = ai_prompt_defaults_default;
|
|
28687
|
+
function renderPrompt(template, vars) {
|
|
28688
|
+
return template.replace(
|
|
28689
|
+
/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g,
|
|
28690
|
+
(match, name) => Object.prototype.hasOwnProperty.call(vars, name) ? String(vars[name]) : match
|
|
28691
|
+
);
|
|
28692
|
+
}
|
|
28693
|
+
var PROMPT_META = [
|
|
28694
|
+
{
|
|
28695
|
+
key: "server.knowledge.distill",
|
|
28696
|
+
ownerApp: "server",
|
|
28697
|
+
phase: 1,
|
|
28698
|
+
title: "Knowledge distillation",
|
|
28699
|
+
description: "Turns a project into a shareable knowledge article (read-only agent in the project folder).",
|
|
28700
|
+
requiredVars: ["projectName"]
|
|
28701
|
+
},
|
|
28702
|
+
{
|
|
28703
|
+
key: "cli.research.policy",
|
|
28704
|
+
ownerApp: "cli",
|
|
28705
|
+
phase: 1,
|
|
28706
|
+
title: "AI Research \u2014 system policy",
|
|
28707
|
+
description: "System instruction + content policy prepended to an org Research question.",
|
|
28708
|
+
requiredVars: ["question"]
|
|
28709
|
+
},
|
|
28710
|
+
{
|
|
28711
|
+
key: "cli.memory.compact",
|
|
28712
|
+
ownerApp: "cli",
|
|
28713
|
+
phase: 1,
|
|
28714
|
+
title: "Console memory compaction",
|
|
28715
|
+
description: "Merges the latest exchange into the rolling per-project AI memory, budget-bounded.",
|
|
28716
|
+
requiredVars: ["budgetChars", "oldMemory", "prompt", "answer"]
|
|
28717
|
+
},
|
|
28718
|
+
{
|
|
28719
|
+
key: "cli.support.answer",
|
|
28720
|
+
ownerApp: "cli",
|
|
28721
|
+
phase: 1,
|
|
28722
|
+
title: "AI Help \u2014 support answer",
|
|
28723
|
+
description: "Answers a user/admin product question from the KB docs, with the inline moderation JSON contract. Edit with care: the response must stay a single JSON object with onTopic/sensitive/reason/answer.",
|
|
28724
|
+
requiredVars: ["roleNote", "docs", "question"]
|
|
28725
|
+
},
|
|
28726
|
+
{
|
|
28727
|
+
key: "cli.support.reply_draft",
|
|
28728
|
+
ownerApp: "cli",
|
|
28729
|
+
phase: 1,
|
|
28730
|
+
title: "Admin draft \u2014 ticket reply",
|
|
28731
|
+
description: "Drafts a support-team reply to a ticket (freeform markdown).",
|
|
28732
|
+
requiredVars: ["docs", "context"]
|
|
28733
|
+
},
|
|
28734
|
+
{
|
|
28735
|
+
key: "cli.support.outreach_draft",
|
|
28736
|
+
ownerApp: "cli",
|
|
28737
|
+
phase: 1,
|
|
28738
|
+
title: "Admin draft \u2014 outreach message",
|
|
28739
|
+
description: "Drafts an outreach message to customer orgs (freeform markdown).",
|
|
28740
|
+
requiredVars: ["docs", "context"]
|
|
28741
|
+
},
|
|
28742
|
+
{
|
|
28743
|
+
key: "cli.support.legal_draft",
|
|
28744
|
+
ownerApp: "cli",
|
|
28745
|
+
phase: 1,
|
|
28746
|
+
title: "Admin draft \u2014 legal document body",
|
|
28747
|
+
description: "Drafts/revises a legal document body in a locale, keeping legal {{placeholders}} verbatim.",
|
|
28748
|
+
requiredVars: ["docs", "context"]
|
|
28749
|
+
},
|
|
28750
|
+
{
|
|
28751
|
+
key: "cli.faq.compose",
|
|
28752
|
+
ownerApp: "cli",
|
|
28753
|
+
phase: 1,
|
|
28754
|
+
title: "Ticket \u2192 FAQ synthesis",
|
|
28755
|
+
description: "Agent-write run that distils resolved tickets into FAQ markdown + a per-ticket JSON summary. Keep the required JSON output block and the {{ticketJsonSkeleton}} intact.",
|
|
28756
|
+
requiredVars: ["transcript", "ticketJsonSkeleton"]
|
|
28757
|
+
},
|
|
28758
|
+
{
|
|
28759
|
+
key: "cli.scaffold.readme",
|
|
28760
|
+
ownerApp: "cli",
|
|
28761
|
+
phase: 1,
|
|
28762
|
+
title: "Scaffold \u2014 README author",
|
|
28763
|
+
description: "Writes the initial README.md for a new project from its spec JSON (on project create).",
|
|
28764
|
+
requiredVars: ["specJson"]
|
|
28765
|
+
},
|
|
28766
|
+
{
|
|
28767
|
+
key: "cli.scaffold.guide",
|
|
28768
|
+
ownerApp: "cli",
|
|
28769
|
+
phase: 1,
|
|
28770
|
+
title: "Scaffold \u2014 agent guide author",
|
|
28771
|
+
description: "Writes the project's CLAUDE.md / AGENT.md guide from its spec JSON (on project create).",
|
|
28772
|
+
requiredVars: ["guideFile", "specJson"]
|
|
28773
|
+
},
|
|
28774
|
+
{
|
|
28775
|
+
key: "cli.autonomous.intake",
|
|
28776
|
+
ownerApp: "cli",
|
|
28777
|
+
phase: 1,
|
|
28778
|
+
title: "Autonomous \u2014 intake",
|
|
28779
|
+
description: "Analyses approved user requests + answered intake questions into sized AI tasks (books only; no git/code).",
|
|
28780
|
+
requiredVars: ["booksIntro", "sMaxFiles", "sMaxLines", "mMaxFiles", "mMaxLines"]
|
|
28781
|
+
},
|
|
28782
|
+
{
|
|
28783
|
+
key: "cli.autonomous.implement",
|
|
28784
|
+
ownerApp: "cli",
|
|
28785
|
+
phase: 1,
|
|
28786
|
+
title: "Autonomous \u2014 implement",
|
|
28787
|
+
description: "Implements exactly one task on the prepared branches (commit only). Keep the final JSON status block intact.",
|
|
28788
|
+
requiredVars: ["taskId", "desc", "branch", "subLines"]
|
|
28789
|
+
},
|
|
28790
|
+
{
|
|
28791
|
+
key: "cli.autonomous.split",
|
|
28792
|
+
ownerApp: "cli",
|
|
28793
|
+
phase: 1,
|
|
28794
|
+
title: "Autonomous \u2014 split",
|
|
28795
|
+
description: "Read-only: proposes smaller child tasks when a task repeatedly fails. Keep the final JSON children block intact.",
|
|
28796
|
+
requiredVars: ["taskId", "desc", "reasons", "branch", "diff"]
|
|
28797
|
+
},
|
|
28798
|
+
{
|
|
28799
|
+
key: "web.generators",
|
|
28800
|
+
ownerApp: "web",
|
|
28801
|
+
phase: 2,
|
|
28802
|
+
title: "Content generator",
|
|
28803
|
+
description: "Produces a report / estimation / WBS / proposal / feasibility Markdown document from the project spec.",
|
|
28804
|
+
requiredVars: ["what", "specContext"]
|
|
28805
|
+
},
|
|
28806
|
+
{
|
|
28807
|
+
key: "web.guide",
|
|
28808
|
+
ownerApp: "web",
|
|
28809
|
+
phase: 2,
|
|
28810
|
+
title: "AI guide (CLAUDE.md / AGENT.md)",
|
|
28811
|
+
description: "Writes/refines the project's agent-guide file from the spec. Keep the mandatory AI_SECURITY.md section.",
|
|
28812
|
+
requiredVars: ["file", "specContext"]
|
|
28813
|
+
},
|
|
28814
|
+
{
|
|
28815
|
+
key: "web.draft_tasks",
|
|
28816
|
+
ownerApp: "web",
|
|
28817
|
+
phase: 2,
|
|
28818
|
+
title: "Draft tasks from spec",
|
|
28819
|
+
description: "Breaks the spec into a JSON array of engineering tasks. Keep the required JSON array output shape.",
|
|
28820
|
+
requiredVars: ["specContext"]
|
|
28821
|
+
},
|
|
28822
|
+
{
|
|
28823
|
+
key: "web.spec.review",
|
|
28824
|
+
ownerApp: "web",
|
|
28825
|
+
phase: 2,
|
|
28826
|
+
title: "Spec review (advisory)",
|
|
28827
|
+
description: "Advises on the whole spec in the fixed ASSESSMENT/ISSUES format (no rewrite). Keep that reply format.",
|
|
28828
|
+
requiredVars: ["specContext"]
|
|
28829
|
+
},
|
|
28830
|
+
{
|
|
28831
|
+
key: "web.spec.compose",
|
|
28832
|
+
ownerApp: "web",
|
|
28833
|
+
phase: 2,
|
|
28834
|
+
title: "Spec compose (gates create)",
|
|
28835
|
+
description: "Revises the spec with minimal edits + judges readiness. GATES project creation \u2014 keep the exact JSON object shape (changes/tree/ok/assessment/blockers).",
|
|
28836
|
+
requiredVars: ["specContext"]
|
|
28837
|
+
},
|
|
28838
|
+
{
|
|
28839
|
+
key: "web.spec.suggest",
|
|
28840
|
+
ownerApp: "web",
|
|
28841
|
+
phase: 2,
|
|
28842
|
+
title: "Spec field suggest (\u2728)",
|
|
28843
|
+
description: "Suggests a value for one spec field. {{shape}} carries the reply-format rule the parser depends on.",
|
|
28844
|
+
requiredVars: ["target", "shape", "specContext"]
|
|
28845
|
+
},
|
|
28846
|
+
{
|
|
28847
|
+
key: "web.spec.group_suggest",
|
|
28848
|
+
ownerApp: "web",
|
|
28849
|
+
phase: 2,
|
|
28850
|
+
title: "Spec section suggest",
|
|
28851
|
+
description: "Suggests values for a whole wizard section. Keep the single-line JSON object (field id \u2192 value) output shape.",
|
|
28852
|
+
requiredVars: ["groupKey", "fieldLines", "specContext"]
|
|
28853
|
+
},
|
|
28854
|
+
{
|
|
28855
|
+
key: "web.spec.subagents_suggest",
|
|
28856
|
+
ownerApp: "web",
|
|
28857
|
+
phase: 2,
|
|
28858
|
+
title: "Subagents suggest",
|
|
28859
|
+
description: "Proposes the project's AI subagents. Keep the JSON array of {name,description} output shape.",
|
|
28860
|
+
requiredVars: ["current", "specContext"]
|
|
28861
|
+
},
|
|
28862
|
+
{
|
|
28863
|
+
key: "web.spec.subagent_desc",
|
|
28864
|
+
ownerApp: "web",
|
|
28865
|
+
phase: 2,
|
|
28866
|
+
title: "Subagent description suggest",
|
|
28867
|
+
description: "Writes one subagent's description (plain text, no JSON).",
|
|
28868
|
+
requiredVars: ["who", "specContext"]
|
|
28869
|
+
},
|
|
28870
|
+
{
|
|
28871
|
+
key: "web.template.analyze",
|
|
28872
|
+
ownerApp: "web",
|
|
28873
|
+
phase: 2,
|
|
28874
|
+
title: "Template update \u2014 analyze impact",
|
|
28875
|
+
description: "Read-only: reports what a project-template update changes + affected files + risk.",
|
|
28876
|
+
requiredVars: ["localVersion", "latestVersion", "changelog"]
|
|
28877
|
+
},
|
|
28878
|
+
{
|
|
28879
|
+
key: "web.template.update",
|
|
28880
|
+
ownerApp: "web",
|
|
28881
|
+
phase: 2,
|
|
28882
|
+
title: "Template update \u2014 apply (branch + PR)",
|
|
28883
|
+
description: "Agent-write: applies the template update on a branch + opens a PR. Keep the branch/PR steps + the final JSON status block.",
|
|
28884
|
+
requiredVars: ["latestVersion", "branch", "baseRef", "changelog"]
|
|
28885
|
+
},
|
|
28886
|
+
{
|
|
28887
|
+
key: "web.checklist.eval",
|
|
28888
|
+
ownerApp: "web",
|
|
28889
|
+
phase: 2,
|
|
28890
|
+
title: "Checklist evaluate",
|
|
28891
|
+
description: "Read-only: verdict (pass/fail/skip/na) per checklist item vs content. Keep the one-line-per-item reply format.",
|
|
28892
|
+
requiredVars: ["contextLabel", "name", "lines", "contextText"]
|
|
28893
|
+
},
|
|
28894
|
+
{
|
|
28895
|
+
key: "web.checklist.author",
|
|
28896
|
+
ownerApp: "web",
|
|
28897
|
+
phase: 2,
|
|
28898
|
+
title: "Checklist authoring",
|
|
28899
|
+
description: "Proposes the full checklist item list from an instruction + current items. Keep the JSON array output shape.",
|
|
28900
|
+
requiredVars: ["current", "instruction"]
|
|
28901
|
+
},
|
|
28902
|
+
{
|
|
28903
|
+
key: "web.memo.compose",
|
|
28904
|
+
ownerApp: "web",
|
|
28905
|
+
phase: 2,
|
|
28906
|
+
title: "Memo \u2192 community post",
|
|
28907
|
+
description: "Turns a teammate's notes (with [Image#N] tokens) into one forum post. Keep the fenced JSON {title,body} output.",
|
|
28908
|
+
requiredVars: ["notes"]
|
|
28909
|
+
},
|
|
28910
|
+
{
|
|
28911
|
+
key: "web.verify",
|
|
28912
|
+
ownerApp: "web",
|
|
28913
|
+
phase: 2,
|
|
28914
|
+
title: "AI Verify (book entries)",
|
|
28915
|
+
description: "Read-only: answers a question per selected book entry. Keep the one-line-per-entry reply format.",
|
|
28916
|
+
requiredVars: ["verifyBook", "entries", "question"]
|
|
28917
|
+
},
|
|
28918
|
+
{
|
|
28919
|
+
key: "web.git.resolve",
|
|
28920
|
+
ownerApp: "web",
|
|
28921
|
+
phase: 2,
|
|
28922
|
+
title: "Git \u2014 resolve conflicts",
|
|
28923
|
+
description: "Agent: resolves merge conflicts + stages (no commit).",
|
|
28924
|
+
requiredVars: ["repoScope"]
|
|
28925
|
+
}
|
|
28926
|
+
];
|
|
28927
|
+
var AI_PROMPTS = Object.fromEntries(
|
|
28928
|
+
PROMPT_META.map((m2) => [m2.key, { ...m2, defaultTemplate: DEFAULTS[m2.key] ?? "" }])
|
|
28929
|
+
);
|
|
28930
|
+
|
|
28572
28931
|
// vendor/@4pm/constants/error-codes.ts
|
|
28573
28932
|
var ErrorCode = {
|
|
28574
28933
|
// Common
|
|
@@ -28587,6 +28946,11 @@ var ErrorCode = {
|
|
|
28587
28946
|
FEATURE_NOT_AVAILABLE: "FEATURE_NOT_AVAILABLE",
|
|
28588
28947
|
// Request body exceeded the HTTP gateway's JSON size limit (see HTTP_JSON_BODY_LIMIT_BYTES).
|
|
28589
28948
|
PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE",
|
|
28949
|
+
// Unified composer attachments (ADR-0388): an upload over ATTACHMENT_MAX_BYTES, a MIME outside the
|
|
28950
|
+
// image + file lists, or a submit carrying more than ATTACHMENT_MAX_COUNT refs.
|
|
28951
|
+
ATTACHMENT_TOO_LARGE: "ATTACHMENT_TOO_LARGE",
|
|
28952
|
+
ATTACHMENT_TYPE_UNSUPPORTED: "ATTACHMENT_TYPE_UNSUPPORTED",
|
|
28953
|
+
ATTACHMENT_LIMIT_EXCEEDED: "ATTACHMENT_LIMIT_EXCEEDED",
|
|
28590
28954
|
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
28591
28955
|
// auth
|
|
28592
28956
|
INVALID_CREDENTIALS: "INVALID_CREDENTIALS",
|
|
@@ -28754,6 +29118,14 @@ var ErrorCode = {
|
|
|
28754
29118
|
// web blocks the dispatch and shows an actionable "spec too large" message instead of letting
|
|
28755
29119
|
// the server reject it as a confusing VALIDATION_FAILED.
|
|
28756
29120
|
AI_PROMPT_TOO_LARGE: "AI_PROMPT_TOO_LARGE",
|
|
29121
|
+
// Server-managed prompt catalog (ADR-0381): the key is not in the @4pm/constants registry.
|
|
29122
|
+
AI_PROMPT_KEY_UNKNOWN: "AI_PROMPT_KEY_UNKNOWN",
|
|
29123
|
+
// The submitted template is missing a required `{{var}}` the builder depends on.
|
|
29124
|
+
AI_PROMPT_TEMPLATE_INVALID: "AI_PROMPT_TEMPLATE_INVALID",
|
|
29125
|
+
// Prompt editing is disabled by the AI_PROMPTS_READONLY env flag (use the defaults).
|
|
29126
|
+
AI_PROMPTS_READONLY: "AI_PROMPTS_READONLY",
|
|
29127
|
+
// Terms & policies editing is disabled by the LEGAL_READONLY env flag.
|
|
29128
|
+
LEGAL_READONLY: "LEGAL_READONLY",
|
|
28757
29129
|
REPO_NOT_FOUND: "REPO_NOT_FOUND",
|
|
28758
29130
|
// invitation
|
|
28759
29131
|
INVITATION_TOKEN_INVALID: "INVITATION_TOKEN_INVALID",
|
|
@@ -33188,6 +33560,59 @@ function prettyResult(text) {
|
|
|
33188
33560
|
return trimmed;
|
|
33189
33561
|
}
|
|
33190
33562
|
|
|
33563
|
+
// vendor/@4pm/dto/attachment.ts
|
|
33564
|
+
var ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024;
|
|
33565
|
+
var ATTACHMENT_MAX_COUNT = 10;
|
|
33566
|
+
var ATTACHMENT_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
|
|
33567
|
+
var ATTACHMENT_FILE_MIME_TYPES = [
|
|
33568
|
+
"application/pdf",
|
|
33569
|
+
"application/zip",
|
|
33570
|
+
"application/x-zip-compressed",
|
|
33571
|
+
"application/msword",
|
|
33572
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
33573
|
+
"application/vnd.ms-excel",
|
|
33574
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
33575
|
+
"text/plain",
|
|
33576
|
+
"text/csv",
|
|
33577
|
+
"application/json"
|
|
33578
|
+
];
|
|
33579
|
+
var ATTACHMENT_FILE_EXTENSIONS = [".pdf", ".zip", ".doc", ".docx", ".xls", ".xlsx", ".txt", ".csv", ".json"];
|
|
33580
|
+
var ATTACHMENT_ACCEPT = [...ATTACHMENT_IMAGE_MIME_TYPES, ...ATTACHMENT_FILE_MIME_TYPES, ...ATTACHMENT_FILE_EXTENSIONS].join(",");
|
|
33581
|
+
function attachmentExt(mime) {
|
|
33582
|
+
switch (mime) {
|
|
33583
|
+
case "image/png":
|
|
33584
|
+
return "png";
|
|
33585
|
+
case "image/jpeg":
|
|
33586
|
+
return "jpg";
|
|
33587
|
+
case "image/webp":
|
|
33588
|
+
return "webp";
|
|
33589
|
+
case "image/gif":
|
|
33590
|
+
return "gif";
|
|
33591
|
+
case "application/pdf":
|
|
33592
|
+
return "pdf";
|
|
33593
|
+
case "application/zip":
|
|
33594
|
+
case "application/x-zip-compressed":
|
|
33595
|
+
return "zip";
|
|
33596
|
+
case "application/msword":
|
|
33597
|
+
return "doc";
|
|
33598
|
+
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
|
33599
|
+
return "docx";
|
|
33600
|
+
case "application/vnd.ms-excel":
|
|
33601
|
+
return "xls";
|
|
33602
|
+
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
|
33603
|
+
return "xlsx";
|
|
33604
|
+
case "text/plain":
|
|
33605
|
+
return "txt";
|
|
33606
|
+
case "text/csv":
|
|
33607
|
+
return "csv";
|
|
33608
|
+
case "application/json":
|
|
33609
|
+
return "json";
|
|
33610
|
+
default:
|
|
33611
|
+
return null;
|
|
33612
|
+
}
|
|
33613
|
+
}
|
|
33614
|
+
var ATTACHMENT_ID_EXT_PATTERN = "png|jpg|webp|gif|pdf|zip|doc|docx|xls|xlsx|txt|csv|json";
|
|
33615
|
+
|
|
33191
33616
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
33192
33617
|
var external_exports = {};
|
|
33193
33618
|
__export(external_exports, {
|
|
@@ -37243,23 +37668,13 @@ var deletedFilterSchema = external_exports.enum(["true", "false"]).transform((v2
|
|
|
37243
37668
|
// vendor/@4pm/dto/command.ts
|
|
37244
37669
|
var COMMAND_MAX_LEN = 8e3;
|
|
37245
37670
|
var AI_PROMPT_MAX_LEN = 5e5;
|
|
37246
|
-
var
|
|
37247
|
-
var
|
|
37248
|
-
var
|
|
37249
|
-
|
|
37671
|
+
var COMMAND_IMAGE_MAX_COUNT = ATTACHMENT_MAX_COUNT;
|
|
37672
|
+
var COMMAND_IMAGE_MIME_TYPES = [...ATTACHMENT_IMAGE_MIME_TYPES, ...ATTACHMENT_FILE_MIME_TYPES];
|
|
37673
|
+
var COMMAND_IMAGE_ID_RE = new RegExp(
|
|
37674
|
+
`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\.(${ATTACHMENT_ID_EXT_PATTERN})$`
|
|
37675
|
+
);
|
|
37250
37676
|
function commandImageExt(mime) {
|
|
37251
|
-
|
|
37252
|
-
case "image/png":
|
|
37253
|
-
return "png";
|
|
37254
|
-
case "image/jpeg":
|
|
37255
|
-
return "jpg";
|
|
37256
|
-
case "image/webp":
|
|
37257
|
-
return "webp";
|
|
37258
|
-
case "image/gif":
|
|
37259
|
-
return "gif";
|
|
37260
|
-
default:
|
|
37261
|
-
return "bin";
|
|
37262
|
-
}
|
|
37677
|
+
return attachmentExt(mime) ?? "bin";
|
|
37263
37678
|
}
|
|
37264
37679
|
var commandImageRefSchema = external_exports.object({
|
|
37265
37680
|
// Constrained to `<uuidv4>.<ext>` (the only id the upload mints) so a client-supplied id can never
|
|
@@ -37267,7 +37682,9 @@ var commandImageRefSchema = external_exports.object({
|
|
|
37267
37682
|
id: external_exports.string().regex(COMMAND_IMAGE_ID_RE),
|
|
37268
37683
|
placeholder: external_exports.string().min(1).max(40),
|
|
37269
37684
|
name: external_exports.string().max(255),
|
|
37270
|
-
mime: external_exports.enum(COMMAND_IMAGE_MIME_TYPES)
|
|
37685
|
+
mime: external_exports.enum(COMMAND_IMAGE_MIME_TYPES),
|
|
37686
|
+
/** Image or file (ADR-0388); omitted by older clients ⇒ derived from `mime`. */
|
|
37687
|
+
category: external_exports.enum(["image", "file"]).optional()
|
|
37271
37688
|
});
|
|
37272
37689
|
var aiRunConfigSchema = external_exports.object({
|
|
37273
37690
|
/** `--model` override for this run (must match the resolved provider's model id). */
|
|
@@ -37444,6 +37861,14 @@ var putGitCredentialRequestSchema = external_exports.object({
|
|
|
37444
37861
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["host"], message: "host is required for GitHub Enterprise" });
|
|
37445
37862
|
}
|
|
37446
37863
|
});
|
|
37864
|
+
var putGitLabCredentialRequestSchema = external_exports.object({
|
|
37865
|
+
host: hostSchema.optional(),
|
|
37866
|
+
group: external_exports.string().trim().min(1).max(200),
|
|
37867
|
+
/** The access token; omit to keep the stored one (required on first save — enforced server-side). */
|
|
37868
|
+
token: external_exports.string().trim().min(1).max(512).optional(),
|
|
37869
|
+
apiBaseUrl: external_exports.string().trim().url().startsWith("https://").max(500).nullable().optional(),
|
|
37870
|
+
caCert: external_exports.string().trim().max(32768).nullable().optional()
|
|
37871
|
+
});
|
|
37447
37872
|
function parseRepoUrl(url) {
|
|
37448
37873
|
const raw = url.trim();
|
|
37449
37874
|
const scp = /^[\w.-]+@([^:/\s]+):([^/\s]+)\/(.+?)(?:\.git)?\/?$/.exec(raw);
|
|
@@ -38077,6 +38502,23 @@ import { tmpdir as tmpdir2 } from "os";
|
|
|
38077
38502
|
import { dirname as dirname3, join as join6, resolve } from "path";
|
|
38078
38503
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
38079
38504
|
import { promisify } from "util";
|
|
38505
|
+
|
|
38506
|
+
// src/core/prompt-overrides.ts
|
|
38507
|
+
var overrides = {};
|
|
38508
|
+
function setPromptOverrides(map) {
|
|
38509
|
+
overrides = map ?? {};
|
|
38510
|
+
}
|
|
38511
|
+
function getPromptOverride(key) {
|
|
38512
|
+
const byLocale = overrides[key];
|
|
38513
|
+
if (!byLocale) return null;
|
|
38514
|
+
return byLocale[getLocale()] ?? byLocale.en ?? null;
|
|
38515
|
+
}
|
|
38516
|
+
function resolveCliPrompt(key, vars) {
|
|
38517
|
+
const template = getPromptOverride(key) ?? AI_PROMPTS[key]?.defaultTemplate ?? "";
|
|
38518
|
+
return renderPrompt(template, vars);
|
|
38519
|
+
}
|
|
38520
|
+
|
|
38521
|
+
// src/core/scaffold.ts
|
|
38080
38522
|
var run = promisify(execFile);
|
|
38081
38523
|
function reposOf(spec) {
|
|
38082
38524
|
const repos = spec?.repos;
|
|
@@ -38492,18 +38934,22 @@ ${(sa.description || "").trim()}
|
|
|
38492
38934
|
generate3,
|
|
38493
38935
|
join6(target, "README.md"),
|
|
38494
38936
|
"README.md",
|
|
38495
|
-
|
|
38496
|
-
|
|
38937
|
+
// Admin override (ADR-0381) for `cli.scaffold.readme`, else the shared registry default.
|
|
38938
|
+
resolveCliPrompt("cli.scaffold.readme", { specJson })
|
|
38497
38939
|
);
|
|
38498
38940
|
await generateFile(
|
|
38499
38941
|
generate3,
|
|
38500
38942
|
join6(target, guideFile),
|
|
38501
38943
|
guideFile,
|
|
38502
|
-
|
|
38503
|
-
|
|
38944
|
+
// Admin override (ADR-0381) for `cli.scaffold.guide`, else the shared registry default.
|
|
38945
|
+
resolveCliPrompt("cli.scaffold.guide", {
|
|
38946
|
+
guideFile,
|
|
38947
|
+
specJson,
|
|
38948
|
+
guideInstructionsBlock: guideInstructions ? `
|
|
38504
38949
|
Also incorporate these additional instructions/content:
|
|
38505
38950
|
${guideInstructions}
|
|
38506
|
-
` : ""
|
|
38951
|
+
` : ""
|
|
38952
|
+
}),
|
|
38507
38953
|
guideInstructions
|
|
38508
38954
|
);
|
|
38509
38955
|
} finally {
|
|
@@ -40280,7 +40726,7 @@ async function materializeImages(physicRoot, commandId, images, fetch2) {
|
|
|
40280
40726
|
continue;
|
|
40281
40727
|
}
|
|
40282
40728
|
const ext = commandImageExt(reply.mime ?? img.mime);
|
|
40283
|
-
const file = join12(dir2, `
|
|
40729
|
+
const file = join12(dir2, `attachment-${n}.${ext}`);
|
|
40284
40730
|
writeFileSync6(file, Buffer.from(reply.dataBase64, "base64"));
|
|
40285
40731
|
map.set(img.placeholder, file);
|
|
40286
40732
|
} catch (err) {
|
|
@@ -40538,13 +40984,13 @@ var ENV_ALLOW = /* @__PURE__ */ new Set([
|
|
|
40538
40984
|
"PROGRAMFILES"
|
|
40539
40985
|
]);
|
|
40540
40986
|
var ENV_ALLOW_PREFIXES = ["LC_", "XDG_", "ANTHROPIC_", "CLAUDE_", "CODEX_", "OPENAI_", "AWS_", "GOOGLE_", "CLOUD_ML_", "VERTEX_"];
|
|
40541
|
-
function agentEnv(extraEnv,
|
|
40987
|
+
function agentEnv(extraEnv, overrides2) {
|
|
40542
40988
|
const env3 = {};
|
|
40543
40989
|
for (const [key, value] of Object.entries(process.env)) {
|
|
40544
40990
|
if (value === void 0) continue;
|
|
40545
40991
|
if (ENV_ALLOW.has(key) || ENV_ALLOW_PREFIXES.some((p2) => key.startsWith(p2))) env3[key] = value;
|
|
40546
40992
|
}
|
|
40547
|
-
return { ...env3, ...extraEnv, ...
|
|
40993
|
+
return { ...env3, ...extraEnv, ...overrides2 };
|
|
40548
40994
|
}
|
|
40549
40995
|
function gitAuthArgs(user, password) {
|
|
40550
40996
|
if (!user && !password) return [];
|
|
@@ -40640,106 +41086,46 @@ ${body}`;
|
|
|
40640
41086
|
}
|
|
40641
41087
|
function buildPrompt(docs, question, askerRole) {
|
|
40642
41088
|
const roleNote = askerRole === "admin" ? "The asker is a platform admin." : "The asker is a regular user \u2014 do NOT reveal admin-only features.";
|
|
40643
|
-
return
|
|
40644
|
-
"You are the 4PM product support assistant. Answer the user's question about how to use 4PM",
|
|
40645
|
-
"using ONLY the documentation and FAQ provided below. If the answer is not in the docs, say",
|
|
40646
|
-
"you don't have that information and suggest contacting human support \u2014 do not invent details.",
|
|
40647
|
-
roleNote,
|
|
40648
|
-
"Answer concisely in the user's language; reference the relevant doc heading/path when useful.",
|
|
40649
|
-
"",
|
|
40650
|
-
// Structured output for inline moderation (ADR-0237): the same run also classifies the question.
|
|
40651
|
-
"Return ONLY a single JSON object (no markdown fences, no prose around it) with these keys:",
|
|
40652
|
-
' "onTopic": boolean \u2014 false if the question is NOT about how to use the 4PM product,',
|
|
40653
|
-
' "sensitive": boolean \u2014 true if the question is inappropriate / abusive OR asks how to hack,',
|
|
40654
|
-
" bypass security, gain unauthorized access, take over or steal another",
|
|
40655
|
-
" user's/org's account or data, obtain credentials, or any malicious intent,",
|
|
40656
|
-
' "reason": string \u2014 a short reason when onTopic is false or sensitive is true, else "",',
|
|
40657
|
-
` "answer": string \u2014 your reply to the user, in markdown (in the user's language).`,
|
|
40658
|
-
"Refusal policy (ADR-0237): if the question is off-topic (onTopic false) OR sensitive, DO NOT",
|
|
40659
|
-
`answer it. Set "answer" to this EXACT same polite refusal for BOTH cases (in the user's`,
|
|
40660
|
-
'language, do not reveal which category or hint at the reason): "Sorry, I can only help with',
|
|
40661
|
-
'questions about how to use 4PM. For anything else, please contact human support." Only answer',
|
|
40662
|
-
"normally when onTopic is true AND sensitive is false.",
|
|
40663
|
-
"",
|
|
40664
|
-
"===== DOCUMENTATION =====",
|
|
40665
|
-
docs,
|
|
40666
|
-
"",
|
|
40667
|
-
"===== QUESTION =====",
|
|
40668
|
-
question
|
|
40669
|
-
].join("\n");
|
|
41089
|
+
return resolveCliPrompt("cli.support.answer", { roleNote, docs, question });
|
|
40670
41090
|
}
|
|
40671
41091
|
function buildDraftPrompt(docs, context, task) {
|
|
40672
|
-
if (task === "
|
|
40673
|
-
|
|
40674
|
-
|
|
40675
|
-
|
|
40676
|
-
|
|
40677
|
-
|
|
40678
|
-
|
|
40679
|
-
"Use the subject and any current draft below as the starting point. Be clear, friendly and concise.",
|
|
40680
|
-
"Write in the language of the subject/draft (or of the admin's instructions when those are empty)."
|
|
40681
|
-
];
|
|
41092
|
+
if (task === "prompt_generate") return buildPromptGeneratePrompt(context);
|
|
41093
|
+
if (task === "prompt_translate") return buildPromptTranslatePrompt(context);
|
|
41094
|
+
if (task === "legal_draft") return resolveCliPrompt("cli.support.legal_draft", { docs, context });
|
|
41095
|
+
const key = task === "reply_draft" ? "cli.support.reply_draft" : "cli.support.outreach_draft";
|
|
41096
|
+
return resolveCliPrompt(key, { docs, context });
|
|
41097
|
+
}
|
|
41098
|
+
function buildPromptGeneratePrompt(context) {
|
|
40682
41099
|
return [
|
|
40683
|
-
|
|
40684
|
-
"
|
|
40685
|
-
"
|
|
40686
|
-
"
|
|
40687
|
-
"
|
|
40688
|
-
"",
|
|
40689
|
-
"Output ONLY the message body in markdown \u2014 no subject line, no preamble, no explanation, no code",
|
|
40690
|
-
"fences around the whole message.",
|
|
40691
|
-
"",
|
|
40692
|
-
"===== DOCUMENTATION =====",
|
|
40693
|
-
docs,
|
|
41100
|
+
"You are writing a reusable AI PROMPT TEMPLATE for the 4PM platform. Based on the specification",
|
|
41101
|
+
"below, write a clear, effective prompt. Include each required {{placeholder}} verbatim where",
|
|
41102
|
+
"appropriate \u2014 never rename or invent placeholders. A human admin reviews your draft before it",
|
|
41103
|
+
"goes live.",
|
|
41104
|
+
"Output ONLY the prompt template text \u2014 no preamble, no explanation, no code fences.",
|
|
40694
41105
|
"",
|
|
40695
|
-
"=====
|
|
41106
|
+
"===== SPECIFICATION =====",
|
|
40696
41107
|
context
|
|
40697
41108
|
].join("\n");
|
|
40698
41109
|
}
|
|
40699
|
-
function
|
|
41110
|
+
function buildPromptTranslatePrompt(context) {
|
|
40700
41111
|
return [
|
|
40701
|
-
"You are
|
|
40702
|
-
"
|
|
40703
|
-
"
|
|
40704
|
-
"
|
|
40705
|
-
"When the target locale is not English and an English reference is given, follow its meaning closely.",
|
|
40706
|
-
"Rules:",
|
|
40707
|
-
"- Every {{placeholder}} (e.g. {{companyName}}) must be kept verbatim \u2014 never fill or rename it.",
|
|
40708
|
-
"- Describe plans, prices, upgrades, downgrades, cancellations, renewals, refunds and add-ons ONLY",
|
|
40709
|
-
" as stated in the PLAN CATALOG and BILLING FACTS below or the documentation; never invent",
|
|
40710
|
-
" commitments, prices or legal guarantees. Where something is unknown write `[TODO: \u2026]`.",
|
|
40711
|
-
"- Plain, precise legal English (or the target language); short numbered sections and bullet lists.",
|
|
40712
|
-
"- This is a draft, not legal advice; do not add a disclaimer about that inside the document.",
|
|
41112
|
+
"You are translating a reusable AI PROMPT TEMPLATE for the 4PM platform into the target language",
|
|
41113
|
+
"given below. Preserve EVERY {{placeholder}} exactly \u2014 never translate, rename, or remove a",
|
|
41114
|
+
"placeholder. Keep the same structure, intent and formatting. A human admin reviews your draft.",
|
|
41115
|
+
"Output ONLY the translated template text \u2014 no preamble, no explanation, no code fences.",
|
|
40713
41116
|
"",
|
|
40714
|
-
"
|
|
40715
|
-
"fences around the whole document.",
|
|
40716
|
-
"",
|
|
40717
|
-
"===== DOCUMENTATION =====",
|
|
40718
|
-
docs,
|
|
40719
|
-
"",
|
|
40720
|
-
"===== CONTEXT =====",
|
|
41117
|
+
"===== SPECIFICATION =====",
|
|
40721
41118
|
context
|
|
40722
41119
|
].join("\n");
|
|
40723
41120
|
}
|
|
40724
41121
|
function imageExt(mime) {
|
|
40725
|
-
|
|
40726
|
-
case "image/png":
|
|
40727
|
-
return "png";
|
|
40728
|
-
case "image/jpeg":
|
|
40729
|
-
return "jpg";
|
|
40730
|
-
case "image/webp":
|
|
40731
|
-
return "webp";
|
|
40732
|
-
case "image/gif":
|
|
40733
|
-
return "gif";
|
|
40734
|
-
default:
|
|
40735
|
-
return "png";
|
|
40736
|
-
}
|
|
41122
|
+
return commandImageExt(mime);
|
|
40737
41123
|
}
|
|
40738
41124
|
function materializeImages2(images, question) {
|
|
40739
41125
|
const dir2 = mkdtempSync2(join14(tmpdir3(), "4pm-help-images-"));
|
|
40740
41126
|
let rewritten = question;
|
|
40741
41127
|
images.forEach((img, i) => {
|
|
40742
|
-
const file = join14(dir2, `
|
|
41128
|
+
const file = join14(dir2, `attachment-${i + 1}.${imageExt(img.mime)}`);
|
|
40743
41129
|
writeFileSync7(file, Buffer.from(img.dataBase64, "base64"));
|
|
40744
41130
|
if (img.placeholder) rewritten = rewritten.split(img.placeholder).join(file);
|
|
40745
41131
|
});
|
|
@@ -41892,6 +42278,24 @@ fi
|
|
|
41892
42278
|
exec "${realGh}" "$@"
|
|
41893
42279
|
`;
|
|
41894
42280
|
}
|
|
42281
|
+
function findRealGlab(pathValue, shimDir) {
|
|
42282
|
+
for (const dir2 of pathValue.split(delimiter)) {
|
|
42283
|
+
if (!dir2 || dir2 === shimDir) continue;
|
|
42284
|
+
const candidate = join20(dir2, process.platform === "win32" ? "glab.exe" : "glab");
|
|
42285
|
+
if (existsSync13(candidate)) return candidate;
|
|
42286
|
+
}
|
|
42287
|
+
return null;
|
|
42288
|
+
}
|
|
42289
|
+
function glabShimScript(realGlab, cli) {
|
|
42290
|
+
return `#!/bin/sh
|
|
42291
|
+
# 4PM glab shim (ADR-0382) \u2014 injects the served project's GitLab group token.
|
|
42292
|
+
if [ -z "$GITLAB_TOKEN" ]; then
|
|
42293
|
+
T="$(${cli} git-token 2>/dev/null)"
|
|
42294
|
+
if [ -n "$T" ]; then export GITLAB_HOST="$${GIT_HOST_ENV}" GITLAB_TOKEN="$T"; fi
|
|
42295
|
+
fi
|
|
42296
|
+
exec "${realGlab}" "$@"
|
|
42297
|
+
`;
|
|
42298
|
+
}
|
|
41895
42299
|
function clearEnv() {
|
|
41896
42300
|
if (!state.baseEnv) return;
|
|
41897
42301
|
const { count, path } = state.baseEnv;
|
|
@@ -41908,7 +42312,8 @@ function clearEnv() {
|
|
|
41908
42312
|
}
|
|
41909
42313
|
function configureGitAuth(method, host, profileDir2, transport) {
|
|
41910
42314
|
try {
|
|
41911
|
-
const
|
|
42315
|
+
const isGitlab = method === "gitlab-group-token";
|
|
42316
|
+
const active = (method === "github-app" || isGitlab) && !!host;
|
|
41912
42317
|
if (!active || state.host && state.host !== host) {
|
|
41913
42318
|
if (state.host) void revokeAll();
|
|
41914
42319
|
clearEnv();
|
|
@@ -41937,22 +42342,26 @@ function configureGitAuth(method, host, profileDir2, transport) {
|
|
|
41937
42342
|
process.env.TSX_TSCONFIG_PATH = devTsconfig;
|
|
41938
42343
|
}
|
|
41939
42344
|
process.env[GIT_HOST_ENV] = host;
|
|
41940
|
-
process.env[GIT_HOST_KIND_ENV] = githubHostKind(host);
|
|
42345
|
+
process.env[GIT_HOST_KIND_ENV] = isGitlab ? "gitlab" : githubHostKind(host);
|
|
41941
42346
|
if (process.platform !== "win32") {
|
|
41942
42347
|
const shimDir = join20(profileDir2, "bin");
|
|
41943
|
-
const
|
|
41944
|
-
|
|
42348
|
+
const toolName = isGitlab ? "glab" : "gh";
|
|
42349
|
+
const otherTool = isGitlab ? "gh" : "glab";
|
|
42350
|
+
const realTool = isGitlab ? findRealGlab(state.baseEnv.path, shimDir) : findRealGh(state.baseEnv.path, shimDir);
|
|
42351
|
+
if (realTool) {
|
|
41945
42352
|
mkdirSync6(shimDir, { recursive: true });
|
|
41946
|
-
const shim = join20(shimDir,
|
|
41947
|
-
|
|
42353
|
+
const shim = join20(shimDir, toolName);
|
|
42354
|
+
const shimContent = isGitlab ? glabShimScript(realTool, cliInvocation()) : shimScript(realTool, cliInvocation());
|
|
42355
|
+
writeFileSync8(shim, shimContent, { encoding: "utf8", mode: 493 });
|
|
41948
42356
|
chmodSync3(shim, 493);
|
|
41949
42357
|
process.env.PATH = `${shimDir}${delimiter}${state.baseEnv.path}`;
|
|
41950
42358
|
} else {
|
|
41951
|
-
rmSync6(join20(shimDir,
|
|
42359
|
+
rmSync6(join20(shimDir, toolName), { force: true });
|
|
41952
42360
|
}
|
|
42361
|
+
rmSync6(join20(shimDir, otherTool), { force: true });
|
|
41953
42362
|
}
|
|
41954
42363
|
state.host = host;
|
|
41955
|
-
logger.info("git.auth.github-app", { host });
|
|
42364
|
+
logger.info(isGitlab ? "git.auth.gitlab" : "git.auth.github-app", { host });
|
|
41956
42365
|
return true;
|
|
41957
42366
|
} catch (err) {
|
|
41958
42367
|
logger.warn("git.auth.configure.failed", { error: String(err) });
|
|
@@ -42838,22 +43247,12 @@ function parseJsonResult(out) {
|
|
|
42838
43247
|
}
|
|
42839
43248
|
var COMPACT_TIMEOUT_MS = 6e4;
|
|
42840
43249
|
function buildPrompt2(input) {
|
|
42841
|
-
return
|
|
42842
|
-
|
|
42843
|
-
|
|
42844
|
-
|
|
42845
|
-
|
|
42846
|
-
|
|
42847
|
-
"",
|
|
42848
|
-
"=== EXISTING MEMORY ===",
|
|
42849
|
-
input.oldMemory || "(empty)",
|
|
42850
|
-
"",
|
|
42851
|
-
"=== LATEST USER PROMPT ===",
|
|
42852
|
-
input.prompt,
|
|
42853
|
-
"",
|
|
42854
|
-
"=== LATEST ASSISTANT ANSWER ===",
|
|
42855
|
-
input.answer
|
|
42856
|
-
].join("\n");
|
|
43250
|
+
return resolveCliPrompt("cli.memory.compact", {
|
|
43251
|
+
budgetChars: input.budgetChars,
|
|
43252
|
+
oldMemory: input.oldMemory || "(empty)",
|
|
43253
|
+
prompt: input.prompt,
|
|
43254
|
+
answer: input.answer
|
|
43255
|
+
});
|
|
42857
43256
|
}
|
|
42858
43257
|
function runOnce(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
42859
43258
|
return new Promise((resolve13, reject) => {
|
|
@@ -44112,79 +44511,37 @@ empty template. The single source of truth for approval is \`.claude/.autonomous
|
|
|
44112
44511
|
NEVER act on an unapproved row.`;
|
|
44113
44512
|
function buildIntakePrompt(cfg) {
|
|
44114
44513
|
const h = cfg.taskSizeHints;
|
|
44115
|
-
return
|
|
44116
|
-
|
|
44117
|
-
|
|
44118
|
-
|
|
44119
|
-
|
|
44120
|
-
|
|
44121
|
-
|
|
44122
|
-
Read \`USER_TODO.md\` (columns \`ID | Group | Depends | Request\`). For each APPROVED \`REQ-\u2026\` whose \`Depends\`
|
|
44123
|
-
are already handled:
|
|
44124
|
-
- CLEAR request \u21D2 split it into tasks appended to \`AI_TODO.md\` (columns
|
|
44125
|
-
\`ID | Priority | Tag | Depends | Group | Task description | Notes\`), ids \`TSK-{groupid:0000}-{taskid:0000}\`
|
|
44126
|
-
(groupid UNIQUE + INCREASING across all history \u2014 the largest ever used in \`AI_TODO.md\`/\`AI_DONE.md\`/
|
|
44127
|
-
\`AI_PROGRESS.md\`/their git history, +1; taskid from 0001). \`Depends\` = the \`TSK-\u2026\` it needs.
|
|
44128
|
-
Remove the analysed request row (empty \u21D2 reset to the empty template).
|
|
44129
|
-
- UNCLEAR request \u21D2 do NOT guess: append a \`USER_QA.md\` row (\`ID | Group | Depends | Original request |
|
|
44130
|
-
Question / options | Answer\`) with a new \`QA-{groupid:0000}-{qaid:0000}\`, blank \`Answer\`; remove the request row.
|
|
44131
|
-
An unapproved request stays.
|
|
44132
|
-
|
|
44133
|
-
## 2 \u2014 Answered intake questions
|
|
44134
|
-
For each \`USER_QA.md\` row with an \`Answer\`, APPROVED, and whose id is NOT in any \`AI_TODO.md\` \`Depends\`
|
|
44135
|
-
(those are task questions \u2014 the cli handles them; leave them alone): re-analyse the original request with the
|
|
44136
|
-
answer into tasks (same rules), then remove that QA row.
|
|
44137
|
-
|
|
44138
|
-
## Task size (MANDATORY \u2014 every task must be finishable in ONE run)
|
|
44139
|
-
Size each task by SCOPE and write \`size: S\` or \`size: M\` at the start of \`Notes\`:
|
|
44140
|
-
- **S** \u2014 one small change in one module/layer (\u2248 \u2264 ${h.sMaxFiles} files, \u2248 \u2264 ${h.sMaxLines} lines), e.g. fix a bug, add a field.
|
|
44141
|
-
- **M** \u2014 one feature slice across 2\u20133 layers with ONE purpose (\u2248 \u2264 ${h.mMaxFiles} files, \u2248 \u2264 ${h.mMaxLines} lines), e.g. one CRUD screen, one API + its UI + tests.
|
|
44142
|
-
- **L** \u2014 several features, a cross-cutting change, a big migration, a new subsystem, or an architecture decision.
|
|
44143
|
-
**Never write an L task**: split it into S/M tasks chained with \`Depends\`, or ask in \`USER_QA.md\` when the split needs a decision.
|
|
44144
|
-
The counts are hints, not hard limits. Each task's \`Task description\` states the acceptance criteria and how to test it.
|
|
44145
|
-
|
|
44146
|
-
When done, reply with one short line summarising what you added.`;
|
|
44514
|
+
return resolveCliPrompt("cli.autonomous.intake", {
|
|
44515
|
+
booksIntro: BOOKS_INTRO,
|
|
44516
|
+
sMaxFiles: h.sMaxFiles,
|
|
44517
|
+
sMaxLines: h.sMaxLines,
|
|
44518
|
+
mMaxFiles: h.mMaxFiles,
|
|
44519
|
+
mMaxLines: h.mMaxLines
|
|
44520
|
+
});
|
|
44147
44521
|
}
|
|
44148
44522
|
function buildImplementPrompt(mine, subs, resumed) {
|
|
44149
44523
|
const subLines = subs.length ? subs.map((s3) => `- submodule \`${s3.dir}\` is on branch \`${s3.branch}\` \u2014 commit changes to it INSIDE \`${s3.dir}\``).join("\n") : "- (no submodules)";
|
|
44150
|
-
|
|
44151
|
-
|
|
44152
|
-
|
|
44153
|
-
|
|
44154
|
-
|
|
44155
|
-
|
|
44156
|
-
|
|
44157
|
-
|
|
44158
|
-
|
|
44159
|
-
|
|
44160
|
-
|
|
44161
|
-
- Follow the project's architecture and \`CLAUDE.md\`. Add/update tests; run the project's test command and WAIT for the result.
|
|
44162
|
-
- Commit your work (\`git add\` + \`git commit\`) in the root and/or inside each submodule you changed.
|
|
44163
|
-
- Do NOT push, do NOT open pull requests, do NOT switch/create branches, do NOT edit the book files
|
|
44164
|
-
(USER_TODO/USER_QA/AI_TODO/AI_PROGRESS/AI_DONE) \u2014 the 4PM cli delivers and records everything.
|
|
44165
|
-
- If you need a human DECISION to continue (ambiguous requirement, missing information, a choice with real
|
|
44166
|
-
trade-offs), do NOT guess: commit what you have and report \`needs-input\` with the question + options.
|
|
44167
|
-
|
|
44168
|
-
Finish with ONLY one fenced \`json\` block:
|
|
44169
|
-
{"status": "done" | "needs-input" | "failed", "summary": "<what you did / why it failed>", "question": "<only for needs-input: the question and the options>"}`;
|
|
44524
|
+
const resumedNote = resumed ? "\nThis is a CONTINUATION: earlier work for this task is already committed on the branches below \u2014 review it (git log / git diff against the base) and continue from it; do not start over.\n" : "";
|
|
44525
|
+
return resolveCliPrompt("cli.autonomous.implement", {
|
|
44526
|
+
taskId: mine.id,
|
|
44527
|
+
group: mine.task.group,
|
|
44528
|
+
attempt: mine.attempt,
|
|
44529
|
+
desc: mine.task.desc,
|
|
44530
|
+
notes: mine.task.notes || "(none)",
|
|
44531
|
+
resumedNote,
|
|
44532
|
+
branch: mine.branch,
|
|
44533
|
+
subLines
|
|
44534
|
+
});
|
|
44170
44535
|
}
|
|
44171
44536
|
function buildSplitPrompt(mine, reasons, diff2) {
|
|
44172
|
-
return
|
|
44173
|
-
|
|
44174
|
-
|
|
44175
|
-
|
|
44176
|
-
|
|
44177
|
-
|
|
44178
|
-
|
|
44179
|
-
|
|
44180
|
-
\`\`\`
|
|
44181
|
-
${diff2 || "(no changes yet)"}
|
|
44182
|
-
\`\`\`
|
|
44183
|
-
The first child continues from that WIP branch: note which parts are already done so they are not redone.
|
|
44184
|
-
Each child description must state its acceptance criteria and how to test it.
|
|
44185
|
-
|
|
44186
|
-
Reply with ONLY one fenced \`json\` block:
|
|
44187
|
-
{"children": [{"desc": "<task + acceptance criteria>", "priority": "High|Medium|Low", "size": "S|M", "notes": "<optional>"}]}`;
|
|
44537
|
+
return resolveCliPrompt("cli.autonomous.split", {
|
|
44538
|
+
taskId: mine.id,
|
|
44539
|
+
desc: mine.task.desc,
|
|
44540
|
+
notes: mine.task.notes || "(none)",
|
|
44541
|
+
reasons: reasons || "(unknown)",
|
|
44542
|
+
branch: mine.branch,
|
|
44543
|
+
diff: diff2 || "(no changes yet)"
|
|
44544
|
+
});
|
|
44188
44545
|
}
|
|
44189
44546
|
function parseJsonReply(text) {
|
|
44190
44547
|
const fenced = [...text.matchAll(/```json\s*([\s\S]*?)```/gi)].map((m2) => m2[1].trim());
|
|
@@ -45601,14 +45958,15 @@ function applyGitAuth(ctx, method, host) {
|
|
|
45601
45958
|
|
|
45602
45959
|
// src/core/research-run.ts
|
|
45603
45960
|
import { spawn as spawn8 } from "child_process";
|
|
45604
|
-
import { mkdtempSync as mkdtempSync3, rmSync as rmSync8 } from "fs";
|
|
45961
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
45605
45962
|
import { tmpdir as tmpdir5 } from "os";
|
|
45606
45963
|
import { join as join29 } from "path";
|
|
45607
45964
|
var RESEARCH_TIMEOUT_MS = 9 * 60 * 1e3;
|
|
45608
45965
|
var RESEARCH_MAX_TURNS = 16;
|
|
45609
45966
|
var NO_USAGE2 = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
45610
45967
|
var RESEARCH_ALLOWED_TOOLS = "WebSearch,WebFetch";
|
|
45611
|
-
var
|
|
45968
|
+
var RESEARCH_ATTACH_TOOLS = "WebSearch,WebFetch,Read,Glob";
|
|
45969
|
+
var RESEARCH_DENIED_BASE = [
|
|
45612
45970
|
"Bash",
|
|
45613
45971
|
"Edit",
|
|
45614
45972
|
"Write",
|
|
@@ -45637,60 +45995,57 @@ var RESEARCH_DENIED_TOOLS = [
|
|
|
45637
45995
|
"ScheduleWakeup",
|
|
45638
45996
|
"SendMessage",
|
|
45639
45997
|
"PushNotification"
|
|
45640
|
-
]
|
|
45998
|
+
];
|
|
45999
|
+
function safeAttachmentName(name, index) {
|
|
46000
|
+
const base = (name || `file-${index + 1}`).replace(/[/\\]/g, "_").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80);
|
|
46001
|
+
return base || `file-${index + 1}`;
|
|
46002
|
+
}
|
|
45641
46003
|
function buildResearchPrompt(question) {
|
|
45642
|
-
return
|
|
45643
|
-
"You are a research assistant for the 4PM platform. Answer the user's question thoroughly and",
|
|
45644
|
-
"accurately, in Markdown. Use the read-only web tools (WebSearch / WebFetch) to find current, factual",
|
|
45645
|
-
"information when helpful, and cite sources inline where relevant.",
|
|
45646
|
-
"",
|
|
45647
|
-
"STRICT CONTENT POLICY \u2014 you MUST refuse, and must not answer, if the question (or a faithful answer",
|
|
45648
|
-
"to it) would involve any of:",
|
|
45649
|
-
"- sexual, abusive, hateful, or illegal content;",
|
|
45650
|
-
"- political persuasion, campaigning, or partisan advocacy;",
|
|
45651
|
-
"- discrimination or demeaning content targeting a race, ethnicity, religion, gender, nationality, or",
|
|
45652
|
-
" other protected group;",
|
|
45653
|
-
"- probing, scanning, exploiting, or attacking the security of ANY system, network, account, device,",
|
|
45654
|
-
" or person you do not own (no vulnerability hunting, no exploit/malware creation, no credential or",
|
|
45655
|
-
" PII harvesting, no evasion of security controls). General defensive-security education is allowed.",
|
|
45656
|
-
"- any other seriously harmful activity.",
|
|
45657
|
-
"",
|
|
45658
|
-
'When you must refuse, reply with EXACTLY one line starting with "REFUSED:" followed by a short,',
|
|
45659
|
-
"neutral reason, and output nothing else.",
|
|
45660
|
-
"",
|
|
45661
|
-
"You have READ-ONLY web tools only. Never attempt to read local files, run shell commands, or interact",
|
|
45662
|
-
"with other systems.",
|
|
45663
|
-
"",
|
|
45664
|
-
"---",
|
|
45665
|
-
"",
|
|
45666
|
-
"Question:",
|
|
45667
|
-
question
|
|
45668
|
-
].join("\n");
|
|
46004
|
+
return resolveCliPrompt("cli.research.policy", { question });
|
|
45669
46005
|
}
|
|
45670
|
-
function runClaudeOnce2(cmd, profile, prompt, extraEnv, onChunk, denyDirs) {
|
|
46006
|
+
function runClaudeOnce2(cmd, profile, prompt, extraEnv, onChunk, denyDirs, attachments) {
|
|
45671
46007
|
return new Promise((resolve13, reject) => {
|
|
45672
46008
|
const isClaude = cmd.includes("claude");
|
|
46009
|
+
const cwd2 = mkdtempSync3(join29(tmpdir5(), "4pm-research-"));
|
|
46010
|
+
const hasAttachments = attachments.length > 0;
|
|
46011
|
+
let finalPrompt = prompt;
|
|
46012
|
+
if (hasAttachments) {
|
|
46013
|
+
const lines = [];
|
|
46014
|
+
attachments.forEach((a, i) => {
|
|
46015
|
+
const file = join29(cwd2, safeAttachmentName(a.name, i));
|
|
46016
|
+
try {
|
|
46017
|
+
writeFileSync9(file, Buffer.from(a.dataBase64, "base64"));
|
|
46018
|
+
lines.push(`- \`${file}\``);
|
|
46019
|
+
} catch {
|
|
46020
|
+
}
|
|
46021
|
+
});
|
|
46022
|
+
if (lines.length > 0) {
|
|
46023
|
+
finalPrompt = `${prompt}
|
|
46024
|
+
|
|
46025
|
+
The user attached these files \u2014 use the Read tool to read them before answering:
|
|
46026
|
+
${lines.join("\n")}`;
|
|
46027
|
+
}
|
|
46028
|
+
}
|
|
45673
46029
|
const args = [
|
|
45674
46030
|
"-p",
|
|
45675
46031
|
...profile?.model ? ["--model", profile.model] : [],
|
|
45676
46032
|
...isClaude ? ["--output-format", "stream-json", "--verbose"] : [],
|
|
45677
46033
|
// Secret-path deny rules (ADR-0347) — reads are not confined to the working dir.
|
|
45678
46034
|
...denySettingsArgs(cmd, [...denyDirs, profile?.dir]),
|
|
45679
|
-
// Web-only agent
|
|
45680
|
-
// `--permission-mode default` is the non-variadic terminator
|
|
45681
|
-
// only their own comma-token — same trick as the
|
|
46035
|
+
// Web-only agent (+ Read/Glob of the scratch dir when attachments are present): allow the tool set,
|
|
46036
|
+
// deny everything else, bound the loop. `--permission-mode default` is the non-variadic terminator
|
|
46037
|
+
// (so the variadic tool flags consume only their own comma-token — same trick as the other runners).
|
|
45682
46038
|
...isClaude ? [
|
|
45683
46039
|
`--max-turns=${RESEARCH_MAX_TURNS}`,
|
|
45684
46040
|
"--allowedTools",
|
|
45685
|
-
RESEARCH_ALLOWED_TOOLS,
|
|
46041
|
+
hasAttachments ? RESEARCH_ATTACH_TOOLS : RESEARCH_ALLOWED_TOOLS,
|
|
45686
46042
|
"--disallowedTools",
|
|
45687
|
-
|
|
46043
|
+
(hasAttachments ? RESEARCH_DENIED_BASE.filter((t2) => t2 !== "Read" && t2 !== "Glob") : RESEARCH_DENIED_BASE).join(","),
|
|
45688
46044
|
"--permission-mode",
|
|
45689
46045
|
"default"
|
|
45690
46046
|
] : []
|
|
45691
46047
|
];
|
|
45692
46048
|
const env3 = agentEnv(extraEnv, profile ? { CLAUDE_CONFIG_DIR: profile.dir } : void 0);
|
|
45693
|
-
const cwd2 = mkdtempSync3(join29(tmpdir5(), "4pm-research-"));
|
|
45694
46049
|
const cleanup = () => {
|
|
45695
46050
|
try {
|
|
45696
46051
|
rmSync8(cwd2, { recursive: true, force: true });
|
|
@@ -45729,18 +46084,18 @@ function runClaudeOnce2(cmd, profile, prompt, extraEnv, onChunk, denyDirs) {
|
|
|
45729
46084
|
}
|
|
45730
46085
|
resolve13({ code: code ?? -1, out, err, usage: parser.usage() });
|
|
45731
46086
|
});
|
|
45732
|
-
child.stdin.write(
|
|
46087
|
+
child.stdin.write(finalPrompt);
|
|
45733
46088
|
child.stdin.end();
|
|
45734
46089
|
});
|
|
45735
46090
|
}
|
|
45736
|
-
async function runResearch(question, ai2, onChunk) {
|
|
46091
|
+
async function runResearch(question, ai2, onChunk, attachments = []) {
|
|
45737
46092
|
const prompt = buildResearchPrompt(question);
|
|
45738
46093
|
const denyDirs = ai2.profiles.map((p2) => p2.dir);
|
|
45739
46094
|
const attempts = ai2.profiles.length > 0 ? ai2.profiles : [null];
|
|
45740
46095
|
let lastReason = "no attempt";
|
|
45741
46096
|
for (let i = 0; i < attempts.length; i++) {
|
|
45742
46097
|
try {
|
|
45743
|
-
const { code, out, err, usage } = await runClaudeOnce2(ai2.cmd, attempts[i], prompt, ai2.env, onChunk, denyDirs);
|
|
46098
|
+
const { code, out, err, usage } = await runClaudeOnce2(ai2.cmd, attempts[i], prompt, ai2.env, onChunk, denyDirs, attachments);
|
|
45744
46099
|
if (code === 0 && out.trim()) {
|
|
45745
46100
|
const u2 = usage.tokens > 0 ? usage : { ...NO_USAGE2, tokens: estimateTokens(out) };
|
|
45746
46101
|
return { text: out.trim(), usage: u2 };
|
|
@@ -45813,7 +46168,7 @@ async function run14(ctx, message, req, config, cmd) {
|
|
|
45813
46168
|
profiles: resolveClaudeProfiles(config, getWorkingProfile(ctx.profileDir, cmd)),
|
|
45814
46169
|
env: config.aiEnv
|
|
45815
46170
|
};
|
|
45816
|
-
const result = await runResearch(req.question, ai2, (delta) => emit({ chunk: delta }));
|
|
46171
|
+
const result = await runResearch(req.question, ai2, (delta) => emit({ chunk: delta }), req.attachments ?? []);
|
|
45817
46172
|
const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
45818
46173
|
const events = [{ metric: UsageMetric.COMMANDS, amount: 1, occurredAt }];
|
|
45819
46174
|
if (result.usage.tokens > 0) {
|
|
@@ -45939,33 +46294,12 @@ function buildPrompt4(tickets, customPrompt) {
|
|
|
45939
46294
|
${thread}`;
|
|
45940
46295
|
}).join("\n\n");
|
|
45941
46296
|
const ids = tickets.map((t2) => t2.id);
|
|
45942
|
-
|
|
45943
|
-
|
|
45944
|
-
|
|
45945
|
-
|
|
45946
|
-
|
|
45947
|
-
|
|
45948
|
-
"Rules:",
|
|
45949
|
-
"- Write generalized Q&A, NOT ticket-specific replies; never include customer names, emails, ids,",
|
|
45950
|
-
" org names or any personal data from the tickets.",
|
|
45951
|
-
"- Merge with existing entries instead of duplicating; keep the existing formatting and headings.",
|
|
45952
|
-
"- If a ticket yields no reusable FAQ value, skip it. If nothing is worth adding, make NO changes.",
|
|
45953
|
-
"- Only edit markdown documentation files; do not touch code, CI, or unrelated files.",
|
|
45954
|
-
"- Do NOT run git commit/push or open a PR yourself \u2014 just leave the edited files in the working",
|
|
45955
|
-
" tree; the surrounding tooling handles commit + PR.",
|
|
45956
|
-
...customPrompt?.trim() ? ["", "Additional instruction from the operator (follow it too):", customPrompt.trim()] : [],
|
|
45957
|
-
"",
|
|
45958
|
-
"===== RESOLVED TICKETS =====",
|
|
45959
|
-
transcript,
|
|
45960
|
-
"",
|
|
45961
|
-
"===== REQUIRED FINAL OUTPUT =====",
|
|
45962
|
-
"After finishing all file edits, output \u2014 as the LAST thing, on its own \u2014 a single fenced JSON",
|
|
45963
|
-
"block summarizing what you did PER TICKET (created/updated which FAQ entry, or skipped + why):",
|
|
45964
|
-
"```json",
|
|
45965
|
-
`{"perTicket":[${ids.map((id) => `{"ticketId":"${id}","summary":"..."}`).join(",")}]}`,
|
|
45966
|
-
"```",
|
|
45967
|
-
"Include exactly one entry per ticket id above; keep each summary to one or two sentences."
|
|
45968
|
-
].join("\n");
|
|
46297
|
+
const ticketJsonSkeleton = `{"perTicket":[${ids.map((id) => `{"ticketId":"${id}","summary":"..."}`).join(",")}]}`;
|
|
46298
|
+
const customInstruction = customPrompt?.trim() ? `
|
|
46299
|
+
|
|
46300
|
+
Additional instruction from the operator (follow it too):
|
|
46301
|
+
${customPrompt.trim()}` : "";
|
|
46302
|
+
return resolveCliPrompt("cli.faq.compose", { transcript, ticketJsonSkeleton, customInstruction });
|
|
45969
46303
|
}
|
|
45970
46304
|
function parsePerTicket(text) {
|
|
45971
46305
|
const idx = text.lastIndexOf('"perTicket"');
|
|
@@ -47731,6 +48065,7 @@ var WsClient = class {
|
|
|
47731
48065
|
if (token.aiMemory && !this.aiMemory) this.aiMemory = token.aiMemory.text;
|
|
47732
48066
|
this.outboundReviewEnabled = token.outboundReview?.enabled === true;
|
|
47733
48067
|
this.restrictToFolder = token.aiRestrictToFolder === true;
|
|
48068
|
+
setPromptOverrides(token.promptOverrides ?? null);
|
|
47734
48069
|
configureGitAuth(
|
|
47735
48070
|
token.gitAuth ?? null,
|
|
47736
48071
|
token.gitAuthHost ?? null,
|
|
@@ -48289,7 +48624,7 @@ var WsClient = class {
|
|
|
48289
48624
|
};
|
|
48290
48625
|
|
|
48291
48626
|
// src/core/instance-lock.ts
|
|
48292
|
-
import { readFileSync as readFileSync17, rmSync as rmSync10, writeFileSync as
|
|
48627
|
+
import { readFileSync as readFileSync17, rmSync as rmSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
48293
48628
|
import { join as join39 } from "path";
|
|
48294
48629
|
function lockPath(profileDir2) {
|
|
48295
48630
|
return join39(profileDir2, "start.lock");
|
|
@@ -48312,7 +48647,7 @@ function lockHolder(profileDir2) {
|
|
|
48312
48647
|
}
|
|
48313
48648
|
function acquireInstanceLock(profileDir2) {
|
|
48314
48649
|
if (lockHolder(profileDir2) !== null) return false;
|
|
48315
|
-
|
|
48650
|
+
writeFileSync10(lockPath(profileDir2), String(process.pid), "utf8");
|
|
48316
48651
|
return true;
|
|
48317
48652
|
}
|
|
48318
48653
|
function releaseInstanceLock(profileDir2) {
|
|
@@ -48643,7 +48978,7 @@ function createFrameParser() {
|
|
|
48643
48978
|
}
|
|
48644
48979
|
|
|
48645
48980
|
// src/core/control-token.ts
|
|
48646
|
-
import { chmodSync as chmodSync4, readFileSync as readFileSync18, writeFileSync as
|
|
48981
|
+
import { chmodSync as chmodSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "fs";
|
|
48647
48982
|
import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
|
|
48648
48983
|
import { join as join40 } from "path";
|
|
48649
48984
|
function controlTokenPath(profileDir2) {
|
|
@@ -48652,7 +48987,7 @@ function controlTokenPath(profileDir2) {
|
|
|
48652
48987
|
function writeControlToken(profileDir2) {
|
|
48653
48988
|
const token = randomBytes3(32).toString("hex");
|
|
48654
48989
|
const path = controlTokenPath(profileDir2);
|
|
48655
|
-
|
|
48990
|
+
writeFileSync11(path, token, { mode: 384 });
|
|
48656
48991
|
try {
|
|
48657
48992
|
chmodSync4(path, 384);
|
|
48658
48993
|
} catch {
|
|
@@ -50749,7 +51084,7 @@ var src_default = Yoga;
|
|
|
50749
51084
|
|
|
50750
51085
|
// ../../node_modules/.pnpm/ink@5.2.1_@types+react@18.3.31_react@18.3.1/node_modules/ink/build/reconciler.js
|
|
50751
51086
|
var import_react_reconciler = __toESM(require_react_reconciler(), 1);
|
|
50752
|
-
var
|
|
51087
|
+
var import_constants15 = __toESM(require_constants2(), 1);
|
|
50753
51088
|
import process3 from "process";
|
|
50754
51089
|
|
|
50755
51090
|
// ../../node_modules/.pnpm/ansi-regex@6.2.2/node_modules/ansi-regex/index.js
|
|
@@ -51991,7 +52326,7 @@ var reconciler_default = (0, import_react_reconciler.default)({
|
|
|
51991
52326
|
scheduleTimeout: setTimeout,
|
|
51992
52327
|
cancelTimeout: clearTimeout,
|
|
51993
52328
|
noTimeout: -1,
|
|
51994
|
-
getCurrentEventPriority: () =>
|
|
52329
|
+
getCurrentEventPriority: () => import_constants15.DefaultEventPriority,
|
|
51995
52330
|
beforeActiveInstanceBlur() {
|
|
51996
52331
|
},
|
|
51997
52332
|
afterActiveInstanceBlur() {
|
|
@@ -54433,7 +54768,7 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
54433
54768
|
var import_react26 = __toESM(require_react(), 1);
|
|
54434
54769
|
|
|
54435
54770
|
// src/core/input-history.ts
|
|
54436
|
-
import { existsSync as existsSync21, readFileSync as readFileSync20, writeFileSync as
|
|
54771
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
|
|
54437
54772
|
import { join as join42 } from "path";
|
|
54438
54773
|
var MAX_HISTORY = 200;
|
|
54439
54774
|
function historyPath(profileDir2) {
|
|
@@ -54454,7 +54789,7 @@ function appendInputHistory(profileDir2, line, current2) {
|
|
|
54454
54789
|
if (!trimmed || current2[current2.length - 1] === trimmed) return current2;
|
|
54455
54790
|
const next = [...current2, trimmed].slice(-MAX_HISTORY);
|
|
54456
54791
|
try {
|
|
54457
|
-
|
|
54792
|
+
writeFileSync12(historyPath(profileDir2), JSON.stringify(next), "utf8");
|
|
54458
54793
|
} catch {
|
|
54459
54794
|
}
|
|
54460
54795
|
return next;
|
|
@@ -55418,7 +55753,8 @@ async function runGitCredential(action) {
|
|
|
55418
55753
|
const host = (input.host ?? "").toLowerCase();
|
|
55419
55754
|
if (input.protocol !== "https" || !host || host !== (process.env[GIT_HOST_ENV] ?? "").toLowerCase()) return;
|
|
55420
55755
|
const token = await requestToken(host, input.path ?? "");
|
|
55421
|
-
|
|
55756
|
+
const username = (process.env[GIT_HOST_KIND_ENV] ?? "") === "gitlab" ? "oauth2" : "x-access-token";
|
|
55757
|
+
if (token) process.stdout.write(`username=${username}
|
|
55422
55758
|
password=${token}
|
|
55423
55759
|
`);
|
|
55424
55760
|
}
|