@aopslabs/aops 0.2.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/LICENSE +19 -0
- package/NOTICE +7 -0
- package/README.md +203 -0
- package/assets/agent-assets/core/SKILL.md +49 -0
- package/assets/agent-assets/core/references/agentspace/SKILL.md +78 -0
- package/assets/agent-assets/core/references/aops-cli-core/SKILL.md +108 -0
- package/assets/agent-assets/core/references/chatv3/SKILL.md +48 -0
- package/assets/agent-assets/core/references/collaborative-work/SKILL.md +85 -0
- package/assets/agent-assets/core/references/discuss/SKILL.md +66 -0
- package/assets/agent-assets/core/references/docman/SKILL.md +69 -0
- package/assets/agent-assets/core/references/hosted-chat/SKILL.md +64 -0
- package/assets/agent-assets/core/references/mission/SKILL.md +54 -0
- package/assets/agent-assets/core/references/projectman/SKILL.md +86 -0
- package/assets/agent-assets/core/references/sys/SKILL.md +36 -0
- package/assets/agent-assets/core/references/view/SKILL.md +58 -0
- package/assets/agent-assets/core/references/working-disciplines/SKILL.md +69 -0
- package/assets/agent-assets/core/user-guides/agent-assets.md +81 -0
- package/assets/agent-assets/core/user-guides/agentspace.md +1098 -0
- package/assets/agent-assets/core/user-guides/aops-cli.md +1107 -0
- package/assets/agent-assets/core/user-guides/aops-system.md +63 -0
- package/assets/agent-assets/core/user-guides/chatv3.md +113 -0
- package/assets/agent-assets/core/user-guides/docman.md +162 -0
- package/assets/agent-assets/core/user-guides/projectman.md +212 -0
- package/assets/agent-assets/core/user-guides/sys.md +94 -0
- package/assets/agent-assets/core/user-guides/working-disciplines.md +588 -0
- package/assets/agent-assets/gateway/aops/SKILL.md +14 -0
- package/assets/r6-remote-session-baseline.json +19 -0
- package/assets/skills/aops-install/SKILL.md +231 -0
- package/assets/skills/aops-install/agents/openai.yaml +4 -0
- package/dist/commands/activity.js +352 -0
- package/dist/commands/agent-profile.js +653 -0
- package/dist/commands/agent.js +710 -0
- package/dist/commands/agents-md.js +125 -0
- package/dist/commands/api.js +84 -0
- package/dist/commands/archive.js +873 -0
- package/dist/commands/artifact.js +535 -0
- package/dist/commands/assets.js +1414 -0
- package/dist/commands/auth/device-login.js +1 -0
- package/dist/commands/auth/legacy-export.js +177 -0
- package/dist/commands/auth/login.js +426 -0
- package/dist/commands/chat.js +1363 -0
- package/dist/commands/chatv3.js +1684 -0
- package/dist/commands/checkpoint.js +549 -0
- package/dist/commands/commercial-artifact.js +475 -0
- package/dist/commands/commercial-license.js +594 -0
- package/dist/commands/community-auth.js +62 -0
- package/dist/commands/community-cockpit.js +185 -0
- package/dist/commands/community-console.js +109 -0
- package/dist/commands/community-doctor.js +335 -0
- package/dist/commands/community-server.js +2062 -0
- package/dist/commands/community-setup.js +284 -0
- package/dist/commands/discuss.js +1448 -0
- package/dist/commands/doc.js +5219 -0
- package/dist/commands/experience.js +922 -0
- package/dist/commands/host.js +1127 -0
- package/dist/commands/init.js +118 -0
- package/dist/commands/memory.js +3129 -0
- package/dist/commands/mission.js +562 -0
- package/dist/commands/official-catalog.js +134 -0
- package/dist/commands/plan.js +84 -0
- package/dist/commands/playbook.js +415 -0
- package/dist/commands/pm/context.js +6 -0
- package/dist/commands/pm/index.js +701 -0
- package/dist/commands/pm/projectman.js +6266 -0
- package/dist/commands/project.js +1080 -0
- package/dist/commands/prompt.js +1024 -0
- package/dist/commands/repo-sync.js +1907 -0
- package/dist/commands/resource.js +527 -0
- package/dist/commands/skill.js +1211 -0
- package/dist/commands/start-disciplines.js +510 -0
- package/dist/commands/start.js +1343 -0
- package/dist/commands/target.js +309 -0
- package/dist/commands/version.js +42 -0
- package/dist/commands/view.js +1852 -0
- package/dist/launchers/cockpit.js +28 -0
- package/dist/launchers/options.js +74 -0
- package/dist/launchers/server.js +41 -0
- package/dist/lib/agent-assets/envelope.js +83 -0
- package/dist/lib/agent-assets/gateway.js +15 -0
- package/dist/lib/agent-assets/guards.js +23 -0
- package/dist/lib/agent-assets/hosted-discovery.js +148 -0
- package/dist/lib/agent-assets/hosted-package-input.js +154 -0
- package/dist/lib/agent-assets/legacy-pointer-migration.js +677 -0
- package/dist/lib/agent-assets/native-fs.js +589 -0
- package/dist/lib/agent-assets/package-manifest.js +324 -0
- package/dist/lib/agent-assets/portable-path.js +67 -0
- package/dist/lib/agent-assets/release-input.js +177 -0
- package/dist/lib/agent-assets/roots.js +45 -0
- package/dist/lib/agent-assets/runtime-binding-reader.js +545 -0
- package/dist/lib/agent-assets/runtime-targets.js +50 -0
- package/dist/lib/agent-assets/store-reader.js +1212 -0
- package/dist/lib/agent-assets/store-types.js +1 -0
- package/dist/lib/agent-assets/store-writer.js +1484 -0
- package/dist/lib/agent-assets/types.js +1 -0
- package/dist/lib/agent-assets/unicode-case-folding-v15-1.generated.js +1548 -0
- package/dist/lib/commercial-final-readiness.js +504 -0
- package/dist/lib/commercial-lifecycle-smoke-isolation.js +236 -0
- package/dist/lib/commercial-managed-postgres-docker-adapter.js +274 -0
- package/dist/lib/commercial-setup-readiness-adapter.js +179 -0
- package/dist/lib/community-client-contract.js +164 -0
- package/dist/lib/community-cockpit-lifecycle.js +601 -0
- package/dist/lib/community-command-abort.js +59 -0
- package/dist/lib/community-diagnostic.js +237 -0
- package/dist/lib/community-docker-adapter.js +498 -0
- package/dist/lib/community-home.js +31 -0
- package/dist/lib/community-instance-contract.js +148 -0
- package/dist/lib/community-instance-layout.js +97 -0
- package/dist/lib/community-lifecycle.js +1631 -0
- package/dist/lib/community-migration-snapshot.js +770 -0
- package/dist/lib/community-native-application-recovery.js +425 -0
- package/dist/lib/community-native-child.js +240 -0
- package/dist/lib/community-native-database-recovery.js +456 -0
- package/dist/lib/community-native-lifecycle.js +2458 -0
- package/dist/lib/community-native-migration.js +339 -0
- package/dist/lib/community-native-postgres.js +596 -0
- package/dist/lib/community-operation-journal.js +773 -0
- package/dist/lib/community-operation-lock.js +596 -0
- package/dist/lib/community-release-resolver.js +291 -0
- package/dist/lib/community-release-verifier.js +475 -0
- package/dist/lib/community-repo-discovery.js +252 -0
- package/dist/lib/community-setup-server-env.js +203 -0
- package/dist/lib/external-url.js +110 -0
- package/dist/lib/official-catalog-gateway.js +108 -0
- package/dist/lib/official-catalog.js +364 -0
- package/dist/lib/server-bootstrap.js +240 -0
- package/dist/lib/setup-agent-assets-bridge.js +176 -0
- package/dist/lib/setup-agent-assets-release.js +52 -0
- package/dist/lib/setup-external-postgres.js +108 -0
- package/dist/lib/setup-init-orchestrator.js +990 -0
- package/dist/lib/setup-install-guide.js +52 -0
- package/dist/lib/setup-installer-release.js +47 -0
- package/dist/lib/setup-local-postgres.js +329 -0
- package/dist/lib/setup-official-catalog-bridge.js +148 -0
- package/dist/lib/setup-readiness.js +888 -0
- package/dist/lib/tui-launcher.js +59 -0
- package/dist/main.js +150 -0
- package/dist/seeds/agents-md.js +54 -0
- package/dist/utils/agent-gateway.js +275 -0
- package/dist/utils/agents-md.js +281 -0
- package/dist/utils/api.js +255 -0
- package/dist/utils/chatv3-session-store.js +185 -0
- package/dist/utils/command.js +67 -0
- package/dist/utils/community-host-runtime.js +376 -0
- package/dist/utils/config.js +1015 -0
- package/dist/utils/discussion-workspace.js +552 -0
- package/dist/utils/experience-workspace.js +326 -0
- package/dist/utils/guide-paths.js +67 -0
- package/dist/utils/hosted-sugar.js +56 -0
- package/dist/utils/hosted-workspace.js +229 -0
- package/dist/utils/memory-workspace.js +537 -0
- package/dist/utils/operator-runtime.js +199 -0
- package/dist/utils/password-crypto.js +39 -0
- package/dist/utils/playbook-workspace.js +479 -0
- package/dist/utils/project-context.js +255 -0
- package/dist/utils/prompts.js +70 -0
- package/dist/utils/refresh-race.js +28 -0
- package/dist/utils/repo-config.js +109 -0
- package/dist/utils/repo-first-projectman.js +194 -0
- package/dist/utils/repo-first-storage.js +13 -0
- package/dist/utils/repo-first-view.js +305 -0
- package/dist/utils/session-guidance.js +278 -0
- package/dist/utils/session-state.js +137 -0
- package/dist/utils/table.js +39 -0
- package/dist/utils/transient-fs.js +49 -0
- package/dist/utils/workspace-root.js +1 -0
- package/launchers/aops-cockpit.ps1 +15 -0
- package/launchers/aops-cockpit.sh +16 -0
- package/launchers/aops-server.ps1 +15 -0
- package/launchers/aops-server.sh +16 -0
- package/native/tui/win32-x64/aops-tui.exe +0 -0
- package/package.json +81 -0
|
@@ -0,0 +1,1343 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { logInfo, logSuccess, logWarn } from '@aopslabs/xf-cli-ui';
|
|
6
|
+
import { promptInput, promptSelect } from '../utils/prompts.js';
|
|
7
|
+
import { loadAopsRepoConfigReadOnly } from '../utils/repo-config.js';
|
|
8
|
+
import { resolveRepoFirstProjectmanPaths } from '../utils/repo-first-projectman.js';
|
|
9
|
+
import { readSessionStateNudges } from '../utils/session-state.js';
|
|
10
|
+
import { buildReadOnlyActivePlaybookNudgePackFromContext, } from '../utils/playbook-workspace.js';
|
|
11
|
+
import { buildSessionGuidancePack } from '../utils/session-guidance.js';
|
|
12
|
+
import { buildReadOnlyRepoFirstMemoryBriefFromContext } from './memory.js';
|
|
13
|
+
import { applyCommonOptions, compactPayload } from '../utils/command.js';
|
|
14
|
+
import { loadMissionResumePack } from './mission.js';
|
|
15
|
+
import { START_DISCIPLINES, START_DISCIPLINE_PROFILES, START_DISCIPLINE_SIGNAL_MAPPING, startGuardrailGroupsForDiscipline, startGuardrailsForDiscipline, } from './start-disciplines.js';
|
|
16
|
+
export const START_PROMPT_MIRROR_PATH = '.aops/hosted/prompts/aops-collaborative-startup.md';
|
|
17
|
+
export const START_MODES = ['solo', 'solo+async-review', 'chat-room'];
|
|
18
|
+
export const START_METHODS = START_DISCIPLINES;
|
|
19
|
+
const MULTI_AGENT_MODES = new Set(['chat-room']);
|
|
20
|
+
const REMOVED_MODES = {
|
|
21
|
+
'full-collab': 'full-collab modu kaldırıldı. Çok-agent canlı akış için --mode chat-room kullanın ' +
|
|
22
|
+
'(hosted chat = koordinasyon/wake, PM review-request = review hakikati, discuss = konsensus).',
|
|
23
|
+
};
|
|
24
|
+
function assertSupportedMode(mode) {
|
|
25
|
+
if (mode === undefined)
|
|
26
|
+
return;
|
|
27
|
+
const removed = REMOVED_MODES[mode];
|
|
28
|
+
if (removed)
|
|
29
|
+
throw new Error(removed);
|
|
30
|
+
if (!START_MODES.includes(mode)) {
|
|
31
|
+
throw new Error(`Unsupported --mode "${mode}". Use one of: ${START_MODES.join(' | ')}.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function assertSupportedDiscipline(value, flag = '--discipline') {
|
|
35
|
+
if (value === undefined)
|
|
36
|
+
return;
|
|
37
|
+
if (!START_DISCIPLINES.includes(value)) {
|
|
38
|
+
throw new Error(`Unsupported ${flag} "${value}". Use one of: ${START_DISCIPLINES.join(' | ')}.`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function assertCompatibleDisciplineAliases(answers) {
|
|
42
|
+
const discipline = normalize(answers.discipline);
|
|
43
|
+
const method = normalize(answers.method);
|
|
44
|
+
assertSupportedDiscipline(discipline, '--discipline');
|
|
45
|
+
assertSupportedDiscipline(method, '--method');
|
|
46
|
+
if (discipline && method && discipline !== method) {
|
|
47
|
+
throw new Error(`Conflicting --discipline "${discipline}" and --method "${method}". --method is a compatibility alias for --discipline.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function normalize(value) {
|
|
51
|
+
if (typeof value !== 'string')
|
|
52
|
+
return undefined;
|
|
53
|
+
const trimmed = value.trim();
|
|
54
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
55
|
+
}
|
|
56
|
+
function booleanFlag(value) {
|
|
57
|
+
if (value === true)
|
|
58
|
+
return true;
|
|
59
|
+
const normalized = normalize(value)?.toLowerCase();
|
|
60
|
+
return normalized === 'true' || normalized === 'yes' || normalized === '1';
|
|
61
|
+
}
|
|
62
|
+
function parsePositiveInteger(value) {
|
|
63
|
+
const normalized = normalize(value);
|
|
64
|
+
if (!normalized)
|
|
65
|
+
return undefined;
|
|
66
|
+
const parsed = Number.parseInt(normalized, 10);
|
|
67
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
68
|
+
}
|
|
69
|
+
function normalizeStartDiscipline(value) {
|
|
70
|
+
const normalized = normalize(value);
|
|
71
|
+
if (!normalized)
|
|
72
|
+
return undefined;
|
|
73
|
+
assertSupportedDiscipline(normalized);
|
|
74
|
+
return normalized;
|
|
75
|
+
}
|
|
76
|
+
function resolveExplicitDiscipline(answers) {
|
|
77
|
+
assertCompatibleDisciplineAliases(answers);
|
|
78
|
+
const discipline = normalizeStartDiscipline(answers.discipline);
|
|
79
|
+
if (discipline)
|
|
80
|
+
return { discipline, source: 'discipline' };
|
|
81
|
+
const method = normalizeStartDiscipline(answers.method);
|
|
82
|
+
if (method)
|
|
83
|
+
return { discipline: method, source: 'method' };
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
function normalizeWorkSize(value) {
|
|
87
|
+
const normalized = normalize(value)?.toLowerCase();
|
|
88
|
+
if (normalized === 'small' || normalized === 'medium' || normalized === 'large')
|
|
89
|
+
return normalized;
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
function normalizeDecisionUncertainty(value) {
|
|
93
|
+
const normalized = normalize(value)?.toLowerCase();
|
|
94
|
+
if (normalized === 'low' || normalized === 'medium' || normalized === 'high')
|
|
95
|
+
return normalized;
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
function normalizeOperatorInterface(value) {
|
|
99
|
+
const normalized = normalize(value)?.toLowerCase();
|
|
100
|
+
if (normalized === 'direct' || normalized === 'delegated')
|
|
101
|
+
return normalized;
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const START_METHOD_RECIPES = {
|
|
105
|
+
'solo-pm-loop': {
|
|
106
|
+
title: 'Solo PM loop',
|
|
107
|
+
steps: [
|
|
108
|
+
'Open or reuse a Projectman kanban task and sprint-backed implementation plan.',
|
|
109
|
+
'Implement in small microtasks.',
|
|
110
|
+
'Validate, request review when risk warrants it, then write a short PM handoff.',
|
|
111
|
+
],
|
|
112
|
+
defaultSurfaces: ['projectman.implementation-plan', 'projectman.review-request'],
|
|
113
|
+
},
|
|
114
|
+
'build-review-chat': {
|
|
115
|
+
title: 'Build + review over chat',
|
|
116
|
+
steps: [
|
|
117
|
+
'Keep chat as coordination and wake surface only.',
|
|
118
|
+
'Store execution truth in Projectman implementation-plan and review-request records.',
|
|
119
|
+
'Post review refs into the room, wait for review, then commit explicitly.',
|
|
120
|
+
],
|
|
121
|
+
defaultSurfaces: ['chatv3.room', 'projectman.implementation-plan', 'projectman.review-request'],
|
|
122
|
+
},
|
|
123
|
+
'design-first-consensus': {
|
|
124
|
+
title: 'Design-first consensus',
|
|
125
|
+
steps: [
|
|
126
|
+
'Run a discuss topic before implementation when material design uncertainty is high.',
|
|
127
|
+
'Carry the consensus ref into the implementation plan.',
|
|
128
|
+
'Implement only after final stances and review scope are clear.',
|
|
129
|
+
],
|
|
130
|
+
defaultSurfaces: ['agentspace.discuss', 'projectman.implementation-plan', 'projectman.review-request'],
|
|
131
|
+
},
|
|
132
|
+
'coordinator-loop': {
|
|
133
|
+
title: 'Coordinator loop',
|
|
134
|
+
steps: [
|
|
135
|
+
'The coordinator researches independently, authors mission/ktask/plan records, and assigns work with canonical refs.',
|
|
136
|
+
'Implementers execute slices, open a review request per slice, and route questions to the coordinator instead of the operator.',
|
|
137
|
+
'The coordinator reviews with code/runtime verification, then instructs the pathspec commit and the next assignment.',
|
|
138
|
+
],
|
|
139
|
+
defaultSurfaces: ['agentspace.mission', 'chatv3.room', 'projectman.implementation-plan', 'projectman.review-request'],
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
function serializeStartGuardrail(guardrail) {
|
|
143
|
+
return {
|
|
144
|
+
id: guardrail.id,
|
|
145
|
+
title: guardrail.title,
|
|
146
|
+
description: guardrail.description,
|
|
147
|
+
phase: guardrail.phase,
|
|
148
|
+
evidence: guardrail.evidence,
|
|
149
|
+
enforcementLevel: guardrail.enforcementLevel,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function quoteCommandArg(value) {
|
|
153
|
+
return JSON.stringify(value);
|
|
154
|
+
}
|
|
155
|
+
function buildCheckpointCreateCommand(params) {
|
|
156
|
+
const parts = [
|
|
157
|
+
'aops-cli checkpoint create',
|
|
158
|
+
'--summary',
|
|
159
|
+
quoteCommandArg(params.summary),
|
|
160
|
+
];
|
|
161
|
+
if (params.position)
|
|
162
|
+
parts.push('--position', quoteCommandArg(params.position));
|
|
163
|
+
if (params.missionId)
|
|
164
|
+
parts.push('--mission-id', params.missionId);
|
|
165
|
+
if (params.sprintId)
|
|
166
|
+
parts.push('--sprint-id', params.sprintId);
|
|
167
|
+
if (params.phaseId)
|
|
168
|
+
parts.push('--phase-id', params.phaseId);
|
|
169
|
+
parts.push('--apply', '--json');
|
|
170
|
+
return parts.join(' ');
|
|
171
|
+
}
|
|
172
|
+
function buildScopedSyncCommand(params) {
|
|
173
|
+
const parts = ['aops-cli', 'sync', params.action];
|
|
174
|
+
if (params.projectSlug)
|
|
175
|
+
parts.push('--project-slug', params.projectSlug);
|
|
176
|
+
parts.push('--json');
|
|
177
|
+
return parts.join(' ');
|
|
178
|
+
}
|
|
179
|
+
function collectStartSignals(answers) {
|
|
180
|
+
return compactPayload({
|
|
181
|
+
mode: normalize(answers.mode),
|
|
182
|
+
workSize: normalizeWorkSize(answers.workSize),
|
|
183
|
+
agentCount: parsePositiveInteger(answers.agentCount),
|
|
184
|
+
decisionUncertainty: normalizeDecisionUncertainty(answers.decisionUncertainty),
|
|
185
|
+
operatorInterface: normalizeOperatorInterface(answers.operatorInterface),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function recommendStartDiscipline(answers) {
|
|
189
|
+
const reasons = [];
|
|
190
|
+
const signals = collectStartSignals(answers);
|
|
191
|
+
const decisionUncertainty = normalizeDecisionUncertainty(answers.decisionUncertainty);
|
|
192
|
+
const workSize = normalizeWorkSize(answers.workSize);
|
|
193
|
+
const agentCount = parsePositiveInteger(answers.agentCount);
|
|
194
|
+
const mode = normalize(answers.mode);
|
|
195
|
+
const operatorInterface = normalizeOperatorInterface(answers.operatorInterface);
|
|
196
|
+
if (decisionUncertainty === 'high') {
|
|
197
|
+
reasons.push('decision uncertainty is high');
|
|
198
|
+
return { discipline: 'design-first-consensus', reasons, signals };
|
|
199
|
+
}
|
|
200
|
+
if (operatorInterface === 'delegated') {
|
|
201
|
+
reasons.push('operator delegates the session to a coordinator agent');
|
|
202
|
+
return { discipline: 'coordinator-loop', reasons, signals };
|
|
203
|
+
}
|
|
204
|
+
if (mode === 'chat-room' || (agentCount !== undefined && agentCount > 1)) {
|
|
205
|
+
if (mode === 'chat-room')
|
|
206
|
+
reasons.push('chat-room mode selected');
|
|
207
|
+
if (agentCount !== undefined && agentCount > 1)
|
|
208
|
+
reasons.push(`agent count is ${agentCount}`);
|
|
209
|
+
return { discipline: 'build-review-chat', reasons, signals };
|
|
210
|
+
}
|
|
211
|
+
if (workSize === 'large' && decisionUncertainty === 'medium') {
|
|
212
|
+
reasons.push('large work with medium decision uncertainty');
|
|
213
|
+
return { discipline: 'design-first-consensus', reasons, signals };
|
|
214
|
+
}
|
|
215
|
+
reasons.push('single-agent or low-uncertainty execution');
|
|
216
|
+
return { discipline: 'solo-pm-loop', reasons, signals };
|
|
217
|
+
}
|
|
218
|
+
function buildMissionPolicySeed(params) {
|
|
219
|
+
const profile = START_DISCIPLINE_PROFILES[params.selected];
|
|
220
|
+
const guardrails = startGuardrailsForDiscipline(params.selected);
|
|
221
|
+
const guardrailGroups = startGuardrailGroupsForDiscipline(params.selected);
|
|
222
|
+
return compactPayload({
|
|
223
|
+
discipline: {
|
|
224
|
+
id: params.selected,
|
|
225
|
+
version: profile.version,
|
|
226
|
+
enforcement: 'advisory',
|
|
227
|
+
selectedBy: params.explicit ? 'operator' : 'signals',
|
|
228
|
+
selectedVia: params.explicitSource ? `--${params.explicitSource}` : 'signal-mapping',
|
|
229
|
+
recommended: params.recommended,
|
|
230
|
+
signals: params.signals,
|
|
231
|
+
rationale: params.reasons,
|
|
232
|
+
},
|
|
233
|
+
guardrails: guardrails.map(serializeStartGuardrail),
|
|
234
|
+
guardrailGroups: {
|
|
235
|
+
execution: guardrailGroups.execution.map(serializeStartGuardrail),
|
|
236
|
+
closeout: guardrailGroups.closeout.map(serializeStartGuardrail),
|
|
237
|
+
},
|
|
238
|
+
closeout: {
|
|
239
|
+
required: true,
|
|
240
|
+
trigger: 'explicit',
|
|
241
|
+
guardrailIds: profile.closeoutGuardrailIds,
|
|
242
|
+
check: {
|
|
243
|
+
command: 'aops-cli mission check --closeout --id <mission-id> --json',
|
|
244
|
+
mode: 'read-only',
|
|
245
|
+
statusStates: ['present', 'missing', 'deferred-with-owner', 'not-applicable', 'waived-by-operator'],
|
|
246
|
+
note: 'Later closeout validator; reports evidence for closeout guardrails and does not mutate PM, memory, git, boards, or chat rooms.',
|
|
247
|
+
},
|
|
248
|
+
handoff: {
|
|
249
|
+
command: 'aops-cli mission handoff --id <mission-id> [--complete] --apply --json',
|
|
250
|
+
mode: 'later-apply-helper',
|
|
251
|
+
note: 'Conservative mission-level helper only; board and room closeout remain operator-approved lifecycle actions.',
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
review: profile.review,
|
|
255
|
+
issue: profile.issue,
|
|
256
|
+
memory: profile.memory,
|
|
257
|
+
plan: profile.plan,
|
|
258
|
+
vocabBridge: {
|
|
259
|
+
mode: {
|
|
260
|
+
value: params.mode,
|
|
261
|
+
meaning: 'transport/session shape',
|
|
262
|
+
},
|
|
263
|
+
discipline: {
|
|
264
|
+
value: params.selected,
|
|
265
|
+
meaning: 'policy preset plus guardrails, review, issue, memory, and plan rhythm',
|
|
266
|
+
},
|
|
267
|
+
method: {
|
|
268
|
+
value: params.selected,
|
|
269
|
+
aliasOf: 'discipline',
|
|
270
|
+
compatibility: 'kept for existing start consumers during the transition',
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
signalMapping: START_DISCIPLINE_SIGNAL_MAPPING,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
function buildStartMissionPack(answers, params) {
|
|
277
|
+
const recommended = recommendStartDiscipline(answers);
|
|
278
|
+
const explicit = resolveExplicitDiscipline(answers);
|
|
279
|
+
const selected = explicit.discipline ?? recommended.discipline;
|
|
280
|
+
const resumeRef = normalize(answers.resume);
|
|
281
|
+
const missionRef = resumeRef ?? normalize(answers.mission);
|
|
282
|
+
const objective = normalize(answers.objective) ?? normalize(answers.task);
|
|
283
|
+
const planId = normalize(answers.plan);
|
|
284
|
+
const mode = normalize(answers.mode) ?? 'solo';
|
|
285
|
+
const board = normalize(answers.board);
|
|
286
|
+
const projectSlug = normalize(answers.projectSlug) ?? params.projectSlug;
|
|
287
|
+
const roomSlug = normalize(answers.roomSlug);
|
|
288
|
+
const checkpointStart = booleanFlag(answers.checkpointStart);
|
|
289
|
+
const checkpointAutoBetweenPhases = booleanFlag(answers.checkpointAutoBetweenPhases);
|
|
290
|
+
const checkpointMissionId = missionRef ?? '<mission-id>';
|
|
291
|
+
const checkpointStartCommand = buildCheckpointCreateCommand({
|
|
292
|
+
summary: objective ? `Mission start: ${objective}` : 'Mission start checkpoint',
|
|
293
|
+
position: planId ? `Starting implementation plan ${planId}` : 'Mission start',
|
|
294
|
+
missionId: checkpointMissionId,
|
|
295
|
+
sprintId: planId,
|
|
296
|
+
});
|
|
297
|
+
const checkpointPhaseTransitionCommand = buildCheckpointCreateCommand({
|
|
298
|
+
summary: '<phase transition checkpoint summary>',
|
|
299
|
+
position: '<from phase> -> <to phase>',
|
|
300
|
+
missionId: checkpointMissionId,
|
|
301
|
+
sprintId: planId ?? '<sprint-id>',
|
|
302
|
+
phaseId: '<phase-id>',
|
|
303
|
+
});
|
|
304
|
+
const checkpointSuggestedActions = [
|
|
305
|
+
checkpointStart
|
|
306
|
+
? {
|
|
307
|
+
kind: 'mission-start-checkpoint',
|
|
308
|
+
policyPath: 'mission.policy.checkpoint.start',
|
|
309
|
+
mode: 'nudge-first',
|
|
310
|
+
applyRequired: true,
|
|
311
|
+
command: checkpointStartCommand,
|
|
312
|
+
}
|
|
313
|
+
: undefined,
|
|
314
|
+
checkpointAutoBetweenPhases
|
|
315
|
+
? {
|
|
316
|
+
kind: 'phase-transition-checkpoint',
|
|
317
|
+
policyPath: 'mission.policy.checkpoint.autoBetweenPhases',
|
|
318
|
+
mode: 'nudge-first',
|
|
319
|
+
applyRequired: true,
|
|
320
|
+
command: checkpointPhaseTransitionCommand,
|
|
321
|
+
}
|
|
322
|
+
: undefined,
|
|
323
|
+
].filter((action) => action !== undefined);
|
|
324
|
+
const syncStatusCommand = buildScopedSyncCommand({ action: 'status', projectSlug });
|
|
325
|
+
const syncDiffCommand = buildScopedSyncCommand({ action: 'diff', projectSlug });
|
|
326
|
+
const syncSuggestedActions = [
|
|
327
|
+
{
|
|
328
|
+
kind: 'cache-sync-status',
|
|
329
|
+
policyPath: 'mission.policy.sync.startStatus',
|
|
330
|
+
mode: 'read-only',
|
|
331
|
+
applyRequired: false,
|
|
332
|
+
command: syncStatusCommand,
|
|
333
|
+
note: 'Run at session start to inspect the read-only local cache vs the hosted server.',
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
kind: 'cache-sync-diff',
|
|
337
|
+
policyPath: 'mission.policy.sync.startDiff',
|
|
338
|
+
mode: 'read-only',
|
|
339
|
+
applyRequired: false,
|
|
340
|
+
command: syncDiffCommand,
|
|
341
|
+
note: 'Run at session start to inspect cache-vs-hosted drift; the hosted server is the source of truth.',
|
|
342
|
+
},
|
|
343
|
+
];
|
|
344
|
+
const playbookSet = params.playbookSet;
|
|
345
|
+
const playbookNudges = params.playbookNudges;
|
|
346
|
+
const playbookSuggestedActions = playbookNudges?.suggestedActions ?? playbookSet?.suggestedActions ?? [];
|
|
347
|
+
const deferredBindings = [
|
|
348
|
+
planId
|
|
349
|
+
? {
|
|
350
|
+
surface: 'projectman.implementation-plan',
|
|
351
|
+
status: 'deferred',
|
|
352
|
+
applyRequired: true,
|
|
353
|
+
command: `aops-cli plan update --id ${planId} --reference <ref> --apply --json`,
|
|
354
|
+
}
|
|
355
|
+
: {
|
|
356
|
+
surface: 'projectman.implementation-plan',
|
|
357
|
+
status: 'deferred',
|
|
358
|
+
applyRequired: true,
|
|
359
|
+
command: 'aops-cli plan create --task <task-id> --name "<name>" --goal "<goal>" --apply --json',
|
|
360
|
+
},
|
|
361
|
+
selected === 'build-review-chat' || mode === 'chat-room'
|
|
362
|
+
? {
|
|
363
|
+
surface: 'projectman.review-request',
|
|
364
|
+
status: 'deferred',
|
|
365
|
+
applyRequired: true,
|
|
366
|
+
command: 'aops-cli pm review-request create --review-scope sprint:<plan-id> --target-agent <reviewer> --apply --json',
|
|
367
|
+
}
|
|
368
|
+
: undefined,
|
|
369
|
+
selected === 'design-first-consensus'
|
|
370
|
+
? {
|
|
371
|
+
surface: 'agentspace.discuss',
|
|
372
|
+
status: 'deferred',
|
|
373
|
+
applyRequired: true,
|
|
374
|
+
command: 'aops-cli discuss start --title "<decision>" --question "<question>" --apply --json',
|
|
375
|
+
}
|
|
376
|
+
: undefined,
|
|
377
|
+
...checkpointSuggestedActions.map((action) => ({
|
|
378
|
+
surface: 'agentspace.memory-item.checkpoint',
|
|
379
|
+
status: 'suggested',
|
|
380
|
+
applyRequired: true,
|
|
381
|
+
policyPath: action.policyPath,
|
|
382
|
+
command: action.command,
|
|
383
|
+
})),
|
|
384
|
+
].filter((binding) => binding !== undefined);
|
|
385
|
+
const state = missionRef ? 'resume' : objective ? 'define' : 'implicit';
|
|
386
|
+
const mission = compactPayload({
|
|
387
|
+
ref: missionRef,
|
|
388
|
+
objective,
|
|
389
|
+
projectSlug,
|
|
390
|
+
});
|
|
391
|
+
const selectionReasons = explicit.discipline
|
|
392
|
+
? [`explicit ${explicit.source === 'method' ? 'method alias' : 'discipline'} flag supplied`, ...recommended.reasons]
|
|
393
|
+
: recommended.reasons;
|
|
394
|
+
const policyBase = buildMissionPolicySeed({
|
|
395
|
+
selected,
|
|
396
|
+
recommended: recommended.discipline,
|
|
397
|
+
explicit: explicit.discipline !== undefined,
|
|
398
|
+
explicitSource: explicit.source,
|
|
399
|
+
reasons: selectionReasons,
|
|
400
|
+
signals: recommended.signals,
|
|
401
|
+
mode,
|
|
402
|
+
});
|
|
403
|
+
const policy = compactPayload({
|
|
404
|
+
...policyBase,
|
|
405
|
+
orchestration: {
|
|
406
|
+
mode: 'read-only-pack',
|
|
407
|
+
sideEffects: 'none',
|
|
408
|
+
activeBindings: 'deferred',
|
|
409
|
+
note: 'start recommends PM/chat/discuss surfaces but does not create or bind records; run deferred commands with --apply to mutate truth surfaces.',
|
|
410
|
+
},
|
|
411
|
+
checkpoint: {
|
|
412
|
+
start: checkpointStart,
|
|
413
|
+
autoBetweenPhases: checkpointAutoBetweenPhases,
|
|
414
|
+
mode: 'nudge-first',
|
|
415
|
+
defaultWrite: false,
|
|
416
|
+
writeRequires: 'explicit aops-cli checkpoint create --apply --json',
|
|
417
|
+
suggestedActions: checkpointSuggestedActions,
|
|
418
|
+
note: 'start composes checkpoint policy and suggested actions only; it never writes checkpoint memory by itself.',
|
|
419
|
+
},
|
|
420
|
+
sync: {
|
|
421
|
+
mode: 'read-only-cache',
|
|
422
|
+
startStatus: true,
|
|
423
|
+
startDiff: true,
|
|
424
|
+
defaultWrite: false,
|
|
425
|
+
readOnly: true,
|
|
426
|
+
readOnlyAtStart: ['status', 'diff'],
|
|
427
|
+
sourceOfTruth: 'hosted-server',
|
|
428
|
+
note: 'Server-first: the hosted aops-server is the source of truth and .aops/** is a read-only local cache. start composes read-only status/diff actions only; there is no push-back from the cache.',
|
|
429
|
+
suggestedActions: syncSuggestedActions,
|
|
430
|
+
},
|
|
431
|
+
playbook: {
|
|
432
|
+
owner: 'agentspace.memory-item',
|
|
433
|
+
surface: 'agentspace.playbook',
|
|
434
|
+
mode: 'nudge-first-read-only',
|
|
435
|
+
defaultWrite: false,
|
|
436
|
+
scopes: ['project', 'session'],
|
|
437
|
+
durability: {
|
|
438
|
+
project: ['sticky', 'durable'],
|
|
439
|
+
session: ['short', 'durable', 'sticky'],
|
|
440
|
+
},
|
|
441
|
+
reviewState: ['accepted'],
|
|
442
|
+
count: playbookSet?.count ?? 0,
|
|
443
|
+
activeCount: playbookNudges?.count ?? playbookSet?.count ?? 0,
|
|
444
|
+
projectCount: playbookNudges?.project.count ?? playbookSet?.count ?? 0,
|
|
445
|
+
sessionCount: playbookNudges?.session.count ?? 0,
|
|
446
|
+
suggestedActions: playbookSuggestedActions,
|
|
447
|
+
closeoutCapture: playbookNudges?.closeoutCapture,
|
|
448
|
+
examples: playbookNudges?.examples,
|
|
449
|
+
note: 'start reads active accepted project/session playbooks as nudge-first guidance only; capture and promotion remain explicit playbook/experience commands with --apply.',
|
|
450
|
+
},
|
|
451
|
+
planning: {
|
|
452
|
+
owner: 'projectman',
|
|
453
|
+
surface: 'projectman.implementation-plan.*',
|
|
454
|
+
sprintBacked: true,
|
|
455
|
+
planId: planId ?? null,
|
|
456
|
+
note: 'implementation-plan id is the underlying sprint id',
|
|
457
|
+
},
|
|
458
|
+
chat: selected === 'build-review-chat' || mode === 'chat-room'
|
|
459
|
+
? {
|
|
460
|
+
owner: 'chatv3',
|
|
461
|
+
roomSlug: roomSlug ?? null,
|
|
462
|
+
note: 'coordination and wake only; PM remains execution/review truth',
|
|
463
|
+
}
|
|
464
|
+
: undefined,
|
|
465
|
+
discuss: selected === 'design-first-consensus'
|
|
466
|
+
? {
|
|
467
|
+
owner: 'agentspace.discuss',
|
|
468
|
+
note: 'record final stances before implementation when design uncertainty is material',
|
|
469
|
+
}
|
|
470
|
+
: undefined,
|
|
471
|
+
});
|
|
472
|
+
const policyJson = JSON.stringify(policy);
|
|
473
|
+
const guardrails = startGuardrailsForDiscipline(selected);
|
|
474
|
+
const guardrailGroups = startGuardrailGroupsForDiscipline(selected);
|
|
475
|
+
const profile = START_DISCIPLINE_PROFILES[selected];
|
|
476
|
+
return {
|
|
477
|
+
state,
|
|
478
|
+
...(Object.keys(mission).length > 0 ? { mission } : {}),
|
|
479
|
+
...(params.resumePack ? { resumePack: params.resumePack.result } : {}),
|
|
480
|
+
discipline: {
|
|
481
|
+
selected,
|
|
482
|
+
recommended: recommended.discipline,
|
|
483
|
+
explicit: explicit.discipline !== undefined,
|
|
484
|
+
selectedBy: explicit.discipline ? 'operator' : 'signals',
|
|
485
|
+
reasons: selectionReasons,
|
|
486
|
+
signals: recommended.signals,
|
|
487
|
+
profile,
|
|
488
|
+
},
|
|
489
|
+
method: {
|
|
490
|
+
selected,
|
|
491
|
+
recommended: recommended.discipline,
|
|
492
|
+
explicit: explicit.discipline !== undefined,
|
|
493
|
+
aliasOf: 'discipline',
|
|
494
|
+
reasons: selectionReasons,
|
|
495
|
+
},
|
|
496
|
+
guardrails,
|
|
497
|
+
guardrailGroups,
|
|
498
|
+
recipe: START_METHOD_RECIPES[selected],
|
|
499
|
+
policy,
|
|
500
|
+
policyJson,
|
|
501
|
+
...(playbookSet ? { playbookSet } : {}),
|
|
502
|
+
...(playbookNudges ? { playbookNudges } : {}),
|
|
503
|
+
startPack: compactPayload({
|
|
504
|
+
mode,
|
|
505
|
+
discipline: selected,
|
|
506
|
+
method: selected,
|
|
507
|
+
methodAliasOf: 'discipline',
|
|
508
|
+
board,
|
|
509
|
+
task: normalize(answers.task),
|
|
510
|
+
objective,
|
|
511
|
+
planId,
|
|
512
|
+
policySeed: {
|
|
513
|
+
target: 'mission.policy',
|
|
514
|
+
flag: '--policy-json',
|
|
515
|
+
json: policy,
|
|
516
|
+
jsonString: policyJson,
|
|
517
|
+
commandHint: missionRef
|
|
518
|
+
? 'aops-cli mission update --id <mission-id> --policy-json \'<result.mission.policyJson>\' --apply --json'
|
|
519
|
+
: 'aops-cli mission create --objective "<objective>" --policy-json \'<result.mission.policyJson>\' --apply --json',
|
|
520
|
+
},
|
|
521
|
+
orchestration: {
|
|
522
|
+
mode: 'read-only-pack',
|
|
523
|
+
sideEffects: 'none',
|
|
524
|
+
activeBindings: 'deferred',
|
|
525
|
+
note: 'start recommends PM/chat/discuss surfaces but does not create or bind records; run deferred commands with --apply to mutate truth surfaces.',
|
|
526
|
+
},
|
|
527
|
+
checkpoint: {
|
|
528
|
+
policyPath: 'mission.policy.checkpoint',
|
|
529
|
+
start: checkpointStart,
|
|
530
|
+
autoBetweenPhases: checkpointAutoBetweenPhases,
|
|
531
|
+
mode: 'nudge-first',
|
|
532
|
+
defaultWrite: false,
|
|
533
|
+
suggestedActions: checkpointSuggestedActions,
|
|
534
|
+
note: 'checkpoint hooks are opt-in nudges; execute the suggested aops-cli checkpoint create command with --apply to write memory.',
|
|
535
|
+
},
|
|
536
|
+
sync: {
|
|
537
|
+
policyPath: 'mission.policy.sync',
|
|
538
|
+
mode: 'read-only-cache',
|
|
539
|
+
startStatus: true,
|
|
540
|
+
startDiff: true,
|
|
541
|
+
readOnly: true,
|
|
542
|
+
sourceOfTruth: 'hosted-server',
|
|
543
|
+
suggestedActions: syncSuggestedActions,
|
|
544
|
+
note: 'Server-first: run read-only status/diff at session start. .aops/** is a read-only local cache refreshed from the hosted server; there is no push-back.',
|
|
545
|
+
},
|
|
546
|
+
playbookSet,
|
|
547
|
+
playbookNudges,
|
|
548
|
+
planning: {
|
|
549
|
+
owner: 'projectman',
|
|
550
|
+
surface: 'projectman.implementation-plan.*',
|
|
551
|
+
sprintBacked: true,
|
|
552
|
+
planId: planId ?? null,
|
|
553
|
+
note: 'implementation-plan id is the underlying sprint id',
|
|
554
|
+
},
|
|
555
|
+
chat: selected === 'build-review-chat' || mode === 'chat-room'
|
|
556
|
+
? {
|
|
557
|
+
owner: 'chatv3',
|
|
558
|
+
roomSlug: roomSlug ?? null,
|
|
559
|
+
note: 'coordination and wake only; PM remains execution/review truth',
|
|
560
|
+
}
|
|
561
|
+
: undefined,
|
|
562
|
+
discuss: selected === 'design-first-consensus'
|
|
563
|
+
? {
|
|
564
|
+
owner: 'agentspace.discuss',
|
|
565
|
+
note: 'record final stances before implementation when design uncertainty is material',
|
|
566
|
+
}
|
|
567
|
+
: undefined,
|
|
568
|
+
closeout: {
|
|
569
|
+
required: true,
|
|
570
|
+
trigger: 'explicit',
|
|
571
|
+
guardrailIds: profile.closeoutGuardrailIds,
|
|
572
|
+
checkCommandHint: 'aops-cli mission check --closeout --id <mission-id> --json',
|
|
573
|
+
handoffCommandHint: 'aops-cli mission handoff --id <mission-id> [--complete] --apply --json',
|
|
574
|
+
note: 'closeout is the before-leaving phase; deferred-with-owner is valid for unresolved PM truth, and board/room closeout stays operator-only',
|
|
575
|
+
},
|
|
576
|
+
deferredBindings,
|
|
577
|
+
nextCommands: [
|
|
578
|
+
missionRef
|
|
579
|
+
? 'aops-cli mission update --id <mission-id> --policy-json \'<result.mission.policyJson>\' --apply --json'
|
|
580
|
+
: 'aops-cli mission create --objective "<objective>" --policy-json \'<result.mission.policyJson>\' --apply --json',
|
|
581
|
+
planId
|
|
582
|
+
? `aops-cli plan get --id ${planId} --json`
|
|
583
|
+
: 'aops-cli plan create --task <task-id> --name "<name>" --goal "<goal>" --apply --json',
|
|
584
|
+
selected === 'build-review-chat'
|
|
585
|
+
? 'aops-cli pm review-request create --review-scope sprint:<plan-id> --target-agent <reviewer> --apply --json'
|
|
586
|
+
: undefined,
|
|
587
|
+
selected === 'design-first-consensus'
|
|
588
|
+
? 'aops-cli discuss start --title "<decision>" --question "<question>" --apply --json'
|
|
589
|
+
: undefined,
|
|
590
|
+
syncStatusCommand,
|
|
591
|
+
syncDiffCommand,
|
|
592
|
+
...playbookSuggestedActions.map((action) => action.command),
|
|
593
|
+
...checkpointSuggestedActions.map((action) => action.command),
|
|
594
|
+
].filter(Boolean),
|
|
595
|
+
}),
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function buildPromptRef(prompt, params) {
|
|
599
|
+
return {
|
|
600
|
+
bytes: Buffer.byteLength(prompt, 'utf8'),
|
|
601
|
+
sha256: createHash('sha256').update(prompt).digest('hex'),
|
|
602
|
+
...(params.path ? { path: params.path } : {}),
|
|
603
|
+
inline: params.inline,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function compactStartGuardrail(guardrail) {
|
|
607
|
+
return compactPayload({
|
|
608
|
+
id: guardrail.id,
|
|
609
|
+
title: guardrail.title,
|
|
610
|
+
phase: guardrail.phase,
|
|
611
|
+
enforcementLevel: guardrail.enforcementLevel,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
function compactStartGuardrailGroups(groups) {
|
|
615
|
+
return compactPayload({
|
|
616
|
+
execution: groups.execution.map(compactStartGuardrail),
|
|
617
|
+
closeout: groups.closeout.map(compactStartGuardrail),
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
function compactStartProfile(profile) {
|
|
621
|
+
return compactPayload({
|
|
622
|
+
id: profile.id,
|
|
623
|
+
version: profile.version,
|
|
624
|
+
title: profile.title,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
function compactPolicySeed(seed) {
|
|
628
|
+
const record = objectValue(seed);
|
|
629
|
+
if (!record)
|
|
630
|
+
return undefined;
|
|
631
|
+
return compactPayload({
|
|
632
|
+
target: record.target,
|
|
633
|
+
flag: record.flag,
|
|
634
|
+
commandHint: record.commandHint,
|
|
635
|
+
jsonRef: 'result.mission.policyJson',
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
function compactStartPack(startPack) {
|
|
639
|
+
const checkpoint = objectValue(startPack.checkpoint);
|
|
640
|
+
const sync = objectValue(startPack.sync);
|
|
641
|
+
const closeout = objectValue(startPack.closeout);
|
|
642
|
+
return compactPayload({
|
|
643
|
+
mode: startPack.mode,
|
|
644
|
+
discipline: startPack.discipline,
|
|
645
|
+
method: startPack.method,
|
|
646
|
+
methodAliasOf: startPack.methodAliasOf,
|
|
647
|
+
board: startPack.board,
|
|
648
|
+
task: startPack.task,
|
|
649
|
+
objective: startPack.objective,
|
|
650
|
+
planId: startPack.planId,
|
|
651
|
+
policySeed: compactPolicySeed(startPack.policySeed),
|
|
652
|
+
orchestration: startPack.orchestration,
|
|
653
|
+
checkpoint: checkpoint
|
|
654
|
+
? compactPayload({
|
|
655
|
+
policyPath: checkpoint.policyPath,
|
|
656
|
+
start: checkpoint.start,
|
|
657
|
+
autoBetweenPhases: checkpoint.autoBetweenPhases,
|
|
658
|
+
mode: checkpoint.mode,
|
|
659
|
+
defaultWrite: checkpoint.defaultWrite,
|
|
660
|
+
suggestedActions: checkpoint.suggestedActions,
|
|
661
|
+
})
|
|
662
|
+
: undefined,
|
|
663
|
+
sync: sync
|
|
664
|
+
? compactPayload({
|
|
665
|
+
policyPath: sync.policyPath,
|
|
666
|
+
mode: sync.mode,
|
|
667
|
+
startStatus: sync.startStatus,
|
|
668
|
+
startDiff: sync.startDiff,
|
|
669
|
+
readOnly: sync.readOnly,
|
|
670
|
+
sourceOfTruth: sync.sourceOfTruth,
|
|
671
|
+
suggestedActions: sync.suggestedActions,
|
|
672
|
+
})
|
|
673
|
+
: undefined,
|
|
674
|
+
planning: startPack.planning,
|
|
675
|
+
chat: startPack.chat,
|
|
676
|
+
discuss: startPack.discuss,
|
|
677
|
+
closeout: closeout
|
|
678
|
+
? compactPayload({
|
|
679
|
+
required: closeout.required,
|
|
680
|
+
trigger: closeout.trigger,
|
|
681
|
+
guardrailIds: closeout.guardrailIds,
|
|
682
|
+
checkCommandHint: closeout.checkCommandHint,
|
|
683
|
+
handoffCommandHint: closeout.handoffCommandHint,
|
|
684
|
+
})
|
|
685
|
+
: undefined,
|
|
686
|
+
deferredBindings: startPack.deferredBindings,
|
|
687
|
+
nextCommands: startPack.nextCommands,
|
|
688
|
+
guidanceRef: 'result.sessionGuidance',
|
|
689
|
+
playbookRef: 'result.sessionGuidance.layers.playbooks',
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
function compactResumePack(resumePack) {
|
|
693
|
+
const record = objectValue(resumePack);
|
|
694
|
+
if (!record)
|
|
695
|
+
return resumePack;
|
|
696
|
+
const rest = { ...record };
|
|
697
|
+
delete rest.playbookNudges;
|
|
698
|
+
return compactPayload({
|
|
699
|
+
...rest,
|
|
700
|
+
playbookRef: 'result.sessionGuidance.layers.playbooks',
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
function compactStartMissionPack(mission, params) {
|
|
704
|
+
return compactPayload({
|
|
705
|
+
state: mission.state,
|
|
706
|
+
mission: mission.mission,
|
|
707
|
+
resumePack: compactResumePack(mission.resumePack),
|
|
708
|
+
discipline: compactPayload({
|
|
709
|
+
selected: mission.discipline.selected,
|
|
710
|
+
recommended: mission.discipline.recommended,
|
|
711
|
+
explicit: mission.discipline.explicit,
|
|
712
|
+
selectedBy: mission.discipline.selectedBy,
|
|
713
|
+
reasons: mission.discipline.reasons,
|
|
714
|
+
signals: mission.discipline.signals,
|
|
715
|
+
profile: compactStartProfile(mission.discipline.profile),
|
|
716
|
+
}),
|
|
717
|
+
method: compactPayload({
|
|
718
|
+
selected: mission.method.selected,
|
|
719
|
+
recommended: mission.method.recommended,
|
|
720
|
+
explicit: mission.method.explicit,
|
|
721
|
+
aliasOf: mission.method.aliasOf,
|
|
722
|
+
reasons: mission.method.reasons,
|
|
723
|
+
}),
|
|
724
|
+
guardrailGroups: compactStartGuardrailGroups(mission.guardrailGroups),
|
|
725
|
+
recipe: mission.recipe,
|
|
726
|
+
policyJson: mission.policyJson,
|
|
727
|
+
policyRef: 'mission.policyJson',
|
|
728
|
+
startPack: compactStartPack(mission.startPack),
|
|
729
|
+
sessionGuidance: params.includeSessionGuidance ? mission.sessionGuidance : undefined,
|
|
730
|
+
sessionGuidanceRef: params.includeSessionGuidance ? undefined : 'result.sessionGuidance',
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
function appendMissionPackToPrompt(prompt, mission) {
|
|
734
|
+
const compactMission = compactStartMissionPack(mission, { includeSessionGuidance: true });
|
|
735
|
+
return `${prompt.trimEnd()}
|
|
736
|
+
|
|
737
|
+
## Mission Orchestration Pack (read-only)
|
|
738
|
+
|
|
739
|
+
Use this compact pack to choose the working discipline and PM/chat/discuss surfaces. \`discipline\` is the canonical policy preset; \`method\` is kept beside it as a compatibility alias. \`start\` is read-only in this v1 path: it does not create plans, bind chat rooms, open discuss topics, promote playbooks, or run sync by itself. Read \`sessionGuidance\` first for layered rules L1-L3: runtime rules, working-discipline guardrails, accepted playbooks, and ranked experience briefs. Read \`startPack.sync\`: run the read-only sync status/diff commands at session start to compare the local cache against the hosted server. Server-first: the hosted aops-server is the source of truth and \`.aops/**\` is a read-only local cache; there is no push-back from the cache. \`policyJson\` is the single mission-policy seed; \`startPack.policySeed.jsonRef\` points back to it instead of duplicating it. Treat \`deferredBindings\` as the apply-required follow-up list, and write Projectman records for durable task state.
|
|
740
|
+
|
|
741
|
+
\`\`\`json
|
|
742
|
+
${JSON.stringify(compactMission, null, 2)}
|
|
743
|
+
\`\`\`
|
|
744
|
+
`;
|
|
745
|
+
}
|
|
746
|
+
function isMultiAgentMode(mode) {
|
|
747
|
+
return mode !== undefined && MULTI_AGENT_MODES.has(mode);
|
|
748
|
+
}
|
|
749
|
+
export async function listLocalBoards(rootDir, localRoot) {
|
|
750
|
+
const boardsDir = resolveRepoFirstProjectmanPaths({ repoRoot: rootDir, localRoot }).boards;
|
|
751
|
+
let entries;
|
|
752
|
+
try {
|
|
753
|
+
entries = await fs.readdir(boardsDir);
|
|
754
|
+
}
|
|
755
|
+
catch {
|
|
756
|
+
return [];
|
|
757
|
+
}
|
|
758
|
+
const boards = [];
|
|
759
|
+
for (const entry of entries) {
|
|
760
|
+
if (!entry.endsWith('.md'))
|
|
761
|
+
continue;
|
|
762
|
+
try {
|
|
763
|
+
const raw = await fs.readFile(path.join(boardsDir, entry), 'utf-8');
|
|
764
|
+
const head = raw.slice(0, 2000);
|
|
765
|
+
const slug = head.match(/^slug:\s*"?([^"\n]+)"?\s*$/m)?.[1]?.trim();
|
|
766
|
+
if (!slug)
|
|
767
|
+
continue;
|
|
768
|
+
const name = head.match(/^name:\s*"?([^"\n]+)"?\s*$/m)?.[1]?.trim();
|
|
769
|
+
boards.push({ slug, name });
|
|
770
|
+
}
|
|
771
|
+
catch {
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
return boards.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
775
|
+
}
|
|
776
|
+
export function buildStartQuestions(answers, boards) {
|
|
777
|
+
const questions = [
|
|
778
|
+
{
|
|
779
|
+
key: 'mode',
|
|
780
|
+
flag: '--mode',
|
|
781
|
+
question: `Session mode? (${START_MODES.join(' | ')})`,
|
|
782
|
+
required: true,
|
|
783
|
+
askOperator: false,
|
|
784
|
+
default: 'solo',
|
|
785
|
+
suggestions: [...START_MODES],
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
key: 'board',
|
|
789
|
+
flag: '--board',
|
|
790
|
+
question: 'PM board slug (existing) or new:<title>?',
|
|
791
|
+
required: true,
|
|
792
|
+
askOperator: false,
|
|
793
|
+
suggestions: boards.map((board) => board.slug),
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
key: 'task',
|
|
797
|
+
flag: '--task',
|
|
798
|
+
question: 'Initial task definition? (optional; empty = set up and wait for task definitions)',
|
|
799
|
+
required: false,
|
|
800
|
+
askOperator: false,
|
|
801
|
+
},
|
|
802
|
+
];
|
|
803
|
+
if (isMultiAgentMode(answers.mode)) {
|
|
804
|
+
const board = normalize(answers.board);
|
|
805
|
+
const roomSlugDefault = board && !board.startsWith('new:') ? `${board}-room` : undefined;
|
|
806
|
+
questions.push({
|
|
807
|
+
key: 'roomSlug',
|
|
808
|
+
flag: '--room-slug',
|
|
809
|
+
question: 'Hosted chat room slug?',
|
|
810
|
+
required: true,
|
|
811
|
+
askOperator: false,
|
|
812
|
+
default: roomSlugDefault,
|
|
813
|
+
}, {
|
|
814
|
+
key: 'roomTitle',
|
|
815
|
+
flag: '--room-title',
|
|
816
|
+
question: 'Hosted chat room title?',
|
|
817
|
+
required: true,
|
|
818
|
+
askOperator: false,
|
|
819
|
+
default: normalize(answers.roomSlug) ?? roomSlugDefault,
|
|
820
|
+
}, {
|
|
821
|
+
key: 'roles',
|
|
822
|
+
flag: '--roles',
|
|
823
|
+
question: 'Participants and roles? (operator-only — e.g. "claude=implementer, codex=reviewer, mzs=operator")',
|
|
824
|
+
required: true,
|
|
825
|
+
askOperator: true,
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
return questions;
|
|
829
|
+
}
|
|
830
|
+
export function stripHostedFrontmatter(content) {
|
|
831
|
+
if (!content.startsWith('---'))
|
|
832
|
+
return content;
|
|
833
|
+
const close = content.indexOf('\n---', 3);
|
|
834
|
+
if (close === -1)
|
|
835
|
+
return content;
|
|
836
|
+
const afterClose = content.indexOf('\n', close + 1 + 3);
|
|
837
|
+
return afterClose === -1 ? '' : content.slice(afterClose + 1).replace(/^\s+/, '');
|
|
838
|
+
}
|
|
839
|
+
function readEnvelopeResult(envelope) {
|
|
840
|
+
return envelope && typeof envelope === 'object' && !Array.isArray(envelope) ? envelope.result : undefined;
|
|
841
|
+
}
|
|
842
|
+
function objectValue(value) {
|
|
843
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
844
|
+
}
|
|
845
|
+
function memoryBriefResultFromEnvelope(envelope) {
|
|
846
|
+
const result = objectValue(readEnvelopeResult(envelope));
|
|
847
|
+
if (!result)
|
|
848
|
+
return undefined;
|
|
849
|
+
const input = objectValue(envelope?.input);
|
|
850
|
+
if ((envelope?.readOnly === true || input?.readOnly === true) && result.readOnly === undefined)
|
|
851
|
+
return { ...result, readOnly: true };
|
|
852
|
+
return result;
|
|
853
|
+
}
|
|
854
|
+
function appendMemoryBriefToPrompt(prompt, memoryBrief) {
|
|
855
|
+
if (!memoryBrief)
|
|
856
|
+
return prompt;
|
|
857
|
+
return `${prompt.trimEnd()}
|
|
858
|
+
|
|
859
|
+
## Repo-First Memory Brief (read-only)
|
|
860
|
+
|
|
861
|
+
This pack was generated without writing memory. Use it as startup context; write follow-up context with \`aops-cli checkpoint create --summary "<summary>" --apply --json\` after meaningful progress.
|
|
862
|
+
|
|
863
|
+
\`\`\`json
|
|
864
|
+
${JSON.stringify(memoryBrief, null, 2)}
|
|
865
|
+
\`\`\`
|
|
866
|
+
`;
|
|
867
|
+
}
|
|
868
|
+
export function composeStartPrompt(body, answers, fallbackProjectSlug, memoryBrief) {
|
|
869
|
+
const replacements = {
|
|
870
|
+
MODE: normalize(answers.mode) ?? 'solo',
|
|
871
|
+
TASK: normalize(answers.task) ?? '(empty — set up and wait for task definitions)',
|
|
872
|
+
PROJECT_SLUG: normalize(answers.projectSlug) ?? fallbackProjectSlug ?? 'current repo project',
|
|
873
|
+
BOARD_SLUG: normalize(answers.board) ?? '-',
|
|
874
|
+
ROOM_SLUG: normalize(answers.roomSlug) ?? '-',
|
|
875
|
+
ROOM_TITLE: normalize(answers.roomTitle) ?? '-',
|
|
876
|
+
AGENTS_AND_ROLES: normalize(answers.roles) ?? '- (solo)',
|
|
877
|
+
};
|
|
878
|
+
let prompt = body;
|
|
879
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
880
|
+
prompt = prompt.split(`{{${key}}}`).join(value);
|
|
881
|
+
}
|
|
882
|
+
const warnings = [];
|
|
883
|
+
const leftover = prompt.match(/\{\{[A-Z_]+\}\}/g);
|
|
884
|
+
if (leftover) {
|
|
885
|
+
warnings.push(`Unresolved placeholders left in the composed prompt: ${[...new Set(leftover)].join(', ')}`);
|
|
886
|
+
}
|
|
887
|
+
return { prompt: appendMemoryBriefToPrompt(prompt, memoryBrief), warnings };
|
|
888
|
+
}
|
|
889
|
+
async function readStarterMirror(rootDir) {
|
|
890
|
+
const mirrorPath = path.join(rootDir, ...START_PROMPT_MIRROR_PATH.split('/'));
|
|
891
|
+
let raw;
|
|
892
|
+
try {
|
|
893
|
+
raw = await fs.readFile(mirrorPath, 'utf-8');
|
|
894
|
+
}
|
|
895
|
+
catch {
|
|
896
|
+
throw new Error(`Starter prompt mirror not found at ${START_PROMPT_MIRROR_PATH}. ` +
|
|
897
|
+
'Refresh hosted mirrors first: aops-cli sync pull --apply --hosted-project-slug aops --json');
|
|
898
|
+
}
|
|
899
|
+
return { mirrorPath, body: stripHostedFrontmatter(raw) };
|
|
900
|
+
}
|
|
901
|
+
function collectAnswers(options) {
|
|
902
|
+
return {
|
|
903
|
+
mode: normalize(options.mode),
|
|
904
|
+
task: normalize(options.task),
|
|
905
|
+
projectSlug: normalize(options.projectSlug),
|
|
906
|
+
board: normalize(options.board),
|
|
907
|
+
roomSlug: normalize(options.roomSlug),
|
|
908
|
+
roomTitle: normalize(options.roomTitle),
|
|
909
|
+
roles: normalize(options.roles),
|
|
910
|
+
mission: normalize(options.mission),
|
|
911
|
+
resume: normalize(options.resume),
|
|
912
|
+
objective: normalize(options.objective),
|
|
913
|
+
discipline: normalize(options.discipline),
|
|
914
|
+
method: normalize(options.method),
|
|
915
|
+
workSize: normalize(options.workSize),
|
|
916
|
+
agentCount: normalize(options.agentCount),
|
|
917
|
+
decisionUncertainty: normalize(options.decisionUncertainty),
|
|
918
|
+
operatorInterface: normalize(options.operatorInterface),
|
|
919
|
+
plan: normalize(options.plan),
|
|
920
|
+
checkpointStart: options.checkpointStart,
|
|
921
|
+
checkpointAutoBetweenPhases: options.checkpointAutoBetweenPhases,
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
async function loadStartMissionResumePack(options, answers, activeProject) {
|
|
925
|
+
const missionId = normalize(answers.resume);
|
|
926
|
+
if (!missionId)
|
|
927
|
+
return undefined;
|
|
928
|
+
const loaded = await loadMissionResumePack({
|
|
929
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
930
|
+
accessToken: options.accessToken,
|
|
931
|
+
refreshToken: options.refreshToken,
|
|
932
|
+
timeoutMs: options.timeoutMs,
|
|
933
|
+
tenantId: options.tenantId,
|
|
934
|
+
locale: options.locale,
|
|
935
|
+
fallbackLocale: options.fallbackLocale,
|
|
936
|
+
scopeId: normalize(options.scopeId) ?? normalize(activeProject?.scopeId) ?? normalize(activeProject?.projectId),
|
|
937
|
+
scopeResolution: options.scopeResolution,
|
|
938
|
+
projectId: normalize(options.projectId) ?? normalize(activeProject?.projectId),
|
|
939
|
+
projectName: normalize(options.projectName),
|
|
940
|
+
id: missionId,
|
|
941
|
+
depth: normalize(options.missionResumeDepth) ?? 'light',
|
|
942
|
+
limit: options.missionResumeLimit ?? 8,
|
|
943
|
+
full: options.missionResumeFull,
|
|
944
|
+
});
|
|
945
|
+
if (!loaded)
|
|
946
|
+
throw new Error('Mission resume pack could not be loaded. Check host availability/auth and retry.');
|
|
947
|
+
return loaded;
|
|
948
|
+
}
|
|
949
|
+
function missingQuestions(answers, questions) {
|
|
950
|
+
return questions.filter((question) => question.required && normalize(answers[question.key]) === undefined);
|
|
951
|
+
}
|
|
952
|
+
async function promptForAnswersInteractively(answers, boards) {
|
|
953
|
+
if (normalize(answers.mode) === undefined) {
|
|
954
|
+
answers.mode = await promptSelect({
|
|
955
|
+
message: 'Session mode?',
|
|
956
|
+
choices: START_MODES.map((mode) => ({ name: mode, value: mode })),
|
|
957
|
+
default: 'solo',
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
if (normalize(answers.board) === undefined) {
|
|
961
|
+
if (boards.length > 0) {
|
|
962
|
+
const OTHER = '__other__';
|
|
963
|
+
const picked = await promptSelect({
|
|
964
|
+
message: 'PM board?',
|
|
965
|
+
choices: [
|
|
966
|
+
...boards.map((board) => ({ name: board.name ? `${board.slug} (${board.name})` : board.slug, value: board.slug })),
|
|
967
|
+
{ name: 'other (type a slug or new:<Title>)', value: OTHER },
|
|
968
|
+
],
|
|
969
|
+
});
|
|
970
|
+
answers.board =
|
|
971
|
+
picked === OTHER
|
|
972
|
+
? await promptInput({
|
|
973
|
+
message: 'Board slug or new:<Title>:',
|
|
974
|
+
validate: (v) => (v.trim().length > 0 ? true : 'Board is required'),
|
|
975
|
+
})
|
|
976
|
+
: picked;
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
answers.board = await promptInput({
|
|
980
|
+
message: 'Board slug or new:<Title>:',
|
|
981
|
+
validate: (v) => (v.trim().length > 0 ? true : 'Board is required'),
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
if (normalize(answers.task) === undefined) {
|
|
986
|
+
answers.task = await promptInput({
|
|
987
|
+
message: 'Initial task (optional, empty = set up and wait):',
|
|
988
|
+
default: '',
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
if (isMultiAgentMode(normalize(answers.mode))) {
|
|
992
|
+
const board = normalize(answers.board);
|
|
993
|
+
const roomSlugDefault = board && !board.startsWith('new:') ? `${board}-room` : undefined;
|
|
994
|
+
if (normalize(answers.roomSlug) === undefined) {
|
|
995
|
+
answers.roomSlug = await promptInput({
|
|
996
|
+
message: 'Chat room slug:',
|
|
997
|
+
default: roomSlugDefault,
|
|
998
|
+
validate: (v) => (v.trim().length > 0 ? true : 'Room slug is required in multi-agent modes'),
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
if (normalize(answers.roomTitle) === undefined) {
|
|
1002
|
+
answers.roomTitle = await promptInput({
|
|
1003
|
+
message: 'Chat room title:',
|
|
1004
|
+
default: normalize(answers.roomSlug),
|
|
1005
|
+
validate: (v) => (v.trim().length > 0 ? true : 'Room title is required in multi-agent modes'),
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
if (normalize(answers.roles) === undefined) {
|
|
1009
|
+
answers.roles = await promptInput({
|
|
1010
|
+
message: 'Participants and roles (e.g. "claude=implementer, codex=reviewer, mzs=operator"):',
|
|
1011
|
+
validate: (v) => (v.trim().length > 0 ? true : 'Roles are operator-assigned and required in multi-agent modes'),
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
export async function composeStart(options = {}) {
|
|
1017
|
+
assertSupportedMode(normalize(options.mode));
|
|
1018
|
+
assertCompatibleDisciplineAliases({
|
|
1019
|
+
discipline: normalize(options.discipline),
|
|
1020
|
+
method: normalize(options.method),
|
|
1021
|
+
});
|
|
1022
|
+
const startDir = options.root ? path.resolve(process.cwd(), options.root) : process.cwd();
|
|
1023
|
+
const loaded = await loadAopsRepoConfigReadOnly(startDir);
|
|
1024
|
+
const rootDir = loaded.rootDir;
|
|
1025
|
+
const activeProject = loaded.config?.projects?.find((project) => project.name === loaded.config?.activeProjectName);
|
|
1026
|
+
const projectSlug = normalize(activeProject?.slug) ?? normalize(activeProject?.name);
|
|
1027
|
+
const { mirrorPath, body } = await readStarterMirror(rootDir);
|
|
1028
|
+
const boards = await listLocalBoards(rootDir, activeProject?.localRoot);
|
|
1029
|
+
const sessionStateNudges = await readSessionStateNudges({ repoRoot: rootDir, localRoot: activeProject?.localRoot });
|
|
1030
|
+
const answers = collectAnswers(options);
|
|
1031
|
+
const interactiveAllowed = options.interactive !== false && process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
1032
|
+
if (interactiveAllowed && missingQuestions(answers, buildStartQuestions(answers, boards)).length > 0) {
|
|
1033
|
+
await promptForAnswersInteractively(answers, boards);
|
|
1034
|
+
}
|
|
1035
|
+
const questions = buildStartQuestions(answers, boards);
|
|
1036
|
+
const missing = missingQuestions(answers, questions);
|
|
1037
|
+
const base = {
|
|
1038
|
+
action: 'start',
|
|
1039
|
+
rootDir,
|
|
1040
|
+
mirrorPath,
|
|
1041
|
+
projectSlug,
|
|
1042
|
+
boards,
|
|
1043
|
+
answers,
|
|
1044
|
+
questions,
|
|
1045
|
+
sessionStateNudges,
|
|
1046
|
+
};
|
|
1047
|
+
if (missing.length > 0) {
|
|
1048
|
+
return { ...base, status: 'needs-input', missing, warnings: [] };
|
|
1049
|
+
}
|
|
1050
|
+
const memoryBriefEnvelope = options.memoryBrief === false
|
|
1051
|
+
? undefined
|
|
1052
|
+
: await buildReadOnlyRepoFirstMemoryBriefFromContext({
|
|
1053
|
+
repoRoot: rootDir,
|
|
1054
|
+
configPath: loaded.configPath,
|
|
1055
|
+
configFound: Boolean(loaded.config),
|
|
1056
|
+
scopeId: activeProject?.scopeId ?? activeProject?.projectId,
|
|
1057
|
+
projectId: activeProject?.projectId,
|
|
1058
|
+
projectName: activeProject?.name,
|
|
1059
|
+
projectSlug,
|
|
1060
|
+
localRoot: activeProject?.localRoot,
|
|
1061
|
+
ownerRepo: activeProject?.ownerRepo,
|
|
1062
|
+
parentProjectSlug: activeProject?.parentProjectSlug,
|
|
1063
|
+
}, {
|
|
1064
|
+
subject: 'project',
|
|
1065
|
+
query: normalize(answers.task) ?? 'startup context',
|
|
1066
|
+
limit: 4,
|
|
1067
|
+
depth: 'light',
|
|
1068
|
+
});
|
|
1069
|
+
const memoryBrief = memoryBriefResultFromEnvelope(memoryBriefEnvelope);
|
|
1070
|
+
const activePlaybookMissionId = normalize(answers.resume) ?? normalize(answers.mission);
|
|
1071
|
+
const playbookNudges = await buildReadOnlyActivePlaybookNudgePackFromContext({
|
|
1072
|
+
repoRoot: rootDir,
|
|
1073
|
+
localRoot: activeProject?.localRoot,
|
|
1074
|
+
projectSlug,
|
|
1075
|
+
}, {
|
|
1076
|
+
missionId: activePlaybookMissionId,
|
|
1077
|
+
});
|
|
1078
|
+
const playbookSet = playbookNudges.project;
|
|
1079
|
+
const missionResumePack = await loadStartMissionResumePack(options, answers, activeProject);
|
|
1080
|
+
const missionBase = buildStartMissionPack(answers, { projectSlug, resumePack: missionResumePack, playbookSet, playbookNudges });
|
|
1081
|
+
const sessionGuidance = await buildSessionGuidancePack({
|
|
1082
|
+
repoRoot: rootDir,
|
|
1083
|
+
localRoot: activeProject?.localRoot,
|
|
1084
|
+
projectSlug,
|
|
1085
|
+
}, {
|
|
1086
|
+
surface: 'start',
|
|
1087
|
+
task: normalize(answers.task) ?? normalize(answers.objective),
|
|
1088
|
+
missionId: normalize(answers.resume) ?? normalize(answers.mission),
|
|
1089
|
+
planId: normalize(answers.plan),
|
|
1090
|
+
area: normalize(options.area),
|
|
1091
|
+
limit: options.limit,
|
|
1092
|
+
playbookNudges,
|
|
1093
|
+
discipline: {
|
|
1094
|
+
selected: missionBase.discipline.selected,
|
|
1095
|
+
recommended: missionBase.discipline.recommended,
|
|
1096
|
+
explicit: missionBase.discipline.explicit,
|
|
1097
|
+
profile: missionBase.discipline.profile,
|
|
1098
|
+
guardrails: missionBase.guardrails,
|
|
1099
|
+
},
|
|
1100
|
+
});
|
|
1101
|
+
const mission = {
|
|
1102
|
+
...missionBase,
|
|
1103
|
+
sessionGuidance,
|
|
1104
|
+
startPack: compactPayload({
|
|
1105
|
+
...missionBase.startPack,
|
|
1106
|
+
sessionGuidance,
|
|
1107
|
+
}),
|
|
1108
|
+
};
|
|
1109
|
+
const composed = composeStartPrompt(body, answers, projectSlug, memoryBrief);
|
|
1110
|
+
const prompt = appendMissionPackToPrompt(composed.prompt, mission);
|
|
1111
|
+
const warnings = composed.warnings;
|
|
1112
|
+
let outFile;
|
|
1113
|
+
if (normalize(options.out)) {
|
|
1114
|
+
outFile = path.resolve(process.cwd(), options.out);
|
|
1115
|
+
await fs.mkdir(path.dirname(outFile), { recursive: true });
|
|
1116
|
+
await fs.writeFile(outFile, prompt, 'utf-8');
|
|
1117
|
+
}
|
|
1118
|
+
const promptRef = buildPromptRef(prompt, { path: outFile, inline: true });
|
|
1119
|
+
return { ...base, status: 'ready', missing: [], warnings, memoryBrief, mission, sessionGuidance, prompt, promptRef, outFile };
|
|
1120
|
+
}
|
|
1121
|
+
export async function composeStartReminder(options = {}) {
|
|
1122
|
+
assertSupportedMode(normalize(options.mode));
|
|
1123
|
+
assertCompatibleDisciplineAliases({
|
|
1124
|
+
discipline: normalize(options.discipline),
|
|
1125
|
+
method: normalize(options.method),
|
|
1126
|
+
});
|
|
1127
|
+
const startDir = options.root ? path.resolve(process.cwd(), options.root) : process.cwd();
|
|
1128
|
+
const loaded = await loadAopsRepoConfigReadOnly(startDir);
|
|
1129
|
+
const rootDir = loaded.rootDir;
|
|
1130
|
+
const activeProject = loaded.config?.projects?.find((project) => project.name === loaded.config?.activeProjectName);
|
|
1131
|
+
const projectSlug = normalize(activeProject?.slug) ?? normalize(activeProject?.name);
|
|
1132
|
+
const answers = collectAnswers(options);
|
|
1133
|
+
const answersForRecommendation = {
|
|
1134
|
+
...answers,
|
|
1135
|
+
mode: normalize(answers.mode) ?? 'solo',
|
|
1136
|
+
};
|
|
1137
|
+
const recommended = recommendStartDiscipline(answersForRecommendation);
|
|
1138
|
+
const explicit = resolveExplicitDiscipline(answersForRecommendation);
|
|
1139
|
+
const selected = explicit.discipline ?? recommended.discipline;
|
|
1140
|
+
const playbookNudges = await buildReadOnlyActivePlaybookNudgePackFromContext({
|
|
1141
|
+
repoRoot: rootDir,
|
|
1142
|
+
localRoot: activeProject?.localRoot,
|
|
1143
|
+
projectSlug,
|
|
1144
|
+
}, {
|
|
1145
|
+
missionId: normalize(answers.resume) ?? normalize(answers.mission),
|
|
1146
|
+
limit: parsePositiveInteger(options.limit),
|
|
1147
|
+
});
|
|
1148
|
+
const guardrails = startGuardrailsForDiscipline(selected);
|
|
1149
|
+
const profile = START_DISCIPLINE_PROFILES[selected];
|
|
1150
|
+
const sessionGuidance = await buildSessionGuidancePack({
|
|
1151
|
+
repoRoot: rootDir,
|
|
1152
|
+
localRoot: activeProject?.localRoot,
|
|
1153
|
+
projectSlug,
|
|
1154
|
+
}, {
|
|
1155
|
+
surface: 'start-reminder',
|
|
1156
|
+
task: normalize(answers.task) ?? normalize(answers.objective),
|
|
1157
|
+
missionId: normalize(answers.resume) ?? normalize(answers.mission),
|
|
1158
|
+
planId: normalize(answers.plan),
|
|
1159
|
+
area: normalize(options.area),
|
|
1160
|
+
limit: options.limit,
|
|
1161
|
+
playbookNudges,
|
|
1162
|
+
discipline: {
|
|
1163
|
+
selected,
|
|
1164
|
+
recommended: recommended.discipline,
|
|
1165
|
+
explicit: explicit.discipline !== undefined,
|
|
1166
|
+
profile,
|
|
1167
|
+
guardrails,
|
|
1168
|
+
},
|
|
1169
|
+
});
|
|
1170
|
+
return {
|
|
1171
|
+
action: 'start.reminder',
|
|
1172
|
+
status: 'ready',
|
|
1173
|
+
rootDir,
|
|
1174
|
+
projectSlug,
|
|
1175
|
+
answers,
|
|
1176
|
+
readOnly: true,
|
|
1177
|
+
defaultWrite: false,
|
|
1178
|
+
sessionGuidance,
|
|
1179
|
+
suggestedActions: sessionGuidance.suggestedActions,
|
|
1180
|
+
warnings: [],
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
function serializableStartResult(result, params) {
|
|
1184
|
+
const base = {
|
|
1185
|
+
action: result.action,
|
|
1186
|
+
status: result.status,
|
|
1187
|
+
rootDir: result.rootDir,
|
|
1188
|
+
mirrorPath: result.mirrorPath,
|
|
1189
|
+
projectSlug: result.projectSlug,
|
|
1190
|
+
boards: result.boards,
|
|
1191
|
+
answers: result.answers,
|
|
1192
|
+
questions: result.questions,
|
|
1193
|
+
missing: result.missing,
|
|
1194
|
+
warnings: result.warnings,
|
|
1195
|
+
memoryBrief: result.memoryBrief,
|
|
1196
|
+
sessionGuidance: result.sessionGuidance,
|
|
1197
|
+
sessionStateNudges: result.sessionStateNudges,
|
|
1198
|
+
promptRef: result.promptRef
|
|
1199
|
+
? { ...result.promptRef, inline: params.compact ? false : result.promptRef.inline }
|
|
1200
|
+
: undefined,
|
|
1201
|
+
outFile: result.outFile,
|
|
1202
|
+
};
|
|
1203
|
+
if (params.compact) {
|
|
1204
|
+
return {
|
|
1205
|
+
...base,
|
|
1206
|
+
mission: result.mission ? compactStartMissionPack(result.mission, { includeSessionGuidance: false }) : undefined,
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
return {
|
|
1210
|
+
...base,
|
|
1211
|
+
mission: result.mission,
|
|
1212
|
+
prompt: result.prompt,
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
function serializableStartReminderResult(result) {
|
|
1216
|
+
return {
|
|
1217
|
+
action: result.action,
|
|
1218
|
+
status: result.status,
|
|
1219
|
+
rootDir: result.rootDir,
|
|
1220
|
+
projectSlug: result.projectSlug,
|
|
1221
|
+
answers: result.answers,
|
|
1222
|
+
readOnly: result.readOnly,
|
|
1223
|
+
defaultWrite: result.defaultWrite,
|
|
1224
|
+
sessionGuidance: result.sessionGuidance,
|
|
1225
|
+
suggestedActions: result.suggestedActions,
|
|
1226
|
+
warnings: result.warnings,
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
export async function runStart(options = {}) {
|
|
1230
|
+
const interactive = options.json === true ? false : options.interactive;
|
|
1231
|
+
if (options.reminder === true) {
|
|
1232
|
+
const result = await composeStartReminder({ ...options, interactive: false });
|
|
1233
|
+
if (options.json) {
|
|
1234
|
+
console.log(JSON.stringify({ command: 'start.reminder', result: serializableStartReminderResult(result) }, null, 2));
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
logSuccess('Session guidance reminder ready (read-only).');
|
|
1238
|
+
console.log(JSON.stringify(result.sessionGuidance, null, 2));
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
const result = await composeStart({ ...options, interactive });
|
|
1242
|
+
if (options.json) {
|
|
1243
|
+
const compact = options.fullOutput === true ? false : true;
|
|
1244
|
+
console.log(JSON.stringify({ command: 'start', result: serializableStartResult(result, { compact }) }, null, 2));
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
result.warnings.forEach((warning) => logWarn(warning));
|
|
1248
|
+
if (result.status === 'needs-input') {
|
|
1249
|
+
logWarn('Missing answers — re-run with the flags below (or answer interactively in a TTY):');
|
|
1250
|
+
for (const question of result.missing) {
|
|
1251
|
+
const hints = [];
|
|
1252
|
+
if (question.default)
|
|
1253
|
+
hints.push(`default: ${question.default}`);
|
|
1254
|
+
if (question.suggestions && question.suggestions.length > 0)
|
|
1255
|
+
hints.push(`suggestions: ${question.suggestions.join(', ')}`);
|
|
1256
|
+
if (question.askOperator)
|
|
1257
|
+
hints.push('ask the operator — do not invent this answer');
|
|
1258
|
+
logInfo(`${question.flag} — ${question.question}${hints.length > 0 ? ` [${hints.join('; ')}]` : ''}`);
|
|
1259
|
+
}
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
if (result.outFile)
|
|
1263
|
+
logSuccess(`Composed kickoff prompt written to ${result.outFile}`);
|
|
1264
|
+
logSuccess(`Kickoff prompt ready (mode: ${result.answers.mode}, board: ${result.answers.board})`);
|
|
1265
|
+
process.stdout.write(`\n${result.prompt ?? ''}`);
|
|
1266
|
+
}
|
|
1267
|
+
export function makeStartCommand() {
|
|
1268
|
+
const cmd = new Command('start')
|
|
1269
|
+
.description('Compose the AOPS Collaborative Startup kickoff prompt (interactive or flag-driven)')
|
|
1270
|
+
.option('--mode <mode>', `Session mode: ${START_MODES.join(' | ')}`)
|
|
1271
|
+
.option('--task <text>', 'Initial task definition (optional; empty = set up and wait)')
|
|
1272
|
+
.option('--board <slug>', 'PM board slug or new:<title>')
|
|
1273
|
+
.option('--project-slug <slug>', 'Project slug override (default: repo config active project)')
|
|
1274
|
+
.option('--room-slug <slug>', 'Hosted chat room slug (chat-room mode)')
|
|
1275
|
+
.option('--room-title <title>', 'Hosted chat room title (chat-room mode)')
|
|
1276
|
+
.option('--roles <list>', 'Operator-assigned roles, e.g. "claude=implementer, codex=reviewer, mzs=operator"')
|
|
1277
|
+
.option('--mission <ref>', 'Mission id or title to resume/define in the startup pack')
|
|
1278
|
+
.option('--resume <mission-id>', 'Hosted mission id to resume; embeds a compact mission resume pack')
|
|
1279
|
+
.option('--objective <text>', 'Mission objective override; defaults to --task when omitted')
|
|
1280
|
+
.option('--discipline <discipline>', `Working discipline: ${START_DISCIPLINES.join(' | ')}`)
|
|
1281
|
+
.option('--method <method>', `Compatibility alias for --discipline: ${START_METHODS.join(' | ')}`)
|
|
1282
|
+
.option('--work-size <size>', 'Work size signal for method recommendation: small | medium | large')
|
|
1283
|
+
.option('--agent-count <n>', 'Agent count signal for method recommendation')
|
|
1284
|
+
.option('--decision-uncertainty <level>', 'Decision uncertainty signal: low | medium | high')
|
|
1285
|
+
.option('--operator-interface <shape>', 'Operator interface signal: direct | delegated (delegated recommends coordinator-loop)')
|
|
1286
|
+
.option('--plan <id>', 'Existing implementation-plan id; this is the underlying sprint id')
|
|
1287
|
+
.option('--checkpoint-start', 'Seed mission.policy.checkpoint.start=true and surface a checkpoint create suggested action; start remains read-only')
|
|
1288
|
+
.option('--checkpoint-auto-between-phases', 'Seed mission.policy.checkpoint.autoBetweenPhases=true and surface phase-transition checkpoint nudges')
|
|
1289
|
+
.option('--mission-resume-depth <depth>', 'Hosted mission resume depth: light | standard', 'light')
|
|
1290
|
+
.option('--mission-resume-limit <n>', 'Maximum refs/rows for the compact hosted mission resume pack', '8')
|
|
1291
|
+
.option('--mission-resume-full', 'Embed the full hosted mission resume skeleton instead of the compact summary')
|
|
1292
|
+
.option('--reminder', 'Build a bounded read-only session guidance reminder pack instead of the full kickoff prompt')
|
|
1293
|
+
.option('--area <area>', 'Area hint for reminder/session guidance experience ranking')
|
|
1294
|
+
.option('--limit <n>', 'Maximum experience briefs in session guidance; default 3, max 5')
|
|
1295
|
+
.option('--compact', 'Use compact JSON output with promptRef instead of inline prompt (default for --json)')
|
|
1296
|
+
.option('--full-output', 'With --json, include the full composed prompt and uncompact mission payload')
|
|
1297
|
+
.option('--root <path>', 'Workspace root override (default: nearest repo config ancestor)')
|
|
1298
|
+
.option('--out <file>', 'Write the composed prompt to a file')
|
|
1299
|
+
.option('--no-memory-brief', 'Do not build/embed the read-only local memory cache brief')
|
|
1300
|
+
.option('--no-interactive', 'Never prompt on a TTY; report missing answers instead')
|
|
1301
|
+
.option('--json', 'Output JSON only (implies --no-interactive)')
|
|
1302
|
+
.action(async (options) => {
|
|
1303
|
+
await runStart(options);
|
|
1304
|
+
})
|
|
1305
|
+
.addHelpText('after', `
|
|
1306
|
+
The composed prompt is the hosted "AOPS Collaborative Startup" starter
|
|
1307
|
+
(mirror: ${START_PROMPT_MIRROR_PATH}) with its operator
|
|
1308
|
+
placeholders filled in. It boots the aops-collaborative-work skill and embeds a
|
|
1309
|
+
read-only local memory cache brief pack for startup context unless
|
|
1310
|
+
--no-memory-brief is set.
|
|
1311
|
+
|
|
1312
|
+
Examples:
|
|
1313
|
+
aops-cli start # interactive (operator at a TTY)
|
|
1314
|
+
aops-cli start --json # agent interview: questions + missing answers
|
|
1315
|
+
aops-cli start --mode solo --board ops --json # compact JSON; read result.promptRef
|
|
1316
|
+
aops-cli start --mode solo --board ops --out tmp/start.md --json
|
|
1317
|
+
aops-cli start --mode solo --board ops --full-output --json
|
|
1318
|
+
aops-cli start --reminder --task "continue S1" --area aops-cli --json
|
|
1319
|
+
aops-cli start --mode chat-room --board ops --discipline build-review-chat --json
|
|
1320
|
+
aops-cli start --resume <mission-id> --mode solo --board ops --json
|
|
1321
|
+
aops-cli start --mode chat-room --board ops \\
|
|
1322
|
+
--room-slug ops-room --room-title "Ops" \\
|
|
1323
|
+
--roles "claude=implementer, codex=reviewer, mzs=operator" --json
|
|
1324
|
+
|
|
1325
|
+
Agent flow:
|
|
1326
|
+
1. Run \`aops-cli start --json\` and read result.missing.
|
|
1327
|
+
2. Answer what you already know from context; ask the operator only the
|
|
1328
|
+
items marked askOperator (roles are always operator-assigned).
|
|
1329
|
+
3. Re-run with the answers as flags until result.status is "ready".
|
|
1330
|
+
4. Default --json is compact: follow result.promptRef.path when --out is used,
|
|
1331
|
+
or re-run with --full-output only when an inline prompt is required. Use
|
|
1332
|
+
result.memoryBrief as the read-only startup pack when present; write
|
|
1333
|
+
\`aops-cli checkpoint create --summary "<summary>" --apply --json\` only
|
|
1334
|
+
after meaningful progress.
|
|
1335
|
+
`);
|
|
1336
|
+
applyCommonOptions(cmd, { withProject: true });
|
|
1337
|
+
cmd.option('--scope-id <id>', 'Canonical owner scope override for hosted mission resume');
|
|
1338
|
+
cmd.option('--scope-resolution <mode>', 'Scope resolution for hosted reads: explicit | cascade');
|
|
1339
|
+
cmd.option('--tenant-id <id>', 'Tenant id header (x-tenant-id)');
|
|
1340
|
+
cmd.option('--locale <locale>', 'Locale header (x-locale)');
|
|
1341
|
+
cmd.option('--fallback-locale <locale>', 'Fallback locale header (x-fallback-locale)');
|
|
1342
|
+
return cmd;
|
|
1343
|
+
}
|