@rse/ase 1.0.7 → 2.0.1
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/dst/ase-agent-definition-api.d.ts +184 -0
- package/dst/ase-agent-definition-api.js +6 -0
- package/dst/ase-agent-definition-builtin/condenser.js +42 -0
- package/dst/ase-agent-definition-builtin/editor.js +45 -0
- package/dst/ase-agent-definition-builtin/estimator.js +98 -0
- package/dst/ase-agent-definition-builtin/implementer.js +121 -0
- package/dst/ase-agent-definition-builtin/integrator.js +56 -0
- package/dst/ase-agent-definition-builtin/qualifier.js +61 -0
- package/dst/ase-agent-definition-builtin/rebooter.js +41 -0
- package/dst/ase-agent-definition-builtin/researcher.js +77 -0
- package/dst/ase-agent-definition-core.js +827 -0
- package/dst/ase-agent-fleet.js +772 -0
- package/dst/ase-agent-process.js +232 -0
- package/dst/ase-agent-relay-client.js +509 -0
- package/dst/ase-agent-relay-core.js +213 -0
- package/dst/ase-agent-relay-server-bind.js +242 -0
- package/dst/ase-agent-relay-server-cli.js +50 -0
- package/dst/ase-agent-runtime-claim.js +189 -0
- package/dst/ase-agent-runtime-engine.js +1100 -0
- package/dst/ase-agent-runtime-exec.js +275 -0
- package/dst/ase-agent-runtime-phase.js +581 -0
- package/dst/ase-agent-runtime-request.js +272 -0
- package/dst/ase-agent-runtime-tool.js +207 -0
- package/dst/ase-agent-runtime.js +1316 -0
- package/dst/ase-agent.js +65 -0
- package/dst/ase-artifact.js +6 -5
- package/dst/ase-config-core.js +465 -0
- package/dst/ase-config-schema.js +357 -0
- package/dst/ase-config-scope.js +172 -0
- package/dst/ase-config.js +25 -585
- package/dst/ase-hook.js +228 -111
- package/dst/ase-lib-daemon-cli.js +130 -0
- package/dst/ase-lib-hash.js +20 -0
- package/dst/{ase-log.js → ase-lib-log.js} +40 -7
- package/dst/ase-lib-server-bind.js +115 -0
- package/dst/ase-lib-server-cli.js +474 -0
- package/dst/{ase-table.js → ase-lib-table.js} +1 -1
- package/dst/{ase-version.js → ase-lib-version.js} +8 -1
- package/dst/ase-man.js +96 -0
- package/dst/ase-mcp.js +166 -13
- package/dst/{ase-getopt.js → ase-service-getopt.js} +82 -80
- package/dst/{ase-markdown.js → ase-service-markdown.js} +18 -11
- package/dst/ase-service-otel.js +1246 -0
- package/dst/{ase-skills.js → ase-service-skills.js} +29 -64
- package/dst/ase-service-state.js +101 -0
- package/dst/ase-service.js +269 -81
- package/dst/ase-setup-common.js +141 -0
- package/dst/ase-setup-mcp.js +359 -0
- package/dst/ase-setup-settings.js +837 -0
- package/dst/ase-setup.js +113 -869
- package/dst/ase-spec.js +31 -19
- package/dst/ase-statusline.js +108 -43
- package/dst/ase-task-board-core.js +739 -0
- package/dst/ase-task-board-filter.js +127 -0
- package/dst/ase-task-board-flow.js +237 -0
- package/dst/ase-task-board-graph.js +497 -0
- package/dst/ase-task-board-stats.js +263 -0
- package/dst/ase-task-board-tui-control.js +775 -0
- package/dst/ase-task-board-tui-model.js +1008 -0
- package/dst/ase-task-board-tui-popup.js +626 -0
- package/dst/ase-task-board-tui-style.js +106 -0
- package/dst/ase-task-board-tui-view.js +847 -0
- package/dst/ase-task-board-tui.js +189 -0
- package/dst/ase-task-board-web-client.css +1 -0
- package/dst/ase-task-board-web-client.html +20 -0
- package/dst/ase-task-board-web-client.js +81 -0
- package/dst/ase-task-board-web-server.js +823 -0
- package/dst/ase-task-board.js +134 -0
- package/dst/ase-task-format.js +757 -0
- package/dst/ase-task-request.js +208 -0
- package/dst/ase-task-store-client.js +495 -0
- package/dst/ase-task-store-core.js +804 -0
- package/dst/ase-task-store-issues.js +237 -0
- package/dst/ase-task-store-plugin-api.js +6 -0
- package/dst/ase-task-store-plugin-ase.js +484 -0
- package/dst/ase-task-store-plugin-delegate.js +137 -0
- package/dst/ase-task-store-plugin-gitea.js +666 -0
- package/dst/ase-task-store-plugin-github.js +696 -0
- package/dst/ase-task-store-plugin-gitlab.js +668 -0
- package/dst/ase-task-store-server-bind.js +538 -0
- package/dst/ase-task-store-server-cli.js +154 -0
- package/dst/ase-task.js +1335 -357
- package/dst/{ase-compat.js → ase-util-compat.js} +2 -2
- package/dst/{ase-diagram.js → ase-util-diagram.js} +107 -32
- package/dst/{ase-meta.js → ase-util-meta.js} +2 -2
- package/dst/{ase-metric.js → ase-util-metric.js} +2 -2
- package/dst/{ase-mint.js → ase-util-mint.js} +19 -12
- package/dst/{ase-worktree.js → ase-util-worktree.js} +30 -19
- package/dst/ase-util.js +20 -0
- package/dst/ase.js +108 -32
- package/dst/man/ase-agent-adhoc.1 +515 -0
- package/dst/man/ase-agent-condenser.1 +193 -0
- package/dst/man/ase-agent-editor.1 +212 -0
- package/dst/man/ase-agent-estimator.1 +214 -0
- package/dst/man/ase-agent-fleet.1 +185 -0
- package/dst/man/ase-agent-implementer.1 +233 -0
- package/dst/man/ase-agent-integrator.1 +213 -0
- package/dst/man/ase-agent-qualifier.1 +214 -0
- package/dst/man/ase-agent-rebooter.1 +193 -0
- package/dst/man/ase-agent-relay.1 +128 -0
- package/dst/man/ase-agent-researcher.1 +217 -0
- package/dst/man/ase-agent.1 +264 -0
- package/dst/man/ase-artifact-list.1 +38 -0
- package/dst/man/ase-artifact-name.1 +39 -0
- package/dst/man/ase-artifact.1 +56 -0
- package/dst/man/ase-config-delete.1 +45 -0
- package/dst/man/ase-config-edit.1 +48 -0
- package/dst/man/ase-config-get.1 +46 -0
- package/dst/man/ase-config-init.1 +60 -0
- package/dst/man/ase-config-list.1 +42 -0
- package/dst/man/ase-config-set.1 +52 -0
- package/dst/man/ase-config.1 +231 -0
- package/dst/man/ase-hook-permission-request.1 +36 -0
- package/dst/man/ase-hook-pre-tool-use.1 +59 -0
- package/dst/man/ase-hook-session-end.1 +47 -0
- package/dst/man/ase-hook-session-persona.1 +37 -0
- package/dst/man/ase-hook-session-start.1 +81 -0
- package/dst/man/ase-hook-stop.1 +34 -0
- package/dst/man/ase-hook-user-prompt-submit.1 +34 -0
- package/dst/man/ase-hook.1 +51 -0
- package/dst/man/ase-man.1 +67 -0
- package/dst/man/ase-mcp.1 +61 -0
- package/dst/man/ase-service-log.1 +43 -0
- package/dst/man/ase-service-send.1 +47 -0
- package/dst/man/ase-service-start.1 +54 -0
- package/dst/man/ase-service-status.1 +46 -0
- package/dst/man/ase-service-stop.1 +37 -0
- package/dst/man/ase-service.1 +85 -0
- package/dst/man/ase-setup-disable.1 +44 -0
- package/dst/man/ase-setup-enable.1 +45 -0
- package/dst/man/ase-setup-install.1 +73 -0
- package/dst/man/ase-setup-mcp.1 +124 -0
- package/dst/man/ase-setup-status.1 +60 -0
- package/dst/man/ase-setup-statusline.1 +92 -0
- package/dst/man/ase-setup-telemetry.1 +92 -0
- package/dst/man/ase-setup-uninstall.1 +59 -0
- package/dst/man/ase-setup-update.1 +64 -0
- package/dst/man/ase-setup.1 +93 -0
- package/dst/man/ase-spec-export.1 +62 -0
- package/dst/man/ase-spec-lint.1 +40 -0
- package/dst/man/ase-spec-preview.1 +40 -0
- package/dst/man/ase-spec.1 +46 -0
- package/dst/man/ase-statusline.1 +189 -0
- package/dst/man/ase-task-assign.1 +71 -0
- package/dst/man/ase-task-board.1 +384 -0
- package/dst/man/ase-task-delete.1 +35 -0
- package/dst/man/ase-task-edit.1 +46 -0
- package/dst/man/ase-task-idscheme.1 +65 -0
- package/dst/man/ase-task-lifecycle.1 +57 -0
- package/dst/man/ase-task-list.1 +47 -0
- package/dst/man/ase-task-load.1 +36 -0
- package/dst/man/ase-task-newid.1 +61 -0
- package/dst/man/ase-task-purge.1 +37 -0
- package/dst/man/ase-task-rename.1 +44 -0
- package/dst/man/ase-task-request.1 +61 -0
- package/dst/man/ase-task-save.1 +48 -0
- package/dst/man/ase-task-status.1 +55 -0
- package/dst/man/ase-task-store.1 +194 -0
- package/dst/man/ase-task-view.1 +40 -0
- package/dst/man/ase-task.1 +160 -0
- package/dst/man/ase-util-compat.1 +25 -0
- package/dst/man/ase-util-diagram.1 +76 -0
- package/dst/man/ase-util-meta.1 +45 -0
- package/dst/man/ase-util-metric.1 +54 -0
- package/dst/man/ase-util-mint.1 +59 -0
- package/dst/man/ase-util-worktree.1 +72 -0
- package/dst/man/ase-util.1 +50 -0
- package/dst/man/ase.1 +124 -0
- package/package.json +81 -21
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/agents/ase-meta-diagram.md +12 -8
- package/plugin/agents/ase-repo-review.md +322 -0
- package/plugin/hooks/hooks-codex.json +5 -0
- package/plugin/hooks/hooks-copilot.json +5 -0
- package/plugin/hooks/hooks.json +8 -0
- package/plugin/meta/ase-common-approach.md +117 -0
- package/plugin/meta/ase-common-changeset.md +256 -0
- package/plugin/meta/ase-common-code.md +10 -173
- package/plugin/meta/ase-common-grill.md +317 -0
- package/plugin/meta/ase-common-resolve.md +305 -0
- package/plugin/meta/ase-common-task.md +83 -4
- package/plugin/meta/ase-constitution.md +0 -2
- package/plugin/meta/ase-format-specbook.md +208 -25
- package/plugin/meta/ase-format-specbook.yaml +242 -851
- package/plugin/meta/ase-format-task.md +669 -125
- package/plugin/meta/ase-persona.md +3 -4
- package/plugin/meta/ase-skill.md +9 -0
- package/plugin/mods/ase-mods.ts +409 -0
- package/plugin/mods/claude-code.d.ts +13417 -0
- package/plugin/skills/ase-arch-analyze/SKILL.md +1 -1
- package/plugin/skills/ase-arch-analyze/help.md +2 -2
- package/plugin/skills/ase-code-analyze/SKILL.md +59 -8
- package/plugin/skills/ase-code-analyze/help.md +29 -4
- package/plugin/skills/ase-code-edit/SKILL.md +107 -181
- package/plugin/skills/ase-code-edit/help.md +118 -31
- package/plugin/skills/ase-code-lint/SKILL.md +64 -13
- package/plugin/skills/ase-code-lint/help.md +32 -2
- package/plugin/skills/ase-docs-proofread/SKILL.md +4 -4
- package/plugin/skills/ase-docs-proofread/help.md +1 -1
- package/plugin/skills/ase-docs-refine/SKILL.md +4 -4
- package/plugin/skills/ase-docs-shorten/SKILL.md +4 -4
- package/plugin/skills/ase-help-intent/data.md +2164 -1087
- package/plugin/skills/ase-help-intent/help.md +1 -1
- package/plugin/skills/ase-help-skill/catalog.md +12 -9
- package/plugin/skills/ase-meta-brainstorm/SKILL.md +1 -1
- package/plugin/skills/ase-meta-config/SKILL.md +3 -4
- package/plugin/skills/ase-meta-config/help.md +31 -10
- package/plugin/skills/ase-meta-mint/SKILL.md +5 -5
- package/plugin/skills/ase-meta-mint/help.md +10 -5
- package/plugin/skills/ase-meta-why/help.md +1 -1
- package/plugin/skills/ase-meta-workflow/SKILL.md +7 -7
- package/plugin/skills/ase-meta-workflow/help.md +4 -4
- package/plugin/skills/ase-meta-workflow/sample.md +3 -3
- package/plugin/skills/ase-meta-workflow/workflow.txt +22 -18
- package/plugin/skills/{ase-meta-changelog → ase-repo-changelog}/SKILL.md +4 -3
- package/plugin/skills/{ase-meta-changelog → ase-repo-changelog}/help.md +5 -5
- package/plugin/skills/{ase-meta-commit → ase-repo-commit}/SKILL.md +6 -6
- package/plugin/skills/{ase-meta-commit → ase-repo-commit}/help.md +5 -5
- package/plugin/skills/{ase-meta-diff → ase-repo-diff}/SKILL.md +6 -6
- package/plugin/skills/{ase-meta-diff → ase-repo-diff}/help.md +8 -8
- package/plugin/skills/{ase-code-dissect → ase-repo-dissect}/SKILL.md +19 -19
- package/plugin/skills/{ase-code-dissect → ase-repo-dissect}/help.md +10 -12
- package/plugin/skills/ase-repo-merge/SKILL.md +346 -0
- package/plugin/skills/ase-repo-merge/help.md +107 -0
- package/plugin/skills/ase-repo-resolve/SKILL.md +158 -0
- package/plugin/skills/ase-repo-resolve/help.md +102 -0
- package/plugin/skills/ase-repo-review/SKILL.md +406 -0
- package/plugin/skills/ase-repo-review/help.md +181 -0
- package/plugin/skills/ase-spec-edit/SKILL.md +117 -183
- package/plugin/skills/ase-spec-edit/help.md +107 -26
- package/plugin/skills/ase-sync-reconcile/help.md +1 -1
- package/plugin/skills/ase-task-assign/SKILL.md +111 -0
- package/plugin/skills/ase-task-assign/help.md +119 -0
- package/plugin/skills/ase-task-condense/SKILL.md +21 -21
- package/plugin/skills/ase-task-condense/help.md +12 -10
- package/plugin/skills/ase-task-delete/SKILL.md +1 -1
- package/plugin/skills/ase-task-dissect/SKILL.md +71 -48
- package/plugin/skills/ase-task-dissect/help.md +10 -9
- package/plugin/skills/ase-task-edit/SKILL.md +379 -94
- package/plugin/skills/ase-task-edit/help.md +95 -22
- package/plugin/skills/ase-task-estimate/SKILL.md +492 -0
- package/plugin/skills/ase-task-estimate/help.md +158 -0
- package/plugin/skills/ase-task-grill/SKILL.md +246 -138
- package/plugin/skills/ase-task-grill/help.md +129 -26
- package/plugin/skills/ase-task-id/SKILL.md +8 -1
- package/plugin/skills/ase-task-id/help.md +6 -3
- package/plugin/skills/ase-task-implement/SKILL.md +535 -145
- package/plugin/skills/ase-task-implement/help.md +138 -47
- package/plugin/skills/ase-task-integrate/SKILL.md +549 -0
- package/plugin/skills/ase-task-integrate/help.md +187 -0
- package/plugin/skills/ase-task-list/SKILL.md +55 -23
- package/plugin/skills/ase-task-list/help.md +45 -21
- package/plugin/skills/ase-task-reboot/SKILL.md +84 -43
- package/plugin/skills/ase-task-reboot/help.md +28 -8
- package/plugin/skills/ase-task-rename/SKILL.md +10 -2
- package/plugin/skills/ase-task-research/SKILL.md +386 -0
- package/plugin/skills/ase-task-research/help.md +102 -0
- package/plugin/skills/ase-task-status/SKILL.md +137 -0
- package/plugin/skills/ase-task-status/help.md +80 -0
- package/plugin/skills/ase-task-view/SKILL.md +49 -24
- package/plugin/skills/ase-task-view/help.md +19 -9
- package/plugin/agents/ase-meta-review.md +0 -187
- package/plugin/skills/ase-code-craft/SKILL.md +0 -264
- package/plugin/skills/ase-code-craft/help.md +0 -122
- package/plugin/skills/ase-code-refactor/SKILL.md +0 -232
- package/plugin/skills/ase-code-refactor/help.md +0 -109
- package/plugin/skills/ase-code-resolve/SKILL.md +0 -289
- package/plugin/skills/ase-code-resolve/help.md +0 -116
- package/plugin/skills/ase-meta-review/SKILL.md +0 -210
- package/plugin/skills/ase-meta-review/help.md +0 -96
- package/plugin/skills/ase-task-preflight/SKILL.md +0 -166
- package/plugin/skills/ase-task-preflight/help.md +0 -82
- /package/dst/{ase-ignore.js → ase-lib-ignore.js} +0 -0
- /package/dst/{ase-stdio.js → ase-lib-stdio.js} +0 -0
- /package/dst/{ase-kv.js → ase-service-kv.js} +0 -0
- /package/dst/{ase-sleep.js → ase-service-sleep.js} +0 -0
- /package/dst/{ase-timestamp.js → ase-service-timestamp.js} +0 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** Agentic Software Engineering (ASE)
|
|
3
|
+
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
|
+
*/
|
|
6
|
+
import { DateTime } from "luxon";
|
|
7
|
+
import * as TaskFormat from "./ase-task-format.js";
|
|
8
|
+
/* the reserved labels: the project registry entry (carrying the lifecycle model
|
|
9
|
+
and task id scheme in its description), the soft deletion marker, and the
|
|
10
|
+
"ase:<key>:<value>" labels of the header keys without a native counterpart */
|
|
11
|
+
export const LABEL_PROJECT = "ase:project";
|
|
12
|
+
export const LABEL_DELETED = "ase:deleted";
|
|
13
|
+
export const LABEL_KEY_RE = /^ase:([A-Za-z]+):(.*)$/;
|
|
14
|
+
/* the hidden metadata header of an attachment comment */
|
|
15
|
+
const ATTACH_RE = /^<!-- ase:attachment\n([\s\S]*?)\n-->\n?([\s\S]*)$/;
|
|
16
|
+
/* escape a hidden metadata value, so it can never close the HTML comment */
|
|
17
|
+
const escapeValue = (value) => value.replace(/&/g, "&").replace(/-->/g, "-->");
|
|
18
|
+
const unescapeValue = (value) => value.replace(/-->/g, "-->").replace(/&/g, "&");
|
|
19
|
+
/* the timestamp of the task plan format for an ISO timestamp of the issue tracker */
|
|
20
|
+
export const stamp = (iso) => DateTime.fromISO(iso).toFormat("yyyy-LL-dd HH:mm");
|
|
21
|
+
/* the task id of an issue number, and the issue number of a task id (0 if not conforming) */
|
|
22
|
+
export const idOf = (scheme, n) => `${scheme.prefix}${String(n).padStart(scheme.width, "0")}${scheme.suffix}`;
|
|
23
|
+
export const numberOf = (scheme, id) => {
|
|
24
|
+
const n = TaskFormat.seqNumber(scheme, id);
|
|
25
|
+
return n > 0 && idOf(scheme, n) === id ? n : 0;
|
|
26
|
+
};
|
|
27
|
+
/* the description of the "ase:project" label for a registry entry, and vice versa */
|
|
28
|
+
export const registryDescription = (entry) => `lifecycle=${entry.lifecycle} idscheme=${entry.idscheme}` + ((entry.seqmark ?? 0) > 0 ? ` seqmark=${String(entry.seqmark)}` : "");
|
|
29
|
+
export const registryEntry = (description) => {
|
|
30
|
+
const m = /^lifecycle=(\S+) idscheme=(\S+)(?: seqmark=(\d+))?$/.exec(description ?? "");
|
|
31
|
+
return {
|
|
32
|
+
lifecycle: m?.[1] ?? "solo",
|
|
33
|
+
idscheme: m?.[2] ?? "seq:#%d",
|
|
34
|
+
...(m?.[3] !== undefined ? { seqmark: Number.parseInt(m[3], 10) } : {})
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/* reject a task id scheme of a project registration other than a "seq" one */
|
|
38
|
+
export const requireSeqScheme = (idscheme, service) => {
|
|
39
|
+
if (TaskFormat.parseIdScheme(idscheme).kind !== "seq")
|
|
40
|
+
throw new Error(`task id scheme "${idscheme}" not supported, as task ids are ${service} issue numbers ` +
|
|
41
|
+
"(use a \"seq\" task id scheme like \"seq:#%d\")");
|
|
42
|
+
};
|
|
43
|
+
export const registryContext = (prjId, entry, service) => {
|
|
44
|
+
const lifecycle = Object.hasOwn(TaskFormat.taskLifecycles, entry.lifecycle) ?
|
|
45
|
+
TaskFormat.taskLifecycles[entry.lifecycle] : TaskFormat.taskLifecycles.solo;
|
|
46
|
+
const scheme = TaskFormat.parseIdScheme(entry.idscheme);
|
|
47
|
+
if (scheme.kind !== "seq")
|
|
48
|
+
throw new Error(`project "${prjId}" carries task id scheme "${entry.idscheme}", but ${service} requires a "seq" one`);
|
|
49
|
+
return { lifecycle, scheme };
|
|
50
|
+
};
|
|
51
|
+
/* decode the labels of an issue into the header: the "ase:<key>:<value>" labels of the
|
|
52
|
+
keys without native counterpart (and of an unassignable "Assignee") and the tags, with
|
|
53
|
+
a legacy "ase:Branch:<name>" label migrated into "Changeset" (see TaskFormat.migrateBranch);
|
|
54
|
+
returns the state of the "ase:Status:<state>" label, as it only refines the issue state */
|
|
55
|
+
export const labelHeader = (header, names, nativeKeys) => {
|
|
56
|
+
const tags = [];
|
|
57
|
+
let status;
|
|
58
|
+
let branch;
|
|
59
|
+
for (const name of names) {
|
|
60
|
+
const m = LABEL_KEY_RE.exec(name);
|
|
61
|
+
if (m === null && !name.startsWith("ase:"))
|
|
62
|
+
tags.push(name);
|
|
63
|
+
else if (m !== null && m[1] === "Status")
|
|
64
|
+
status = m[2];
|
|
65
|
+
else if (m !== null && m[1] === "Branch")
|
|
66
|
+
branch = m[2];
|
|
67
|
+
else if (m !== null && (!nativeKeys.includes(m[1]) || m[1] === "Assignee"))
|
|
68
|
+
header[m[1]] = m[2];
|
|
69
|
+
}
|
|
70
|
+
if (tags.length > 0)
|
|
71
|
+
header.Tags = tags;
|
|
72
|
+
if (branch !== undefined) {
|
|
73
|
+
const changeset = TaskFormat.migrateBranch(branch);
|
|
74
|
+
if (changeset !== undefined && header.Changeset === undefined)
|
|
75
|
+
header.Changeset = changeset;
|
|
76
|
+
}
|
|
77
|
+
return status;
|
|
78
|
+
};
|
|
79
|
+
/* encode the header into labels: the "ase:<key>:<value>" labels of the keys
|
|
80
|
+
without native counterpart and the tags (except the reserved "ase:" ones) */
|
|
81
|
+
export const headerLabels = (header, nativeKeys) => {
|
|
82
|
+
const labels = [];
|
|
83
|
+
for (const [key, value] of Object.entries(header))
|
|
84
|
+
if (!nativeKeys.includes(key) && typeof value === "string")
|
|
85
|
+
labels.push(`ase:${key}:${value}`);
|
|
86
|
+
for (const tag of Array.isArray(header.Tags) ? header.Tags : []) {
|
|
87
|
+
if (tag.startsWith("ase:"))
|
|
88
|
+
throw new Error(`tag "${tag}" collides with the reserved "ase:" labels`);
|
|
89
|
+
labels.push(tag);
|
|
90
|
+
}
|
|
91
|
+
return labels;
|
|
92
|
+
};
|
|
93
|
+
/* derive "Status" from the issue state: a closed issue carries a finished state
|
|
94
|
+
("CANCELLED" if cancelled, else the finished one of its label, else the first
|
|
95
|
+
finished one), an open issue the unfinished one of its label (else none) */
|
|
96
|
+
export const issueStatus = (lifecycle, closed, cancelled, label) => {
|
|
97
|
+
if (closed)
|
|
98
|
+
return cancelled ? "CANCELLED" :
|
|
99
|
+
label !== undefined && label !== "CANCELLED" && lifecycle.finished.includes(label) ? label : lifecycle.finished[0];
|
|
100
|
+
return label !== undefined && !lifecycle.finished.includes(label) ? label : undefined;
|
|
101
|
+
};
|
|
102
|
+
/* derive the body of a task plan from an issue: the "# TASK:" heading from the
|
|
103
|
+
issue title plus the issue body, and the issue title and body from a task plan */
|
|
104
|
+
export const issueBody = (title, text) => {
|
|
105
|
+
const body = (text ?? "").replace(/\r\n/g, "\n").replace(/\n+$/, "");
|
|
106
|
+
return `# TASK: ${title}\n` + (body !== "" ? `\n${body}\n` : "");
|
|
107
|
+
};
|
|
108
|
+
export const planIssue = (taskId, plan) => ({
|
|
109
|
+
title: TaskFormat.taskTitle(plan.body) || taskId,
|
|
110
|
+
body: plan.body.replace(/^#[ \t]+TASK:.*(?:\n|$)/m, "").replace(/^\n+/, "").replace(/\n+$/, "")
|
|
111
|
+
});
|
|
112
|
+
/* whether an attachment is embedded as-is (Markdown) instead of as fenced code block */
|
|
113
|
+
const isMarkdown = (type) => /^text\/markdown\b/i.test(type ?? "");
|
|
114
|
+
/* whether two attachments are equal */
|
|
115
|
+
export const same = (a, b) => {
|
|
116
|
+
const ka = Object.keys(a).sort();
|
|
117
|
+
const kb = Object.keys(b).sort();
|
|
118
|
+
return ka.length === kb.length && ka.every((key, i) => key === kb[i] && a[key] === b[key]);
|
|
119
|
+
};
|
|
120
|
+
/* derive the attachment of a comment: an attachment comment carries its keys in
|
|
121
|
+
the hidden metadata header (with "Data" giving the "|4+" or "|4-" chomping)
|
|
122
|
+
followed by the data (fenced, unless Markdown), with a legacy "preflight" draft
|
|
123
|
+
kind migrated into "draft" (see TaskFormat.migrateAttachment), and any other
|
|
124
|
+
comment reads as a Markdown attachment */
|
|
125
|
+
export const commentAttachment = (body, author, created, modified) => {
|
|
126
|
+
const text = (body ?? "").replace(/\r\n/g, "\n");
|
|
127
|
+
const m = ATTACH_RE.exec(text);
|
|
128
|
+
if (m === null)
|
|
129
|
+
return {
|
|
130
|
+
Type: "text/markdown",
|
|
131
|
+
Desc: `comment by @${author}`,
|
|
132
|
+
Created: stamp(created),
|
|
133
|
+
Modified: stamp(modified),
|
|
134
|
+
Data: text
|
|
135
|
+
};
|
|
136
|
+
const attachment = {};
|
|
137
|
+
let chomp;
|
|
138
|
+
for (const line of m[1].split("\n")) {
|
|
139
|
+
const kv = /^([A-Za-z]+):[ \t]*(.*)$/.exec(line);
|
|
140
|
+
if (kv === null)
|
|
141
|
+
continue;
|
|
142
|
+
if (kv[1] === "Data")
|
|
143
|
+
chomp = kv[2].trim();
|
|
144
|
+
else
|
|
145
|
+
attachment[kv[1]] = unescapeValue(kv[2].trimEnd());
|
|
146
|
+
}
|
|
147
|
+
if (chomp !== undefined) {
|
|
148
|
+
const data = isMarkdown(attachment.Type) ?
|
|
149
|
+
m[2].replace(/\n+$/, "") :
|
|
150
|
+
m[2].replace(/^[^\n]*\n/, "").replace(/\n?[^\n]*$/, "");
|
|
151
|
+
attachment.Data = data !== "" && chomp !== "|4-" ? `${data}\n` : data;
|
|
152
|
+
}
|
|
153
|
+
return TaskFormat.migrateAttachment(attachment);
|
|
154
|
+
};
|
|
155
|
+
/* render the comment of an attachment (see above), with the key column rule
|
|
156
|
+
of TaskFormat.keyLine (a key too long for the column keeps one space) */
|
|
157
|
+
export const attachmentComment = (attachment) => {
|
|
158
|
+
const lines = Object.keys(attachment).filter((key) => key !== "Data")
|
|
159
|
+
.map((key) => `${key}:`.padEnd(Math.max(11, key.length + 2)) + escapeValue(attachment[key]));
|
|
160
|
+
let text = "";
|
|
161
|
+
if (attachment.Data !== undefined) {
|
|
162
|
+
const data = attachment.Data.replace(/\n$/, "");
|
|
163
|
+
lines.push("Data:".padEnd(11) + (attachment.Data !== "" && !attachment.Data.endsWith("\n") ? "|4-" : "|4+"));
|
|
164
|
+
if (isMarkdown(attachment.Type))
|
|
165
|
+
text = data;
|
|
166
|
+
else {
|
|
167
|
+
const fence = "`".repeat(Math.max(3, ...Array.from(data.matchAll(/`+/g), (run) => run[0].length + 1)));
|
|
168
|
+
const lang = /diff/i.test(attachment.Type ?? "") ? "diff" : /yaml/i.test(attachment.Type ?? "") ? "yaml" : "";
|
|
169
|
+
text = `${fence}${lang}\n${data}${data !== "" ? "\n" : ""}${fence}`;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return `<!-- ase:attachment\n${lines.join("\n")}\n-->\n${text}`;
|
|
173
|
+
};
|
|
174
|
+
/* determine the operations synchronizing the comments (given by their attachments)
|
|
175
|
+
with the wanted attachments: the attachments of their longest common subsequence
|
|
176
|
+
keep their comments untouched, and in each gap before a kept attachment (and in the
|
|
177
|
+
final gap) the remaining ones are paired by position and updated, with surplus
|
|
178
|
+
comments deleted and surplus attachments created, so deleting or changing an
|
|
179
|
+
attachment touches its own comment only (and never the ones of others, like the
|
|
180
|
+
comments of humans); as comments can only be appended, an attachment inserted
|
|
181
|
+
before a kept one falls back to the rewriting of all comments by position */
|
|
182
|
+
export const attachmentSync = (current, wanted) => {
|
|
183
|
+
/* determine the equality of all attachment pairs and the lengths
|
|
184
|
+
of the longest common subsequences of all suffix pairs */
|
|
185
|
+
const n = current.length;
|
|
186
|
+
const m = wanted.length;
|
|
187
|
+
const eq = Array.from({ length: n }, () => new Array(m).fill(false));
|
|
188
|
+
const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
189
|
+
for (let i = n - 1; i >= 0; i--)
|
|
190
|
+
for (let j = m - 1; j >= 0; j--) {
|
|
191
|
+
eq[i][j] = same(current[i], wanted[j]);
|
|
192
|
+
lcs[i][j] = eq[i][j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
|
|
193
|
+
}
|
|
194
|
+
/* determine the pairs of the longest common subsequence (plus the end as sentinel) */
|
|
195
|
+
const pairs = [];
|
|
196
|
+
for (let i = 0, j = 0; i < n && j < m;) {
|
|
197
|
+
if (eq[i][j])
|
|
198
|
+
pairs.push([i++, j++]);
|
|
199
|
+
else if (lcs[i + 1][j] >= lcs[i][j + 1])
|
|
200
|
+
i++;
|
|
201
|
+
else
|
|
202
|
+
j++;
|
|
203
|
+
}
|
|
204
|
+
pairs.push([n, m]);
|
|
205
|
+
/* synchronize the gaps before the pairs */
|
|
206
|
+
const ops = [];
|
|
207
|
+
let i = 0;
|
|
208
|
+
let j = 0;
|
|
209
|
+
for (const [pi, pj] of pairs) {
|
|
210
|
+
const olds = pi - i;
|
|
211
|
+
const news = pj - j;
|
|
212
|
+
if (news > olds && pi < n) {
|
|
213
|
+
/* fallback: rewrite all comments by position */
|
|
214
|
+
ops.length = 0;
|
|
215
|
+
for (let k = 0; k < Math.max(n, m); k++) {
|
|
216
|
+
if (k >= m)
|
|
217
|
+
ops.push({ op: "delete", index: k });
|
|
218
|
+
else if (k >= n)
|
|
219
|
+
ops.push({ op: "create", attachment: wanted[k] });
|
|
220
|
+
else if (!eq[k][k])
|
|
221
|
+
ops.push({ op: "update", index: k, attachment: wanted[k] });
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
for (let k = 0; k < Math.max(olds, news); k++) {
|
|
226
|
+
if (k >= news)
|
|
227
|
+
ops.push({ op: "delete", index: i + k });
|
|
228
|
+
else if (k >= olds)
|
|
229
|
+
ops.push({ op: "create", attachment: wanted[j + k] });
|
|
230
|
+
else
|
|
231
|
+
ops.push({ op: "update", index: i + k, attachment: wanted[j + k] });
|
|
232
|
+
}
|
|
233
|
+
i = pi + 1;
|
|
234
|
+
j = pj + 1;
|
|
235
|
+
}
|
|
236
|
+
return ops;
|
|
237
|
+
};
|