@danypops/papyrus 0.55.0 → 0.57.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/package.json +14 -1
- package/src/artifact/artifact-relationship-view.ts +1 -1
- package/src/artifact/artifact-scope-store.ts +32 -26
- package/src/artifact/in-memory-artifact-scope-store.ts +82 -43
- package/src/artifact/index.ts +3 -0
- package/src/artifact/sqlite-artifact-scope-store.ts +105 -56
- package/src/cli/docs-command.ts +71 -0
- package/src/cli/gates-command.ts +1 -1
- package/src/cli/note-command.ts +1 -1
- package/src/cli/playbooks-command.ts +71 -0
- package/src/cli/rules-command.ts +71 -0
- package/src/cli/scope-groups-command.ts +197 -0
- package/src/cli/task-command.ts +1 -1
- package/src/cli.ts +7 -0
- package/src/constants.ts +10 -2
- package/src/db.ts +117 -7
- package/src/{stores → discussion}/discussion-round-store.ts +1 -1
- package/src/discussion/discussion-service.ts +2 -2
- package/src/discussion/index.ts +1 -0
- package/src/{stores → discussion}/sqlite-discussion-round-store.ts +2 -2
- package/src/display-graph/graph-renderer.ts +5 -0
- package/src/docs/docs-service.ts +55 -20
- package/src/domain-service-shared.ts +90 -2
- package/src/{domain → gate}/gate-execution.ts +1 -1
- package/src/{stores → gate}/gate-runner.ts +1 -1
- package/src/{stores → gate}/sqlite-gate-runner.ts +3 -3
- package/src/graph-projection/graph-projection-service.ts +2 -2
- package/src/{stores → graph-projection}/graph-projection-store.ts +1 -1
- package/src/{stores → graph-projection}/sqlite-graph-projection-store.ts +2 -2
- package/src/handlers/discuss.ts +1 -1
- package/src/handlers/docs.ts +57 -2
- package/src/handlers/playbooks.ts +49 -8
- package/src/handlers/projects.ts +1 -1
- package/src/handlers/registry.ts +10 -5
- package/src/handlers/rules.ts +66 -2
- package/src/handlers/scope-groups.ts +100 -0
- package/src/handlers/task-classifiers.ts +1 -1
- package/src/handlers/tasks.ts +4 -4
- package/src/index.ts +9 -9
- package/src/log/log-service.ts +2 -2
- package/src/{stores → log}/log-store.ts +1 -1
- package/src/{stores → log}/sqlite-log-store.ts +2 -2
- package/src/modules/docs.ts +21 -1
- package/src/modules/graph-projection.ts +2 -2
- package/src/modules/logs.ts +1 -1
- package/src/modules/notes.ts +1 -1
- package/src/modules/playbooks.ts +24 -3
- package/src/modules/projects.ts +3 -3
- package/src/modules/rules.ts +21 -1
- package/src/modules/scope-groups.ts +92 -0
- package/src/modules/tasks.ts +3 -3
- package/src/note/index.ts +1 -0
- package/src/{stores → note}/note-event-store.ts +1 -1
- package/src/note/note-service.ts +2 -2
- package/src/{stores → note}/sqlite-note-event-store.ts +2 -2
- package/src/ops.ts +1 -1
- package/src/playbook/index.ts +2 -0
- package/src/playbook/playbook-definition.ts +2 -2
- package/src/playbook/playbook-execution.ts +1 -1
- package/src/playbook/playbook-service.ts +61 -26
- package/src/playbook/workflow-execution.ts +8 -8
- package/src/{stores → project-registry}/in-memory-project-registry-store.ts +2 -2
- package/src/{ports → project-registry}/project-registry-store.ts +1 -1
- package/src/{stores → project-registry}/sqlite-project-registry-store.ts +3 -3
- package/src/rules/rules-service.ts +56 -24
- package/src/scope-group/delete-scope-group.ts +17 -0
- package/src/scope-group/in-memory-scope-group-store.ts +173 -0
- package/src/scope-group/scope-group-store.ts +28 -0
- package/src/scope-group/scope-group.ts +79 -0
- package/src/scope-group/sqlite-scope-group-store.ts +223 -0
- package/src/service.ts +46 -21
- package/src/session-identity/session-identity-service.ts +2 -2
- package/src/{stores → session-identity}/sqlite-session-identity-store.ts +1 -1
- package/src/task/index.ts +13 -0
- package/src/task/task-context.ts +1 -1
- package/src/task/task-edges.ts +2 -2
- package/src/task/task-focus-coordinator.ts +3 -3
- package/src/task/task-graph-view.ts +1 -1
- package/src/task/task-lease-coordinator.ts +2 -2
- package/src/task/task-mutation-coordinator.ts +1 -1
- package/src/task/task-project-scope.ts +5 -5
- package/src/task/task-relationship-view.ts +1 -1
- package/src/task/task-service.ts +24 -24
- package/src/{stores → task-create-request}/sqlite-task-create-request-store.ts +1 -1
- package/src/{stores → task-event}/sqlite-task-event-store.ts +2 -2
- package/src/{stores → task-event}/task-event-store.ts +1 -1
- package/src/{stores → task-focus}/sqlite-task-focus-store.ts +1 -1
- package/src/{stores → task-lease}/sqlite-task-lease-store.ts +2 -2
- package/src/{stores → task-lease}/task-lease-store.ts +1 -1
- package/src/{stores → task-mutation-request}/sqlite-task-mutation-request-store.ts +5 -1
- package/src/{stores → task-scope}/sqlite-task-scope-store.ts +3 -3
- package/src/{stores → task-scope}/task-scope-store.ts +3 -3
- package/src/{domain → task-scope}/task-scope.ts +1 -1
- package/src/stores/graph-renderer.ts +0 -5
- /package/src/{domain → discussion}/discussion.ts +0 -0
- /package/src/{domain → display-graph}/display-graph.ts +0 -0
- /package/src/{domain → gate}/gate.ts +0 -0
- /package/src/{domain → graph-projection}/graph-projection.ts +0 -0
- /package/src/{domain → log}/log-entry.ts +0 -0
- /package/src/{domain → note}/note-event.ts +0 -0
- /package/src/{domain → playbook}/blueprint-definition.ts +0 -0
- /package/src/{domain → project-registry}/project-registry.ts +0 -0
- /package/src/{stores → session-identity}/session-identity-store.ts +0 -0
- /package/src/{domain → session-identity}/session-identity.ts +0 -0
- /package/src/{domain → task}/checklist.ts +0 -0
- /package/src/{stores → task-create-request}/task-create-request-store.ts +0 -0
- /package/src/{domain → task-event}/task-event.ts +0 -0
- /package/src/{stores → task-focus}/task-focus-store.ts +0 -0
- /package/src/{domain → task-lease}/task-lease.ts +0 -0
- /package/src/{stores → task-mutation-request}/task-mutation-request-store.ts +0 -0
package/src/cli/docs-command.ts
CHANGED
|
@@ -210,6 +210,73 @@ const replaceProjectsCommand = buildCommand({
|
|
|
210
210
|
docs: { brief: "Replace a Doc's entire project membership" },
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
+
const setNoneCommand = buildCommand({
|
|
214
|
+
func: async function (this: DocsContext, _flags: Record<string, never>, id: string) {
|
|
215
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("docs.set_none", { id });
|
|
216
|
+
render.call(this, scope, renderScope(scope));
|
|
217
|
+
},
|
|
218
|
+
parameters: { flags: {}, positional: { kind: "tuple", parameters: [{ brief: "Document id", parse: String, placeholder: "id" }] } },
|
|
219
|
+
docs: { brief: "Fully hide a Doc -- never applicable, never context-injected, regardless of project" },
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const addGroupCommand = buildCommand({
|
|
223
|
+
func: async function (this: DocsContext, _flags: Record<string, never>, id: string, group: string) {
|
|
224
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("docs.add_group", { id, group });
|
|
225
|
+
render.call(this, scope, renderScope(scope));
|
|
226
|
+
},
|
|
227
|
+
parameters: {
|
|
228
|
+
flags: {},
|
|
229
|
+
positional: {
|
|
230
|
+
kind: "tuple",
|
|
231
|
+
parameters: [
|
|
232
|
+
{ brief: "Document id", parse: String, placeholder: "id" },
|
|
233
|
+
{ brief: "Scope group id/name/alias to add", parse: String, placeholder: "group" },
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
docs: { brief: "Add one scope group to a Doc's explicit scope" },
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const removeGroupCommand = buildCommand({
|
|
241
|
+
func: async function (this: DocsContext, _flags: Record<string, never>, id: string, group: string) {
|
|
242
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("docs.remove_group", { id, group });
|
|
243
|
+
render.call(this, scope, renderScope(scope));
|
|
244
|
+
},
|
|
245
|
+
parameters: {
|
|
246
|
+
flags: {},
|
|
247
|
+
positional: {
|
|
248
|
+
kind: "tuple",
|
|
249
|
+
parameters: [
|
|
250
|
+
{ brief: "Document id", parse: String, placeholder: "id" },
|
|
251
|
+
{ brief: "Scope group id/name/alias to remove", parse: String, placeholder: "group" },
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
docs: { brief: "Remove one scope group from a Doc's explicit scope" },
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const replaceGroupsCommand = buildCommand({
|
|
259
|
+
func: async function (this: DocsContext, flags: { groupsJson: string[] }, id: string) {
|
|
260
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("docs.replace_groups", {
|
|
261
|
+
id,
|
|
262
|
+
groups: flags.groupsJson,
|
|
263
|
+
});
|
|
264
|
+
render.call(this, scope, renderScope(scope));
|
|
265
|
+
},
|
|
266
|
+
parameters: {
|
|
267
|
+
flags: {
|
|
268
|
+
groupsJson: {
|
|
269
|
+
brief: "JSON string array of scope group id/name/alias references",
|
|
270
|
+
kind: "parsed",
|
|
271
|
+
parse: parseStringArray,
|
|
272
|
+
placeholder: "json",
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
positional: { kind: "tuple", parameters: [{ brief: "Document id", parse: String, placeholder: "id" }] },
|
|
276
|
+
},
|
|
277
|
+
docs: { brief: "Replace a Doc's entire scope-group membership" },
|
|
278
|
+
});
|
|
279
|
+
|
|
213
280
|
function buildStatusTransitionCommand(action: "activate" | "archive" | "reopen") {
|
|
214
281
|
return buildCommand({
|
|
215
282
|
func: async function (this: DocsContext, _flags: Record<string, never>, id: string) {
|
|
@@ -271,9 +338,13 @@ const app = buildApplication(
|
|
|
271
338
|
"assign-project": assignProjectCommand,
|
|
272
339
|
scope: scopeCommand,
|
|
273
340
|
"set-global": setGlobalCommand,
|
|
341
|
+
"set-none": setNoneCommand,
|
|
274
342
|
"add-project": addProjectCommand,
|
|
275
343
|
"remove-project": removeProjectCommand,
|
|
276
344
|
"replace-projects": replaceProjectsCommand,
|
|
345
|
+
"add-group": addGroupCommand,
|
|
346
|
+
"remove-group": removeGroupCommand,
|
|
347
|
+
"replace-groups": replaceGroupsCommand,
|
|
277
348
|
show: showCommand,
|
|
278
349
|
activate: buildStatusTransitionCommand("activate"),
|
|
279
350
|
archive: buildStatusTransitionCommand("archive"),
|
package/src/cli/gates-command.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CommandContext } from "@stricli/core";
|
|
2
2
|
import { buildApplication, buildCommand, buildRouteMap } from "@stricli/core";
|
|
3
3
|
import type { PapyrusClient } from "../client.ts";
|
|
4
|
-
import type { GateResult } from "../
|
|
4
|
+
import type { GateResult } from "../gate/gate.ts";
|
|
5
5
|
import { runStricliToString } from "./stricli-run.ts";
|
|
6
6
|
|
|
7
7
|
type GatesClient = Pick<PapyrusClient, "call">;
|
package/src/cli/note-command.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CommandContext } from "@stricli/core";
|
|
2
2
|
import { buildApplication, buildCommand, buildRouteMap, numberParser } from "@stricli/core";
|
|
3
3
|
import type { PapyrusClient } from "../client.ts";
|
|
4
|
-
import type { NoteHistoryPage } from "../
|
|
4
|
+
import type { NoteHistoryPage } from "../note/note-event.ts";
|
|
5
5
|
import { artifactLabel, type CliArtifact } from "./shared.ts";
|
|
6
6
|
import { runStricliToString } from "./stricli-run.ts";
|
|
7
7
|
|
|
@@ -318,6 +318,73 @@ const replaceProjectsCommand = buildCommand({
|
|
|
318
318
|
docs: { brief: "Replace a Playbook's entire project membership" },
|
|
319
319
|
});
|
|
320
320
|
|
|
321
|
+
const setNoneCommand = buildCommand({
|
|
322
|
+
func: async function (this: PlaybooksContext, _flags: Record<string, never>, id: string) {
|
|
323
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("playbooks.set_none", { id });
|
|
324
|
+
render.call(this, scope, renderScope(scope));
|
|
325
|
+
},
|
|
326
|
+
parameters: { flags: {}, positional: { kind: "tuple", parameters: [{ brief: "Playbook id", parse: String, placeholder: "id" }] } },
|
|
327
|
+
docs: { brief: "Fully hide a Playbook -- never applicable, never injected, regardless of project" },
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const addGroupCommand = buildCommand({
|
|
331
|
+
func: async function (this: PlaybooksContext, _flags: Record<string, never>, id: string, group: string) {
|
|
332
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("playbooks.add_group", { id, group });
|
|
333
|
+
render.call(this, scope, renderScope(scope));
|
|
334
|
+
},
|
|
335
|
+
parameters: {
|
|
336
|
+
flags: {},
|
|
337
|
+
positional: {
|
|
338
|
+
kind: "tuple",
|
|
339
|
+
parameters: [
|
|
340
|
+
{ brief: "Playbook id", parse: String, placeholder: "id" },
|
|
341
|
+
{ brief: "Scope group id/name/alias to add", parse: String, placeholder: "group" },
|
|
342
|
+
],
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
docs: { brief: "Add one scope group to a Playbook's explicit scope" },
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const removeGroupCommand = buildCommand({
|
|
349
|
+
func: async function (this: PlaybooksContext, _flags: Record<string, never>, id: string, group: string) {
|
|
350
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("playbooks.remove_group", { id, group });
|
|
351
|
+
render.call(this, scope, renderScope(scope));
|
|
352
|
+
},
|
|
353
|
+
parameters: {
|
|
354
|
+
flags: {},
|
|
355
|
+
positional: {
|
|
356
|
+
kind: "tuple",
|
|
357
|
+
parameters: [
|
|
358
|
+
{ brief: "Playbook id", parse: String, placeholder: "id" },
|
|
359
|
+
{ brief: "Scope group id/name/alias to remove", parse: String, placeholder: "group" },
|
|
360
|
+
],
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
docs: { brief: "Remove one scope group from a Playbook's explicit scope" },
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const replaceGroupsCommand = buildCommand({
|
|
367
|
+
func: async function (this: PlaybooksContext, flags: { groupsJson: string[] }, id: string) {
|
|
368
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("playbooks.replace_groups", {
|
|
369
|
+
id,
|
|
370
|
+
groups: flags.groupsJson,
|
|
371
|
+
});
|
|
372
|
+
render.call(this, scope, renderScope(scope));
|
|
373
|
+
},
|
|
374
|
+
parameters: {
|
|
375
|
+
flags: {
|
|
376
|
+
groupsJson: {
|
|
377
|
+
brief: "JSON string array of scope group id/name/alias references",
|
|
378
|
+
kind: "parsed",
|
|
379
|
+
parse: parseStringArray,
|
|
380
|
+
placeholder: "json",
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
positional: { kind: "tuple", parameters: [{ brief: "Playbook id", parse: String, placeholder: "id" }] },
|
|
384
|
+
},
|
|
385
|
+
docs: { brief: "Replace a Playbook's entire scope-group membership" },
|
|
386
|
+
});
|
|
387
|
+
|
|
321
388
|
const updateCommand = buildCommand({
|
|
322
389
|
func: async function (
|
|
323
390
|
this: PlaybooksContext,
|
|
@@ -402,9 +469,13 @@ const app = buildApplication(
|
|
|
402
469
|
"assign-project": assignProjectCommand,
|
|
403
470
|
scope: scopeCommand,
|
|
404
471
|
"set-global": setGlobalCommand,
|
|
472
|
+
"set-none": setNoneCommand,
|
|
405
473
|
"add-project": addProjectCommand,
|
|
406
474
|
"remove-project": removeProjectCommand,
|
|
407
475
|
"replace-projects": replaceProjectsCommand,
|
|
476
|
+
"add-group": addGroupCommand,
|
|
477
|
+
"remove-group": removeGroupCommand,
|
|
478
|
+
"replace-groups": replaceGroupsCommand,
|
|
408
479
|
update: updateCommand,
|
|
409
480
|
contain: buildPairedIdCommand(
|
|
410
481
|
"playbooks.contain",
|
package/src/cli/rules-command.ts
CHANGED
|
@@ -200,6 +200,73 @@ const replaceProjectsCommand = buildCommand({
|
|
|
200
200
|
docs: { brief: "Replace a Rule's entire project membership" },
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
+
const setNoneCommand = buildCommand({
|
|
204
|
+
func: async function (this: RulesContext, _flags: Record<string, never>, id: string) {
|
|
205
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("rules.set_none", { id });
|
|
206
|
+
render.call(this, scope, renderScope(scope));
|
|
207
|
+
},
|
|
208
|
+
parameters: { flags: {}, positional: { kind: "tuple", parameters: [{ brief: "Rule id", parse: String, placeholder: "id" }] } },
|
|
209
|
+
docs: { brief: "Fully hide a Rule -- never applicable, never injected, regardless of project" },
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const addGroupCommand = buildCommand({
|
|
213
|
+
func: async function (this: RulesContext, _flags: Record<string, never>, id: string, group: string) {
|
|
214
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("rules.add_group", { id, group });
|
|
215
|
+
render.call(this, scope, renderScope(scope));
|
|
216
|
+
},
|
|
217
|
+
parameters: {
|
|
218
|
+
flags: {},
|
|
219
|
+
positional: {
|
|
220
|
+
kind: "tuple",
|
|
221
|
+
parameters: [
|
|
222
|
+
{ brief: "Rule id", parse: String, placeholder: "id" },
|
|
223
|
+
{ brief: "Scope group id/name/alias to add", parse: String, placeholder: "group" },
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
docs: { brief: "Add one scope group to a Rule's explicit scope" },
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const removeGroupCommand = buildCommand({
|
|
231
|
+
func: async function (this: RulesContext, _flags: Record<string, never>, id: string, group: string) {
|
|
232
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("rules.remove_group", { id, group });
|
|
233
|
+
render.call(this, scope, renderScope(scope));
|
|
234
|
+
},
|
|
235
|
+
parameters: {
|
|
236
|
+
flags: {},
|
|
237
|
+
positional: {
|
|
238
|
+
kind: "tuple",
|
|
239
|
+
parameters: [
|
|
240
|
+
{ brief: "Rule id", parse: String, placeholder: "id" },
|
|
241
|
+
{ brief: "Scope group id/name/alias to remove", parse: String, placeholder: "group" },
|
|
242
|
+
],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
docs: { brief: "Remove one scope group from a Rule's explicit scope" },
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const replaceGroupsCommand = buildCommand({
|
|
249
|
+
func: async function (this: RulesContext, flags: { groupsJson: string[] }, id: string) {
|
|
250
|
+
const scope = await this.client.call<Record<string, unknown>, CliArtifactScope>("rules.replace_groups", {
|
|
251
|
+
id,
|
|
252
|
+
groups: flags.groupsJson,
|
|
253
|
+
});
|
|
254
|
+
render.call(this, scope, renderScope(scope));
|
|
255
|
+
},
|
|
256
|
+
parameters: {
|
|
257
|
+
flags: {
|
|
258
|
+
groupsJson: {
|
|
259
|
+
brief: "JSON string array of scope group id/name/alias references",
|
|
260
|
+
kind: "parsed",
|
|
261
|
+
parse: parseStringArray,
|
|
262
|
+
placeholder: "json",
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
positional: { kind: "tuple", parameters: [{ brief: "Rule id", parse: String, placeholder: "id" }] },
|
|
266
|
+
},
|
|
267
|
+
docs: { brief: "Replace a Rule's entire scope-group membership" },
|
|
268
|
+
});
|
|
269
|
+
|
|
203
270
|
const showCommand = buildCommand({
|
|
204
271
|
func: async function (this: RulesContext, _flags: Record<string, never>, id: string) {
|
|
205
272
|
const artifact = await this.client.call<Record<string, unknown>, CliArtifact>("rules.show", { id });
|
|
@@ -293,9 +360,13 @@ const app = buildApplication(
|
|
|
293
360
|
"assign-project": assignProjectCommand,
|
|
294
361
|
scope: scopeCommand,
|
|
295
362
|
"set-global": setGlobalCommand,
|
|
363
|
+
"set-none": setNoneCommand,
|
|
296
364
|
"add-project": addProjectCommand,
|
|
297
365
|
"remove-project": removeProjectCommand,
|
|
298
366
|
"replace-projects": replaceProjectsCommand,
|
|
367
|
+
"add-group": addGroupCommand,
|
|
368
|
+
"remove-group": removeGroupCommand,
|
|
369
|
+
"replace-groups": replaceGroupsCommand,
|
|
299
370
|
show: showCommand,
|
|
300
371
|
preview: previewCommand,
|
|
301
372
|
enable: buildEnableDisableCommand("enable"),
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { CommandContext } from "@stricli/core";
|
|
2
|
+
import { buildApplication, buildCommand, buildRouteMap, numberParser } from "@stricli/core";
|
|
3
|
+
import type { PapyrusClient } from "../client.ts";
|
|
4
|
+
import { runStricliToString } from "./stricli-run.ts";
|
|
5
|
+
|
|
6
|
+
type ScopeGroupsClient = Pick<PapyrusClient, "call">;
|
|
7
|
+
|
|
8
|
+
interface ScopeGroupsContext extends CommandContext {
|
|
9
|
+
readonly client: ScopeGroupsClient;
|
|
10
|
+
readonly json: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function parseStringArray(value: string): string[] {
|
|
14
|
+
const parsed = JSON.parse(value) as unknown;
|
|
15
|
+
if (!Array.isArray(parsed) || parsed.some((entry) => typeof entry !== "string")) throw new Error("value must be a JSON string array");
|
|
16
|
+
return parsed as string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface CliScopeGroup {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
aliases: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface CliScopeMember {
|
|
26
|
+
type: "project" | "group";
|
|
27
|
+
id: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function render(this: ScopeGroupsContext, result: unknown, human: string): void {
|
|
31
|
+
this.process.stdout.write(this.json ? JSON.stringify(result) : human);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const listCommand = buildCommand({
|
|
35
|
+
func: async function (this: ScopeGroupsContext, flags: { query?: string; limit?: number }) {
|
|
36
|
+
const groups = await this.client.call<Record<string, unknown>, CliScopeGroup[]>("scope_groups.list", {
|
|
37
|
+
query: flags.query,
|
|
38
|
+
limit: flags.limit,
|
|
39
|
+
});
|
|
40
|
+
render.call(this, groups, groups.length === 0 ? "No registered scope groups." : groups.map((group) => group.name).join("\n"));
|
|
41
|
+
},
|
|
42
|
+
parameters: {
|
|
43
|
+
flags: {
|
|
44
|
+
query: { brief: "Filter by scope group name or alias", kind: "parsed", parse: String, placeholder: "text", optional: true },
|
|
45
|
+
limit: { brief: "Maximum results", kind: "parsed", parse: numberParser, placeholder: "n", optional: true },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
docs: { brief: "List registered scope groups" },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const resolveCommand = buildCommand({
|
|
52
|
+
func: async function (this: ScopeGroupsContext, _flags: Record<string, never>, reference: string) {
|
|
53
|
+
const group = await this.client.call<Record<string, unknown>, CliScopeGroup>("scope_groups.resolve", { reference });
|
|
54
|
+
render.call(this, group, group.name);
|
|
55
|
+
},
|
|
56
|
+
parameters: {
|
|
57
|
+
flags: {},
|
|
58
|
+
positional: { kind: "tuple", parameters: [{ brief: "Scope group id, name, or alias", parse: String, placeholder: "reference" }] },
|
|
59
|
+
},
|
|
60
|
+
docs: { brief: "Resolve a scope group reference to its canonical identity" },
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const registerCommand = buildCommand({
|
|
64
|
+
func: async function (this: ScopeGroupsContext, flags: { aliasesJson?: string[]; existingId?: string }, name: string) {
|
|
65
|
+
const registered = await this.client.call<Record<string, unknown>, CliScopeGroup>("scope_groups.register", {
|
|
66
|
+
name,
|
|
67
|
+
aliases: flags.aliasesJson,
|
|
68
|
+
existing_id: flags.existingId,
|
|
69
|
+
});
|
|
70
|
+
render.call(this, registered, registered.name);
|
|
71
|
+
},
|
|
72
|
+
parameters: {
|
|
73
|
+
flags: {
|
|
74
|
+
aliasesJson: { brief: "JSON string array of aliases", kind: "parsed", parse: parseStringArray, placeholder: "json", optional: true },
|
|
75
|
+
existingId: {
|
|
76
|
+
brief: "Existing scope group id when renaming",
|
|
77
|
+
kind: "parsed",
|
|
78
|
+
parse: String,
|
|
79
|
+
placeholder: "id",
|
|
80
|
+
optional: true,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
positional: { kind: "tuple", parameters: [{ brief: "Scope group name", parse: String, placeholder: "name" }] },
|
|
84
|
+
},
|
|
85
|
+
docs: { brief: "Register a new scope group, or rename an existing one" },
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const showCommand = buildCommand({
|
|
89
|
+
func: async function (this: ScopeGroupsContext, _flags: Record<string, never>, group: string) {
|
|
90
|
+
const result = await this.client.call<Record<string, unknown>, { group: CliScopeGroup; members: CliScopeMember[] }>(
|
|
91
|
+
"scope_groups.show",
|
|
92
|
+
{ group },
|
|
93
|
+
);
|
|
94
|
+
render.call(
|
|
95
|
+
this,
|
|
96
|
+
result,
|
|
97
|
+
`${result.group.name}: ${result.members.length === 0 ? "(no members)" : result.members.map((m) => `${m.type}:${m.id}`).join(", ")}`,
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
parameters: {
|
|
101
|
+
flags: {},
|
|
102
|
+
positional: { kind: "tuple", parameters: [{ brief: "Scope group id, name, or alias", parse: String, placeholder: "group" }] },
|
|
103
|
+
},
|
|
104
|
+
docs: { brief: "Show a scope group's identity and its own direct membership" },
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const addMemberCommand = buildCommand({
|
|
108
|
+
func: async function (
|
|
109
|
+
this: ScopeGroupsContext,
|
|
110
|
+
_flags: Record<string, never>,
|
|
111
|
+
group: string,
|
|
112
|
+
memberType: string,
|
|
113
|
+
memberReference: string,
|
|
114
|
+
) {
|
|
115
|
+
const result = await this.client.call<Record<string, unknown>, { group: CliScopeGroup; members: CliScopeMember[] }>(
|
|
116
|
+
"scope_groups.add_member",
|
|
117
|
+
{ group, member_type: memberType, member_reference: memberReference },
|
|
118
|
+
);
|
|
119
|
+
render.call(this, result, `${result.group.name}: ${result.members.length} member(s)`);
|
|
120
|
+
},
|
|
121
|
+
parameters: {
|
|
122
|
+
flags: {},
|
|
123
|
+
positional: {
|
|
124
|
+
kind: "tuple",
|
|
125
|
+
parameters: [
|
|
126
|
+
{ brief: "Scope group id, name, or alias", parse: String, placeholder: "group" },
|
|
127
|
+
{ brief: '"project" or "group"', parse: String, placeholder: "member-type" },
|
|
128
|
+
{ brief: "Exact project or scope group id/name/alias/root reference", parse: String, placeholder: "member-reference" },
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
docs: { brief: "Add one member (a project, or another scope group) to a scope group" },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const removeMemberCommand = buildCommand({
|
|
136
|
+
func: async function (
|
|
137
|
+
this: ScopeGroupsContext,
|
|
138
|
+
_flags: Record<string, never>,
|
|
139
|
+
group: string,
|
|
140
|
+
memberType: string,
|
|
141
|
+
memberReference: string,
|
|
142
|
+
) {
|
|
143
|
+
const result = await this.client.call<Record<string, unknown>, { group: CliScopeGroup; members: CliScopeMember[] }>(
|
|
144
|
+
"scope_groups.remove_member",
|
|
145
|
+
{ group, member_type: memberType, member_reference: memberReference },
|
|
146
|
+
);
|
|
147
|
+
render.call(this, result, `${result.group.name}: ${result.members.length} member(s)`);
|
|
148
|
+
},
|
|
149
|
+
parameters: {
|
|
150
|
+
flags: {},
|
|
151
|
+
positional: {
|
|
152
|
+
kind: "tuple",
|
|
153
|
+
parameters: [
|
|
154
|
+
{ brief: "Scope group id, name, or alias", parse: String, placeholder: "group" },
|
|
155
|
+
{ brief: '"project" or "group"', parse: String, placeholder: "member-type" },
|
|
156
|
+
{ brief: "Exact project or scope group id/name/alias/root reference", parse: String, placeholder: "member-reference" },
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
docs: { brief: "Remove one member from a scope group" },
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const deleteCommand = buildCommand({
|
|
164
|
+
func: async function (this: ScopeGroupsContext, _flags: Record<string, never>, group: string) {
|
|
165
|
+
const result = await this.client.call<Record<string, unknown>, { deleted: boolean; id: string }>("scope_groups.delete", { group });
|
|
166
|
+
render.call(this, result, `Deleted scope group ${result.id}`);
|
|
167
|
+
},
|
|
168
|
+
parameters: {
|
|
169
|
+
flags: {},
|
|
170
|
+
positional: { kind: "tuple", parameters: [{ brief: "Scope group id, name, or alias", parse: String, placeholder: "group" }] },
|
|
171
|
+
},
|
|
172
|
+
docs: { brief: "Delete a scope group outright (refuses if still referenced)" },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const app = buildApplication(
|
|
176
|
+
buildRouteMap({
|
|
177
|
+
routes: {
|
|
178
|
+
list: listCommand,
|
|
179
|
+
resolve: resolveCommand,
|
|
180
|
+
register: registerCommand,
|
|
181
|
+
show: showCommand,
|
|
182
|
+
"add-member": addMemberCommand,
|
|
183
|
+
"remove-member": removeMemberCommand,
|
|
184
|
+
delete: deleteCommand,
|
|
185
|
+
},
|
|
186
|
+
docs: {
|
|
187
|
+
brief: "Scope group operations -- named, reusable, possibly-nested collections of projects/groups for Doc/Rule/Playbook scoping",
|
|
188
|
+
},
|
|
189
|
+
}),
|
|
190
|
+
{ name: "scope-groups", scanner: { caseStyle: "allow-kebab-for-camel" } },
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
export async function runScopeGroupsCli(args: string[], client: ScopeGroupsClient): Promise<string> {
|
|
194
|
+
const json = args.includes("--json");
|
|
195
|
+
const positional = args.filter((arg) => arg !== "--json");
|
|
196
|
+
return runStricliToString(app, positional, { client, json });
|
|
197
|
+
}
|
package/src/cli/task-command.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { CommandContext } from "@stricli/core";
|
|
|
2
2
|
import { buildApplication, buildCommand, buildRouteMap, numberParser } from "@stricli/core";
|
|
3
3
|
import type { PapyrusClient } from "../client.ts";
|
|
4
4
|
import { TASK_EXECUTION_MAX_NODES } from "../constants.ts";
|
|
5
|
-
import type { GateResult } from "../
|
|
5
|
+
import type { GateResult } from "../gate/gate.ts";
|
|
6
6
|
import type { OperationName } from "../service.ts";
|
|
7
7
|
import type { TaskExecutionPlan } from "../task/task-execution.ts";
|
|
8
8
|
import type { TaskBlockage, TaskCompletion } from "../task/task-service.ts";
|
package/src/cli.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { runNoteCli } from "./cli/note-command.ts";
|
|
|
16
16
|
import { runPlaybooksCli } from "./cli/playbooks-command.ts";
|
|
17
17
|
import { runProjectsCli } from "./cli/projects-command.ts";
|
|
18
18
|
import { runRulesCli } from "./cli/rules-command.ts";
|
|
19
|
+
import { runScopeGroupsCli } from "./cli/scope-groups-command.ts";
|
|
19
20
|
import { runSessionIdentityCli } from "./cli/session-identity-command.ts";
|
|
20
21
|
import { runTaskCli } from "./cli/task-command.ts";
|
|
21
22
|
import { connectPapyrusClient } from "./client.ts";
|
|
@@ -362,6 +363,7 @@ export {
|
|
|
362
363
|
runPlaybooksCli,
|
|
363
364
|
runProjectsCli,
|
|
364
365
|
runRulesCli,
|
|
366
|
+
runScopeGroupsCli,
|
|
365
367
|
runSessionIdentityCli,
|
|
366
368
|
runTaskCli,
|
|
367
369
|
};
|
|
@@ -387,6 +389,11 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
|
|
|
387
389
|
console.log(await runProjectsCli(args.slice(1), client));
|
|
388
390
|
return;
|
|
389
391
|
}
|
|
392
|
+
if (command === "scope-groups") {
|
|
393
|
+
const client = await connectPapyrusClient();
|
|
394
|
+
console.log(await runScopeGroupsCli(args.slice(1), client));
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
390
397
|
if (command === "notes") {
|
|
391
398
|
const client = await connectPapyrusClient();
|
|
392
399
|
console.log(await runNoteCli(args.slice(1), client));
|
package/src/constants.ts
CHANGED
|
@@ -17,7 +17,7 @@ export const DAEMON_PROBE_TIMEOUT_MS = 800;
|
|
|
17
17
|
export const DAEMON_UNIT_NAME = "papyrus.service";
|
|
18
18
|
export const DAEMON_DIR_ENV = "PAPYRUS_DAEMON_DIR";
|
|
19
19
|
export const SQLITE_BUSY_TIMEOUT_MS = 5_000;
|
|
20
|
-
export const SQLITE_SCHEMA_VERSION =
|
|
20
|
+
export const SQLITE_SCHEMA_VERSION = 29;
|
|
21
21
|
export const SERVICE_MAX_BODY_BYTES = 1_048_576;
|
|
22
22
|
|
|
23
23
|
export const WAL_CHECKPOINT_INTERVAL_MS = 60_000;
|
|
@@ -278,12 +278,20 @@ export const ARTIFACT_TRASH_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
|
278
278
|
export const TASK_SCOPE_MAX_TASKS = 1_000;
|
|
279
279
|
/** Docs/Rules/Skills project scope listing bound, mirroring TASK_SCOPE_MAX_TASKS. */
|
|
280
280
|
export const ARTIFACT_SCOPE_MAX_ARTIFACTS = 1_000;
|
|
281
|
-
/** How many distinct registered projects a single Doc/Rule/Playbook may belong to at once, in "projects" scope mode. */
|
|
281
|
+
/** How many distinct registered projects a single Doc/Rule/Playbook may belong to at once, in "projects" scope mode. Kept alongside ARTIFACT_SCOPE_MAX_MEMBERS_PER_ARTIFACT (identical value) for the pure-project call sites/tests that predate mixed project+group membership. */
|
|
282
282
|
export const ARTIFACT_SCOPE_MAX_PROJECTS_PER_ARTIFACT = 50;
|
|
283
|
+
/** How many total members (projects and/or scope groups combined) a single Doc/Rule/Playbook may have at once, in "explicit" scope mode. */
|
|
284
|
+
export const ARTIFACT_SCOPE_MAX_MEMBERS_PER_ARTIFACT = 50;
|
|
283
285
|
export const TASK_PROJECT_ROOT_MAX_LENGTH = 4_096;
|
|
284
286
|
export const TASK_PROJECT_NAME_MAX_LENGTH = 200;
|
|
285
287
|
export const TASK_PROJECT_ALIAS_MAX_COUNT = 20;
|
|
286
288
|
export const TASK_PROJECT_LIST_MAX_RESULTS = 100;
|
|
289
|
+
export const SCOPE_GROUP_NAME_MAX_LENGTH = 200;
|
|
290
|
+
export const SCOPE_GROUP_ALIAS_MAX_COUNT = 20;
|
|
291
|
+
export const SCOPE_GROUP_MAX_MEMBERS = 50;
|
|
292
|
+
/** Bounds recursive group-in-group expansion at both add-time (refuses a nesting that would exceed it) and resolution time. */
|
|
293
|
+
export const SCOPE_GROUP_MAX_NESTING_DEPTH = 5;
|
|
294
|
+
export const SCOPE_GROUP_LIST_MAX_RESULTS = 100;
|
|
287
295
|
export const TASK_CREATE_IDEMPOTENCY_KEY_MAX_LENGTH = 200;
|
|
288
296
|
export const TASK_CREATE_IDEMPOTENCY_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
289
297
|
export const TASK_MUTATION_IDEMPOTENCY_KEY_MAX_LENGTH = 200;
|