@tiangong-ai/cli 0.0.34 → 0.0.36
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 +18 -6
- package/README.md +147 -2
- package/dist/research/orchestration.js +651 -24
- package/dist/research/orchestration.js.map +1 -1
- package/dist/research/workspace/audit-bundle.d.ts +44 -0
- package/dist/research/workspace/audit-bundle.js +357 -0
- package/dist/research/workspace/audit-bundle.js.map +1 -0
- package/dist/research/workspace/input-plan.js +27 -1
- package/dist/research/workspace/input-plan.js.map +1 -1
- package/dist/research/workspace/preflight.d.ts +34 -2
- package/dist/research/workspace/preflight.js +145 -1
- package/dist/research/workspace/preflight.js.map +1 -1
- package/dist/research/workspace/projects.d.ts +32 -5
- package/dist/research/workspace/projects.js +263 -7
- package/dist/research/workspace/projects.js.map +1 -1
- package/dist/research/workspace/publication-workflow.d.ts +146 -0
- package/dist/research/workspace/publication-workflow.js +994 -0
- package/dist/research/workspace/publication-workflow.js.map +1 -0
- package/dist/research/workspace/publication.d.ts +110 -0
- package/dist/research/workspace/publication.js +246 -0
- package/dist/research/workspace/publication.js.map +1 -0
- package/dist/research/workspace/research-policy-wizard.d.ts +40 -0
- package/dist/research/workspace/research-policy-wizard.js +167 -0
- package/dist/research/workspace/research-policy-wizard.js.map +1 -0
- package/dist/research/workspace/research-policy.d.ts +70 -0
- package/dist/research/workspace/research-policy.js +886 -0
- package/dist/research/workspace/research-policy.js.map +1 -0
- package/dist/research/workspace/runtime.d.ts +17 -0
- package/dist/research/workspace/runtime.js +84 -2
- package/dist/research/workspace/runtime.js.map +1 -1
- package/dist/research/workspace/sanitization.js +9 -3
- package/dist/research/workspace/sanitization.js.map +1 -1
- package/dist/research/workspace/scientific-design.d.ts +336 -0
- package/dist/research/workspace/scientific-design.js +1845 -0
- package/dist/research/workspace/scientific-design.js.map +1 -0
- package/dist/research/workspace/scientific-review.d.ts +101 -0
- package/dist/research/workspace/scientific-review.js +1167 -0
- package/dist/research/workspace/scientific-review.js.map +1 -0
- package/dist/research/workspace/setup-catalog.js +2 -2
- package/dist/research/workspace/setup-wizard.d.ts +18 -1
- package/dist/research/workspace/setup-wizard.js +1 -1
- package/dist/research/workspace/setup-wizard.js.map +1 -1
- package/dist/research/workspace/setup.d.ts +1 -0
- package/dist/research/workspace/setup.js +64 -0
- package/dist/research/workspace/setup.js.map +1 -1
- package/dist/research/workspace/types.d.ts +58 -0
- package/dist/research/workspace/workspace.js +25 -7
- package/dist/research/workspace/workspace.js.map +1 -1
- package/package.json +4 -2
|
@@ -11,6 +11,7 @@ import { configureExternalSkillProfile, doctorExternalCapabilities, EXTERNAL_SKI
|
|
|
11
11
|
import { appendJournalEvent } from "./workspace/journal.js";
|
|
12
12
|
import { fetchNativeCandidateSource } from "./workspace/broker.js";
|
|
13
13
|
import { registerEvidenceArtifact } from "./workspace/artifacts.js";
|
|
14
|
+
import { exportProjectAuditBundle, verifyProjectAuditBundle } from "./workspace/audit-bundle.js";
|
|
14
15
|
import { loadCurrentEvidenceSnapshot } from "./workspace/acquisition.js";
|
|
15
16
|
import { inspectDiscoveryProgress } from "./workspace/discovery-status.js";
|
|
16
17
|
import { recordDiscoveryAssessmentBatch } from "./workspace/discovery.js";
|
|
@@ -20,8 +21,13 @@ import { recordNativeResearchActivity } from "./workspace/native-activity.js";
|
|
|
20
21
|
import { readAndVerifyProjectInputPlan } from "./workspace/input-plan.js";
|
|
21
22
|
import { addProjectInput, createProjectAddendum, initializeProject, forkProject, listProjects, loadProject, nextReadyPackage, normalizeEvidenceRequirements, refreshProject, retryProjectPackage, setProjectDisposition, } from "./workspace/projects.js";
|
|
22
23
|
import { evaluateProjectPreflight } from "./workspace/preflight.js";
|
|
24
|
+
import { closePublication, freezePublicationManuscript, inspectPublicationStatus, preparePublicationReview, publicationAssessmentSchema, publicationReviewSchema, submitPublicationReview, } from "./workspace/publication-workflow.js";
|
|
25
|
+
import { approveResearchPolicy, initializeResearchPolicy, inspectResearchPolicyCatalog, inspectResearchPolicyStatus, loadApprovedResearchPolicy, } from "./workspace/research-policy.js";
|
|
26
|
+
import { resolveInstalledResearchPolicySource, runInteractiveResearchPolicyWizard, } from "./workspace/research-policy-wizard.js";
|
|
23
27
|
import { abortNativeResearchStage, inspectNativeResearchStage, prepareNativeResearchStage, requestResearchHandoff, resolveResearchHandoff, runResearchWorkspace, submitNativeResearchStage, } from "./workspace/runtime.js";
|
|
24
28
|
import { schemaForStage } from "./workspace/schemas.js";
|
|
29
|
+
import { readAndVerifyScientificDesign, scientificDesignSchema, } from "./workspace/scientific-design.js";
|
|
30
|
+
import { inspectScientificReviewStatus, prepareScientificReview, scientificGateAssessmentSchema, scientificReviewSchema, submitScientificReview, } from "./workspace/scientific-review.js";
|
|
25
31
|
import { pathExists, sha256Text, workspacePaths } from "./workspace/storage.js";
|
|
26
32
|
import { doctorResearchWorkspace, initializeResearchWorkspace, requireResearchWorkspace, withWorkspaceLock, } from "./workspace/workspace.js";
|
|
27
33
|
const COMMON_OPTIONS = { help: "boolean", json: "boolean" };
|
|
@@ -35,6 +41,10 @@ export async function runResearchOrchestrationCommand(subcommand, argv, io) {
|
|
|
35
41
|
return runWorkspace(argv, io);
|
|
36
42
|
if (subcommand === "capability")
|
|
37
43
|
return runCapability(argv, io);
|
|
44
|
+
if (subcommand === "policy")
|
|
45
|
+
return runPolicy(argv, io);
|
|
46
|
+
if (subcommand === "publication")
|
|
47
|
+
return runPublication(argv, io);
|
|
38
48
|
if (subcommand === "project")
|
|
39
49
|
return runProject(argv, io);
|
|
40
50
|
if (subcommand === "schema")
|
|
@@ -59,16 +69,33 @@ export function researchOrchestrationHelp() {
|
|
|
59
69
|
tiangong-ai research capability credential set --id <logical-id> --from-env <name> [--workspace <absolute-path>] [--json]
|
|
60
70
|
tiangong-ai research capability lock [--workspace <absolute-path>] [--json]
|
|
61
71
|
tiangong-ai research capability verify [--workspace <absolute-path>] [--json]
|
|
62
|
-
tiangong-ai research
|
|
63
|
-
tiangong-ai research
|
|
64
|
-
tiangong-ai research
|
|
72
|
+
tiangong-ai research policy wizard <project-id> [--workspace <path>] [--json]
|
|
73
|
+
tiangong-ai research policy catalog [--source-root <absolute-installed-skill-root>] [--workspace <path>] [--json]
|
|
74
|
+
tiangong-ai research policy init <project-id> [--source-root <absolute-installed-skill-root>] --article-type <id> --field <id> --journal-class <id> [--include-exact-journal-template] [--workspace <path>] [--json]
|
|
75
|
+
tiangong-ai research policy status <project-id> [--workspace <path>] [--json]
|
|
76
|
+
tiangong-ai research policy validate <project-id> [--workspace <path>] [--json]
|
|
77
|
+
tiangong-ai research policy approve <project-id> --confirm [--acknowledge-defaults] [--workspace <path>] [--json]
|
|
78
|
+
tiangong-ai research policy resolve <project-id> [--workspace <path>] [--json]
|
|
79
|
+
tiangong-ai research publication freeze <project-id> --manuscript <absolute-file> --assessment <absolute-json> --producer-agent codex|claude --producer-session <opaque-id> [--supplements <absolute-json-array>] [--workspace <path>] [--json]
|
|
80
|
+
tiangong-ai research publication review prepare <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --reviewer-agent codex|claude --reviewer-session <opaque-id> [--workspace <path>] [--json]
|
|
81
|
+
tiangong-ai research publication review submit <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --review <absolute-json> [--workspace <path>] [--json]
|
|
82
|
+
tiangong-ai research publication status <project-id> [--workspace <path>] [--json]
|
|
83
|
+
tiangong-ai research publication close <project-id> [--workspace <path>] [--json]
|
|
84
|
+
tiangong-ai research project init <project-id> --question <question> [--goal evidence-report|top-journal] [--design <absolute-json> --design-producer-agent codex|claude --design-producer-session <opaque-id>] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--confirm-budget] [--workspace <path>] [--json]
|
|
85
|
+
tiangong-ai research project preflight --question <question> [--goal evidence-report|top-journal] [--policy-project <project-id> --design <absolute-json>] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--workspace <path>] [--json]
|
|
86
|
+
tiangong-ai research project input add <project-id> --path <absolute-file> [--role primary|reference|replication] [--trust-status verified-owner-input|unverified-owner-input|reference-only|replication-candidate] [--independently-reproduced] [--workspace <path>] [--json]
|
|
65
87
|
tiangong-ai research project retry <project-id> [--package <package-id>] [--workspace <path>] [--json]
|
|
66
|
-
tiangong-ai research project fork <source-project-id> --to <target-project-id> [--resume-through discover|acquire|analyze|synthesize] [--workspace <path>] [--json]
|
|
67
|
-
tiangong-ai research project addendum <closed-project-id> --to <target-project-id> [--workspace <path>] [--json]
|
|
88
|
+
tiangong-ai research project fork <source-project-id> --to <target-project-id> [--resume-through discover|acquire|analyze|synthesize] [--design <absolute-json> --design-producer-agent codex|claude --design-producer-session <opaque-id>] [--workspace <path>] [--json]
|
|
89
|
+
tiangong-ai research project addendum <closed-project-id> --to <target-project-id> [--design <absolute-json> --design-producer-agent codex|claude --design-producer-session <opaque-id>] [--workspace <path>] [--json]
|
|
68
90
|
tiangong-ai research project archive <project-id> --reason <text> [--workspace <path>] [--json]
|
|
69
91
|
tiangong-ai research project abandon <project-id> --reason <text> [--workspace <path>] [--json]
|
|
70
92
|
tiangong-ai research project handoff request <project-id> --record <absolute-json> [--workspace <path>] [--json]
|
|
71
93
|
tiangong-ai research project handoff resolve <project-id> --note <text> [--workspace <path>] [--json]
|
|
94
|
+
tiangong-ai research project scientific review prepare <project-id> --role research-design|evidence-construct|pilot-methods --assessment <absolute-json> --reviewer-agent codex|claude --reviewer-session <opaque-id> [--workspace <path>] [--json]
|
|
95
|
+
tiangong-ai research project scientific review submit <project-id> --role research-design|evidence-construct|pilot-methods --review <absolute-json> [--workspace <path>] [--json]
|
|
96
|
+
tiangong-ai research project scientific status <project-id> [--workspace <path>] [--json]
|
|
97
|
+
tiangong-ai research project audit export <project-id> --output <absolute-new-directory> [--workspace <path>] [--json]
|
|
98
|
+
tiangong-ai research project audit verify --bundle <absolute-directory> [--json]
|
|
72
99
|
tiangong-ai research project stage prepare <project-id> --stage discover|acquire|analyze|synthesize --host-agent codex|claude [--workspace <path>] [--json]
|
|
73
100
|
tiangong-ai research project stage submit <project-id> --session <id> --output <absolute-json> [--confirm-model <id>] [--workspace <path>] [--json]
|
|
74
101
|
tiangong-ai research project stage abort <project-id> --session <id> [--workspace <path>] [--json]
|
|
@@ -78,37 +105,280 @@ export function researchOrchestrationHelp() {
|
|
|
78
105
|
tiangong-ai research project evidence assessment record <project-id> --record <absolute-json> [--workspace <path>] [--json]
|
|
79
106
|
tiangong-ai research project evidence download bind <project-id> --candidate <id> --record <absolute-json> [--workspace <path>] [--json]
|
|
80
107
|
tiangong-ai research project evidence artifact register <project-id> --candidate <id> --path <absolute-file> [--download-binding <id> | --derived-from-artifact <id>] [--media-type <type>] [--source-url <https-url>] [--license <declared-license>] [--license-url <https-url>] [--host-type <type>] [--article-version <version>] [--workspace <path>] [--json]
|
|
81
|
-
tiangong-ai research schema show <discover|acquire|analyze|synthesize|review|doctor> [--json]
|
|
108
|
+
tiangong-ai research schema show <discover|acquire|analyze|synthesize|review|doctor|scientific-design|scientific-assessment-research-design|scientific-assessment-evidence-construct|scientific-assessment-pilot-methods|scientific-review-research-design|scientific-review-evidence-construct|scientific-review-pilot-methods|publication-assessment|publication-review-evidence|publication-review-methods-reproducibility|publication-review-domain-novelty|publication-review-journal-editor> [--compatibility claude-code] [--json]
|
|
82
109
|
tiangong-ai research status [--project <project-id>] [--all] [--workspace <absolute-path>] [--json]
|
|
83
110
|
tiangong-ai research run [--project <project-id>] [--max-parallel <1-8>] [--max-cycles <1-100>] [--dry-run] [--progress-jsonl] [--workspace <absolute-path>] [--json]
|
|
84
111
|
|
|
85
112
|
${researchSetupHelp()}
|
|
86
113
|
`;
|
|
87
114
|
}
|
|
115
|
+
async function runPublication(argv, io) {
|
|
116
|
+
const [action, ...rest] = argv;
|
|
117
|
+
if (!action || action === "--help" || action === "-h")
|
|
118
|
+
return writeHelp(io);
|
|
119
|
+
if (action === "freeze") {
|
|
120
|
+
const args = parseStrictArgs(rest, {
|
|
121
|
+
...WORKSPACE_OPTIONS,
|
|
122
|
+
manuscript: "string",
|
|
123
|
+
assessment: "string",
|
|
124
|
+
supplements: "string",
|
|
125
|
+
"producer-agent": "string",
|
|
126
|
+
"producer-session": "string",
|
|
127
|
+
}, "research publication freeze");
|
|
128
|
+
if (strictBoolean(args, "help"))
|
|
129
|
+
return writeHelp(io);
|
|
130
|
+
const projectId = onePositional(args.positionals, "research publication freeze");
|
|
131
|
+
const manuscriptPath = strictString(args, "manuscript");
|
|
132
|
+
const assessmentPath = strictString(args, "assessment");
|
|
133
|
+
const producerSessionId = strictString(args, "producer-session");
|
|
134
|
+
if (!manuscriptPath || !assessmentPath || !producerSessionId) {
|
|
135
|
+
throw new CliError("research publication freeze requires --manuscript, --assessment, --producer-agent, and --producer-session.", { code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED", exitCode: 2 });
|
|
136
|
+
}
|
|
137
|
+
const root = await workspaceFromArgs(args);
|
|
138
|
+
const supplementsPath = strictString(args, "supplements");
|
|
139
|
+
writeJson(io, await freezePublicationManuscript({
|
|
140
|
+
root,
|
|
141
|
+
projectId,
|
|
142
|
+
manuscriptPath,
|
|
143
|
+
assessmentPath,
|
|
144
|
+
supplementPaths: supplementsPath ? await readAbsolutePathArray(supplementsPath) : [],
|
|
145
|
+
producerAgent: publicationAgent(strictString(args, "producer-agent"), "producer"),
|
|
146
|
+
producerSessionId,
|
|
147
|
+
}), args);
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
if (action === "review") {
|
|
151
|
+
const [reviewAction, ...reviewRest] = rest;
|
|
152
|
+
if (reviewAction === "--help" || reviewAction === "-h")
|
|
153
|
+
return writeHelp(io);
|
|
154
|
+
if (reviewAction === "prepare") {
|
|
155
|
+
const args = parseStrictArgs(reviewRest, {
|
|
156
|
+
...WORKSPACE_OPTIONS,
|
|
157
|
+
role: "string",
|
|
158
|
+
"reviewer-agent": "string",
|
|
159
|
+
"reviewer-session": "string",
|
|
160
|
+
}, "research publication review prepare");
|
|
161
|
+
if (strictBoolean(args, "help"))
|
|
162
|
+
return writeHelp(io);
|
|
163
|
+
const projectId = onePositional(args.positionals, "research publication review prepare");
|
|
164
|
+
const reviewerSessionId = strictString(args, "reviewer-session");
|
|
165
|
+
if (!reviewerSessionId) {
|
|
166
|
+
throw new CliError("publication review prepare requires --reviewer-session.", {
|
|
167
|
+
code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED",
|
|
168
|
+
exitCode: 2,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const root = await workspaceFromArgs(args);
|
|
172
|
+
writeJson(io, await preparePublicationReview({
|
|
173
|
+
root,
|
|
174
|
+
projectId,
|
|
175
|
+
role: publicationReviewRole(strictString(args, "role")),
|
|
176
|
+
reviewerAgent: publicationAgent(strictString(args, "reviewer-agent"), "reviewer"),
|
|
177
|
+
reviewerSessionId,
|
|
178
|
+
}), args);
|
|
179
|
+
return 0;
|
|
180
|
+
}
|
|
181
|
+
if (reviewAction === "submit") {
|
|
182
|
+
const args = parseStrictArgs(reviewRest, { ...WORKSPACE_OPTIONS, role: "string", review: "string" }, "research publication review submit");
|
|
183
|
+
if (strictBoolean(args, "help"))
|
|
184
|
+
return writeHelp(io);
|
|
185
|
+
const projectId = onePositional(args.positionals, "research publication review submit");
|
|
186
|
+
const reviewPath = strictString(args, "review");
|
|
187
|
+
if (!reviewPath) {
|
|
188
|
+
throw new CliError("publication review submit requires --review.", {
|
|
189
|
+
code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED",
|
|
190
|
+
exitCode: 2,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
const root = await workspaceFromArgs(args);
|
|
194
|
+
writeJson(io, await submitPublicationReview({
|
|
195
|
+
root,
|
|
196
|
+
projectId,
|
|
197
|
+
role: publicationReviewRole(strictString(args, "role")),
|
|
198
|
+
reviewPath,
|
|
199
|
+
}), args);
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
throw unknownAction("research publication review", reviewAction ?? "missing");
|
|
203
|
+
}
|
|
204
|
+
if (action === "status" || action === "close") {
|
|
205
|
+
const args = parseStrictArgs(rest, WORKSPACE_OPTIONS, `research publication ${action}`);
|
|
206
|
+
if (strictBoolean(args, "help"))
|
|
207
|
+
return writeHelp(io);
|
|
208
|
+
const projectId = onePositional(args.positionals, `research publication ${action}`);
|
|
209
|
+
const root = await workspaceFromArgs(args);
|
|
210
|
+
writeJson(io, action === "status"
|
|
211
|
+
? await inspectPublicationStatus(root, projectId)
|
|
212
|
+
: await closePublication(root, projectId), args);
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
throw unknownAction("research publication", action);
|
|
216
|
+
}
|
|
217
|
+
async function runPolicy(argv, io) {
|
|
218
|
+
const [action, ...rest] = argv;
|
|
219
|
+
if (!action || action === "--help" || action === "-h")
|
|
220
|
+
return writeHelp(io);
|
|
221
|
+
if (action === "wizard") {
|
|
222
|
+
const args = parseStrictArgs(rest, WORKSPACE_OPTIONS, "research policy wizard");
|
|
223
|
+
if (strictBoolean(args, "help"))
|
|
224
|
+
return writeHelp(io);
|
|
225
|
+
const projectId = onePositional(args.positionals, "research policy wizard");
|
|
226
|
+
const root = await workspaceFromArgs(args);
|
|
227
|
+
return runInteractiveResearchPolicyWizard({
|
|
228
|
+
root,
|
|
229
|
+
projectId,
|
|
230
|
+
io,
|
|
231
|
+
json: strictBoolean(args, "json"),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (action === "catalog") {
|
|
235
|
+
const args = parseStrictArgs(rest, { ...WORKSPACE_OPTIONS, "source-root": "string" }, "research policy catalog");
|
|
236
|
+
if (strictBoolean(args, "help"))
|
|
237
|
+
return writeHelp(io);
|
|
238
|
+
rejectPositionals(args.positionals, "research policy catalog");
|
|
239
|
+
const explicitSourceRoot = strictString(args, "source-root");
|
|
240
|
+
const sourceRoot = explicitSourceRoot ??
|
|
241
|
+
(await resolveInstalledResearchPolicySource(await workspaceFromArgs(args), io.env));
|
|
242
|
+
writeJson(io, await inspectResearchPolicyCatalog(sourceRoot), args);
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
if (action === "init") {
|
|
246
|
+
const args = parseStrictArgs(rest, {
|
|
247
|
+
...WORKSPACE_OPTIONS,
|
|
248
|
+
"source-root": "string",
|
|
249
|
+
"article-type": "string",
|
|
250
|
+
field: "string",
|
|
251
|
+
"journal-class": "string",
|
|
252
|
+
"include-exact-journal-template": "boolean",
|
|
253
|
+
}, "research policy init");
|
|
254
|
+
if (strictBoolean(args, "help"))
|
|
255
|
+
return writeHelp(io);
|
|
256
|
+
const projectId = onePositional(args.positionals, "research policy init");
|
|
257
|
+
const articleType = strictString(args, "article-type");
|
|
258
|
+
const field = strictString(args, "field");
|
|
259
|
+
const journalClass = strictString(args, "journal-class");
|
|
260
|
+
if (!articleType || !field || !journalClass) {
|
|
261
|
+
throw new CliError("research policy init requires --article-type, --field, and --journal-class.", { code: "RESEARCH_POLICY_INVALID", exitCode: 2 });
|
|
262
|
+
}
|
|
263
|
+
const root = await workspaceFromArgs(args);
|
|
264
|
+
const sourceRoot = strictString(args, "source-root") ??
|
|
265
|
+
(await resolveInstalledResearchPolicySource(root, io.env));
|
|
266
|
+
writeJson(io, await initializeResearchPolicy({
|
|
267
|
+
root,
|
|
268
|
+
projectId,
|
|
269
|
+
sourceRoot,
|
|
270
|
+
articleType,
|
|
271
|
+
field,
|
|
272
|
+
journalClass,
|
|
273
|
+
includeExactJournalTemplate: strictBoolean(args, "include-exact-journal-template"),
|
|
274
|
+
}), args);
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
if (["status", "validate", "approve", "resolve"].includes(action)) {
|
|
278
|
+
const args = parseStrictArgs(rest, {
|
|
279
|
+
...WORKSPACE_OPTIONS,
|
|
280
|
+
confirm: "boolean",
|
|
281
|
+
"acknowledge-defaults": "boolean",
|
|
282
|
+
}, `research policy ${action}`);
|
|
283
|
+
if (strictBoolean(args, "help"))
|
|
284
|
+
return writeHelp(io);
|
|
285
|
+
const projectId = onePositional(args.positionals, `research policy ${action}`);
|
|
286
|
+
const root = await workspaceFromArgs(args);
|
|
287
|
+
if (action === "approve") {
|
|
288
|
+
const result = await approveResearchPolicy(root, projectId, {
|
|
289
|
+
confirm: strictBoolean(args, "confirm"),
|
|
290
|
+
acknowledgeDefaults: strictBoolean(args, "acknowledge-defaults"),
|
|
291
|
+
});
|
|
292
|
+
writeJson(io, result, args);
|
|
293
|
+
return 0;
|
|
294
|
+
}
|
|
295
|
+
if (action === "resolve") {
|
|
296
|
+
writeJson(io, await loadApprovedResearchPolicy(root, projectId), args);
|
|
297
|
+
return 0;
|
|
298
|
+
}
|
|
299
|
+
const result = await inspectResearchPolicyStatus(root, projectId);
|
|
300
|
+
writeJson(io, result, args);
|
|
301
|
+
return action === "validate" &&
|
|
302
|
+
["missing", "invalid", "changed", "stale"].includes(result.status)
|
|
303
|
+
? 3
|
|
304
|
+
: 0;
|
|
305
|
+
}
|
|
306
|
+
throw unknownAction("research policy", action);
|
|
307
|
+
}
|
|
88
308
|
async function runSchema(argv, io) {
|
|
89
309
|
const [action, ...rest] = argv;
|
|
90
310
|
if (!action || action === "--help" || action === "-h")
|
|
91
311
|
return writeHelp(io);
|
|
92
312
|
if (action !== "show")
|
|
93
313
|
throw unknownAction("research schema", action);
|
|
94
|
-
const args = parseStrictArgs(rest, COMMON_OPTIONS, "research schema show");
|
|
314
|
+
const args = parseStrictArgs(rest, { ...COMMON_OPTIONS, compatibility: "string" }, "research schema show");
|
|
95
315
|
if (strictBoolean(args, "help"))
|
|
96
316
|
return writeHelp(io);
|
|
97
317
|
const stage = onePositional(args.positionals, "research schema show");
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
318
|
+
let schema;
|
|
319
|
+
if (stage === "scientific-design") {
|
|
320
|
+
schema = scientificDesignSchema();
|
|
321
|
+
}
|
|
322
|
+
else if (stage.startsWith("scientific-assessment-")) {
|
|
323
|
+
const role = scientificReviewRole(stage.slice("scientific-assessment-".length));
|
|
324
|
+
schema = scientificGateAssessmentSchema(role);
|
|
325
|
+
}
|
|
326
|
+
else if (stage.startsWith("scientific-review-")) {
|
|
327
|
+
const role = scientificReviewRole(stage.slice("scientific-review-".length));
|
|
328
|
+
schema = scientificReviewSchema(role);
|
|
329
|
+
}
|
|
330
|
+
else if (stage === "publication-assessment") {
|
|
331
|
+
schema = publicationAssessmentSchema();
|
|
332
|
+
}
|
|
333
|
+
else if (stage.startsWith("publication-review-")) {
|
|
334
|
+
const role = publicationReviewRole(stage.slice("publication-review-".length));
|
|
335
|
+
schema = publicationReviewSchema(role);
|
|
336
|
+
}
|
|
337
|
+
else if (stage === "discover" ||
|
|
338
|
+
stage === "acquire" ||
|
|
339
|
+
stage === "analyze" ||
|
|
340
|
+
stage === "synthesize" ||
|
|
341
|
+
stage === "review" ||
|
|
342
|
+
stage === "doctor") {
|
|
343
|
+
schema = schemaForStage(stage);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
104
346
|
throw new CliError(`Unsupported research schema stage: ${stage}`, {
|
|
105
347
|
code: "RESEARCH_SCHEMA_INVALID",
|
|
106
348
|
exitCode: 2,
|
|
107
349
|
});
|
|
108
350
|
}
|
|
109
|
-
writeJson(io,
|
|
351
|
+
writeJson(io, compatibleSchema(schema, strictString(args, "compatibility")), args);
|
|
110
352
|
return 0;
|
|
111
353
|
}
|
|
354
|
+
function compatibleSchema(schema, compatibility) {
|
|
355
|
+
if (!compatibility)
|
|
356
|
+
return schema;
|
|
357
|
+
if (compatibility !== "claude-code") {
|
|
358
|
+
throw new CliError(`Unsupported schema compatibility target: ${compatibility}`, {
|
|
359
|
+
code: "RESEARCH_SCHEMA_COMPATIBILITY_INVALID",
|
|
360
|
+
exitCode: 2,
|
|
361
|
+
details: { supported: ["claude-code"] },
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
const compatible = structuredClone(schema);
|
|
365
|
+
const stripMetadata = (value) => {
|
|
366
|
+
if (Array.isArray(value)) {
|
|
367
|
+
for (const item of value)
|
|
368
|
+
stripMetadata(item);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (!value || typeof value !== "object")
|
|
372
|
+
return;
|
|
373
|
+
const record = value;
|
|
374
|
+
delete record.$schema;
|
|
375
|
+
delete record.$id;
|
|
376
|
+
for (const item of Object.values(record))
|
|
377
|
+
stripMetadata(item);
|
|
378
|
+
};
|
|
379
|
+
stripMetadata(compatible);
|
|
380
|
+
return compatible;
|
|
381
|
+
}
|
|
112
382
|
async function runContext(argv, io) {
|
|
113
383
|
const [action, ...rest] = argv;
|
|
114
384
|
if (!action || action === "--help" || action === "-h")
|
|
@@ -299,6 +569,41 @@ async function runProject(argv, io) {
|
|
|
299
569
|
const [action, ...rest] = argv;
|
|
300
570
|
if (!action || action === "--help" || action === "-h")
|
|
301
571
|
return writeHelp(io);
|
|
572
|
+
if (action === "audit") {
|
|
573
|
+
const [auditAction, ...auditRest] = rest;
|
|
574
|
+
if (auditAction === "export") {
|
|
575
|
+
const args = parseStrictArgs(auditRest, { ...WORKSPACE_OPTIONS, output: "string" }, "research project audit export");
|
|
576
|
+
if (strictBoolean(args, "help"))
|
|
577
|
+
return writeHelp(io);
|
|
578
|
+
const projectId = onePositional(args.positionals, "research project audit export");
|
|
579
|
+
const destination = strictString(args, "output");
|
|
580
|
+
if (!destination) {
|
|
581
|
+
throw new CliError("Audit export requires --output.", {
|
|
582
|
+
code: "RESEARCH_AUDIT_BUNDLE_PATH_INVALID",
|
|
583
|
+
exitCode: 2,
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
const root = await workspaceFromArgs(args);
|
|
587
|
+
writeJson(io, await exportProjectAuditBundle({ root, projectId, destination }), args);
|
|
588
|
+
return 0;
|
|
589
|
+
}
|
|
590
|
+
if (auditAction === "verify") {
|
|
591
|
+
const args = parseStrictArgs(auditRest, { ...COMMON_OPTIONS, bundle: "string" }, "research project audit verify");
|
|
592
|
+
if (strictBoolean(args, "help"))
|
|
593
|
+
return writeHelp(io);
|
|
594
|
+
rejectPositionals(args.positionals, "research project audit verify");
|
|
595
|
+
const bundlePath = strictString(args, "bundle");
|
|
596
|
+
if (!bundlePath) {
|
|
597
|
+
throw new CliError("Audit verification requires --bundle.", {
|
|
598
|
+
code: "RESEARCH_AUDIT_BUNDLE_PATH_INVALID",
|
|
599
|
+
exitCode: 2,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
writeJson(io, await verifyProjectAuditBundle(bundlePath), args);
|
|
603
|
+
return 0;
|
|
604
|
+
}
|
|
605
|
+
throw unknownAction("research project audit", auditAction ?? "");
|
|
606
|
+
}
|
|
302
607
|
if (action === "handoff") {
|
|
303
608
|
const [handoffAction, ...handoffRest] = rest;
|
|
304
609
|
if (handoffAction === "request") {
|
|
@@ -338,6 +643,71 @@ async function runProject(argv, io) {
|
|
|
338
643
|
}
|
|
339
644
|
throw unknownAction("research project handoff", handoffAction ?? "");
|
|
340
645
|
}
|
|
646
|
+
if (action === "scientific") {
|
|
647
|
+
const [scientificAction, ...scientificRest] = rest;
|
|
648
|
+
if (scientificAction === "status") {
|
|
649
|
+
const args = parseStrictArgs(scientificRest, WORKSPACE_OPTIONS, "research project scientific status");
|
|
650
|
+
if (strictBoolean(args, "help"))
|
|
651
|
+
return writeHelp(io);
|
|
652
|
+
const projectId = onePositional(args.positionals, "research project scientific status");
|
|
653
|
+
const root = await workspaceFromArgs(args);
|
|
654
|
+
writeJson(io, await inspectScientificReviewStatus(root, projectId), args);
|
|
655
|
+
return 0;
|
|
656
|
+
}
|
|
657
|
+
if (scientificAction !== "review") {
|
|
658
|
+
throw unknownAction("research project scientific", scientificAction ?? "");
|
|
659
|
+
}
|
|
660
|
+
const [reviewAction, ...reviewRest] = scientificRest;
|
|
661
|
+
if (reviewAction === "prepare") {
|
|
662
|
+
const args = parseStrictArgs(reviewRest, {
|
|
663
|
+
...WORKSPACE_OPTIONS,
|
|
664
|
+
role: "string",
|
|
665
|
+
assessment: "string",
|
|
666
|
+
"reviewer-agent": "string",
|
|
667
|
+
"reviewer-session": "string",
|
|
668
|
+
}, "research project scientific review prepare");
|
|
669
|
+
if (strictBoolean(args, "help"))
|
|
670
|
+
return writeHelp(io);
|
|
671
|
+
const projectId = onePositional(args.positionals, "research project scientific review prepare");
|
|
672
|
+
const assessmentPath = strictString(args, "assessment");
|
|
673
|
+
const reviewerSessionId = strictString(args, "reviewer-session");
|
|
674
|
+
if (!assessmentPath || !reviewerSessionId) {
|
|
675
|
+
throw new CliError("Scientific review prepare requires --assessment, --reviewer-agent, and --reviewer-session.", { code: "RESEARCH_SCIENTIFIC_REVIEW_ARGUMENT_REQUIRED", exitCode: 2 });
|
|
676
|
+
}
|
|
677
|
+
const root = await workspaceFromArgs(args);
|
|
678
|
+
writeJson(io, await prepareScientificReview({
|
|
679
|
+
root,
|
|
680
|
+
projectId,
|
|
681
|
+
role: scientificReviewRole(strictString(args, "role")),
|
|
682
|
+
assessmentPath,
|
|
683
|
+
reviewerAgent: publicationAgent(strictString(args, "reviewer-agent"), "reviewer"),
|
|
684
|
+
reviewerSessionId,
|
|
685
|
+
}), args);
|
|
686
|
+
return 0;
|
|
687
|
+
}
|
|
688
|
+
if (reviewAction === "submit") {
|
|
689
|
+
const args = parseStrictArgs(reviewRest, { ...WORKSPACE_OPTIONS, role: "string", review: "string" }, "research project scientific review submit");
|
|
690
|
+
if (strictBoolean(args, "help"))
|
|
691
|
+
return writeHelp(io);
|
|
692
|
+
const projectId = onePositional(args.positionals, "research project scientific review submit");
|
|
693
|
+
const reviewPath = strictString(args, "review");
|
|
694
|
+
if (!reviewPath) {
|
|
695
|
+
throw new CliError("Scientific review submit requires --review.", {
|
|
696
|
+
code: "RESEARCH_SCIENTIFIC_REVIEW_ARGUMENT_REQUIRED",
|
|
697
|
+
exitCode: 2,
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
const root = await workspaceFromArgs(args);
|
|
701
|
+
writeJson(io, await submitScientificReview({
|
|
702
|
+
root,
|
|
703
|
+
projectId,
|
|
704
|
+
role: scientificReviewRole(strictString(args, "role")),
|
|
705
|
+
reviewPath,
|
|
706
|
+
}), args);
|
|
707
|
+
return 0;
|
|
708
|
+
}
|
|
709
|
+
throw unknownAction("research project scientific review", reviewAction ?? "");
|
|
710
|
+
}
|
|
341
711
|
if (action === "stage") {
|
|
342
712
|
const [stageAction, ...stageRest] = rest;
|
|
343
713
|
if (stageAction === "prepare") {
|
|
@@ -576,8 +946,12 @@ async function runProject(argv, io) {
|
|
|
576
946
|
const args = parseStrictArgs(rest, {
|
|
577
947
|
...WORKSPACE_OPTIONS,
|
|
578
948
|
question: "string",
|
|
949
|
+
goal: "string",
|
|
579
950
|
requirements: "string",
|
|
580
951
|
"input-plan": "string",
|
|
952
|
+
design: "string",
|
|
953
|
+
"design-producer-agent": "string",
|
|
954
|
+
"design-producer-session": "string",
|
|
581
955
|
"confirm-budget": "boolean",
|
|
582
956
|
}, "research project init");
|
|
583
957
|
if (strictBoolean(args, "help"))
|
|
@@ -591,9 +965,29 @@ async function runProject(argv, io) {
|
|
|
591
965
|
});
|
|
592
966
|
}
|
|
593
967
|
const root = await workspaceFromArgs(args);
|
|
968
|
+
const goal = researchGoal(strictString(args, "goal"));
|
|
594
969
|
const requirementsPath = strictString(args, "requirements");
|
|
595
970
|
const inputPlanPath = strictString(args, "input-plan");
|
|
596
|
-
const
|
|
971
|
+
const publicationPolicy = goal === "top-journal" ? await loadApprovedResearchPolicy(root, projectId) : undefined;
|
|
972
|
+
const designPath = strictString(args, "design");
|
|
973
|
+
const designProducerAgent = strictString(args, "design-producer-agent");
|
|
974
|
+
const designProducerSession = strictString(args, "design-producer-session");
|
|
975
|
+
if (goal === "top-journal" && (!designPath || !designProducerAgent || !designProducerSession)) {
|
|
976
|
+
throw new CliError("Top-journal init requires --design, --design-producer-agent, and --design-producer-session.", { code: "RESEARCH_SCIENTIFIC_DESIGN_REQUIRED", exitCode: 2 });
|
|
977
|
+
}
|
|
978
|
+
if (goal !== "top-journal" && (designPath || designProducerAgent || designProducerSession)) {
|
|
979
|
+
throw new CliError("Scientific design options are only valid with --goal top-journal.", {
|
|
980
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_POLICY_REQUIRED",
|
|
981
|
+
exitCode: 2,
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
const project = await initializeProject(root, projectId, question, requirementsPath ? await readEvidenceRequirements(requirementsPath) : undefined, strictBoolean(args, "confirm-budget"), inputPlanPath ? await readAndVerifyProjectInputPlan(inputPlanPath) : undefined, publicationPolicy, designPath && designProducerAgent && designProducerSession
|
|
985
|
+
? {
|
|
986
|
+
design: await readAndVerifyScientificDesign(designPath, projectId),
|
|
987
|
+
producerAgent: publicationAgent(designProducerAgent, "design producer"),
|
|
988
|
+
producerSessionId: designProducerSession,
|
|
989
|
+
}
|
|
990
|
+
: undefined);
|
|
597
991
|
writeJson(io, project, args);
|
|
598
992
|
return 0;
|
|
599
993
|
}
|
|
@@ -601,8 +995,11 @@ async function runProject(argv, io) {
|
|
|
601
995
|
const args = parseStrictArgs(rest, {
|
|
602
996
|
...WORKSPACE_OPTIONS,
|
|
603
997
|
question: "string",
|
|
998
|
+
goal: "string",
|
|
999
|
+
"policy-project": "string",
|
|
604
1000
|
requirements: "string",
|
|
605
1001
|
"input-plan": "string",
|
|
1002
|
+
design: "string",
|
|
606
1003
|
}, "research project preflight");
|
|
607
1004
|
if (strictBoolean(args, "help"))
|
|
608
1005
|
return writeHelp(io);
|
|
@@ -615,11 +1012,48 @@ async function runProject(argv, io) {
|
|
|
615
1012
|
});
|
|
616
1013
|
}
|
|
617
1014
|
const root = await workspaceFromArgs(args);
|
|
1015
|
+
const goal = researchGoal(strictString(args, "goal"));
|
|
1016
|
+
const policyProject = strictString(args, "policy-project");
|
|
1017
|
+
let publicationPolicy = null;
|
|
1018
|
+
if (goal === "top-journal") {
|
|
1019
|
+
if (!policyProject) {
|
|
1020
|
+
throw new CliError("Top-journal preflight requires --policy-project.", {
|
|
1021
|
+
code: "RESEARCH_POLICY_REQUIRED",
|
|
1022
|
+
exitCode: 2,
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
publicationPolicy = await loadApprovedResearchPolicy(root, policyProject);
|
|
1026
|
+
}
|
|
1027
|
+
else if (policyProject) {
|
|
1028
|
+
throw new CliError("--policy-project is only valid with --goal top-journal.", {
|
|
1029
|
+
code: "RESEARCH_POLICY_INVALID",
|
|
1030
|
+
exitCode: 2,
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
618
1033
|
const requirementsPath = strictString(args, "requirements");
|
|
619
1034
|
const requirements = requirementsPath ? await readEvidenceRequirements(requirementsPath) : null;
|
|
620
1035
|
const inputPlanPath = strictString(args, "input-plan");
|
|
621
1036
|
const inputPlan = inputPlanPath ? await readAndVerifyProjectInputPlan(inputPlanPath) : null;
|
|
622
|
-
const
|
|
1037
|
+
const designPath = strictString(args, "design");
|
|
1038
|
+
if (goal === "top-journal" && !designPath) {
|
|
1039
|
+
throw new CliError("Top-journal preflight requires --design.", {
|
|
1040
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_REQUIRED",
|
|
1041
|
+
exitCode: 2,
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
if (goal !== "top-journal" && designPath) {
|
|
1045
|
+
throw new CliError("--design is only valid with --goal top-journal.", {
|
|
1046
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_POLICY_REQUIRED",
|
|
1047
|
+
exitCode: 2,
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
const scientificDesign = designPath
|
|
1051
|
+
? await readAndVerifyScientificDesign(designPath, policyProject)
|
|
1052
|
+
: null;
|
|
1053
|
+
const result = await evaluateProjectPreflight(root, question, requirements, inputPlan, {
|
|
1054
|
+
publicationPolicy,
|
|
1055
|
+
scientificDesign,
|
|
1056
|
+
});
|
|
623
1057
|
writeJson(io, result, args);
|
|
624
1058
|
return result.readyToInitialize ? 0 : 3;
|
|
625
1059
|
}
|
|
@@ -629,7 +1063,13 @@ async function runProject(argv, io) {
|
|
|
629
1063
|
return writeHelp(io);
|
|
630
1064
|
if (inputAction !== "add")
|
|
631
1065
|
throw unknownAction("research project input", inputAction ?? "");
|
|
632
|
-
const args = parseStrictArgs(inputRest, {
|
|
1066
|
+
const args = parseStrictArgs(inputRest, {
|
|
1067
|
+
...WORKSPACE_OPTIONS,
|
|
1068
|
+
path: "string",
|
|
1069
|
+
role: "string",
|
|
1070
|
+
"trust-status": "string",
|
|
1071
|
+
"independently-reproduced": "boolean",
|
|
1072
|
+
}, "research project input add");
|
|
633
1073
|
if (strictBoolean(args, "help"))
|
|
634
1074
|
return writeHelp(io);
|
|
635
1075
|
const projectId = onePositional(args.positionals, "research project input add");
|
|
@@ -642,7 +1082,11 @@ async function runProject(argv, io) {
|
|
|
642
1082
|
}
|
|
643
1083
|
const role = inputRole(strictString(args, "role"));
|
|
644
1084
|
const root = await workspaceFromArgs(args);
|
|
645
|
-
const
|
|
1085
|
+
const trustStatus = projectInputTrustStatus(strictString(args, "trust-status"), role);
|
|
1086
|
+
const input = await addProjectInput(root, projectId, inputPath, role, {
|
|
1087
|
+
...(trustStatus ? { trustStatus } : {}),
|
|
1088
|
+
independentlyReproduced: strictBoolean(args, "independently-reproduced"),
|
|
1089
|
+
});
|
|
646
1090
|
writeJson(io, input, args);
|
|
647
1091
|
return 0;
|
|
648
1092
|
}
|
|
@@ -657,7 +1101,14 @@ async function runProject(argv, io) {
|
|
|
657
1101
|
return 0;
|
|
658
1102
|
}
|
|
659
1103
|
if (action === "fork") {
|
|
660
|
-
const args = parseStrictArgs(rest, {
|
|
1104
|
+
const args = parseStrictArgs(rest, {
|
|
1105
|
+
...WORKSPACE_OPTIONS,
|
|
1106
|
+
to: "string",
|
|
1107
|
+
"resume-through": "string",
|
|
1108
|
+
design: "string",
|
|
1109
|
+
"design-producer-agent": "string",
|
|
1110
|
+
"design-producer-session": "string",
|
|
1111
|
+
}, "research project fork");
|
|
661
1112
|
if (strictBoolean(args, "help"))
|
|
662
1113
|
return writeHelp(io);
|
|
663
1114
|
const sourceProjectId = onePositional(args.positionals, "research project fork");
|
|
@@ -669,12 +1120,41 @@ async function runProject(argv, io) {
|
|
|
669
1120
|
});
|
|
670
1121
|
}
|
|
671
1122
|
const root = await workspaceFromArgs(args);
|
|
672
|
-
const
|
|
1123
|
+
const source = await loadProject(root, sourceProjectId);
|
|
1124
|
+
const designPath = strictString(args, "design");
|
|
1125
|
+
const designProducerAgent = strictString(args, "design-producer-agent");
|
|
1126
|
+
const designProducerSession = strictString(args, "design-producer-session");
|
|
1127
|
+
if (source.publicationPolicy &&
|
|
1128
|
+
(!designPath || !designProducerAgent || !designProducerSession)) {
|
|
1129
|
+
throw new CliError("A top-journal fork requires a target-specific approved policy plus --design, --design-producer-agent, and --design-producer-session.", { code: "RESEARCH_SCIENTIFIC_DESIGN_REAPPROVAL_REQUIRED", exitCode: 3 });
|
|
1130
|
+
}
|
|
1131
|
+
if (!source.publicationPolicy && (designPath || designProducerAgent || designProducerSession)) {
|
|
1132
|
+
throw new CliError("Scientific reapproval options require a top-journal source project.", {
|
|
1133
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_REAPPROVAL_INVALID",
|
|
1134
|
+
exitCode: 2,
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
const project = await forkProject(root, sourceProjectId, targetProjectId, resumeStage(strictString(args, "resume-through")), designPath && designProducerAgent && designProducerSession
|
|
1138
|
+
? {
|
|
1139
|
+
publicationPolicy: await loadApprovedResearchPolicy(root, targetProjectId),
|
|
1140
|
+
scientificDesign: {
|
|
1141
|
+
design: await readAndVerifyScientificDesign(designPath, targetProjectId),
|
|
1142
|
+
producerAgent: publicationAgent(designProducerAgent, "design producer"),
|
|
1143
|
+
producerSessionId: designProducerSession,
|
|
1144
|
+
},
|
|
1145
|
+
}
|
|
1146
|
+
: undefined);
|
|
673
1147
|
writeJson(io, project, args);
|
|
674
1148
|
return 0;
|
|
675
1149
|
}
|
|
676
1150
|
if (action === "addendum") {
|
|
677
|
-
const args = parseStrictArgs(rest, {
|
|
1151
|
+
const args = parseStrictArgs(rest, {
|
|
1152
|
+
...WORKSPACE_OPTIONS,
|
|
1153
|
+
to: "string",
|
|
1154
|
+
design: "string",
|
|
1155
|
+
"design-producer-agent": "string",
|
|
1156
|
+
"design-producer-session": "string",
|
|
1157
|
+
}, "research project addendum");
|
|
678
1158
|
if (strictBoolean(args, "help"))
|
|
679
1159
|
return writeHelp(io);
|
|
680
1160
|
const sourceProjectId = onePositional(args.positionals, "research project addendum");
|
|
@@ -686,7 +1166,30 @@ async function runProject(argv, io) {
|
|
|
686
1166
|
});
|
|
687
1167
|
}
|
|
688
1168
|
const root = await workspaceFromArgs(args);
|
|
689
|
-
const
|
|
1169
|
+
const source = await loadProject(root, sourceProjectId);
|
|
1170
|
+
const designPath = strictString(args, "design");
|
|
1171
|
+
const designProducerAgent = strictString(args, "design-producer-agent");
|
|
1172
|
+
const designProducerSession = strictString(args, "design-producer-session");
|
|
1173
|
+
if (source.publicationPolicy &&
|
|
1174
|
+
(!designPath || !designProducerAgent || !designProducerSession)) {
|
|
1175
|
+
throw new CliError("A top-journal addendum requires a target-specific approved policy plus --design, --design-producer-agent, and --design-producer-session.", { code: "RESEARCH_SCIENTIFIC_DESIGN_REAPPROVAL_REQUIRED", exitCode: 3 });
|
|
1176
|
+
}
|
|
1177
|
+
if (!source.publicationPolicy && (designPath || designProducerAgent || designProducerSession)) {
|
|
1178
|
+
throw new CliError("Scientific reapproval options require a top-journal source project.", {
|
|
1179
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_REAPPROVAL_INVALID",
|
|
1180
|
+
exitCode: 2,
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
const project = await createProjectAddendum(root, sourceProjectId, targetProjectId, designPath && designProducerAgent && designProducerSession
|
|
1184
|
+
? {
|
|
1185
|
+
publicationPolicy: await loadApprovedResearchPolicy(root, targetProjectId),
|
|
1186
|
+
scientificDesign: {
|
|
1187
|
+
design: await readAndVerifyScientificDesign(designPath, targetProjectId),
|
|
1188
|
+
producerAgent: publicationAgent(designProducerAgent, "design producer"),
|
|
1189
|
+
producerSessionId: designProducerSession,
|
|
1190
|
+
},
|
|
1191
|
+
}
|
|
1192
|
+
: undefined);
|
|
690
1193
|
writeJson(io, project, args);
|
|
691
1194
|
return 0;
|
|
692
1195
|
}
|
|
@@ -744,6 +1247,10 @@ async function runStatus(argv, io) {
|
|
|
744
1247
|
const nativeStage = await inspectNativeResearchStage(root, current);
|
|
745
1248
|
const snapshot = await inspectSnapshotForStatus(root, current.id);
|
|
746
1249
|
const readyPackage = nextReadyPackage(current)?.id ?? null;
|
|
1250
|
+
const scientificReview = await inspectScientificReviewStatus(root, current.id);
|
|
1251
|
+
const publication = current.publicationPolicy
|
|
1252
|
+
? await inspectPublicationForStatus(root, current.id)
|
|
1253
|
+
: null;
|
|
747
1254
|
return {
|
|
748
1255
|
id: current.id,
|
|
749
1256
|
question: current.question,
|
|
@@ -754,8 +1261,10 @@ async function runStatus(argv, io) {
|
|
|
754
1261
|
evidenceState: current.evidenceState,
|
|
755
1262
|
snapshot,
|
|
756
1263
|
nativeStage,
|
|
1264
|
+
scientificReview,
|
|
1265
|
+
publication,
|
|
757
1266
|
readyPackage,
|
|
758
|
-
recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage),
|
|
1267
|
+
recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage, scientificReview, publication),
|
|
759
1268
|
usage: current.usage,
|
|
760
1269
|
inputs: current.inputs,
|
|
761
1270
|
packages: current.packages,
|
|
@@ -812,7 +1321,18 @@ async function inspectSnapshotForStatus(root, projectId) {
|
|
|
812
1321
|
};
|
|
813
1322
|
}
|
|
814
1323
|
}
|
|
815
|
-
function
|
|
1324
|
+
async function inspectPublicationForStatus(root, projectId) {
|
|
1325
|
+
try {
|
|
1326
|
+
return await inspectPublicationStatus(root, projectId);
|
|
1327
|
+
}
|
|
1328
|
+
catch (error) {
|
|
1329
|
+
return {
|
|
1330
|
+
generationStatus: "invalid",
|
|
1331
|
+
code: error instanceof CliError ? error.code : "RESEARCH_PUBLICATION_STATE_INVALID",
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
function projectRecommendedAction(root, project, readyPackage, nativeStage, scientificReview, publication) {
|
|
816
1336
|
if (project.lineage.supersededBy) {
|
|
817
1337
|
return `Continue with superseding project ${project.lineage.supersededBy}.`;
|
|
818
1338
|
}
|
|
@@ -831,7 +1351,30 @@ function projectRecommendedAction(root, project, readyPackage, nativeStage) {
|
|
|
831
1351
|
if (nativeStage.status === "active") {
|
|
832
1352
|
return nativeStage.recommendedAction ?? "Resume the active native stage.";
|
|
833
1353
|
}
|
|
1354
|
+
if (scientificReview.nextGate) {
|
|
1355
|
+
const gate = scientificReview.nextGate;
|
|
1356
|
+
if (gate.status === "stopped") {
|
|
1357
|
+
return `Scientific ${gate.role} review stopped the project; inspect the frozen review and request user or external action instead of continuing.`;
|
|
1358
|
+
}
|
|
1359
|
+
const schema = `scientific-assessment-${gate.role}`;
|
|
1360
|
+
return `Use the native producer App to create a bounded ${gate.role} assessment from schema ${schema}, then prepare an independent review: tiangong-ai research project scientific review prepare ${project.id} --role ${gate.role} --assessment <absolute-json> --reviewer-agent <codex|claude> --reviewer-session <fresh-opaque-id> --workspace ${root}`;
|
|
1361
|
+
}
|
|
834
1362
|
if (project.status === "complete") {
|
|
1363
|
+
if (project.publicationPolicy) {
|
|
1364
|
+
if (publication && "code" in publication) {
|
|
1365
|
+
return `Publication state is invalid (${publication.code}); inspect Research Policy and frozen object bindings before continuing.`;
|
|
1366
|
+
}
|
|
1367
|
+
if (!publication || publication.generationStatus === "not-started") {
|
|
1368
|
+
return `Freeze the final manuscript and publication assessment, then inspect publication status: tiangong-ai research publication freeze ${project.id} --help`;
|
|
1369
|
+
}
|
|
1370
|
+
if (publication.closureSha256) {
|
|
1371
|
+
return `Publication closure is complete at ${publication.readinessVerdict}; inspect the immutable closure before post-closure authoring.`;
|
|
1372
|
+
}
|
|
1373
|
+
if (publication.reviewState !== "complete") {
|
|
1374
|
+
return `Complete fresh independent publication reviews for ${publication.missingReviewRoles.join(", ")}; inspect: tiangong-ai research publication status ${project.id} --workspace ${root}`;
|
|
1375
|
+
}
|
|
1376
|
+
return `Mechanically close the reviewed frozen manuscript: tiangong-ai research publication close ${project.id} --workspace ${root}`;
|
|
1377
|
+
}
|
|
835
1378
|
return `Create an immutable evidence addendum only when new evidence exists: tiangong-ai research project addendum ${project.id} --to <new-project-id> --workspace ${root}`;
|
|
836
1379
|
}
|
|
837
1380
|
if (project.status === "blocked") {
|
|
@@ -933,6 +1476,16 @@ function researchMode(value) {
|
|
|
933
1476
|
exitCode: 2,
|
|
934
1477
|
});
|
|
935
1478
|
}
|
|
1479
|
+
function researchGoal(value) {
|
|
1480
|
+
if (!value || value === "evidence-report")
|
|
1481
|
+
return "evidence-report";
|
|
1482
|
+
if (value === "top-journal")
|
|
1483
|
+
return value;
|
|
1484
|
+
throw new CliError(`Unsupported research goal: ${value}`, {
|
|
1485
|
+
code: "RESEARCH_GOAL_INVALID",
|
|
1486
|
+
exitCode: 2,
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
936
1489
|
function resumeStage(value) {
|
|
937
1490
|
if (!value)
|
|
938
1491
|
return undefined;
|
|
@@ -1034,6 +1587,80 @@ function inputRole(value) {
|
|
|
1034
1587
|
exitCode: 2,
|
|
1035
1588
|
});
|
|
1036
1589
|
}
|
|
1590
|
+
function projectInputTrustStatus(value, role) {
|
|
1591
|
+
if (!value)
|
|
1592
|
+
return undefined;
|
|
1593
|
+
if (value === "verified-owner-input" ||
|
|
1594
|
+
value === "unverified-owner-input" ||
|
|
1595
|
+
value === "reference-only" ||
|
|
1596
|
+
value === "replication-candidate") {
|
|
1597
|
+
return value;
|
|
1598
|
+
}
|
|
1599
|
+
throw new CliError(`Unsupported trust status for ${role} input: ${value}`, {
|
|
1600
|
+
code: "RESEARCH_INPUT_TRUST_INVALID",
|
|
1601
|
+
exitCode: 2,
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
function publicationReviewRole(value) {
|
|
1605
|
+
if (value === "evidence" ||
|
|
1606
|
+
value === "methods-reproducibility" ||
|
|
1607
|
+
value === "domain-novelty" ||
|
|
1608
|
+
value === "journal-editor") {
|
|
1609
|
+
return value;
|
|
1610
|
+
}
|
|
1611
|
+
throw new CliError("--role must be evidence, methods-reproducibility, domain-novelty, or journal-editor.", { code: "RESEARCH_PUBLICATION_REVIEW_ROLE_INVALID", exitCode: 2 });
|
|
1612
|
+
}
|
|
1613
|
+
function scientificReviewRole(value) {
|
|
1614
|
+
if (value === "research-design" || value === "evidence-construct" || value === "pilot-methods") {
|
|
1615
|
+
return value;
|
|
1616
|
+
}
|
|
1617
|
+
throw new CliError("--role must be research-design, evidence-construct, or pilot-methods.", {
|
|
1618
|
+
code: "RESEARCH_SCIENTIFIC_REVIEW_ROLE_INVALID",
|
|
1619
|
+
exitCode: 2,
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
function publicationAgent(value, label) {
|
|
1623
|
+
if (value === "codex" || value === "claude")
|
|
1624
|
+
return value;
|
|
1625
|
+
throw new CliError(`--${label}-agent must be codex or claude.`, {
|
|
1626
|
+
code: "RESEARCH_PUBLICATION_AGENT_INVALID",
|
|
1627
|
+
exitCode: 2,
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
async function readAbsolutePathArray(path) {
|
|
1631
|
+
if (!isAbsolute(path)) {
|
|
1632
|
+
throw new CliError("--supplements must be an absolute JSON file path.", {
|
|
1633
|
+
code: "RESEARCH_PUBLICATION_FILE_INVALID",
|
|
1634
|
+
exitCode: 2,
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
const selected = resolve(path);
|
|
1638
|
+
const info = await lstat(selected).catch(() => undefined);
|
|
1639
|
+
if (!info?.isFile() || info.isSymbolicLink() || info.size > 1024 * 1024) {
|
|
1640
|
+
throw new CliError("--supplements must be a bounded regular non-symlink JSON file.", {
|
|
1641
|
+
code: "RESEARCH_PUBLICATION_FILE_INVALID",
|
|
1642
|
+
exitCode: 2,
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
let value;
|
|
1646
|
+
try {
|
|
1647
|
+
value = JSON.parse(await readFile(selected, "utf8"));
|
|
1648
|
+
}
|
|
1649
|
+
catch {
|
|
1650
|
+
throw new CliError("--supplements contains invalid JSON.", {
|
|
1651
|
+
code: "RESEARCH_PUBLICATION_FILE_INVALID",
|
|
1652
|
+
exitCode: 2,
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
if (!Array.isArray(value) ||
|
|
1656
|
+
value.some((item) => typeof item !== "string" || !isAbsolute(item) || resolve(item) !== item)) {
|
|
1657
|
+
throw new CliError("--supplements must contain an array of absolute canonical paths.", {
|
|
1658
|
+
code: "RESEARCH_PUBLICATION_FILE_INVALID",
|
|
1659
|
+
exitCode: 2,
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
return value;
|
|
1663
|
+
}
|
|
1037
1664
|
function integerOption(value, fallback, label) {
|
|
1038
1665
|
if (!value)
|
|
1039
1666
|
return fallback;
|