@galda/cli 0.10.13 → 0.10.14

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/engine/server.mjs CHANGED
@@ -20,8 +20,8 @@ import { resolve, dirname, join, basename } from 'node:path';
20
20
  import { homedir } from 'node:os';
21
21
  import { fileURLToPath } from 'node:url';
22
22
  import { pathToFileURL } from 'node:url';
23
- import { needsProjectFolder, folderLabel, buildFolderQuestion, resolveFolderAnswer, needsReviewPreference, buildReviewPreferenceQuestion, resolveReviewPreferenceAnswer } from './lib.mjs';
24
- import { parseStreamEvents, parsePlan, refinePlanTasks, findOverlappingGoal, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, isForcedStop, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, buildGoalProofMd, buildGoalPrBody, nextGoalStatus, isPrMerged, isPrApproved, reviewToDoneStatus, advanceReviewGoal, revertReviewGoal, approveGoal, dismissGoal, permissionModeFor, isPlanReview, nextAfterPlanApprove, GOAL_MODES, parseSkillFrontmatter, collectSkills, retestGoal, cancelRetestGoal, computeRetestOutcome, revertGoal, planRevertActions, archiveGoal, unarchiveGoal, undismissGoal, parseNumstat, truncateDiffText, sumUsage, resolveEntryUrl, parseGitLog, diffNewCommits, replayQueueLog, reconcileOrphanGoals, reorderQueue, sortQueueByPriority, TASK_PRIORITIES, validateWorkflowColumns, DEFAULT_WORKFLOW_COLUMNS, validateReviewDefinition, DEFAULT_REVIEW_DEFINITION, buildEphemeralSeedLog, buildEphemeralSeedProjects, trimTaskActivityForState, buildAskContext, WORKER_TOOLS, verifyCfAccessJwt, resolveIdentity, goalVisibleTo, clampParallelLimit, canStartMore, nextRunnableTasks, goalsConflict, detectConflicts, isUiChange, shouldCaptureProof, shouldCaptureBaseline, hasTestRelevantChanges, buildExecutionPlan, buildContextHandoffSummary, buildFailurePostmortem, appendFailureMemory, latestFailurePolicy, classifyChangeRisk, checkRunBudget, usageBudgetTokens, isRateLimited, nextResumeDelay, checkFreeTierLimit, resolveEntitlement, resolveCachedEntitlement, FREE_TIER_LIMITS, verifyLicenseToken, shouldEmitSetupCompleted, pickAnalyticsUid, shouldEmitFreeExhausted, detectRequestLanguage, isNothingVerifiable } from './lib.mjs';
23
+ import { needsProjectFolder, folderLabel, buildFolderQuestion, resolveFolderAnswer } from './lib.mjs';
24
+ import { parseStreamEvents, parsePlan, refinePlanTasks, findOverlappingGoal, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, isForcedStop, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, buildGoalProofMd, buildReviewRuleSection, nextGoalStatus, isPrMerged, isPrApproved, reviewToDoneStatus, advanceReviewGoal, revertReviewGoal, approveGoal, dismissGoal, permissionModeFor, isPlanReview, nextAfterPlanApprove, GOAL_MODES, parseSkillFrontmatter, collectSkills, retestGoal, cancelRetestGoal, computeRetestOutcome, revertGoal, planRevertActions, archiveGoal, unarchiveGoal, undismissGoal, parseNumstat, truncateDiffText, sumUsage, resolveEntryUrl, parseGitLog, diffNewCommits, replayQueueLog, reconcileOrphanGoals, reorderQueue, sortQueueByPriority, TASK_PRIORITIES, validateWorkflowColumns, DEFAULT_WORKFLOW_COLUMNS, validateReviewDefinition, DEFAULT_REVIEW_DEFINITION, buildEphemeralSeedLog, buildEphemeralSeedProjects, trimTaskActivityForState, buildAskContext, WORKER_TOOLS, verifyCfAccessJwt, resolveIdentity, goalVisibleTo, clampParallelLimit, canStartMore, nextRunnableTasks, goalsConflict, detectConflicts, isUiChange, shouldCaptureProof, shouldCaptureBaseline, hasTestRelevantChanges, buildExecutionPlan, buildContextHandoffSummary, buildFailurePostmortem, appendFailureMemory, latestFailurePolicy, classifyChangeRisk, checkRunBudget, usageBudgetTokens, isRateLimited, nextResumeDelay, checkFreeTierLimit, resolveEntitlement, resolveCachedEntitlement, FREE_TIER_LIMITS, verifyLicenseToken, shouldEmitSetupCompleted, pickAnalyticsUid, shouldEmitFreeExhausted, detectRequestLanguage, isNothingVerifiable } from './lib.mjs';
25
25
  import { openPR } from './pr.mjs';
