@loicngr/kobo 1.7.7 → 1.7.9
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/AGENTS.md +29 -0
- package/README.md +62 -4
- package/dist/mcp-server/kobo-tasks-server.js +27 -0
- package/dist/server/routes/health.js +14 -0
- package/dist/server/routes/workspaces.js +60 -16
- package/dist/server/services/agent/engines/claude-code/capabilities.js +7 -0
- package/dist/server/services/agent/engines/codex/capabilities.js +18 -0
- package/dist/server/services/agent/engines/codex/client.js +36 -0
- package/dist/server/services/agent/engines/codex/engine.js +276 -0
- package/dist/server/services/agent/engines/codex/event-mapper.js +473 -0
- package/dist/server/services/agent/engines/codex/jsonrpc/peer.js +60 -0
- package/dist/server/services/agent/engines/codex/jsonrpc/transport.js +31 -0
- package/dist/server/services/agent/engines/codex/options-builder.js +81 -0
- package/dist/server/services/agent/engines/codex/protocol/types.js +11 -0
- package/dist/server/services/agent/engines/codex/server-requests.js +99 -0
- package/dist/server/services/agent/engines/codex/spawn.js +27 -0
- package/dist/server/services/agent/engines/registry.js +2 -0
- package/dist/server/services/agent/orchestrator.js +1 -1
- package/dist/server/services/auto-loop-service.js +16 -2
- package/dist/server/services/pr-watcher-service.js +84 -33
- package/dist/server/services/review-template-service.js +24 -31
- package/dist/server/services/settings-service.js +140 -6
- package/dist/server/services/skill-suite-prompts.js +80 -0
- package/dist/server/utils/git-ops.js +69 -15
- package/dist/server/utils/paths.js +7 -0
- package/dist/shared/auto-loop-prompts.js +18 -1
- package/dist/shared/codex-models.js +43 -0
- package/dist/shared/project-colors.js +23 -0
- package/dist/shared/skill-suite-prompts.js +66 -0
- package/package.json +2 -1
- package/src/client/dist/spa/assets/ActivityFeed-CKjFT9t6.js +8 -0
- package/src/client/dist/spa/assets/{ActivityFeed-tE4LVYck.css → ActivityFeed-WjiQ9716.css} +1 -1
- package/src/client/dist/spa/assets/{ClosePopup-DTcbxsC0.js → ClosePopup-DMnQG6nw.js} +1 -1
- package/src/client/dist/spa/assets/CreatePage-BhFrUkEN.js +2 -0
- package/src/client/dist/spa/assets/CreatePage-ZyBHUbl0.css +1 -0
- package/src/client/dist/spa/assets/{DiffViewer-D-uNbBq0.js → DiffViewer-BSnvba7W.js} +3 -3
- package/src/client/dist/spa/assets/HealthPage-DZYZWGHp.js +1 -0
- package/src/client/dist/spa/assets/MainLayout-C45J7rSF.css +1 -0
- package/src/client/dist/spa/assets/MainLayout-CMuiNpet.js +37 -0
- package/src/client/dist/spa/assets/QChip-BgzxI33B.js +36 -0
- package/src/client/dist/spa/assets/{QExpansionItem-BGg74no1.js → QExpansionItem-Fij7yBbG.js} +1 -1
- package/src/client/dist/spa/assets/{QItemSection-CQUDd0Vg.js → QItemSection-Bz1ZDJO5.js} +1 -1
- package/src/client/dist/spa/assets/{QMenu-D6uqosRg.js → QMenu-CMoolewZ.js} +1 -1
- package/src/client/dist/spa/assets/QRadio-4HnR_A-K.js +1 -0
- package/src/client/dist/spa/assets/{QTooltip-DUGPNNeQ.js → QTooltip-CbLXk2Bs.js} +1 -1
- package/src/client/dist/spa/assets/{SearchPage-C07dgzT9.js → SearchPage-CBSgEvVF.js} +1 -1
- package/src/client/dist/spa/assets/SettingsPage-C7TkcKXU.css +1 -0
- package/src/client/dist/spa/assets/SettingsPage-pY-zbPxn.js +9 -0
- package/src/client/dist/spa/assets/{TouchPan-DvVlszwO.js → TouchPan-DiBNjOPH.js} +1 -1
- package/src/client/dist/spa/assets/{WorkspacePage-CRIcsASQ.css → WorkspacePage-B4YnZ6re.css} +1 -1
- package/src/client/dist/spa/assets/WorkspacePage-BTHvQga-.js +4 -0
- package/src/client/dist/spa/assets/{build-path-tree-CCMckvpr.js → build-path-tree-BmBqRiCQ.js} +1 -1
- package/src/client/dist/spa/assets/{cssMode-D6XTTdwy.js → cssMode-ypFF7quM.js} +1 -1
- package/src/client/dist/spa/assets/{editor.api-6hDVHddO.js → editor.api-DtpZuH_B.js} +1 -1
- package/src/client/dist/spa/assets/{editor.main-DsLU1RWu.js → editor.main-C7a7L2WP.js} +3 -3
- package/src/client/dist/spa/assets/{AutoLoopChip-CkSzkC0C.js → engineFeatures-BxAOQcPU.js} +1 -1
- package/src/client/dist/spa/assets/{expand-template-Crz1uiBt.js → expand-template-GaEux9_o.js} +1 -1
- package/src/client/dist/spa/assets/{formatters-guwb-rzl.js → formatters-h0XBETG5.js} +1 -1
- package/src/client/dist/spa/assets/{freemarker2-Bn1f0t2U.js → freemarker2-DUBmhe3W.js} +1 -1
- package/src/client/dist/spa/assets/{handlebars-O92Cbq66.js → handlebars-_XEXkADl.js} +1 -1
- package/src/client/dist/spa/assets/{html-Ck95BMBU.js → html-D8gmyhgI.js} +1 -1
- package/src/client/dist/spa/assets/{htmlMode-DDYhH2FJ.js → htmlMode-B84S5YOM.js} +1 -1
- package/src/client/dist/spa/assets/i18n-DLoe3l25.js +1 -0
- package/src/client/dist/spa/assets/index-Dx_W9yYo.js +2 -0
- package/src/client/dist/spa/assets/{javascript-Cy2ddqHg.js → javascript-500DcdS9.js} +1 -1
- package/src/client/dist/spa/assets/{jsonMode-BIfVcp5z.js → jsonMode-DmrWg6b7.js} +1 -1
- package/src/client/dist/spa/assets/kobo-commands-DCoQW_NQ.js +9 -0
- package/src/client/dist/spa/assets/{liquid-B287eegh.js → liquid-CfPJszlt.js} +1 -1
- package/src/client/dist/spa/assets/{mdx-B8HSzGai.js → mdx-DtjLwENT.js} +1 -1
- package/src/client/dist/spa/assets/{monaco.contribution-CofcHzEf.js → monaco.contribution-CxiO5UJd.js} +2 -2
- package/src/client/dist/spa/assets/{notifications-BPnKFW60.js → notifications-CEyiPnmw.js} +1 -1
- package/src/client/dist/spa/assets/permissionModes-CPZlEHoF.js +1 -0
- package/src/client/dist/spa/assets/project-color-C4vMEn4C.js +1 -0
- package/src/client/dist/spa/assets/{purify.es-BCEwTYRx.js → purify.es-C92_EGvT.js} +1 -1
- package/src/client/dist/spa/assets/{python-csaKR6_U.js → python-BS46_AMt.js} +1 -1
- package/src/client/dist/spa/assets/{razor-C2wEv-nX.js → razor-Ce9zcIFo.js} +1 -1
- package/src/client/dist/spa/assets/{render-chat-markdown-Bjcei0vn.js → render-chat-markdown-BvJwlMiW.js} +1 -1
- package/src/client/dist/spa/assets/{tsMode-DGLVs57K.js → tsMode-Cr9FJjYY.js} +1 -1
- package/src/client/dist/spa/assets/{typescript-w0GWHzZ3.js → typescript-Ov3wChBg.js} +1 -1
- package/src/client/dist/spa/assets/{use-panel-CbJ44rqY.js → use-panel-lBh91vcW.js} +1 -1
- package/src/client/dist/spa/assets/{xml-CTn-vnEd.js → xml-euA4jBI1.js} +1 -1
- package/src/client/dist/spa/assets/{yaml-CTyUSvLZ.js → yaml-BuPSq_BT.js} +1 -1
- package/src/client/dist/spa/index.html +5 -5
- package/src/mcp-server/kobo-tasks-server.ts +27 -0
- package/src/client/dist/spa/assets/ActivityFeed-DlPVoOGb.js +0 -7
- package/src/client/dist/spa/assets/CreatePage-BoRappO3.css +0 -1
- package/src/client/dist/spa/assets/CreatePage-DpCVNwYk.js +0 -2
- package/src/client/dist/spa/assets/HealthPage-xZ0PP4F-.js +0 -1
- package/src/client/dist/spa/assets/MainLayout-DdkKM2ba.js +0 -37
- package/src/client/dist/spa/assets/MainLayout-drolsINz.css +0 -1
- package/src/client/dist/spa/assets/QChip-erWIZgxW.js +0 -1
- package/src/client/dist/spa/assets/QRadio-DJxOyOA3.js +0 -1
- package/src/client/dist/spa/assets/QTabPanels-ClPY9y4T.js +0 -1
- package/src/client/dist/spa/assets/SettingsPage-CLNmI0Rr.css +0 -1
- package/src/client/dist/spa/assets/SettingsPage-D0CZNqkA.js +0 -9
- package/src/client/dist/spa/assets/WorkspacePage-CKeCLPi0.js +0 -4
- package/src/client/dist/spa/assets/i18n-BLgknHpf.js +0 -1
- package/src/client/dist/spa/assets/index-CdHDdk1y.js +0 -2
- package/src/client/dist/spa/assets/kobo-commands-w8VepGvD.js +0 -11
- package/src/client/dist/spa/assets/models-Bd_v3W7Q.js +0 -1
- /package/src/client/dist/spa/assets/{QBtn-DEuWKHbR.js → QBtn-CaJSOyt8.js} +0 -0
- /package/src/client/dist/spa/assets/{vue-i18n-DI-gS-CC.js → vue-i18n-CKCtKE87.js} +0 -0
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { WORKTREES_PATH } from '../../shared/consts.js';
|
|
4
|
+
import { isValidProjectColor } from '../../shared/project-colors.js';
|
|
5
|
+
import { isValidSkillSuite } from '../../shared/skill-suite-prompts.js';
|
|
4
6
|
import { listClaudeMcpEntries } from '../utils/mcp-client.js';
|
|
5
7
|
import { getSettingsPath } from '../utils/paths.js';
|
|
6
8
|
import { InvalidWorktreesPathError, resolveGlobalWorktreesRoot, sanitizeWorktreesPath, validateWorktreesPath, } from '../utils/worktree-paths.js';
|
|
7
9
|
import { DEFAULT_NOTION_INITIAL_PROMPT, DEFAULT_SENTRY_INITIAL_PROMPT } from './initial-prompt-template-service.js';
|
|
8
10
|
import { DEFAULT_REVIEW_PROMPT_TEMPLATE } from './review-template-service.js';
|
|
11
|
+
import { AGNOSTIC_PROMPTS } from './skill-suite-prompts.js';
|
|
9
12
|
export const DEFAULT_GIT_CONVENTIONS = `# Git conventions
|
|
10
13
|
|
|
11
14
|
## Commits
|
|
@@ -299,6 +302,101 @@ const settingsMigrations = [
|
|
|
299
302
|
global.voiceSuppressNonSpeechTokens = true;
|
|
300
303
|
},
|
|
301
304
|
},
|
|
305
|
+
{
|
|
306
|
+
version: 19,
|
|
307
|
+
name: 'split-default-model-by-engine',
|
|
308
|
+
// Codex was added alongside Claude Code; the single `defaultModel` is now
|
|
309
|
+
// ambiguous (different engines have different model catalogues). Split it
|
|
310
|
+
// into a per-engine map. Preserve the legacy value as the claude-code
|
|
311
|
+
// default for back-compat, and seed codex with `'auto'`.
|
|
312
|
+
migrate({ global }) {
|
|
313
|
+
const existing = global.defaultModelByEngine;
|
|
314
|
+
if (typeof existing !== 'object' || existing === null || Array.isArray(existing)) {
|
|
315
|
+
const legacyModel = typeof global.defaultModel === 'string' && global.defaultModel.length > 0
|
|
316
|
+
? global.defaultModel
|
|
317
|
+
: 'auto';
|
|
318
|
+
global.defaultModelByEngine = {
|
|
319
|
+
'claude-code': legacyModel,
|
|
320
|
+
codex: 'auto',
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
// Backfill any missing engine entries idempotently.
|
|
325
|
+
const map = existing;
|
|
326
|
+
if (typeof map['claude-code'] !== 'string')
|
|
327
|
+
map['claude-code'] = 'auto';
|
|
328
|
+
if (typeof map.codex !== 'string')
|
|
329
|
+
map.codex = 'auto';
|
|
330
|
+
}
|
|
331
|
+
// Drop the legacy field once migrated.
|
|
332
|
+
delete global.defaultModel;
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
version: 20,
|
|
337
|
+
name: 'split-default-permission-mode-by-engine',
|
|
338
|
+
// Mirrors v19 for permission modes. Both engines accept the full mode set.
|
|
339
|
+
migrate({ global }) {
|
|
340
|
+
const existing = global.defaultPermissionModeByEngine;
|
|
341
|
+
const legacyMode = typeof global.defaultPermissionMode === 'string' && global.defaultPermissionMode.length > 0
|
|
342
|
+
? global.defaultPermissionMode
|
|
343
|
+
: 'plan';
|
|
344
|
+
if (typeof existing !== 'object' || existing === null || Array.isArray(existing)) {
|
|
345
|
+
global.defaultPermissionModeByEngine = {
|
|
346
|
+
'claude-code': legacyMode,
|
|
347
|
+
codex: legacyMode,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
const map = existing;
|
|
352
|
+
if (typeof map['claude-code'] !== 'string')
|
|
353
|
+
map['claude-code'] = legacyMode;
|
|
354
|
+
if (typeof map.codex !== 'string')
|
|
355
|
+
map.codex = legacyMode;
|
|
356
|
+
}
|
|
357
|
+
delete global.defaultPermissionMode;
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
version: 21,
|
|
362
|
+
name: 'add-project-color-and-flatten',
|
|
363
|
+
migrate: ({ global, projects }) => {
|
|
364
|
+
if (typeof global.flattenWorkspaceList !== 'boolean') {
|
|
365
|
+
global.flattenWorkspaceList = false;
|
|
366
|
+
}
|
|
367
|
+
for (const p of projects) {
|
|
368
|
+
if (!('color' in p) || (p.color !== null && !isValidProjectColor(p.color))) {
|
|
369
|
+
;
|
|
370
|
+
p.color = null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
version: 22,
|
|
377
|
+
name: 'add-skill-suite-selector',
|
|
378
|
+
// Auto-migrate every existing user to `superpowers` (the closest match to
|
|
379
|
+
// today's behaviour). New installs get the same default via
|
|
380
|
+
// `defaultSettings()`. The 4 `custom*` fields seed with the agnostic
|
|
381
|
+
// baseline so users switching to `custom` mode have a sane editable start.
|
|
382
|
+
migrate: ({ global }) => {
|
|
383
|
+
if (!isValidSkillSuite(global.skillSuite)) {
|
|
384
|
+
global.skillSuite = 'superpowers';
|
|
385
|
+
}
|
|
386
|
+
if (typeof global.customReviewTemplate !== 'string') {
|
|
387
|
+
global.customReviewTemplate = AGNOSTIC_PROMPTS.reviewTemplate;
|
|
388
|
+
}
|
|
389
|
+
if (typeof global.customAutoLoopReviewGate !== 'string') {
|
|
390
|
+
global.customAutoLoopReviewGate = AGNOSTIC_PROMPTS.autoLoopReviewGate;
|
|
391
|
+
}
|
|
392
|
+
if (typeof global.customAutoLoopGroomingIntro !== 'string') {
|
|
393
|
+
global.customAutoLoopGroomingIntro = AGNOSTIC_PROMPTS.autoLoopGroomingIntro;
|
|
394
|
+
}
|
|
395
|
+
if (typeof global.customQaPromptTemplate !== 'string') {
|
|
396
|
+
global.customQaPromptTemplate = AGNOSTIC_PROMPTS.qaPromptTemplate;
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
},
|
|
302
400
|
];
|
|
303
401
|
/** Current settings schema version — always equals the highest migration version. */
|
|
304
402
|
export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
|
|
@@ -326,7 +424,7 @@ function defaultSettings() {
|
|
|
326
424
|
return {
|
|
327
425
|
schemaVersion: SETTINGS_SCHEMA_VERSION,
|
|
328
426
|
global: {
|
|
329
|
-
|
|
427
|
+
defaultModelByEngine: { 'claude-code': 'auto', codex: 'auto' },
|
|
330
428
|
dangerouslySkipPermissions: true,
|
|
331
429
|
prPromptTemplate: DEFAULT_PR_PROMPT_TEMPLATE,
|
|
332
430
|
reviewPromptTemplate: DEFAULT_REVIEW_PROMPT_TEMPLATE,
|
|
@@ -340,7 +438,7 @@ function defaultSettings() {
|
|
|
340
438
|
audioNotificationVolume: 1,
|
|
341
439
|
notionStatusProperty: '',
|
|
342
440
|
notionInProgressStatus: '',
|
|
343
|
-
|
|
441
|
+
defaultPermissionModeByEngine: { 'claude-code': 'plan', codex: 'plan' },
|
|
344
442
|
notionMcpKey: '',
|
|
345
443
|
sentryMcpKey: '',
|
|
346
444
|
tags: [...DEFAULT_WORKSPACE_TAGS],
|
|
@@ -356,6 +454,12 @@ function defaultSettings() {
|
|
|
356
454
|
voicePrompt: '',
|
|
357
455
|
voiceTranslateToEnglish: false,
|
|
358
456
|
voiceSuppressNonSpeechTokens: true,
|
|
457
|
+
flattenWorkspaceList: false,
|
|
458
|
+
skillSuite: 'superpowers',
|
|
459
|
+
customReviewTemplate: AGNOSTIC_PROMPTS.reviewTemplate,
|
|
460
|
+
customAutoLoopReviewGate: AGNOSTIC_PROMPTS.autoLoopReviewGate,
|
|
461
|
+
customAutoLoopGroomingIntro: AGNOSTIC_PROMPTS.autoLoopGroomingIntro,
|
|
462
|
+
customQaPromptTemplate: AGNOSTIC_PROMPTS.qaPromptTemplate,
|
|
359
463
|
},
|
|
360
464
|
projects: [],
|
|
361
465
|
};
|
|
@@ -385,6 +489,7 @@ function defaultProjectSettings(projectPath) {
|
|
|
385
489
|
finalization: {
|
|
386
490
|
prompt: DEFAULT_FINALIZATION_PROMPT,
|
|
387
491
|
},
|
|
492
|
+
color: null,
|
|
388
493
|
};
|
|
389
494
|
}
|
|
390
495
|
function pickKnownKeys(data, allowedKeys) {
|
|
@@ -540,8 +645,9 @@ export function getEffectiveSettings(projectPath) {
|
|
|
540
645
|
const settings = readSettings();
|
|
541
646
|
const project = settings.projects.find((p) => p.path === projectPath) ?? null;
|
|
542
647
|
if (!project) {
|
|
648
|
+
const claudeCodeDefault = settings.global.defaultModelByEngine?.['claude-code'] ?? 'auto';
|
|
543
649
|
return {
|
|
544
|
-
model:
|
|
650
|
+
model: claudeCodeDefault,
|
|
545
651
|
dangerouslySkipPermissions: settings.global.dangerouslySkipPermissions,
|
|
546
652
|
prPromptTemplate: settings.global.prPromptTemplate,
|
|
547
653
|
reviewPromptTemplate: settings.global.reviewPromptTemplate,
|
|
@@ -555,8 +661,14 @@ export function getEffectiveSettings(projectPath) {
|
|
|
555
661
|
notionInProgressStatus: settings.global.notionInProgressStatus,
|
|
556
662
|
};
|
|
557
663
|
}
|
|
664
|
+
// `model` here is the legacy single-string field exposed via EffectiveSettings
|
|
665
|
+
// for back-compat with existing callers. The engine-aware default lives in
|
|
666
|
+
// `global.defaultModelByEngine[engineId]` and is read directly by the create
|
|
667
|
+
// flow / settings UI. Fall back through claude-code's entry (the historical
|
|
668
|
+
// semantics) so this field never goes empty.
|
|
669
|
+
const claudeCodeDefault = settings.global.defaultModelByEngine?.['claude-code'] ?? 'auto';
|
|
558
670
|
return {
|
|
559
|
-
model: project.defaultModel ||
|
|
671
|
+
model: project.defaultModel || claudeCodeDefault,
|
|
560
672
|
dangerouslySkipPermissions: project.dangerouslySkipPermissions ?? settings.global.dangerouslySkipPermissions,
|
|
561
673
|
prPromptTemplate: project.prPromptTemplate || settings.global.prPromptTemplate,
|
|
562
674
|
reviewPromptTemplate: project.reviewPromptTemplate || settings.global.reviewPromptTemplate,
|
|
@@ -573,8 +685,16 @@ export function getEffectiveSettings(projectPath) {
|
|
|
573
685
|
/** Merge partial updates into global settings and persist. */
|
|
574
686
|
export function updateGlobalSettings(data) {
|
|
575
687
|
const settings = readSettings();
|
|
688
|
+
// Validate skillSuite before merging: drop invalid values so the previous
|
|
689
|
+
// value is preserved (same pattern as `upsertProject`'s color validation).
|
|
690
|
+
if ('skillSuite' in data) {
|
|
691
|
+
if (!isValidSkillSuite(data.skillSuite)) {
|
|
692
|
+
console.warn(`[settings] Invalid skillSuite value rejected: ${data.skillSuite}`);
|
|
693
|
+
delete data.skillSuite;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
576
696
|
const allowedGlobalKeys = [
|
|
577
|
-
'
|
|
697
|
+
'defaultModelByEngine',
|
|
578
698
|
'dangerouslySkipPermissions',
|
|
579
699
|
'prPromptTemplate',
|
|
580
700
|
'reviewPromptTemplate',
|
|
@@ -588,7 +708,7 @@ export function updateGlobalSettings(data) {
|
|
|
588
708
|
'audioNotificationVolume',
|
|
589
709
|
'notionStatusProperty',
|
|
590
710
|
'notionInProgressStatus',
|
|
591
|
-
'
|
|
711
|
+
'defaultPermissionModeByEngine',
|
|
592
712
|
'notionMcpKey',
|
|
593
713
|
'sentryMcpKey',
|
|
594
714
|
'tags',
|
|
@@ -604,6 +724,12 @@ export function updateGlobalSettings(data) {
|
|
|
604
724
|
'voicePrompt',
|
|
605
725
|
'voiceTranslateToEnglish',
|
|
606
726
|
'voiceSuppressNonSpeechTokens',
|
|
727
|
+
'flattenWorkspaceList',
|
|
728
|
+
'skillSuite',
|
|
729
|
+
'customReviewTemplate',
|
|
730
|
+
'customAutoLoopReviewGate',
|
|
731
|
+
'customAutoLoopGroomingIntro',
|
|
732
|
+
'customQaPromptTemplate',
|
|
607
733
|
];
|
|
608
734
|
const filtered = pickKnownKeys(data, allowedGlobalKeys);
|
|
609
735
|
if (filtered.tags !== undefined) {
|
|
@@ -647,6 +773,13 @@ function isNonNativeWindowsPath(value) {
|
|
|
647
773
|
}
|
|
648
774
|
/** Create or update project-specific settings. Merges devServer, e2e, and finalization fields on update. */
|
|
649
775
|
export function upsertProject(projectPath, data) {
|
|
776
|
+
// Validate color: accept null or a valid palette entry; drop anything else.
|
|
777
|
+
if ('color' in data) {
|
|
778
|
+
if (data.color !== null && !isValidProjectColor(data.color)) {
|
|
779
|
+
console.warn(`[settings] Invalid color value rejected for project '${projectPath}': ${data.color}`);
|
|
780
|
+
delete data.color;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
650
783
|
const allowedProjectKeys = [
|
|
651
784
|
'displayName',
|
|
652
785
|
'defaultSourceBranch',
|
|
@@ -661,6 +794,7 @@ export function upsertProject(projectPath, data) {
|
|
|
661
794
|
'devServer',
|
|
662
795
|
'e2e',
|
|
663
796
|
'finalization',
|
|
797
|
+
'color',
|
|
664
798
|
];
|
|
665
799
|
const allowedDevServerKeys = ['startCommand', 'stopCommand'];
|
|
666
800
|
const allowedE2eKeys = ['framework', 'skill', 'prompt'];
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { AGNOSTIC_AUTO_LOOP_GROOMING_INTRO, AGNOSTIC_AUTO_LOOP_REVIEW_GATE, AGNOSTIC_QA_PROMPT_TEMPLATE, AGNOSTIC_REVIEW_TEMPLATE, GROOMING_INTRO_GSTACK, GROOMING_INTRO_SUPERPOWERS, } from '../../shared/skill-suite-prompts.js';
|
|
2
|
+
// Headers and body shared with the agnostic baseline. We reconstruct them
|
|
3
|
+
// locally here so the suite-specific text can sit between header and body
|
|
4
|
+
// in the same shape across all 3 suites.
|
|
5
|
+
const REVIEW_HEADER = `You are reviewing code changes on workspace "{{workspace_name}}" in project {{project_name}}.
|
|
6
|
+
|
|
7
|
+
Branch: {{branch_name}} (base: {{source_branch}})
|
|
8
|
+
Base commit: {{base_commit}}
|
|
9
|
+
|
|
10
|
+
`;
|
|
11
|
+
const REVIEW_BODY = `## Scope
|
|
12
|
+
|
|
13
|
+
Review ALL changes — both committed and uncommitted in the working tree:
|
|
14
|
+
- \`git diff {{base_commit}}..HEAD\` — committed changes on this branch
|
|
15
|
+
- \`git status\` and \`git diff\` — uncommitted changes (staged + unstaged)
|
|
16
|
+
|
|
17
|
+
## Diff summary
|
|
18
|
+
{{diff_stats}}
|
|
19
|
+
|
|
20
|
+
## Commits
|
|
21
|
+
{{commits}}
|
|
22
|
+
|
|
23
|
+
## Additional instructions
|
|
24
|
+
{{additional_instructions}}
|
|
25
|
+
|
|
26
|
+
## Output
|
|
27
|
+
|
|
28
|
+
If no review skill is available, structure your reply as:
|
|
29
|
+
1. Summary — what changed and why
|
|
30
|
+
2. Issues — bugs, regressions, security or perf concerns (with file:line)
|
|
31
|
+
3. Suggestions — refactor / improvement opportunities
|
|
32
|
+
4. Tests — coverage gaps
|
|
33
|
+
5. Verdict — ship / fix-then-ship / blocked
|
|
34
|
+
`;
|
|
35
|
+
export const SUPERPOWERS_PROMPTS = {
|
|
36
|
+
reviewTemplate: REVIEW_HEADER +
|
|
37
|
+
'If a code-review skill is available (e.g. superpowers:requesting-code-review), invoke it to drive this review. Otherwise follow the steps below directly.\n\n' +
|
|
38
|
+
REVIEW_BODY,
|
|
39
|
+
autoLoopReviewGate: 'Code review gate — BEFORE marking the task done, dispatch an independent code-reviewer subagent via the Task tool with `subagent_type: "code-reviewer"` (or `"superpowers:code-reviewer"` / `"pr-review-toolkit:code-reviewer"` — use whichever exists in this environment; fall back to `superpowers:requesting-code-review` skill if none is available). Brief the reviewer with: what you just implemented, the task title, and the commit SHA (via `git rev-parse HEAD`). Ask specifically whether the change matches the task scope, whether edge cases are handled, and whether the commit is clean.',
|
|
40
|
+
autoLoopGroomingIntro: GROOMING_INTRO_SUPERPOWERS,
|
|
41
|
+
qaPromptTemplate: 'QA pass for workspace "{{workspace_name}}" in project {{project_name}}.\n\nBranch: {{branch_name}}\nStaging URL: {{staging_url}}\n\nIf a QA-style skill that drives a real browser is available in this environment (e.g. via the superpowers-chrome browsing skill), use it to navigate the staging URL and exercise the changes. Otherwise, fall back to manually scripting the smoke checks and recording your findings as a bug report.',
|
|
42
|
+
};
|
|
43
|
+
export const GSTACK_PROMPTS = {
|
|
44
|
+
reviewTemplate: REVIEW_HEADER +
|
|
45
|
+
'Run /review to drive this audit (the gstack Staff Engineer skill — finds bugs that pass CI but blow up in production, auto-fixes the obvious ones, flags completeness gaps). If /review is unavailable in this environment, fall back to the manual checklist below.\n\n' +
|
|
46
|
+
REVIEW_BODY,
|
|
47
|
+
autoLoopReviewGate: 'Code review gate — BEFORE marking the task done, run /review (the gstack Staff Engineer skill). Brief it with: what you just implemented, the task title, and the commit SHA (via `git rev-parse HEAD`). Ask specifically whether the change matches the task scope, whether edge cases are handled, and whether the commit is clean. If /review auto-fixes minor issues, accept the fixes via an amend or fix-up commit, then re-run step 3 checks.',
|
|
48
|
+
autoLoopGroomingIntro: GROOMING_INTRO_GSTACK,
|
|
49
|
+
qaPromptTemplate: 'QA pass for workspace "{{workspace_name}}" in project {{project_name}}.\n\nBranch: {{branch_name}}\nStaging URL: {{staging_url}}\n\nRun /qa {{staging_url}} (gstack QA Lead skill — opens a real browser, clicks through flows, finds bugs, fixes them with atomic commits, generates regression tests). If you only want a bug report without code changes, use /qa-only {{staging_url}} instead.',
|
|
50
|
+
};
|
|
51
|
+
export const AGNOSTIC_PROMPTS = {
|
|
52
|
+
reviewTemplate: AGNOSTIC_REVIEW_TEMPLATE,
|
|
53
|
+
autoLoopReviewGate: AGNOSTIC_AUTO_LOOP_REVIEW_GATE,
|
|
54
|
+
autoLoopGroomingIntro: AGNOSTIC_AUTO_LOOP_GROOMING_INTRO,
|
|
55
|
+
qaPromptTemplate: AGNOSTIC_QA_PROMPT_TEMPLATE,
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Resolve the suite prompts to use right now, given the global `skillSuite`
|
|
59
|
+
* and the four user-editable `custom*` fields (only consulted in `custom` mode).
|
|
60
|
+
* Empty-string or whitespace-only overrides fall back to AGNOSTIC defaults.
|
|
61
|
+
*/
|
|
62
|
+
export function getSuitePrompts(suite, overrides) {
|
|
63
|
+
if (suite === 'superpowers')
|
|
64
|
+
return SUPERPOWERS_PROMPTS;
|
|
65
|
+
if (suite === 'gstack')
|
|
66
|
+
return GSTACK_PROMPTS;
|
|
67
|
+
// custom mode: per-field fallback to AGNOSTIC when the override is missing/blank
|
|
68
|
+
const pick = (k) => {
|
|
69
|
+
const value = overrides[k];
|
|
70
|
+
if (typeof value !== 'string')
|
|
71
|
+
return AGNOSTIC_PROMPTS[k];
|
|
72
|
+
return value.trim() ? value : AGNOSTIC_PROMPTS[k];
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
reviewTemplate: pick('reviewTemplate'),
|
|
76
|
+
autoLoopReviewGate: pick('autoLoopReviewGate'),
|
|
77
|
+
autoLoopGroomingIntro: pick('autoLoopGroomingIntro'),
|
|
78
|
+
qaPromptTemplate: pick('qaPromptTemplate'),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -409,21 +409,80 @@ export function getPrUrl(repoPath, branchName) {
|
|
|
409
409
|
return null;
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
|
-
|
|
412
|
+
const GH_PR_FIELDS = [
|
|
413
|
+
'number',
|
|
414
|
+
'title',
|
|
415
|
+
'url',
|
|
416
|
+
'state',
|
|
417
|
+
'baseRefName',
|
|
418
|
+
'reviewDecision',
|
|
419
|
+
'author',
|
|
420
|
+
'assignees',
|
|
421
|
+
'labels',
|
|
422
|
+
'latestReviews',
|
|
423
|
+
'reviewRequests',
|
|
424
|
+
'reviewThreads',
|
|
425
|
+
'statusCheckRollup',
|
|
426
|
+
'updatedAt',
|
|
427
|
+
].join(',');
|
|
428
|
+
function mapGhPrToSnapshot(raw) {
|
|
429
|
+
const reviewers = [];
|
|
430
|
+
const seen = new Set();
|
|
431
|
+
for (const r of raw.latestReviews ?? []) {
|
|
432
|
+
const login = r.author?.login;
|
|
433
|
+
if (!login || seen.has(login))
|
|
434
|
+
continue;
|
|
435
|
+
seen.add(login);
|
|
436
|
+
reviewers.push({ login, state: r.state ?? 'COMMENTED' });
|
|
437
|
+
}
|
|
438
|
+
for (const r of raw.reviewRequests ?? []) {
|
|
439
|
+
if (!r.login || seen.has(r.login))
|
|
440
|
+
continue;
|
|
441
|
+
seen.add(r.login);
|
|
442
|
+
reviewers.push({ login: r.login, state: 'PENDING' });
|
|
443
|
+
}
|
|
444
|
+
const checks = (raw.statusCheckRollup ?? []).map((c) => ({
|
|
445
|
+
name: c.name,
|
|
446
|
+
conclusion: c.conclusion ?? null,
|
|
447
|
+
status: c.status,
|
|
448
|
+
detailsUrl: c.detailsUrl ?? null,
|
|
449
|
+
}));
|
|
450
|
+
let rollup = null;
|
|
451
|
+
if (checks.length > 0) {
|
|
452
|
+
if (checks.some((c) => c.conclusion === 'FAILURE'))
|
|
453
|
+
rollup = 'FAILURE';
|
|
454
|
+
else if (checks.some((c) => c.status !== 'COMPLETED'))
|
|
455
|
+
rollup = 'PENDING';
|
|
456
|
+
else
|
|
457
|
+
rollup = 'SUCCESS';
|
|
458
|
+
}
|
|
459
|
+
const unresolvedReviewThreadsCount = (raw.reviewThreads ?? []).reduce((acc, t) => acc + (t.isResolved ? 0 : 1), 0);
|
|
460
|
+
return {
|
|
461
|
+
number: raw.number,
|
|
462
|
+
title: raw.title,
|
|
463
|
+
url: raw.url,
|
|
464
|
+
state: raw.state,
|
|
465
|
+
base: raw.baseRefName ?? '',
|
|
466
|
+
reviewDecision: raw.reviewDecision ?? null,
|
|
467
|
+
author: { login: raw.author?.login ?? '' },
|
|
468
|
+
assignees: (raw.assignees ?? []).map((a) => ({ login: a.login })),
|
|
469
|
+
reviewers,
|
|
470
|
+
labels: (raw.labels ?? []).map((l) => ({ name: l.name, color: l.color })),
|
|
471
|
+
ci: { rollup, checks },
|
|
472
|
+
updatedAt: raw.updatedAt ?? '',
|
|
473
|
+
unresolvedReviewThreadsCount,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
/** Get a rich snapshot of the PR for a branch. Returns null if no PR exists. */
|
|
413
477
|
export function getPrStatus(repoPath, branchName) {
|
|
414
478
|
try {
|
|
415
|
-
const raw = execFileSync('gh', ['pr', 'view', branchName, '--json',
|
|
479
|
+
const raw = execFileSync('gh', ['pr', 'view', branchName, '--json', GH_PR_FIELDS], {
|
|
416
480
|
cwd: repoPath,
|
|
417
481
|
encoding: 'utf-8',
|
|
418
482
|
}).trim();
|
|
419
483
|
if (!raw)
|
|
420
484
|
return null;
|
|
421
|
-
|
|
422
|
-
return {
|
|
423
|
-
state: parsed.state,
|
|
424
|
-
url: parsed.url,
|
|
425
|
-
base: parsed.baseRefName || undefined,
|
|
426
|
-
};
|
|
485
|
+
return mapGhPrToSnapshot(JSON.parse(raw));
|
|
427
486
|
}
|
|
428
487
|
catch {
|
|
429
488
|
return null;
|
|
@@ -766,19 +825,14 @@ export async function getPrUrlAsync(repoPath, branchName) {
|
|
|
766
825
|
/** Async version of getPrStatus. Returns null if no PR exists. */
|
|
767
826
|
export async function getPrStatusAsync(repoPath, branchName) {
|
|
768
827
|
try {
|
|
769
|
-
const { stdout } = await execFileAsync('gh', ['pr', 'view', branchName, '--json',
|
|
828
|
+
const { stdout } = await execFileAsync('gh', ['pr', 'view', branchName, '--json', GH_PR_FIELDS], {
|
|
770
829
|
cwd: repoPath,
|
|
771
830
|
encoding: 'utf-8',
|
|
772
831
|
});
|
|
773
832
|
const raw = stdout.trim();
|
|
774
833
|
if (!raw)
|
|
775
834
|
return null;
|
|
776
|
-
|
|
777
|
-
return {
|
|
778
|
-
state: parsed.state,
|
|
779
|
-
url: parsed.url,
|
|
780
|
-
base: parsed.baseRefName || undefined,
|
|
781
|
-
};
|
|
835
|
+
return mapGhPrToSnapshot(JSON.parse(raw));
|
|
782
836
|
}
|
|
783
837
|
catch {
|
|
784
838
|
return null;
|
|
@@ -100,8 +100,15 @@ export function getTemplatesPath() {
|
|
|
100
100
|
* Absolute path to the compiled MCP server entry (shipped in the published
|
|
101
101
|
* package as dist/mcp-server/kobo-tasks-server.js). Returns null if not
|
|
102
102
|
* present — callers (orchestrator) then fall back to the TS source for dev.
|
|
103
|
+
*
|
|
104
|
+
* `KOBO_ENFORCE_LOCAL_HOME=1` (set by `npm run dev`) forces the source path
|
|
105
|
+
* even if a stale `dist/mcp-server/` from a prior `npm run build` is hanging
|
|
106
|
+
* around. Without this guard, edits to `kobo-tasks-server.ts` are silently
|
|
107
|
+
* ignored during dev (the orchestrator spawns the months-old compiled binary).
|
|
103
108
|
*/
|
|
104
109
|
export function getCompiledMcpServerPath() {
|
|
110
|
+
if (process.env.KOBO_ENFORCE_LOCAL_HOME === '1')
|
|
111
|
+
return null;
|
|
105
112
|
const compiled = getPackageAssetPath('dist', 'mcp-server', 'kobo-tasks-server.js');
|
|
106
113
|
return fs.existsSync(compiled) ? compiled : null;
|
|
107
114
|
}
|
|
@@ -16,7 +16,24 @@
|
|
|
16
16
|
*
|
|
17
17
|
* `AUTO_LOOP_HARD_RULES` is the trailing hard-rules block, same for both.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
import { getGroomingIntro } from './skill-suite-prompts.js';
|
|
20
|
+
/**
|
|
21
|
+
* Build the grooming intro for the chosen skill suite. In `custom` mode the
|
|
22
|
+
* user's `customAutoLoopGroomingIntro` overrides the agnostic default (when
|
|
23
|
+
* non-empty). This is the runtime-correct way to obtain the intro — every
|
|
24
|
+
* new consumer should call this rather than reading the legacy
|
|
25
|
+
* `PREP_AUTOLOOP_INTRO` constant below.
|
|
26
|
+
*/
|
|
27
|
+
export function buildGroomingIntro(suite, customOverride) {
|
|
28
|
+
return getGroomingIntro(suite, customOverride);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Back-compat alias resolving to the `superpowers` variant (the historical
|
|
32
|
+
* inlined text). Kept so any existing import still works without a
|
|
33
|
+
* behaviour change. New code should call `buildGroomingIntro(suite, override)`
|
|
34
|
+
* with live settings so the user's `skillSuite` choice is honoured.
|
|
35
|
+
*/
|
|
36
|
+
export const PREP_AUTOLOOP_INTRO = getGroomingIntro('superpowers');
|
|
20
37
|
export function buildAutoLoopGroomingSteps(e2e, finalization) {
|
|
21
38
|
const steps = [
|
|
22
39
|
`1. Call \`kobo__list_tasks\` FIRST to inspect any pre-existing tasks (they may have been seeded from Notion, a template, or the CreatePage form).`,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex model catalogue — kept in sync with the official roster published at
|
|
3
|
+
* developers.openai.com/codex/models. The Codex CLI accepts arbitrary strings
|
|
4
|
+
* in `--model`, so power users can still pin a model not listed here by
|
|
5
|
+
* editing the workspace `model` field directly. This list reflects the
|
|
6
|
+
* recommended set surfaced in the create-workspace selector.
|
|
7
|
+
*
|
|
8
|
+
* Auth caveat: `gpt-5.5` is currently only reachable when authenticated via
|
|
9
|
+
* ChatGPT (Plus/Pro/Team/Enterprise). API-key auth is limited to `gpt-5.4`
|
|
10
|
+
* and below.
|
|
11
|
+
*/
|
|
12
|
+
export const CODEX_MODELS = [
|
|
13
|
+
{
|
|
14
|
+
id: 'auto',
|
|
15
|
+
label: 'Auto',
|
|
16
|
+
i18nLabelKey: 'model.auto',
|
|
17
|
+
i18nDescriptionKey: 'model.autoDescription',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'gpt-5.5',
|
|
21
|
+
label: 'GPT-5.5',
|
|
22
|
+
i18nLabelKey: 'model.gpt55',
|
|
23
|
+
i18nDescriptionKey: 'model.gpt55Description',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'gpt-5.4',
|
|
27
|
+
label: 'GPT-5.4',
|
|
28
|
+
i18nLabelKey: 'model.gpt54',
|
|
29
|
+
i18nDescriptionKey: 'model.gpt54Description',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'gpt-5.4-mini',
|
|
33
|
+
label: 'GPT-5.4 mini',
|
|
34
|
+
i18nLabelKey: 'model.gpt54mini',
|
|
35
|
+
i18nDescriptionKey: 'model.gpt54miniDescription',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'gpt-5.3-codex',
|
|
39
|
+
label: 'GPT-5.3 Codex',
|
|
40
|
+
i18nLabelKey: 'model.gpt53codex',
|
|
41
|
+
i18nDescriptionKey: 'model.gpt53codexDescription',
|
|
42
|
+
},
|
|
43
|
+
];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curated palette for per-project colours. Quasar colour keys at the `-5`
|
|
3
|
+
* intensity — picked to read well against Kōbō's dark theme. Append-only —
|
|
4
|
+
* existing values must never move or be removed (settings.json may reference
|
|
5
|
+
* them). New colours can be added at the end.
|
|
6
|
+
*/
|
|
7
|
+
export const PROJECT_COLOR_PALETTE = [
|
|
8
|
+
'red-5',
|
|
9
|
+
'pink-5',
|
|
10
|
+
'purple-5',
|
|
11
|
+
'deep-purple-5',
|
|
12
|
+
'indigo-5',
|
|
13
|
+
'blue-5',
|
|
14
|
+
'cyan-5',
|
|
15
|
+
'teal-5',
|
|
16
|
+
'green-5',
|
|
17
|
+
'amber-5',
|
|
18
|
+
'orange-5',
|
|
19
|
+
'brown-5',
|
|
20
|
+
];
|
|
21
|
+
export function isValidProjectColor(value) {
|
|
22
|
+
return typeof value === 'string' && PROJECT_COLOR_PALETTE.includes(value);
|
|
23
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/shared/skill-suite-prompts.ts
|
|
2
|
+
export function isValidSkillSuite(value) {
|
|
3
|
+
return value === 'superpowers' || value === 'gstack' || value === 'custom';
|
|
4
|
+
}
|
|
5
|
+
// ── Agnostic prompt strings ───────────────────────────────────────────────────
|
|
6
|
+
// These are the neutral, suite-free versions used both:
|
|
7
|
+
// 1. As the seed for `custom` mode (so the user has a sane base to edit).
|
|
8
|
+
// 2. As building blocks for the backend full prompt sets in
|
|
9
|
+
// `src/server/services/skill-suite-prompts.ts`.
|
|
10
|
+
//
|
|
11
|
+
// They MUST NOT mention any specific skill suite by name.
|
|
12
|
+
const REVIEW_HEADER = `You are reviewing code changes on workspace "{{workspace_name}}" in project {{project_name}}.
|
|
13
|
+
|
|
14
|
+
Branch: {{branch_name}} (base: {{source_branch}})
|
|
15
|
+
Base commit: {{base_commit}}
|
|
16
|
+
|
|
17
|
+
`;
|
|
18
|
+
const REVIEW_BODY = `## Scope
|
|
19
|
+
|
|
20
|
+
Review ALL changes — both committed and uncommitted in the working tree:
|
|
21
|
+
- \`git diff {{base_commit}}..HEAD\` — committed changes on this branch
|
|
22
|
+
- \`git status\` and \`git diff\` — uncommitted changes (staged + unstaged)
|
|
23
|
+
|
|
24
|
+
## Diff summary
|
|
25
|
+
{{diff_stats}}
|
|
26
|
+
|
|
27
|
+
## Commits
|
|
28
|
+
{{commits}}
|
|
29
|
+
|
|
30
|
+
## Additional instructions
|
|
31
|
+
{{additional_instructions}}
|
|
32
|
+
|
|
33
|
+
## Output
|
|
34
|
+
|
|
35
|
+
If no review skill is available, structure your reply as:
|
|
36
|
+
1. Summary — what changed and why
|
|
37
|
+
2. Issues — bugs, regressions, security or perf concerns (with file:line)
|
|
38
|
+
3. Suggestions — refactor / improvement opportunities
|
|
39
|
+
4. Tests — coverage gaps
|
|
40
|
+
5. Verdict — ship / fix-then-ship / blocked
|
|
41
|
+
`;
|
|
42
|
+
export const AGNOSTIC_REVIEW_TEMPLATE = REVIEW_HEADER +
|
|
43
|
+
'If a code-review skill is available in this environment, invoke it to drive this audit. Otherwise follow the manual checklist below.\n\n' +
|
|
44
|
+
REVIEW_BODY;
|
|
45
|
+
export const AGNOSTIC_AUTO_LOOP_REVIEW_GATE = 'Code review gate — BEFORE marking the task done, run whichever code-review skill is configured in this environment. Brief it with: what you just implemented, the task title, and the commit SHA (via `git rev-parse HEAD`). Ask specifically whether the change matches the task scope, whether edge cases are handled, and whether the commit is clean. If no review skill is available, do a manual self-review against the same criteria.';
|
|
46
|
+
export const AGNOSTIC_AUTO_LOOP_GROOMING_INTRO = 'You are preparing this workspace for Kōbō auto-loop mode. This is a GROOMING session only — DO NOT implement anything, DO NOT write or edit code, DO NOT run tests or builds, DO NOT invoke any implementation, planning, or release skill (your environment may have several). Your ONLY job is to curate the Kōbō task list via MCP tools.';
|
|
47
|
+
export const AGNOSTIC_QA_PROMPT_TEMPLATE = 'QA pass for workspace "{{workspace_name}}" in project {{project_name}}.\n\nBranch: {{branch_name}}\nStaging URL: {{staging_url}}\n\nIf a QA skill is configured in this environment, invoke it on {{staging_url}}. Otherwise, manually exercise the staging URL against the workspace\'s acceptance criteria and report findings.';
|
|
48
|
+
// ── Grooming intro: superpowers + gstack variants ────────────────────────────
|
|
49
|
+
// Only the grooming intro is needed shared (both backend auto-loop-prompts and
|
|
50
|
+
// frontend kobo-commands use it). The review/review-gate/QA variants are
|
|
51
|
+
// backend-only — they live in src/server/services/skill-suite-prompts.ts.
|
|
52
|
+
export const GROOMING_INTRO_SUPERPOWERS = 'You are preparing this workspace for Kōbō auto-loop mode. This is a GROOMING session only — DO NOT implement anything, DO NOT write or edit code, DO NOT run tests or builds, DO NOT invoke `superpowers:executing-plans` or any implementation skill. Your ONLY job is to curate the Kōbō task list via MCP tools.';
|
|
53
|
+
export const GROOMING_INTRO_GSTACK = 'You are preparing this workspace for Kōbō auto-loop mode. This is a GROOMING session only — DO NOT implement anything, DO NOT write or edit code, DO NOT run tests or builds, DO NOT invoke `/ship`, `/autoplan`, `/land-and-deploy`, or any implementation skill. Your ONLY job is to curate the Kōbō task list via MCP tools.';
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the grooming intro for the given suite. In `custom` mode, the
|
|
56
|
+
* user-provided override is used (or AGNOSTIC if the override is empty/blank).
|
|
57
|
+
*/
|
|
58
|
+
export function getGroomingIntro(suite, customOverride) {
|
|
59
|
+
if (suite === 'custom') {
|
|
60
|
+
const trimmed = (customOverride ?? '').trim();
|
|
61
|
+
return trimmed || AGNOSTIC_AUTO_LOOP_GROOMING_INTRO;
|
|
62
|
+
}
|
|
63
|
+
if (suite === 'gstack')
|
|
64
|
+
return GROOMING_INTRO_GSTACK;
|
|
65
|
+
return GROOMING_INTRO_SUPERPOWERS;
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.9",
|
|
4
4
|
"description": "Kōbō — multi-workspace agent manager for Claude Code. Orchestrates isolated git worktrees with dev servers, Notion integration, and MCP tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"@emnapi/runtime": "^1.10.0",
|
|
72
72
|
"@hono/node-server": "^2.0.2",
|
|
73
73
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
74
|
+
"@openai/codex": "^0.130.0",
|
|
74
75
|
"better-sqlite3": "^12.9.0",
|
|
75
76
|
"cron-parser": "^5.5.0",
|
|
76
77
|
"hono": "^4.12.18",
|