@plainconceptsplatform/workflows 0.4.32 → 0.4.33
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/dist/catalog-installation.js +3 -2
- package/dist/catalog-installation.test.js +17 -0
- package/dist/catalog-listing.js +6 -4
- package/dist/catalog-listing.test.js +14 -1
- package/dist/index.js +1 -0
- package/dist/index.test.js +3 -3
- package/dist/workflow-catalog.d.ts +2 -1
- package/dist/workflow-catalog.js +4 -0
- package/dist/workflow-catalog.test.js +1 -1
- package/loops/templates/issues/bug_report.yml +109 -0
- package/loops/templates/issues/feature_request.yml +75 -0
- package/package.json +2 -2
|
@@ -299,11 +299,12 @@ function catalogTemplateMeta(template) {
|
|
|
299
299
|
const entry = catalogTemplates.find((item) => item.name === template);
|
|
300
300
|
if (entry === undefined)
|
|
301
301
|
throw new Error(`Unknown template: ${template}`);
|
|
302
|
-
const directory = template.startsWith("opencode") ? "opencode" : template.startsWith("app-ci-") ? "ci" : template === "github-release" ? "release" : template === "visual-evidence" ? "visual-evidence" : "agentics";
|
|
302
|
+
const directory = template.startsWith("opencode") ? "opencode" : template.startsWith("app-ci-") ? "ci" : template === "github-release" ? "release" : template === "visual-evidence" ? "visual-evidence" : template === "bug-report" || template === "feature-request" ? "issues" : "agentics";
|
|
303
303
|
const isWorkflow = entry.file.endsWith(".yml");
|
|
304
|
-
const
|
|
304
|
+
const inferredTarget = template === "app-ci-dotnet-next"
|
|
305
305
|
? ".github/workflows/app-ci.yml"
|
|
306
306
|
: isWorkflow ? `.github/workflows/${entry.file}` : entry.file;
|
|
307
|
+
const target = entry.target ?? inferredTarget;
|
|
307
308
|
return { directory, file: entry.file, target };
|
|
308
309
|
}
|
|
309
310
|
async function catalogFiles(sourcePath) {
|
|
@@ -384,6 +384,23 @@ describe("catalog installation", () => {
|
|
|
384
384
|
installed: [".github/workflows/agentics-checks.yml"],
|
|
385
385
|
});
|
|
386
386
|
});
|
|
387
|
+
it("installs issue templates to .github/ISSUE_TEMPLATE/", async () => {
|
|
388
|
+
const sourcePath = await createDirectory({
|
|
389
|
+
"templates/issues/bug_report.yml": "name: Bug report\n",
|
|
390
|
+
"templates/issues/feature_request.yml": "name: Feature request\n",
|
|
391
|
+
});
|
|
392
|
+
const repositoryPath = await createDirectory({});
|
|
393
|
+
await expect(installTemplate(repositoryPath, "bug-report", { sourcePath })).resolves.toEqual({
|
|
394
|
+
installed: [".github/ISSUE_TEMPLATE/bug_report.yml"],
|
|
395
|
+
conflicts: [],
|
|
396
|
+
});
|
|
397
|
+
await expect(installTemplate(repositoryPath, "feature-request", { sourcePath })).resolves.toEqual({
|
|
398
|
+
installed: [".github/ISSUE_TEMPLATE/feature_request.yml"],
|
|
399
|
+
conflicts: [],
|
|
400
|
+
});
|
|
401
|
+
await expect(readFile(join(repositoryPath, ".github/ISSUE_TEMPLATE/bug_report.yml"), "utf8")).resolves.toBe("name: Bug report\n");
|
|
402
|
+
await expect(readFile(join(repositoryPath, ".github/ISSUE_TEMPLATE/feature_request.yml"), "utf8")).resolves.toBe("name: Feature request\n");
|
|
403
|
+
});
|
|
387
404
|
});
|
|
388
405
|
describe("route lifecycle", () => {
|
|
389
406
|
it("detects installed route workers in workflowRoutes order", async () => {
|
package/dist/catalog-listing.js
CHANGED
|
@@ -15,7 +15,7 @@ export async function listCatalog(options = {}) {
|
|
|
15
15
|
name: template.name,
|
|
16
16
|
description: template.description,
|
|
17
17
|
file: template.file,
|
|
18
|
-
installed: await isFileInstalled(basePath, template.file),
|
|
18
|
+
installed: await isFileInstalled(basePath, template.file, template.target),
|
|
19
19
|
})));
|
|
20
20
|
return [...routes, ...templates];
|
|
21
21
|
}
|
|
@@ -52,8 +52,8 @@ function formatEntry(entry) {
|
|
|
52
52
|
const mark = entry.installed ? "[x]" : "[ ]";
|
|
53
53
|
return ` ${mark} ${entry.name} — ${entry.description}`;
|
|
54
54
|
}
|
|
55
|
-
async function isFileInstalled(basePath, workerOrTemplateFile) {
|
|
56
|
-
const installedPath = templateInstallPath(basePath, workerOrTemplateFile);
|
|
55
|
+
async function isFileInstalled(basePath, workerOrTemplateFile, explicitTarget) {
|
|
56
|
+
const installedPath = templateInstallPath(basePath, workerOrTemplateFile, explicitTarget);
|
|
57
57
|
try {
|
|
58
58
|
await access(installedPath, constants.F_OK);
|
|
59
59
|
return true;
|
|
@@ -62,7 +62,9 @@ async function isFileInstalled(basePath, workerOrTemplateFile) {
|
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
function templateInstallPath(basePath, file) {
|
|
65
|
+
function templateInstallPath(basePath, file, explicitTarget) {
|
|
66
|
+
if (explicitTarget !== undefined)
|
|
67
|
+
return join(basePath, ...explicitTarget.split("/"));
|
|
66
68
|
const isRootTemplate = file.endsWith(".json");
|
|
67
69
|
return isRootTemplate ? join(basePath, file) : join(basePath, ".github", "workflows", file);
|
|
68
70
|
}
|
|
@@ -16,7 +16,7 @@ describe("catalog listing", () => {
|
|
|
16
16
|
const routeNames = entries.filter((entry) => entry.kind === "route").map((entry) => entry.name);
|
|
17
17
|
const templateNames = entries.filter((entry) => entry.kind === "template").map((entry) => entry.name);
|
|
18
18
|
expect(routeNames).toEqual(["refine", "implement", "direct", "apply-review", "merge-gate", "audit", "propose"]);
|
|
19
|
-
expect(templateNames).toEqual(["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "github-release", "opencode.ci.json", "visual-evidence"]);
|
|
19
|
+
expect(templateNames).toEqual(["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "bug-report", "feature-request", "github-release", "opencode.ci.json", "visual-evidence"]);
|
|
20
20
|
});
|
|
21
21
|
it("reports all entries as not installed in an empty repository", async () => {
|
|
22
22
|
const repositoryPath = await createRepository({});
|
|
@@ -41,6 +41,19 @@ describe("catalog listing", () => {
|
|
|
41
41
|
expect(checksEntry).toBeDefined();
|
|
42
42
|
expect(checksEntry.installed).toBe(true);
|
|
43
43
|
});
|
|
44
|
+
it("marks an issue template as installed when its file exists in .github/ISSUE_TEMPLATE/", async () => {
|
|
45
|
+
const repositoryPath = await createRepository({
|
|
46
|
+
".github/ISSUE_TEMPLATE/bug_report.yml": "name: Bug report",
|
|
47
|
+
".github/ISSUE_TEMPLATE/feature_request.yml": "name: Feature request",
|
|
48
|
+
});
|
|
49
|
+
const entries = await listCatalog({ installedPath: repositoryPath });
|
|
50
|
+
const bugEntry = entries.find((entry) => entry.name === "bug-report");
|
|
51
|
+
const featureEntry = entries.find((entry) => entry.name === "feature-request");
|
|
52
|
+
expect(bugEntry).toBeDefined();
|
|
53
|
+
expect(bugEntry.installed).toBe(true);
|
|
54
|
+
expect(featureEntry).toBeDefined();
|
|
55
|
+
expect(featureEntry.installed).toBe(true);
|
|
56
|
+
});
|
|
44
57
|
it("marks the opencode.ci.json template as installed when the file exists at repository root", async () => {
|
|
45
58
|
const repositoryPath = await createRepository({
|
|
46
59
|
"opencode.ci.json": "{ \"model\": \"plainconcepts/glm-5-2\" }",
|
package/dist/index.js
CHANGED
|
@@ -48,6 +48,7 @@ Options:
|
|
|
48
48
|
--template <name> Install a standalone template alongside or instead of routes.
|
|
49
49
|
Templates: agentics-checks, agentics-maintenance,
|
|
50
50
|
app-ci-dotnet-next, app-ci-node-monorepo,
|
|
51
|
+
bug-report, feature-request, github-release,
|
|
51
52
|
opencode.ci.json.
|
|
52
53
|
--force Overwrite managed files that differ from the package source.
|
|
53
54
|
-h, --help Show this help text.
|
package/dist/index.test.js
CHANGED
|
@@ -34,7 +34,7 @@ describe("workflows CLI", () => {
|
|
|
34
34
|
it("rejects an unsupported template name", async () => {
|
|
35
35
|
const error = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
|
36
36
|
await expect(run(["add", "--template", "unknown"])).resolves.toBe(1);
|
|
37
|
-
expect(error).toHaveBeenCalledWith("--template must be one of: agentics-checks|agentics-maintenance|app-ci-dotnet-next|app-ci-node-monorepo|github-release|opencode.ci.json|visual-evidence.");
|
|
37
|
+
expect(error).toHaveBeenCalledWith("--template must be one of: agentics-checks|agentics-maintenance|app-ci-dotnet-next|app-ci-node-monorepo|bug-report|feature-request|github-release|opencode.ci.json|visual-evidence.");
|
|
38
38
|
error.mockRestore();
|
|
39
39
|
});
|
|
40
40
|
it("rejects an unknown route passed to add", async () => {
|
|
@@ -65,9 +65,9 @@ describe("workflows CLI", () => {
|
|
|
65
65
|
// None installed: all [ ]
|
|
66
66
|
const installedCount = (output.match(/\[x\]/g) ?? []).length;
|
|
67
67
|
expect(installedCount).toBe(0);
|
|
68
|
-
// 7 routes +
|
|
68
|
+
// 7 routes + 9 templates = 16 entries
|
|
69
69
|
const uninstalledCount = (output.match(/\[ \]/g) ?? []).length;
|
|
70
|
-
expect(uninstalledCount).toBe(
|
|
70
|
+
expect(uninstalledCount).toBe(16);
|
|
71
71
|
log.mockRestore();
|
|
72
72
|
});
|
|
73
73
|
it("marks installed workflows with [x]", async () => {
|
|
@@ -14,11 +14,12 @@ export interface MandatoryFile {
|
|
|
14
14
|
}
|
|
15
15
|
export declare const mandatoryFiles: readonly MandatoryFile[];
|
|
16
16
|
export declare const generatedConsumerTargets: readonly [".github/workflows/agent-*.lock.yml", ".github/aw/actions-lock.json"];
|
|
17
|
-
export declare const templateNames: readonly ["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "github-release", "opencode.ci.json", "visual-evidence"];
|
|
17
|
+
export declare const templateNames: readonly ["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "bug-report", "feature-request", "github-release", "opencode.ci.json", "visual-evidence"];
|
|
18
18
|
export type TemplateName = (typeof templateNames)[number];
|
|
19
19
|
export interface CatalogTemplate {
|
|
20
20
|
readonly name: TemplateName;
|
|
21
21
|
readonly file: string;
|
|
22
22
|
readonly description: string;
|
|
23
|
+
readonly target?: string;
|
|
23
24
|
}
|
|
24
25
|
export declare const catalogTemplates: readonly CatalogTemplate[];
|
package/dist/workflow-catalog.js
CHANGED
|
@@ -38,6 +38,8 @@ export const templateNames = [
|
|
|
38
38
|
"agentics-maintenance",
|
|
39
39
|
"app-ci-dotnet-next",
|
|
40
40
|
"app-ci-node-monorepo",
|
|
41
|
+
"bug-report",
|
|
42
|
+
"feature-request",
|
|
41
43
|
"github-release",
|
|
42
44
|
"opencode.ci.json",
|
|
43
45
|
"visual-evidence",
|
|
@@ -47,6 +49,8 @@ export const catalogTemplates = [
|
|
|
47
49
|
{ name: "agentics-maintenance", file: "agentics-maintenance.yml", description: "Agentic maintenance: scheduled daily maintenance workflow for keeping workflows and actions up to date." },
|
|
48
50
|
{ name: "app-ci-dotnet-next", file: "app-ci-dotnet-next.yml", description: "App CI pipeline for a .NET + Next.js monorepo: build, test, and lint on PRs and schedule." },
|
|
49
51
|
{ name: "app-ci-node-monorepo", file: "app-ci-node-monorepo.yml", description: "App CI pipeline for a Node monorepo: build, test, and lint on PRs and schedule." },
|
|
52
|
+
{ name: "bug-report", file: "bug_report.yml", description: "Bug report issue template: what happened, repro steps, expected behavior, acceptance criteria, environment, logs.", target: ".github/ISSUE_TEMPLATE/bug_report.yml" },
|
|
53
|
+
{ name: "feature-request", file: "feature_request.yml", description: "Feature request issue template scoped to small, well-scoped improvements (Small/Medium only; large work belongs in a planning issue).", target: ".github/ISSUE_TEMPLATE/feature_request.yml" },
|
|
50
54
|
{ name: "github-release", file: "github-release.yml", description: "Publishes or updates a GitHub Release with generated notes whenever a v* tag is pushed." },
|
|
51
55
|
{ name: "opencode.ci.json", file: "opencode.ci.json", description: "Standalone OpenCode CI config: plainconcepts provider, GLM model registration, ci-workflow-agent, and LSP defaults for consumer repositories." },
|
|
52
56
|
{ name: "visual-evidence", file: "visual-evidence.yml", description: "Visual evidence: captures screenshots of UI changes on bot-authored PRs by reading the capturePlan left by the agent in evidence.json and executing it on a runner with Docker and Chrome access." },
|
|
@@ -17,7 +17,7 @@ describe("workflow catalog", () => {
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
it("lists supported optional templates", () => {
|
|
20
|
-
expect(templateNames).toEqual(["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "github-release", "opencode.ci.json", "visual-evidence"]);
|
|
20
|
+
expect(templateNames).toEqual(["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "bug-report", "feature-request", "github-release", "opencode.ci.json", "visual-evidence"]);
|
|
21
21
|
});
|
|
22
22
|
it("gives every catalog template a non-empty description and file", () => {
|
|
23
23
|
expect(catalogTemplates.map((template) => template.name)).toEqual([...templateNames]);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a defect
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Thanks for reporting. Do **not** include real secrets or production data — use placeholders.
|
|
9
|
+
|
|
10
|
+
For security vulnerabilities, contact the team directly instead of filing a public issue.
|
|
11
|
+
- type: dropdown
|
|
12
|
+
id: severity
|
|
13
|
+
attributes:
|
|
14
|
+
label: Severity
|
|
15
|
+
options:
|
|
16
|
+
- Production-down / data loss
|
|
17
|
+
- Major — broken core flow
|
|
18
|
+
- Minor — broken edge case
|
|
19
|
+
- Cosmetic / polish
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
- type: dropdown
|
|
23
|
+
id: frequency
|
|
24
|
+
attributes:
|
|
25
|
+
label: Frequency
|
|
26
|
+
description: How often does this happen?
|
|
27
|
+
options:
|
|
28
|
+
- Always
|
|
29
|
+
- Intermittent
|
|
30
|
+
- Only once
|
|
31
|
+
validations:
|
|
32
|
+
required: true
|
|
33
|
+
- type: textarea
|
|
34
|
+
id: what-happened
|
|
35
|
+
attributes:
|
|
36
|
+
label: What happened?
|
|
37
|
+
description: A clear description of the bug and its impact.
|
|
38
|
+
validations:
|
|
39
|
+
required: true
|
|
40
|
+
- type: textarea
|
|
41
|
+
id: repro
|
|
42
|
+
attributes:
|
|
43
|
+
label: Steps to reproduce
|
|
44
|
+
description: How can a maintainer reproduce it?
|
|
45
|
+
placeholder: |
|
|
46
|
+
1. Go to ...
|
|
47
|
+
2. Do ...
|
|
48
|
+
3. See error
|
|
49
|
+
validations:
|
|
50
|
+
required: true
|
|
51
|
+
- type: textarea
|
|
52
|
+
id: expected
|
|
53
|
+
attributes:
|
|
54
|
+
label: Expected behavior
|
|
55
|
+
validations:
|
|
56
|
+
required: true
|
|
57
|
+
- type: dropdown
|
|
58
|
+
id: area
|
|
59
|
+
attributes:
|
|
60
|
+
label: Where does it occur?
|
|
61
|
+
options:
|
|
62
|
+
- Backend
|
|
63
|
+
- Frontend
|
|
64
|
+
- Both
|
|
65
|
+
- CLI / tooling
|
|
66
|
+
- Configuration / deployment
|
|
67
|
+
- Documentation
|
|
68
|
+
- Other
|
|
69
|
+
validations:
|
|
70
|
+
required: true
|
|
71
|
+
- type: textarea
|
|
72
|
+
id: acceptance
|
|
73
|
+
attributes:
|
|
74
|
+
label: Acceptance criteria
|
|
75
|
+
description: How will we know it's fixed? A checklist a reviewer can verify (include the test that should turn green).
|
|
76
|
+
placeholder: |
|
|
77
|
+
- [ ] ...
|
|
78
|
+
- [ ] A test that fails before the fix and passes after
|
|
79
|
+
validations:
|
|
80
|
+
required: false
|
|
81
|
+
- type: textarea
|
|
82
|
+
id: open-questions
|
|
83
|
+
attributes:
|
|
84
|
+
label: Open questions
|
|
85
|
+
description: Decisions a maintainer must make before this can be planned. Leave blank if none.
|
|
86
|
+
validations:
|
|
87
|
+
required: false
|
|
88
|
+
- type: textarea
|
|
89
|
+
id: environment
|
|
90
|
+
attributes:
|
|
91
|
+
label: Environment
|
|
92
|
+
description: Local vs deployed, branch/commit, OS, runtime versions if relevant.
|
|
93
|
+
validations:
|
|
94
|
+
required: false
|
|
95
|
+
- type: textarea
|
|
96
|
+
id: logs
|
|
97
|
+
attributes:
|
|
98
|
+
label: Logs / screenshots
|
|
99
|
+
description: Paste relevant logs or screenshots. Redact secrets and PII.
|
|
100
|
+
render: shell
|
|
101
|
+
validations:
|
|
102
|
+
required: false
|
|
103
|
+
- type: textarea
|
|
104
|
+
id: related
|
|
105
|
+
attributes:
|
|
106
|
+
label: Related links
|
|
107
|
+
description: Links to related issues, PRs, discussions, or docs. Leave blank if none.
|
|
108
|
+
validations:
|
|
109
|
+
required: false
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Propose a small improvement or new capability
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
This template is for small, well-scoped improvements — a focused
|
|
9
|
+
enhancement, a missing convenience, a quality-of-life fix.
|
|
10
|
+
|
|
11
|
+
For large features, architecture changes, or cross-cutting work,
|
|
12
|
+
open a planning issue or an ADR instead.
|
|
13
|
+
|
|
14
|
+
Do **not** include real secrets or production data — use placeholders.
|
|
15
|
+
- type: dropdown
|
|
16
|
+
id: scope
|
|
17
|
+
attributes:
|
|
18
|
+
label: Scope
|
|
19
|
+
description: How big is this change? If it doesn't fit Small or Medium, use a planning issue instead.
|
|
20
|
+
options:
|
|
21
|
+
- Small — a single file or component, no refactoring
|
|
22
|
+
- Medium — touches a few files or a feature area, may need minor refactoring
|
|
23
|
+
validations:
|
|
24
|
+
required: true
|
|
25
|
+
- type: textarea
|
|
26
|
+
id: current-behavior
|
|
27
|
+
attributes:
|
|
28
|
+
label: Current behavior
|
|
29
|
+
description: How does it work today? Describe the existing behavior or limitation.
|
|
30
|
+
validations:
|
|
31
|
+
required: true
|
|
32
|
+
- type: textarea
|
|
33
|
+
id: problem
|
|
34
|
+
attributes:
|
|
35
|
+
label: Problem / motivation
|
|
36
|
+
description: What user or business need does this address? Why is the current behavior a problem?
|
|
37
|
+
validations:
|
|
38
|
+
required: true
|
|
39
|
+
- type: textarea
|
|
40
|
+
id: proposal
|
|
41
|
+
attributes:
|
|
42
|
+
label: Proposed solution
|
|
43
|
+
description: What would you like to happen?
|
|
44
|
+
validations:
|
|
45
|
+
required: true
|
|
46
|
+
- type: textarea
|
|
47
|
+
id: alternatives
|
|
48
|
+
attributes:
|
|
49
|
+
label: Alternatives considered
|
|
50
|
+
validations:
|
|
51
|
+
required: false
|
|
52
|
+
- type: textarea
|
|
53
|
+
id: acceptance
|
|
54
|
+
attributes:
|
|
55
|
+
label: Acceptance criteria
|
|
56
|
+
description: A checklist a reviewer can verify when this is done (include the tests).
|
|
57
|
+
placeholder: |
|
|
58
|
+
- [ ] ...
|
|
59
|
+
- [ ] Tests
|
|
60
|
+
validations:
|
|
61
|
+
required: false
|
|
62
|
+
- type: textarea
|
|
63
|
+
id: open-questions
|
|
64
|
+
attributes:
|
|
65
|
+
label: Open questions
|
|
66
|
+
description: Decisions a maintainer must make before this can be planned. Leave blank if none.
|
|
67
|
+
validations:
|
|
68
|
+
required: false
|
|
69
|
+
- type: textarea
|
|
70
|
+
id: related
|
|
71
|
+
attributes:
|
|
72
|
+
label: Related links
|
|
73
|
+
description: Links to related issues, PRs, discussions, or docs. Leave blank if none.
|
|
74
|
+
validations:
|
|
75
|
+
required: false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plainconceptsplatform/workflows",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.33",
|
|
4
4
|
"description": "Install and update Platform GitHub agentic workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github-actions",
|
|
@@ -40,4 +40,4 @@
|
|
|
40
40
|
"typescript": "^5.8.0",
|
|
41
41
|
"vitest": "^3.0.0"
|
|
42
42
|
}
|
|
43
|
-
}
|
|
43
|
+
}
|