26
26
  import { runVerification, exerciseUi } from './verify.mjs';
27
27
 
@@ -2160,11 +2160,6 @@ async function finishGoalIfComplete(goal, project, activityTask = null) {
2160
2160
  // point (all its tasks are done), so its conflictsWith is accurate even if
2161
2161
  // PR creation itself fails (e.g. no `gh` auth) or is still no-op'd (no code).
2162
2162
  updateGoalConflicts(goal);
2163
- // Computed BEFORE createGoalPR (not inside verifyGate, which used to run
2164
- // after) so the PR body's "確認ポイント" headline is available the first
2165
- // time the PR is opened, not only on the review card (Masa 2026-07-16).
2166
- goalAct('Writing the review summary from the worker report and diff.');
2167
- goal.reviewSummary = await summarizeForReview(goal, siblings, wdir, reviewDefinition);
2168
2163
  goalAct('Checking changed files and preparing the PR branch if code changed.');
2169
2164
  await createGoalPR(goal, prProject, siblings, reviewDefinition, activityTask);
2170
2165
  // goal 427 §4: classify the change's risk from the SAME changed-files/diff
@@ -2233,13 +2228,8 @@ async function verifyGate(goal, prProject, siblings, baseProject, activityTask =
2233
2228
  goal.testResult = { ran: false, skipped: true, skippedReason: changedFiles.length ? 'documentation-only change' : 'no changed files' };
2234
2229
  goalAct(`Skipped project tests: ${goal.testResult.skippedReason}.`);
2235
2230
  }
2236
- // Normally already set by finishGoalIfComplete before the PR was opened
2237
- // (so the PR body can include it too); only fall back here for the
2238
- // standalone /reverify path or any older goal that never got one.
2239
- if (!goal.reviewSummary) {
2240
- goalAct('Writing the review summary from the worker report and diff.');
2241
- goal.reviewSummary = await summarizeForReview(goal, siblings, wdir, reviewDefinition);
2242
- }
2231
+ goalAct('Writing the review summary from the worker report and diff.');
2232
+ goal.reviewSummary = await summarizeForReview(goal, siblings, wdir, reviewDefinition);
2243
2233
  const uiTasks = siblings.filter((t) => isUiChange(t.changedFiles));
2244
2234
  // Only require/capture proof for projects we can actually boot & screenshot
2245
2235
  // (Manager-shaped). A generic web repo that edits a stylesheet can't be
@@ -2381,12 +2371,26 @@ async function createGoalPR(goal, project, goalTasks, reviewDefinition = DEFAULT
2381
2371
  // (Masa: 「ユーザーの言語で」).
2382
2372
  const lang = reviewDefinition.language === 'auto' ? detectRequestLanguage(goal.text) : (reviewDefinition.language || 'ja');
2383
2373
  const L = (en, ja) => (lang === 'en' ? en : ja);
2384
- const bodyFor = (branch) => buildGoalPrBody({
2385
- lang, goal, goalTasks, files, owner, branch, proofRel,
2386
- proofItemsCount: proofItems.length,
2387
- reviewDescription: reviewDefinition.description,
2388
- reviewSummary: goal.reviewSummary,
2389
- });
2374
+ const bodyFor = (branch) => [
2375
+ ...(proofItems.length && owner ? [
2376
+ L('## Proof — one tap, plays in place', '## 動作確認 — 1タップでその場で再生'),
2377
+ '',
2378
+ `https://github.com/${owner}/blob/${branch}/${proofRel}/PROOF.md`,
2379
+ '',
2380
+ ] : []),
2381
+ L('## Goal', '## ゴール'),
2382
+ '',
2383
+ goal.text.slice(0, 1500),
2384
+ '',
2385
+ ...buildReviewRuleSection(reviewDefinition.description),
2386
+ L(`Implemented by Manager for AI: the goal was decomposed into ${goalTasks.length} task(s), each executed by a Claude Code worker (flat-rate, no extra API), tests written and run per task.${proofItems.length ? ' Tasks with a pass condition were verified independently in a headless browser.' : ''}`,
2387
+ `Manager for AI が実装: ゴールを${goalTasks.length}個のタスクに分解し、Claude Code worker が順に実行(既存サブスク・追加API無し・タスク毎にテストを作成し実行)。${proofItems.length ? '成功条件つきタスクは headless ブラウザで独立検証済み。' : ''}`),
2388
+ '',
2389
+ L('## Tasks and worker reports', '## タスクとworker報告'),
2390
+ ...goalTasks.map((t) => `\n### ${t.num} ${t.title} — ${t.status} (${t.secs}s)\n${(t.result ?? '').slice(0, 800)}`),
2391
+ '',
2392
+ L(`Changed files: ${files.map((f) => `\`${f}\``).join(' ')}`, `変更ファイル: ${files.map((f) => `\`${f}\``).join(' ')}`),
2393
+ ].join('\n');
2390
2394
  const title = `Manager for AI: ${isRework ? L('rework — ', '再作業 — ') : ''}${goal.plan?.[0] ?? goal.text.slice(0, 60)}${goal.plan?.length > 1 ? L(` +${goal.plan.length - 1} more`, ` 他${goal.plan.length - 1}件`) : ''}`;
2391
2395
  const { url, branch } = openPR({
2392
2396
  repoDir: project.dir, slug: `goal-${goal.id}`,
@@ -2397,7 +2401,7 @@ async function createGoalPR(goal, project, goalTasks, reviewDefinition = DEFAULT
2397
2401
  extraCopies,
2398
2402
  extraWrites: proofItems.length ? [{
2399
2403
  destRel: `${proofRel}/PROOF.md`,
2400
- content: buildGoalProofMd({ goalText: goal.text, items: proofItems, lang }),
2404
+ content: buildGoalProofMd({ goalText: goal.text, items: proofItems }),
2401
2405
  }] : [],
2402
2406
  ...(isRework ? { existingBranch: goal.prBranch, existingPrUrl: goal.pr } : {}),
2403
2407
  });
@@ -2746,22 +2750,15 @@ const server = createServer(async (req, res) => {
2746
2750
  // for pending/review items (they never spawn a worker).
2747
2751
  const runnable = !pending && !review;
2748
2752
  const needsFolder = runnable && needsProjectFolder({ dir: project.dir, homeDir: HOME, isRepo: isGitRepo(project.dir) });
2749
- const lang = /[぀-ヿ㐀-鿿]/.test(text) ? 'ja' : 'en';
2750
- const folderQuestion = needsFolder ? buildFolderQuestion(detectRepos(HOME), { lang }) : undefined;
2751
- // Task 5 (PR運用ヒアリング): once the folder is settled, ask — once per
2752
- // project, before its first runnable goal — whether this project wants
2753
- // PRs. Skipped this round if a folder question already claimed the
2754
- // needsInput slot; that goal gets rebound to a real repo via /answer
2755
- // and the NEXT goal for that project is what actually asks this.
2756
- const needsReviewPref = runnable && !needsFolder && needsReviewPreference({ askedBefore: project.reviewPrefAsked });
2757
- const reviewPrefQuestion = needsReviewPref ? buildReviewPreferenceQuestion({ lang }) : undefined;
2758
- if (needsReviewPref) { project.reviewPrefAsked = true; saveProjects(); }
2753
+ const folderQuestion = needsFolder
2754
+ ? buildFolderQuestion(detectRepos(HOME), { lang: /[぀-ヿ㐀-鿿]/.test(text) ? 'ja' : 'en' })
2755
+ : undefined;
2759
2756
  const goalAgent = workerAgent(agent);
2760
2757
  const priorFailureMemory = recentProjectFailureMemory(projectId, text.trim());
2761
2758
  const goal = {
2762
2759
  id: nextId++, projectId, text: text.trim().slice(0, 8000),
2763
- status: (needsFolder || needsReviewPref) ? 'needsInput' : review ? 'review' : pending ? 'pending' : 'planning',
2764
- question: folderQuestion || reviewPrefQuestion,
2760
+ status: needsFolder ? 'needsInput' : review ? 'review' : pending ? 'pending' : 'planning',
2761
+ question: folderQuestion,
2765
2762
  reviewOnly: review ? true : undefined,
2766
2763
  note: review && typeof note === 'string' && note.trim() ? note.trim().slice(0, 8000) : undefined,
2767
2764
  wantsPR: review ? false : resolveWantsPR(pr, text, getReviewDefinition(projectId).defaultWantsPR),
@@ -2791,7 +2788,7 @@ const server = createServer(async (req, res) => {
2791
2788
  // pending = deliberately shelved (Phase 2 material): never planned,
2792
2789
  // never queued, until POST /api/goals/:id/activate. review-only items
2793
2790
  // are terminal-until-human too, so they also skip planGoal.
2794
- if (runnable && !needsFolder && !needsReviewPref) {
2791
+ if (runnable && !needsFolder) {
2795
2792
  planGoal(goal).catch((e) => {
2796
2793
  goal.status = 'failed'; goal.prError = String(e).slice(0, 300); saveGoal(goal);
2797
2794
  });
@@ -3118,28 +3115,6 @@ const server = createServer(async (req, res) => {
3118
3115
  saveGoal(goal);
3119
3116
  return json(res, 200, goal);
3120
3117
  }
3121
- // Review-preference answer (task 5 onboarding funnel): fold into the
3122
- // project's review definition (defaultWantsPR) rather than goal.text —
3123
- // like the folder question, this is project setup, not a task detail.
3124
- if (goal.question?.kind === 'reviewPref') {
3125
- const project = projects.find((p) => p.id === goal.projectId);
3126
- const patch = resolveReviewPreferenceAnswer(answer, goal.question);
3127
- if (project) {
3128
- const merged = validateReviewDefinition({ ...getReviewDefinition(project.id), ...patch });
3129
- if (merged.ok) {
3130
- reviewDefinitions[project.id] = merged.definition;
3131
- saveReviewDefinitions();
3132
- send({ ev: 'review-definition', projectId: project.id, reviewDefinition: merged.definition });
3133
- }
3134
- }
3135
- goal.wantsPR = resolveWantsPR(undefined, goal.text, patch.defaultWantsPR);
3136
- goal.question = null;
3137
- goal.clarified = true;
3138
- goal.status = 'planning';
3139
- saveGoal(goal);
3140
- planGoal(goal).catch((e) => { goal.status = 'failed'; goal.prError = String(e).slice(0, 300); saveGoal(goal); });
3141
- return json(res, 200, goal);
3142
- }
3143
3118
  goal.text = `${goal.text}\n[確認: ${goal.question?.text ?? ''} → ${answer}]`;
3144
3119
  goal.clarified = true; // answered once → planGoal must never re-ask (persisted via saveGoal)
3145
3120
  goal.question = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@galda/cli",
3
- "version": "0.10.13",
3
+ "version": "0.10.14",
4
4
  "type": "module",
5
5
  "description": "Galda - hand off work to your Claude Code, get proof back. Runs on your existing subscription, no extra API cost.",
6
6
  "scripts": {
@@ -1,20 +0,0 @@
1
- {
2
- "task": {
3
- "id": "task-001",
4
- "title": "Save button toast",
5
- "passCondition": "click the #save button, then an element matching .toast becomes visible with text containing 'Saved' within 2 seconds"
6
- },
7
- "status": "done",
8
- "attempts": [
9
- {
10
- "attempt": 1,
11
- "workerSecs": "21.7",
12
- "pass": true,
13
- "detail": "clicked #save → toast \"Saved!\" visible",
14
- "proofScreenshot": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T06-23-57-441Z/attempt-1-proof.png",
15
- "proofVideo": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T06-23-57-441Z/attempt-1.webm"
16
- }
17
- ],
18
- "runDir": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T06-23-57-441Z",
19
- "finishedAt": "2026-07-03T06:24:22.279Z"
20
- }
@@ -1,20 +0,0 @@
1
- {
2
- "task": {
3
- "id": "task-001",
4
- "title": "Save button toast",
5
- "passCondition": "click the #save button, then an element matching .toast becomes visible with text containing 'Saved' within 2 seconds"
6
- },
7
- "status": "done",
8
- "attempts": [
9
- {
10
- "attempt": 1,
11
- "workerSecs": "19.8",
12
- "pass": true,
13
- "detail": "clicked #save → toast \"Saved!\" visible",
14
- "proofScreenshot": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T06-29-54-517Z/attempt-1-proof.png",
15
- "proofVideo": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T06-29-54-517Z/attempt-1.webm"
16
- }
17
- ],
18
- "runDir": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T06-29-54-517Z",
19
- "finishedAt": "2026-07-03T06:30:17.155Z"
20
- }
@@ -1,20 +0,0 @@
1
- {
2
- "task": {
3
- "id": "task-001",
4
- "title": "Save button toast",
5
- "passCondition": "click the #save button, then an element matching .toast becomes visible with text containing 'Saved' within 2 seconds"
6
- },
7
- "status": "done",
8
- "attempts": [
9
- {
10
- "attempt": 1,
11
- "workerSecs": "18.2",
12
- "pass": true,
13
- "detail": "clicked #save → toast \"Saved!\" visible",
14
- "proofScreenshot": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T06-40-23-231Z/attempt-1-proof.png",
15
- "proofVideo": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T06-40-23-231Z/attempt-1.webm"
16
- }
17
- ],
18
- "runDir": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T06-40-23-231Z",
19
- "finishedAt": "2026-07-03T06:40:46.785Z"
20
- }
@@ -1,21 +0,0 @@
1
- {
2
- "task": {
3
- "id": "task-001",
4
- "title": "Save button toast",
5
- "passCondition": "click the #save button, then an element matching .toast becomes visible with text containing 'Saved' within 2 seconds"
6
- },
7
- "status": "done",
8
- "attempts": [
9
- {
10
- "attempt": 1,
11
- "workerSecs": "22.3",
12
- "pass": true,
13
- "detail": "clicked #save → toast \"Saved!\" visible",
14
- "proofScreenshot": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T07-34-58-109Z/attempt-1-proof.png",
15
- "proofVideo": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T07-34-58-109Z/attempt-1.mp4"
16
- }
17
- ],
18
- "runDir": "/Users/masakinakata/common/project/galda2/tasks/task-001/runs/2026-07-03T07-34-58-109Z",
19
- "finishedAt": "2026-07-03T07:35:25.892Z",
20
- "pr": "https://github.com/kodo-inc/common/pull/1"
21
- }
@@ -1,21 +0,0 @@
1
- {
2
- "task": {
3
- "id": "task-001",
4
- "title": "Save button toast",
5
- "passCondition": "click the #save button, then an element matching .toast becomes visible with text containing 'Saved' within 2 seconds"
6
- },
7
- "status": "done",
8
- "attempts": [
9
- {
10
- "attempt": 1,
11
- "workerSecs": "21.9",
12
- "pass": true,
13
- "detail": "clicked #save → toast \"Saved!\" visible",
14
- "proofScreenshot": "/Users/masakinakata/galda2/tasks/task-001/runs/2026-07-03T07-53-44-639Z/attempt-1-proof.png",
15
- "proofVideo": "/Users/masakinakata/galda2/tasks/task-001/runs/2026-07-03T07-53-44-639Z/attempt-1.mp4"
16
- }
17
- ],
18
- "runDir": "/Users/masakinakata/galda2/tasks/task-001/runs/2026-07-03T07-53-44-639Z",
19
- "finishedAt": "2026-07-03T07:54:11.642Z",
20
- "pr": "https://github.com/kodo-inc/galda2/pull/1"
21
- }
@@ -1,21 +0,0 @@
1
- {
2
- "task": {
3
- "id": "task-001",
4
- "title": "Save button toast",
5
- "passCondition": "click the #save button, then an element matching .toast becomes visible with text containing 'Saved' within 2 seconds"
6
- },
7
- "status": "done",
8
- "attempts": [
9
- {
10
- "attempt": 1,
11
- "workerSecs": "16.3",
12
- "pass": true,
13
- "detail": "clicked #save → toast \"Saved!\" visible",
14
- "proofScreenshot": "/Users/masakinakata/galda2/tasks/task-001/runs/2026-07-03T08-02-12-117Z/attempt-1-proof.png",
15
- "proofVideo": "/Users/masakinakata/galda2/tasks/task-001/runs/2026-07-03T08-02-12-117Z/attempt-1.mp4"
16
- }
17
- ],
18
- "runDir": "/Users/masakinakata/galda2/tasks/task-001/runs/2026-07-03T08-02-12-117Z",
19
- "finishedAt": "2026-07-03T08:02:33.935Z",
20
- "pr": "https://github.com/kodo-inc/galda2/pull/2"
21
- }