Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
4e19a2c
fix(role): align role commands with the v3 Roles API
jbrazda Sep 30, 2026
27ed61d
feat(role): interactive privilege editor and role cloning (CR-0040)
jbrazda Sep 30, 2026
ef41cc5
chore: move CR-0040 and role API bug to completed
jbrazda Sep 30, 2026
5e21c5d
docs: add CR-0041, CR-0042 and permission API bug
jbrazda Sep 30, 2026
3af25c3
feat(tui): shared huh-based prompts (CR-0041 step 0)
jbrazda Sep 30, 2026
980dada
feat(user): paged user wizard with review (CR-0041 #1)
jbrazda Sep 30, 2026
deea7e9
feat(group,environment): checklist wizards with review (CR-0041 #2, #3)
jbrazda Sep 30, 2026
b9941b6
refactor(cmd): shared pickers and confirmAction (CR-0041 #4)
jbrazda Sep 30, 2026
800a3aa
feat(user): update-roles and update-groups commands (CR-0043)
jbrazda Sep 30, 2026
d28c18d
refactor(user): replace user update with interactive user edit
jbrazda Sep 30, 2026
089f26c
fix(permission): align permission commands with the object permission…
jbrazda Sep 30, 2026
0d9c87c
feat(permission): interactive ACL editor (CR-0042)
jbrazda Sep 30, 2026
76a8123
feat(menu): interactive main menu for bare iics (CR-0044)
jbrazda Sep 30, 2026
2d5b88b
feat(agent): interactive installer download and installer info menu e…
jbrazda Sep 30, 2026
84045fb
feat(user): configurable user name and email patterns for new users (…
jbrazda Sep 30, 2026
d9d6e42
feat(user): Show user menu entry with user search; server-side lookups
jbrazda Sep 30, 2026
cca4e9e
feat(user): reject existing user names in the create wizard
jbrazda Sep 30, 2026
ae4ef85
fix(user): keep last direct role; skip roles inherited from groups
jbrazda Oct 1, 2026
e1d5aa9
fix(menu): clear the screen before each menu entry runs
jbrazda Oct 2, 2026
bf246b8
feat(profile): profile wizard and global settings editor (CR-0046)
jbrazda Oct 2, 2026
3682328
build: move main package to cmd/iics so go install produces iics
jbrazda Oct 5, 2026
20052b4
docs: note cmd/iics install path change in CHANGELOG
jbrazda Oct 5, 2026
7de106c
feat(menu): Delete profile entry with profile picker
jbrazda Oct 5, 2026
ffac154
fix(menu): pass global flags on to commands run from the menu
jbrazda Oct 5, 2026
75024c8
chore: move CR-0041 to CR-0046 and pending bugs to completed
jbrazda Oct 8, 2026
02004e2
docs: add package doc comments and AGENTS.md
jbrazda Oct 11, 2026
901c88c
docs: fix markdownlint errors across all Markdown files
jbrazda Oct 11, 2026
0a34081
docs: point AGENTS.md at docs/CLAUDE.md
jbrazda Oct 11, 2026
a800412
docs: use go from PATH instead of /opt/local/bin/go
jbrazda Oct 11, 2026
867f2f9
feat: connection management, connector metadata, project/folder PATCH…
jbrazda Oct 19, 2026
a012dca
feat: project list and folder list with created/updated details (CR-0…
jbrazda Oct 19, 2026
e8c8d53
chore: move CR-0047 to completed
jbrazda Oct 19, 2026
6d9e98a
feat: Esc/q cancel prompts, single-key confirmations (CR-0048)
jbrazda Oct 19, 2026
779e5de
feat: Esc cancels wizards; cancel-key hint under prompts (CR-0048)
jbrazda Oct 19, 2026
5ca7773
chore: move CR-0048 to completed
jbrazda Oct 19, 2026
c0270cc
feat: agent configs get/set for agent-level service properties (CR-0049)
jbrazda Oct 20, 2026
e39aeee
feat: YAML/JSON change files, configs get --export, restart hint (CR-…
jbrazda Oct 20, 2026
8f86d59
feat: agent rename; hide non-editable agent system properties (CR-0049)
jbrazda Oct 20, 2026
2f2867e
feat: agent configs get table grouped by service like agent details -…
jbrazda Oct 20, 2026
f940863
feat: interactive agent property editor, agent configs edit (CR-0049)
jbrazda Oct 20, 2026
525735e
feat: custom agent properties (Custom Configuration Details) (CR-0049)
jbrazda Oct 20, 2026
8db041d
feat: reset all agent properties; agent details -i with view picker (…
jbrazda Oct 20, 2026
407eb19
docs: add asciinema demo of key CLI features to README
jbrazda Oct 20, 2026
6e3bba0
chore: add README demo recorder with local-only masking rules
jbrazda Oct 20, 2026
4859fe0
chore: update CHANGELOG for v0.6.0 release
jbrazda Oct 20, 2026
1314e20
Merge branch 'main' into dev after v0.6.0
jbrazda Oct 20, 2026
fa77997
chore: move CR-0049 to completed
jbrazda Oct 20, 2026
eee0f74
fix: agent configs save via configFormJson; ignore whitespace-only ov…
jbrazda Oct 23, 2026
9ee5bab
fix: agent start/restart wait up to 20 minutes; label service subStates
jbrazda Oct 23, 2026
9d87600
fix: agent details --full reports overridden properties like configs get
jbrazda Oct 23, 2026
02fdb15
chore: move agent configs HTTP 500 bug to completed
jbrazda Oct 23, 2026
3cd23f4
chore: version the commit and release skills
jbrazda Oct 23, 2026
47a1fdf
chore: update CHANGELOG for v0.6.1 release
jbrazda Oct 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .claude/skills/commit/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
name: commit
description: Commit work in the iics-cli repo with the project's checks - branch guard (dev only), gofmt, go vet, golangci-lint, tests, docs and completions, CR and bug file moves, then a conventional commit without a Co-Authored-By trailer. Use when asked to commit, save, or check in changes in this repo, including "commit and push".
---

# Commit

Follow these steps in order. Stop and report on any failure.

## 1. Branch guard

Run `git branch --show-current`. Continue only on `dev`; otherwise stop and ask.

## 2. Checks

```bash
gofmt -s -w .
go vet ./...
golangci-lint run ./...
go test ./...
```

Fix what they report, then run them again. If `go.mod` changed, check that its
Go version matches `go-version` in `.github/workflows/*.yml`.

## 3. Completeness

Check the diff (`git status --short`, `git diff --stat`) against these rules:

- A command or flag was added or changed: `docs/documentation/<resource>.md`
and the `README.md` commands table are updated, and `make completions` was
run. Commit the `completions/` changes with the code.
- A user-visible fix or feature: `CHANGELOG.md` has an entry under
`## [Unreleased]`.
- A CR was implemented: its file moves from `docs/ChangeRequests/new/` to
`pending/` in the same commit. A bug was fixed: same for `docs/issues/`.
Move files to `completed/` only when the user confirms.
- Markdown files were changed: they lint clean (`markdown-lint` skill).

Report anything missing and fix it, or ask if it needs a decision.

## 4. Stage and commit

- Stage the files of this change by path (`git add <paths>`). Do not use
`git add -A`; leave unrelated or unknown files unstaged and mention them.
- Write a conventional commit message (`feat:`, `fix:`, `docs:`, `chore:`,
`refactor:`, `test:`; scope optional, CR number in the subject when there is
one, for example `feat(agent): ... (CR-0049)`). The body says why.
- No `Co-Authored-By` trailer (project rule; it overrides the default
attribution).
- Commit with a heredoc (`git commit -F - <<'EOF'`), then show
`git log --oneline -1` and `git status --short`.

## 5. Push

Push only when the user asked for it: `git push origin dev`.
114 changes: 114 additions & 0 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
name: release
description: Release a new iics-cli version from dev - checks, CHANGELOG, a release PR to main, fast-forward (keeps every commit) or squash merge, tag, GoReleaser, and CI monitoring. Use when asked to release, cut, tag or publish a version, or to merge dev into main for a release.
---

# Release

The release goes from `dev` to `main` through a pull request, which records the
release and runs CI on it. How it lands on `main`:

- **Fast-forward (default):** push `dev` to `main`. Every commit is kept,
`main` and `dev` stay identical, and GitHub marks the PR as merged because
its commits are now on `main`. Needs your bypass of the `main` ruleset while
it requires pull requests.
- **Squash (when the user asks for one commit):** `gh pr merge --squash`, then
merge `main` back into `dev`.

Stop and report on any failure. Ask before every step that pushes.

## 1. Branch guard and sync

```bash
git branch --show-current # must be dev
git fetch origin
git status --short # must be clean
git merge-base --is-ancestor origin/main dev # fast-forward possible
```

If `dev` is behind `origin/dev`, stop. If `main` is not an ancestor of `dev`
(a squash release or a commit made on `main`), a fast-forward is impossible:
report it and offer squash, or `git merge origin/main` into `dev` first.

## 2. Checks

```bash
gofmt -s -l . # must print nothing
go vet ./...
golangci-lint run ./...
go test -race ./...
```

Check that the Go version in `go.mod` matches `go-version` in
`.github/workflows/*.yml`, and that `make completions` leaves no changes.

## 3. Pending CRs and bugs

List the files in `docs/ChangeRequests/pending/` and `docs/issues/pending/`
with the commit that implemented each (`git log --oneline -- <file>`). Ask the
user which ones are confirmed. Move only those with `git mv` to `completed/`
and commit: `chore: move <ids> to completed`.

## 4. Version

Show the latest tag (`git tag --sort=-version:refname | head -1`) and the
commits since it (`git log --oneline <tag>..dev`). Suggest a version: minor
for features or breaking changes, patch for fixes only. Ask the user to
confirm; it must match `v\d+\.\d+\.\d+`.

## 5. CHANGELOG

In `CHANGELOG.md`, rename `## [Unreleased]` to `## [X.Y.Z] - YYYY-MM-DD`
(today) and add an empty `## [Unreleased]` above it. Edit in place; do not
rewrite the file. Check that breaking changes to commands, flags or output
fields are called out. Lint the file (`markdown-lint` skill), then commit
`chore: update CHANGELOG for vX.Y.Z release` (no `Co-Authored-By` trailer)
and push `dev`.

## 6. Release PR

```bash
gh pr create --base main --head dev --title "Release vX.Y.Z" --body-file <notes>
```

The body: one line on what the release is, the merge method, and the
CHANGELOG section for the version. End it with the PR attribution line. Wait
for the PR checks (`gh pr checks <n> --watch`); stop if any fail.

## 7. Land on main

Fast-forward:

```bash
git push origin dev:main
gh pr view <n> --json state # MERGED
```

Squash, only when asked:

```bash
gh pr merge <n> --squash --subject "Release vX.Y.Z (#<n>)"
git fetch origin
git merge --no-edit origin/main # on dev
git push origin dev
```

## 8. Tag

Tag the commit now on `main` and push the tag; it starts the GoReleaser
workflow.

```bash
git tag -a vX.Y.Z -m "Release vX.Y.Z" origin/main
git push origin vX.Y.Z
```

## 9. Monitor

```bash
gh run list --workflow Release --limit 3
gh run watch <run-id> --exit-status
gh release view vX.Y.Z
```

Report the result of the Release job and the release URL with its assets.
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ dist/
# Debug binaries
__debug_bin*

# Claude Code runtime artifacts
.claude/
# Claude Code runtime artifacts; the project skills are versioned
.claude/*
!.claude/skills/

target/
# Demo recording masking rules (they name the values they hide)
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.6.1] - 2026-10-23

### Fixed

- `agent configs set`, `agent configs edit` and `agent rename` failed with
HTTP 500 after an Administrator UI change. The agent form's property rows
are now sent in the `configFormJson` field, as the UI sends them.
- `agent configs` no longer reports values that differ from the default only
in line endings or whitespace as overridden. Setting such a value, or
resetting a property that is not overridden, is no longer shown as a
change.
- `agent restart --blocking` and `agent start --blocking` timed out while
Process Server was still starting. `--max-wait-time` now defaults to 1200
seconds (20 minutes) for `start` and `restart` (`stop` keeps 300), poll
lines label subStates (`-1 - Initializing`, `2 - Starting`,
`0 - Operational`), and a timeout reports the last state seen.
- `agent details --full` showed every property as not customized, because the
API's `customized` flag only marks custom properties. The `CUSTOMIZED`
column is replaced by `OVERRIDDEN` (`custom`, `yes` or empty), computed from
value and default as in `agent configs get`, and JSON/YAML output adds an
`overridden` field.

## [0.6.0] - 2026-10-20

First production-ready release. The core functionality (profiles and
Expand Down
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ golangci-lint run ./...

## Commit Style

- Commit with the `commit` skill and release with the `release` skill
(`.claude/skills/`); they carry the full checklists below.
- Do **not** include `Co-Authored-By: Claude ...` trailers in commit messages.
- **Before every commit**, run the following and fix all reported issues:

Expand Down Expand Up @@ -146,7 +148,9 @@ Commit the updated files in `completions/` together with the code change.

## Markdown Rules

- Generate Markdown that follows the [Markdown Lint Rules](https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md)
- Generate Markdown that follows the [Markdown Lint Rules](https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md).
`.markdownlint-cli2.yaml` holds the project rules; a global hook formats and
lints every Markdown file Claude writes (prettier, then markdownlint-cli2).
- Do **not** use em dashes (`-`) in generated Markdown; use a regular hyphen (`-`) instead

See [docs/CLAUDE.md](docs/CLAUDE.md) for complete code templates for all four files.
7 changes: 5 additions & 2 deletions cmd/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,15 @@ var agentEngineConfigCols = []output.Column{
{Header: "NAME", Field: "name", Width: 30},
{Header: "VALUE", Field: "value", Width: 40, Func: wrapConfigField("value")},
{Header: "DEFAULT", Field: "defaultValue", Width: 40, Func: wrapConfigField("defaultValue")},
{Header: "CUSTOMIZED", Field: "customized", Width: 10, Func: func(v interface{}) string {
{Header: "OVERRIDDEN", Field: "overridden", Width: 10, Func: func(v interface{}) string {
row, _ := v.(map[string]interface{})
if b, _ := row["customized"].(bool); b {
return "custom"
}
if b, _ := row["overridden"].(bool); b {
return "yes"
}
return "no"
return ""
}},
}

Expand Down
56 changes: 47 additions & 9 deletions cmd/agent_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,22 @@ type agentServiceFlags struct {
maxWaitTime int
}

func (f *agentServiceFlags) bind(cmd *cobra.Command, past string) {
// Default --max-wait-time values. Process Server can take well over five
// minutes to become operational after a start.
const (
defaultStopWaitSec = 300
defaultStartWaitSec = 1200
)

func (f *agentServiceFlags) bind(cmd *cobra.Command, past string, maxWaitSec int) {
cmd.Flags().StringVar(&f.id, "id", "", "agent ID")
cmd.Flags().StringVar(&f.name, "name", "", "agent name")
cmd.Flags().StringVar(&f.hostname, "hostname", "", "agent host name")
cmd.Flags().StringVar(&f.service, "service", "", "service display name (required unless --interactive)")
cmd.Flags().BoolVarP(&f.interactive, "interactive", "i", false, "select the agent and service interactively")
cmd.Flags().BoolVar(&f.blocking, "blocking", false, fmt.Sprintf("poll the service status until it has %s", past))
cmd.Flags().IntVar(&f.pollInterval, "poll-interval", 10, "seconds between status polls (with --blocking)")
cmd.Flags().IntVar(&f.maxWaitTime, "max-wait-time", 300, "maximum seconds to wait (with --blocking)")
cmd.Flags().IntVar(&f.maxWaitTime, "max-wait-time", maxWaitSec, "maximum seconds to wait (with --blocking)")
cmd.MarkFlagsMutuallyExclusive("id", "name", "hostname")
}

Expand Down Expand Up @@ -149,7 +156,11 @@ func newAgentServiceCmd(use, short string, action client.AgentServiceAction, pas
return nil
},
}
f.bind(cmd, past)
maxWait := defaultStopWaitSec
if action == client.AgentServiceStart {
maxWait = defaultStartWaitSec
}
f.bind(cmd, past, maxWait)
return cmd
}

Expand All @@ -163,7 +174,9 @@ func newAgentRestartCmd() *cobra.Command {
The service is stopped and the command always waits for it to leave the agent's
service listing, then it is started again. With --blocking (or after the
interactive prompt) it also waits for the service to come back up (RUNNING with
subState 0). --max-wait-time bounds the whole operation.`,
subState 0). --max-wait-time bounds the whole operation; the default of 20
minutes allows for services such as Process Server that take well over five
minutes to start.`,
Example: ` iics agent restart --id <agent-id> --service "Data Integration Server"
iics agent restart -i`,
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -223,7 +236,7 @@ subState 0). --max-wait-time bounds the whole operation.`,
return nil
},
}
f.bind(cmd, "restarted")
f.bind(cmd, "restarted", defaultStartWaitSec)
return cmd
}

Expand Down Expand Up @@ -304,6 +317,20 @@ func serviceStates(d *client.AgentDetails, svc string) []client.AgentEngineStatu
return out
}

// subStateLabel describes an engine subState in the poll log. "2" is shown as
// a warning in the Administrator UI, but the service is still starting.
func subStateLabel(sub string) string {
switch sub {
case "0":
return "Operational"
case "-1":
return "Initializing"
case "2":
return "Starting"
}
return ""
}

func servicePending(status string) bool {
switch strings.ToUpper(status) {
case "NEED_RUNNING", "NEED_STOP", "DEPLOYING", "STARTING", "STOPPING":
Expand All @@ -328,8 +355,8 @@ func serviceReached(verb string, states []client.AgentEngineStatus) (bool, error
return false, nil
}
if strings.EqualFold(s.Status, "RUNNING") {
// subState "0" means the engine is fully operational; "-1"
// (and other values) mean it is still initializing.
// subState "0" means the engine is fully operational; "-1",
// "2" and other values mean it is still initializing.
if s.SubState == "0" {
operational = true
} else {
Expand Down Expand Up @@ -372,8 +399,15 @@ func waitForAgentService(ctx context.Context, c *client.Client, agentID, svc, ve
if len(states) == 0 {
_, _ = fmt.Fprintf(w, "%s%s: (not reported)\n", ts(), svc)
}
var last []string
for _, s := range states {
_, _ = fmt.Fprintf(w, "%s%s: %s (desired %s, subState %s)\n", ts(), svc, s.Status, s.DesiredStatus, s.SubState)
sub := s.SubState
if l := subStateLabel(sub); l != "" {
sub += " - " + l
}
line := fmt.Sprintf("%s (desired %s, subState %s)", s.Status, s.DesiredStatus, sub)
last = append(last, line)
_, _ = fmt.Fprintf(w, "%s%s: %s\n", ts(), svc, line)
}

done, rerr := serviceReached(verb, states)
Expand All @@ -385,7 +419,11 @@ func waitForAgentService(ctx context.Context, c *client.Client, agentID, svc, ve
return nil
}
if time.Now().After(deadline) {
return fmt.Errorf("timed out waiting for service %q to %s", svc, verb)
if len(last) == 0 {
last = []string{"not reported"}
}
return fmt.Errorf("timed out waiting for service %q to %s; last state: %s (raise --max-wait-time to wait longer)",
svc, verb, strings.Join(last, "; "))
}
}
}
Loading
Loading